fix: add assets-controller temp state migration healing - #9393
Merged
Conversation
Prithpal-Sooriya
marked this pull request as ready for review
July 6, 2026 14:08
Prithpal-Sooriya
temporarily deployed
to
default-branch
July 6, 2026 14:08 — with
GitHub Actions
Inactive
- Updated the `tempHealAssetsInfoMetadata` function to return the updated controller state directly instead of a callback for the `update` method. - Introduced a new `selectedCurrency` field in `AssetsControllerStateInternal` to track the currently active ISO 4217 currency code. - Improved test coverage for `tempHealAssetsInfoMetadata`, including scenarios for healing state and error handling. This change aims to streamline the healing process for asset metadata and ensure better state management during migrations.
…ate healing - Added a test to verify that the `tempHealAssetsInfoMetadata` function does not mutate existing `customAssets` arrays in the input state. - Refactored the `tempHealAssetsInfoMetadata` function to use `cloneDeep` for state management, ensuring that the original state remains unchanged during the healing process. This change enhances the reliability of state migrations by preventing unintended side effects on the input state.
salimtb
reviewed
Jul 7, 2026
|
|
||
| for (const [hexChainId, accountTokens] of Object.entries(allTokens)) { | ||
| if (!isObject(accountTokens)) { | ||
| continue; |
Contributor
There was a problem hiding this comment.
nice we silently skip if no tokens
salimtb
reviewed
Jul 7, 2026
| * TEMPORARY — will be removed in a future release. | ||
| * Issue: https://consensyssoftware.atlassian.net/browse/ASSETS-3346 | ||
| */ | ||
| tempMigrateAssetsInfoMetadataAssets3346?: () => Assets3346MigrationState; |
Contributor
There was a problem hiding this comment.
we can have this patten for future migration , agreed on keep it temp for now but we can consider later
salimtb
approved these changes
Jul 7, 2026
This was referenced Jul 8, 2026
pull Bot
pushed a commit
to Eric-Johnson-1/metamask-extension
that referenced
this pull request
Jul 8, 2026
) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** Bumps `@metamask/assets-controller` from the patched `10.0.1` to `^10.1.0` ([changelog](https://github.com/MetaMask/core/blob/main/packages/assets-controller/CHANGELOG.md#1010)). Notable changes in `10.1.0`: - **Fixed**: Fetch balances when switching account groups, enabling RPC-only networks, or after a new account is added to the account tree ([core#9388](MetaMask/core#9388)). This release contains the exact fix that was previously applied via the local yarn patch (`@metamask-assets-controller-npm-10.0.1-d3c86d8983.patch`), so this PR stops using the patch in `package.json` resolutions/dependencies. The patch file itself is intentionally left in `.yarn/patches/` (removing it would require an additional codeowner review); it is no longer referenced by the install. - **Added**: temporary `tempMigrateAssetsInfoMetadataAssets3346` constructor option ([core#9393](MetaMask/core#9393)). Wiring this option up is intentionally **not** done here and will be a fast-follow in a separate PR. - **Changed**: `@metamask/messenger` bumped from `^1.2.0` to `^2.0.0` ([core#9392](MetaMask/core#9392)). No action needed here anymore: `main` has since migrated the app to messenger v2, so after merging `main` into this branch everything dedupes onto a single `@metamask/messenger@2.0.0` copy. Also updates the `@metamask/assets-controller@npm:^9.1.0` resolution (needed for `@metamask/transaction-pay-controller`'s transitive request) to point at `^10.1.0` instead of the patch. The previous `@npm:^10.0.1` resolution entry is removed because `^10.0.1` now naturally resolves to `10.1.0`. `yarn lavamoat:auto`, `yarn allow-scripts auto`, and `yarn lint:lockfile:dedupe:fix` were run — no policy or allow-scripts changes were produced. `yarn attributions:generate` produced only unrelated stale-file churn, so attribution updates are left to `@metamaskbot update-attributions` if required. ## **Changelog** CHANGELOG entry: null ## **Related issues** Related to: MetaMask/core#9388, MetaMask/core#9392 ## **Manual testing steps** 1. Run the extension and unlock the wallet. 2. Go to the homepage asset list. 3. Add a new account to an account group and switch account groups. 4. Verify balances populate for the new account, including on custom/RPC-only networks. 5. Enable an RPC-only network (e.g. a custom chain) and verify token balances are fetched. <!-- ## **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 - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] 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-e5245e2b-5547-4357-8a2c-94466489dd60"><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-e5245e2b-5547-4357-8a2c-94466489dd60"><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>
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Jul 8, 2026
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** Bumps `@metamask/assets-controller` from the patched `10.0.1` to `^10.1.0` ([changelog](https://github.com/MetaMask/core/blob/main/packages/assets-controller/CHANGELOG.md#1010)). Notable changes in `10.1.0`: - **Fixed**: Fetch balances when switching account groups, enabling RPC-only networks, or after a new account is added to the account tree ([core#9388](MetaMask/core#9388)). This release contains the exact fix that was previously applied via the local yarn patch (`@metamask-assets-controller-npm-10.0.1-d3c86d8983.patch`), so this PR stops using the patch in `package.json` resolutions/dependencies. The patch file itself is intentionally left in `.yarn/patches/` (removing it would require an additional codeowner review); it is no longer referenced by the install. - **Added**: temporary `tempMigrateAssetsInfoMetadataAssets3346` constructor option ([core#9393](MetaMask/core#9393)). Wiring this option up is intentionally **not** done here and will be a fast-follow in a separate PR. - **Changed**: `@metamask/messenger` bumped from `^1.2.0` to `^2.0.0` ([core#9392](MetaMask/core#9392)). No change needed here: the existing blanket `"@metamask/messenger": "^1.2.0"` resolution keeps a single deduped messenger copy (`1.2.0`) for the whole app. `@metamask/assets-controller@10.1.0` does not use any messenger v2-only APIs (`buildChild`, `getRegisteredActionTypes`), so this is safe at runtime. Validated with `yarn setup:expo`, `yarn lint:tsc` (zero errors), and the assets-controller unit tests (`assets-controller-init.test.ts`, `assets-controller.test.ts` — 30 passed). ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: MetaMask/core#9388 ## **Manual testing steps** ```gherkin Feature: unified assets balances (AssetsController) Scenario: user adds a new account to the account tree Given the wallet is unlocked with an existing account group When user creates or imports a new account Then balances for the new account are fetched and displayed, including on custom/RPC-only networks Scenario: user enables an RPC-only network Given the wallet is unlocked When user enables a custom RPC network Then token balances on that network are fetched without requiring an app restart ``` ## **Screenshots/Recordings** N/A — dependency bump only, no UI changes. ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [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 - [ ] 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) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] 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 - [ ] 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-e5245e2b-5547-4357-8a2c-94466489dd60"><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-e5245e2b-5547-4357-8a2c-94466489dd60"><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>
pull Bot
pushed a commit
to Reality2byte/metamask-mobile
that referenced
this pull request
Jul 20, 2026
…ller init (MetaMask#33015) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** Fast-follow to MetaMask#32950, which bumped `@metamask/assets-controller` to `^10.1.0` and intentionally left the new temporary `tempMigrateAssetsInfoMetadataAssets3346` constructor option unwired. This PR wires the option up in `assetsControllerInit` (`app/core/Engine/controllers/assets-controller/assets-controller-init.ts`) by passing a getter that returns the legacy persisted `TokensController` (`allTokens` / `allIgnoredTokens`) and `AccountsController` (`internalAccounts.accounts`) state slices from the engine's persisted state. With this wired, the `AssetsController` constructor runs the temporary ASSETS-3346 healing (`tempHealAssetsInfoMetadata`, added in [core#9393](MetaMask/core#9393)): it restores `assetsInfo` metadata and `customAssets` tracking for custom tokens on niche EVM chains that cannot self-heal via Accounts-API auto-detection, after a prior defect wiped that metadata. The healing in core treats the supplied state as untrusted (`unknown`), re-validates every shape, never overwrites existing metadata, respects ignored/hidden tokens, and skips Accounts-API-supported chains — so this wiring is safe even with partial or malformed persisted state. Both the controller option and this wiring are temporary and should be removed together in a future release. ## **Changelog** CHANGELOG entry: Fixed missing custom token metadata on networks without token auto-detection by restoring it from previously saved token data ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-3346 Refs: MetaMask/core#9393, MetaMask#32950 ## **Manual testing steps** ```gherkin Feature: heal wiped custom token metadata (ASSETS-3346) Scenario: user with wiped assetsInfo metadata on a niche EVM chain Given a wallet whose persisted TokensController.allTokens contains custom tokens on a chain without Accounts API support (e.g. Gnosis-like niche chains) And the AssetsController assetsInfo metadata for those tokens was wiped by the prior defect When user launches the app and unlocks the wallet (AssetsController is constructed) Then the custom tokens reappear in the token list with symbol, decimals and name restored And tokens the user previously hid/ignored are not restored Scenario: user with healthy state Given a wallet whose assetsInfo metadata is intact When user launches the app Then existing metadata is not overwritten and no duplicate customAssets entries are created ``` ## **Screenshots/Recordings** N/A — state-healing wiring only, no UI changes. ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [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 - [ ] 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) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] 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 - [ ] 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-7f42a7bd-9eec-48e3-b8f9-78d342b926c2"><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-7f42a7bd-9eec-48e3-b8f9-78d342b926c2"><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>
pull Bot
pushed a commit
to Reality2byte/metamask-extension
that referenced
this pull request
Jul 21, 2026
…tsController init (MetaMask#44303) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## **Description** After a prior defect in `AssetsController`, metadata (`assetsInfo`) for custom tokens was wiped. Popular chains covered by the Accounts API self-heal via auto-detection, but niche EVM chains (e.g. Flare) cannot. Extension migration #215 healed this once, and `@metamask/assets-controller@10.1.0` (already bumped in MetaMask#44246) added a temporary constructor option, `tempMigrateAssetsInfoMetadataAssets3346`, that performs the same healing at controller construction using legacy `TokensController` state provided by the host (see [MetaMask/core#9393](MetaMask/core#9393)). This PR wires that option into `AssetsControllerInit`: the getter returns an object with the `TokensController` and `AccountsController` persisted state slices, from which the controller reads `allTokens` / `allIgnoredTokens` and `internalAccounts.accounts` to restore wiped `assetsInfo` metadata and custom-asset tracking. The controller treats the input as untrusted and re-validates every shape. The option and its wiring are temporary and should be removed together once the healing is no longer needed. ## **Changelog** CHANGELOG entry: Fixed custom tokens on niche EVM networks losing their name, symbol, and icon by restoring the wiped metadata on startup. ## **Related issues** Fixes: [ASSETS-3346](https://consensyssoftware.atlassian.net/browse/ASSETS-3346) Related: [MetaMask/core#9393](MetaMask/core#9393) (controller-side implementation), [MetaMask#44246](MetaMask#44246) (`@metamask/assets-controller` bump to `^10.1.0`), incident `#incident-metamask-1731` ## **Manual testing steps** 1. Run the extension with a wallet that has custom (manually imported) tokens on a niche EVM network not covered by the Accounts API (e.g. Flare, chain ID 14). 2. Reproduce the wiped-metadata state: the affected tokens appear in the asset list without a symbol/name/icon. 3. Restart the extension (or reload the service worker) so the `AssetsController` is re-initialized. 4. Verify the custom tokens on the niche network show their symbol, name, and icon again in the asset list. 5. Verify tokens the user previously hid/removed stay hidden, and tokens on popular networks (Mainnet, Linea, etc.) are unaffected. <!-- ## **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 - [ ] 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-deae9301-8a94-4cba-b8c5-d34b04a55681"><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-deae9301-8a94-4cba-b8c5-d34b04a55681"><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> [ASSETS-3346]: https://consensyssoftware.atlassian.net/browse/ASSETS-3346?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
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
Background: After a prior defect in AssetsController, metadata (
AssetsController.assetsInfo) for custom tokens were wiped. Most popular chains support auto-detection and can self-heal, however not all chains support auto-detection.Fix: This migration restores metadata for custom tokens on niche EVM chains that are unable to auto-detect/self-heal.
References
Checklist
Note
Medium Risk
Mutates persisted wallet asset state at construction from untrusted legacy data; behavior is heavily guarded and idempotent, but incorrect host wiring could still affect token display and custom-asset lists.
Overview
Adds a temporary constructor hook
tempMigrateAssetsInfoMetadataAssets3346so hosts can supply legacyTokensController/AccountsControllerpersisted state. On startup,AssetsControllerrunstempHealAssetsInfoMetadatato restore wipedassetsInfoERC-20 metadata andcustomAssetstracking for niche EVM chains that do not self-heal via the Accounts API.Healing reads
allTokens, respectsallIgnoredTokensand current hiddenassetPreferences, skips Accounts-API-supported chains and ERC-721s, never overwrites existing metadata, and treats legacy input as untrusted with defensive validation. Failures are reported through optionalcaptureExceptionwithout blocking controller construction.Also documents the fix in the package changelog and aligns
AssetsControllerStateInternalintypes.tswithselectedCurrency(typed asSupportedCurrency).Reviewed by Cursor Bugbot for commit 5d54234. Bugbot is set up for automated code reviews on this repo. Configure here.