Repository navigation
perf(assets-controller): add hand-rolled aggregate3 codecs - #11078
Merged
Merged
Conversation
Prithpal-Sooriya
temporarily deployed
to
default-branch
October 26, 2026 11:02 — with
GitHub Actions
Inactive
Encode and decode balance-batch aggregate3 calls without the Ethers ABI coder, and keep the Ethers codecs so tests can require the same bytes. Co-authored-by: Cursor <cursoragent@cursor.com>
Prithpal-Sooriya
force-pushed
the
feature/add-multicall-encodi-3af
branch
from
October 26, 2026 20:35
0feb74d to
5454aa9
Compare
…-encodi-3af Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # packages/assets-controller/CHANGELOG.md
The production batch cap is 300, and the random parity test only builds four calls. Co-authored-by: Cursor <cursoragent@cursor.com>
Main released 20.0.0, so the aggregate3 entry has to stay above that version. The codec tests now only compare their output with Ethers. Co-authored-by: Cursor <cursoragent@cursor.com>
salimtb
approved these changes
Oct 28, 2026
Closed
5 tasks done
This was referenced Oct 28, 2026
Merged
Merged
Prithpal-Sooriya
added a commit
to Prithpal-Sooriya/metamask-mobile
that referenced
this pull request
Nov 2, 2026
…38486) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> Bumps `@metamask/assets-controller` from `^20.0.0` to `^20.0.2`. `20.0.2` ([core#11078](MetaMask/core#11078)) keeps the 20.0.0 API. The only product change is a fix: balance batches use hand-rolled Multicall3 `aggregate3` codecs, and the Ethers codecs stay in place for a byte-identical comparison. `20.0.1` is included on the way up. It moves the controller onto `@metamask/assets-controllers` `^113.0.0`, `@metamask/transaction-controller` `^72.3.0`, and `@metamask/permission-controller` `^14.1.0`. Mobile already calls `AssetsController.syncAssetsPrices` on main, so this PR does not change call sites. Companion ranges move so Yarn resolves a single `@metamask/assets-controller@20.0.2`: - `@metamask/bridge-controller` `^83.1.1` → `^83.1.2` (depends on assets-controller `^20.0.1`) - `@metamask/transaction-controller` `^72.2.1` → `^72.3.0`, and the resolution pin `72.2.1` → `72.3.0` - `@metamask/transaction-pay-controller` `^30.3.0` → `^30.3.1` ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Refs: N/A — dependency bump with no linked issue. Follow-up to MetaMask#38308 and [core#11078](MetaMask/core#11078). ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> N/A — no screen or copy change. `20.0.2` only replaces the Multicall3 balance-batch codec inside the controller, and the call sites already use `syncAssetsPrices`. ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A — no user-visible UI change. ### **Before** N/A ### **After** N/A ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-b8869cb8-6f26-4143-9bcb-78ef352a2bbc?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-b8869cb8-6f26-4143-9bcb-78ef352a2bbc&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Pulls in transaction-controller 72.3.0, which changes how type-4 smart-account upgrades are estimated and surfaced in activity, alongside assets-controller balance-batch encoding changes. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e03cd81. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
LabinatorSolutions
pushed a commit
to LabinatorSolutions/metamask-extension
that referenced
this pull request
Nov 3, 2026
…47489) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Bumps `@metamask/assets-controller` from `^20.0.0` to `^20.0.2`. `20.0.2` ([core#11078](MetaMask/core#11078)) keeps the 20.0.0 API. Balance batches use hand-rolled Multicall3 `aggregate3` codecs, and the Ethers codecs stay in place for a byte-identical comparison. `20.0.1` is included on the way up and moves the package onto `@metamask/assets-controllers` `^113.0.0`, `@metamask/transaction-controller` `^72.3.0`, and `@metamask/permission-controller` `^14.1.0`. Companion ranges move so Yarn resolves a single `@metamask/assets-controller@20.0.2`: - `@metamask/assets-controllers` `^112.0.2` → `^113.0.0`. `NftDetectionController` now requires `removeNft`, so initialization forwards `nftController().removeNft`. - `@metamask/bridge-controller` `^83.1.1` → `^83.1.2` - `@metamask/transaction-pay-controller` `^30.3.0` → `^30.3.1` LavaMoat policies pick up the permission-controller dedupe (`immer` is now `@metamask/permission-controller>immer`). The 20.0.0 call-site migration is already on main. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: N/A — dependency bump with no linked issue. Follow-up to MetaMask#47400 and [core#11078](MetaMask/core#11078). ## **Manual testing steps** 1. Build and load this branch, then unlock a wallet that holds tokens on Ethereum Mainnet. 2. Open the home asset list and confirm symbols, balances, and fiat prices still appear. 3. Confirm NFT detection still starts when the preference is enabled. <!-- ## **Screenshots/Recordings** ### **Before** ### **After** --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-b8869cb8-6f26-4143-9bcb-78ef352a2bbc?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-b8869cb8-6f26-4143-9bcb-78ef352a2bbc&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches asset balance/NFT-related controller packages and LavaMoat sandbox permissions; behavior changes live in upstream packages rather than extension code in this diff. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit da12433. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Naz-Ovh
pushed a commit
to 0x-fork/metamask-core
that referenced
this pull request
Nov 4, 2026
## Explanation Release 1375.0.0, bumping `@metamask/assets-controller` from 20.0.1 to 20.0.2 (patch). - Fixed: hand-rolled Multicall3 `aggregate3` codecs for balance batches (MetaMask#11078) - `@metamask/bridge-controller` and `@metamask/transaction-pay-controller` only have their `@metamask/assets-controller` dependency range bumped to `^20.0.2`; they are intentionally not released. ## Checklist - [x] `yarn changelog:validate` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > This PR is release bookkeeping only; the balance-batch codec behavior ships via the already-merged assets-controller 20.0.2 release, which touches on-chain balance reads but is classified as a fix with parity checks against Ethers. > > **Overview** > **Release 1375.0.0** cuts a patch release of **`@metamask/assets-controller`** at **20.0.2** (monorepo root version **1374.0.0 → 1375.0.0**). The new changelog entry documents the shipped fix from [MetaMask#11078](MetaMask#11078): balance batches use **hand-rolled Multicall3 `aggregate3` codecs**, with Ethers codecs kept for byte-identical comparison. > > **`@metamask/bridge-controller`** and **`@metamask/transaction-pay-controller`** only update their **`@metamask/assets-controller`** dependency to **`^20.0.2`** and adjust changelogs; they are **not** version-bumped in this PR. **`yarn.lock`** is refreshed accordingly. > > There are **no application source changes** in this diff—versioning, changelog, dependency ranges, and lockfile only. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 880c066. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Naz-Ovh
pushed a commit
to 0x-fork/metamask-core
that referenced
this pull request
Nov 4, 2026
## Explanation Re-lands the release that was reverted in MetaMask#11187 (the revert happened because CI on main was broken; the CI issue has since been fixed via MetaMask#11191, and Release/1375.0.0 was subsequently shipped by another team in MetaMask#11192, so this release is now **Release/1376.0.0**). To avoid cutting two separate releases, this PR also folds in the `@metamask/profile-controller` patch release that was staged in MetaMask#11197 (that PR is now redundant and closed in favor of this one). Two packages are released: **`@metamask/assets-controller` 20.0.2 (patch)** — 20.0.2 was never published to npm, so this re-release is safe. - **Fixed:** hand-rolled Multicall3 `aggregate3` codecs for balance batches (MetaMask#11078) **`@metamask/profile-controller` 1.2.1 (patch)** - **Added:** `clearState` method on `ProfileController` (MetaMask#11177) - **Changed:** bump `@metamask/utils` from `^12.0.0` to `^12.0.1` (MetaMask#11107) - **Fixed:** `ProfileService.disconnectX` now calls `DELETE profiles/x/account` instead of the non-existent `DELETE profiles/{profileId}/x` endpoint, since the linked X account is resolved server-side from the verified bearer token (MetaMask#11195) All functional changes above are already merged on `main`; this diff contains only version bumps and changelog finalization (plus a merge of `main` to pick up MetaMask#11195). - `@metamask/bridge-controller` and `@metamask/transaction-pay-controller` only have their `@metamask/assets-controller` dependency range bumped to `^20.0.2`; they are intentionally not released. ## Checklist - [x] `yarn changelog:validate` passes
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.
Explanation
Multicall3
aggregate3balance batches currently go through the generic Ethers ABI coder. This adds fixed-layout encode and decode functions for that calldata shape and uses them on the balance hot path, following the approach in #11010. Initial research found this to be ~44x faster.The previous Ethers functions remain exported as
encodeAggregate3WithEthers,decodeAggregate3ResponseWithEthers, anddecodeUint256WithEthers. Table-driven cases and 100 deterministic pseudo-random batches check that the new codecs match those Ethers results byte for byte, including empty, partial-word, and multiword call data.https://www.loom.com/share/c696e7903eba4e46ae75e2b8984b0230
References
Checklist
Test plan
yarn workspace @metamask/assets-controller exec vitest run --coverage.enabled=false src/data-sources/evm-rpc-services/clients/MulticallClient.test.ts(56 tests)yarn oxlinton the changed MulticallClient filesyarn workspace @metamask/assets-controller run changelog:validateNote
Medium Risk
Changes how on-chain balance batch calldata is built and parsed on every RPC refresh; parity tests reduce but do not eliminate risk from a hand-maintained ABI layout diverging from Ethers.
Overview
Multicall balance batches no longer encode/decode Multicall3
aggregate3through the generic Ethers ABI coder on the hot path. A newaggregate3Codecmodule writes and reads the fixedaggregate3layout directly (encodeAggregate3,decodeAggregate3Response,decodeUint256), andMulticallClientnow calls those helpers when building and parsing balance multicalls.The previous Ethers implementations remain as
encodeAggregate3WithEthers,decodeAggregate3ResponseWithEthers, anddecodeUint256WithEthersfor reference and tests.aggregate3Codec.test.tsasserts byte-for-byte parity with Ethers (including 100 random batches and 300-call encode/decode cases). Package exports forencodeAggregate3/decodeAggregate3Responsenow come from the codec module. An architecture decision doc and changelog entry document motivation (mobile encode/decode cost) and maintenance tradeoffs.Reviewed by Cursor Bugbot for commit 08db40a. Bugbot is set up for automated code reviews on this repo. Configure here.