@metamask-previews/assets-controllers 93.1.0-preview-1dd40c75 → 93.1.0-preview-267e79c3

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