@metamask-previews/assets-controllers 105.1.0-preview-938a7fe → 106.0.0-preview-d3f1b8bd9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/TokenDetectionController-method-action-types.cjs.map +1 -1
  3. package/dist/TokenDetectionController-method-action-types.d.cts +1 -1
  4. package/dist/TokenDetectionController-method-action-types.d.mts +1 -1
  5. package/dist/TokenDetectionController-method-action-types.mjs.map +1 -1
  6. package/dist/TokenDetectionController.cjs +70 -63
  7. package/dist/TokenDetectionController.cjs.map +1 -1
  8. package/dist/TokenDetectionController.d.cts +8 -12
  9. package/dist/TokenDetectionController.d.cts.map +1 -1
  10. package/dist/TokenDetectionController.d.mts +8 -12
  11. package/dist/TokenDetectionController.d.mts.map +1 -1
  12. package/dist/TokenDetectionController.mjs +69 -62
  13. package/dist/TokenDetectionController.mjs.map +1 -1
  14. package/dist/TokenListService.cjs +107 -0
  15. package/dist/TokenListService.cjs.map +1 -0
  16. package/dist/TokenListService.d.cts +40 -0
  17. package/dist/TokenListService.d.cts.map +1 -0
  18. package/dist/TokenListService.d.mts +40 -0
  19. package/dist/TokenListService.d.mts.map +1 -0
  20. package/dist/TokenListService.mjs +102 -0
  21. package/dist/TokenListService.mjs.map +1 -0
  22. package/dist/TokensController.cjs +46 -32
  23. package/dist/TokensController.cjs.map +1 -1
  24. package/dist/TokensController.d.cts +5 -3
  25. package/dist/TokensController.d.cts.map +1 -1
  26. package/dist/TokensController.d.mts +5 -3
  27. package/dist/TokensController.d.mts.map +1 -1
  28. package/dist/TokensController.mjs +46 -32
  29. package/dist/TokensController.mjs.map +1 -1
  30. package/dist/index.cjs +4 -1
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +1 -0
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.mts +1 -0
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs +1 -0
  37. package/dist/index.mjs.map +1 -1
  38. package/package.json +7 -6
package/CHANGELOG.md CHANGED
@@ -7,8 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+
12
+ - Bump `@metamask/account-tree-controller` from `^7.2.0` to `^7.3.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
13
+ - Bump `@metamask/keyring-controller` from `^25.4.0` to `^25.5.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
14
+ - Bump `@metamask/multichain-account-service` from `^8.0.1` to `^9.0.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
15
+ - Bump `@metamask/permission-controller` from `^13.0.0` to `^13.1.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
16
+ - Bump `@metamask/transaction-controller` from `^65.1.0` to `^65.2.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
17
+
18
+ ## [106.0.0]
19
+
10
20
  ### Added
11
21
 
22
+ - Add `TokenListService`, a shared service for fetching and caching the token list per chain ([#8700](https://github.com/MetaMask/core/pull/8700))
23
+ - Wraps `@tanstack/query-core` to cache results in-memory for 4 hours per chain ID, matching `TokenListController`'s existing threshold.
24
+ - Multiple controllers sharing the same `TokenListService` instance share the same cache: only one HTTP request is made per chain per 4-hour window regardless of how many callers invoke `fetchTokensByChainId`.
25
+ - Exported from the package as `TokenListService` and `buildTokenListMap`.
26
+ - Add `@tanstack/query-core` `^5.62.16` as a direct dependency ([#8700](https://github.com/MetaMask/core/pull/8700))
12
27
  - Expose missing public `AccountTrackerController` methods through its messenger ([#8693](https://github.com/MetaMask/core/pull/8693))
13
28
  - The following actions are now available:
14
29
  - `AccountTrackerController:refresh`
@@ -22,6 +37,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
37
 
23
38
  ### Changed
24
39
 
40
+ - **BREAKING:** `TokenDetectionController` now requires a `tokenListService: TokenListService` constructor option ([#8700](https://github.com/MetaMask/core/pull/8700))
41
+ - Token list data is fetched directly from `TokenListService` instead of reading `TokenListController` state on each detection pass.
42
+ - `GetTokenListState` has been removed from `AllowedActions` and `TokenListStateChange` has been removed from `AllowedEvents` on `TokenDetectionControllerMessenger`.
43
+ - **BREAKING:** `TokensController` now requires a `tokenListService: TokenListService` constructor option ([#8700](https://github.com/MetaMask/core/pull/8700))
44
+ - `TokenListStateChange` has been removed from `AllowedEvents` on `TokensControllerMessenger`.
45
+ - Token `name` and `rwaData` enrichment now happens once at controller initialization instead of reactively on every `TokenListController:stateChange` event.
25
46
  - Bump `@metamask/transaction-controller` from `^65.0.0` to `^65.1.0` ([#8691](https://github.com/MetaMask/core/pull/8691))
26
47
  - Switch the default mUSD asset from upfront `allTokens` state seeding to detection-based discovery on Ethereum mainnet (`0x1`), Linea (`0xe708`), and Monad mainnet (`0x8f`) ([#8688](https://github.com/MetaMask/core/pull/8688))
27
48
  - `TokenDetectionController` now merges mUSD into the in-memory token list cache for these chains so it is treated as a regular detection candidate, replacing the previous `start()`-time `TokensController:addTokens` call and the per-event re-seed runs.
@@ -34,6 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
34
55
 
35
56
  ### Removed
36
57
 
58
+ - `TokenDetectionController` no longer restarts token detection when `TokenListController` publishes a `stateChange` event ([#8700](https://github.com/MetaMask/core/pull/8700))
59
+ - Detection is still triggered on wallet unlock, account change, network change, and preference changes; the extra restart that occurred whenever `TokenListController` refreshed its cache is gone.
37
60
  - Stop seeding mUSD directly into `TokensController` state and remove the related event subscriptions ([#8688](https://github.com/MetaMask/core/pull/8688))
38
61
  - `TokensController` no longer subscribes to `KeyringController:unlock`, `AccountsController:accountAdded`, `AccountsController:selectedEvmAccountChange`, `NetworkController:networkAdded`, or `NetworkController:stateChange` for mUSD seeding purposes.
39
62
  - `TokensControllerMessenger` no longer requires `NetworkControllerNetworkAddedEvent`, `AccountsControllerAccountAddedEvent`, or `KeyringControllerUnlockEvent` as allowed events.
@@ -3028,7 +3051,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3028
3051
 
3029
3052
  - Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
3030
3053
 
3031
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@105.1.0...HEAD
3054
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@106.0.0...HEAD
3055
+ [106.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@105.1.0...@metamask/assets-controllers@106.0.0
3032
3056
  [105.1.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@105.0.0...@metamask/assets-controllers@105.1.0
3033
3057
  [105.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@104.3.0...@metamask/assets-controllers@105.0.0
3034
3058
  [104.3.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@104.2.0...@metamask/assets-controllers@104.3.0
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetectionController-method-action-types.cjs","sourceRoot":"","sources":["../src/TokenDetectionController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { TokenDetectionController } from './TokenDetectionController';\n\n/**\n * Allows controller to make active and passive polling requests\n */\nexport type TokenDetectionControllerEnableAction = {\n type: `TokenDetectionController:enable`;\n handler: TokenDetectionController['enable'];\n};\n\n/**\n * Blocks controller from making network calls\n */\nexport type TokenDetectionControllerDisableAction = {\n type: `TokenDetectionController:disable`;\n handler: TokenDetectionController['disable'];\n};\n\n/**\n * Start polling for detected tokens.\n */\nexport type TokenDetectionControllerStartAction = {\n type: `TokenDetectionController:start`;\n handler: TokenDetectionController['start'];\n};\n\n/**\n * Stop polling for detected tokens.\n */\nexport type TokenDetectionControllerStopAction = {\n type: `TokenDetectionController:stop`;\n handler: TokenDetectionController['stop'];\n};\n\n/**\n * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.\n * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.\n *\n * @param options - Options for token detection.\n * @param options.chainIds - The chain IDs of the network client to use.\n * @param options.selectedAddress - the selectedAddress against which to detect for token balances.\n * @param options.forceRpc - Force RPC-based token detection for all specified chains,\n * bypassing external services check and ensuring RPC is used even for chains\n * that might otherwise be handled by the Accounts API.\n */\nexport type TokenDetectionControllerDetectTokensAction = {\n type: `TokenDetectionController:detectTokens`;\n handler: TokenDetectionController['detectTokens'];\n};\n\n/**\n * Add tokens detected from websocket balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the websocket\n * - Ignored tokens have been filtered out by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from websocket (already filtered to exclude ignored tokens)\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaWsAction = {\n type: `TokenDetectionController:addDetectedTokensViaWs`;\n handler: TokenDetectionController['addDetectedTokensViaWs'];\n};\n\n/**\n * Add tokens detected from polling balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Filters out tokens already in allTokens or allIgnoredTokens\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from polling\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaPollingAction = {\n type: `TokenDetectionController:addDetectedTokensViaPolling`;\n handler: TokenDetectionController['addDetectedTokensViaPolling'];\n};\n\n/**\n * Union of all TokenDetectionController action types.\n */\nexport type TokenDetectionControllerMethodActions =\n | TokenDetectionControllerEnableAction\n | TokenDetectionControllerDisableAction\n | TokenDetectionControllerStartAction\n | TokenDetectionControllerStopAction\n | TokenDetectionControllerDetectTokensAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | TokenDetectionControllerAddDetectedTokensViaPollingAction;\n"]}
1
+ {"version":3,"file":"TokenDetectionController-method-action-types.cjs","sourceRoot":"","sources":["../src/TokenDetectionController-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { TokenDetectionController } from './TokenDetectionController';\n\n/**\n * Allows controller to make active and passive polling requests\n */\nexport type TokenDetectionControllerEnableAction = {\n type: `TokenDetectionController:enable`;\n handler: TokenDetectionController['enable'];\n};\n\n/**\n * Blocks controller from making network calls\n */\nexport type TokenDetectionControllerDisableAction = {\n type: `TokenDetectionController:disable`;\n handler: TokenDetectionController['disable'];\n};\n\n/**\n * Start polling for detected tokens.\n */\nexport type TokenDetectionControllerStartAction = {\n type: `TokenDetectionController:start`;\n handler: TokenDetectionController['start'];\n};\n\n/**\n * Stop polling for detected tokens.\n */\nexport type TokenDetectionControllerStopAction = {\n type: `TokenDetectionController:stop`;\n handler: TokenDetectionController['stop'];\n};\n\n/**\n * For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.\n * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.\n *\n * @param options - Options for token detection.\n * @param options.chainIds - The chain IDs of the network client to use.\n * @param options.selectedAddress - the selectedAddress against which to detect for token balances.\n * @param options.forceRpc - Force RPC-based token detection for all specified chains,\n * bypassing external services check and ensuring RPC is used even for chains\n * that might otherwise be handled by the Accounts API.\n */\nexport type TokenDetectionControllerDetectTokensAction = {\n type: `TokenDetectionController:detectTokens`;\n handler: TokenDetectionController['detectTokens'];\n};\n\n/**\n * Add tokens detected from websocket balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the websocket\n * - Ignored tokens have been filtered out by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from websocket (already filtered to exclude ignored tokens)\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaWsAction = {\n type: `TokenDetectionController:addDetectedTokensViaWs`;\n handler: TokenDetectionController['addDetectedTokensViaWs'];\n};\n\n/**\n * Add tokens detected from polling balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Filters out tokens already in allTokens or allIgnoredTokens\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from polling\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaPollingAction = {\n type: `TokenDetectionController:addDetectedTokensViaPolling`;\n handler: TokenDetectionController['addDetectedTokensViaPolling'];\n};\n\n/**\n * Union of all TokenDetectionController action types.\n */\nexport type TokenDetectionControllerMethodActions =\n | TokenDetectionControllerEnableAction\n | TokenDetectionControllerDisableAction\n | TokenDetectionControllerStartAction\n | TokenDetectionControllerStopAction\n | TokenDetectionControllerDetectTokensAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | TokenDetectionControllerAddDetectedTokensViaPollingAction;\n"]}
@@ -32,7 +32,7 @@ export type TokenDetectionControllerStopAction = {
32
32
  handler: TokenDetectionController['stop'];
33
33
  };
34
34
  /**
35
- * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.
35
+ * For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.
36
36
  * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.
37
37
  *
38
38
  * @param options - Options for token detection.
@@ -32,7 +32,7 @@ export type TokenDetectionControllerStopAction = {
32
32
  handler: TokenDetectionController['stop'];
33
33
  };
34
34
  /**
35
- * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.
35
+ * For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.
36
36
  * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.
37
37
  *
38
38
  * @param options - Options for token detection.
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetectionController-method-action-types.mjs","sourceRoot":"","sources":["../src/TokenDetectionController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { TokenDetectionController } from './TokenDetectionController';\n\n/**\n * Allows controller to make active and passive polling requests\n */\nexport type TokenDetectionControllerEnableAction = {\n type: `TokenDetectionController:enable`;\n handler: TokenDetectionController['enable'];\n};\n\n/**\n * Blocks controller from making network calls\n */\nexport type TokenDetectionControllerDisableAction = {\n type: `TokenDetectionController:disable`;\n handler: TokenDetectionController['disable'];\n};\n\n/**\n * Start polling for detected tokens.\n */\nexport type TokenDetectionControllerStartAction = {\n type: `TokenDetectionController:start`;\n handler: TokenDetectionController['start'];\n};\n\n/**\n * Stop polling for detected tokens.\n */\nexport type TokenDetectionControllerStopAction = {\n type: `TokenDetectionController:stop`;\n handler: TokenDetectionController['stop'];\n};\n\n/**\n * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.\n * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.\n *\n * @param options - Options for token detection.\n * @param options.chainIds - The chain IDs of the network client to use.\n * @param options.selectedAddress - the selectedAddress against which to detect for token balances.\n * @param options.forceRpc - Force RPC-based token detection for all specified chains,\n * bypassing external services check and ensuring RPC is used even for chains\n * that might otherwise be handled by the Accounts API.\n */\nexport type TokenDetectionControllerDetectTokensAction = {\n type: `TokenDetectionController:detectTokens`;\n handler: TokenDetectionController['detectTokens'];\n};\n\n/**\n * Add tokens detected from websocket balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the websocket\n * - Ignored tokens have been filtered out by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from websocket (already filtered to exclude ignored tokens)\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaWsAction = {\n type: `TokenDetectionController:addDetectedTokensViaWs`;\n handler: TokenDetectionController['addDetectedTokensViaWs'];\n};\n\n/**\n * Add tokens detected from polling balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Filters out tokens already in allTokens or allIgnoredTokens\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from polling\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaPollingAction = {\n type: `TokenDetectionController:addDetectedTokensViaPolling`;\n handler: TokenDetectionController['addDetectedTokensViaPolling'];\n};\n\n/**\n * Union of all TokenDetectionController action types.\n */\nexport type TokenDetectionControllerMethodActions =\n | TokenDetectionControllerEnableAction\n | TokenDetectionControllerDisableAction\n | TokenDetectionControllerStartAction\n | TokenDetectionControllerStopAction\n | TokenDetectionControllerDetectTokensAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | TokenDetectionControllerAddDetectedTokensViaPollingAction;\n"]}
1
+ {"version":3,"file":"TokenDetectionController-method-action-types.mjs","sourceRoot":"","sources":["../src/TokenDetectionController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { TokenDetectionController } from './TokenDetectionController';\n\n/**\n * Allows controller to make active and passive polling requests\n */\nexport type TokenDetectionControllerEnableAction = {\n type: `TokenDetectionController:enable`;\n handler: TokenDetectionController['enable'];\n};\n\n/**\n * Blocks controller from making network calls\n */\nexport type TokenDetectionControllerDisableAction = {\n type: `TokenDetectionController:disable`;\n handler: TokenDetectionController['disable'];\n};\n\n/**\n * Start polling for detected tokens.\n */\nexport type TokenDetectionControllerStartAction = {\n type: `TokenDetectionController:start`;\n handler: TokenDetectionController['start'];\n};\n\n/**\n * Stop polling for detected tokens.\n */\nexport type TokenDetectionControllerStopAction = {\n type: `TokenDetectionController:stop`;\n handler: TokenDetectionController['stop'];\n};\n\n/**\n * For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.\n * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.\n *\n * @param options - Options for token detection.\n * @param options.chainIds - The chain IDs of the network client to use.\n * @param options.selectedAddress - the selectedAddress against which to detect for token balances.\n * @param options.forceRpc - Force RPC-based token detection for all specified chains,\n * bypassing external services check and ensuring RPC is used even for chains\n * that might otherwise be handled by the Accounts API.\n */\nexport type TokenDetectionControllerDetectTokensAction = {\n type: `TokenDetectionController:detectTokens`;\n handler: TokenDetectionController['detectTokens'];\n};\n\n/**\n * Add tokens detected from websocket balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the websocket\n * - Ignored tokens have been filtered out by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from websocket (already filtered to exclude ignored tokens)\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaWsAction = {\n type: `TokenDetectionController:addDetectedTokensViaWs`;\n handler: TokenDetectionController['addDetectedTokensViaWs'];\n};\n\n/**\n * Add tokens detected from polling balance updates\n * This method:\n * - Checks if useTokenDetection preference is enabled (skips if disabled)\n * - Checks if external services are enabled (skips if disabled)\n * - Filters out tokens already in allTokens or allIgnoredTokens\n * - Tokens are expected to be in the tokensChainsCache with full metadata\n * - Balance fetching is skipped since balances are provided by the caller\n *\n * @param options - The options object\n * @param options.tokensSlice - Array of token addresses detected from polling\n * @param options.chainId - Hex chain ID\n * @returns Promise that resolves when tokens are added\n */\nexport type TokenDetectionControllerAddDetectedTokensViaPollingAction = {\n type: `TokenDetectionController:addDetectedTokensViaPolling`;\n handler: TokenDetectionController['addDetectedTokensViaPolling'];\n};\n\n/**\n * Union of all TokenDetectionController action types.\n */\nexport type TokenDetectionControllerMethodActions =\n | TokenDetectionControllerEnableAction\n | TokenDetectionControllerDisableAction\n | TokenDetectionControllerStartAction\n | TokenDetectionControllerStopAction\n | TokenDetectionControllerDetectTokensAction\n | TokenDetectionControllerAddDetectedTokensViaWsAction\n | TokenDetectionControllerAddDetectedTokensViaPollingAction;\n"]}
@@ -13,13 +13,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- var _TokenDetectionController_instances, _TokenDetectionController_intervalId, _TokenDetectionController_selectedAccountId, _TokenDetectionController_tokensChainsCache, _TokenDetectionController_disabled, _TokenDetectionController_isUnlocked, _TokenDetectionController_isDetectionEnabledFromPreferences, _TokenDetectionController_useTokenDetection, _TokenDetectionController_useExternalServices, _TokenDetectionController_getBalancesInSingleCall, _TokenDetectionController_trackMetaMetricsEvent, _TokenDetectionController_registerEventListeners, _TokenDetectionController_stopPolling, _TokenDetectionController_startPolling, _TokenDetectionController_compareTokensChainsCache, _TokenDetectionController_getCorrectNetworkClientIdByChainId, _TokenDetectionController_restartTokenDetection, _TokenDetectionController_shouldDetectTokens, _TokenDetectionController_detectTokensUsingRpc, _TokenDetectionController_getSlicesOfTokensToDetect, _TokenDetectionController_getConvertedStaticMainnetTokenList, _TokenDetectionController_buildMusdTokenListToken, _TokenDetectionController_mergeMusdIntoTokenListMap, _TokenDetectionController_applyMusdDefaultToTokensChainsCache, _TokenDetectionController_includeMusdInTokenDetectionSlice, _TokenDetectionController_addDetectedTokens, _TokenDetectionController_getSelectedAccount, _TokenDetectionController_getSelectedAddress;
16
+ var _TokenDetectionController_instances, _TokenDetectionController_intervalId, _TokenDetectionController_selectedAccountId, _TokenDetectionController_tokenListService, _TokenDetectionController_disabled, _TokenDetectionController_isUnlocked, _TokenDetectionController_isDetectionEnabledFromPreferences, _TokenDetectionController_useTokenDetection, _TokenDetectionController_useExternalServices, _TokenDetectionController_getBalancesInSingleCall, _TokenDetectionController_trackMetaMetricsEvent, _TokenDetectionController_registerEventListeners, _TokenDetectionController_stopPolling, _TokenDetectionController_startPolling, _TokenDetectionController_getCorrectNetworkClientIdByChainId, _TokenDetectionController_restartTokenDetection, _TokenDetectionController_getChainCacheForDetection, _TokenDetectionController_detectTokensUsingRpc, _TokenDetectionController_getSlicesOfTokensToDetect, _TokenDetectionController_getConvertedStaticMainnetTokenList, _TokenDetectionController_buildMusdTokenListToken, _TokenDetectionController_mergeMusdIntoTokenListMap, _TokenDetectionController_applyMusdDefaultToTokensChainsCache, _TokenDetectionController_includeMusdInTokenDetectionSlice, _TokenDetectionController_addDetectedTokens, _TokenDetectionController_getSelectedAccount, _TokenDetectionController_getSelectedAddress;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.TokenDetectionController = exports.controllerName = exports.mapChainIdWithTokenListMap = exports.STATIC_MAINNET_TOKEN_LIST = void 0;
18
+ exports.TokenDetectionController = exports.controllerName = exports.STATIC_MAINNET_TOKEN_LIST = void 0;
19
19
  const contract_metadata_1 = __importDefault(require("@metamask/contract-metadata"));
20
20
  const controller_utils_1 = require("@metamask/controller-utils");
21
21
  const polling_controller_1 = require("@metamask/polling-controller");
22
- const lodash_1 = require("lodash");
23
22
  const assetsUtil_1 = require("./assetsUtil.cjs");
24
23
  const constants_1 = require("./constants.cjs");
25
24
  const DEFAULT_INTERVAL = 180000;
@@ -36,21 +35,6 @@ exports.STATIC_MAINNET_TOKEN_LIST = Object.entries(contract_metadata_1.default).
36
35
  };
37
36
  }, {});
38
37
  const MUSD_TOKEN_DETECTION_CHAIN_ID_SET = new Set(constants_1.MUSD_TOKEN_DETECTION_CHAIN_IDS);
39
- /**
40
- * Function that takes a TokensChainsCache object and maps chainId with TokenListMap.
41
- *
42
- * @param tokensChainsCache - TokensChainsCache input object
43
- * @returns returns the map of chainId with TokenListMap
44
- */
45
- function mapChainIdWithTokenListMap(tokensChainsCache) {
46
- return (0, lodash_1.mapValues)(tokensChainsCache, (value) => {
47
- if ((0, lodash_1.isObject)(value) && 'data' in value) {
48
- return (0, lodash_1.get)(value, ['data']);
49
- }
50
- return value;
51
- });
52
- }
53
- exports.mapChainIdWithTokenListMap = mapChainIdWithTokenListMap;
54
38
  exports.controllerName = 'TokenDetectionController';
55
39
  const MESSENGER_EXPOSED_METHODS = [
56
40
  'addDetectedTokensViaWs',
@@ -83,6 +67,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
83
67
  *
84
68
  * @param options - The controller options.
85
69
  * @param options.messenger - The controller messenger.
70
+ * @param options.tokenListService - Shared service for fetching the token list per chain.
86
71
  * @param options.disabled - If set to true, all network requests are blocked.
87
72
  * @param options.interval - Polling interval used to fetch new token rates
88
73
  * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.
@@ -90,7 +75,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
90
75
  * @param options.useTokenDetection - Feature Switch for using token detection (default: true)
91
76
  * @param options.useExternalServices - Feature Switch for using external services (default: false)
92
77
  */
93
- constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, useTokenDetection = () => true, useExternalServices = () => true, }) {
78
+ constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, tokenListService, useTokenDetection = () => true, useExternalServices = () => true, }) {
94
79
  super({
95
80
  name: exports.controllerName,
96
81
  messenger,
@@ -100,7 +85,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
100
85
  _TokenDetectionController_instances.add(this);
101
86
  _TokenDetectionController_intervalId.set(this, void 0);
102
87
  _TokenDetectionController_selectedAccountId.set(this, void 0);
103
- _TokenDetectionController_tokensChainsCache.set(this, {});
88
+ _TokenDetectionController_tokenListService.set(this, void 0);
104
89
  _TokenDetectionController_disabled.set(this, void 0);
105
90
  _TokenDetectionController_isUnlocked.set(this, void 0);
106
91
  _TokenDetectionController_isDetectionEnabledFromPreferences.set(this, void 0);
@@ -112,8 +97,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
112
97
  __classPrivateFieldSet(this, _TokenDetectionController_disabled, disabled, "f");
113
98
  this.setIntervalLength(interval);
114
99
  __classPrivateFieldSet(this, _TokenDetectionController_selectedAccountId, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAccount).call(this).id, "f");
115
- const { tokensChainsCache } = this.messenger.call('TokenListController:getState');
116
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, tokensChainsCache, "f");
100
+ __classPrivateFieldSet(this, _TokenDetectionController_tokenListService, tokenListService, "f");
117
101
  const { useTokenDetection: defaultUseTokenDetection } = this.messenger.call('PreferencesController:getState');
118
102
  __classPrivateFieldSet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, defaultUseTokenDetection, "f");
119
103
  __classPrivateFieldSet(this, _TokenDetectionController_getBalancesInSingleCall, getBalancesInSingleCall, "f");
@@ -168,7 +152,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
168
152
  });
169
153
  }
170
154
  /**
171
- * For each token in the token list provided by the TokenListController, checks the token's balance for the selected account address on the active network.
155
+ * For each token in the token list provided by the TokenListService, checks the token's balance for the selected account address on the active network.
172
156
  * On mainnet, if token detection is disabled in preferences, ERC20 token auto detection will be triggered for each contract address in the legacy token list from the @metamask/contract-metadata repo.
173
157
  *
174
158
  * @param options - Options for token detection.
@@ -225,10 +209,19 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
225
209
  if (!__classPrivateFieldGet(this, _TokenDetectionController_useExternalServices, "f").call(this)) {
226
210
  return;
227
211
  }
228
- // Refresh the token cache to ensure we have the latest token metadata
229
- // This fixes a bug where the cache from construction time could be stale/empty
230
- const { tokensChainsCache } = this.messenger.call('TokenListController:getState');
231
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, tokensChainsCache ?? {}), "f");
212
+ let tokenListMap;
213
+ try {
214
+ tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
215
+ }
216
+ catch {
217
+ // These methods return void; there is no token array to return.
218
+ // Gracefully exit so the caller is unaffected — the next polling cycle
219
+ // will retry the fetch.
220
+ return;
221
+ }
222
+ const chainCache = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
223
+ [chainId]: { data: tokenListMap, timestamp: Date.now() },
224
+ });
232
225
  const effectiveSlice = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_includeMusdInTokenDetectionSlice).call(this, tokensSlice, chainId);
233
226
  const tokensWithBalance = [];
234
227
  const eventTokensDetails = [];
@@ -237,7 +230,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
237
230
  const lowercaseTokenAddress = tokenAddress.toLowerCase();
238
231
  const checksummedTokenAddress = (0, controller_utils_1.toChecksumHexAddress)(tokenAddress);
239
232
  // Check map of validated tokens (cache keys are lowercase)
240
- const tokenData = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId]?.data?.[lowercaseTokenAddress];
233
+ const tokenData = chainCache[chainId]?.data?.[lowercaseTokenAddress];
241
234
  if (!tokenData) {
242
235
  continue;
243
236
  }
@@ -293,10 +286,19 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
293
286
  if (!__classPrivateFieldGet(this, _TokenDetectionController_useExternalServices, "f").call(this)) {
294
287
  return;
295
288
  }
296
- // Refresh the token cache to ensure we have the latest token metadata
297
- // This fixes a bug where the cache from construction time could be stale/empty
298
- const { tokensChainsCache } = this.messenger.call('TokenListController:getState');
299
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, tokensChainsCache ?? {}), "f");
289
+ let tokenListMap;
290
+ try {
291
+ tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
292
+ }
293
+ catch {
294
+ // These methods return void; there is no token array to return.
295
+ // Gracefully exit so the caller is unaffected — the next polling cycle
296
+ // will retry the fetch.
297
+ return;
298
+ }
299
+ const chainCache = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
300
+ [chainId]: { data: tokenListMap, timestamp: Date.now() },
301
+ });
300
302
  const selectedAddress = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
301
303
  // Get current token states to filter out already tracked/ignored tokens
302
304
  const { allTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
@@ -317,7 +319,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
317
319
  continue;
318
320
  }
319
321
  // Check map of validated tokens (cache keys are lowercase)
320
- const tokenData = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId]?.data?.[lowercaseTokenAddress];
322
+ const tokenData = chainCache[chainId]?.data?.[lowercaseTokenAddress];
321
323
  if (!tokenData) {
322
324
  continue;
323
325
  }
@@ -351,7 +353,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
351
353
  }
352
354
  }
353
355
  exports.TokenDetectionController = TokenDetectionController;
354
- _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_selectedAccountId = new WeakMap(), _TokenDetectionController_tokensChainsCache = new WeakMap(), _TokenDetectionController_disabled = new WeakMap(), _TokenDetectionController_isUnlocked = new WeakMap(), _TokenDetectionController_isDetectionEnabledFromPreferences = new WeakMap(), _TokenDetectionController_useTokenDetection = new WeakMap(), _TokenDetectionController_useExternalServices = new WeakMap(), _TokenDetectionController_getBalancesInSingleCall = new WeakMap(), _TokenDetectionController_trackMetaMetricsEvent = new WeakMap(), _TokenDetectionController_instances = new WeakSet(), _TokenDetectionController_registerEventListeners = function _TokenDetectionController_registerEventListeners() {
356
+ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_selectedAccountId = new WeakMap(), _TokenDetectionController_tokenListService = new WeakMap(), _TokenDetectionController_disabled = new WeakMap(), _TokenDetectionController_isUnlocked = new WeakMap(), _TokenDetectionController_isDetectionEnabledFromPreferences = new WeakMap(), _TokenDetectionController_useTokenDetection = new WeakMap(), _TokenDetectionController_useExternalServices = new WeakMap(), _TokenDetectionController_getBalancesInSingleCall = new WeakMap(), _TokenDetectionController_trackMetaMetricsEvent = new WeakMap(), _TokenDetectionController_instances = new WeakSet(), _TokenDetectionController_registerEventListeners = function _TokenDetectionController_registerEventListeners() {
355
357
  this.messenger.subscribe('KeyringController:unlock', () => {
356
358
  __classPrivateFieldSet(this, _TokenDetectionController_isUnlocked, true, "f");
357
359
  __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this).catch(() => {
@@ -362,14 +364,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
362
364
  __classPrivateFieldSet(this, _TokenDetectionController_isUnlocked, false, "f");
363
365
  __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_stopPolling).call(this);
364
366
  });
365
- this.messenger.subscribe('TokenListController:stateChange', ({ tokensChainsCache }) => {
366
- const isEqualValues = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_compareTokensChainsCache).call(this, tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f"));
367
- if (!isEqualValues) {
368
- __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this).catch(() => {
369
- // Silently handle token detection errors
370
- });
371
- }
372
- });
373
367
  this.messenger.subscribe('PreferencesController:stateChange', ({ useTokenDetection }) => {
374
368
  const selectedAccount = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAccount).call(this);
375
369
  const isDetectionChangedFromPreferences = __classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") !== useTokenDetection;
@@ -422,11 +416,6 @@ async function _TokenDetectionController_startPolling() {
422
416
  __classPrivateFieldSet(this, _TokenDetectionController_intervalId, setInterval(async () => {
423
417
  await this.detectTokens();
424
418
  }, this.getIntervalLength()), "f");
425
- }, _TokenDetectionController_compareTokensChainsCache = function _TokenDetectionController_compareTokensChainsCache(tokensChainsCache, previousTokensChainsCache) {
426
- const cleanPreviousTokensChainsCache = mapChainIdWithTokenListMap(previousTokensChainsCache);
427
- const cleanTokensChainsCache = mapChainIdWithTokenListMap(tokensChainsCache);
428
- const isEqualValues = (0, lodash_1.isEqual)(cleanTokensChainsCache, cleanPreviousTokensChainsCache);
429
- return isEqualValues;
430
419
  }, _TokenDetectionController_getCorrectNetworkClientIdByChainId = function _TokenDetectionController_getCorrectNetworkClientIdByChainId(chainIds) {
431
420
  const { networkConfigurationsByChainId, selectedNetworkClientId } = this.messenger.call('NetworkController:getState');
432
421
  if (!chainIds) {
@@ -461,30 +450,41 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
461
450
  selectedAddress,
462
451
  });
463
452
  this.setIntervalLength(DEFAULT_INTERVAL);
464
- }, _TokenDetectionController_shouldDetectTokens = function _TokenDetectionController_shouldDetectTokens(chainId) {
453
+ }, _TokenDetectionController_getChainCacheForDetection =
454
+ /**
455
+ * Returns the token cache for `chainId` if detection should proceed, or `null` if it
456
+ * should be skipped. Each call fetches a fresh snapshot from `TokenListService` (which
457
+ * may serve from its in-memory cache) so concurrent calls for different chains never
458
+ * overwrite each other's data.
459
+ *
460
+ * @param chainId - The chain ID to build a detection cache for.
461
+ * @returns A `TokensChainsCache` scoped to `chainId`, or `null` when detection should be skipped.
462
+ */
463
+ async function _TokenDetectionController_getChainCacheForDetection(chainId) {
465
464
  if (!(0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(chainId)) {
466
- return false;
465
+ return null;
467
466
  }
468
467
  if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
469
468
  chainId !== controller_utils_1.ChainId.mainnet) {
470
- return false;
469
+ return null;
471
470
  }
472
471
  const isMainnetDetectionInactive = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") && chainId === controller_utils_1.ChainId.mainnet;
473
472
  if (isMainnetDetectionInactive) {
474
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this), "f");
475
- }
476
- else {
477
- const { tokensChainsCache } = this.messenger.call('TokenListController:getState');
478
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, tokensChainsCache ?? {}), "f");
473
+ return __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this);
479
474
  }
480
- return true;
475
+ const tokenListMap = await __classPrivateFieldGet(this, _TokenDetectionController_tokenListService, "f").fetchTokensByChainId(chainId);
476
+ return __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_applyMusdDefaultToTokensChainsCache).call(this, chainId, {
477
+ [chainId]: { data: tokenListMap, timestamp: Date.now() },
478
+ });
481
479
  }, _TokenDetectionController_detectTokensUsingRpc = async function _TokenDetectionController_detectTokensUsingRpc(chainsToDetectUsingRpc, addressToDetect) {
482
480
  for (const { chainId, networkClientId } of chainsToDetectUsingRpc) {
483
- if (!__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_shouldDetectTokens).call(this, chainId)) {
481
+ const chainCache = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainCacheForDetection).call(this, chainId);
482
+ if (!chainCache) {
484
483
  continue;
485
484
  }
486
485
  const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
487
486
  chainId,
487
+ chainCache,
488
488
  selectedAddress: addressToDetect,
489
489
  });
490
490
  const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) => __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokens).call(this, {
@@ -492,10 +492,11 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
492
492
  selectedAddress: addressToDetect,
493
493
  networkClientId,
494
494
  chainId,
495
+ chainCache,
495
496
  }));
496
497
  await Promise.all(tokenDetectionPromises);
497
498
  }
498
- }, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, selectedAddress, }) {
499
+ }, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, chainCache, selectedAddress, }) {
499
500
  const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
500
501
  const [tokensAddresses, detectedTokensAddresses, ignoredTokensAddresses] = [
501
502
  allTokens,
@@ -503,7 +504,7 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
503
504
  allIgnoredTokens,
504
505
  ].map((tokens) => (tokens[chainId]?.[selectedAddress] ?? []).map((value) => typeof value === 'string' ? value : value.address));
505
506
  const tokensToDetect = [];
506
- for (const tokenAddress of Object.keys(__classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")?.[chainId]?.data || {})) {
507
+ for (const tokenAddress of Object.keys(chainCache[chainId]?.data ?? {})) {
507
508
  if ([
508
509
  tokensAddresses,
509
510
  detectedTokensAddresses,
@@ -575,13 +576,20 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
575
576
  return tokensSlice;
576
577
  }
577
578
  return [...tokensSlice, constants_1.MUSD_ERC20_ADDRESS_LOWER];
578
- }, _TokenDetectionController_addDetectedTokens = async function _TokenDetectionController_addDetectedTokens({ tokensSlice, selectedAddress, networkClientId, chainId, }) {
579
+ }, _TokenDetectionController_addDetectedTokens = async function _TokenDetectionController_addDetectedTokens({ tokensSlice, selectedAddress, networkClientId, chainId, chainCache, }) {
579
580
  await (0, controller_utils_1.safelyExecute)(async () => {
580
581
  const balances = await __classPrivateFieldGet(this, _TokenDetectionController_getBalancesInSingleCall, "f").call(this, selectedAddress, tokensSlice, networkClientId);
582
+ const chainData = chainCache[chainId]?.data ?? {};
581
583
  const tokensWithBalance = [];
582
584
  const eventTokensDetails = [];
583
585
  for (const nonZeroTokenAddress of Object.keys(balances)) {
584
- const { decimals, symbol, aggregators, iconUrl, name, rwaData } = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data[nonZeroTokenAddress];
586
+ // chainData keys are lowercase (normalised by buildTokenListMap);
587
+ // balance keys are checksummed, so normalise before lookup.
588
+ const tokenListEntry = chainData[nonZeroTokenAddress.toLowerCase()];
589
+ if (!tokenListEntry) {
590
+ continue;
591
+ }
592
+ const { decimals, symbol, aggregators, iconUrl, name, rwaData } = tokenListEntry;
585
593
  eventTokensDetails.push(`${symbol} - ${nonZeroTokenAddress}`);
586
594
  tokensWithBalance.push({
587
595
  address: nonZeroTokenAddress,
@@ -601,8 +609,7 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
601
609
  const musdInSlice = tokensSlice.some((addr) => (0, controller_utils_1.isEqualCaseInsensitive)(addr, constants_1.MUSD_ERC20_ADDRESS_LOWER));
602
610
  const musdHasNonZeroFromRpc = Object.keys(balances).some((addr) => (0, controller_utils_1.isEqualCaseInsensitive)(addr, constants_1.MUSD_ERC20_ADDRESS_LOWER));
603
611
  if (musdInSlice && !musdHasNonZeroFromRpc) {
604
- const listData = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data;
605
- const musdListToken = Object.entries(listData).find(([key]) => (0, controller_utils_1.isEqualCaseInsensitive)(key, constants_1.MUSD_ERC20_ADDRESS_LOWER))?.[1];
612
+ const musdListToken = Object.entries(chainData).find(([key]) => (0, controller_utils_1.isEqualCaseInsensitive)(key, constants_1.MUSD_ERC20_ADDRESS_LOWER))?.[1];
606
613
  if (musdListToken) {
607
614
  const { decimals, symbol, aggregators, iconUrl, name, rwaData } = musdListToken;
608
615
  eventTokensDetails.push(`${symbol} - ${constants_1.MUSD_ERC20_ADDRESS_LOWER}`);