Batch Wallet.getBalance asset fetches via Multicall3#506
Draft
its-everdred wants to merge 2 commits into
Draft
Conversation
✅ Deploy Preview for actions-ui ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #452
Summary
Reorganizes
Wallet.getBalancefrom a per-(chain, asset) RPC fan-out to a per-chain Multicall3 batch.Before: each asset helper (
fetchETHBalance/fetchERC20Balance) iterated across chains, costingchains × (1 + erc20s)RPCs. After: onepublicClient.multicallper chain reads the wallet's native balance (via Multicall3getEthBalance) and every ERC-20balanceOfin a singleeth_call. Total RPCs drop tochains(e.g. 5 chains × 6 assets: 30 → 5). Cross-chain parallelism is preserved viaPromise.all.The public
TokenBalance[]shape is unchanged — per-chain results are transposed back into per-asset records, and the array preserves asset order.Changes
src/constants/multicall.ts: canonicalMULTICALL3_ADDRESS+ a minimalgetEthBalanceABI fragment (viem's exportedmulticall3Abionly carriesaggregate3).src/services/tokenBalance.ts: replaced the two per-asset helpers with a singlefetchBalances(chainManager, walletAddress, assets, options)that batches per chain and transposes. The per-chain Multicall3 address is resolved from the chain's own viem config with the canonical address as fallback (the "override hook" from the issue), keeping thegetEthBalancetarget consistent with how viem routes the aggregate call.Wallet.getBalancenow makes onefetchBalances([ETH, ...supportedAssets], …)call.MockChainManagergained amulticallmock; both affected spec files were rewritten to mockmulticall/fetchBalances.Failure handling decision
The issue left open whether a failed inner
balanceOfshould surface or zero. This PR usesallowFailure: trueand omits the (chain, asset) entry on a per-call failure (e.g. a revert from a non-token address), mirroring how an asset with no configured address is already skipped. A transport-level failure of the whole multicall still rejects, preserving the previous loud-failure behavior for real RPC outages.Validation
pnpm test(sdk): 666 passed / 10 skipped.pnpm typecheck,pnpm build,pnpm lint: clean (0 errors).actions wallet balancerun against live base-sepolia / op-sepolia RPCs with a throwaway zero-balance key — ETH/USDC_DEMO/OP_DEMO balances fetched through the new Multicall3 path.Self-review
Self-review across correctness, performance, testing, and maintainability before marking ready. Fixes applied:
ETH.address(e.g.celo,superseed) silently vanished — a regression vs. the old unconditional native fan-out.balanceContractnow resolves native assets togetEthBalanceregardless of the address map. Added aceloregression test.result.result as bigintcast (viem already infersbigint; typecheck stays clean without it).Performance reviewer confirmed the efficiency goal is met (one multicall per chain, cross-chain parallelism preserved, zero-asset chains skip the RPC entirely).