@metamask/assets-controllers 41.0.0 → 43.0.0
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 +31 -1
- package/dist/AccountTrackerController.cjs +30 -10
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +14 -9
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +14 -9
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +30 -10
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/AssetsContractController.cjs +61 -1
- package/dist/AssetsContractController.cjs.map +1 -1
- package/dist/AssetsContractController.d.cts +13 -0
- package/dist/AssetsContractController.d.cts.map +1 -1
- package/dist/AssetsContractController.d.mts +13 -0
- package/dist/AssetsContractController.d.mts.map +1 -1
- package/dist/AssetsContractController.mjs +61 -1
- package/dist/AssetsContractController.mjs.map +1 -1
- package/dist/CurrencyRateController.d.cts +4 -4
- package/dist/CurrencyRateController.d.mts +4 -4
- package/dist/NftController.cjs +8 -0
- package/dist/NftController.cjs.map +1 -1
- package/dist/NftController.d.cts +4 -0
- package/dist/NftController.d.cts.map +1 -1
- package/dist/NftController.d.mts +4 -0
- package/dist/NftController.d.mts.map +1 -1
- package/dist/NftController.mjs +8 -0
- package/dist/NftController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +8 -0
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +4 -0
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +4 -0
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +8 -0
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +196 -129
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +23 -12
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +23 -12
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +195 -129
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/TokenListController.cjs +60 -53
- package/dist/TokenListController.cjs.map +1 -1
- package/dist/TokenListController.d.cts +25 -14
- package/dist/TokenListController.d.cts.map +1 -1
- package/dist/TokenListController.d.mts +25 -14
- package/dist/TokenListController.d.mts.map +1 -1
- package/dist/TokenListController.mjs +60 -53
- package/dist/TokenListController.mjs.map +1 -1
- package/dist/TokenRatesController.cjs +43 -13
- package/dist/TokenRatesController.cjs.map +1 -1
- package/dist/TokenRatesController.d.cts +12 -8
- package/dist/TokenRatesController.d.cts.map +1 -1
- package/dist/TokenRatesController.d.mts +12 -8
- package/dist/TokenRatesController.d.mts.map +1 -1
- package/dist/TokenRatesController.mjs +43 -13
- package/dist/TokenRatesController.mjs.map +1 -1
- package/dist/TokensController.cjs +15 -4
- package/dist/TokensController.cjs.map +1 -1
- package/dist/TokensController.d.cts +4 -0
- package/dist/TokensController.d.cts.map +1 -1
- package/dist/TokensController.d.mts +4 -0
- package/dist/TokensController.d.mts.map +1 -1
- package/dist/TokensController.mjs +15 -4
- package/dist/TokensController.mjs.map +1 -1
- package/dist/assetsUtil.cjs +13 -1
- package/dist/assetsUtil.cjs.map +1 -1
- package/dist/assetsUtil.d.cts +7 -0
- package/dist/assetsUtil.d.cts.map +1 -1
- package/dist/assetsUtil.d.mts +7 -0
- package/dist/assetsUtil.d.mts.map +1 -1
- package/dist/assetsUtil.mjs +12 -0
- package/dist/assetsUtil.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs +18 -0
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs +18 -0
- package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs +7 -2
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts +3 -2
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts +3 -2
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs +7 -2
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n RestrictedControllerMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute, toHex } from '@metamask/controller-utils';\n\nimport type { AssetsContractControllerGetERC20BalanceOfAction } from './AssetsContractController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensControllerStateChangeEvent } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n contractBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property tokens - List of tokens to track balances for.\n * @property disabled - If set to true, all tracked tokens contract balances updates are blocked.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n tokens?: Token[];\n disabled?: boolean;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * Represents a mapping of hash token contract addresses to their balances.\n */\ntype ContractBalances = Record<string, string>;\n\n/**\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport type TokenBalancesControllerState = {\n contractBalances: ContractBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | AccountsControllerGetSelectedAccountAction\n | AssetsContractControllerGetERC20BalanceOfAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents = TokensControllerStateChangeEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n contractBalances: {},\n };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #handle?: ReturnType<typeof setTimeout>;\n\n #interval: number;\n\n #tokens: Token[];\n\n #disabled: boolean;\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.tokens - List of tokens to track balances for.\n * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n tokens = [],\n disabled = false,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.#disabled = disabled;\n this.#interval = interval;\n this.#tokens = tokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n ({ tokens: newTokens, detectedTokens }) => {\n this.#tokens = [...newTokens, ...detectedTokens];\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateBalances();\n },\n );\n\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll();\n }\n\n /**\n * Allows controller to update tracked tokens contract balances.\n */\n enable() {\n this.#disabled = false;\n }\n\n /**\n * Blocks controller from updating tracked tokens contract balances.\n */\n disable() {\n this.#disabled = true;\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n if (interval) {\n this.#interval = interval;\n }\n\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n\n await safelyExecute(() => this.updateBalances());\n\n this.#handle = setTimeout(() => {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll(this.#interval);\n }, this.#interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.#disabled) {\n return;\n }\n const selectedInternalAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const newContractBalances: ContractBalances = {};\n for (const token of this.#tokens) {\n const { address } = token;\n try {\n const balance = await this.messagingSystem.call(\n 'AssetsContractController:getERC20BalanceOf',\n address,\n selectedInternalAccount.address,\n );\n newContractBalances[address] = toHex(balance);\n token.hasBalanceError = false;\n } catch (error) {\n newContractBalances[address] = toHex(0);\n token.hasBalanceError = true;\n }\n }\n\n this.update((state) => {\n state.contractBalances = newContractBalances;\n });\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n RestrictedControllerMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute, toHex } from '@metamask/controller-utils';\n\nimport type { AssetsContractControllerGetERC20BalanceOfAction } from './AssetsContractController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensControllerStateChangeEvent } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n contractBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property tokens - List of tokens to track balances for.\n * @property disabled - If set to true, all tracked tokens contract balances updates are blocked.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n tokens?: Token[];\n disabled?: boolean;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * Represents a mapping of hash token contract addresses to their balances.\n */\ntype ContractBalances = Record<string, string>;\n\n/**\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport type TokenBalancesControllerState = {\n contractBalances: ContractBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | AccountsControllerGetSelectedAccountAction\n | AssetsContractControllerGetERC20BalanceOfAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents = TokensControllerStateChangeEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n contractBalances: {},\n };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #handle?: ReturnType<typeof setTimeout>;\n\n #interval: number;\n\n #tokens: Token[];\n\n #disabled: boolean;\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.tokens - List of tokens to track balances for.\n * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n tokens = [],\n disabled = false,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.#disabled = disabled;\n this.#interval = interval;\n this.#tokens = tokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n ({ tokens: newTokens, detectedTokens }) => {\n this.#tokens = [...newTokens, ...detectedTokens];\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateBalances();\n },\n );\n\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll();\n }\n\n /**\n * Allows controller to update tracked tokens contract balances.\n */\n enable() {\n this.#disabled = false;\n }\n\n /**\n * Blocks controller from updating tracked tokens contract balances.\n */\n disable() {\n this.#disabled = true;\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n if (interval) {\n this.#interval = interval;\n }\n\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n\n await safelyExecute(() => this.updateBalances());\n\n this.#handle = setTimeout(() => {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll(this.#interval);\n }, this.#interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.#disabled) {\n return;\n }\n const selectedInternalAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const newContractBalances: ContractBalances = {};\n for (const token of this.#tokens) {\n const { address } = token;\n try {\n const balance = await this.messagingSystem.call(\n 'AssetsContractController:getERC20BalanceOf',\n address,\n selectedInternalAccount.address,\n );\n newContractBalances[address] = toHex(balance);\n token.hasBalanceError = false;\n } catch (error) {\n newContractBalances[address] = toHex(0);\n token.hasBalanceError = true;\n }\n }\n\n this.update((state) => {\n state.contractBalances = newContractBalances;\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -13,9 +13,9 @@ 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_networkClientId, _TokenDetectionController_tokensChainsCache, _TokenDetectionController_disabled, _TokenDetectionController_isUnlocked, _TokenDetectionController_isDetectionEnabledFromPreferences, _TokenDetectionController_isDetectionEnabledForNetwork, _TokenDetectionController_getBalancesInSingleCall, _TokenDetectionController_trackMetaMetricsEvent, _TokenDetectionController_accountsAPI, _TokenDetectionController_registerEventListeners, _TokenDetectionController_stopPolling, _TokenDetectionController_startPolling, _TokenDetectionController_compareTokensChainsCache, _TokenDetectionController_getCorrectChainIdAndNetworkClientId, _TokenDetectionController_restartTokenDetection, _TokenDetectionController_getSlicesOfTokensToDetect, _TokenDetectionController_getConvertedStaticMainnetTokenList, _TokenDetectionController_addDetectedTokensViaAPI, _TokenDetectionController_addDetectedTokens, _TokenDetectionController_getSelectedAccount, _TokenDetectionController_getSelectedAddress;
|
|
16
|
+
var _TokenDetectionController_instances, _TokenDetectionController_intervalId, _TokenDetectionController_selectedAccountId, _TokenDetectionController_networkClientId, _TokenDetectionController_tokensChainsCache, _TokenDetectionController_disabled, _TokenDetectionController_isUnlocked, _TokenDetectionController_isDetectionEnabledFromPreferences, _TokenDetectionController_isDetectionEnabledForNetwork, _TokenDetectionController_getBalancesInSingleCall, _TokenDetectionController_trackMetaMetricsEvent, _TokenDetectionController_accountsAPI, _TokenDetectionController_registerEventListeners, _TokenDetectionController_stopPolling, _TokenDetectionController_startPolling, _TokenDetectionController_compareTokensChainsCache, _TokenDetectionController_getCorrectNetworkClientIdByChainId, _TokenDetectionController_getCorrectChainIdAndNetworkClientId, _TokenDetectionController_restartTokenDetection, _TokenDetectionController_getChainsToDetect, _TokenDetectionController_attemptAccountAPIDetection, _TokenDetectionController_addChainsToRpcDetection, _TokenDetectionController_shouldDetectTokens, _TokenDetectionController_detectTokensUsingRpc, _TokenDetectionController_getSlicesOfTokensToDetect, _TokenDetectionController_getConvertedStaticMainnetTokenList, _TokenDetectionController_addDetectedTokensViaAPI, _TokenDetectionController_filterAndBuildTokensWithBalance, _TokenDetectionController_addDetectedTokens, _TokenDetectionController_getSelectedAccount, _TokenDetectionController_getSelectedAddress;
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.TokenDetectionController = exports.controllerName = exports.STATIC_MAINNET_TOKEN_LIST = void 0;
|
|
18
|
+
exports.TokenDetectionController = exports.controllerName = exports.mapChainIdWithTokenListMap = 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");
|
|
@@ -49,6 +49,7 @@ function mapChainIdWithTokenListMap(tokensChainsCache) {
|
|
|
49
49
|
return value;
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
+
exports.mapChainIdWithTokenListMap = mapChainIdWithTokenListMap;
|
|
52
53
|
exports.controllerName = 'TokenDetectionController';
|
|
53
54
|
/**
|
|
54
55
|
* Controller that passively polls on a set interval for Tokens auto detection
|
|
@@ -71,8 +72,9 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
71
72
|
* @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.
|
|
72
73
|
* @param options.trackMetaMetricsEvent - Sets options for MetaMetrics event tracking.
|
|
73
74
|
* @param options.useAccountsAPI - Feature Switch for using the accounts API when detecting tokens (default: true)
|
|
75
|
+
* @param options.platform - Indicates whether the platform is extension or mobile
|
|
74
76
|
*/
|
|
75
|
-
constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, useAccountsAPI = true, }) {
|
|
77
|
+
constructor({ interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, trackMetaMetricsEvent, messenger, useAccountsAPI = true, platform, }) {
|
|
76
78
|
super({
|
|
77
79
|
name: exports.controllerName,
|
|
78
80
|
messenger,
|
|
@@ -93,6 +95,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
93
95
|
_TokenDetectionController_accountsAPI.set(this, {
|
|
94
96
|
isAccountsAPIEnabled: true,
|
|
95
97
|
supportedNetworksCache: null,
|
|
98
|
+
platform: '',
|
|
96
99
|
async getSupportedNetworks() {
|
|
97
100
|
/* istanbul ignore next */
|
|
98
101
|
if (!this.isAccountsAPIEnabled) {
|
|
@@ -106,19 +109,16 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
106
109
|
this.supportedNetworksCache = result;
|
|
107
110
|
return result;
|
|
108
111
|
},
|
|
109
|
-
async
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const chainIdNumber = (0, utils_1.hexToNumber)(chainId);
|
|
114
|
-
const supportedNetworks = await this.getSupportedNetworks();
|
|
115
|
-
if (!supportedNetworks || !supportedNetworks.includes(chainIdNumber)) {
|
|
112
|
+
async getMultiNetworksBalances(address, chainIds, supportedNetworks) {
|
|
113
|
+
const chainIdNumbers = chainIds.map((chainId) => (0, utils_1.hexToNumber)(chainId));
|
|
114
|
+
if (!supportedNetworks ||
|
|
115
|
+
!chainIdNumbers.every((id) => supportedNetworks.includes(id))) {
|
|
116
116
|
const supportedNetworksErrStr = (supportedNetworks ?? []).toString();
|
|
117
|
-
throw new Error(`Unsupported Network: supported networks ${supportedNetworksErrStr},
|
|
117
|
+
throw new Error(`Unsupported Network: supported networks ${supportedNetworksErrStr}, requested networks: ${chainIdNumbers.toString()}`);
|
|
118
118
|
}
|
|
119
119
|
const result = await (0, multi_chain_accounts_service_1.fetchMultiChainBalances)(address, {
|
|
120
|
-
networks:
|
|
121
|
-
});
|
|
120
|
+
networks: chainIdNumbers,
|
|
121
|
+
}, this.platform);
|
|
122
122
|
return result.balances;
|
|
123
123
|
},
|
|
124
124
|
});
|
|
@@ -137,6 +137,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
137
137
|
const { isUnlocked } = this.messagingSystem.call('KeyringController:getState');
|
|
138
138
|
__classPrivateFieldSet(this, _TokenDetectionController_isUnlocked, isUnlocked, "f");
|
|
139
139
|
__classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").isAccountsAPIEnabled = useAccountsAPI;
|
|
140
|
+
__classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").platform = platform;
|
|
140
141
|
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_registerEventListeners).call(this);
|
|
141
142
|
}
|
|
142
143
|
/**
|
|
@@ -172,12 +173,12 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
172
173
|
this.disable();
|
|
173
174
|
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_stopPolling).call(this);
|
|
174
175
|
}
|
|
175
|
-
async _executePoll({
|
|
176
|
+
async _executePoll({ chainIds, address, }) {
|
|
176
177
|
if (!this.isActive) {
|
|
177
178
|
return;
|
|
178
179
|
}
|
|
179
180
|
await this.detectTokens({
|
|
180
|
-
|
|
181
|
+
chainIds,
|
|
181
182
|
selectedAddress: address,
|
|
182
183
|
});
|
|
183
184
|
}
|
|
@@ -186,51 +187,35 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo
|
|
|
186
187
|
* 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.
|
|
187
188
|
*
|
|
188
189
|
* @param options - Options for token detection.
|
|
189
|
-
* @param options.
|
|
190
|
+
* @param options.chainIds - The chain IDs of the network client to use.
|
|
190
191
|
* @param options.selectedAddress - the selectedAddress against which to detect for token balances.
|
|
191
192
|
*/
|
|
192
|
-
async detectTokens({
|
|
193
|
+
async detectTokens({ chainIds, selectedAddress, } = {}) {
|
|
193
194
|
if (!this.isActive) {
|
|
194
195
|
return;
|
|
195
196
|
}
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return;
|
|
197
|
+
const addressToDetect = selectedAddress ?? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
|
|
198
|
+
const clientNetworks = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectNetworkClientIdByChainId).call(this, chainIds);
|
|
199
|
+
let supportedNetworks;
|
|
200
|
+
if (__classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").isAccountsAPIEnabled) {
|
|
201
|
+
supportedNetworks = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").getSupportedNetworks();
|
|
202
202
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks);
|
|
204
|
+
// Try detecting tokens via Account API first if conditions allow
|
|
205
|
+
if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) {
|
|
206
|
+
const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks);
|
|
207
|
+
// If API succeeds and no chains are left for RPC detection, we can return early
|
|
208
|
+
if (apiResult?.result === 'success' &&
|
|
209
|
+
chainsToDetectUsingRpc.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection
|
|
213
|
+
__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks);
|
|
206
214
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, isTokenDetectionInactiveInMainnet
|
|
211
|
-
? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this)
|
|
212
|
-
: tokensChainsCache ?? {}, "f");
|
|
213
|
-
const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
|
|
214
|
-
chainId: chainIdAgainstWhichToDetect,
|
|
215
|
-
selectedAddress: addressAgainstWhichToDetect,
|
|
216
|
-
});
|
|
217
|
-
// Attempt Accounts API Detection
|
|
218
|
-
const accountAPIResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokensViaAPI).call(this, {
|
|
219
|
-
chainId: chainIdAgainstWhichToDetect,
|
|
220
|
-
selectedAddress: addressAgainstWhichToDetect,
|
|
221
|
-
tokenCandidateSlices,
|
|
222
|
-
});
|
|
223
|
-
if (accountAPIResult?.result === 'success') {
|
|
224
|
-
return;
|
|
215
|
+
// Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc
|
|
216
|
+
if (chainsToDetectUsingRpc.length > 0) {
|
|
217
|
+
await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_detectTokensUsingRpc).call(this, chainsToDetectUsingRpc, addressToDetect);
|
|
225
218
|
}
|
|
226
|
-
// Attempt RPC Detection
|
|
227
|
-
const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) => __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokens).call(this, {
|
|
228
|
-
tokensSlice,
|
|
229
|
-
selectedAddress: addressAgainstWhichToDetect,
|
|
230
|
-
networkClientId: networkClientIdAgainstWhichToDetect,
|
|
231
|
-
chainId: chainIdAgainstWhichToDetect,
|
|
232
|
-
}));
|
|
233
|
-
await Promise.all(tokenDetectionPromises);
|
|
234
219
|
}
|
|
235
220
|
}
|
|
236
221
|
exports.TokenDetectionController = TokenDetectionController;
|
|
@@ -279,20 +264,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
|
|
|
279
264
|
});
|
|
280
265
|
}
|
|
281
266
|
});
|
|
282
|
-
this.messagingSystem.subscribe('NetworkController:networkDidChange',
|
|
283
|
-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
284
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
285
|
-
async ({ selectedNetworkClientId }) => {
|
|
286
|
-
const isNetworkClientIdChanged = __classPrivateFieldGet(this, _TokenDetectionController_networkClientId, "f") !== selectedNetworkClientId;
|
|
287
|
-
const { chainId: newChainId } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectChainIdAndNetworkClientId).call(this, selectedNetworkClientId);
|
|
288
|
-
__classPrivateFieldSet(this, _TokenDetectionController_isDetectionEnabledForNetwork, (0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(newChainId), "f");
|
|
289
|
-
if (isNetworkClientIdChanged && __classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledForNetwork, "f")) {
|
|
290
|
-
__classPrivateFieldSet(this, _TokenDetectionController_networkClientId, selectedNetworkClientId, "f");
|
|
291
|
-
await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this, {
|
|
292
|
-
networkClientId: __classPrivateFieldGet(this, _TokenDetectionController_networkClientId, "f"),
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
267
|
}, _TokenDetectionController_stopPolling = function _TokenDetectionController_stopPolling() {
|
|
297
268
|
if (__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f")) {
|
|
298
269
|
clearInterval(__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f"));
|
|
@@ -317,16 +288,26 @@ async function _TokenDetectionController_startPolling() {
|
|
|
317
288
|
const cleanTokensChainsCache = mapChainIdWithTokenListMap(tokensChainsCache);
|
|
318
289
|
const isEqualValues = (0, lodash_1.isEqual)(cleanTokensChainsCache, cleanPreviousTokensChainsCache);
|
|
319
290
|
return isEqualValues;
|
|
320
|
-
},
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
291
|
+
}, _TokenDetectionController_getCorrectNetworkClientIdByChainId = function _TokenDetectionController_getCorrectNetworkClientIdByChainId(chainIds) {
|
|
292
|
+
const { networkConfigurationsByChainId, selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
|
|
293
|
+
if (!chainIds) {
|
|
294
|
+
const networkConfiguration = this.messagingSystem.call('NetworkController:getNetworkConfigurationByNetworkClientId', selectedNetworkClientId);
|
|
295
|
+
return [
|
|
296
|
+
{
|
|
297
|
+
chainId: networkConfiguration?.chainId ?? controller_utils_1.ChainId.mainnet,
|
|
298
|
+
networkClientId: selectedNetworkClientId,
|
|
299
|
+
},
|
|
300
|
+
];
|
|
329
301
|
}
|
|
302
|
+
return chainIds.map((chainId) => {
|
|
303
|
+
const configuration = networkConfigurationsByChainId[chainId];
|
|
304
|
+
return {
|
|
305
|
+
chainId,
|
|
306
|
+
networkClientId: configuration.rpcEndpoints[configuration.defaultRpcEndpointIndex]
|
|
307
|
+
.networkClientId,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}, _TokenDetectionController_getCorrectChainIdAndNetworkClientId = function _TokenDetectionController_getCorrectChainIdAndNetworkClientId() {
|
|
330
311
|
const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
|
|
331
312
|
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
|
332
313
|
return {
|
|
@@ -340,14 +321,76 @@ async function _TokenDetectionController_startPolling() {
|
|
|
340
321
|
*
|
|
341
322
|
* @param options - Options for restart token detection.
|
|
342
323
|
* @param options.selectedAddress - the selectedAddress against which to detect for token balances
|
|
343
|
-
* @param options.
|
|
324
|
+
* @param options.chainIds - The chain IDs of the network client to use.
|
|
344
325
|
*/
|
|
345
|
-
async function _TokenDetectionController_restartTokenDetection({ selectedAddress,
|
|
326
|
+
async function _TokenDetectionController_restartTokenDetection({ selectedAddress, chainIds, } = {}) {
|
|
346
327
|
await this.detectTokens({
|
|
347
|
-
|
|
328
|
+
chainIds,
|
|
348
329
|
selectedAddress,
|
|
349
330
|
});
|
|
350
331
|
this.setIntervalLength(DEFAULT_INTERVAL);
|
|
332
|
+
}, _TokenDetectionController_getChainsToDetect = function _TokenDetectionController_getChainsToDetect(clientNetworks, supportedNetworks) {
|
|
333
|
+
const chainsToDetectUsingAccountAPI = [];
|
|
334
|
+
const chainsToDetectUsingRpc = [];
|
|
335
|
+
clientNetworks.forEach(({ chainId, networkClientId }) => {
|
|
336
|
+
if (supportedNetworks?.includes((0, utils_1.hexToNumber)(chainId))) {
|
|
337
|
+
chainsToDetectUsingAccountAPI.push(chainId);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
chainsToDetectUsingRpc.push({ chainId, networkClientId });
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
return { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI };
|
|
344
|
+
}, _TokenDetectionController_attemptAccountAPIDetection = async function _TokenDetectionController_attemptAccountAPIDetection(chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks) {
|
|
345
|
+
return await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokensViaAPI).call(this, {
|
|
346
|
+
chainIds: chainsToDetectUsingAccountAPI,
|
|
347
|
+
selectedAddress: addressToDetect,
|
|
348
|
+
supportedNetworks,
|
|
349
|
+
});
|
|
350
|
+
}, _TokenDetectionController_addChainsToRpcDetection = function _TokenDetectionController_addChainsToRpcDetection(chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks) {
|
|
351
|
+
chainsToDetectUsingAccountAPI.forEach((chainId) => {
|
|
352
|
+
const networkEntry = clientNetworks.find((network) => network.chainId === chainId);
|
|
353
|
+
if (networkEntry) {
|
|
354
|
+
chainsToDetectUsingRpc.push({
|
|
355
|
+
chainId: networkEntry.chainId,
|
|
356
|
+
networkClientId: networkEntry.networkClientId,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}, _TokenDetectionController_shouldDetectTokens = function _TokenDetectionController_shouldDetectTokens(chainId) {
|
|
361
|
+
if (!(0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(chainId)) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
|
|
365
|
+
chainId !== controller_utils_1.ChainId.mainnet) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
const isMainnetDetectionInactive = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") && chainId === controller_utils_1.ChainId.mainnet;
|
|
369
|
+
if (isMainnetDetectionInactive) {
|
|
370
|
+
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this), "f");
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
const { tokensChainsCache } = this.messagingSystem.call('TokenListController:getState');
|
|
374
|
+
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, tokensChainsCache ?? {}, "f");
|
|
375
|
+
}
|
|
376
|
+
return true;
|
|
377
|
+
}, _TokenDetectionController_detectTokensUsingRpc = async function _TokenDetectionController_detectTokensUsingRpc(chainsToDetectUsingRpc, addressToDetect) {
|
|
378
|
+
for (const { chainId, networkClientId } of chainsToDetectUsingRpc) {
|
|
379
|
+
if (!__classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_shouldDetectTokens).call(this, chainId)) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
|
|
383
|
+
chainId,
|
|
384
|
+
selectedAddress: addressToDetect,
|
|
385
|
+
});
|
|
386
|
+
const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) => __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokens).call(this, {
|
|
387
|
+
tokensSlice,
|
|
388
|
+
selectedAddress: addressToDetect,
|
|
389
|
+
networkClientId,
|
|
390
|
+
chainId,
|
|
391
|
+
}));
|
|
392
|
+
await Promise.all(tokenDetectionPromises);
|
|
393
|
+
}
|
|
351
394
|
}, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, selectedAddress, }) {
|
|
352
395
|
const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messagingSystem.call('TokensController:getState');
|
|
353
396
|
const [tokensAddresses, detectedTokensAddresses, ignoredTokensAddresses] = [
|
|
@@ -393,68 +436,92 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
|
|
|
393
436
|
* This adds detected tokens from the Accounts API, avoiding the multi-call RPC calls for balances
|
|
394
437
|
* @param options - method arguments
|
|
395
438
|
* @param options.selectedAddress - address to check against
|
|
396
|
-
* @param options.
|
|
397
|
-
* @param options.
|
|
398
|
-
* We will use these these token candidates to determine if a token found from the API is valid to be added on the users wallet.
|
|
399
|
-
* It will also prevent us to adding tokens a user already has
|
|
439
|
+
* @param options.chainIds - array of chainIds to check tokens for
|
|
440
|
+
* @param options.supportedNetworks - array of chainIds to check tokens for
|
|
400
441
|
* @returns a success or failed object
|
|
401
442
|
*/
|
|
402
|
-
async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddress,
|
|
443
|
+
async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddress, chainIds, supportedNetworks, }) {
|
|
403
444
|
return await (0, controller_utils_1.safelyExecute)(async () => {
|
|
404
|
-
|
|
405
|
-
|
|
445
|
+
// Fetch balances for multiple chain IDs at once
|
|
446
|
+
const tokenBalancesByChain = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f")
|
|
447
|
+
.getMultiNetworksBalances(selectedAddress, chainIds, supportedNetworks)
|
|
406
448
|
.catch(() => null);
|
|
407
|
-
if (!
|
|
449
|
+
if (!tokenBalancesByChain ||
|
|
450
|
+
Object.keys(tokenBalancesByChain).length === 0) {
|
|
408
451
|
return { result: 'failed' };
|
|
409
452
|
}
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// We need specific data from tokensChainsCache to correctly create a token
|
|
421
|
-
// So even if we have a token that was detected correctly by the API, if its missing data we cannot safely add it.
|
|
422
|
-
if (!__classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data[token.address]) {
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
const { decimals, symbol, aggregators, iconUrl, name } = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data[token.address];
|
|
426
|
-
eventTokensDetails.push(`${symbol} - ${tokenAddress}`);
|
|
427
|
-
tokensWithBalance.push({
|
|
428
|
-
address: tokenAddress,
|
|
429
|
-
decimals,
|
|
430
|
-
symbol,
|
|
431
|
-
aggregators,
|
|
432
|
-
image: iconUrl,
|
|
433
|
-
isERC721: false,
|
|
434
|
-
name,
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
if (tokensWithBalance.length) {
|
|
438
|
-
__classPrivateFieldGet(this, _TokenDetectionController_trackMetaMetricsEvent, "f").call(this, {
|
|
439
|
-
event: 'Token Detected',
|
|
440
|
-
category: 'Wallet',
|
|
441
|
-
properties: {
|
|
442
|
-
tokens: eventTokensDetails,
|
|
443
|
-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
444
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
445
|
-
token_standard: controller_utils_1.ERC20,
|
|
446
|
-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
447
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
448
|
-
asset_type: controller_utils_1.ASSET_TYPES.TOKEN,
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
await this.messagingSystem.call('TokensController:addDetectedTokens', tokensWithBalance, {
|
|
452
|
-
selectedAddress,
|
|
453
|
+
// Process each chain ID individually
|
|
454
|
+
for (const chainId of chainIds) {
|
|
455
|
+
const isTokenDetectionInactiveInMainnet = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
|
|
456
|
+
chainId === controller_utils_1.ChainId.mainnet;
|
|
457
|
+
const { tokensChainsCache } = this.messagingSystem.call('TokenListController:getState');
|
|
458
|
+
__classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, isTokenDetectionInactiveInMainnet
|
|
459
|
+
? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this)
|
|
460
|
+
: tokensChainsCache ?? {}, "f");
|
|
461
|
+
// Generate token candidates based on chainId and selectedAddress
|
|
462
|
+
const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
|
|
453
463
|
chainId,
|
|
464
|
+
selectedAddress,
|
|
454
465
|
});
|
|
466
|
+
// Filter balances for the current chainId
|
|
467
|
+
const tokenBalances = tokenBalancesByChain.filter((balance) => balance.chainId === (0, utils_1.hexToNumber)(chainId));
|
|
468
|
+
if (!tokenBalances || tokenBalances.length === 0) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
// Use helper function to filter tokens with balance for this chainId
|
|
472
|
+
const { tokensWithBalance, eventTokensDetails } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_filterAndBuildTokensWithBalance).call(this, tokenCandidateSlices, tokenBalances, chainId);
|
|
473
|
+
if (tokensWithBalance.length) {
|
|
474
|
+
__classPrivateFieldGet(this, _TokenDetectionController_trackMetaMetricsEvent, "f").call(this, {
|
|
475
|
+
event: 'Token Detected',
|
|
476
|
+
category: 'Wallet',
|
|
477
|
+
properties: {
|
|
478
|
+
tokens: eventTokensDetails,
|
|
479
|
+
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
480
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
481
|
+
token_standard: controller_utils_1.ERC20,
|
|
482
|
+
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
483
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
484
|
+
asset_type: controller_utils_1.ASSET_TYPES.TOKEN,
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
await this.messagingSystem.call('TokensController:addDetectedTokens', tokensWithBalance, {
|
|
488
|
+
selectedAddress,
|
|
489
|
+
chainId,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
455
492
|
}
|
|
456
493
|
return { result: 'success' };
|
|
457
494
|
});
|
|
495
|
+
}, _TokenDetectionController_filterAndBuildTokensWithBalance = function _TokenDetectionController_filterAndBuildTokensWithBalance(tokenCandidateSlices, tokenBalances, chainId) {
|
|
496
|
+
const tokensWithBalance = [];
|
|
497
|
+
const eventTokensDetails = [];
|
|
498
|
+
const tokenCandidateSet = new Set(tokenCandidateSlices.flat());
|
|
499
|
+
tokenBalances?.forEach((token) => {
|
|
500
|
+
const tokenAddress = token.address;
|
|
501
|
+
// Make sure the token to add is in our candidate list
|
|
502
|
+
if (!tokenCandidateSet.has(tokenAddress)) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
// Retrieve token data from cache to safely add it
|
|
506
|
+
const tokenData = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId]?.data[tokenAddress];
|
|
507
|
+
// We need specific data from tokensChainsCache to correctly create a token
|
|
508
|
+
// So even if we have a token that was detected correctly by the API, if its missing data we cannot safely add it.
|
|
509
|
+
if (!tokenData) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const { decimals, symbol, aggregators, iconUrl, name } = tokenData;
|
|
513
|
+
eventTokensDetails.push(`${symbol} - ${tokenAddress}`);
|
|
514
|
+
tokensWithBalance.push({
|
|
515
|
+
address: tokenAddress,
|
|
516
|
+
decimals,
|
|
517
|
+
symbol,
|
|
518
|
+
aggregators,
|
|
519
|
+
image: iconUrl,
|
|
520
|
+
isERC721: false,
|
|
521
|
+
name,
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
return { tokensWithBalance, eventTokensDetails };
|
|
458
525
|
}, _TokenDetectionController_addDetectedTokens = async function _TokenDetectionController_addDetectedTokens({ tokensSlice, selectedAddress, networkClientId, chainId, }) {
|
|
459
526
|
await (0, controller_utils_1.safelyExecute)(async () => {
|
|
460
527
|
const balances = await __classPrivateFieldGet(this, _TokenDetectionController_getBalancesInSingleCall, "f").call(this, selectedAddress, tokensSlice, networkClientId);
|