@metamask/assets-controllers 42.0.0 → 43.1.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 (66) hide show
  1. package/CHANGELOG.md +24 -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/RatesController/RatesController.cjs +12 -1
  21. package/dist/RatesController/RatesController.cjs.map +1 -1
  22. package/dist/RatesController/RatesController.d.cts +8 -1
  23. package/dist/RatesController/RatesController.d.cts.map +1 -1
  24. package/dist/RatesController/RatesController.d.mts +8 -1
  25. package/dist/RatesController/RatesController.d.mts.map +1 -1
  26. package/dist/RatesController/RatesController.mjs +12 -1
  27. package/dist/RatesController/RatesController.mjs.map +1 -1
  28. package/dist/TokenDetectionController.cjs +191 -127
  29. package/dist/TokenDetectionController.cjs.map +1 -1
  30. package/dist/TokenDetectionController.d.cts +20 -11
  31. package/dist/TokenDetectionController.d.cts.map +1 -1
  32. package/dist/TokenDetectionController.d.mts +20 -11
  33. package/dist/TokenDetectionController.d.mts.map +1 -1
  34. package/dist/TokenDetectionController.mjs +190 -127
  35. package/dist/TokenDetectionController.mjs.map +1 -1
  36. package/dist/TokenListController.cjs +60 -53
  37. package/dist/TokenListController.cjs.map +1 -1
  38. package/dist/TokenListController.d.cts +25 -14
  39. package/dist/TokenListController.d.cts.map +1 -1
  40. package/dist/TokenListController.d.mts +25 -14
  41. package/dist/TokenListController.d.mts.map +1 -1
  42. package/dist/TokenListController.mjs +60 -53
  43. package/dist/TokenListController.mjs.map +1 -1
  44. package/dist/TokenRatesController.d.cts +4 -4
  45. package/dist/TokenRatesController.d.mts +4 -4
  46. package/dist/TokensController.cjs +7 -4
  47. package/dist/TokensController.cjs.map +1 -1
  48. package/dist/TokensController.d.cts.map +1 -1
  49. package/dist/TokensController.d.mts.map +1 -1
  50. package/dist/TokensController.mjs +7 -4
  51. package/dist/TokensController.mjs.map +1 -1
  52. package/dist/assetsUtil.cjs +13 -1
  53. package/dist/assetsUtil.cjs.map +1 -1
  54. package/dist/assetsUtil.d.cts +7 -0
  55. package/dist/assetsUtil.d.cts.map +1 -1
  56. package/dist/assetsUtil.d.mts +7 -0
  57. package/dist/assetsUtil.d.mts.map +1 -1
  58. package/dist/assetsUtil.mjs +12 -0
  59. package/dist/assetsUtil.mjs.map +1 -1
  60. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs +18 -0
  61. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.cjs.map +1 -1
  62. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.cts.map +1 -1
  63. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.d.mts.map +1 -1
  64. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs +18 -0
  65. package/dist/multi-chain-accounts-service/mocks/mock-get-balances.mjs.map +1 -1
  66. package/package.json +9 -9
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- 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;
12
+ 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;
13
13
  function $importDefault(module) {
14
14
  if (module?.__esModule) {
15
15
  return module.default;
@@ -43,7 +43,7 @@ export const STATIC_MAINNET_TOKEN_LIST = Object.entries(contractMap).reduce((acc
43
43
  * @param tokensChainsCache - TokensChainsCache input object
44
44
  * @returns returns the map of chainId with TokenListMap
45
45
  */
46
- function mapChainIdWithTokenListMap(tokensChainsCache) {
46
+ export function mapChainIdWithTokenListMap(tokensChainsCache) {
47
47
  return mapValues(tokensChainsCache, (value) => {
48
48
  if (isObject(value) && 'data' in value) {
49
49
  return get(value, ['data']);
@@ -110,18 +110,15 @@ export class TokenDetectionController extends StaticIntervalPollingController()
110
110
  this.supportedNetworksCache = result;
111
111
  return result;
112
112
  },
113
- async getMultiChainBalances(address, chainId) {
114
- if (!this.isAccountsAPIEnabled) {
115
- throw new Error('Accounts API Feature Switch is disabled');
116
- }
117
- const chainIdNumber = hexToNumber(chainId);
118
- const supportedNetworks = await this.getSupportedNetworks();
119
- if (!supportedNetworks || !supportedNetworks.includes(chainIdNumber)) {
113
+ async getMultiNetworksBalances(address, chainIds, supportedNetworks) {
114
+ const chainIdNumbers = chainIds.map((chainId) => hexToNumber(chainId));
115
+ if (!supportedNetworks ||
116
+ !chainIdNumbers.every((id) => supportedNetworks.includes(id))) {
120
117
  const supportedNetworksErrStr = (supportedNetworks ?? []).toString();
121
- throw new Error(`Unsupported Network: supported networks ${supportedNetworksErrStr}, network: ${chainIdNumber}`);
118
+ throw new Error(`Unsupported Network: supported networks ${supportedNetworksErrStr}, requested networks: ${chainIdNumbers.toString()}`);
122
119
  }
123
120
  const result = await fetchMultiChainBalances(address, {
124
- networks: [chainIdNumber],
121
+ networks: chainIdNumbers,
125
122
  }, this.platform);
126
123
  return result.balances;
127
124
  },
@@ -177,12 +174,12 @@ export class TokenDetectionController extends StaticIntervalPollingController()
177
174
  this.disable();
178
175
  __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_stopPolling).call(this);
179
176
  }
180
- async _executePoll({ networkClientId, address, }) {
177
+ async _executePoll({ chainIds, address, }) {
181
178
  if (!this.isActive) {
182
179
  return;
183
180
  }
184
181
  await this.detectTokens({
185
- networkClientId,
182
+ chainIds,
186
183
  selectedAddress: address,
187
184
  });
188
185
  }
@@ -191,51 +188,35 @@ export class TokenDetectionController extends StaticIntervalPollingController()
191
188
  * 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.
192
189
  *
193
190
  * @param options - Options for token detection.
194
- * @param options.networkClientId - The ID of the network client to use.
191
+ * @param options.chainIds - The chain IDs of the network client to use.
195
192
  * @param options.selectedAddress - the selectedAddress against which to detect for token balances.
196
193
  */
197
- async detectTokens({ networkClientId, selectedAddress, } = {}) {
194
+ async detectTokens({ chainIds, selectedAddress, } = {}) {
198
195
  if (!this.isActive) {
199
196
  return;
200
197
  }
201
- const addressAgainstWhichToDetect = selectedAddress ?? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
202
- const { chainId, networkClientId: selectedNetworkClientId } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectChainIdAndNetworkClientId).call(this, networkClientId);
203
- const chainIdAgainstWhichToDetect = chainId;
204
- const networkClientIdAgainstWhichToDetect = selectedNetworkClientId;
205
- if (!isTokenDetectionSupportedForNetwork(chainIdAgainstWhichToDetect)) {
206
- return;
198
+ const addressToDetect = selectedAddress ?? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSelectedAddress).call(this);
199
+ const clientNetworks = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectNetworkClientIdByChainId).call(this, chainIds);
200
+ let supportedNetworks;
201
+ if (__classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").isAccountsAPIEnabled) {
202
+ supportedNetworks = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f").getSupportedNetworks();
207
203
  }
208
- if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
209
- chainIdAgainstWhichToDetect !== ChainId.mainnet) {
210
- return;
204
+ const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks);
205
+ // Try detecting tokens via Account API first if conditions allow
206
+ if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) {
207
+ const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks);
208
+ // If API succeeds and no chains are left for RPC detection, we can return early
209
+ if (apiResult?.result === 'success' &&
210
+ chainsToDetectUsingRpc.length === 0) {
211
+ return;
212
+ }
213
+ // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection
214
+ __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks);
211
215
  }
212
- const isTokenDetectionInactiveInMainnet = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
213
- chainIdAgainstWhichToDetect === ChainId.mainnet;
214
- const { tokensChainsCache } = this.messagingSystem.call('TokenListController:getState');
215
- __classPrivateFieldSet(this, _TokenDetectionController_tokensChainsCache, isTokenDetectionInactiveInMainnet
216
- ? __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getConvertedStaticMainnetTokenList).call(this)
217
- : tokensChainsCache ?? {}, "f");
218
- const tokenCandidateSlices = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getSlicesOfTokensToDetect).call(this, {
219
- chainId: chainIdAgainstWhichToDetect,
220
- selectedAddress: addressAgainstWhichToDetect,
221
- });
222
- // Attempt Accounts API Detection
223
- const accountAPIResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokensViaAPI).call(this, {
224
- chainId: chainIdAgainstWhichToDetect,
225
- selectedAddress: addressAgainstWhichToDetect,
226
- tokenCandidateSlices,
227
- });
228
- if (accountAPIResult?.result === 'success') {
229
- return;
216
+ // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc
217
+ if (chainsToDetectUsingRpc.length > 0) {
218
+ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_detectTokensUsingRpc).call(this, chainsToDetectUsingRpc, addressToDetect);
230
219
  }
231
- // Attempt RPC Detection
232
- const tokenDetectionPromises = tokenCandidateSlices.map((tokensSlice) => __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addDetectedTokens).call(this, {
233
- tokensSlice,
234
- selectedAddress: addressAgainstWhichToDetect,
235
- networkClientId: networkClientIdAgainstWhichToDetect,
236
- chainId: chainIdAgainstWhichToDetect,
237
- }));
238
- await Promise.all(tokenDetectionPromises);
239
220
  }
240
221
  }
241
222
  _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_selectedAccountId = new WeakMap(), _TokenDetectionController_networkClientId = new WeakMap(), _TokenDetectionController_tokensChainsCache = new WeakMap(), _TokenDetectionController_disabled = new WeakMap(), _TokenDetectionController_isUnlocked = new WeakMap(), _TokenDetectionController_isDetectionEnabledFromPreferences = new WeakMap(), _TokenDetectionController_isDetectionEnabledForNetwork = new WeakMap(), _TokenDetectionController_getBalancesInSingleCall = new WeakMap(), _TokenDetectionController_trackMetaMetricsEvent = new WeakMap(), _TokenDetectionController_accountsAPI = new WeakMap(), _TokenDetectionController_instances = new WeakSet(), _TokenDetectionController_registerEventListeners = function _TokenDetectionController_registerEventListeners() {
@@ -283,20 +264,6 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
283
264
  });
284
265
  }
285
266
  });
286
- this.messagingSystem.subscribe('NetworkController:networkDidChange',
287
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
288
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
289
- async ({ selectedNetworkClientId }) => {
290
- const isNetworkClientIdChanged = __classPrivateFieldGet(this, _TokenDetectionController_networkClientId, "f") !== selectedNetworkClientId;
291
- const { chainId: newChainId } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getCorrectChainIdAndNetworkClientId).call(this, selectedNetworkClientId);
292
- __classPrivateFieldSet(this, _TokenDetectionController_isDetectionEnabledForNetwork, isTokenDetectionSupportedForNetwork(newChainId), "f");
293
- if (isNetworkClientIdChanged && __classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledForNetwork, "f")) {
294
- __classPrivateFieldSet(this, _TokenDetectionController_networkClientId, selectedNetworkClientId, "f");
295
- await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this, {
296
- networkClientId: __classPrivateFieldGet(this, _TokenDetectionController_networkClientId, "f"),
297
- });
298
- }
299
- });
300
267
  }, _TokenDetectionController_stopPolling = function _TokenDetectionController_stopPolling() {
301
268
  if (__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f")) {
302
269
  clearInterval(__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f"));
@@ -321,16 +288,26 @@ async function _TokenDetectionController_startPolling() {
321
288
  const cleanTokensChainsCache = mapChainIdWithTokenListMap(tokensChainsCache);
322
289
  const isEqualValues = isEqual(cleanTokensChainsCache, cleanPreviousTokensChainsCache);
323
290
  return isEqualValues;
324
- }, _TokenDetectionController_getCorrectChainIdAndNetworkClientId = function _TokenDetectionController_getCorrectChainIdAndNetworkClientId(networkClientId) {
325
- if (networkClientId) {
326
- const networkConfiguration = this.messagingSystem.call('NetworkController:getNetworkConfigurationByNetworkClientId', networkClientId);
327
- if (networkConfiguration) {
328
- return {
329
- chainId: networkConfiguration.chainId,
330
- networkClientId,
331
- };
332
- }
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 ?? ChainId.mainnet,
298
+ networkClientId: selectedNetworkClientId,
299
+ },
300
+ ];
333
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() {
334
311
  const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
335
312
  const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
336
313
  return {
@@ -344,14 +321,76 @@ async function _TokenDetectionController_startPolling() {
344
321
  *
345
322
  * @param options - Options for restart token detection.
346
323
  * @param options.selectedAddress - the selectedAddress against which to detect for token balances
347
- * @param options.networkClientId - The ID of the network client to use.
324
+ * @param options.chainIds - The chain IDs of the network client to use.
348
325
  */
349
- async function _TokenDetectionController_restartTokenDetection({ selectedAddress, networkClientId, } = {}) {
326
+ async function _TokenDetectionController_restartTokenDetection({ selectedAddress, chainIds, } = {}) {
350
327
  await this.detectTokens({
351
- networkClientId,
328
+ chainIds,
352
329
  selectedAddress,
353
330
  });
354
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(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 (!isTokenDetectionSupportedForNetwork(chainId)) {
362
+ return false;
363
+ }
364
+ if (!__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
365
+ chainId !== ChainId.mainnet) {
366
+ return false;
367
+ }
368
+ const isMainnetDetectionInactive = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") && chainId === 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
+ }
355
394
  }, _TokenDetectionController_getSlicesOfTokensToDetect = function _TokenDetectionController_getSlicesOfTokensToDetect({ chainId, selectedAddress, }) {
356
395
  const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messagingSystem.call('TokensController:getState');
357
396
  const [tokensAddresses, detectedTokensAddresses, ignoredTokensAddresses] = [
@@ -397,68 +436,92 @@ async function _TokenDetectionController_restartTokenDetection({ selectedAddress
397
436
  * This adds detected tokens from the Accounts API, avoiding the multi-call RPC calls for balances
398
437
  * @param options - method arguments
399
438
  * @param options.selectedAddress - address to check against
400
- * @param options.chainId - chainId to check tokens for
401
- * @param options.tokenCandidateSlices - these are tokens we know a user does not have (by checking the tokens controller).
402
- * 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.
403
- * 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
404
441
  * @returns a success or failed object
405
442
  */
406
- async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddress, chainId, tokenCandidateSlices, }) {
443
+ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddress, chainIds, supportedNetworks, }) {
407
444
  return await safelyExecute(async () => {
408
- const tokenBalances = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f")
409
- .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)
410
448
  .catch(() => null);
411
- if (!tokenBalances || tokenBalances.length === 0) {
449
+ if (!tokenBalancesByChain ||
450
+ Object.keys(tokenBalancesByChain).length === 0) {
412
451
  return { result: 'failed' };
413
452
  }
414
- const tokensWithBalance = [];
415
- const eventTokensDetails = [];
416
- const tokenCandidateSet = new Set(tokenCandidateSlices.flat());
417
- tokenBalances.forEach((token) => {
418
- const tokenAddress = token.address;
419
- // Make sure that the token to add is in our candidate list
420
- // Ensures we don't add tokens we already own
421
- if (!tokenCandidateSet.has(token.address)) {
422
- return;
423
- }
424
- // We need specific data from tokensChainsCache to correctly create a token
425
- // So even if we have a token that was detected correctly by the API, if its missing data we cannot safely add it.
426
- if (!__classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data[token.address]) {
427
- return;
428
- }
429
- const { decimals, symbol, aggregators, iconUrl, name } = __classPrivateFieldGet(this, _TokenDetectionController_tokensChainsCache, "f")[chainId].data[token.address];
430
- eventTokensDetails.push(`${symbol} - ${tokenAddress}`);
431
- tokensWithBalance.push({
432
- address: tokenAddress,
433
- decimals,
434
- symbol,
435
- aggregators,
436
- image: iconUrl,
437
- isERC721: false,
438
- name,
439
- });
440
- });
441
- if (tokensWithBalance.length) {
442
- __classPrivateFieldGet(this, _TokenDetectionController_trackMetaMetricsEvent, "f").call(this, {
443
- event: 'Token Detected',
444
- category: 'Wallet',
445
- properties: {
446
- tokens: eventTokensDetails,
447
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
448
- // eslint-disable-next-line @typescript-eslint/naming-convention
449
- token_standard: ERC20,
450
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
451
- // eslint-disable-next-line @typescript-eslint/naming-convention
452
- asset_type: ASSET_TYPES.TOKEN,
453
- },
454
- });
455
- await this.messagingSystem.call('TokensController:addDetectedTokens', tokensWithBalance, {
456
- selectedAddress,
453
+ // Process each chain ID individually
454
+ for (const chainId of chainIds) {
455
+ const isTokenDetectionInactiveInMainnet = !__classPrivateFieldGet(this, _TokenDetectionController_isDetectionEnabledFromPreferences, "f") &&
456
+ chainId === 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, {
457
463
  chainId,
464
+ selectedAddress,
458
465
  });
466
+ // Filter balances for the current chainId
467
+ const tokenBalances = tokenBalancesByChain.filter((balance) => balance.chainId === 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: 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: ASSET_TYPES.TOKEN,
485
+ },
486
+ });
487
+ await this.messagingSystem.call('TokensController:addDetectedTokens', tokensWithBalance, {
488
+ selectedAddress,
489
+ chainId,
490
+ });
491
+ }
459
492
  }
460
493
  return { result: 'success' };
461
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 };
462
525
  }, _TokenDetectionController_addDetectedTokens = async function _TokenDetectionController_addDetectedTokens({ tokensSlice, selectedAddress, networkClientId, chainId, }) {
463
526
  await safelyExecute(async () => {
464
527
  const balances = await __classPrivateFieldGet(this, _TokenDetectionController_getBalancesInSingleCall, "f").call(this, selectedAddress, tokensSlice, networkClientId);