@metamask/assets-controllers 79.0.0 → 80.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 +41 -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 +228 -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 +226 -9
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +73 -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 +73 -44
- 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 +16 -14
|
@@ -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,85 @@ 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 account = 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, account, chainId);
|
|
278
|
+
// Update state once with all token balances
|
|
279
|
+
if (tokenBalances.length > 0) {
|
|
280
|
+
this.update((state) => {
|
|
281
|
+
var _a, _b;
|
|
282
|
+
// Initialize account and chain structure
|
|
283
|
+
(_a = state.tokenBalances)[account] ?? (_a[account] = {});
|
|
284
|
+
(_b = state.tokenBalances[account])[chainId] ?? (_b[chainId] = {});
|
|
285
|
+
// Apply all token balance updates
|
|
286
|
+
for (const { tokenAddress, balance } of tokenBalances) {
|
|
287
|
+
state.tokenBalances[account][chainId][tokenAddress] = balance;
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
// Update native balances in AccountTrackerController
|
|
292
|
+
if (nativeBalanceUpdates.length > 0) {
|
|
293
|
+
this.messagingSystem.call('AccountTrackerController:updateNativeBalances', nativeBalanceUpdates);
|
|
294
|
+
}
|
|
295
|
+
// Import any new tokens that were discovered (balance already updated from websocket)
|
|
296
|
+
if (newTokens.length > 0) {
|
|
297
|
+
await this.messagingSystem.call('TokenDetectionController:addDetectedTokensViaWs', {
|
|
298
|
+
tokensSlice: newTokens,
|
|
299
|
+
chainId: chainId,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
console.warn(`Error updating balances from AccountActivityService for chain ${chain}, account ${address}:`, error);
|
|
305
|
+
console.warn('Balance update data:', JSON.stringify(updates, null, 2));
|
|
306
|
+
// On error, trigger fallback polling
|
|
307
|
+
await this.updateBalances({ chainIds: [chainId] }).catch(() => {
|
|
308
|
+
// Silently handle polling errors
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
/**
|
|
313
|
+
* Handle status changes from AccountActivityService
|
|
314
|
+
* Uses aggressive debouncing to prevent excessive HTTP calls from rapid up/down changes
|
|
315
|
+
*
|
|
316
|
+
* @param options0 - Status change event data
|
|
317
|
+
* @param options0.chainIds - Array of chain identifiers
|
|
318
|
+
* @param options0.status - Connection status ('up' for connected, 'down' for disconnected)
|
|
319
|
+
*/
|
|
320
|
+
_TokenBalancesController_onAccountActivityStatusChanged.set(this, ({ chainIds, status, }) => {
|
|
321
|
+
// Update pending changes (latest status wins for each chain)
|
|
322
|
+
for (const chainId of chainIds) {
|
|
323
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.set(chainId, status);
|
|
324
|
+
}
|
|
325
|
+
// Clear existing timer to extend debounce window
|
|
326
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
327
|
+
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
328
|
+
}
|
|
329
|
+
// Set new timer - only process changes after activity settles
|
|
330
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = setTimeout(() => {
|
|
331
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_processAccumulatedStatusChanges).call(this);
|
|
332
|
+
}, 5000); // 5-second debounce window
|
|
333
|
+
});
|
|
211
334
|
__classPrivateFieldSet(this, _TokenBalancesController_platform, platform ?? 'extension', "f");
|
|
212
335
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
213
336
|
__classPrivateFieldSet(this, _TokenBalancesController_accountsApiChainIds, accountsApiChainIds, "f");
|
|
214
337
|
__classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
|
|
338
|
+
__classPrivateFieldSet(this, _TokenBalancesController_websocketActivePollingInterval, websocketActivePollingInterval, "f");
|
|
215
339
|
__classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
|
|
216
340
|
// Strategy order: API first, then RPC fallback
|
|
217
341
|
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
@@ -225,9 +349,10 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
225
349
|
], "f");
|
|
226
350
|
this.setIntervalLength(interval);
|
|
227
351
|
// initial token state & subscriptions
|
|
228
|
-
const { allTokens, allDetectedTokens } = this.messagingSystem.call('TokensController:getState');
|
|
352
|
+
const { allTokens, allDetectedTokens, allIgnoredTokens } = this.messagingSystem.call('TokensController:getState');
|
|
229
353
|
__classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
|
|
230
354
|
__classPrivateFieldSet(this, _TokenBalancesController_detectedTokens, allDetectedTokens, "f");
|
|
355
|
+
__classPrivateFieldSet(this, _TokenBalancesController_allIgnoredTokens, allIgnoredTokens, "f");
|
|
231
356
|
this.messagingSystem.subscribe('TokensController:stateChange', (tokensState) => {
|
|
232
357
|
__classPrivateFieldGet(this, _TokenBalancesController_onTokensChanged, "f").call(this, tokensState).catch((error) => {
|
|
233
358
|
console.warn('Error handling token state change:', error);
|
|
@@ -238,6 +363,10 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
238
363
|
// Register action handlers for polling interval control
|
|
239
364
|
this.messagingSystem.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
240
365
|
this.messagingSystem.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
366
|
+
// Subscribe to AccountActivityService balance updates for real-time updates
|
|
367
|
+
this.messagingSystem.subscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityBalanceUpdate, "f").bind(this));
|
|
368
|
+
// Subscribe to AccountActivityService status changes for dynamic polling management
|
|
369
|
+
this.messagingSystem.subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _TokenBalancesController_onAccountActivityStatusChanged, "f").bind(this));
|
|
241
370
|
}
|
|
242
371
|
/**
|
|
243
372
|
* Override to support per-chain polling intervals by grouping chains by interval
|
|
@@ -392,15 +521,14 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
392
521
|
}
|
|
393
522
|
// Update with actual fetched balances only if the value has changed
|
|
394
523
|
aggregated.forEach(({ success, value, account, token, chainId }) => {
|
|
395
|
-
var _a, _b;
|
|
524
|
+
var _a, _b, _c;
|
|
396
525
|
if (success && value !== undefined) {
|
|
397
526
|
const newBalance = toHex(value);
|
|
398
527
|
const tokenAddress = checksum(token);
|
|
399
528
|
const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress];
|
|
400
529
|
// Only update if the balance has actually changed
|
|
401
530
|
if (currentBalance !== newBalance) {
|
|
402
|
-
((
|
|
403
|
-
newBalance;
|
|
531
|
+
((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance;
|
|
404
532
|
}
|
|
405
533
|
}
|
|
406
534
|
});
|
|
@@ -465,13 +593,18 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
465
593
|
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
466
594
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
467
595
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
596
|
+
// Clean up debouncing timer
|
|
597
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer) {
|
|
598
|
+
clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer);
|
|
599
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
600
|
+
}
|
|
468
601
|
// Unregister action handlers
|
|
469
602
|
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
|
|
470
603
|
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
|
|
471
604
|
super.destroy();
|
|
472
605
|
}
|
|
473
606
|
}
|
|
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() {
|
|
607
|
+
_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
608
|
return [
|
|
476
609
|
...new Set([
|
|
477
610
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|
|
@@ -526,6 +659,90 @@ _TokenBalancesController_platform = new WeakMap(), _TokenBalancesController_quer
|
|
|
526
659
|
});
|
|
527
660
|
}, interval);
|
|
528
661
|
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
|
|
662
|
+
}, _TokenBalancesController_isTokenTracked = function _TokenBalancesController_isTokenTracked(tokenAddress, account, chainId) {
|
|
663
|
+
const normalizedAddress = tokenAddress.toLowerCase();
|
|
664
|
+
// Check if token exists in allTokens
|
|
665
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((token) => token.address.toLowerCase() === normalizedAddress)) {
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
// Check if token exists in allIgnoredTokens
|
|
669
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_allIgnoredTokens, "f")?.[chainId]?.[account.toLowerCase()]?.some((addr) => addr.toLowerCase() === normalizedAddress)) {
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
return false;
|
|
673
|
+
}, _TokenBalancesController_prepareBalanceUpdates = function _TokenBalancesController_prepareBalanceUpdates(updates, account, chainId) {
|
|
674
|
+
const tokenBalances = [];
|
|
675
|
+
const newTokens = [];
|
|
676
|
+
const nativeBalanceUpdates = [];
|
|
677
|
+
for (const update of updates) {
|
|
678
|
+
const { asset, postBalance } = update;
|
|
679
|
+
// Throw if balance update has an error
|
|
680
|
+
if (postBalance.error) {
|
|
681
|
+
throw new Error('Balance update has error');
|
|
682
|
+
}
|
|
683
|
+
// Parse token address from asset type
|
|
684
|
+
const parsed = parseAssetType(asset.type);
|
|
685
|
+
if (!parsed) {
|
|
686
|
+
throw new Error('Failed to parse asset type');
|
|
687
|
+
}
|
|
688
|
+
const [tokenAddress, isNativeToken] = parsed;
|
|
689
|
+
// Validate token address
|
|
690
|
+
if (!isStrictHexString(tokenAddress) ||
|
|
691
|
+
!isValidHexAddress(tokenAddress)) {
|
|
692
|
+
throw new Error('Invalid token address');
|
|
693
|
+
}
|
|
694
|
+
const checksumTokenAddress = checksum(tokenAddress);
|
|
695
|
+
const isTracked = __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_isTokenTracked).call(this, checksumTokenAddress, account, chainId);
|
|
696
|
+
// postBalance.amount is in hex format (raw units)
|
|
697
|
+
const balanceHex = postBalance.amount;
|
|
698
|
+
// Add token balance (tracked tokens, ignored tokens, and native tokens all get balance updates)
|
|
699
|
+
tokenBalances.push({
|
|
700
|
+
tokenAddress: checksumTokenAddress,
|
|
701
|
+
balance: balanceHex,
|
|
702
|
+
});
|
|
703
|
+
// Add native balance update if this is a native token
|
|
704
|
+
if (isNativeToken) {
|
|
705
|
+
nativeBalanceUpdates.push({
|
|
706
|
+
address: account,
|
|
707
|
+
chainId,
|
|
708
|
+
balance: balanceHex,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
// Handle untracked ERC20 tokens - queue for import
|
|
712
|
+
if (!isNativeToken && !isTracked) {
|
|
713
|
+
newTokens.push(checksumTokenAddress);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return { tokenBalances, newTokens, nativeBalanceUpdates };
|
|
717
|
+
}, _TokenBalancesController_processAccumulatedStatusChanges = function _TokenBalancesController_processAccumulatedStatusChanges() {
|
|
718
|
+
const changes = Array.from(__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.entries());
|
|
719
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").pendingChanges.clear();
|
|
720
|
+
__classPrivateFieldGet(this, _TokenBalancesController_statusChangeDebouncer, "f").timer = null;
|
|
721
|
+
if (changes.length === 0) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
// Calculate final polling configurations
|
|
725
|
+
const chainConfigs = {};
|
|
726
|
+
for (const [chainId, status] of changes) {
|
|
727
|
+
// Convert CAIP format (eip155:1) to hex format (0x1)
|
|
728
|
+
// chainId is always in CAIP format from AccountActivityService
|
|
729
|
+
const hexChainId = caipChainIdToHex(chainId);
|
|
730
|
+
if (status === 'down') {
|
|
731
|
+
// Chain is down - use default polling since no real-time updates available
|
|
732
|
+
chainConfigs[hexChainId] = { interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f") };
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
// Chain is up - use longer intervals since WebSocket provides real-time updates
|
|
736
|
+
chainConfigs[hexChainId] = {
|
|
737
|
+
interval: __classPrivateFieldGet(this, _TokenBalancesController_websocketActivePollingInterval, "f"),
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
// Add jitter to prevent synchronized requests across instances
|
|
742
|
+
const jitterDelay = Math.random() * __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"); // 0 to default interval
|
|
743
|
+
setTimeout(() => {
|
|
744
|
+
this.updateChainPollingConfigs(chainConfigs, { immediateUpdate: true });
|
|
745
|
+
}, jitterDelay);
|
|
529
746
|
};
|
|
530
747
|
export default TokenBalancesController;
|
|
531
748
|
//# sourceMappingURL=TokenBalancesController.mjs.map
|