@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.
- package/CHANGELOG.md +25 -1
- package/dist/TokenDetectionController-method-action-types.cjs.map +1 -1
- package/dist/TokenDetectionController-method-action-types.d.cts +1 -1
- package/dist/TokenDetectionController-method-action-types.d.mts +1 -1
- package/dist/TokenDetectionController-method-action-types.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +70 -63
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +8 -12
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +8 -12
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +69 -62
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/TokenListService.cjs +107 -0
- package/dist/TokenListService.cjs.map +1 -0
- package/dist/TokenListService.d.cts +40 -0
- package/dist/TokenListService.d.cts.map +1 -0
- package/dist/TokenListService.d.mts +40 -0
- package/dist/TokenListService.d.mts.map +1 -0
- package/dist/TokenListService.mjs +102 -0
- package/dist/TokenListService.mjs.map +1 -0
- package/dist/TokensController.cjs +46 -32
- package/dist/TokensController.cjs.map +1 -1
- package/dist/TokensController.d.cts +5 -3
- package/dist/TokensController.d.cts.map +1 -1
- package/dist/TokensController.d.mts +5 -3
- package/dist/TokensController.d.mts.map +1 -1
- package/dist/TokensController.mjs +46 -32
- package/dist/TokensController.mjs.map +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- 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@
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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 =
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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 =
|
|
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(),
|
|
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
|
-
},
|
|
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
|
|
465
|
+
return null;
|
|
467
466
|
}
|
|
468
467
|
if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
|
|
469
468
|
chainId !== controller_utils_1.ChainId.mainnet) {
|
|
470
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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}`);
|