@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.
Files changed (90) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/dist/AccountTrackerController.cjs +30 -10
  3. package/dist/AccountTrackerController.cjs.map +1 -1
  4. package/dist/AccountTrackerController.d.cts +14 -9
  5. package/dist/AccountTrackerController.d.cts.map +1 -1
  6. package/dist/AccountTrackerController.d.mts +14 -9
  7. package/dist/AccountTrackerController.d.mts.map +1 -1
  8. package/dist/AccountTrackerController.mjs +30 -10
  9. package/dist/AccountTrackerController.mjs.map +1 -1
  10. package/dist/AssetsContractController.cjs +61 -1
  11. package/dist/AssetsContractController.cjs.map +1 -1
  12. package/dist/AssetsContractController.d.cts +13 -0
  13. package/dist/AssetsContractController.d.cts.map +1 -1
  14. package/dist/AssetsContractController.d.mts +13 -0
  15. package/dist/AssetsContractController.d.mts.map +1 -1
  16. package/dist/AssetsContractController.mjs +61 -1
  17. package/dist/AssetsContractController.mjs.map +1 -1
  18. package/dist/CurrencyRateController.d.cts +4 -4
  19. package/dist/CurrencyRateController.d.mts +4 -4
  20. package/dist/NftController.cjs +8 -0
  21. package/dist/NftController.cjs.map +1 -1
  22. package/dist/NftController.d.cts +4 -0
  23. package/dist/NftController.d.cts.map +1 -1
  24. package/dist/NftController.d.mts +4 -0
  25. package/dist/NftController.d.mts.map +1 -1
  26. package/dist/NftController.mjs +8 -0
  27. package/dist/NftController.mjs.map +1 -1
  28. package/dist/TokenBalancesController.cjs +8 -0
  29. package/dist/TokenBalancesController.cjs.map +1 -1
  30. package/dist/TokenBalancesController.d.cts +4 -0
  31. package/dist/TokenBalancesController.d.cts.map +1 -1
  32. package/dist/TokenBalancesController.d.mts +4 -0
  33. package/dist/TokenBalancesController.d.mts.map +1 -1
  34. package/dist/TokenBalancesController.mjs +8 -0
  35. package/dist/TokenBalancesController.mjs.map +1 -1
  36. package/dist/TokenDetectionController.cjs +196 -129
  37. package/dist/TokenDetectionController.cjs.map +1 -1
  38. package/dist/TokenDetectionController.d.cts +23 -12
  39. package/dist/TokenDetectionController.d.cts.map +1 -1
  40. package/dist/TokenDetectionController.d.mts +23 -12
  41. package/dist/TokenDetectionController.d.mts.map +1 -1
  42. package/dist/TokenDetectionController.mjs +195 -129
  43. package/dist/TokenDetectionController.mjs.map +1 -1
  44. package/dist/TokenListController.cjs +60 -53
  45. package/dist/TokenListController.cjs.map +1 -1
  46. package/dist/TokenListController.d.cts +25 -14
  47. package/dist/TokenListController.d.cts.map +1 -1
  48. package/dist/TokenListController.d.mts +25 -14
  49. package/dist/TokenListController.d.mts.map +1 -1
  50. package/dist/TokenListController.mjs +60 -53
  51. package/dist/TokenListController.mjs.map +1 -1
  52. package/dist/TokenRatesController.cjs +43 -13
  53. package/dist/TokenRatesController.cjs.map +1 -1
  54. package/dist/TokenRatesController.d.cts +12 -8
  55. package/dist/TokenRatesController.d.cts.map +1 -1
  56. package/dist/TokenRatesController.d.mts +12 -8
  57. package/dist/TokenRatesController.d.mts.map +1 -1
  58. package/dist/TokenRatesController.mjs +43 -13
  59. package/dist/TokenRatesController.mjs.map +1 -1
  60. package/dist/TokensController.cjs +15 -4
  61. package/dist/TokensController.cjs.map +1 -1
  62. package/dist/TokensController.d.cts +4 -0
  63. package/dist/TokensController.d.cts.map +1 -1
  64. package/dist/TokensController.d.mts +4 -0
  65. package/dist/TokensController.d.mts.map +1 -1
  66. package/dist/TokensController.mjs +15 -4
  67. package/dist/TokensController.mjs.map +1 -1
  68. package/dist/assetsUtil.cjs +13 -1
  69. package/dist/assetsUtil.cjs.map +1 -1
  70. package/dist/assetsUtil.d.cts +7 -0
  71. package/dist/assetsUtil.d.cts.map +1 -1
  72. package/dist/assetsUtil.d.mts +7 -0
  73. package/dist/assetsUtil.d.mts.map +1 -1
  74. package/dist/assetsUtil.mjs +12 -0
  75. package/dist/assetsUtil.mjs.map +1 -1
  76. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs +18 -0
  77. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs.map +1 -1
  78. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.cts.map +1 -1
  79. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.mts.map +1 -1
  80. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs +18 -0
  81. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs.map +1 -1
  82. package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs +7 -2
  83. package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs.map +1 -1
  84. package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts +3 -2
  85. package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts.map +1 -1
  86. package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts +3 -2
  87. package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts.map +1 -1
  88. package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs +7 -2
  89. package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs.map +1 -1
  90. 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 getMultiChainBalances(address, chainId) {
110
- if (!this.isAccountsAPIEnabled) {
111
- throw new Error('Accounts API Feature Switch is disabled');
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}, network: ${chainIdNumber}`);
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: [chainIdNumber],
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({ networkClientId, address, }) {
176
+ async _executePoll({ chainIds, address, }) {
176
177
  if (!this.isActive) {
177
178
  return;
178
179
  }
179
180
  await this.detectTokens({
180
- networkClientId,
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.networkClientId - The ID of the network client to use.
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({ networkClientId, selectedAddress, } = {}) {
193
+ async detectTokens({ chainIds, selectedAddress, } = {}) {
193
194
  if (!this.isActive) {
194
195
  return;
195
196
  }
196
- const addressAgainstWhichToDetect = selectedAddress ?? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
197
- const { chainId, networkClientId: selectedNetworkClientId } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectChainIdAndNetworkClientId).call(this, networkClientId);
198
- const chainIdAgainstWhichToDetect = chainId;
199
- const networkClientIdAgainstWhichToDetect = selectedNetworkClientId;
200
- if (!(0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(chainIdAgainstWhichToDetect)) {
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
- if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
204
- chainIdAgainstWhichToDetect !== controller_utils_1.ChainId.mainnet) {
205
- return;
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
- const isTokenDetectionInactiveInMainnet = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
208
- chainIdAgainstWhichToDetect === controller_utils_1.ChainId.mainnet;
209
- const { tokensChainsCache } = this.messagingSystem.call('TokenListController:getState');
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
- }, _TokenDetectionController_getCorrectChainIdAndNetworkClientId = function _TokenDetectionController_getCorrectChainIdAndNetworkClientId(networkClientId) {
321
- if (networkClientId) {
322
- const networkConfiguration = this.messagingSystem.call('NetworkController:getNetworkConfigurationByNetworkClientId', networkClientId);
323
- if (networkConfiguration) {
324
- return {
325
- chainId: networkConfiguration.chainId,
326
- networkClientId,
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.networkClientId - The ID of the network client to use.
324
+ * @param options.chainIds - The chain IDs of the network client to use.
344
325
  */
345
- async function _TokenDetectionController_restartTokenDetection({ selectedAddress, networkClientId, } = {}) {
326
+ async function _TokenDetectionController_restartTokenDetection({ selectedAddress, chainIds, } = {}) {
346
327
  await this.detectTokens({
347
- networkClientId,
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.chainId - chainId to check tokens for
397
- * @param options.tokenCandidateSlices - these are tokens we know a user does not have (by checking the tokens controller).
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, chainId, tokenCandidateSlices, }) {
443
+ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddress, chainIds, supportedNetworks, }) {
403
444
  return await (0, controller_utils_1.safelyExecute)(async () => {
404
- const tokenBalances = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f")
405
- .getMultiChainBalances(selectedAddress, chainId)
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 (!tokenBalances || tokenBalances.length === 0) {
449
+ if (!tokenBalancesByChain ||
450
+ Object.keys(tokenBalancesByChain).length === 0) {
408
451
  return { result: 'failed' };
409
452
  }
410
- const tokensWithBalance = [];
411
- const eventTokensDetails = [];
412
- const tokenCandidateSet = new Set(tokenCandidateSlices.flat());
413
- tokenBalances.forEach((token) => {
414
- const tokenAddress = token.address;
415
- // Make sure that the token to add is in our candidate list
416
- // Ensures we don't add tokens we already own
417
- if (!tokenCandidateSet.has(token.address)) {
418
- return;
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);