@metamask-previews/assets-controllers 93.1.0-preview-d2037635 → 94.0.0-preview-a4b203f

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 (36) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/dist/TokenBalancesController.cjs +342 -378
  3. package/dist/TokenBalancesController.cjs.map +1 -1
  4. package/dist/TokenBalancesController.d.cts +17 -38
  5. package/dist/TokenBalancesController.d.cts.map +1 -1
  6. package/dist/TokenBalancesController.d.mts +17 -38
  7. package/dist/TokenBalancesController.d.mts.map +1 -1
  8. package/dist/TokenBalancesController.mjs +342 -378
  9. package/dist/TokenBalancesController.mjs.map +1 -1
  10. package/dist/TokenDetectionController.cjs +131 -207
  11. package/dist/TokenDetectionController.cjs.map +1 -1
  12. package/dist/TokenDetectionController.d.cts +38 -12
  13. package/dist/TokenDetectionController.d.cts.map +1 -1
  14. package/dist/TokenDetectionController.d.mts +38 -12
  15. package/dist/TokenDetectionController.d.mts.map +1 -1
  16. package/dist/TokenDetectionController.mjs +132 -208
  17. package/dist/TokenDetectionController.mjs.map +1 -1
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +1 -1
  20. package/dist/index.d.cts.map +1 -1
  21. package/dist/index.d.mts +1 -1
  22. package/dist/index.d.mts.map +1 -1
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +13 -2
  25. package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -1
  26. package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -1
  27. package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -1
  28. package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +13 -2
  29. package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -1
  30. package/dist/multi-chain-accounts-service/types.cjs.map +1 -1
  31. package/dist/multi-chain-accounts-service/types.d.cts +2 -1
  32. package/dist/multi-chain-accounts-service/types.d.cts.map +1 -1
  33. package/dist/multi-chain-accounts-service/types.d.mts +2 -1
  34. package/dist/multi-chain-accounts-service/types.d.mts.map +1 -1
  35. package/dist/multi-chain-accounts-service/types.mjs.map +1 -1
  36. package/package.json +1 -1
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
11
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
12
  };
13
- var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_normalizeAccountAddresses, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_onAccountChanged, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
13
+ var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_allIgnoredTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_websocketActivePollingInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_isUnlocked, _TokenBalancesController_requestedChainIds, _TokenBalancesController_statusChangeDebouncer, _TokenBalancesController_subscribeToControllers, _TokenBalancesController_registerActions, _TokenBalancesController_normalizeAccountAddresses, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_stopAllPolling, _TokenBalancesController_getTargetChains, _TokenBalancesController_getAccountsAndJwt, _TokenBalancesController_fetchAllBalances, _TokenBalancesController_filterByTokenAddresses, _TokenBalancesController_getAccountsToProcess, _TokenBalancesController_applyTokenBalancesToState, _TokenBalancesController_buildNativeBalanceUpdates, _TokenBalancesController_buildStakedBalanceUpdates, _TokenBalancesController_importUntrackedTokens, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_onAccountChanged, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.TokenBalancesController = exports.parseAssetType = exports.caipChainIdToHex = void 0;
16
16
  const providers_1 = require("@ethersproject/providers");
@@ -33,19 +33,14 @@ const metadata = {
33
33
  usedInUi: true,
34
34
  },
35
35
  };
36
- // endregion
37
- // ────────────────────────────────────────────────────────────────────────────
38
- // region: Helper utilities
39
36
  const draft = (base, fn) => (0, immer_1.produce)(base, fn);
40
37
  const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
41
38
  const checksum = (addr) => (0, controller_utils_1.toChecksumHexAddress)(addr);
42
39
  /**
43
- * Convert CAIP chain ID or hex chain ID to hex chain ID
44
- * Handles both CAIP-2 format (e.g., "eip155:1") and hex format (e.g., "0x1")
40
+ * Convert CAIP chain ID or hex chain ID to hex chain ID.
45
41
  *
46
- * @param chainId - CAIP chain ID (e.g., "eip155:1") or hex chain ID (e.g., "0x1")
47
- * @returns Hex chain ID (e.g., "0x1")
48
- * @throws {Error} If chainId is neither a valid CAIP-2 chain ID nor a hex string
42
+ * @param chainId - CAIP chain ID or hex chain ID.
43
+ * @returns Hex chain ID.
49
44
  */
50
45
  const caipChainIdToHex = (chainId) => {
51
46
  if ((0, utils_1.isStrictHexString)(chainId)) {
@@ -58,31 +53,25 @@ const caipChainIdToHex = (chainId) => {
58
53
  };
59
54
  exports.caipChainIdToHex = caipChainIdToHex;
60
55
  /**
61
- * Extract token address from asset type
62
- * Returns tuple of [tokenAddress, isNativeToken] or null if invalid
56
+ * Extract token address from asset type.
63
57
  *
64
- * @param assetType - Asset type string (e.g., 'eip155:1/erc20:0x...' or 'eip155:1/slip44:60')
65
- * @returns Tuple of [tokenAddress, isNativeToken] or null if invalid
58
+ * @param assetType - Asset type string.
59
+ * @returns Tuple of [tokenAddress, isNativeToken] or null if invalid.
66
60
  */
67
61
  const parseAssetType = (assetType) => {
68
62
  if (!(0, utils_1.isCaipAssetType)(assetType)) {
69
63
  return null;
70
64
  }
71
65
  const parsed = (0, utils_1.parseCaipAssetType)(assetType);
72
- // ERC20 token (e.g., "eip155:1/erc20:0x...")
73
66
  if (parsed.assetNamespace === 'erc20') {
74
67
  return [parsed.assetReference, false];
75
68
  }
76
- // Native token (e.g., "eip155:1/slip44:60")
77
69
  if (parsed.assetNamespace === 'slip44') {
78
70
  return [ZERO_ADDRESS, true];
79
71
  }
80
72
  return null;
81
73
  };
82
74
  exports.parseAssetType = parseAssetType;
83
- // endregion
84
- // ────────────────────────────────────────────────────────────────────────────
85
- // region: Main controller
86
75
  class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPollingController)() {
87
76
  constructor({ messenger, interval = DEFAULT_INTERVAL_MS, websocketActivePollingInterval = DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, accountsApiChainIds = () => [], allowExternalServices = () => true, platform, }) {
88
77
  super({
@@ -109,6 +98,8 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
109
98
  _TokenBalancesController_intervalPollingTimers.set(this, new Map());
110
99
  /** Track if controller-level polling is active */
111
100
  _TokenBalancesController_isControllerPollingActive.set(this, false);
101
+ /** Track if the keyring is unlocked */
102
+ _TokenBalancesController_isUnlocked.set(this, false);
112
103
  /** Store original chainIds from startPolling to preserve intent */
113
104
  _TokenBalancesController_requestedChainIds.set(this, []);
114
105
  /** Debouncing for rapid status changes to prevent excessive HTTP calls */
@@ -118,52 +109,32 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
118
109
  });
119
110
  _TokenBalancesController_getProvider.set(this, (chainId) => {
120
111
  const { networkConfigurationsByChainId } = this.messenger.call('NetworkController:getState');
121
- const cfg = networkConfigurationsByChainId[chainId];
122
- const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
112
+ const networkConfig = networkConfigurationsByChainId[chainId];
113
+ const { networkClientId } = networkConfig.rpcEndpoints[networkConfig.defaultRpcEndpointIndex];
123
114
  const client = this.messenger.call('NetworkController:getNetworkClientById', networkClientId);
124
115
  return new providers_1.Web3Provider(client.provider);
125
116
  });
126
117
  _TokenBalancesController_getNetworkClient.set(this, (chainId) => {
127
118
  const { networkConfigurationsByChainId } = this.messenger.call('NetworkController:getState');
128
- const cfg = networkConfigurationsByChainId[chainId];
129
- const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
119
+ const networkConfig = networkConfigurationsByChainId[chainId];
120
+ const { networkClientId } = networkConfig.rpcEndpoints[networkConfig.defaultRpcEndpointIndex];
130
121
  return this.messenger.call('NetworkController:getNetworkClientById', networkClientId);
131
122
  });
132
- /**
133
- * Creates an AccountsApiBalanceFetcher that only supports chains in the accountsApiChainIds array
134
- *
135
- * @returns A BalanceFetcher that wraps AccountsApiBalanceFetcher with chainId filtering
136
- */
137
123
  _TokenBalancesController_createAccountsApiFetcher.set(this, () => {
138
124
  const originalFetcher = new api_balance_fetcher_1.AccountsApiBalanceFetcher(__classPrivateFieldGet(this, _TokenBalancesController_platform, "f"), __classPrivateFieldGet(this, _TokenBalancesController_getProvider, "f"));
139
125
  return {
140
- supports: (chainId) => {
141
- // Only support chains that are both:
142
- // 1. In our specified accountsApiChainIds array
143
- // 2. Actually supported by the AccountsApi
144
- return (__classPrivateFieldGet(this, _TokenBalancesController_accountsApiChainIds, "f").call(this).includes(chainId) &&
145
- originalFetcher.supports(chainId));
146
- },
126
+ supports: (chainId) => __classPrivateFieldGet(this, _TokenBalancesController_accountsApiChainIds, "f").call(this).includes(chainId) &&
127
+ originalFetcher.supports(chainId),
147
128
  fetch: originalFetcher.fetch.bind(originalFetcher),
148
129
  };
149
130
  });
150
131
  _TokenBalancesController_onTokensChanged.set(this, async (state) => {
151
132
  const changed = [];
152
133
  let hasChanges = false;
153
- // Get chains that have existing balances
154
- const chainsWithBalances = new Set();
155
- for (const address of Object.keys(this.state.tokenBalances)) {
156
- const addressKey = address;
157
- for (const chainId of Object.keys(this.state.tokenBalances[addressKey] || {})) {
158
- chainsWithBalances.add(chainId);
159
- }
160
- }
161
- // Only process chains that are explicitly mentioned in the incoming state change
162
134
  const incomingChainIds = new Set([
163
135
  ...Object.keys(state.allTokens),
164
136
  ...Object.keys(state.allDetectedTokens),
165
137
  ]);
166
- // Only proceed if there are actual changes to chains that have balances or are being added
167
138
  const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {
168
139
  const id = chainId;
169
140
  const hasTokensNow = (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||
@@ -172,20 +143,16 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
172
143
  const hadTokensBefore = (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id] && Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
173
144
  (__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
174
145
  Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
175
- // Check if there's an actual change in token state
176
146
  const hasTokenChange = !(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
177
147
  !(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]);
178
- // Process chains that have actual changes OR are new chains getting tokens
179
148
  return hasTokenChange || (!hadTokensBefore && hasTokensNow);
180
149
  });
181
- if (relevantChainIds.length === 0) {
182
- // No relevant changes, just update internal state
150
+ if (!relevantChainIds.length) {
183
151
  __classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
184
152
  __classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
185
153
  return;
186
154
  }
187
- // Handle both cleanup and updates in a single state update
188
- this.update((s) => {
155
+ this.update((currentState) => {
189
156
  for (const chainId of relevantChainIds) {
190
157
  const id = chainId;
191
158
  const hasTokensNow = (state.allTokens[id] &&
@@ -196,20 +163,20 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
196
163
  Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]).length > 0) ||
197
164
  (__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id] &&
198
165
  Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]).length > 0);
199
- if (!(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
200
- !(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id])) {
201
- if (hasTokensNow) {
202
- // Chain still has tokens - mark for async balance update
203
- changed.push(id);
204
- }
205
- else if (hadTokensBefore) {
206
- // Chain had tokens before but doesn't now - clean up balances immediately
207
- for (const address of Object.keys(s.tokenBalances)) {
208
- const addressKey = address;
209
- if (s.tokenBalances[addressKey]?.[id]) {
210
- s.tokenBalances[addressKey][id] = {};
211
- hasChanges = true;
212
- }
166
+ const tokensChanged = !(0, lodash_1.isEqual)(state.allTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[id]) ||
167
+ !(0, lodash_1.isEqual)(state.allDetectedTokens[id], __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[id]);
168
+ if (!tokensChanged) {
169
+ continue;
170
+ }
171
+ if (hasTokensNow) {
172
+ changed.push(id);
173
+ }
174
+ else if (hadTokensBefore) {
175
+ for (const address of Object.keys(currentState.tokenBalances)) {
176
+ const addressKey = address;
177
+ if (currentState.tokenBalances[addressKey]?.[id]) {
178
+ currentState.tokenBalances[addressKey][id] = {};
179
+ hasChanges = true;
213
180
  }
214
181
  }
215
182
  }
@@ -218,7 +185,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
218
185
  __classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
219
186
  __classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
220
187
  __classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, state.allIgnoredTokens, "f");
221
- // Only update balances for chains that still have tokens (and only if we haven't already updated state)
222
188
  if (changed.length && !hasChanges) {
223
189
  this.updateBalances({ chainIds: changed }).catch((error) => {
224
190
  console.warn('Error updating balances after token change:', error);
@@ -226,9 +192,7 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
226
192
  }
227
193
  });
228
194
  _TokenBalancesController_onNetworkChanged.set(this, (state) => {
229
- // Check if any networks were removed by comparing with previous state
230
195
  const currentNetworks = new Set(Object.keys(state.networkConfigurationsByChainId));
231
- // Get all networks that currently have balances
232
196
  const networksWithBalances = new Set();
233
197
  for (const address of Object.keys(this.state.tokenBalances)) {
234
198
  const addressKey = address;
@@ -236,83 +200,59 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
236
200
  networksWithBalances.add(network);
237
201
  }
238
202
  }
239
- // Find networks that were removed
240
203
  const removedNetworks = Array.from(networksWithBalances).filter((network) => !currentNetworks.has(network));
241
- if (removedNetworks.length > 0) {
242
- this.update((s) => {
243
- // Remove balances for all accounts on the deleted networks
244
- for (const address of Object.keys(s.tokenBalances)) {
245
- const addressKey = address;
246
- for (const removedNetwork of removedNetworks) {
247
- const networkKey = removedNetwork;
248
- if (s.tokenBalances[addressKey]?.[networkKey]) {
249
- delete s.tokenBalances[addressKey][networkKey];
250
- }
204
+ if (!removedNetworks.length) {
205
+ return;
206
+ }
207
+ this.update((currentState) => {
208
+ for (const address of Object.keys(currentState.tokenBalances)) {
209
+ const addressKey = address;
210
+ for (const removedNetwork of removedNetworks) {
211
+ const networkKey = removedNetwork;
212
+ if (currentState.tokenBalances[addressKey]?.[networkKey]) {
213
+ delete currentState.tokenBalances[addressKey][networkKey];
251
214
  }
252
215
  }
253
- });
254
- }
216
+ }
217
+ });
255
218
  });
256
219
  _TokenBalancesController_onAccountRemoved.set(this, (addr) => {
257
220
  if (!(0, utils_1.isStrictHexString)(addr) || !(0, controller_utils_1.isValidHexAddress)(addr)) {
258
221
  return;
259
222
  }
260
- this.update((s) => {
261
- delete s.tokenBalances[addr];
223
+ this.update((currentState) => {
224
+ delete currentState.tokenBalances[addr];
262
225
  });
263
226
  });
264
- /**
265
- * Handle account selection changes
266
- * Triggers immediate balance fetch to ensure we have the latest balances
267
- * since WebSocket only provides updates for changes going forward
268
- */
269
227
  _TokenBalancesController_onAccountChanged.set(this, () => {
270
- // Fetch balances for all chains with tokens when account changes
271
228
  const chainIds = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
272
- if (chainIds.length > 0) {
273
- this.updateBalances({ chainIds }).catch(() => {
274
- // Silently handle polling errors
275
- });
229
+ if (!chainIds.length) {
230
+ return;
276
231
  }
232
+ this.updateBalances({ chainIds }).catch(() => {
233
+ // Silently handle polling errors
234
+ });
277
235
  });
278
- // ────────────────────────────────────────────────────────────────────────────
279
- // AccountActivityService event handlers
280
- /**
281
- * Handle real-time balance updates from AccountActivityService
282
- * Processes balance updates and updates the token balance state
283
- * If any balance update has an error, triggers fallback polling for the chain
284
- *
285
- * @param options0 - Balance update parameters
286
- * @param options0.address - Account address
287
- * @param options0.chain - CAIP chain identifier
288
- * @param options0.updates - Array of balance updates for the account
289
- */
290
236
  _TokenBalancesController_onAccountActivityBalanceUpdate.set(this, async ({ address, chain, updates, }) => {
291
237
  const chainId = (0, exports.caipChainIdToHex)(chain);
292
238
  const checksummedAccount = checksum(address);
293
239
  try {
294
- // Process all balance updates at once
295
240
  const { tokenBalances, newTokens, nativeBalanceUpdates } = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_prepareBalanceUpdates).call(this, updates, checksummedAccount, chainId);
296
- // Update state once with all token balances
297
241
  if (tokenBalances.length > 0) {
298
242
  this.update((state) => {
299
243
  var _a, _b;
300
- // Temporary until ADR to normalize all keys - tokenBalances state requires: account in lowercase, token in checksum
301
244
  const lowercaseAccount = checksummedAccount.toLowerCase();
302
245
  (_a = state.tokenBalances)[lowercaseAccount] ?? (_a[lowercaseAccount] = {});
303
246
  (_b = state.tokenBalances[lowercaseAccount])[chainId] ?? (_b[chainId] = {});
304
- // Apply all token balance updates
305
247
  for (const { tokenAddress, balance } of tokenBalances) {
306
248
  state.tokenBalances[lowercaseAccount][chainId][tokenAddress] =
307
249
  balance;
308
250
  }
309
251
  });
310
252
  }
311
- // Update native balances in AccountTrackerController
312
253
  if (nativeBalanceUpdates.length > 0) {
313
254
  this.messenger.call('AccountTrackerController:updateNativeBalances', nativeBalanceUpdates);
314
255
  }
315
- // Import any new tokens that were discovered (balance already updated from websocket)
316
256
  if (newTokens.length > 0) {
317
257
  await this.messenger.call('TokenDetectionController:addDetectedTokensViaWs', {
318
258
  tokensSlice: newTokens,
@@ -323,35 +263,22 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
323
263
  catch (error) {
324
264
  console.warn(`Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`, error);
325
265
  console.warn('Balance update data:', JSON.stringify(updates, null, 2));
326
- // On error, trigger fallback polling
327
266
  await this.updateBalances({ chainIds: [chainId] }).catch(() => {
328
267
  // Silently handle polling errors
329
268
  });
330
269
  }
331
270
  });
332
- /**
333
- * Handle status changes from AccountActivityService
334
- * Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes
335
- *
336
- * @param options0 - Status change event data
337
- * @param options0.chainIds - Array of chain identifiers
338
- * @param options0.status - Connection status ('up' for connected, 'down' for disconnected)
339
- */
340
271
  _TokenBalancesController_onAccountActivityStatusChanged.set(this, ({ chainIds, status, }) => {
341
- // Update pending changes (latest status wins for each chain)
342
272
  for (const chainId of chainIds) {
343
273
  __classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.set(chainId, status);
344
274
  }
345
- // Clear existing timer to extend debounce window
346
275
  if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
347
276
  clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
348
277
  }
349
- // Set new timer - only process changes after activity settles
350
278
  __classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = setTimeout(() => {
351
279
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_processAccumulatedStatusChanges).call(this);
352
- }, 5000); // 5-second debounce window
280
+ }, 5000);
353
281
  });
354
- // Normalize all account addresses to lowercase in existing state
355
282
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_normalizeAccountAddresses).call(this);
356
283
  __classPrivateFieldSet(this, _TokenBalancesController_platform, platform ?? 'extension', "f");
357
284
  __classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
@@ -359,7 +286,6 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
359
286
  __classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
360
287
  __classPrivateFieldSet(this, _TokenBalancesController_websocketActivePollingInterval, websocketActivePollingInterval, "f");
361
288
  __classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
362
- // Strategy order: API first, then RPC fallback
363
289
  __classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
364
290
  ...(accountsApiChainIds().length > 0 && allowExternalServices()
365
291
  ? [__classPrivateFieldGet(this, _TokenBalancesController_createAccountsApiFetcher, "f").call(this)]
@@ -370,303 +296,173 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
370
296
  })),
371
297
  ], "f");
372
298
  this.setIntervalLength(interval);
373
- // initial token state & subscriptions
374
299
  const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messenger.call('TokensController:getState');
375
300
  __classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
376
301
  __classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
377
302
  __classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, allIgnoredTokens, "f");
378
- this.messenger.subscribe('TokensController:stateChange', (tokensState) => {
379
- __classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
380
- console.warn('Error handling token state change:', error);
381
- });
382
- });
383
- this.messenger.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
384
- this.messenger.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
385
- this.messenger.subscribe('AccountsController:selectedEvmAccountChange', __classPrivateFieldGet(this, _TokenBalancesController_onAccountChanged, "f"));
386
- // Register action handlers for polling interval control
387
- this.messenger.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
388
- this.messenger.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
389
- // Subscribe to AccountActivityService balance updates for real-time updates
390
- this.messenger.subscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").bind(this));
391
- // Subscribe to AccountActivityService status changes for dynamic polling management
392
- this.messenger.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
303
+ const { isUnlocked } = this.messenger.call('KeyringController:getState');
304
+ __classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, isUnlocked, "f");
305
+ __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_subscribeToControllers).call(this);
306
+ __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_registerActions).call(this);
393
307
  }
394
308
  /**
395
- * Override to support per-chain polling intervals by grouping chains by interval
309
+ * Whether the controller is active (keyring is unlocked).
310
+ * When locked, balance updates should be skipped.
396
311
  *
397
- * @param options0 - The polling options
398
- * @param options0.chainIds - Chain IDs to start polling for
312
+ * @returns Whether the keyring is unlocked.
399
313
  */
314
+ get isActive() {
315
+ return __classPrivateFieldGet(this, _TokenBalancesController_isUnlocked, "f");
316
+ }
400
317
  _startPolling({ chainIds }) {
401
- // Store the original chainIds to preserve intent across config updates
402
318
  __classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [...chainIds], "f");
403
319
  __classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, true, "f");
404
320
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, chainIds, true);
405
321
  }
406
- /**
407
- * Override to handle our custom polling approach
408
- *
409
- * @param tokenSetId - The token set ID to stop polling for
410
- */
411
322
  _stopPollingByPollingTokenSetId(tokenSetId) {
412
- let parsedTokenSetId;
413
323
  let chainsToStop = [];
414
324
  try {
415
- parsedTokenSetId = JSON.parse(tokenSetId);
416
- chainsToStop = parsedTokenSetId.chainIds || [];
325
+ const parsedTokenSetId = JSON.parse(tokenSetId);
326
+ chainsToStop = parsedTokenSetId.chainIds ?? [];
417
327
  }
418
328
  catch (error) {
419
329
  console.warn('Failed to parse tokenSetId, stopping all polling:', error);
420
- // Fallback: stop all polling if we can't parse the tokenSetId
421
- __classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
422
- __classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
423
- __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
424
- __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
330
+ __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_stopAllPolling).call(this);
425
331
  return;
426
332
  }
427
- // Compare with current chains - only stop if it matches our current session
428
333
  const currentChainsSet = new Set(__classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"));
429
334
  const stopChainsSet = new Set(chainsToStop);
430
- // Check if this stop request is for our current session
431
335
  const isCurrentSession = currentChainsSet.size === stopChainsSet.size &&
432
336
  [...currentChainsSet].every((chain) => stopChainsSet.has(chain));
433
337
  if (isCurrentSession) {
434
- __classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
435
- __classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
436
- __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
437
- __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
338
+ __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_stopAllPolling).call(this);
438
339
  }
439
340
  }
440
- /**
441
- * Get polling configuration for a chain (includes default fallback)
442
- *
443
- * @param chainId - The chain ID to get config for
444
- * @returns The polling configuration for the chain
445
- */
446
341
  getChainPollingConfig(chainId) {
447
342
  return (__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f")[chainId] ?? {
448
343
  interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"),
449
344
  });
450
345
  }
451
346
  async _executePoll({ chainIds, queryAllAccounts = false, }) {
452
- // This won't be called with our custom implementation, but keep for compatibility
453
347
  await this.updateBalances({ chainIds, queryAllAccounts });
454
348
  }
455
- /**
456
- * Update multiple chain polling configurations at once
457
- *
458
- * @param configs - Object mapping chain IDs to polling configurations
459
- * @param options - Optional configuration for the update behavior
460
- * @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
461
- */
462
349
  updateChainPollingConfigs(configs, options = { immediateUpdate: true }) {
463
350
  Object.assign(__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f"), configs);
464
- // If polling is currently active, restart with new interval groupings
465
351
  if (__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
466
- // Restart polling with immediate fetch by default, unless explicitly disabled
467
352
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, __classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"), options.immediateUpdate);
468
353
  }
469
354
  }
470
- async updateBalances({ chainIds, queryAllAccounts = false, } = {}) {
471
- const targetChains = chainIds ?? __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
472
- if (!targetChains.length) {
355
+ async updateBalances({ chainIds, tokenAddresses, queryAllAccounts = false, } = {}) {
356
+ if (!this.isActive) {
473
357
  return;
474
358
  }
475
- const { address: selected } = this.messenger.call('AccountsController:getSelectedAccount');
476
- const allAccounts = this.messenger.call('AccountsController:listAccounts');
477
- const jwtToken = await (0, controller_utils_1.safelyExecuteWithTimeout)(() => {
478
- return this.messenger.call('AuthenticationController:getBearerToken');
479
- }, false, 5000);
480
- const aggregated = [];
481
- let remainingChains = [...targetChains];
482
- // Try each fetcher in order, removing successfully processed chains
483
- for (const fetcher of __classPrivateFieldGet(this, _TokenBalancesController_balanceFetchers, "f")) {
484
- const supportedChains = remainingChains.filter((c) => fetcher.supports(c));
485
- if (!supportedChains.length) {
486
- continue;
487
- }
488
- try {
489
- const result = await fetcher.fetch({
490
- chainIds: supportedChains,
491
- queryAllAccounts: queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
492
- selectedAccount: selected,
493
- allAccounts,
494
- jwtToken,
495
- });
496
- if (result.balances && result.balances.length > 0) {
497
- aggregated.push(...result.balances);
498
- // Remove chains that were successfully processed
499
- const processedChains = new Set(result.balances.map((b) => b.chainId));
500
- remainingChains = remainingChains.filter((chain) => !processedChains.has(chain));
501
- }
502
- // Add unprocessed chains back to remainingChains for next fetcher
503
- if (result.unprocessedChainIds &&
504
- result.unprocessedChainIds.length > 0) {
505
- const currentRemainingChains = remainingChains;
506
- const chainsToAdd = result.unprocessedChainIds.filter((chainId) => supportedChains.includes(chainId) &&
507
- !currentRemainingChains.includes(chainId));
508
- remainingChains.push(...chainsToAdd);
509
- }
510
- }
511
- catch (error) {
512
- console.warn(`Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`);
513
- // Continue to next fetcher (fallback)
514
- }
515
- // If all chains have been processed, break early
516
- if (remainingChains.length === 0) {
517
- break;
518
- }
359
+ const targetChains = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getTargetChains).call(this, chainIds);
360
+ if (!targetChains.length) {
361
+ return;
519
362
  }
520
- // Determine which accounts to process based on queryAllAccounts parameter
521
- const accountsToProcess = (queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"))
522
- ? allAccounts.map((a) => a.address)
523
- : [selected];
363
+ const { selectedAccount, allAccounts, jwtToken } = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getAccountsAndJwt).call(this);
364
+ const aggregatedBalances = await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_fetchAllBalances).call(this, {
365
+ targetChains,
366
+ selectedAccount,
367
+ allAccounts,
368
+ jwtToken,
369
+ queryAllAccounts: queryAllAccounts ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f"),
370
+ });
371
+ const filteredAggregated = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_filterByTokenAddresses).call(this, aggregatedBalances, tokenAddresses);
372
+ const accountsToProcess = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getAccountsToProcess).call(this, queryAllAccounts, allAccounts, selectedAccount);
524
373
  const prev = this.state;
525
- const next = draft(prev, (d) => {
526
- var _a, _b;
527
- // Initialize account and chain structures if they don't exist, but preserve existing balances
528
- for (const chainId of targetChains) {
529
- for (const account of accountsToProcess) {
530
- // Ensure the nested structure exists without overwriting existing balances
531
- (_a = d.tokenBalances)[account] ?? (_a[account] = {});
532
- (_b = d.tokenBalances[account])[chainId] ?? (_b[chainId] = {});
533
- // Initialize tokens from allTokens only if they don't exist yet
534
- const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
535
- if (chainTokens?.[account]) {
536
- Object.values(chainTokens[account]).forEach((token) => {
537
- const tokenAddress = checksum(token.address);
538
- // Only initialize if the token balance doesn't exist yet
539
- if (!(tokenAddress in d.tokenBalances[account][chainId])) {
540
- d.tokenBalances[account][chainId][tokenAddress] = '0x0';
541
- }
542
- });
543
- }
544
- // Initialize tokens from allDetectedTokens only if they don't exist yet
545
- const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
546
- if (detectedChainTokens?.[account]) {
547
- Object.values(detectedChainTokens[account]).forEach((token) => {
548
- const tokenAddress = checksum(token.address);
549
- // Only initialize if the token balance doesn't exist yet
550
- if (!(tokenAddress in d.tokenBalances[account][chainId])) {
551
- d.tokenBalances[account][chainId][tokenAddress] = '0x0';
552
- }
553
- });
554
- }
555
- }
556
- }
557
- // Update with actual fetched balances only if the value has changed
558
- aggregated.forEach(({ success, value, account, token, chainId }) => {
559
- var _a, _b;
560
- if (success && value !== undefined) {
561
- // Ensure all accounts we add/update are in lower-case
562
- const lowerCaseAccount = account.toLowerCase();
563
- const newBalance = (0, controller_utils_1.toHex)(value);
564
- const tokenAddress = checksum(token);
565
- const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
566
- // Only update if the balance has actually changed
567
- if (currentBalance !== newBalance) {
568
- ((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
569
- }
570
- }
571
- });
374
+ const next = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_applyTokenBalancesToState).call(this, {
375
+ prev,
376
+ targetChains,
377
+ accountsToProcess,
378
+ balances: filteredAggregated,
572
379
  });
573
380
  if (!(0, lodash_1.isEqual)(prev, next)) {
574
381
  this.update(() => next);
575
- const nativeBalances = aggregated.filter((r) => r.success && r.token === ZERO_ADDRESS);
576
- // Get current AccountTracker state to compare existing balances
577
382
  const accountTrackerState = this.messenger.call('AccountTrackerController:getState');
578
- // Update native token balances only if they have changed
579
- if (nativeBalances.length > 0) {
580
- const balanceUpdates = nativeBalances
581
- .map((balance) => ({
582
- address: balance.account,
583
- chainId: balance.chainId,
584
- balance: balance.value ? (0, controller_utils_1.BNToHex)(balance.value) : '0x0',
585
- }))
586
- .filter((update) => {
587
- const currentBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.balance;
588
- // Only include if the balance has actually changed
589
- return currentBalance !== update.balance;
590
- });
591
- if (balanceUpdates.length > 0) {
592
- this.messenger.call('AccountTrackerController:updateNativeBalances', balanceUpdates);
593
- }
383
+ const nativeUpdates = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_buildNativeBalanceUpdates).call(this, filteredAggregated, accountTrackerState);
384
+ if (nativeUpdates.length > 0) {
385
+ this.messenger.call('AccountTrackerController:updateNativeBalances', nativeUpdates);
594
386
  }
595
- // Filter and update staked balances in a single batch operation for better performance
596
- const stakedBalances = aggregated.filter((r) => {
597
- if (!r.success || r.token === ZERO_ADDRESS) {
598
- return false;
599
- }
600
- // Check if the chainId and token address match any staking contract
601
- const stakingContractAddress = AssetsContractController_1.STAKING_CONTRACT_ADDRESS_BY_CHAINID[r.chainId];
602
- return (stakingContractAddress &&
603
- stakingContractAddress.toLowerCase() === r.token.toLowerCase());
604
- });
605
- if (stakedBalances.length > 0) {
606
- const stakedBalanceUpdates = stakedBalances
607
- .map((balance) => ({
608
- address: balance.account,
609
- chainId: balance.chainId,
610
- stakedBalance: balance.value ? (0, controller_utils_1.toHex)(balance.value) : '0x0',
611
- }))
612
- .filter((update) => {
613
- const currentStakedBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.stakedBalance;
614
- // Only include if the staked balance has actually changed
615
- return currentStakedBalance !== update.stakedBalance;
616
- });
617
- if (stakedBalanceUpdates.length > 0) {
618
- this.messenger.call('AccountTrackerController:updateStakedBalances', stakedBalanceUpdates);
619
- }
387
+ const stakedUpdates = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_buildStakedBalanceUpdates).call(this, filteredAggregated, accountTrackerState);
388
+ if (stakedUpdates.length > 0) {
389
+ this.messenger.call('AccountTrackerController:updateStakedBalances', stakedUpdates);
620
390
  }
621
391
  }
392
+ await __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_importUntrackedTokens).call(this, filteredAggregated);
622
393
  }
623
394
  resetState() {
624
395
  this.update(() => ({ tokenBalances: {} }));
625
396
  }
626
- /**
627
- * Clean up all timers and resources when controller is destroyed
628
- */
629
397
  destroy() {
630
398
  __classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
631
399
  __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
632
400
  __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
633
- // Clean up debouncing timer
634
401
  if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
635
402
  clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
636
403
  __classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
637
404
  }
638
- // Unregister action handlers
639
405
  this.messenger.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
640
406
  this.messenger.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
641
407
  super.destroy();
642
408
  }
643
409
  }
644
410
  exports.TokenBalancesController = TokenBalancesController;
645
- _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountChanged = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_normalizeAccountAddresses = function _TokenBalancesController_normalizeAccountAddresses() {
411
+ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_accountsApiChainIds = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_allIgnoredTokens = new WeakMap(), _TokenBalancesController_defaultInterval = new WeakMap(), _TokenBalancesController_websocketActivePollingInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_isUnlocked = new WeakMap(), _TokenBalancesController_requestedChainIds = new WeakMap(), _TokenBalancesController_statusChangeDebouncer = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_createAccountsApiFetcher = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_onAccountChanged = new WeakMap(), _TokenBalancesController_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_subscribeToControllers = function _TokenBalancesController_subscribeToControllers() {
412
+ this.messenger.subscribe('TokensController:stateChange', (tokensState) => {
413
+ __classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
414
+ console.warn('Error handling token state change:', error);
415
+ });
416
+ });
417
+ this.messenger.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
418
+ this.messenger.subscribe('KeyringController:unlock', () => {
419
+ __classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, true, "f");
420
+ });
421
+ this.messenger.subscribe('KeyringController:lock', () => {
422
+ __classPrivateFieldSet(this, _TokenBalancesController_isUnlocked, false, "f");
423
+ });
424
+ this.messenger.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
425
+ this.messenger.subscribe('AccountsController:selectedEvmAccountChange', __classPrivateFieldGet(this, _TokenBalancesController_onAccountChanged, "f"));
426
+ this.messenger.subscribe('AccountActivityService:balanceUpdated', (event) => {
427
+ __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").call(this, event).catch((error) => {
428
+ console.warn('Error handling balance update:', error);
429
+ });
430
+ });
431
+ this.messenger.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
432
+ this.messenger.subscribe('TransactionController:transactionConfirmed', (transactionMeta) => {
433
+ this.updateBalances({
434
+ chainIds: [transactionMeta.chainId],
435
+ }).catch(() => {
436
+ // Silently handle balance update errors
437
+ });
438
+ });
439
+ this.messenger.subscribe('TransactionController:incomingTransactionsReceived', (incomingTransactions) => {
440
+ this.updateBalances({
441
+ chainIds: incomingTransactions.map((tx) => tx.chainId),
442
+ }).catch(() => {
443
+ // Silently handle balance update errors
444
+ });
445
+ });
446
+ }, _TokenBalancesController_registerActions = function _TokenBalancesController_registerActions() {
447
+ this.messenger.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
448
+ this.messenger.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
449
+ }, _TokenBalancesController_normalizeAccountAddresses = function _TokenBalancesController_normalizeAccountAddresses() {
450
+ var _a;
646
451
  const currentState = this.state.tokenBalances;
647
452
  const normalizedBalances = {};
648
- // Iterate through all accounts and normalize to lowercase
649
453
  for (const address of Object.keys(currentState)) {
650
454
  const lowercaseAddress = address.toLowerCase();
651
455
  const accountBalances = currentState[address];
652
456
  if (!accountBalances) {
653
457
  continue;
654
458
  }
655
- // If this lowercase address doesn't exist yet, create it
656
- if (!normalizedBalances[lowercaseAddress]) {
657
- normalizedBalances[lowercaseAddress] = {};
658
- }
659
- // Merge chain data
459
+ normalizedBalances[lowercaseAddress] ?? (normalizedBalances[lowercaseAddress] = {});
660
460
  for (const chainId of Object.keys(accountBalances)) {
661
461
  const chainIdKey = chainId;
662
- if (!normalizedBalances[lowercaseAddress][chainIdKey]) {
663
- normalizedBalances[lowercaseAddress][chainIdKey] = {};
664
- }
665
- // Merge token balances (later values override earlier ones if duplicates exist)
462
+ (_a = normalizedBalances[lowercaseAddress])[chainIdKey] ?? (_a[chainIdKey] = {});
666
463
  Object.assign(normalizedBalances[lowercaseAddress][chainIdKey], accountBalances[chainIdKey]);
667
464
  }
668
465
  }
669
- // Only update if there were changes
670
466
  if (Object.keys(currentState).length !==
671
467
  Object.keys(normalizedBalances).length ||
672
468
  Object.keys(currentState).some((addr) => addr !== addr.toLowerCase())) {
@@ -680,18 +476,15 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
680
476
  ]),
681
477
  ];
682
478
  }, _TokenBalancesController_startIntervalGroupPolling = function _TokenBalancesController_startIntervalGroupPolling(chainIds, immediate = true) {
683
- // Stop any existing interval timers
684
479
  __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
685
480
  __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
686
- // Group chains by their polling intervals
687
481
  const intervalGroups = new Map();
688
482
  for (const chainId of chainIds) {
689
483
  const config = this.getChainPollingConfig(chainId);
690
- const existing = intervalGroups.get(config.interval) || [];
691
- existing.push(chainId);
692
- intervalGroups.set(config.interval, existing);
484
+ const group = intervalGroups.get(config.interval) ?? [];
485
+ group.push(chainId);
486
+ intervalGroups.set(config.interval, group);
693
487
  }
694
- // Start separate polling loop for each interval group
695
488
  for (const [interval, chainIdsGroup] of intervalGroups) {
696
489
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startPollingForInterval).call(this, interval, chainIdsGroup, immediate);
697
490
  }
@@ -707,33 +500,221 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
707
500
  console.warn(`Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`, error);
708
501
  }
709
502
  };
710
- // Poll immediately first if requested
711
503
  if (immediate) {
712
504
  pollFunction().catch((error) => {
713
505
  console.warn(`Immediate polling failed for chains ${chainIds.join(', ')}:`, error);
714
506
  });
715
507
  }
716
- // Then start regular interval polling
717
508
  __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_setPollingTimer).call(this, interval, chainIds, pollFunction);
718
509
  }, _TokenBalancesController_setPollingTimer = function _TokenBalancesController_setPollingTimer(interval, chainIds, pollFunction) {
719
- // Clear any existing timer for this interval first
720
- const existingTimer = __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").get(interval);
721
- if (existingTimer) {
722
- clearInterval(existingTimer);
723
- }
724
510
  const timer = setInterval(() => {
725
511
  pollFunction().catch((error) => {
726
512
  console.warn(`Interval polling failed for chains ${chainIds.join(', ')}:`, error);
727
513
  });
728
514
  }, interval);
729
515
  __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
516
+ }, _TokenBalancesController_stopAllPolling = function _TokenBalancesController_stopAllPolling() {
517
+ __classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
518
+ __classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f");
519
+ __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
520
+ __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
521
+ }, _TokenBalancesController_getTargetChains = function _TokenBalancesController_getTargetChains(chainIds) {
522
+ return chainIds?.length ? chainIds : __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
523
+ }, _TokenBalancesController_getAccountsAndJwt = async function _TokenBalancesController_getAccountsAndJwt() {
524
+ const { address: selected } = this.messenger.call('AccountsController:getSelectedAccount');
525
+ const allAccounts = this.messenger.call('AccountsController:listAccounts');
526
+ const jwtToken = await (0, controller_utils_1.safelyExecuteWithTimeout)(() => {
527
+ return this.messenger.call('AuthenticationController:getBearerToken');
528
+ }, false, 5000);
529
+ return {
530
+ selectedAccount: selected,
531
+ allAccounts,
532
+ jwtToken,
533
+ };
534
+ }, _TokenBalancesController_fetchAllBalances = async function _TokenBalancesController_fetchAllBalances({ targetChains, selectedAccount, allAccounts, jwtToken, queryAllAccounts, }) {
535
+ const aggregated = [];
536
+ let remainingChains = [...targetChains];
537
+ for (const fetcher of __classPrivateFieldGet(this, _TokenBalancesController_balanceFetchers, "f")) {
538
+ const supportedChains = remainingChains.filter((chain) => fetcher.supports(chain));
539
+ if (!supportedChains.length) {
540
+ continue;
541
+ }
542
+ try {
543
+ const result = await fetcher.fetch({
544
+ chainIds: supportedChains,
545
+ queryAllAccounts,
546
+ selectedAccount,
547
+ allAccounts,
548
+ jwtToken,
549
+ });
550
+ if (result.balances?.length) {
551
+ aggregated.push(...result.balances);
552
+ const processed = new Set(result.balances.map((b) => b.chainId));
553
+ remainingChains = remainingChains.filter((chain) => !processed.has(chain));
554
+ }
555
+ if (result.unprocessedChainIds?.length) {
556
+ const currentRemaining = [...remainingChains];
557
+ const chainsToAdd = result.unprocessedChainIds.filter((chainId) => supportedChains.includes(chainId) &&
558
+ !currentRemaining.includes(chainId));
559
+ remainingChains.push(...chainsToAdd);
560
+ this.messenger
561
+ .call('TokenDetectionController:detectTokens', {
562
+ chainIds: result.unprocessedChainIds,
563
+ forceRpc: true,
564
+ })
565
+ .catch(() => {
566
+ // Silently handle token detection errors
567
+ });
568
+ }
569
+ }
570
+ catch (error) {
571
+ console.warn(`Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`);
572
+ this.messenger
573
+ .call('TokenDetectionController:detectTokens', {
574
+ chainIds: supportedChains,
575
+ forceRpc: true,
576
+ })
577
+ .catch(() => {
578
+ // Silently handle token detection errors
579
+ });
580
+ }
581
+ if (!remainingChains.length) {
582
+ break;
583
+ }
584
+ }
585
+ return aggregated;
586
+ }, _TokenBalancesController_filterByTokenAddresses = function _TokenBalancesController_filterByTokenAddresses(balances, tokenAddresses) {
587
+ if (!tokenAddresses?.length) {
588
+ return balances;
589
+ }
590
+ const lowered = tokenAddresses.map((a) => a.toLowerCase());
591
+ return balances.filter((balance) => lowered.includes(balance.token.toLowerCase()));
592
+ }, _TokenBalancesController_getAccountsToProcess = function _TokenBalancesController_getAccountsToProcess(queryAllAccountsParam, allAccounts, selectedAccount) {
593
+ const effectiveQueryAll = queryAllAccountsParam ?? __classPrivateFieldGet(this, _TokenBalancesController_queryAllAccounts, "f") ?? false;
594
+ if (!effectiveQueryAll) {
595
+ return [selectedAccount];
596
+ }
597
+ return allAccounts.map((account) => account.address);
598
+ }, _TokenBalancesController_applyTokenBalancesToState = function _TokenBalancesController_applyTokenBalancesToState({ prev, targetChains, accountsToProcess, balances, }) {
599
+ return draft(prev, (draftState) => {
600
+ var _a, _b;
601
+ for (const chainId of targetChains) {
602
+ for (const account of accountsToProcess) {
603
+ (_a = draftState.tokenBalances)[account] ?? (_a[account] = {});
604
+ (_b = draftState.tokenBalances[account])[chainId] ?? (_b[chainId] = {});
605
+ const chainTokens = __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId];
606
+ if (chainTokens?.[account]) {
607
+ Object.values(chainTokens[account]).forEach((token) => {
608
+ var _a;
609
+ const tokenAddress = checksum(token.address);
610
+ (_a = draftState.tokenBalances[account][chainId])[tokenAddress] ?? (_a[tokenAddress] = '0x0');
611
+ });
612
+ }
613
+ const detectedChainTokens = __classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")[chainId];
614
+ if (detectedChainTokens?.[account]) {
615
+ Object.values(detectedChainTokens[account]).forEach((token) => {
616
+ var _a;
617
+ const tokenAddress = checksum(token.address);
618
+ (_a = draftState.tokenBalances[account][chainId])[tokenAddress] ?? (_a[tokenAddress] = '0x0');
619
+ });
620
+ }
621
+ }
622
+ }
623
+ balances.forEach(({ success, value, account, token, chainId }) => {
624
+ var _a, _b;
625
+ if (!success || value === undefined) {
626
+ return;
627
+ }
628
+ const lowerCaseAccount = account.toLowerCase();
629
+ const newBalance = (0, controller_utils_1.toHex)(value);
630
+ const tokenAddress = checksum(token);
631
+ const currentBalance = draftState.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress];
632
+ if (currentBalance !== newBalance) {
633
+ ((_b = ((_a = draftState.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance;
634
+ }
635
+ });
636
+ });
637
+ }, _TokenBalancesController_buildNativeBalanceUpdates = function _TokenBalancesController_buildNativeBalanceUpdates(balances, accountTrackerState) {
638
+ const nativeBalances = balances.filter((balance) => balance.success && balance.token === ZERO_ADDRESS);
639
+ if (!nativeBalances.length) {
640
+ return [];
641
+ }
642
+ return nativeBalances
643
+ .map((balance) => ({
644
+ address: balance.account,
645
+ chainId: balance.chainId,
646
+ balance: balance.value ? (0, controller_utils_1.BNToHex)(balance.value) : '0x0',
647
+ }))
648
+ .filter((update) => {
649
+ const currentBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.balance;
650
+ return currentBalance !== update.balance;
651
+ });
652
+ }, _TokenBalancesController_buildStakedBalanceUpdates = function _TokenBalancesController_buildStakedBalanceUpdates(balances, accountTrackerState) {
653
+ const stakedBalances = balances.filter((balance) => {
654
+ if (!balance.success || balance.token === ZERO_ADDRESS) {
655
+ return false;
656
+ }
657
+ const stakingContractAddress = AssetsContractController_1.STAKING_CONTRACT_ADDRESS_BY_CHAINID[balance.chainId];
658
+ return (stakingContractAddress &&
659
+ stakingContractAddress.toLowerCase() === balance.token.toLowerCase());
660
+ });
661
+ if (!stakedBalances.length) {
662
+ return [];
663
+ }
664
+ return stakedBalances
665
+ .map((balance) => ({
666
+ address: balance.account,
667
+ chainId: balance.chainId,
668
+ stakedBalance: balance.value ? (0, controller_utils_1.toHex)(balance.value) : '0x0',
669
+ }))
670
+ .filter((update) => {
671
+ const currentStakedBalance = accountTrackerState.accountsByChainId[update.chainId]?.[checksum(update.address)]?.stakedBalance;
672
+ return currentStakedBalance !== update.stakedBalance;
673
+ });
674
+ }, _TokenBalancesController_importUntrackedTokens =
675
+ /**
676
+ * Import untracked tokens that have non-zero balances.
677
+ * This mirrors the v2 behavior where only tokens with actual balances are added.
678
+ * Delegates to TokenDetectionController:addDetectedTokensViaPolling which handles:
679
+ * - Checking if useTokenDetection preference is enabled
680
+ * - Filtering tokens already in allTokens or allIgnoredTokens
681
+ * - Token metadata lookup and addition via TokensController
682
+ *
683
+ * @param balances - Array of processed balance results from fetchers
684
+ */
685
+ async function _TokenBalancesController_importUntrackedTokens(balances) {
686
+ const tokensByChain = new Map();
687
+ for (const balance of balances) {
688
+ // Skip failed fetches, native tokens, and zero balances (like v2 did)
689
+ if (!balance.success ||
690
+ balance.token === ZERO_ADDRESS ||
691
+ !balance.value ||
692
+ balance.value.isZero()) {
693
+ continue;
694
+ }
695
+ const tokenAddress = checksum(balance.token);
696
+ const existing = tokensByChain.get(balance.chainId) ?? [];
697
+ if (!existing.includes(tokenAddress)) {
698
+ existing.push(tokenAddress);
699
+ tokensByChain.set(balance.chainId, existing);
700
+ }
701
+ }
702
+ // Add detected tokens via TokenDetectionController (handles preference check,
703
+ // filtering of allTokens/allIgnoredTokens, and metadata lookup)
704
+ for (const [chainId, tokenAddresses] of tokensByChain) {
705
+ if (tokenAddresses.length) {
706
+ await this.messenger.call('TokenDetectionController:addDetectedTokensViaPolling', {
707
+ tokensSlice: tokenAddresses,
708
+ chainId,
709
+ });
710
+ }
711
+ }
730
712
  }, _TokenBalancesController_isTokenTracked = function _TokenBalancesController_isTokenTracked(tokenAddress, account, chainId) {
731
- // Check if token exists in allTokens
732
- if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token.address === tokenAddress)) {
713
+ const normalizedAccount = account.toLowerCase();
714
+ if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[normalizedAccount]?.some((token) => token.address === tokenAddress)) {
733
715
  return true;
734
716
  }
735
- // Check if token exists in allIgnoredTokens
736
- if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token === tokenAddress)) {
717
+ if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[normalizedAccount]?.some((token) => token === tokenAddress)) {
737
718
  return true;
738
719
  }
739
720
  return false;
@@ -743,31 +724,25 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
743
724
  const nativeBalanceUpdates = [];
744
725
  for (const update of updates) {
745
726
  const { asset, postBalance } = update;
746
- // Throw if balance update has an error
747
727
  if (postBalance.error) {
748
728
  throw new Error('Balance update has error');
749
729
  }
750
- // Parse token address from asset type
751
730
  const parsed = (0, exports.parseAssetType)(asset.type);
752
731
  if (!parsed) {
753
732
  throw new Error('Failed to parse asset type');
754
733
  }
755
734
  const [tokenAddress, isNativeToken] = parsed;
756
- // Validate token address
757
735
  if (!(0, utils_1.isStrictHexString)(tokenAddress) ||
758
736
  !(0, controller_utils_1.isValidHexAddress)(tokenAddress)) {
759
737
  throw new Error('Invalid token address');
760
738
  }
761
739
  const checksumTokenAddress = checksum(tokenAddress);
762
740
  const isTracked = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_isTokenTracked).call(this, checksumTokenAddress, account, chainId);
763
- // postBalance.amount is in hex format (raw units)
764
741
  const balanceHex = postBalance.amount;
765
- // Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)
766
742
  tokenBalances.push({
767
743
  tokenAddress: checksumTokenAddress,
768
744
  balance: balanceHex,
769
745
  });
770
- // Add native balance update if this is a native token
771
746
  if (isNativeToken) {
772
747
  nativeBalanceUpdates.push({
773
748
  address: account,
@@ -775,7 +750,6 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
775
750
  balance: balanceHex,
776
751
  });
777
752
  }
778
- // Handle untracked ERC20 tokens - queue for import
779
753
  if (!isNativeToken && !isTracked) {
780
754
  newTokens.push(checksumTokenAddress);
781
755
  }
@@ -785,28 +759,18 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
785
759
  const changes = Array.from(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.entries());
786
760
  __classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.clear();
787
761
  __classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
788
- if (changes.length === 0) {
762
+ if (!changes.length) {
789
763
  return;
790
764
  }
791
- // Calculate final polling configurations
792
765
  const chainConfigs = {};
793
766
  for (const [chainId, status] of changes) {
794
- // Convert CAIP format (eip155:1) to hex format (0x1)
795
- // chainId is always in CAIP format from AccountActivityService
796
767
  const hexChainId = (0, exports.caipChainIdToHex)(chainId);
797
- if (status === 'down') {
798
- // Chain is down - use default polling since no real-time updates available
799
- chainConfigs[hexChainId] = { interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f") };
800
- }
801
- else {
802
- // Chain is up - use longer intervals since WebSocket provides real-time updates
803
- chainConfigs[hexChainId] = {
804
- interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f"),
805
- };
806
- }
768
+ chainConfigs[hexChainId] =
769
+ status === 'down'
770
+ ? { interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f") }
771
+ : { interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f") };
807
772
  }
808
- // Add jitter to prevent synchronized requests across instances
809
- const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"); // 0 to default interval
773
+ const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f");
810
774
  setTimeout(() => {
811
775
  this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });
812
776
  }, jitterDelay);