Add fetchAllMixedAccounts helper for heterogeneous multi-account fetching#248
Add fetchAllMixedAccounts helper for heterogeneous multi-account fetching#248blockiosaurus wants to merge 3 commits intov1.0from
Conversation
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR introduces fixed-size option type support across IDL, node system, and renderers; adds BigInt value handling; implements padding attributes for instruction arguments and struct fields; enables nested account structures; performs a major Rust renderer overhaul with Anchor trait support; and updates TypeScript compilation target to es2020. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Summary by CodeRabbit
WalkthroughThe pull request introduces batch-fetch utilities for multiple accounts of different types in a single RPC call. A new template emits helper functions and types, the render map is updated to emit this template when accounts exist, and the main accounts index re-exports the new utilities. Changes
Sequence Diagram(s)sequenceDiagram
participant Consumer
participant Context as Context/RPC
participant Deserializer as Deserialization Logic
rect rgba(200, 150, 100, 0.5)
Note over Consumer,Deserializer: fetchAllMixedAccounts Flow
Consumer->>Consumer: Extract publicKeys from inputs
Consumer->>Context: context.rpc.getAccounts(publicKeys, options)
Context-->>Consumer: RPC accounts[]
Consumer->>Consumer: assertAccountExists for each account
Consumer->>Deserializer: Deserialize each account using input.deserialize
Deserializer-->>Consumer: Deserialized typed tuple
Consumer-->>Consumer: Return DeserializedAccounts<T>
end
rect rgba(100, 150, 200, 0.5)
Note over Consumer,Deserializer: safeFetchAllMixedAccounts Flow
Consumer->>Consumer: Extract publicKeys from inputs
Consumer->>Context: context.rpc.getAccounts(publicKeys, options)
Context-->>Consumer: RPC accounts[] (may include nulls)
Consumer->>Deserializer: Conditionally deserialize if account exists
Deserializer-->>Consumer: Deserialized value or null
Consumer-->>Consumer: Return MaybeDeserializedAccounts<T>
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderers/js/templates/accountsFetchHelpers.njk`:
- Around line 36-54: Update the JSDoc for fetchAllMixedAccounts to explicitly
state the 100-account RPC limit and the function's behavior when that limit is
exceeded: note that the implementation does not enforce or perform client-side
chunking, that the underlying RPC (getMultipleAccounts / getAccounts) may reject
requests over 100 accounts, and recommend either chunking client-side or calling
a helper that does so (e.g., safeFetchAllMixedAccounts) if callers need to fetch
more than 100 accounts in one logical operation.
- Around line 100-104: The explicit cast "(maybeAccount as RpcAccount)" is
unnecessary because MaybeRpcAccount is a discriminated union and checking
maybeAccount.exists should narrow the type; remove the cast in the return
mapping (the block using maybeAccounts, maybeAccount.exists and
inputs[index].deserialize) so you call inputs[index].deserialize(maybeAccount)
directly, and if TypeScript still complains ensure the source type of
maybeAccounts is declared/returned as MaybeRpcAccount so narrowing works (or add
a short type guard function isRpcAccount(m: MaybeRpcAccount): m is RpcAccount
that checks m.exists and use it before calling inputs[index].deserialize).
| /** | ||
| * Fetches multiple accounts of potentially different types in a single RPC | ||
| * call and deserializes each one using its provided deserializer. | ||
| * | ||
| * This is useful in frontends and other scenarios where you want to minimize | ||
| * the number of RPC round-trips by batching up to 100 accounts into a single | ||
| * `getMultipleAccounts` call while still getting fully typed results. | ||
| * | ||
| * All accounts must exist, otherwise an error is thrown. Use | ||
| * {@link safeFetchAllMixedAccounts} if some accounts may not exist. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const [metadata, edition] = await fetchAllMixedAccounts(context, [ | ||
| * { publicKey: metadataAddr, deserialize: deserializeMetadata }, | ||
| * { publicKey: editionAddr, deserialize: deserializeEdition }, | ||
| * ]); | ||
| * // metadata: Metadata, edition: Edition — fully typed! | ||
| * ``` |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding a note about the account limit.
The JSDoc mentions "batching up to 100 accounts" but the implementation doesn't enforce this limit. This is likely intentional (leaving enforcement to the RPC layer), but it may be worth clarifying whether exceeding 100 accounts will cause the RPC call to fail or if getAccounts handles chunking internally.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderers/js/templates/accountsFetchHelpers.njk` around lines 36 - 54,
Update the JSDoc for fetchAllMixedAccounts to explicitly state the 100-account
RPC limit and the function's behavior when that limit is exceeded: note that the
implementation does not enforce or perform client-side chunking, that the
underlying RPC (getMultipleAccounts / getAccounts) may reject requests over 100
accounts, and recommend either chunking client-side or calling a helper that
does so (e.g., safeFetchAllMixedAccounts) if callers need to fetch more than 100
accounts in one logical operation.
| return maybeAccounts.map((maybeAccount, index) => { | ||
| return maybeAccount.exists | ||
| ? inputs[index].deserialize(maybeAccount as RpcAccount) | ||
| : null; | ||
| }) as MaybeDeserializedAccounts<T>; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and examine the file
find . -name "accountsFetchHelpers.njk" -type fRepository: metaplex-foundation/kinobi
Length of output: 123
🏁 Script executed:
# Also search for imports and usage of MaybeRpcAccount in the codebase
rg "MaybeRpcAccount|MaybeDeserializedAccounts" -A 2 -B 2Repository: metaplex-foundation/kinobi
Length of output: 2665
🌐 Web query:
Umi MaybeRpcAccount type definition discriminated union RpcAccount
💡 Result:
In Umi, MaybeRpcAccount is a discriminated union over the exists flag, where the “exists” branch is an RpcAccount:
type RpcAccount = AccountHeader & {
publicKey: PublicKey;
data: Uint8Array;
};
type MaybeRpcAccount =
| ({ exists: true } & RpcAccount)
| { exists: false; publicKey: PublicKey };[1]
🏁 Script executed:
# View the actual template file around the lines in question
sed -n '90,110p' src/renderers/js/templates/accountsFetchHelpers.njkRepository: metaplex-foundation/kinobi
Length of output: 617
🏁 Script executed:
# Check if there are any type imports or declarations in the template
head -50 src/renderers/js/templates/accountsFetchHelpers.njkRepository: metaplex-foundation/kinobi
Length of output: 1608
🏁 Script executed:
# Search for RpcAccount definition in the codebase
rg "type RpcAccount|interface RpcAccount" -A 5Repository: metaplex-foundation/kinobi
Length of output: 52
Type narrowing with maybeAccount.exists should eliminate the need for explicit RpcAccount cast.
The MaybeRpcAccount type from Umi is correctly structured as a discriminated union: { exists: true } & RpcAccount | { exists: false; publicKey: PublicKey }. TypeScript should automatically narrow maybeAccount to include all RpcAccount properties after the exists check, making the explicit as RpcAccount cast redundant. Consider removing the cast and relying on the discriminated union narrowing instead, or document why the cast is necessary if there are type inference limitations from the RPC call.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderers/js/templates/accountsFetchHelpers.njk` around lines 100 - 104,
The explicit cast "(maybeAccount as RpcAccount)" is unnecessary because
MaybeRpcAccount is a discriminated union and checking maybeAccount.exists should
narrow the type; remove the cast in the return mapping (the block using
maybeAccounts, maybeAccount.exists and inputs[index].deserialize) so you call
inputs[index].deserialize(maybeAccount) directly, and if TypeScript still
complains ensure the source type of maybeAccounts is declared/returned as
MaybeRpcAccount so narrowing works (or add a short type guard function
isRpcAccount(m: MaybeRpcAccount): m is RpcAccount that checks m.exists and use
it before calling inputs[index].deserialize).
…hing
Generate a new `fetchHelpers.ts` in the JS accounts output that provides
`fetchAllMixedAccounts` and `safeFetchAllMixedAccounts`. These helpers
take an array of { publicKey, deserialize } inputs of different account
types, batch them into a single `rpc.getAccounts()` call, and return a
fully-typed tuple where each position matches the corresponding
deserializer's output type.
https://claude.ai/code/session_01Dddxti9yQJBCaoBqKPjye1
68fc59f to
1dc3100
Compare
…ount helpers
Instead of requiring callers to pass explicit deserializer functions,
generate per-program helpers that automatically identify account types
from their discriminator bytes. For each program with discriminated
accounts, generates:
- Union type (e.g. MplTokenMetadataAccount)
- deserialize{Program}Account() with discriminator matching
- fetchAll{Program}Accounts() for batch fetching
- safeFetchAll{Program}Accounts() for nullable batch fetching
Handles both Anchor-style byte discriminators (raw byte comparison)
and Shank-style field discriminators (runtime serialization + comparison),
mirroring the pattern already used by the js-experimental renderer.
https://claude.ai/code/session_01Dddxti9yQJBCaoBqKPjye1
| * @see https://github.com/metaplex-foundation/kinobi | ||
| */ | ||
|
|
||
| import { |
There was a problem hiding this comment.
I think we still want this file because it allows you to fetch mixed types which we do (also i didn't even know this helper existed, i implemented something similar on the app side
| }); | ||
| } | ||
|
|
||
| export async function safeFetchAllMplCandyMachineCoreAccounts( |
| return staticVisitor((node) => stringify(node)) as Visitor<string>; | ||
| } | ||
| return mapVisitor(removeDocsVisitor(), (node) => stringify(node)); | ||
| return mapVisitor(removeDocsVisitor(), (node) => stringify(node)) as Visitor<string>; |
There was a problem hiding this comment.
we shouldn't ever need to cast if the types are correct. (we should make the types correct)
- Restore generic fetchHelpers.ts alongside per-program helpers per reviewer feedback that mixed-type fetching is valuable - Clarify JSDoc about 100-account RPC limit and lack of client-side chunking - Remove unnecessary `as RpcAccount` casts in templates, using discriminated union narrowing instead - Fix getUniqueHashStringVisitor types properly with nullish coalescing instead of `as Visitor<string>` casts https://claude.ai/code/session_01Dddxti9yQJBCaoBqKPjye1
Generate a new
fetchHelpers.tsin the JS accounts output that providesfetchAllMixedAccountsandsafeFetchAllMixedAccounts. These helperstake an array of { publicKey, deserialize } inputs of different account
types, batch them into a single
rpc.getAccounts()call, and return afully-typed tuple where each position matches the corresponding
deserializer's output type. No new Umi primitives are needed — the
existing
rpc.getAccounts()already supports multi-account fetching.https://claude.ai/code/session_01Dddxti9yQJBCaoBqKPjye1