@metamask/assets-controller 1.0.0 → 2.0.1

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 (166) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/dist/AssetsController.cjs +175 -64
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +68 -9
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +68 -9
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +176 -65
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountsApiDataSource.cjs +70 -15
  11. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  12. package/dist/data-sources/AccountsApiDataSource.d.cts +22 -3
  13. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  14. package/dist/data-sources/AccountsApiDataSource.d.mts +22 -3
  15. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  16. package/dist/data-sources/AccountsApiDataSource.mjs +66 -12
  17. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  18. package/dist/data-sources/BackendWebsocketDataSource.cjs +7 -5
  19. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  20. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  21. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  22. package/dist/data-sources/BackendWebsocketDataSource.mjs +7 -5
  23. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  24. package/dist/data-sources/PriceDataSource.cjs +17 -4
  25. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  26. package/dist/data-sources/PriceDataSource.d.cts +8 -4
  27. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  28. package/dist/data-sources/PriceDataSource.d.mts +8 -4
  29. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  30. package/dist/data-sources/PriceDataSource.mjs +17 -4
  31. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  32. package/dist/data-sources/RpcDataSource.cjs +123 -44
  33. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  34. package/dist/data-sources/RpcDataSource.d.cts +16 -62
  35. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  36. package/dist/data-sources/RpcDataSource.d.mts +16 -62
  37. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  38. package/dist/data-sources/RpcDataSource.mjs +124 -45
  39. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  40. package/dist/data-sources/SnapDataSource.cjs +22 -10
  41. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  42. package/dist/data-sources/SnapDataSource.d.cts +2 -1
  43. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  44. package/dist/data-sources/SnapDataSource.d.mts +2 -1
  45. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  46. package/dist/data-sources/SnapDataSource.mjs +22 -10
  47. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  48. package/dist/data-sources/StakedBalanceDataSource.cjs +597 -0
  49. package/dist/data-sources/StakedBalanceDataSource.cjs.map +1 -0
  50. package/dist/data-sources/StakedBalanceDataSource.d.cts +72 -0
  51. package/dist/data-sources/StakedBalanceDataSource.d.cts.map +1 -0
  52. package/dist/data-sources/StakedBalanceDataSource.d.mts +72 -0
  53. package/dist/data-sources/StakedBalanceDataSource.d.mts.map +1 -0
  54. package/dist/data-sources/StakedBalanceDataSource.mjs +593 -0
  55. package/dist/data-sources/StakedBalanceDataSource.mjs.map +1 -0
  56. package/dist/data-sources/TokenDataSource.cjs +12 -5
  57. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  58. package/dist/data-sources/TokenDataSource.d.cts +1 -0
  59. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  60. package/dist/data-sources/TokenDataSource.d.mts +1 -0
  61. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  62. package/dist/data-sources/TokenDataSource.mjs +12 -5
  63. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  64. package/dist/data-sources/evm-rpc-services/index.cjs +5 -1
  65. package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -1
  66. package/dist/data-sources/evm-rpc-services/index.d.cts +1 -1
  67. package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -1
  68. package/dist/data-sources/evm-rpc-services/index.d.mts +1 -1
  69. package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -1
  70. package/dist/data-sources/evm-rpc-services/index.mjs +1 -1
  71. package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -1
  72. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.cjs +132 -0
  73. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.cjs.map +1 -0
  74. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.cts +80 -0
  75. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.cts.map +1 -0
  76. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.mts +80 -0
  77. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.d.mts.map +1 -0
  78. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.mjs +126 -0
  79. package/dist/data-sources/evm-rpc-services/services/StakedBalanceFetcher.mjs.map +1 -0
  80. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +16 -0
  81. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
  82. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +5 -1
  83. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -1
  84. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +5 -1
  85. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
  86. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +16 -0
  87. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -1
  88. package/dist/data-sources/evm-rpc-services/services/index.cjs +6 -1
  89. package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -1
  90. package/dist/data-sources/evm-rpc-services/services/index.d.cts +1 -0
  91. package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -1
  92. package/dist/data-sources/evm-rpc-services/services/index.d.mts +1 -0
  93. package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -1
  94. package/dist/data-sources/evm-rpc-services/services/index.mjs +1 -0
  95. package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -1
  96. package/dist/data-sources/evm-rpc-services/types/services.cjs.map +1 -1
  97. package/dist/data-sources/evm-rpc-services/types/services.d.cts +4 -0
  98. package/dist/data-sources/evm-rpc-services/types/services.d.cts.map +1 -1
  99. package/dist/data-sources/evm-rpc-services/types/services.d.mts +4 -0
  100. package/dist/data-sources/evm-rpc-services/types/services.d.mts.map +1 -1
  101. package/dist/data-sources/evm-rpc-services/types/services.mjs.map +1 -1
  102. package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -1
  103. package/dist/data-sources/evm-rpc-services/types/state.d.cts +3 -3
  104. package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -1
  105. package/dist/data-sources/evm-rpc-services/types/state.d.mts +3 -3
  106. package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -1
  107. package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -1
  108. package/dist/data-sources/evm-rpc-services/utils/index.cjs +9 -1
  109. package/dist/data-sources/evm-rpc-services/utils/index.cjs.map +1 -1
  110. package/dist/data-sources/evm-rpc-services/utils/index.d.cts +2 -0
  111. package/dist/data-sources/evm-rpc-services/utils/index.d.cts.map +1 -1
  112. package/dist/data-sources/evm-rpc-services/utils/index.d.mts +2 -0
  113. package/dist/data-sources/evm-rpc-services/utils/index.d.mts.map +1 -1
  114. package/dist/data-sources/evm-rpc-services/utils/index.mjs +2 -0
  115. package/dist/data-sources/evm-rpc-services/utils/index.mjs.map +1 -1
  116. package/dist/data-sources/evm-rpc-services/utils/parsing.cjs +47 -0
  117. package/dist/data-sources/evm-rpc-services/utils/parsing.cjs.map +1 -0
  118. package/dist/data-sources/evm-rpc-services/utils/parsing.d.cts +25 -0
  119. package/dist/data-sources/evm-rpc-services/utils/parsing.d.cts.map +1 -0
  120. package/dist/data-sources/evm-rpc-services/utils/parsing.d.mts +25 -0
  121. package/dist/data-sources/evm-rpc-services/utils/parsing.d.mts.map +1 -0
  122. package/dist/data-sources/evm-rpc-services/utils/parsing.mjs +39 -0
  123. package/dist/data-sources/evm-rpc-services/utils/parsing.mjs.map +1 -0
  124. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.cjs +68 -0
  125. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.cjs.map +1 -0
  126. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.cts +25 -0
  127. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.cts.map +1 -0
  128. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.mts +25 -0
  129. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.d.mts.map +1 -0
  130. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.mjs +62 -0
  131. package/dist/data-sources/evm-rpc-services/utils/staking-contracts.mjs.map +1 -0
  132. package/dist/data-sources/index.cjs +3 -1
  133. package/dist/data-sources/index.cjs.map +1 -1
  134. package/dist/data-sources/index.d.cts +3 -2
  135. package/dist/data-sources/index.d.cts.map +1 -1
  136. package/dist/data-sources/index.d.mts +3 -2
  137. package/dist/data-sources/index.d.mts.map +1 -1
  138. package/dist/data-sources/index.mjs +1 -0
  139. package/dist/data-sources/index.mjs.map +1 -1
  140. package/dist/index.cjs.map +1 -1
  141. package/dist/index.d.cts +3 -3
  142. package/dist/index.d.cts.map +1 -1
  143. package/dist/index.d.mts +3 -3
  144. package/dist/index.d.mts.map +1 -1
  145. package/dist/index.mjs.map +1 -1
  146. package/dist/middlewares/DetectionMiddleware.cjs +4 -1
  147. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  148. package/dist/middlewares/DetectionMiddleware.d.cts +1 -0
  149. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  150. package/dist/middlewares/DetectionMiddleware.d.mts +1 -0
  151. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  152. package/dist/middlewares/DetectionMiddleware.mjs +4 -1
  153. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  154. package/dist/selectors/balance.cjs +2 -2
  155. package/dist/selectors/balance.cjs.map +1 -1
  156. package/dist/selectors/balance.d.cts.map +1 -1
  157. package/dist/selectors/balance.d.mts.map +1 -1
  158. package/dist/selectors/balance.mjs +2 -2
  159. package/dist/selectors/balance.mjs.map +1 -1
  160. package/dist/types.cjs.map +1 -1
  161. package/dist/types.d.cts +3 -3
  162. package/dist/types.d.cts.map +1 -1
  163. package/dist/types.d.mts +3 -3
  164. package/dist/types.d.mts.map +1 -1
  165. package/dist/types.mjs.map +1 -1
  166. package/package.json +17 -12
@@ -10,13 +10,14 @@ 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 _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_initializeActiveChains, _AccountsApiDataSource_refreshActiveChains, _AccountsApiDataSource_fetchActiveChains, _AccountsApiDataSource_processV5Balances;
13
+ var _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_tokenDetectionEnabled, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_getAssetsState, _AccountsApiDataSource_initializeActiveChains, _AccountsApiDataSource_refreshActiveChains, _AccountsApiDataSource_fetchActiveChains, _AccountsApiDataSource_processV5Balances;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.AccountsApiDataSource = void 0;
15
+ exports.AccountsApiDataSource = exports.filterResponseToKnownAssets = void 0;
16
16
  const core_backend_1 = require("@metamask/core-backend");
17
+ const utils_1 = require("@metamask/utils");
17
18
  const AbstractDataSource_1 = require("./AbstractDataSource.cjs");
18
19
  const logger_1 = require("../logger.cjs");
19
- const utils_1 = require("../utils.cjs");
20
+ const utils_2 = require("../utils.cjs");
20
21
  // ============================================================================
21
22
  // CONSTANTS
22
23
  // ============================================================================
@@ -32,30 +33,63 @@ const defaultState = {
32
33
  function decimalToChainId(decimalChainId) {
33
34
  // Handle both decimal numbers and already-formatted CAIP chain IDs
34
35
  if (typeof decimalChainId === 'string') {
35
- // If already a CAIP chain ID (e.g., "eip155:1"), return as-is
36
- if (decimalChainId.startsWith('eip155:')) {
36
+ if ((0, utils_1.isCaipChainId)(decimalChainId)) {
37
37
  return decimalChainId;
38
38
  }
39
- // If it's a string number, convert
40
- return `eip155:${decimalChainId}`;
39
+ return (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, decimalChainId);
41
40
  }
42
- return `eip155:${decimalChainId}`;
41
+ return (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, String(decimalChainId));
43
42
  }
44
43
  /**
45
44
  * Convert a CAIP-2 chain ID from the API response to our ChainId type.
46
45
  * Handles both formats: "eip155:1" or just "1" (decimal).
46
+ * Uses @metamask/utils for CAIP parsing.
47
47
  *
48
48
  * @param chainIdStr - The chain ID string to convert.
49
49
  * @returns The normalized ChainId.
50
50
  */
51
51
  function caipChainIdToChainId(chainIdStr) {
52
- // If already in CAIP-2 format, return as-is
53
- if (chainIdStr.includes(':')) {
52
+ if ((0, utils_1.isCaipChainId)(chainIdStr)) {
54
53
  return chainIdStr;
55
54
  }
56
- // If decimal number, convert to CAIP-2
57
- return `eip155:${chainIdStr}`;
55
+ return (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, chainIdStr);
58
56
  }
57
+ /**
58
+ * Filter a response to only include balances for assets already in state.
59
+ * Used when tokenDetectionEnabled is false to prevent adding new tokens.
60
+ *
61
+ * @param response - The fetch response to filter.
62
+ * @param assetsState - Current assets controller state to check existing balances against.
63
+ * @returns A new response with only known asset balances.
64
+ */
65
+ function filterResponseToKnownAssets(response, assetsState) {
66
+ if (!response.assetsBalance) {
67
+ return response;
68
+ }
69
+ const filteredBalance = {};
70
+ for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
71
+ const existingBalances = assetsState.assetsBalance[accountId];
72
+ if (!existingBalances) {
73
+ // Account has no balances in state yet — skip all its tokens
74
+ continue;
75
+ }
76
+ const filtered = {};
77
+ for (const [assetId, balance] of Object.entries(accountBalances)) {
78
+ // Only include assets already tracked in state
79
+ if (assetId in existingBalances) {
80
+ filtered[assetId] = balance;
81
+ }
82
+ }
83
+ if (Object.keys(filtered).length > 0) {
84
+ filteredBalance[accountId] = filtered;
85
+ }
86
+ }
87
+ return {
88
+ ...response,
89
+ assetsBalance: Object.keys(filteredBalance).length > 0 ? filteredBalance : undefined,
90
+ };
91
+ }
92
+ exports.filterResponseToKnownAssets = filterResponseToKnownAssets;
59
93
  // ============================================================================
60
94
  // ACCOUNTS API DATA SOURCE
61
95
  // ============================================================================
@@ -74,12 +108,17 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
74
108
  _AccountsApiDataSource_instances.add(this);
75
109
  _AccountsApiDataSource_onActiveChainsUpdated.set(this, void 0);
76
110
  _AccountsApiDataSource_pollInterval.set(this, void 0);
111
+ /** Getter avoids stale value when user toggles token detection at runtime. */
112
+ _AccountsApiDataSource_tokenDetectionEnabled.set(this, void 0);
77
113
  /** ApiPlatformClient for cached API calls */
78
114
  _AccountsApiDataSource_apiClient.set(this, void 0);
79
115
  /** Chains refresh timer */
80
116
  _AccountsApiDataSource_chainsRefreshTimer.set(this, null);
117
+ /** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */
118
+ _AccountsApiDataSource_getAssetsState.set(this, void 0);
81
119
  __classPrivateFieldSet(this, _AccountsApiDataSource_onActiveChainsUpdated, options.onActiveChainsUpdated, "f");
82
120
  __classPrivateFieldSet(this, _AccountsApiDataSource_pollInterval, options.pollInterval ?? DEFAULT_POLL_INTERVAL, "f");
121
+ __classPrivateFieldSet(this, _AccountsApiDataSource_tokenDetectionEnabled, options.tokenDetectionEnabled ?? (() => true), "f");
83
122
  __classPrivateFieldSet(this, _AccountsApiDataSource_apiClient, options.queryApiClient, "f");
84
123
  __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_initializeActiveChains).call(this).catch(console.error);
85
124
  }
@@ -90,7 +129,7 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
90
129
  // FETCH
91
130
  // ============================================================================
92
131
  async fetch(request) {
93
- const response = {};
132
+ let response = {};
94
133
  // Filter to only chains supported by Accounts API
95
134
  const supportedChains = new Set(this.state.activeChains);
96
135
  const chainsToFetch = request.chainIds.filter((chainId) => supportedChains.has(chainId));
@@ -143,6 +182,10 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
143
182
  response.errors[chainId] = 'Chain not supported by Accounts API';
144
183
  }
145
184
  }
185
+ // When token detection is disabled, filter out tokens not already in state
186
+ if (!__classPrivateFieldGet(this, _AccountsApiDataSource_tokenDetectionEnabled, "f").call(this) && __classPrivateFieldGet(this, _AccountsApiDataSource_getAssetsState, "f")) {
187
+ response = filterResponseToKnownAssets(response, __classPrivateFieldGet(this, _AccountsApiDataSource_getAssetsState, "f").call(this));
188
+ }
146
189
  return response;
147
190
  }
148
191
  // ============================================================================
@@ -182,6 +225,14 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
182
225
  // Determine successfully handled chains (exclude unprocessed/error chains)
183
226
  const unprocessedChains = new Set(Object.keys(response.errors ?? {}));
184
227
  successfullyHandledChains = request.chainIds.filter((chainId) => !unprocessedChains.has(chainId));
228
+ // When token detection is off and we filtered out all balance data (e.g. new
229
+ // account with empty state), do not claim any chain as handled so that RPC
230
+ // middleware can still process them and fetch native balances (ETH, MATIC, etc.).
231
+ if (!__classPrivateFieldGet(this, _AccountsApiDataSource_tokenDetectionEnabled, "f").call(this) &&
232
+ (!response.assetsBalance ||
233
+ Object.keys(response.assetsBalance).length === 0)) {
234
+ successfullyHandledChains = [];
235
+ }
185
236
  }
186
237
  catch (error) {
187
238
  log('Middleware fetch failed', { error });
@@ -207,6 +258,10 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
207
258
  // ============================================================================
208
259
  async subscribe(subscriptionRequest) {
209
260
  const { request, subscriptionId, isUpdate } = subscriptionRequest;
261
+ // Store state accessor for filtering when tokenDetectionEnabled is false
262
+ if (subscriptionRequest.getAssetsState) {
263
+ __classPrivateFieldSet(this, _AccountsApiDataSource_getAssetsState, subscriptionRequest.getAssetsState, "f");
264
+ }
210
265
  // Try all requested chains - API will handle unsupported ones via unprocessedNetworks
211
266
  const chainsToSubscribe = request.chainIds;
212
267
  if (chainsToSubscribe.length === 0) {
@@ -272,7 +327,7 @@ class AccountsApiDataSource extends AbstractDataSource_1.AbstractDataSource {
272
327
  }
273
328
  }
274
329
  exports.AccountsApiDataSource = AccountsApiDataSource;
275
- _AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_initializeActiveChains =
330
+ _AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_tokenDetectionEnabled = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_getAssetsState = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_initializeActiveChains =
276
331
  // ============================================================================
277
332
  // INITIALIZATION
278
333
  // ============================================================================
@@ -336,7 +391,7 @@ async function _AccountsApiDataSource_initializeActiveChains() {
336
391
  assetsBalance[accountId] = {};
337
392
  }
338
393
  // Normalize asset ID (checksum EVM addresses for ERC20 tokens)
339
- const normalizedAssetId = (0, utils_1.normalizeAssetId)(item.assetId);
394
+ const normalizedAssetId = (0, utils_2.normalizeAssetId)(item.assetId);
340
395
  // Store balance as returned by API
341
396
  assetsBalance[accountId][normalizedAssetId] = {
342
397
  amount: item.balance,
@@ -1 +1 @@
1
- {"version":3,"file":"AccountsApiDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V5BalanceItem } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\n\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport { AbstractDataSource } from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetBalance,\n DataRequest,\n DataResponse,\n Middleware,\n} from '../types';\nimport { normalizeAssetId } from '../utils';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountsApiDataSource';\nconst DEFAULT_POLL_INTERVAL = 30_000;\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Allowed actions that AccountsApiDataSource can call (none - uses callbacks).\n// Note: Uses ApiPlatformClient directly, so no BackendApiClient actions needed\nexport type AccountsApiDataSourceAllowedActions = never;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountsApiDataSourceState = DataSourceState;\n\nconst defaultState: AccountsApiDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type AccountsApiDataSourceOptions = {\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n pollInterval?: number;\n state?: Partial<AccountsApiDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\nfunction decimalToChainId(decimalChainId: number | string): ChainId {\n // Handle both decimal numbers and already-formatted CAIP chain IDs\n if (typeof decimalChainId === 'string') {\n // If already a CAIP chain ID (e.g., \"eip155:1\"), return as-is\n if (decimalChainId.startsWith('eip155:')) {\n return decimalChainId as ChainId;\n }\n // If it's a string number, convert\n return `eip155:${decimalChainId}` as ChainId;\n }\n return `eip155:${decimalChainId}` as ChainId;\n}\n\n/**\n * Convert a CAIP-2 chain ID from the API response to our ChainId type.\n * Handles both formats: \"eip155:1\" or just \"1\" (decimal).\n *\n * @param chainIdStr - The chain ID string to convert.\n * @returns The normalized ChainId.\n */\nfunction caipChainIdToChainId(chainIdStr: string): ChainId {\n // If already in CAIP-2 format, return as-is\n if (chainIdStr.includes(':')) {\n return chainIdStr as ChainId;\n }\n // If decimal number, convert to CAIP-2\n return `eip155:${chainIdStr}` as ChainId;\n}\n\n// ============================================================================\n// ACCOUNTS API DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for fetching balances from the MetaMask Accounts API.\n *\n * Uses ApiPlatformClient (queryApiClient) for all API calls. Does not use the\n * messenger. Reports active chains via onActiveChainsUpdated callback.\n */\nexport class AccountsApiDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountsApiDataSourceState\n> {\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #pollInterval: number;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Chains refresh timer */\n #chainsRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: AccountsApiDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#apiClient = options.queryApiClient;\n\n this.#initializeActiveChains().catch(console.error);\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n async #initializeActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n\n // Periodically refresh active chains (every 20 minutes)\n this.#chainsRefreshTimer = setInterval(\n () => {\n this.#refreshActiveChains().catch(console.error);\n },\n 20 * 60 * 1000,\n );\n } catch (error) {\n log('Failed to fetch active chains', error);\n }\n }\n\n async #refreshActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n // Check if chains changed\n const added = chains.filter((chain) => !previousChains.has(chain));\n const removed = Array.from(previousChains).filter(\n (chain) => !newChains.has(chain),\n );\n\n if (added.length > 0 || removed.length > 0) {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n }\n } catch (error) {\n log('Failed to refresh active chains', error);\n }\n }\n\n async #fetchActiveChains(): Promise<ChainId[]> {\n const response = await this.#apiClient.accounts.fetchV2SupportedNetworks();\n\n // Use fullSupport networks as active chains\n return response.fullSupport.map(decimalToChainId);\n }\n\n // ============================================================================\n // ACCOUNT SCOPE HELPERS\n // ============================================================================\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n const response: DataResponse = {};\n\n // Filter to only chains supported by Accounts API\n const supportedChains = new Set(this.state.activeChains);\n const chainsToFetch = request.chainIds.filter((chainId) =>\n supportedChains.has(chainId),\n );\n\n if (chainsToFetch.length === 0) {\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n return response;\n }\n\n try {\n // Build CAIP-10 account IDs (e.g., \"eip155:1:0x1234...\")\n // Use pre-computed supportedChains per account from the request\n const accountIds = request.accountsWithSupportedChains.flatMap(\n ({ account, supportedChains: accountChains }) =>\n chainsToFetch\n .filter((chainId) => accountChains.includes(chainId))\n .map((chainId) => `${chainId}:${account.address}`),\n );\n\n // Skip API call if no valid account-chain combinations\n if (accountIds.length === 0) {\n return response;\n }\n\n const apiResponse =\n await this.#apiClient.accounts.fetchV5MultiAccountBalances(accountIds);\n\n // Handle unprocessed networks - these will be passed to next middleware\n if (apiResponse.unprocessedNetworks.length > 0) {\n const unprocessedChainIds =\n apiResponse.unprocessedNetworks.map(caipChainIdToChainId);\n\n // Add unprocessed chains to errors so middleware passes them to next data source\n response.errors = response.errors ?? {};\n for (const chainId of unprocessedChainIds) {\n response.errors[chainId] = 'Unprocessed by Accounts API';\n }\n }\n\n const { assetsBalance } = this.#processV5Balances(\n apiResponse.balances,\n request,\n );\n\n response.assetsBalance = assetsBalance;\n } catch (error) {\n log('Fetch FAILED', { error, chains: chainsToFetch });\n\n // On error, mark all chains as errors so they can be handled by next middleware\n response.errors = response.errors ?? {};\n for (const chainId of chainsToFetch) {\n response.errors[chainId] =\n `Fetch failed: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n\n return response;\n }\n\n /**\n * Process V5 API balances response.\n * V5 returns a flat array of balance items, each with accountId and assetId.\n *\n * @param balances - Array of balance items from the V5 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV5Balances(\n balances: V5BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = new Map<string, string>();\n for (const { account } of request.accountsWithSupportedChains) {\n if (account.address) {\n addressToAccountId.set(account.address.toLowerCase(), account.id);\n }\n }\n\n // V5 response: array of { accountId, assetId, balance, ... }\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = {};\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Accounts API.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Uses unprocessedNetworks from API response to determine what to pass to next middleware\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // If no chains requested, skip to next middleware\n if (request.chainIds.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n const response = await this.fetch(request);\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n\n // Determine successfully handled chains (exclude unprocessed/error chains)\n const unprocessedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = request.chainIds.filter(\n (chainId) => !unprocessedChains.has(chainId),\n );\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Remove successfully handled chains from request for next middleware\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n\n return next({\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n });\n }\n\n // No chains handled - pass context unchanged\n return next(context);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Try all requested chains - API will handle unsupported ones via unprocessedNetworks\n const chainsToSubscribe = request.chainIds;\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Handle subscription update - update both chains AND request (for accounts)\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.chains = chainsToSubscribe;\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function for this subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (!subscription?.request) {\n return;\n }\n\n // Use stored request (which gets updated on account changes)\n const fetchResponse = await this.fetch({\n ...subscription.request,\n chainIds: subscription.chains,\n });\n\n // Report update to AssetsController via callback\n await subscription.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription with request for account updates\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n chains: chainsToSubscribe,\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // Clean up timers\n if (this.#chainsRefreshTimer) {\n clearInterval(this.#chainsRefreshTimer);\n }\n\n // Clean up subscriptions\n super.destroy();\n }\n}\n"]}
1
+ {"version":3,"file":"AccountsApiDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V5BalanceItem } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport {\n isCaipChainId,\n KnownCaipNamespace,\n toCaipChainId,\n} from '@metamask/utils';\n\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport { AbstractDataSource } from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetBalance,\n DataRequest,\n DataResponse,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types';\nimport { normalizeAssetId } from '../utils';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountsApiDataSource';\nconst DEFAULT_POLL_INTERVAL = 30_000;\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Allowed actions that AccountsApiDataSource can call (none - uses callbacks).\n// Note: Uses ApiPlatformClient directly, so no BackendApiClient actions needed\nexport type AccountsApiDataSourceAllowedActions = never;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountsApiDataSourceState = DataSourceState;\n\nconst defaultState: AccountsApiDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\n/** Optional configuration for AccountsApiDataSource. */\nexport type AccountsApiDataSourceConfig = {\n /** Polling interval in ms (default: 30000) */\n pollInterval?: number;\n /**\n * Function returning whether token detection is enabled (default: () => true).\n * When it returns false, balances are only returned for tokens already in state.\n * Using a getter avoids stale values when the user toggles the preference at runtime.\n */\n tokenDetectionEnabled?: () => boolean;\n};\n\nexport type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n state?: Partial<AccountsApiDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\nfunction decimalToChainId(decimalChainId: number | string): ChainId {\n // Handle both decimal numbers and already-formatted CAIP chain IDs\n if (typeof decimalChainId === 'string') {\n if (isCaipChainId(decimalChainId)) {\n return decimalChainId;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, decimalChainId);\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, String(decimalChainId));\n}\n\n/**\n * Convert a CAIP-2 chain ID from the API response to our ChainId type.\n * Handles both formats: \"eip155:1\" or just \"1\" (decimal).\n * Uses @metamask/utils for CAIP parsing.\n *\n * @param chainIdStr - The chain ID string to convert.\n * @returns The normalized ChainId.\n */\nfunction caipChainIdToChainId(chainIdStr: string): ChainId {\n if (isCaipChainId(chainIdStr)) {\n return chainIdStr;\n }\n return toCaipChainId(KnownCaipNamespace.Eip155, chainIdStr);\n}\n\n/**\n * Filter a response to only include balances for assets already in state.\n * Used when tokenDetectionEnabled is false to prevent adding new tokens.\n *\n * @param response - The fetch response to filter.\n * @param assetsState - Current assets controller state to check existing balances against.\n * @returns A new response with only known asset balances.\n */\nexport function filterResponseToKnownAssets(\n response: DataResponse,\n assetsState: AssetsControllerStateInternal,\n): DataResponse {\n if (!response.assetsBalance) {\n return response;\n }\n\n const filteredBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n const existingBalances = assetsState.assetsBalance[accountId];\n if (!existingBalances) {\n // Account has no balances in state yet — skip all its tokens\n continue;\n }\n\n const filtered: Record<Caip19AssetId, AssetBalance> = {};\n for (const [assetId, balance] of Object.entries(accountBalances)) {\n // Only include assets already tracked in state\n if (assetId in existingBalances) {\n filtered[assetId as Caip19AssetId] = balance;\n }\n }\n\n if (Object.keys(filtered).length > 0) {\n filteredBalance[accountId] = filtered;\n }\n }\n\n return {\n ...response,\n assetsBalance:\n Object.keys(filteredBalance).length > 0 ? filteredBalance : undefined,\n };\n}\n\n// ============================================================================\n// ACCOUNTS API DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for fetching balances from the MetaMask Accounts API.\n *\n * Uses ApiPlatformClient (queryApiClient) for all API calls. Does not use the\n * messenger. Reports active chains via onActiveChainsUpdated callback.\n */\nexport class AccountsApiDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountsApiDataSourceState\n> {\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #pollInterval: number;\n\n /** Getter avoids stale value when user toggles token detection at runtime. */\n readonly #tokenDetectionEnabled: () => boolean;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Chains refresh timer */\n #chainsRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n /** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */\n #getAssetsState?: () => AssetsControllerStateInternal;\n\n constructor(options: AccountsApiDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#tokenDetectionEnabled =\n options.tokenDetectionEnabled ?? ((): boolean => true);\n this.#apiClient = options.queryApiClient;\n\n this.#initializeActiveChains().catch(console.error);\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n async #initializeActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n\n // Periodically refresh active chains (every 20 minutes)\n this.#chainsRefreshTimer = setInterval(\n () => {\n this.#refreshActiveChains().catch(console.error);\n },\n 20 * 60 * 1000,\n );\n } catch (error) {\n log('Failed to fetch active chains', error);\n }\n }\n\n async #refreshActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n // Check if chains changed\n const added = chains.filter((chain) => !previousChains.has(chain));\n const removed = Array.from(previousChains).filter(\n (chain) => !newChains.has(chain),\n );\n\n if (added.length > 0 || removed.length > 0) {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n }\n } catch (error) {\n log('Failed to refresh active chains', error);\n }\n }\n\n async #fetchActiveChains(): Promise<ChainId[]> {\n const response = await this.#apiClient.accounts.fetchV2SupportedNetworks();\n\n // Use fullSupport networks as active chains\n return response.fullSupport.map(decimalToChainId);\n }\n\n // ============================================================================\n // ACCOUNT SCOPE HELPERS\n // ============================================================================\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n let response: DataResponse = {};\n\n // Filter to only chains supported by Accounts API\n const supportedChains = new Set(this.state.activeChains);\n const chainsToFetch = request.chainIds.filter((chainId) =>\n supportedChains.has(chainId),\n );\n\n if (chainsToFetch.length === 0) {\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n return response;\n }\n\n try {\n // Build CAIP-10 account IDs (e.g., \"eip155:1:0x1234...\")\n // Use pre-computed supportedChains per account from the request\n const accountIds = request.accountsWithSupportedChains.flatMap(\n ({ account, supportedChains: accountChains }) =>\n chainsToFetch\n .filter((chainId) => accountChains.includes(chainId))\n .map((chainId) => `${chainId}:${account.address}`),\n );\n\n // Skip API call if no valid account-chain combinations\n if (accountIds.length === 0) {\n return response;\n }\n\n const apiResponse =\n await this.#apiClient.accounts.fetchV5MultiAccountBalances(accountIds);\n\n // Handle unprocessed networks - these will be passed to next middleware\n if (apiResponse.unprocessedNetworks.length > 0) {\n const unprocessedChainIds =\n apiResponse.unprocessedNetworks.map(caipChainIdToChainId);\n\n // Add unprocessed chains to errors so middleware passes them to next data source\n response.errors = response.errors ?? {};\n for (const chainId of unprocessedChainIds) {\n response.errors[chainId] = 'Unprocessed by Accounts API';\n }\n }\n\n const { assetsBalance } = this.#processV5Balances(\n apiResponse.balances,\n request,\n );\n\n response.assetsBalance = assetsBalance;\n } catch (error) {\n log('Fetch FAILED', { error, chains: chainsToFetch });\n\n // On error, mark all chains as errors so they can be handled by next middleware\n response.errors = response.errors ?? {};\n for (const chainId of chainsToFetch) {\n response.errors[chainId] =\n `Fetch failed: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n\n // Mark unsupported chains as errors so they pass to next middleware\n for (const chainId of request.chainIds) {\n if (!supportedChains.has(chainId)) {\n response.errors = response.errors ?? {};\n response.errors[chainId] = 'Chain not supported by Accounts API';\n }\n }\n\n // When token detection is disabled, filter out tokens not already in state\n if (!this.#tokenDetectionEnabled() && this.#getAssetsState) {\n response = filterResponseToKnownAssets(response, this.#getAssetsState());\n }\n\n return response;\n }\n\n /**\n * Process V5 API balances response.\n * V5 returns a flat array of balance items, each with accountId and assetId.\n *\n * @param balances - Array of balance items from the V5 API response.\n * @param request - The original data request containing accounts to map.\n * @returns Object containing processed asset balances by account.\n */\n #processV5Balances(\n balances: V5BalanceItem[],\n request: DataRequest,\n ): {\n assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>>;\n } {\n const assetsBalance: Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n > = {};\n\n // Build a map of lowercase addresses to account IDs for efficient lookup\n const addressToAccountId = new Map<string, string>();\n for (const { account } of request.accountsWithSupportedChains) {\n if (account.address) {\n addressToAccountId.set(account.address.toLowerCase(), account.id);\n }\n }\n\n // V5 response: array of { accountId, assetId, balance, ... }\n for (const item of balances) {\n // Extract address from CAIP-10 account ID (e.g., \"eip155:1:0x1234...\" -> \"0x1234...\")\n const addressParts = item.accountId.split(':');\n if (addressParts.length < 3) {\n continue;\n }\n const address = addressParts[2].toLowerCase();\n\n // Find the matching account ID from request\n const accountId = addressToAccountId.get(address);\n if (!accountId) {\n // This is normal - API returns balances for all chains, but request may only have one account\n continue;\n }\n\n if (!assetsBalance[accountId]) {\n assetsBalance[accountId] = {};\n }\n\n // Normalize asset ID (checksum EVM addresses for ERC20 tokens)\n const normalizedAssetId = normalizeAssetId(item.assetId as Caip19AssetId);\n\n // Store balance as returned by API\n assetsBalance[accountId][normalizedAssetId] = {\n amount: item.balance,\n };\n }\n\n return { assetsBalance };\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Accounts API.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Uses unprocessedNetworks from API response to determine what to pass to next middleware\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // If no chains requested, skip to next middleware\n if (request.chainIds.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n const response = await this.fetch(request);\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n\n // Determine successfully handled chains (exclude unprocessed/error chains)\n const unprocessedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = request.chainIds.filter(\n (chainId) => !unprocessedChains.has(chainId),\n );\n\n // When token detection is off and we filtered out all balance data (e.g. new\n // account with empty state), do not claim any chain as handled so that RPC\n // middleware can still process them and fetch native balances (ETH, MATIC, etc.).\n if (\n !this.#tokenDetectionEnabled() &&\n (!response.assetsBalance ||\n Object.keys(response.assetsBalance).length === 0)\n ) {\n successfullyHandledChains = [];\n }\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Remove successfully handled chains from request for next middleware\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n\n return next({\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n });\n }\n\n // No chains handled - pass context unchanged\n return next(context);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Store state accessor for filtering when tokenDetectionEnabled is false\n if (subscriptionRequest.getAssetsState) {\n this.#getAssetsState = subscriptionRequest.getAssetsState;\n }\n\n // Try all requested chains - API will handle unsupported ones via unprocessedNetworks\n const chainsToSubscribe = request.chainIds;\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Handle subscription update - update both chains AND request (for accounts)\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.chains = chainsToSubscribe;\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function for this subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (!subscription?.request) {\n return;\n }\n\n // Use stored request (which gets updated on account changes)\n const fetchResponse = await this.fetch({\n ...subscription.request,\n chainIds: subscription.chains,\n });\n\n // Report update to AssetsController via callback\n await subscription.onAssetsUpdate(fetchResponse);\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription with request for account updates\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n chains: chainsToSubscribe,\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // Clean up timers\n if (this.#chainsRefreshTimer) {\n clearInterval(this.#chainsRefreshTimer);\n }\n\n // Clean up subscriptions\n super.destroy();\n }\n}\n"]}
@@ -1,18 +1,37 @@
1
1
  import { ApiPlatformClient } from "@metamask/core-backend";
2
2
  import type { DataSourceState, SubscriptionRequest } from "./AbstractDataSource.cjs";
3
3
  import { AbstractDataSource } from "./AbstractDataSource.cjs";
4
- import type { ChainId, DataRequest, DataResponse, Middleware } from "../types.cjs";
4
+ import type { ChainId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from "../types.cjs";
5
5
  declare const CONTROLLER_NAME = "AccountsApiDataSource";
6
6
  export type AccountsApiDataSourceAllowedActions = never;
7
7
  export type AccountsApiDataSourceState = DataSourceState;
8
- export type AccountsApiDataSourceOptions = {
8
+ /** Optional configuration for AccountsApiDataSource. */
9
+ export type AccountsApiDataSourceConfig = {
10
+ /** Polling interval in ms (default: 30000) */
11
+ pollInterval?: number;
12
+ /**
13
+ * Function returning whether token detection is enabled (default: () => true).
14
+ * When it returns false, balances are only returned for tokens already in state.
15
+ * Using a getter avoids stale values when the user toggles the preference at runtime.
16
+ */
17
+ tokenDetectionEnabled?: () => boolean;
18
+ };
19
+ export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
9
20
  /** ApiPlatformClient for API calls with caching */
10
21
  queryApiClient: ApiPlatformClient;
11
22
  /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
12
23
  onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
13
- pollInterval?: number;
14
24
  state?: Partial<AccountsApiDataSourceState>;
15
25
  };
26
+ /**
27
+ * Filter a response to only include balances for assets already in state.
28
+ * Used when tokenDetectionEnabled is false to prevent adding new tokens.
29
+ *
30
+ * @param response - The fetch response to filter.
31
+ * @param assetsState - Current assets controller state to check existing balances against.
32
+ * @returns A new response with only known asset balances.
33
+ */
34
+ export declare function filterResponseToKnownAssets(response: DataResponse, assetsState: AssetsControllerStateInternal): DataResponse;
16
35
  /**
17
36
  * Data source for fetching balances from the MetaMask Accounts API.
18
37
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AccountsApiDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAE3D,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAA6B;AAC9B,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D,OAAO,KAAK,EACV,OAAO,EAGP,WAAW,EACX,YAAY,EACZ,UAAU,EACX,qBAAiB;AAOlB,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAWhD,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAMxD,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,MAAM,MAAM,4BAA4B,GAAG;IACzC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAuCF;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;gBAea,OAAO,EAAE,4BAA4B;IA2E3C,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA6IxD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAuDjC;IAMK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqExE,OAAO,IAAI,IAAI;CAShB"}
1
+ {"version":3,"file":"AccountsApiDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAO3D,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAA6B;AAC9B,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D,OAAO,KAAK,EACV,OAAO,EAGP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,6BAA6B,EAC9B,qBAAiB;AAOlB,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAWhD,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAMxD,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,wDAAwD;AACxD,MAAM,MAAM,2BAA2B,GAAG;IACxC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,2BAA2B,GAAG;IACvE,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,6BAA6B,GACzC,YAAY,CAqCd;AAMD;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;gBAqBa,OAAO,EAAE,4BAA4B;IA6E3C,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkJxD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAkEjC;IAMK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ExE,OAAO,IAAI,IAAI;CAShB"}
@@ -1,18 +1,37 @@
1
1
  import { ApiPlatformClient } from "@metamask/core-backend";
2
2
  import type { DataSourceState, SubscriptionRequest } from "./AbstractDataSource.mjs";
3
3
  import { AbstractDataSource } from "./AbstractDataSource.mjs";
4
- import type { ChainId, DataRequest, DataResponse, Middleware } from "../types.mjs";
4
+ import type { ChainId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from "../types.mjs";
5
5
  declare const CONTROLLER_NAME = "AccountsApiDataSource";
6
6
  export type AccountsApiDataSourceAllowedActions = never;
7
7
  export type AccountsApiDataSourceState = DataSourceState;
8
- export type AccountsApiDataSourceOptions = {
8
+ /** Optional configuration for AccountsApiDataSource. */
9
+ export type AccountsApiDataSourceConfig = {
10
+ /** Polling interval in ms (default: 30000) */
11
+ pollInterval?: number;
12
+ /**
13
+ * Function returning whether token detection is enabled (default: () => true).
14
+ * When it returns false, balances are only returned for tokens already in state.
15
+ * Using a getter avoids stale values when the user toggles the preference at runtime.
16
+ */
17
+ tokenDetectionEnabled?: () => boolean;
18
+ };
19
+ export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
9
20
  /** ApiPlatformClient for API calls with caching */
10
21
  queryApiClient: ApiPlatformClient;
11
22
  /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
12
23
  onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
13
- pollInterval?: number;
14
24
  state?: Partial<AccountsApiDataSourceState>;
15
25
  };
26
+ /**
27
+ * Filter a response to only include balances for assets already in state.
28
+ * Used when tokenDetectionEnabled is false to prevent adding new tokens.
29
+ *
30
+ * @param response - The fetch response to filter.
31
+ * @param assetsState - Current assets controller state to check existing balances against.
32
+ * @returns A new response with only known asset balances.
33
+ */
34
+ export declare function filterResponseToKnownAssets(response: DataResponse, assetsState: AssetsControllerStateInternal): DataResponse;
16
35
  /**
17
36
  * Data source for fetching balances from the MetaMask Accounts API.
18
37
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AccountsApiDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAE3D,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAA6B;AAC9B,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D,OAAO,KAAK,EACV,OAAO,EAGP,WAAW,EACX,YAAY,EACZ,UAAU,EACX,qBAAiB;AAOlB,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAWhD,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAMxD,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,MAAM,MAAM,4BAA4B,GAAG;IACzC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAuCF;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;gBAea,OAAO,EAAE,4BAA4B;IA2E3C,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA6IxD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAuDjC;IAMK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqExE,OAAO,IAAI,IAAI;CAShB"}
1
+ {"version":3,"file":"AccountsApiDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAO3D,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAA6B;AAC9B,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D,OAAO,KAAK,EACV,OAAO,EAGP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,6BAA6B,EAC9B,qBAAiB;AAOlB,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAWhD,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAMxD,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,wDAAwD;AACxD,MAAM,MAAM,2BAA2B,GAAG;IACxC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,2BAA2B,GAAG;IACvE,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAgCF;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,6BAA6B,GACzC,YAAY,CAqCd;AAMD;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;gBAqBa,OAAO,EAAE,4BAA4B;IA6E3C,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAkJxD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAkEjC;IAMK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ExE,OAAO,IAAI,IAAI;CAShB"}
@@ -9,8 +9,9 @@ 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 _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_initializeActiveChains, _AccountsApiDataSource_refreshActiveChains, _AccountsApiDataSource_fetchActiveChains, _AccountsApiDataSource_processV5Balances;
12
+ var _AccountsApiDataSource_instances, _AccountsApiDataSource_onActiveChainsUpdated, _AccountsApiDataSource_pollInterval, _AccountsApiDataSource_tokenDetectionEnabled, _AccountsApiDataSource_apiClient, _AccountsApiDataSource_chainsRefreshTimer, _AccountsApiDataSource_getAssetsState, _AccountsApiDataSource_initializeActiveChains, _AccountsApiDataSource_refreshActiveChains, _AccountsApiDataSource_fetchActiveChains, _AccountsApiDataSource_processV5Balances;
13
13
  import { ApiPlatformClient } from "@metamask/core-backend";
14
+ import { isCaipChainId, KnownCaipNamespace, toCaipChainId } from "@metamask/utils";
14
15
  import { AbstractDataSource } from "./AbstractDataSource.mjs";
15
16
  import { projectLogger, createModuleLogger } from "../logger.mjs";
16
17
  import { normalizeAssetId } from "../utils.mjs";
@@ -29,29 +30,61 @@ const defaultState = {
29
30
  function decimalToChainId(decimalChainId) {
30
31
  // Handle both decimal numbers and already-formatted CAIP chain IDs
31
32
  if (typeof decimalChainId === 'string') {
32
- // If already a CAIP chain ID (e.g., "eip155:1"), return as-is
33
- if (decimalChainId.startsWith('eip155:')) {
33
+ if (isCaipChainId(decimalChainId)) {
34
34
  return decimalChainId;
35
35
  }
36
- // If it's a string number, convert
37
- return `eip155:${decimalChainId}`;
36
+ return toCaipChainId(KnownCaipNamespace.Eip155, decimalChainId);
38
37
  }
39
- return `eip155:${decimalChainId}`;
38
+ return toCaipChainId(KnownCaipNamespace.Eip155, String(decimalChainId));
40
39
  }
41
40
  /**
42
41
  * Convert a CAIP-2 chain ID from the API response to our ChainId type.
43
42
  * Handles both formats: "eip155:1" or just "1" (decimal).
43
+ * Uses @metamask/utils for CAIP parsing.
44
44
  *
45
45
  * @param chainIdStr - The chain ID string to convert.
46
46
  * @returns The normalized ChainId.
47
47
  */
48
48
  function caipChainIdToChainId(chainIdStr) {
49
- // If already in CAIP-2 format, return as-is
50
- if (chainIdStr.includes(':')) {
49
+ if (isCaipChainId(chainIdStr)) {
51
50
  return chainIdStr;
52
51
  }
53
- // If decimal number, convert to CAIP-2
54
- return `eip155:${chainIdStr}`;
52
+ return toCaipChainId(KnownCaipNamespace.Eip155, chainIdStr);
53
+ }
54
+ /**
55
+ * Filter a response to only include balances for assets already in state.
56
+ * Used when tokenDetectionEnabled is false to prevent adding new tokens.
57
+ *
58
+ * @param response - The fetch response to filter.
59
+ * @param assetsState - Current assets controller state to check existing balances against.
60
+ * @returns A new response with only known asset balances.
61
+ */
62
+ export function filterResponseToKnownAssets(response, assetsState) {
63
+ if (!response.assetsBalance) {
64
+ return response;
65
+ }
66
+ const filteredBalance = {};
67
+ for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
68
+ const existingBalances = assetsState.assetsBalance[accountId];
69
+ if (!existingBalances) {
70
+ // Account has no balances in state yet — skip all its tokens
71
+ continue;
72
+ }
73
+ const filtered = {};
74
+ for (const [assetId, balance] of Object.entries(accountBalances)) {
75
+ // Only include assets already tracked in state
76
+ if (assetId in existingBalances) {
77
+ filtered[assetId] = balance;
78
+ }
79
+ }
80
+ if (Object.keys(filtered).length > 0) {
81
+ filteredBalance[accountId] = filtered;
82
+ }
83
+ }
84
+ return {
85
+ ...response,
86
+ assetsBalance: Object.keys(filteredBalance).length > 0 ? filteredBalance : undefined,
87
+ };
55
88
  }
56
89
  // ============================================================================
57
90
  // ACCOUNTS API DATA SOURCE
@@ -71,12 +104,17 @@ export class AccountsApiDataSource extends AbstractDataSource {
71
104
  _AccountsApiDataSource_instances.add(this);
72
105
  _AccountsApiDataSource_onActiveChainsUpdated.set(this, void 0);
73
106
  _AccountsApiDataSource_pollInterval.set(this, void 0);
107
+ /** Getter avoids stale value when user toggles token detection at runtime. */
108
+ _AccountsApiDataSource_tokenDetectionEnabled.set(this, void 0);
74
109
  /** ApiPlatformClient for cached API calls */
75
110
  _AccountsApiDataSource_apiClient.set(this, void 0);
76
111
  /** Chains refresh timer */
77
112
  _AccountsApiDataSource_chainsRefreshTimer.set(this, null);
113
+ /** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */
114
+ _AccountsApiDataSource_getAssetsState.set(this, void 0);
78
115
  __classPrivateFieldSet(this, _AccountsApiDataSource_onActiveChainsUpdated, options.onActiveChainsUpdated, "f");
79
116
  __classPrivateFieldSet(this, _AccountsApiDataSource_pollInterval, options.pollInterval ?? DEFAULT_POLL_INTERVAL, "f");
117
+ __classPrivateFieldSet(this, _AccountsApiDataSource_tokenDetectionEnabled, options.tokenDetectionEnabled ?? (() => true), "f");
80
118
  __classPrivateFieldSet(this, _AccountsApiDataSource_apiClient, options.queryApiClient, "f");
81
119
  __classPrivateFieldGet(this, _AccountsApiDataSource_instances, "m", _AccountsApiDataSource_initializeActiveChains).call(this).catch(console.error);
82
120
  }
@@ -87,7 +125,7 @@ export class AccountsApiDataSource extends AbstractDataSource {
87
125
  // FETCH
88
126
  // ============================================================================
89
127
  async fetch(request) {
90
- const response = {};
128
+ let response = {};
91
129
  // Filter to only chains supported by Accounts API
92
130
  const supportedChains = new Set(this.state.activeChains);
93
131
  const chainsToFetch = request.chainIds.filter((chainId) => supportedChains.has(chainId));
@@ -140,6 +178,10 @@ export class AccountsApiDataSource extends AbstractDataSource {
140
178
  response.errors[chainId] = 'Chain not supported by Accounts API';
141
179
  }
142
180
  }
181
+ // When token detection is disabled, filter out tokens not already in state
182
+ if (!__classPrivateFieldGet(this, _AccountsApiDataSource_tokenDetectionEnabled, "f").call(this) && __classPrivateFieldGet(this, _AccountsApiDataSource_getAssetsState, "f")) {
183
+ response = filterResponseToKnownAssets(response, __classPrivateFieldGet(this, _AccountsApiDataSource_getAssetsState, "f").call(this));
184
+ }
143
185
  return response;
144
186
  }
145
187
  // ============================================================================
@@ -179,6 +221,14 @@ export class AccountsApiDataSource extends AbstractDataSource {
179
221
  // Determine successfully handled chains (exclude unprocessed/error chains)
180
222
  const unprocessedChains = new Set(Object.keys(response.errors ?? {}));
181
223
  successfullyHandledChains = request.chainIds.filter((chainId) => !unprocessedChains.has(chainId));
224
+ // When token detection is off and we filtered out all balance data (e.g. new
225
+ // account with empty state), do not claim any chain as handled so that RPC
226
+ // middleware can still process them and fetch native balances (ETH, MATIC, etc.).
227
+ if (!__classPrivateFieldGet(this, _AccountsApiDataSource_tokenDetectionEnabled, "f").call(this) &&
228
+ (!response.assetsBalance ||
229
+ Object.keys(response.assetsBalance).length === 0)) {
230
+ successfullyHandledChains = [];
231
+ }
182
232
  }
183
233
  catch (error) {
184
234
  log('Middleware fetch failed', { error });
@@ -204,6 +254,10 @@ export class AccountsApiDataSource extends AbstractDataSource {
204
254
  // ============================================================================
205
255
  async subscribe(subscriptionRequest) {
206
256
  const { request, subscriptionId, isUpdate } = subscriptionRequest;
257
+ // Store state accessor for filtering when tokenDetectionEnabled is false
258
+ if (subscriptionRequest.getAssetsState) {
259
+ __classPrivateFieldSet(this, _AccountsApiDataSource_getAssetsState, subscriptionRequest.getAssetsState, "f");
260
+ }
207
261
  // Try all requested chains - API will handle unsupported ones via unprocessedNetworks
208
262
  const chainsToSubscribe = request.chainIds;
209
263
  if (chainsToSubscribe.length === 0) {
@@ -268,7 +322,7 @@ export class AccountsApiDataSource extends AbstractDataSource {
268
322
  super.destroy();
269
323
  }
270
324
  }
271
- _AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_initializeActiveChains =
325
+ _AccountsApiDataSource_onActiveChainsUpdated = new WeakMap(), _AccountsApiDataSource_pollInterval = new WeakMap(), _AccountsApiDataSource_tokenDetectionEnabled = new WeakMap(), _AccountsApiDataSource_apiClient = new WeakMap(), _AccountsApiDataSource_chainsRefreshTimer = new WeakMap(), _AccountsApiDataSource_getAssetsState = new WeakMap(), _AccountsApiDataSource_instances = new WeakSet(), _AccountsApiDataSource_initializeActiveChains =
272
326
  // ============================================================================
273
327
  // INITIALIZATION
274
328
  // ============================================================================