@metamask/assets-controllers 79.0.1 → 81.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/README.md +1 -1
- package/dist/NftDetectionController.cjs +1 -72
- package/dist/NftDetectionController.cjs.map +1 -1
- package/dist/NftDetectionController.d.cts +0 -1
- package/dist/NftDetectionController.d.cts.map +1 -1
- package/dist/NftDetectionController.d.mts +0 -1
- package/dist/NftDetectionController.d.mts.map +1 -1
- package/dist/NftDetectionController.mjs +1 -72
- package/dist/NftDetectionController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +229 -9
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +27 -3
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +27 -3
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +227 -9
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +76 -44
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts +37 -9
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts +37 -9
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +77 -45
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +6 -2
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +6 -2
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -1
- package/package.json +5 -3
|
@@ -9,11 +9,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _TokenBalancesController_instances, _TokenBalancesController_platform, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_accountsApiChainIds, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
12
|
+
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_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_createAccountsApiFetcher, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_isTokenTracked, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved, _TokenBalancesController_prepareBalanceUpdates, _TokenBalancesController_onAccountActivityBalanceUpdate, _TokenBalancesController_onAccountActivityStatusChanged, _TokenBalancesController_processAccumulatedStatusChanges;
|
|
13
13
|
import { Web3Provider } from "@ethersproject/providers";
|
|
14
14
|
import { BNToHex, isValidHexAddress, toChecksumHexAddress, toHex } from "@metamask/controller-utils";
|
|
15
15
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
16
|
-
import { isStrictHexString } from "@metamask/utils";
|
|
16
|
+
import { isCaipAssetType, isCaipChainId, isStrictHexString, parseCaipAssetType, parseCaipChainId } from "@metamask/utils";
|
|
17
17
|
import { produce } from "immer";
|
|
18
18
|
import $lodash from "lodash";
|
|
19
19
|
const { isEqual } = $lodash;
|
|
@@ -21,7 +21,8 @@ import { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from "./AssetsContractController.
|
|
|
21
21
|
import { AccountsApiBalanceFetcher } from "./multi-chain-accounts-service/api-balance-fetcher.mjs";
|
|
22
22
|
import { RpcBalanceFetcher } from "./rpc-service/rpc-balance-fetcher.mjs";
|
|
23
23
|
const CONTROLLER = 'TokenBalancesController';
|
|
24
|
-
const DEFAULT_INTERVAL_MS =
|
|
24
|
+
const DEFAULT_INTERVAL_MS = 30000; // 30 seconds
|
|
25
|
+
const DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS = 300000; // 5 minutes
|
|
25
26
|
const metadata = {
|
|
26
27
|
tokenBalances: {
|
|
27
28
|
includeInStateLogs: false,
|
|
@@ -36,11 +37,50 @@ const metadata = {
|
|
|
36
37
|
const draft = (base, fn) => produce(base, fn);
|
|
37
38
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
38
39
|
const checksum = (addr) => toChecksumHexAddress(addr);
|
|
40
|
+
/**
|
|
41
|
+
* Convert CAIP chain ID or hex chain ID to hex chain ID
|
|
42
|
+
* Handles both CAIP-2 format (e.g., "eip155:1") and hex format (e.g., "0x1")
|
|
43
|
+
*
|
|
44
|
+
* @param chainId - CAIP chain ID (e.g., "eip155:1") or hex chain ID (e.g., "0x1")
|
|
45
|
+
* @returns Hex chain ID (e.g., "0x1")
|
|
46
|
+
* @throws {Error} If chainId is neither a valid CAIP-2 chain ID nor a hex string
|
|
47
|
+
*/
|
|
48
|
+
export const caipChainIdToHex = (chainId) => {
|
|
49
|
+
if (isStrictHexString(chainId)) {
|
|
50
|
+
return chainId;
|
|
51
|
+
}
|
|
52
|
+
if (isCaipChainId(chainId)) {
|
|
53
|
+
return toHex(parseCaipChainId(chainId).reference);
|
|
54
|
+
}
|
|
55
|
+
throw new Error('caipChainIdToHex - Failed to provide CAIP-2 or Hex chainId');
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Extract token address from asset type
|
|
59
|
+
* Returns tuple of [tokenAddress, isNativeToken] or null if invalid
|
|
60
|
+
*
|
|
61
|
+
* @param assetType - Asset type string (e.g., 'eip155:1/erc20:0x...' or 'eip155:1/slip44:60')
|
|
62
|
+
* @returns Tuple of [tokenAddress, isNativeToken] or null if invalid
|
|
63
|
+
*/
|
|
64
|
+
export const parseAssetType = (assetType) => {
|
|
65
|
+
if (!isCaipAssetType(assetType)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const parsed = parseCaipAssetType(assetType);
|
|
69
|
+
// ERC20 token (e.g., "eip155:1/erc20:0x...")
|
|
70
|
+
if (parsed.assetNamespace === 'erc20') {
|
|
71
|
+
return [parsed.assetReference, false];
|
|
72
|
+
}
|
|
73
|
+
// Native token (e.g., "eip155:1/slip44:60")
|
|
74
|
+
if (parsed.assetNamespace === 'slip44') {
|
|
75
|
+
return [ZERO_ADDRESS, true];
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
39
79
|
// endregion
|
|
40
80
|
// ────────────────────────────────────────────────────────────────────────────
|
|
41
81
|
// region: Main controller
|
|
42
82
|
export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
43
|
-
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, accountsApiChainIds = () => [], allowExternalServices = () => true, platform, }) {
|
|
83
|
+
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, websocketActivePollingInterval = DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, accountsApiChainIds = () => [], allowExternalServices = () => true, platform, }) {
|
|
44
84
|
super({
|
|
45
85
|
name: CONTROLLER,
|
|
46
86
|
messenger,
|
|
@@ -54,8 +94,11 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
54
94
|
_TokenBalancesController_balanceFetchers.set(this, void 0);
|
|
55
95
|
_TokenBalancesController_allTokens.set(this, {});
|
|
56
96
|
_TokenBalancesController_detectedTokens.set(this, {});
|
|
97
|
+
_TokenBalancesController_allIgnoredTokens.set(this, {});
|
|
57
98
|
/** Default polling interval for chains without specific configuration */
|
|
58
99
|
_TokenBalancesController_defaultInterval.set(this, void 0);
|
|
100
|
+
/** Polling interval when WebSocket is active and providing real-time updates */
|
|
101
|
+
_TokenBalancesController_websocketActivePollingInterval.set(this, void 0);
|
|
59
102
|
/** Per-chain polling configuration */
|
|
60
103
|
_TokenBalancesController_chainPollingConfig.set(this, void 0);
|
|
61
104
|
/** Active polling timers grouped by interval */
|
|
@@ -64,6 +107,11 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
64
107
|
_TokenBalancesController_isControllerPollingActive.set(this, false);
|
|
65
108
|
/** Store original chainIds from startPolling to preserve intent */
|
|
66
109
|
_TokenBalancesController_requestedChainIds.set(this, []);
|
|
110
|
+
/** Debouncing for rapid status changes to prevent excessive HTTP calls */
|
|
111
|
+
_TokenBalancesController_statusChangeDebouncer.set(this, {
|
|
112
|
+
timer: null,
|
|
113
|
+
pendingChanges: new Map(),
|
|
114
|
+
});
|
|
67
115
|
_TokenBalancesController_getProvider.set(this, (chainId) => {
|
|
68
116
|
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
69
117
|
const cfg = networkConfigurationsByChainId[chainId];
|
|
@@ -165,6 +213,7 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
165
213
|
});
|
|
166
214
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, state.allTokens, "f");
|
|
167
215
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, state.allDetectedTokens, "f");
|
|
216
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, state.allIgnoredTokens, "f");
|
|
168
217
|
// Only update balances for chains that still have tokens (and only if we haven't already updated state)
|
|
169
218
|
if (changed.length && !hasChanges) {
|
|
170
219
|
this.updateBalances({ chainIds: changed }).catch((error) => {
|
|
@@ -208,10 +257,87 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
208
257
|
delete s.tokenBalances[addr];
|
|
209
258
|
});
|
|
210
259
|
});
|
|
260
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
261
|
+
// AccountActivityService event handlers
|
|
262
|
+
/**
|
|
263
|
+
* Handle real-time balance updates from AccountActivityService
|
|
264
|
+
* Processes balance updates and updates the token balance state
|
|
265
|
+
* If any balance update has an error, triggers fallback polling for the chain
|
|
266
|
+
*
|
|
267
|
+
* @param options0 - Balance update parameters
|
|
268
|
+
* @param options0.address - Account address
|
|
269
|
+
* @param options0.chain - CAIP chain identifier
|
|
270
|
+
* @param options0.updates - Array of balance updates for the account
|
|
271
|
+
*/
|
|
272
|
+
_TokenBalancesController_onAccountActivityBalanceUpdate.set(this, async ({ address, chain, updates, }) => {
|
|
273
|
+
const chainId = caipChainIdToHex(chain);
|
|
274
|
+
const checksummedAccount = checksum(address);
|
|
275
|
+
try {
|
|
276
|
+
// Process all balance updates at once
|
|
277
|
+
const { tokenBalances, newTokens, nativeBalanceUpdates } = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_prepareBalanceUpdates).call(this, updates, checksummedAccount, chainId);
|
|
278
|
+
// Update state once with all token balances
|
|
279
|
+
if (tokenBalances.length > 0) {
|
|
280
|
+
this.update((state) => {
|
|
281
|
+
var _a, _b;
|
|
282
|
+
// Temporary until ADR to normalize all keys - tokenBalances state requires: account in lowercase, token in checksum
|
|
283
|
+
const lowercaseAccount = checksummedAccount.toLowerCase();
|
|
284
|
+
(_a = state.tokenBalances)[lowercaseAccount] ?? (_a[lowercaseAccount] = {});
|
|
285
|
+
(_b = state.tokenBalances[lowercaseAccount])[chainId] ?? (_b[chainId] = {});
|
|
286
|
+
// Apply all token balance updates
|
|
287
|
+
for (const { tokenAddress, balance } of tokenBalances) {
|
|
288
|
+
state.tokenBalances[lowercaseAccount][chainId][tokenAddress] =
|
|
289
|
+
balance;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
// Update native balances in AccountTrackerController
|
|
294
|
+
if (nativeBalanceUpdates.length > 0) {
|
|
295
|
+
this.messagingSystem.call('AccountTrackerController:updateNativeBalances', nativeBalanceUpdates);
|
|
296
|
+
}
|
|
297
|
+
// Import any new tokens that were discovered (balance already updated from websocket)
|
|
298
|
+
if (newTokens.length > 0) {
|
|
299
|
+
await this.messagingSystem.call('TokenDetectionController:addDetectedTokensViaWs', {
|
|
300
|
+
tokensSlice: newTokens,
|
|
301
|
+
chainId: chainId,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.warn(`Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`, error);
|
|
307
|
+
console.warn('Balance update data:', JSON.stringify(updates, null, 2));
|
|
308
|
+
// On error, trigger fallback polling
|
|
309
|
+
await this.updateBalances({ chainIds: [chainId] }).catch(() => {
|
|
310
|
+
// Silently handle polling errors
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
/**
|
|
315
|
+
* Handle status changes from AccountActivityService
|
|
316
|
+
* Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes
|
|
317
|
+
*
|
|
318
|
+
* @param options0 - Status change event data
|
|
319
|
+
* @param options0.chainIds - Array of chain identifiers
|
|
320
|
+
* @param options0.status - Connection status ('up' for connected, 'down' for disconnected)
|
|
321
|
+
*/
|
|
322
|
+
_TokenBalancesController_onAccountActivityStatusChanged.set(this, ({ chainIds, status, }) => {
|
|
323
|
+
// Update pending changes (latest status wins for each chain)
|
|
324
|
+
for (const chainId of chainIds) {
|
|
325
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.set(chainId, status);
|
|
326
|
+
}
|
|
327
|
+
// Clear existing timer to extend debounce window
|
|
328
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
329
|
+
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
330
|
+
}
|
|
331
|
+
// Set new timer - only process changes after activity settles
|
|
332
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = setTimeout(() => {
|
|
333
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_processAccumulatedStatusChanges).call(this);
|
|
334
|
+
}, 5000); // 5-second debounce window
|
|
335
|
+
});
|
|
211
336
|
__classPrivateFieldSet(this, _TokenBalancesController_platform, platform ?? 'extension', "f");
|
|
212
337
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
213
338
|
__classPrivateFieldSet(this, _TokenBalancesController_accountsApiChainIds, accountsApiChainIds, "f");
|
|
214
339
|
__classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
|
|
340
|
+
__classPrivateFieldSet(this, _TokenBalancesController_websocketActivePollingInterval, websocketActivePollingInterval, "f");
|
|
215
341
|
__classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
|
|
216
342
|
// Strategy order: API first, then RPC fallback
|
|
217
343
|
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
@@ -225,9 +351,10 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
225
351
|
], "f");
|
|
226
352
|
this.setIntervalLength(interval);
|
|
227
353
|
// initial token state & subscriptions
|
|
228
|
-
const { allTokens, allDetectedTokens } = this.messagingSystem.call('TokensController:getState');
|
|
354
|
+
const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messagingSystem.call('TokensController:getState');
|
|
229
355
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
230
356
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
357
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, allIgnoredTokens, "f");
|
|
231
358
|
this.messagingSystem.subscribe('TokensController:stateChange', (tokensState) => {
|
|
232
359
|
__classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
|
|
233
360
|
console.warn('Error handling token state change:', error);
|
|
@@ -238,6 +365,10 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
238
365
|
// Register action handlers for polling interval control
|
|
239
366
|
this.messagingSystem.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
240
367
|
this.messagingSystem.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
368
|
+
// Subscribe to AccountActivityService balance updates for real-time updates
|
|
369
|
+
this.messagingSystem.subscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").bind(this));
|
|
370
|
+
// Subscribe to AccountActivityService status changes for dynamic polling management
|
|
371
|
+
this.messagingSystem.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
|
|
241
372
|
}
|
|
242
373
|
/**
|
|
243
374
|
* Override to support per-chain polling intervals by grouping chains by interval
|
|
@@ -392,15 +523,14 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
392
523
|
}
|
|
393
524
|
// Update with actual fetched balances only if the value has changed
|
|
394
525
|
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
395
|
-
var _a, _b;
|
|
526
|
+
var _a, _b, _c;
|
|
396
527
|
if (success && value !== undefined) {
|
|
397
528
|
const newBalance = toHex(value);
|
|
398
529
|
const tokenAddress = checksum(token);
|
|
399
530
|
const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress];
|
|
400
531
|
// Only update if the balance has actually changed
|
|
401
532
|
if (currentBalance !== newBalance) {
|
|
402
|
-
((
|
|
403
|
-
newBalance;
|
|
533
|
+
((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance;
|
|
404
534
|
}
|
|
405
535
|
}
|
|
406
536
|
});
|
|
@@ -465,13 +595,18 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
465
595
|
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
466
596
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
467
597
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
598
|
+
// Clean up debouncing timer
|
|
599
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
600
|
+
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
601
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
602
|
+
}
|
|
468
603
|
// Unregister action handlers
|
|
469
604
|
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
|
|
470
605
|
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
|
|
471
606
|
super.destroy();
|
|
472
607
|
}
|
|
473
608
|
}
|
|
474
|
-
_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_defaultInterval = new WeakMap(), _TokenBalancesController_chainPollingConfig = new WeakMap(), _TokenBalancesController_intervalPollingTimers = new WeakMap(), _TokenBalancesController_isControllerPollingActive = new WeakMap(), _TokenBalancesController_requestedChainIds = 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_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
609
|
+
_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_onAccountActivityBalanceUpdate = new WeakMap(), _TokenBalancesController_onAccountActivityStatusChanged = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
475
610
|
return [
|
|
476
611
|
...new Set([
|
|
477
612
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|
|
@@ -526,6 +661,89 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
526
661
|
});
|
|
527
662
|
}, interval);
|
|
528
663
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
|
|
664
|
+
}, _TokenBalancesController_isTokenTracked = function _TokenBalancesController_isTokenTracked(tokenAddress, account, chainId) {
|
|
665
|
+
// Check if token exists in allTokens
|
|
666
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token.address === tokenAddress)) {
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
// Check if token exists in allIgnoredTokens
|
|
670
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token === tokenAddress)) {
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}, _TokenBalancesController_prepareBalanceUpdates = function _TokenBalancesController_prepareBalanceUpdates(updates, account, chainId) {
|
|
675
|
+
const tokenBalances = [];
|
|
676
|
+
const newTokens = [];
|
|
677
|
+
const nativeBalanceUpdates = [];
|
|
678
|
+
for (const update of updates) {
|
|
679
|
+
const { asset, postBalance } = update;
|
|
680
|
+
// Throw if balance update has an error
|
|
681
|
+
if (postBalance.error) {
|
|
682
|
+
throw new Error('Balance update has error');
|
|
683
|
+
}
|
|
684
|
+
// Parse token address from asset type
|
|
685
|
+
const parsed = parseAssetType(asset.type);
|
|
686
|
+
if (!parsed) {
|
|
687
|
+
throw new Error('Failed to parse asset type');
|
|
688
|
+
}
|
|
689
|
+
const [tokenAddress, isNativeToken] = parsed;
|
|
690
|
+
// Validate token address
|
|
691
|
+
if (!isStrictHexString(tokenAddress) ||
|
|
692
|
+
!isValidHexAddress(tokenAddress)) {
|
|
693
|
+
throw new Error('Invalid token address');
|
|
694
|
+
}
|
|
695
|
+
const checksumTokenAddress = checksum(tokenAddress);
|
|
696
|
+
const isTracked = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_isTokenTracked).call(this, checksumTokenAddress, account, chainId);
|
|
697
|
+
// postBalance.amount is in hex format (raw units)
|
|
698
|
+
const balanceHex = postBalance.amount;
|
|
699
|
+
// Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)
|
|
700
|
+
tokenBalances.push({
|
|
701
|
+
tokenAddress: checksumTokenAddress,
|
|
702
|
+
balance: balanceHex,
|
|
703
|
+
});
|
|
704
|
+
// Add native balance update if this is a native token
|
|
705
|
+
if (isNativeToken) {
|
|
706
|
+
nativeBalanceUpdates.push({
|
|
707
|
+
address: account,
|
|
708
|
+
chainId,
|
|
709
|
+
balance: balanceHex,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
// Handle untracked ERC20 tokens - queue for import
|
|
713
|
+
if (!isNativeToken && !isTracked) {
|
|
714
|
+
newTokens.push(checksumTokenAddress);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return { tokenBalances, newTokens, nativeBalanceUpdates };
|
|
718
|
+
}, _TokenBalancesController_processAccumulatedStatusChanges = function _TokenBalancesController_processAccumulatedStatusChanges() {
|
|
719
|
+
const changes = Array.from(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.entries());
|
|
720
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.clear();
|
|
721
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
722
|
+
if (changes.length === 0) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
// Calculate final polling configurations
|
|
726
|
+
const chainConfigs = {};
|
|
727
|
+
for (const [chainId, status] of changes) {
|
|
728
|
+
// Convert CAIP format (eip155:1) to hex format (0x1)
|
|
729
|
+
// chainId is always in CAIP format from AccountActivityService
|
|
730
|
+
const hexChainId = caipChainIdToHex(chainId);
|
|
731
|
+
if (status === 'down') {
|
|
732
|
+
// Chain is down - use default polling since no real-time updates available
|
|
733
|
+
chainConfigs[hexChainId] = { interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f") };
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
// Chain is up - use longer intervals since WebSocket provides real-time updates
|
|
737
|
+
chainConfigs[hexChainId] = {
|
|
738
|
+
interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f"),
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Add jitter to prevent synchronized requests across instances
|
|
743
|
+
const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"); // 0 to default interval
|
|
744
|
+
setTimeout(() => {
|
|
745
|
+
this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });
|
|
746
|
+
}, jitterDelay);
|
|
529
747
|
};
|
|
530
748
|
export default TokenBalancesController;
|
|
531
749
|
//# sourceMappingURL=TokenBalancesController.mjs.map
|