@metamask-previews/assets-controller 4.0.0-preview-0bc2ac9 → 4.0.0-preview-0fc9da15f

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 (62) hide show
  1. package/CHANGELOG.md +0 -4
  2. package/dist/AssetsController.cjs +1 -9
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +2 -1
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +2 -1
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +1 -9
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/RpcDataSource.cjs +59 -26
  11. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  12. package/dist/data-sources/RpcDataSource.d.cts +2 -1
  13. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  14. package/dist/data-sources/RpcDataSource.d.mts +2 -1
  15. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  16. package/dist/data-sources/RpcDataSource.mjs +61 -28
  17. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  18. package/dist/data-sources/StakedBalanceDataSource.cjs +2 -9
  19. package/dist/data-sources/StakedBalanceDataSource.cjs.map +1 -1
  20. package/dist/data-sources/StakedBalanceDataSource.d.cts.map +1 -1
  21. package/dist/data-sources/StakedBalanceDataSource.d.mts.map +1 -1
  22. package/dist/data-sources/StakedBalanceDataSource.mjs +2 -9
  23. package/dist/data-sources/StakedBalanceDataSource.mjs.map +1 -1
  24. package/dist/data-sources/evm-rpc-services/clients/index.cjs +1 -3
  25. package/dist/data-sources/evm-rpc-services/clients/index.cjs.map +1 -1
  26. package/dist/data-sources/evm-rpc-services/clients/index.d.cts +0 -1
  27. package/dist/data-sources/evm-rpc-services/clients/index.d.cts.map +1 -1
  28. package/dist/data-sources/evm-rpc-services/clients/index.d.mts +0 -1
  29. package/dist/data-sources/evm-rpc-services/clients/index.d.mts.map +1 -1
  30. package/dist/data-sources/evm-rpc-services/clients/index.mjs +0 -1
  31. package/dist/data-sources/evm-rpc-services/clients/index.mjs.map +1 -1
  32. package/dist/data-sources/evm-rpc-services/index.cjs +1 -2
  33. package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -1
  34. package/dist/data-sources/evm-rpc-services/index.d.cts +1 -1
  35. package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -1
  36. package/dist/data-sources/evm-rpc-services/index.d.mts +1 -1
  37. package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -1
  38. package/dist/data-sources/evm-rpc-services/index.mjs +1 -1
  39. package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -1
  40. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +50 -47
  41. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
  42. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +9 -12
  43. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -1
  44. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +9 -12
  45. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
  46. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +50 -47
  47. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -1
  48. package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -1
  49. package/dist/data-sources/evm-rpc-services/services/index.d.cts +1 -1
  50. package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -1
  51. package/dist/data-sources/evm-rpc-services/services/index.d.mts +1 -1
  52. package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -1
  53. package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -1
  54. package/package.json +1 -1
  55. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs +0 -67
  56. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs.map +0 -1
  57. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts +0 -23
  58. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts.map +0 -1
  59. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts +0 -23
  60. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts.map +0 -1
  61. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs +0 -63
  62. package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs.map +0 -1
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.isStakingContractAssetId = exports.getStakingContractAddress = exports.getSupportedStakingChainIds = exports.StakedBalanceFetcher = exports.TokenDetector = exports.BalanceFetcher = exports.TokensApiClient = exports.MulticallClient = void 0;
3
+ exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.isStakingContractAssetId = exports.getStakingContractAddress = exports.getSupportedStakingChainIds = exports.StakedBalanceFetcher = exports.TokenDetector = exports.BalanceFetcher = exports.MulticallClient = void 0;
4
4
  var clients_1 = require("./clients/index.cjs");
5
5
  Object.defineProperty(exports, "MulticallClient", { enumerable: true, get: function () { return clients_1.MulticallClient; } });
6
- Object.defineProperty(exports, "TokensApiClient", { enumerable: true, get: function () { return clients_1.TokensApiClient; } });
7
6
  var services_1 = require("./services/index.cjs");
8
7
  Object.defineProperty(exports, "BalanceFetcher", { enumerable: true, get: function () { return services_1.BalanceFetcher; } });
9
8
  Object.defineProperty(exports, "TokenDetector", { enumerable: true, get: function () { return services_1.TokenDetector; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":";;;AAaA,+CAKmB;AAJjB,0GAAA,eAAe,OAAA;AAEf,0GAAA,eAAe,OAAA;AAGjB,iDAcoB;AAblB,0GAAA,cAAc,OAAA;AACd,yGAAA,aAAa,OAAA;AACb,gHAAA,oBAAoB,OAAA;AACpB,uHAAA,2BAA2B,OAAA;AAC3B,qHAAA,yBAAyB,OAAA;AACzB,oHAAA,wBAAwB,OAAA;AAS1B,2CAAqE;AAA5D,0GAAA,iBAAiB,OAAA;AAAE,gHAAA,uBAAuB,OAAA","sourcesContent":["export type {\n Address,\n AssetFetchEntry,\n AssetsBalanceState,\n ChainId,\n GetProviderFunction,\n Provider,\n BalanceOfRequest,\n BalanceOfResponse,\n TokenListState,\n BalanceFetchResult,\n TokenDetectionResult,\n} from './types';\nexport {\n MulticallClient,\n type MulticallClientConfig,\n TokensApiClient,\n type TokensApiClientConfig,\n} from './clients';\nexport {\n BalanceFetcher,\n TokenDetector,\n StakedBalanceFetcher,\n getSupportedStakingChainIds,\n getStakingContractAddress,\n isStakingContractAssetId,\n type BalancePollingInput,\n type DetectionPollingInput,\n type StakedBalancePollingInput,\n type StakedBalanceFetchResult,\n type OnBalanceUpdateCallback,\n type OnDetectionUpdateCallback,\n type OnStakedBalanceUpdateCallback,\n} from './services';\nexport { divideIntoBatches, reduceInBatchesSerially } from './utils';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":";;;AAaA,+CAAwE;AAA/D,0GAAA,eAAe,OAAA;AACxB,iDAcoB;AAblB,0GAAA,cAAc,OAAA;AACd,yGAAA,aAAa,OAAA;AACb,gHAAA,oBAAoB,OAAA;AACpB,uHAAA,2BAA2B,OAAA;AAC3B,qHAAA,yBAAyB,OAAA;AACzB,oHAAA,wBAAwB,OAAA;AAS1B,2CAAqE;AAA5D,0GAAA,iBAAiB,OAAA;AAAE,gHAAA,uBAAuB,OAAA","sourcesContent":["export type {\n Address,\n AssetFetchEntry,\n AssetsBalanceState,\n ChainId,\n GetProviderFunction,\n Provider,\n BalanceOfRequest,\n BalanceOfResponse,\n TokenListState,\n BalanceFetchResult,\n TokenDetectionResult,\n} from './types';\nexport { MulticallClient, type MulticallClientConfig } from './clients';\nexport {\n BalanceFetcher,\n TokenDetector,\n StakedBalanceFetcher,\n getSupportedStakingChainIds,\n getStakingContractAddress,\n isStakingContractAssetId,\n type BalancePollingInput,\n type DetectionPollingInput,\n type StakedBalancePollingInput,\n type StakedBalanceFetchResult,\n type OnBalanceUpdateCallback,\n type OnDetectionUpdateCallback,\n type OnStakedBalanceUpdateCallback,\n} from './services';\nexport { divideIntoBatches, reduceInBatchesSerially } from './utils';\n"]}
@@ -1,5 +1,5 @@
1
1
  export type { Address, AssetFetchEntry, AssetsBalanceState, ChainId, GetProviderFunction, Provider, BalanceOfRequest, BalanceOfResponse, TokenListState, BalanceFetchResult, TokenDetectionResult, } from "./types/index.cjs";
2
- export { MulticallClient, type MulticallClientConfig, TokensApiClient, type TokensApiClientConfig, } from "./clients/index.cjs";
2
+ export { MulticallClient, type MulticallClientConfig } from "./clients/index.cjs";
3
3
  export { BalanceFetcher, TokenDetector, StakedBalanceFetcher, getSupportedStakingChainIds, getStakingContractAddress, isStakingContractAssetId, type BalancePollingInput, type DetectionPollingInput, type StakedBalancePollingInput, type StakedBalanceFetchResult, type OnBalanceUpdateCallback, type OnDetectionUpdateCallback, type OnStakedBalanceUpdateCallback, } from "./services/index.cjs";
4
4
  export { divideIntoBatches, reduceInBatchesSerially } from "./utils/index.cjs";
5
5
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,OAAO,EACP,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,0BAAgB;AACjB,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,eAAe,EACf,KAAK,qBAAqB,GAC3B,4BAAkB;AACnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,GACnC,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,OAAO,EACP,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,0BAAgB;AACjB,OAAO,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,4BAAkB;AACxE,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,GACnC,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB"}
@@ -1,5 +1,5 @@
1
1
  export type { Address, AssetFetchEntry, AssetsBalanceState, ChainId, GetProviderFunction, Provider, BalanceOfRequest, BalanceOfResponse, TokenListState, BalanceFetchResult, TokenDetectionResult, } from "./types/index.mjs";
2
- export { MulticallClient, type MulticallClientConfig, TokensApiClient, type TokensApiClientConfig, } from "./clients/index.mjs";
2
+ export { MulticallClient, type MulticallClientConfig } from "./clients/index.mjs";
3
3
  export { BalanceFetcher, TokenDetector, StakedBalanceFetcher, getSupportedStakingChainIds, getStakingContractAddress, isStakingContractAssetId, type BalancePollingInput, type DetectionPollingInput, type StakedBalancePollingInput, type StakedBalanceFetchResult, type OnBalanceUpdateCallback, type OnDetectionUpdateCallback, type OnStakedBalanceUpdateCallback, } from "./services/index.mjs";
4
4
  export { divideIntoBatches, reduceInBatchesSerially } from "./utils/index.mjs";
5
5
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,OAAO,EACP,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,0BAAgB;AACjB,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,eAAe,EACf,KAAK,qBAAqB,GAC3B,4BAAkB;AACnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,GACnC,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,OAAO,EACP,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,0BAAgB;AACjB,OAAO,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,4BAAkB;AACxE,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,GACnC,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB"}
@@ -1,4 +1,4 @@
1
- export { MulticallClient, TokensApiClient } from "./clients/index.mjs";
1
+ export { MulticallClient } from "./clients/index.mjs";
2
2
  export { BalanceFetcher, TokenDetector, StakedBalanceFetcher, getSupportedStakingChainIds, getStakingContractAddress, isStakingContractAssetId } from "./services/index.mjs";
3
3
  export { divideIntoBatches, reduceInBatchesSerially } from "./utils/index.mjs";
4
4
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,eAAe,EAEf,eAAe,EAEhB,4BAAkB;AACnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EAQzB,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB","sourcesContent":["export type {\n Address,\n AssetFetchEntry,\n AssetsBalanceState,\n ChainId,\n GetProviderFunction,\n Provider,\n BalanceOfRequest,\n BalanceOfResponse,\n TokenListState,\n BalanceFetchResult,\n TokenDetectionResult,\n} from './types';\nexport {\n MulticallClient,\n type MulticallClientConfig,\n TokensApiClient,\n type TokensApiClientConfig,\n} from './clients';\nexport {\n BalanceFetcher,\n TokenDetector,\n StakedBalanceFetcher,\n getSupportedStakingChainIds,\n getStakingContractAddress,\n isStakingContractAssetId,\n type BalancePollingInput,\n type DetectionPollingInput,\n type StakedBalancePollingInput,\n type StakedBalanceFetchResult,\n type OnBalanceUpdateCallback,\n type OnDetectionUpdateCallback,\n type OnStakedBalanceUpdateCallback,\n} from './services';\nexport { divideIntoBatches, reduceInBatchesSerially } from './utils';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/data-sources/evm-rpc-services/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAA8B,4BAAkB;AACxE,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,EAQzB,6BAAmB;AACpB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,0BAAgB","sourcesContent":["export type {\n Address,\n AssetFetchEntry,\n AssetsBalanceState,\n ChainId,\n GetProviderFunction,\n Provider,\n BalanceOfRequest,\n BalanceOfResponse,\n TokenListState,\n BalanceFetchResult,\n TokenDetectionResult,\n} from './types';\nexport { MulticallClient, type MulticallClientConfig } from './clients';\nexport {\n BalanceFetcher,\n TokenDetector,\n StakedBalanceFetcher,\n getSupportedStakingChainIds,\n getStakingContractAddress,\n isStakingContractAssetId,\n type BalancePollingInput,\n type DetectionPollingInput,\n type StakedBalancePollingInput,\n type StakedBalanceFetchResult,\n type OnBalanceUpdateCallback,\n type OnDetectionUpdateCallback,\n type OnStakedBalanceUpdateCallback,\n} from './services';\nexport { divideIntoBatches, reduceInBatchesSerially } from './utils';\n"]}
@@ -10,36 +10,33 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  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");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _TokenDetector_instances, _TokenDetector_multicallClient, _TokenDetector_tokensApiClient, _TokenDetector_config, _TokenDetector_tokenListCache, _TokenDetector_onDetectionUpdate, _TokenDetector_fetchAndCacheTokenList, _TokenDetector_processBalanceResponses, _TokenDetector_formatBalance, _TokenDetector_getTokenMetadata, _TokenDetector_createAsset;
13
+ var _TokenDetector_instances, _TokenDetector_multicallClient, _TokenDetector_messenger, _TokenDetector_config, _TokenDetector_onDetectionUpdate, _TokenDetector_processBalanceResponses, _TokenDetector_formatBalance, _TokenDetector_getTokenMetadata, _TokenDetector_createAsset;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.TokenDetector = void 0;
16
16
  const polling_controller_1 = require("@metamask/polling-controller");
17
- const logger_1 = require("../../../logger.cjs");
18
17
  const utils_1 = require("../utils/index.cjs");
19
- const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, 'TokenDetector');
20
18
  const DEFAULT_DETECTION_INTERVAL = 180000; // 3 minutes
21
19
  /**
22
20
  * TokenDetector - Detects tokens with non-zero balances via multicall.
23
- * Fetches the token list from the Tokens API and uses multicall to check balances.
24
21
  * Extends StaticIntervalPollingControllerOnly for built-in polling support.
25
22
  */
26
23
  class TokenDetector extends (0, polling_controller_1.StaticIntervalPollingControllerOnly)() {
27
- constructor(multicallClient, tokensApiClient, config) {
24
+ constructor(multicallClient, messenger, config) {
28
25
  super();
29
26
  _TokenDetector_instances.add(this);
30
27
  _TokenDetector_multicallClient.set(this, void 0);
31
- _TokenDetector_tokensApiClient.set(this, void 0);
28
+ _TokenDetector_messenger.set(this, void 0);
32
29
  _TokenDetector_config.set(this, void 0);
33
- _TokenDetector_tokenListCache.set(this, new Map());
34
30
  _TokenDetector_onDetectionUpdate.set(this, void 0);
35
31
  __classPrivateFieldSet(this, _TokenDetector_multicallClient, multicallClient, "f");
36
- __classPrivateFieldSet(this, _TokenDetector_tokensApiClient, tokensApiClient, "f");
32
+ __classPrivateFieldSet(this, _TokenDetector_messenger, messenger, "f");
37
33
  __classPrivateFieldSet(this, _TokenDetector_config, {
38
34
  tokenDetectionEnabled: config?.tokenDetectionEnabled ?? (() => true),
39
35
  useExternalService: config?.useExternalService ?? (() => true),
40
36
  defaultBatchSize: config?.defaultBatchSize ?? 300,
41
37
  defaultTimeoutMs: config?.defaultTimeoutMs ?? 30000,
42
38
  }, "f");
39
+ // Set the polling interval
43
40
  this.setIntervalLength(config?.pollingInterval ?? DEFAULT_DETECTION_INTERVAL);
44
41
  }
45
42
  /**
@@ -57,26 +54,35 @@ class TokenDetector extends (0, polling_controller_1.StaticIntervalPollingContro
57
54
  * @param input - The polling input.
58
55
  */
59
56
  async _executePoll(input) {
60
- try {
61
- const result = await this.detectTokens(input.chainId, input.accountId, input.accountAddress);
62
- if (__classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f") && result.detectedAssets.length > 0) {
63
- __classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f").call(this, result);
64
- }
57
+ // Check if token list is available for this chain
58
+ const tokensToCheck = this.getTokensToCheck(input.chainId);
59
+ if (tokensToCheck.length === 0) {
60
+ // No tokens in list for chain, will retry on next poll
61
+ return;
65
62
  }
66
- catch (error) {
67
- log('Token detection poll failed', { chainId: input.chainId, error });
63
+ const result = await this.detectTokens(input.chainId, input.accountId, input.accountAddress);
64
+ if (__classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f") && result.detectedAssets.length > 0) {
65
+ __classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f").call(this, result);
68
66
  }
69
67
  }
70
- /**
71
- * Fetch the list of token addresses to check for the given chain.
72
- * Calls the Tokens API and caches the result for metadata lookups.
73
- *
74
- * @param chainId - Chain ID in hex format.
75
- * @returns Array of token contract addresses.
76
- */
77
- async getTokensToCheck(chainId) {
78
- const tokenList = await __classPrivateFieldGet(this, _TokenDetector_instances, "m", _TokenDetector_fetchAndCacheTokenList).call(this, chainId);
79
- return tokenList.map((entry) => entry.address);
68
+ getTokensToCheck(chainId) {
69
+ const tokenListState = __classPrivateFieldGet(this, _TokenDetector_messenger, "f").call('TokenListController:getState');
70
+ // Defensive check for tokensChainsCache
71
+ if (!tokenListState?.tokensChainsCache) {
72
+ return [];
73
+ }
74
+ // Try direct lookup first
75
+ let chainCacheEntry = tokenListState.tokensChainsCache[chainId];
76
+ // If not found, try normalizing the chain ID (e.g., 0x0a -> 0xa)
77
+ if (!chainCacheEntry) {
78
+ const normalizedChainId = `0x${parseInt(chainId, 16).toString(16)}`;
79
+ chainCacheEntry = tokenListState.tokensChainsCache[normalizedChainId];
80
+ }
81
+ const chainTokenList = chainCacheEntry?.data;
82
+ if (!chainTokenList) {
83
+ return [];
84
+ }
85
+ return Object.keys(chainTokenList);
80
86
  }
81
87
  async detectTokens(chainId, accountId, accountAddress, options) {
82
88
  const tokenDetectionEnabled = options?.tokenDetectionEnabled ?? __classPrivateFieldGet(this, _TokenDetector_config, "f").tokenDetectionEnabled();
@@ -95,7 +101,7 @@ class TokenDetector extends (0, polling_controller_1.StaticIntervalPollingContro
95
101
  }
96
102
  const batchSize = options?.batchSize ?? __classPrivateFieldGet(this, _TokenDetector_config, "f").defaultBatchSize;
97
103
  const timestamp = Date.now();
98
- const tokensToCheck = await this.getTokensToCheck(chainId);
104
+ const tokensToCheck = this.getTokensToCheck(chainId);
99
105
  if (tokensToCheck.length === 0) {
100
106
  return {
101
107
  chainId,
@@ -136,22 +142,7 @@ class TokenDetector extends (0, polling_controller_1.StaticIntervalPollingContro
136
142
  }
137
143
  }
138
144
  exports.TokenDetector = TokenDetector;
139
- _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_tokensApiClient = new WeakMap(), _TokenDetector_config = new WeakMap(), _TokenDetector_tokenListCache = new WeakMap(), _TokenDetector_onDetectionUpdate = new WeakMap(), _TokenDetector_instances = new WeakSet(), _TokenDetector_fetchAndCacheTokenList = async function _TokenDetector_fetchAndCacheTokenList(chainId) {
140
- try {
141
- const list = await __classPrivateFieldGet(this, _TokenDetector_tokensApiClient, "f").fetchTokenList(chainId);
142
- __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").set(chainId, list);
143
- return list;
144
- }
145
- catch (error) {
146
- const cached = __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").get(chainId);
147
- log('Failed to fetch token list; using stale cache', {
148
- chainId,
149
- cachedCount: cached?.length ?? 0,
150
- error,
151
- });
152
- return cached ?? [];
153
- }
154
- }, _TokenDetector_processBalanceResponses = function _TokenDetector_processBalanceResponses(responses, accumulator, chainId, accountId, timestamp) {
145
+ _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_messenger = new WeakMap(), _TokenDetector_config = new WeakMap(), _TokenDetector_onDetectionUpdate = new WeakMap(), _TokenDetector_instances = new WeakSet(), _TokenDetector_processBalanceResponses = function _TokenDetector_processBalanceResponses(responses, accumulator, chainId, accountId, timestamp) {
155
146
  const { detectedAssets, detectedBalances, zeroBalanceAddresses, failedAddresses, } = accumulator;
156
147
  for (const response of responses) {
157
148
  if (!response.success) {
@@ -204,13 +195,25 @@ _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_tokensApiClient =
204
195
  return rawBalance;
205
196
  }
206
197
  }, _TokenDetector_getTokenMetadata = function _TokenDetector_getTokenMetadata(chainId, tokenAddress) {
207
- const list = __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").get(chainId) ?? [];
198
+ const tokenListState = __classPrivateFieldGet(this, _TokenDetector_messenger, "f").call('TokenListController:getState');
199
+ if (!tokenListState?.tokensChainsCache) {
200
+ return undefined;
201
+ }
202
+ const chainCacheEntry = tokenListState.tokensChainsCache[chainId];
203
+ const chainTokenList = chainCacheEntry?.data;
204
+ if (!chainTokenList) {
205
+ return undefined;
206
+ }
207
+ if (chainTokenList[tokenAddress]) {
208
+ return chainTokenList[tokenAddress];
209
+ }
208
210
  const lowerAddress = tokenAddress.toLowerCase();
209
- const exact = list.find((entry) => entry.address === tokenAddress);
210
- if (exact) {
211
- return exact;
211
+ for (const [address, metadata] of Object.entries(chainTokenList)) {
212
+ if (address.toLowerCase() === lowerAddress) {
213
+ return metadata;
214
+ }
212
215
  }
213
- return list.find((entry) => entry.address.toLowerCase() === lowerAddress);
216
+ return undefined;
214
217
  }, _TokenDetector_createAsset = function _TokenDetector_createAsset(chainId, tokenAddress, metadata) {
215
218
  const chainIdDecimal = parseInt(chainId, 16);
216
219
  const assetId = `eip155:${chainIdDecimal}/erc20:${tokenAddress.toLowerCase()}`;
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetector.cjs","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"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","sourcesContent":["import { StaticIntervalPollingControllerOnly } from '@metamask/polling-controller';\nimport type { CaipAssetType } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from '../../../logger';\nimport type { MulticallClient } from '../clients';\nimport type { TokensApiClient } from '../clients/TokensApiClient';\nimport type {\n AccountId,\n Address,\n Asset,\n AssetBalance,\n BalanceOfRequest,\n BalanceOfResponse,\n ChainId,\n TokenDetectionOptions,\n TokenDetectionResult,\n TokenListEntry,\n} from '../types';\nimport { reduceInBatchesSerially } from '../utils';\n\nconst log = createModuleLogger(projectLogger, 'TokenDetector');\n\nconst DEFAULT_DETECTION_INTERVAL = 180_000; // 3 minutes\n\nexport type TokenDetectorConfig = {\n /** Function returning whether token detection is enabled (avoids stale value) */\n tokenDetectionEnabled?: () => boolean;\n /** Function returning whether external services are allowed (avoids stale value; default: () => true) */\n useExternalService?: () => boolean;\n defaultBatchSize?: number;\n defaultTimeoutMs?: number;\n /** Polling interval in ms (default: 3 minutes) */\n pollingInterval?: number;\n};\n\n/**\n * Polling input for TokenDetector - identifies what to poll for.\n */\nexport type DetectionPollingInput = {\n /** Chain ID (hex format) */\n chainId: ChainId;\n /** Account ID */\n accountId: AccountId;\n /** Account address */\n accountAddress: Address;\n};\n\n/**\n * Callback type for token detection updates.\n */\nexport type OnDetectionUpdateCallback = (result: TokenDetectionResult) => void;\n\n/**\n * TokenDetector - Detects tokens with non-zero balances via multicall.\n * Fetches the token list from the Tokens API and uses multicall to check balances.\n * Extends StaticIntervalPollingControllerOnly for built-in polling support.\n */\nexport class TokenDetector extends StaticIntervalPollingControllerOnly<DetectionPollingInput>() {\n readonly #multicallClient: MulticallClient;\n\n readonly #tokensApiClient: TokensApiClient;\n\n readonly #config: Required<Omit<TokenDetectorConfig, 'pollingInterval'>>;\n\n readonly #tokenListCache: Map<ChainId, TokenListEntry[]> = new Map();\n\n #onDetectionUpdate: OnDetectionUpdateCallback | undefined;\n\n constructor(\n multicallClient: MulticallClient,\n tokensApiClient: TokensApiClient,\n config?: TokenDetectorConfig,\n ) {\n super();\n this.#multicallClient = multicallClient;\n this.#tokensApiClient = tokensApiClient;\n this.#config = {\n tokenDetectionEnabled:\n config?.tokenDetectionEnabled ?? ((): boolean => true),\n useExternalService: config?.useExternalService ?? ((): boolean => true),\n defaultBatchSize: config?.defaultBatchSize ?? 300,\n defaultTimeoutMs: config?.defaultTimeoutMs ?? 30000,\n };\n\n this.setIntervalLength(\n config?.pollingInterval ?? DEFAULT_DETECTION_INTERVAL,\n );\n }\n\n /**\n * Set the callback to receive detection updates during polling.\n *\n * @param callback - Function to call with detection results.\n */\n setOnDetectionUpdate(callback: OnDetectionUpdateCallback): void {\n this.#onDetectionUpdate = callback;\n }\n\n /**\n * Execute a poll cycle (required by base class).\n * Detects tokens and calls the update callback.\n *\n * @param input - The polling input.\n */\n async _executePoll(input: DetectionPollingInput): Promise<void> {\n try {\n const result = await this.detectTokens(\n input.chainId,\n input.accountId,\n input.accountAddress,\n );\n\n if (this.#onDetectionUpdate && result.detectedAssets.length > 0) {\n this.#onDetectionUpdate(result);\n }\n } catch (error) {\n log('Token detection poll failed', { chainId: input.chainId, error });\n }\n }\n\n /**\n * Fetch the list of token addresses to check for the given chain.\n * Calls the Tokens API and caches the result for metadata lookups.\n *\n * @param chainId - Chain ID in hex format.\n * @returns Array of token contract addresses.\n */\n async getTokensToCheck(chainId: ChainId): Promise<Address[]> {\n const tokenList = await this.#fetchAndCacheTokenList(chainId);\n return tokenList.map((entry) => entry.address as Address);\n }\n\n async detectTokens(\n chainId: ChainId,\n accountId: AccountId,\n accountAddress: Address,\n options?: TokenDetectionOptions,\n ): Promise<TokenDetectionResult> {\n const tokenDetectionEnabled =\n options?.tokenDetectionEnabled ?? this.#config.tokenDetectionEnabled();\n const useExternalService =\n options?.useExternalService ?? this.#config.useExternalService();\n if (!tokenDetectionEnabled || !useExternalService) {\n return {\n chainId,\n accountId,\n accountAddress,\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n timestamp: Date.now(),\n };\n }\n const batchSize = options?.batchSize ?? this.#config.defaultBatchSize;\n const timestamp = Date.now();\n\n const tokensToCheck = await this.getTokensToCheck(chainId);\n\n if (tokensToCheck.length === 0) {\n return {\n chainId,\n accountId,\n accountAddress,\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n timestamp,\n };\n }\n\n const balanceRequests: BalanceOfRequest[] = tokensToCheck.map(\n (tokenAddress) => ({\n tokenAddress,\n accountAddress,\n }),\n );\n\n type DetectionAccumulator = {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n };\n\n const result = await reduceInBatchesSerially<\n BalanceOfRequest,\n DetectionAccumulator\n >({\n values: balanceRequests,\n batchSize,\n initialResult: {\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n },\n eachBatch: async (workingResult, batch) => {\n const responses = await this.#multicallClient.batchBalanceOf(\n chainId,\n batch,\n );\n\n return this.#processBalanceResponses(\n responses,\n workingResult as DetectionAccumulator,\n chainId,\n accountId,\n timestamp,\n );\n },\n });\n\n return {\n chainId,\n accountId,\n accountAddress,\n ...result,\n timestamp,\n };\n }\n\n async #fetchAndCacheTokenList(chainId: ChainId): Promise<TokenListEntry[]> {\n try {\n const list = await this.#tokensApiClient.fetchTokenList(chainId);\n this.#tokenListCache.set(chainId, list);\n return list;\n } catch (error) {\n const cached = this.#tokenListCache.get(chainId);\n log('Failed to fetch token list; using stale cache', {\n chainId,\n cachedCount: cached?.length ?? 0,\n error,\n });\n return cached ?? [];\n }\n }\n\n #processBalanceResponses(\n responses: BalanceOfResponse[],\n accumulator: {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n },\n chainId: ChainId,\n accountId: AccountId,\n timestamp: number,\n ): {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n } {\n const {\n detectedAssets,\n detectedBalances,\n zeroBalanceAddresses,\n failedAddresses,\n } = accumulator;\n\n for (const response of responses) {\n if (!response.success) {\n failedAddresses.push(response.tokenAddress);\n continue;\n }\n\n const balance = response.balance ?? '0';\n\n if (balance === '0' || balance === '') {\n zeroBalanceAddresses.push(response.tokenAddress);\n continue;\n }\n\n const tokenMetadata = this.#getTokenMetadata(\n chainId,\n response.tokenAddress,\n );\n\n const asset = this.#createAsset(\n chainId,\n response.tokenAddress,\n tokenMetadata,\n );\n detectedAssets.push(asset);\n\n if (tokenMetadata?.decimals === undefined) {\n continue;\n }\n\n const { decimals } = tokenMetadata;\n const formattedBalance = this.#formatBalance(balance, decimals);\n\n detectedBalances.push({\n assetId: asset.assetId,\n accountId,\n chainId,\n balance,\n formattedBalance,\n decimals,\n timestamp,\n });\n }\n\n return {\n detectedAssets,\n detectedBalances,\n zeroBalanceAddresses,\n failedAddresses,\n };\n }\n\n #formatBalance(rawBalance: string, decimals: number): string {\n try {\n const balanceBigInt = BigInt(rawBalance);\n const divisor = BigInt(10 ** decimals);\n\n const integerPart = balanceBigInt / divisor;\n const remainder = balanceBigInt % divisor;\n const fractionalStr = remainder.toString().padStart(decimals, '0');\n const trimmedFractional = fractionalStr.replace(/0+$/u, '');\n\n if (trimmedFractional === '') {\n return integerPart.toString();\n }\n\n return `${integerPart}.${trimmedFractional}`;\n } catch {\n return rawBalance;\n }\n }\n\n #getTokenMetadata(\n chainId: ChainId,\n tokenAddress: Address,\n ): TokenListEntry | undefined {\n const list = this.#tokenListCache.get(chainId) ?? [];\n const lowerAddress = tokenAddress.toLowerCase();\n\n const exact = list.find((entry) => entry.address === tokenAddress);\n if (exact) {\n return exact;\n }\n\n return list.find((entry) => entry.address.toLowerCase() === lowerAddress);\n }\n\n #createAsset(\n chainId: ChainId,\n tokenAddress: Address,\n metadata: TokenListEntry | undefined,\n ): Asset {\n const chainIdDecimal = parseInt(chainId, 16);\n\n const assetId =\n `eip155:${chainIdDecimal}/erc20:${tokenAddress.toLowerCase()}` as CaipAssetType;\n\n return {\n assetId,\n chainId,\n address: tokenAddress,\n type: 'erc20',\n symbol: metadata?.symbol,\n name: metadata?.name,\n decimals: metadata?.decimals,\n image: metadata?.iconUrl,\n isNative: false,\n aggregators: metadata?.aggregators,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"TokenDetector.cjs","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"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","sourcesContent":["import { StaticIntervalPollingControllerOnly } from '@metamask/polling-controller';\nimport type { CaipAssetType } from '@metamask/utils';\n\nimport type { MulticallClient } from '../clients';\nimport type {\n AccountId,\n Address,\n Asset,\n AssetBalance,\n BalanceOfRequest,\n BalanceOfResponse,\n ChainId,\n TokenDetectionOptions,\n TokenDetectionResult,\n TokenListEntry,\n TokenListState,\n} from '../types';\nimport { reduceInBatchesSerially } from '../utils';\n\nconst DEFAULT_DETECTION_INTERVAL = 180_000; // 3 minutes\n\n/**\n * Minimal messenger interface for TokenDetector.\n */\nexport type TokenDetectorMessenger = {\n call: (action: 'TokenListController:getState') => TokenListState;\n};\n\nexport type TokenDetectorConfig = {\n /** Function returning whether token detection is enabled (avoids stale value) */\n tokenDetectionEnabled?: () => boolean;\n /** Function returning whether external services are allowed (avoids stale value; default: () => true) */\n useExternalService?: () => boolean;\n defaultBatchSize?: number;\n defaultTimeoutMs?: number;\n /** Polling interval in ms (default: 3 minutes) */\n pollingInterval?: number;\n};\n\n/**\n * Polling input for TokenDetector - identifies what to poll for.\n */\nexport type DetectionPollingInput = {\n /** Chain ID (hex format) */\n chainId: ChainId;\n /** Account ID */\n accountId: AccountId;\n /** Account address */\n accountAddress: Address;\n};\n\n/**\n * Callback type for token detection updates.\n */\nexport type OnDetectionUpdateCallback = (result: TokenDetectionResult) => void;\n\n/**\n * TokenDetector - Detects tokens with non-zero balances via multicall.\n * Extends StaticIntervalPollingControllerOnly for built-in polling support.\n */\nexport class TokenDetector extends StaticIntervalPollingControllerOnly<DetectionPollingInput>() {\n readonly #multicallClient: MulticallClient;\n\n readonly #messenger: TokenDetectorMessenger;\n\n readonly #config: Required<Omit<TokenDetectorConfig, 'pollingInterval'>>;\n\n #onDetectionUpdate: OnDetectionUpdateCallback | undefined;\n\n constructor(\n multicallClient: MulticallClient,\n messenger: TokenDetectorMessenger,\n config?: TokenDetectorConfig,\n ) {\n super();\n this.#multicallClient = multicallClient;\n this.#messenger = messenger;\n this.#config = {\n tokenDetectionEnabled:\n config?.tokenDetectionEnabled ?? ((): boolean => true),\n useExternalService: config?.useExternalService ?? ((): boolean => true),\n defaultBatchSize: config?.defaultBatchSize ?? 300,\n defaultTimeoutMs: config?.defaultTimeoutMs ?? 30000,\n };\n\n // Set the polling interval\n this.setIntervalLength(\n config?.pollingInterval ?? DEFAULT_DETECTION_INTERVAL,\n );\n }\n\n /**\n * Set the callback to receive detection updates during polling.\n *\n * @param callback - Function to call with detection results.\n */\n setOnDetectionUpdate(callback: OnDetectionUpdateCallback): void {\n this.#onDetectionUpdate = callback;\n }\n\n /**\n * Execute a poll cycle (required by base class).\n * Detects tokens and calls the update callback.\n *\n * @param input - The polling input.\n */\n async _executePoll(input: DetectionPollingInput): Promise<void> {\n // Check if token list is available for this chain\n const tokensToCheck = this.getTokensToCheck(input.chainId);\n\n if (tokensToCheck.length === 0) {\n // No tokens in list for chain, will retry on next poll\n return;\n }\n\n const result = await this.detectTokens(\n input.chainId,\n input.accountId,\n input.accountAddress,\n );\n\n if (this.#onDetectionUpdate && result.detectedAssets.length > 0) {\n this.#onDetectionUpdate(result);\n }\n }\n\n getTokensToCheck(chainId: ChainId): Address[] {\n const tokenListState = this.#messenger.call('TokenListController:getState');\n\n // Defensive check for tokensChainsCache\n if (!tokenListState?.tokensChainsCache) {\n return [];\n }\n\n // Try direct lookup first\n let chainCacheEntry = tokenListState.tokensChainsCache[chainId];\n\n // If not found, try normalizing the chain ID (e.g., 0x0a -> 0xa)\n if (!chainCacheEntry) {\n const normalizedChainId: ChainId = `0x${parseInt(chainId, 16).toString(\n 16,\n )}`;\n chainCacheEntry = tokenListState.tokensChainsCache[normalizedChainId];\n }\n\n const chainTokenList = chainCacheEntry?.data;\n\n if (!chainTokenList) {\n return [];\n }\n\n return Object.keys(chainTokenList) as Address[];\n }\n\n async detectTokens(\n chainId: ChainId,\n accountId: AccountId,\n accountAddress: Address,\n options?: TokenDetectionOptions,\n ): Promise<TokenDetectionResult> {\n const tokenDetectionEnabled =\n options?.tokenDetectionEnabled ?? this.#config.tokenDetectionEnabled();\n const useExternalService =\n options?.useExternalService ?? this.#config.useExternalService();\n if (!tokenDetectionEnabled || !useExternalService) {\n return {\n chainId,\n accountId,\n accountAddress,\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n timestamp: Date.now(),\n };\n }\n const batchSize = options?.batchSize ?? this.#config.defaultBatchSize;\n const timestamp = Date.now();\n\n const tokensToCheck = this.getTokensToCheck(chainId);\n\n if (tokensToCheck.length === 0) {\n return {\n chainId,\n accountId,\n accountAddress,\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n timestamp,\n };\n }\n\n const balanceRequests: BalanceOfRequest[] = tokensToCheck.map(\n (tokenAddress) => ({\n tokenAddress,\n accountAddress,\n }),\n );\n\n type DetectionAccumulator = {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n };\n\n const result = await reduceInBatchesSerially<\n BalanceOfRequest,\n DetectionAccumulator\n >({\n values: balanceRequests,\n batchSize,\n initialResult: {\n detectedAssets: [],\n detectedBalances: [],\n zeroBalanceAddresses: [],\n failedAddresses: [],\n },\n eachBatch: async (workingResult, batch) => {\n const responses = await this.#multicallClient.batchBalanceOf(\n chainId,\n batch,\n );\n\n return this.#processBalanceResponses(\n responses,\n workingResult as DetectionAccumulator,\n chainId,\n accountId,\n timestamp,\n );\n },\n });\n\n return {\n chainId,\n accountId,\n accountAddress,\n ...result,\n timestamp,\n };\n }\n\n #processBalanceResponses(\n responses: BalanceOfResponse[],\n accumulator: {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n },\n chainId: ChainId,\n accountId: AccountId,\n timestamp: number,\n ): {\n detectedAssets: Asset[];\n detectedBalances: AssetBalance[];\n zeroBalanceAddresses: Address[];\n failedAddresses: Address[];\n } {\n const {\n detectedAssets,\n detectedBalances,\n zeroBalanceAddresses,\n failedAddresses,\n } = accumulator;\n\n for (const response of responses) {\n if (!response.success) {\n failedAddresses.push(response.tokenAddress);\n continue;\n }\n\n const balance = response.balance ?? '0';\n\n if (balance === '0' || balance === '') {\n zeroBalanceAddresses.push(response.tokenAddress);\n continue;\n }\n\n const tokenMetadata = this.#getTokenMetadata(\n chainId,\n response.tokenAddress,\n );\n\n const asset = this.#createAsset(\n chainId,\n response.tokenAddress,\n tokenMetadata,\n );\n detectedAssets.push(asset);\n\n if (tokenMetadata?.decimals === undefined) {\n continue;\n }\n\n const { decimals } = tokenMetadata;\n const formattedBalance = this.#formatBalance(balance, decimals);\n\n detectedBalances.push({\n assetId: asset.assetId,\n accountId,\n chainId,\n balance,\n formattedBalance,\n decimals,\n timestamp,\n });\n }\n\n return {\n detectedAssets,\n detectedBalances,\n zeroBalanceAddresses,\n failedAddresses,\n };\n }\n\n #formatBalance(rawBalance: string, decimals: number): string {\n try {\n const balanceBigInt = BigInt(rawBalance);\n const divisor = BigInt(10 ** decimals);\n\n const integerPart = balanceBigInt / divisor;\n const remainder = balanceBigInt % divisor;\n const fractionalStr = remainder.toString().padStart(decimals, '0');\n const trimmedFractional = fractionalStr.replace(/0+$/u, '');\n\n if (trimmedFractional === '') {\n return integerPart.toString();\n }\n\n return `${integerPart}.${trimmedFractional}`;\n } catch {\n return rawBalance;\n }\n }\n\n #getTokenMetadata(\n chainId: ChainId,\n tokenAddress: Address,\n ): TokenListEntry | undefined {\n const tokenListState = this.#messenger.call('TokenListController:getState');\n if (!tokenListState?.tokensChainsCache) {\n return undefined;\n }\n\n const chainCacheEntry = tokenListState.tokensChainsCache[chainId];\n const chainTokenList = chainCacheEntry?.data;\n if (!chainTokenList) {\n return undefined;\n }\n\n if (chainTokenList[tokenAddress]) {\n return chainTokenList[tokenAddress];\n }\n\n const lowerAddress = tokenAddress.toLowerCase();\n for (const [address, metadata] of Object.entries(chainTokenList)) {\n if (address.toLowerCase() === lowerAddress) {\n return metadata;\n }\n }\n\n return undefined;\n }\n\n #createAsset(\n chainId: ChainId,\n tokenAddress: Address,\n metadata: TokenListEntry | undefined,\n ): Asset {\n const chainIdDecimal = parseInt(chainId, 16);\n\n const assetId =\n `eip155:${chainIdDecimal}/erc20:${tokenAddress.toLowerCase()}` as CaipAssetType;\n\n return {\n assetId,\n chainId,\n address: tokenAddress,\n type: 'erc20',\n symbol: metadata?.symbol,\n name: metadata?.name,\n decimals: metadata?.decimals,\n image: metadata?.iconUrl,\n isNative: false,\n aggregators: metadata?.aggregators,\n };\n }\n}\n"]}
@@ -1,6 +1,11 @@
1
1
  import type { MulticallClient } from "../clients/index.cjs";
2
- import type { TokensApiClient } from "../clients/TokensApiClient.cjs";
3
- import type { AccountId, Address, ChainId, TokenDetectionOptions, TokenDetectionResult } from "../types/index.cjs";
2
+ import type { AccountId, Address, ChainId, TokenDetectionOptions, TokenDetectionResult, TokenListState } from "../types/index.cjs";
3
+ /**
4
+ * Minimal messenger interface for TokenDetector.
5
+ */
6
+ export type TokenDetectorMessenger = {
7
+ call: (action: 'TokenListController:getState') => TokenListState;
8
+ };
4
9
  export type TokenDetectorConfig = {
5
10
  /** Function returning whether token detection is enabled (avoids stale value) */
6
11
  tokenDetectionEnabled?: () => boolean;
@@ -45,12 +50,11 @@ declare const TokenDetector_base: (abstract new (...args: any[]) => {
45
50
  };
46
51
  /**
47
52
  * TokenDetector - Detects tokens with non-zero balances via multicall.
48
- * Fetches the token list from the Tokens API and uses multicall to check balances.
49
53
  * Extends StaticIntervalPollingControllerOnly for built-in polling support.
50
54
  */
51
55
  export declare class TokenDetector extends TokenDetector_base {
52
56
  #private;
53
- constructor(multicallClient: MulticallClient, tokensApiClient: TokensApiClient, config?: TokenDetectorConfig);
57
+ constructor(multicallClient: MulticallClient, messenger: TokenDetectorMessenger, config?: TokenDetectorConfig);
54
58
  /**
55
59
  * Set the callback to receive detection updates during polling.
56
60
  *
@@ -64,14 +68,7 @@ export declare class TokenDetector extends TokenDetector_base {
64
68
  * @param input - The polling input.
65
69
  */
66
70
  _executePoll(input: DetectionPollingInput): Promise<void>;
67
- /**
68
- * Fetch the list of token addresses to check for the given chain.
69
- * Calls the Tokens API and caches the result for metadata lookups.
70
- *
71
- * @param chainId - Chain ID in hex format.
72
- * @returns Array of token contract addresses.
73
- */
74
- getTokensToCheck(chainId: ChainId): Promise<Address[]>;
71
+ getTokensToCheck(chainId: ChainId): Address[];
75
72
  detectTokens(chainId: ChainId, accountId: AccountId, accountAddress: Address, options?: TokenDetectionOptions): Promise<TokenDetectionResult>;
76
73
  }
77
74
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetector.d.cts","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAAmB;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAmC;AAClE,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EAKP,OAAO,EACP,qBAAqB,EACrB,oBAAoB,EAErB,2BAAiB;AAOlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,yGAAyG;IACzG,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,sBAAsB;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;AAE/E;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,kBAA4D;;gBAY3F,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,eAAe,EAChC,MAAM,CAAC,EAAE,mBAAmB;IAkB9B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAI/D;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/D;;;;;;OAMG;IACG,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAKtD,YAAY,CAChB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;CA2OjC"}
1
+ {"version":3,"file":"TokenDetector.d.cts","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAAmB;AAClD,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EAKP,OAAO,EACP,qBAAqB,EACrB,oBAAoB,EAEpB,cAAc,EACf,2BAAiB;AAKlB;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,8BAA8B,KAAK,cAAc,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,yGAAyG;IACzG,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,sBAAsB;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;AAE/E;;;GAGG;AACH,qBAAa,aAAc,SAAQ,kBAA4D;;gBAU3F,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,sBAAsB,EACjC,MAAM,CAAC,EAAE,mBAAmB;IAmB9B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAI/D;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB/D,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE;IA4BvC,YAAY,CAChB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;CAyOjC"}
@@ -1,6 +1,11 @@
1
1
  import type { MulticallClient } from "../clients/index.mjs";
2
- import type { TokensApiClient } from "../clients/TokensApiClient.mjs";
3
- import type { AccountId, Address, ChainId, TokenDetectionOptions, TokenDetectionResult } from "../types/index.mjs";
2
+ import type { AccountId, Address, ChainId, TokenDetectionOptions, TokenDetectionResult, TokenListState } from "../types/index.mjs";
3
+ /**
4
+ * Minimal messenger interface for TokenDetector.
5
+ */
6
+ export type TokenDetectorMessenger = {
7
+ call: (action: 'TokenListController:getState') => TokenListState;
8
+ };
4
9
  export type TokenDetectorConfig = {
5
10
  /** Function returning whether token detection is enabled (avoids stale value) */
6
11
  tokenDetectionEnabled?: () => boolean;
@@ -45,12 +50,11 @@ declare const TokenDetector_base: (abstract new (...args: any[]) => {
45
50
  };
46
51
  /**
47
52
  * TokenDetector - Detects tokens with non-zero balances via multicall.
48
- * Fetches the token list from the Tokens API and uses multicall to check balances.
49
53
  * Extends StaticIntervalPollingControllerOnly for built-in polling support.
50
54
  */
51
55
  export declare class TokenDetector extends TokenDetector_base {
52
56
  #private;
53
- constructor(multicallClient: MulticallClient, tokensApiClient: TokensApiClient, config?: TokenDetectorConfig);
57
+ constructor(multicallClient: MulticallClient, messenger: TokenDetectorMessenger, config?: TokenDetectorConfig);
54
58
  /**
55
59
  * Set the callback to receive detection updates during polling.
56
60
  *
@@ -64,14 +68,7 @@ export declare class TokenDetector extends TokenDetector_base {
64
68
  * @param input - The polling input.
65
69
  */
66
70
  _executePoll(input: DetectionPollingInput): Promise<void>;
67
- /**
68
- * Fetch the list of token addresses to check for the given chain.
69
- * Calls the Tokens API and caches the result for metadata lookups.
70
- *
71
- * @param chainId - Chain ID in hex format.
72
- * @returns Array of token contract addresses.
73
- */
74
- getTokensToCheck(chainId: ChainId): Promise<Address[]>;
71
+ getTokensToCheck(chainId: ChainId): Address[];
75
72
  detectTokens(chainId: ChainId, accountId: AccountId, accountAddress: Address, options?: TokenDetectionOptions): Promise<TokenDetectionResult>;
76
73
  }
77
74
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetector.d.mts","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAAmB;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAmC;AAClE,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EAKP,OAAO,EACP,qBAAqB,EACrB,oBAAoB,EAErB,2BAAiB;AAOlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,yGAAyG;IACzG,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,sBAAsB;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;AAE/E;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,kBAA4D;;gBAY3F,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,eAAe,EAChC,MAAM,CAAC,EAAE,mBAAmB;IAkB9B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAI/D;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/D;;;;;;OAMG;IACG,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAKtD,YAAY,CAChB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;CA2OjC"}
1
+ {"version":3,"file":"TokenDetector.d.mts","sourceRoot":"","sources":["../../../../src/data-sources/evm-rpc-services/services/TokenDetector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,6BAAmB;AAClD,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EAKP,OAAO,EACP,qBAAqB,EACrB,oBAAoB,EAEpB,cAAc,EACf,2BAAiB;AAKlB;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE,8BAA8B,KAAK,cAAc,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC,yGAAyG;IACzG,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,sBAAsB;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;AAE/E;;;GAGG;AACH,qBAAa,aAAc,SAAQ,kBAA4D;;gBAU3F,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,sBAAsB,EACjC,MAAM,CAAC,EAAE,mBAAmB;IAmB9B;;;;OAIG;IACH,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAI/D;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB/D,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE;IA4BvC,YAAY,CAChB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;CAyOjC"}
@@ -9,34 +9,31 @@ 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 _TokenDetector_instances, _TokenDetector_multicallClient, _TokenDetector_tokensApiClient, _TokenDetector_config, _TokenDetector_tokenListCache, _TokenDetector_onDetectionUpdate, _TokenDetector_fetchAndCacheTokenList, _TokenDetector_processBalanceResponses, _TokenDetector_formatBalance, _TokenDetector_getTokenMetadata, _TokenDetector_createAsset;
12
+ var _TokenDetector_instances, _TokenDetector_multicallClient, _TokenDetector_messenger, _TokenDetector_config, _TokenDetector_onDetectionUpdate, _TokenDetector_processBalanceResponses, _TokenDetector_formatBalance, _TokenDetector_getTokenMetadata, _TokenDetector_createAsset;
13
13
  import { StaticIntervalPollingControllerOnly } from "@metamask/polling-controller";
14
- import { projectLogger, createModuleLogger } from "../../../logger.mjs";
15
14
  import { reduceInBatchesSerially } from "../utils/index.mjs";
16
- const log = createModuleLogger(projectLogger, 'TokenDetector');
17
15
  const DEFAULT_DETECTION_INTERVAL = 180000; // 3 minutes
18
16
  /**
19
17
  * TokenDetector - Detects tokens with non-zero balances via multicall.
20
- * Fetches the token list from the Tokens API and uses multicall to check balances.
21
18
  * Extends StaticIntervalPollingControllerOnly for built-in polling support.
22
19
  */
23
20
  export class TokenDetector extends StaticIntervalPollingControllerOnly() {
24
- constructor(multicallClient, tokensApiClient, config) {
21
+ constructor(multicallClient, messenger, config) {
25
22
  super();
26
23
  _TokenDetector_instances.add(this);
27
24
  _TokenDetector_multicallClient.set(this, void 0);
28
- _TokenDetector_tokensApiClient.set(this, void 0);
25
+ _TokenDetector_messenger.set(this, void 0);
29
26
  _TokenDetector_config.set(this, void 0);
30
- _TokenDetector_tokenListCache.set(this, new Map());
31
27
  _TokenDetector_onDetectionUpdate.set(this, void 0);
32
28
  __classPrivateFieldSet(this, _TokenDetector_multicallClient, multicallClient, "f");
33
- __classPrivateFieldSet(this, _TokenDetector_tokensApiClient, tokensApiClient, "f");
29
+ __classPrivateFieldSet(this, _TokenDetector_messenger, messenger, "f");
34
30
  __classPrivateFieldSet(this, _TokenDetector_config, {
35
31
  tokenDetectionEnabled: config?.tokenDetectionEnabled ?? (() => true),
36
32
  useExternalService: config?.useExternalService ?? (() => true),
37
33
  defaultBatchSize: config?.defaultBatchSize ?? 300,
38
34
  defaultTimeoutMs: config?.defaultTimeoutMs ?? 30000,
39
35
  }, "f");
36
+ // Set the polling interval
40
37
  this.setIntervalLength(config?.pollingInterval ?? DEFAULT_DETECTION_INTERVAL);
41
38
  }
42
39
  /**
@@ -54,26 +51,35 @@ export class TokenDetector extends StaticIntervalPollingControllerOnly() {
54
51
  * @param input - The polling input.
55
52
  */
56
53
  async _executePoll(input) {
57
- try {
58
- const result = await this.detectTokens(input.chainId, input.accountId, input.accountAddress);
59
- if (__classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f") && result.detectedAssets.length > 0) {
60
- __classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f").call(this, result);
61
- }
54
+ // Check if token list is available for this chain
55
+ const tokensToCheck = this.getTokensToCheck(input.chainId);
56
+ if (tokensToCheck.length === 0) {
57
+ // No tokens in list for chain, will retry on next poll
58
+ return;
62
59
  }
63
- catch (error) {
64
- log('Token detection poll failed', { chainId: input.chainId, error });
60
+ const result = await this.detectTokens(input.chainId, input.accountId, input.accountAddress);
61
+ if (__classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f") && result.detectedAssets.length > 0) {
62
+ __classPrivateFieldGet(this, _TokenDetector_onDetectionUpdate, "f").call(this, result);
65
63
  }
66
64
  }
67
- /**
68
- * Fetch the list of token addresses to check for the given chain.
69
- * Calls the Tokens API and caches the result for metadata lookups.
70
- *
71
- * @param chainId - Chain ID in hex format.
72
- * @returns Array of token contract addresses.
73
- */
74
- async getTokensToCheck(chainId) {
75
- const tokenList = await __classPrivateFieldGet(this, _TokenDetector_instances, "m", _TokenDetector_fetchAndCacheTokenList).call(this, chainId);
76
- return tokenList.map((entry) => entry.address);
65
+ getTokensToCheck(chainId) {
66
+ const tokenListState = __classPrivateFieldGet(this, _TokenDetector_messenger, "f").call('TokenListController:getState');
67
+ // Defensive check for tokensChainsCache
68
+ if (!tokenListState?.tokensChainsCache) {
69
+ return [];
70
+ }
71
+ // Try direct lookup first
72
+ let chainCacheEntry = tokenListState.tokensChainsCache[chainId];
73
+ // If not found, try normalizing the chain ID (e.g., 0x0a -> 0xa)
74
+ if (!chainCacheEntry) {
75
+ const normalizedChainId = `0x${parseInt(chainId, 16).toString(16)}`;
76
+ chainCacheEntry = tokenListState.tokensChainsCache[normalizedChainId];
77
+ }
78
+ const chainTokenList = chainCacheEntry?.data;
79
+ if (!chainTokenList) {
80
+ return [];
81
+ }
82
+ return Object.keys(chainTokenList);
77
83
  }
78
84
  async detectTokens(chainId, accountId, accountAddress, options) {
79
85
  const tokenDetectionEnabled = options?.tokenDetectionEnabled ?? __classPrivateFieldGet(this, _TokenDetector_config, "f").tokenDetectionEnabled();
@@ -92,7 +98,7 @@ export class TokenDetector extends StaticIntervalPollingControllerOnly() {
92
98
  }
93
99
  const batchSize = options?.batchSize ?? __classPrivateFieldGet(this, _TokenDetector_config, "f").defaultBatchSize;
94
100
  const timestamp = Date.now();
95
- const tokensToCheck = await this.getTokensToCheck(chainId);
101
+ const tokensToCheck = this.getTokensToCheck(chainId);
96
102
  if (tokensToCheck.length === 0) {
97
103
  return {
98
104
  chainId,
@@ -132,22 +138,7 @@ export class TokenDetector extends StaticIntervalPollingControllerOnly() {
132
138
  };
133
139
  }
134
140
  }
135
- _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_tokensApiClient = new WeakMap(), _TokenDetector_config = new WeakMap(), _TokenDetector_tokenListCache = new WeakMap(), _TokenDetector_onDetectionUpdate = new WeakMap(), _TokenDetector_instances = new WeakSet(), _TokenDetector_fetchAndCacheTokenList = async function _TokenDetector_fetchAndCacheTokenList(chainId) {
136
- try {
137
- const list = await __classPrivateFieldGet(this, _TokenDetector_tokensApiClient, "f").fetchTokenList(chainId);
138
- __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").set(chainId, list);
139
- return list;
140
- }
141
- catch (error) {
142
- const cached = __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").get(chainId);
143
- log('Failed to fetch token list; using stale cache', {
144
- chainId,
145
- cachedCount: cached?.length ?? 0,
146
- error,
147
- });
148
- return cached ?? [];
149
- }
150
- }, _TokenDetector_processBalanceResponses = function _TokenDetector_processBalanceResponses(responses, accumulator, chainId, accountId, timestamp) {
141
+ _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_messenger = new WeakMap(), _TokenDetector_config = new WeakMap(), _TokenDetector_onDetectionUpdate = new WeakMap(), _TokenDetector_instances = new WeakSet(), _TokenDetector_processBalanceResponses = function _TokenDetector_processBalanceResponses(responses, accumulator, chainId, accountId, timestamp) {
151
142
  const { detectedAssets, detectedBalances, zeroBalanceAddresses, failedAddresses, } = accumulator;
152
143
  for (const response of responses) {
153
144
  if (!response.success) {
@@ -200,13 +191,25 @@ _TokenDetector_multicallClient = new WeakMap(), _TokenDetector_tokensApiClient =
200
191
  return rawBalance;
201
192
  }
202
193
  }, _TokenDetector_getTokenMetadata = function _TokenDetector_getTokenMetadata(chainId, tokenAddress) {
203
- const list = __classPrivateFieldGet(this, _TokenDetector_tokenListCache, "f").get(chainId) ?? [];
194
+ const tokenListState = __classPrivateFieldGet(this, _TokenDetector_messenger, "f").call('TokenListController:getState');
195
+ if (!tokenListState?.tokensChainsCache) {
196
+ return undefined;
197
+ }
198
+ const chainCacheEntry = tokenListState.tokensChainsCache[chainId];
199
+ const chainTokenList = chainCacheEntry?.data;
200
+ if (!chainTokenList) {
201
+ return undefined;
202
+ }
203
+ if (chainTokenList[tokenAddress]) {
204
+ return chainTokenList[tokenAddress];
205
+ }
204
206
  const lowerAddress = tokenAddress.toLowerCase();
205
- const exact = list.find((entry) => entry.address === tokenAddress);
206
- if (exact) {
207
- return exact;
207
+ for (const [address, metadata] of Object.entries(chainTokenList)) {
208
+ if (address.toLowerCase() === lowerAddress) {
209
+ return metadata;
210
+ }
208
211
  }
209
- return list.find((entry) => entry.address.toLowerCase() === lowerAddress);
212
+ return undefined;
210
213
  }, _TokenDetector_createAsset = function _TokenDetector_createAsset(chainId, tokenAddress, metadata) {
211
214
  const chainIdDecimal = parseInt(chainId, 16);
212
215
  const assetId = `eip155:${chainIdDecimal}/erc20:${tokenAddress.toLowerCase()}`;