@metamask/assets-controllers 74.3.2 → 74.3.3
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 +9 -1
- package/dist/TokenBalancesController.cjs +126 -3
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +53 -2
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +53 -2
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +126 -3
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [74.3.3]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Enhance `TokenBalancesController` with internal dynamic polling per chain support, enabling configurable polling intervals for different networks with automatic interval grouping for improved performance (transparent to existing API) ([#6357](https://github.com/MetaMask/core/pull/6357))
|
|
15
|
+
- Bump `@metamask/base-controller` from `^8.2.0` to `^8.3.0` ([#6465](https://github.com/MetaMask/core/pull/6465))
|
|
16
|
+
|
|
10
17
|
## [74.3.2]
|
|
11
18
|
|
|
12
19
|
### Changed
|
|
@@ -1954,7 +1961,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1954
1961
|
|
|
1955
1962
|
- Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
|
|
1956
1963
|
|
|
1957
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.3.
|
|
1964
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.3.3...HEAD
|
|
1965
|
+
[74.3.3]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.3.2...@metamask/assets-controllers@74.3.3
|
|
1958
1966
|
[74.3.2]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.3.1...@metamask/assets-controllers@74.3.2
|
|
1959
1967
|
[74.3.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.3.0...@metamask/assets-controllers@74.3.1
|
|
1960
1968
|
[74.3.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@74.2.0...@metamask/assets-controllers@74.3.0
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _TokenBalancesController_instances, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
13
|
+
var _TokenBalancesController_instances, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.TokenBalancesController = void 0;
|
|
16
16
|
const providers_1 = require("@ethersproject/providers");
|
|
@@ -37,7 +37,7 @@ const checksum = (addr) => (0, controller_utils_1.toChecksumHexAddress)(addr);
|
|
|
37
37
|
// ────────────────────────────────────────────────────────────────────────────
|
|
38
38
|
// region: Main controller
|
|
39
39
|
class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPollingController)() {
|
|
40
|
-
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, state = {}, queryMultipleAccounts = true, useAccountsAPI = false, allowExternalServices = () => true, }) {
|
|
40
|
+
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, useAccountsAPI = false, allowExternalServices = () => true, }) {
|
|
41
41
|
super({
|
|
42
42
|
name: CONTROLLER,
|
|
43
43
|
messenger,
|
|
@@ -49,6 +49,16 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
49
49
|
_TokenBalancesController_balanceFetchers.set(this, void 0);
|
|
50
50
|
_TokenBalancesController_allTokens.set(this, {});
|
|
51
51
|
_TokenBalancesController_detectedTokens.set(this, {});
|
|
52
|
+
/** Default polling interval for chains without specific configuration */
|
|
53
|
+
_TokenBalancesController_defaultInterval.set(this, void 0);
|
|
54
|
+
/** Per-chain polling configuration */
|
|
55
|
+
_TokenBalancesController_chainPollingConfig.set(this, void 0);
|
|
56
|
+
/** Active polling timers grouped by interval */
|
|
57
|
+
_TokenBalancesController_intervalPollingTimers.set(this, new Map());
|
|
58
|
+
/** Track if controller-level polling is active */
|
|
59
|
+
_TokenBalancesController_isControllerPollingActive.set(this, false);
|
|
60
|
+
/** Store original chainIds from startPolling to preserve intent */
|
|
61
|
+
_TokenBalancesController_requestedChainIds.set(this, []);
|
|
52
62
|
_TokenBalancesController_getProvider.set(this, (chainId) => {
|
|
53
63
|
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
54
64
|
const cfg = networkConfigurationsByChainId[chainId];
|
|
@@ -176,6 +186,8 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
176
186
|
});
|
|
177
187
|
});
|
|
178
188
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
189
|
+
__classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
|
|
190
|
+
__classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
|
|
179
191
|
// Strategy order: API first, then RPC fallback
|
|
180
192
|
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
181
193
|
...(useAccountsAPI && allowExternalServices()
|
|
@@ -198,10 +210,61 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
198
210
|
});
|
|
199
211
|
this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
200
212
|
this.messagingSystem.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
213
|
+
// Register action handlers for polling interval control
|
|
214
|
+
this.messagingSystem.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
215
|
+
this.messagingSystem.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Override to support per-chain polling intervals by grouping chains by interval
|
|
219
|
+
*
|
|
220
|
+
* @param options0 - The polling options
|
|
221
|
+
* @param options0.chainIds - Chain IDs to start polling for
|
|
222
|
+
*/
|
|
223
|
+
_startPolling({ chainIds }) {
|
|
224
|
+
// Store the original chainIds to preserve intent across config updates
|
|
225
|
+
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [...chainIds], "f");
|
|
226
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, true, "f");
|
|
227
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, chainIds, true);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Override to handle our custom polling approach
|
|
231
|
+
*/
|
|
232
|
+
_stopPollingByPollingTokenSetId() {
|
|
233
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
234
|
+
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f"); // Clear original intent when stopping
|
|
235
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
236
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get polling configuration for a chain (includes default fallback)
|
|
240
|
+
*
|
|
241
|
+
* @param chainId - The chain ID to get config for
|
|
242
|
+
* @returns The polling configuration for the chain
|
|
243
|
+
*/
|
|
244
|
+
getChainPollingConfig(chainId) {
|
|
245
|
+
return (__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f")[chainId] ?? {
|
|
246
|
+
interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"),
|
|
247
|
+
});
|
|
201
248
|
}
|
|
202
249
|
async _executePoll({ chainIds }) {
|
|
250
|
+
// This won't be called with our custom implementation, but keep for compatibility
|
|
203
251
|
await this.updateBalances({ chainIds });
|
|
204
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Update multiple chain polling configurations at once
|
|
255
|
+
*
|
|
256
|
+
* @param configs - Object mapping chain IDs to polling configurations
|
|
257
|
+
* @param options - Optional configuration for the update behavior
|
|
258
|
+
* @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
|
|
259
|
+
*/
|
|
260
|
+
updateChainPollingConfigs(configs, options = { immediateUpdate: true }) {
|
|
261
|
+
Object.assign(__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f"), configs);
|
|
262
|
+
// If polling is currently active, restart with new interval groupings
|
|
263
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
|
|
264
|
+
// Restart polling with immediate fetch by default, unless explicitly disabled
|
|
265
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, __classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"), options.immediateUpdate);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
205
268
|
async updateBalances({ chainIds } = {}) {
|
|
206
269
|
const targetChains = chainIds ?? __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
|
|
207
270
|
if (!targetChains.length) {
|
|
@@ -312,15 +375,75 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol
|
|
|
312
375
|
resetState() {
|
|
313
376
|
this.update(() => ({ tokenBalances: {} }));
|
|
314
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Clean up all timers and resources when controller is destroyed
|
|
380
|
+
*/
|
|
381
|
+
destroy() {
|
|
382
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
383
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
384
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
385
|
+
// Unregister action handlers
|
|
386
|
+
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
|
|
387
|
+
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
|
|
388
|
+
super.destroy();
|
|
389
|
+
}
|
|
315
390
|
}
|
|
316
391
|
exports.TokenBalancesController = TokenBalancesController;
|
|
317
|
-
_TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
392
|
+
_TokenBalancesController_queryAllAccounts = 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_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
318
393
|
return [
|
|
319
394
|
...new Set([
|
|
320
395
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|
|
321
396
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")),
|
|
322
397
|
]),
|
|
323
398
|
];
|
|
399
|
+
}, _TokenBalancesController_startIntervalGroupPolling = function _TokenBalancesController_startIntervalGroupPolling(chainIds, immediate = true) {
|
|
400
|
+
// Stop any existing interval timers
|
|
401
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
402
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
403
|
+
// Group chains by their polling intervals
|
|
404
|
+
const intervalGroups = new Map();
|
|
405
|
+
for (const chainId of chainIds) {
|
|
406
|
+
const config = this.getChainPollingConfig(chainId);
|
|
407
|
+
const existing = intervalGroups.get(config.interval) || [];
|
|
408
|
+
existing.push(chainId);
|
|
409
|
+
intervalGroups.set(config.interval, existing);
|
|
410
|
+
}
|
|
411
|
+
// Start separate polling loop for each interval group
|
|
412
|
+
for (const [interval, chainIdsGroup] of intervalGroups) {
|
|
413
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startPollingForInterval).call(this, interval, chainIdsGroup, immediate);
|
|
414
|
+
}
|
|
415
|
+
}, _TokenBalancesController_startPollingForInterval = function _TokenBalancesController_startPollingForInterval(interval, chainIds, immediate = true) {
|
|
416
|
+
const pollFunction = async () => {
|
|
417
|
+
if (!__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
await this._executePoll({ chainIds });
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
console.warn(`Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`, error);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
// Poll immediately first if requested
|
|
428
|
+
if (immediate) {
|
|
429
|
+
pollFunction().catch((error) => {
|
|
430
|
+
console.warn(`Immediate polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
// Then start regular interval polling
|
|
434
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_setPollingTimer).call(this, interval, chainIds, pollFunction);
|
|
435
|
+
}, _TokenBalancesController_setPollingTimer = function _TokenBalancesController_setPollingTimer(interval, chainIds, pollFunction) {
|
|
436
|
+
// Clear any existing timer for this interval first
|
|
437
|
+
const existingTimer = __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").get(interval);
|
|
438
|
+
if (existingTimer) {
|
|
439
|
+
clearInterval(existingTimer);
|
|
440
|
+
}
|
|
441
|
+
const timer = setInterval(() => {
|
|
442
|
+
pollFunction().catch((error) => {
|
|
443
|
+
console.warn(`Interval polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
444
|
+
});
|
|
445
|
+
}, interval);
|
|
446
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
|
|
324
447
|
};
|
|
325
448
|
exports.default = TokenBalancesController;
|
|
326
449
|
//# sourceMappingURL=TokenBalancesController.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.cjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerUpdateChainPollingConfigsAction = {\n type: `TokenBalancesController:updateChainPollingConfigs`;\n handler: TokenBalancesController['updateChainPollingConfigs'];\n};\n\nexport type TokenBalancesControllerGetChainPollingConfigAction = {\n type: `TokenBalancesController:getChainPollingConfig`;\n handler: TokenBalancesController['getChainPollingConfig'];\n};\n\nexport type TokenBalancesControllerActions =\n | TokenBalancesControllerGetStateAction\n | TokenBalancesControllerUpdateChainPollingConfigsAction\n | TokenBalancesControllerGetChainPollingConfigAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type ChainPollingConfig = {\n /** Polling interval in milliseconds for this chain */\n interval: number;\n};\n\nexport type UpdateChainPollingConfigsOptions = {\n /** Whether to immediately fetch balances after updating configs (default: true) */\n immediateUpdate?: boolean;\n};\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n /** Default interval for chains not specified in chainPollingIntervals */\n interval?: number;\n /** Per-chain polling configuration */\n chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n /** Default polling interval for chains without specific configuration */\n readonly #defaultInterval: number;\n\n /** Per-chain polling configuration */\n readonly #chainPollingConfig: Record<ChainIdHex, ChainPollingConfig>;\n\n /** Active polling timers grouped by interval */\n readonly #intervalPollingTimers: Map<number, NodeJS.Timeout> = new Map();\n\n /** Track if controller-level polling is active */\n #isControllerPollingActive = false;\n\n /** Store original chainIds from startPolling to preserve intent */\n #requestedChainIds: ChainIdHex[] = [];\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n chainPollingIntervals = {},\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n this.#defaultInterval = interval;\n this.#chainPollingConfig = { ...chainPollingIntervals };\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n\n // Register action handlers for polling interval control\n this.messagingSystem.registerActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n this.updateChainPollingConfigs.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n this.getChainPollingConfig.bind(this),\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n /**\n * Override to support per-chain polling intervals by grouping chains by interval\n *\n * @param options0 - The polling options\n * @param options0.chainIds - Chain IDs to start polling for\n */\n override _startPolling({ chainIds }: { chainIds: ChainIdHex[] }) {\n // Store the original chainIds to preserve intent across config updates\n this.#requestedChainIds = [...chainIds];\n this.#isControllerPollingActive = true;\n this.#startIntervalGroupPolling(chainIds, true);\n }\n\n /**\n * Start or restart interval-based polling for multiple chains\n *\n * @param chainIds - Chain IDs to start polling for\n * @param immediate - Whether to poll immediately before starting timers (default: true)\n */\n #startIntervalGroupPolling(chainIds: ChainIdHex[], immediate = true) {\n // Stop any existing interval timers\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Group chains by their polling intervals\n const intervalGroups = new Map<number, ChainIdHex[]>();\n\n for (const chainId of chainIds) {\n const config = this.getChainPollingConfig(chainId);\n const existing = intervalGroups.get(config.interval) || [];\n existing.push(chainId);\n intervalGroups.set(config.interval, existing);\n }\n\n // Start separate polling loop for each interval group\n for (const [interval, chainIdsGroup] of intervalGroups) {\n this.#startPollingForInterval(interval, chainIdsGroup, immediate);\n }\n }\n\n /**\n * Start polling loop for chains that share the same interval\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs that share this interval\n * @param immediate - Whether to poll immediately before starting the timer (default: true)\n */\n #startPollingForInterval(\n interval: number,\n chainIds: ChainIdHex[],\n immediate = true,\n ) {\n const pollFunction = async () => {\n if (!this.#isControllerPollingActive) {\n return;\n }\n try {\n await this._executePoll({ chainIds });\n } catch (error) {\n console.warn(\n `Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`,\n error,\n );\n }\n };\n\n // Poll immediately first if requested\n if (immediate) {\n pollFunction().catch((error) => {\n console.warn(\n `Immediate polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }\n\n // Then start regular interval polling\n this.#setPollingTimer(interval, chainIds, pollFunction);\n }\n\n /**\n * Helper method to set up polling timer\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs for this interval\n * @param pollFunction - The function to call on each poll\n */\n #setPollingTimer(\n interval: number,\n chainIds: ChainIdHex[],\n pollFunction: () => Promise<void>,\n ) {\n // Clear any existing timer for this interval first\n const existingTimer = this.#intervalPollingTimers.get(interval);\n if (existingTimer) {\n clearInterval(existingTimer);\n }\n\n const timer = setInterval(() => {\n pollFunction().catch((error) => {\n console.warn(\n `Interval polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }, interval);\n this.#intervalPollingTimers.set(interval, timer);\n }\n\n /**\n * Override to handle our custom polling approach\n */\n override _stopPollingByPollingTokenSetId() {\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = []; // Clear original intent when stopping\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n }\n\n /**\n * Get polling configuration for a chain (includes default fallback)\n *\n * @param chainId - The chain ID to get config for\n * @returns The polling configuration for the chain\n */\n getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig {\n return (\n this.#chainPollingConfig[chainId] ?? {\n interval: this.#defaultInterval,\n }\n );\n }\n\n override async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n // This won't be called with our custom implementation, but keep for compatibility\n await this.updateBalances({ chainIds });\n }\n\n /**\n * Update multiple chain polling configurations at once\n *\n * @param configs - Object mapping chain IDs to polling configurations\n * @param options - Optional configuration for the update behavior\n * @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)\n */\n updateChainPollingConfigs(\n configs: Record<ChainIdHex, ChainPollingConfig>,\n options: UpdateChainPollingConfigsOptions = { immediateUpdate: true },\n ): void {\n Object.assign(this.#chainPollingConfig, configs);\n\n // If polling is currently active, restart with new interval groupings\n if (this.#isControllerPollingActive) {\n // Restart polling with immediate fetch by default, unless explicitly disabled\n this.#startIntervalGroupPolling(\n this.#requestedChainIds,\n options.immediateUpdate,\n );\n }\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n\n /**\n * Clean up all timers and resources when controller is destroyed\n */\n override destroy(): void {\n this.#isControllerPollingActive = false;\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Unregister action handlers\n this.messagingSystem.unregisterActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n );\n this.messagingSystem.unregisterActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n );\n\n super.destroy();\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
|
@@ -14,7 +14,15 @@ export type TokenBalancesControllerState = {
|
|
|
14
14
|
tokenBalances: TokenBalances;
|
|
15
15
|
};
|
|
16
16
|
export type TokenBalancesControllerGetStateAction = ControllerGetStateAction<typeof CONTROLLER, TokenBalancesControllerState>;
|
|
17
|
-
export type
|
|
17
|
+
export type TokenBalancesControllerUpdateChainPollingConfigsAction = {
|
|
18
|
+
type: `TokenBalancesController:updateChainPollingConfigs`;
|
|
19
|
+
handler: TokenBalancesController['updateChainPollingConfigs'];
|
|
20
|
+
};
|
|
21
|
+
export type TokenBalancesControllerGetChainPollingConfigAction = {
|
|
22
|
+
type: `TokenBalancesController:getChainPollingConfig`;
|
|
23
|
+
handler: TokenBalancesController['getChainPollingConfig'];
|
|
24
|
+
};
|
|
25
|
+
export type TokenBalancesControllerActions = TokenBalancesControllerGetStateAction | TokenBalancesControllerUpdateChainPollingConfigsAction | TokenBalancesControllerGetChainPollingConfigAction;
|
|
18
26
|
export type TokenBalancesControllerStateChangeEvent = ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;
|
|
19
27
|
export type NativeBalanceEvent = {
|
|
20
28
|
type: `${typeof CONTROLLER}:updatedNativeBalance`;
|
|
@@ -24,9 +32,20 @@ export type TokenBalancesControllerEvents = TokenBalancesControllerStateChangeEv
|
|
|
24
32
|
export type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | TokensControllerGetStateAction | PreferencesControllerGetStateAction | AccountsControllerGetSelectedAccountAction | AccountsControllerListAccountsAction | AccountTrackerUpdateNativeBalancesAction | AccountTrackerUpdateStakedBalancesAction;
|
|
25
33
|
export type AllowedEvents = TokensControllerStateChangeEvent | PreferencesControllerStateChangeEvent | NetworkControllerStateChangeEvent | KeyringControllerAccountRemovedEvent;
|
|
26
34
|
export type TokenBalancesControllerMessenger = RestrictedMessenger<typeof CONTROLLER, TokenBalancesControllerActions | AllowedActions, TokenBalancesControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
35
|
+
export type ChainPollingConfig = {
|
|
36
|
+
/** Polling interval in milliseconds for this chain */
|
|
37
|
+
interval: number;
|
|
38
|
+
};
|
|
39
|
+
export type UpdateChainPollingConfigsOptions = {
|
|
40
|
+
/** Whether to immediately fetch balances after updating configs (default: true) */
|
|
41
|
+
immediateUpdate?: boolean;
|
|
42
|
+
};
|
|
27
43
|
export type TokenBalancesControllerOptions = {
|
|
28
44
|
messenger: TokenBalancesControllerMessenger;
|
|
45
|
+
/** Default interval for chains not specified in chainPollingIntervals */
|
|
29
46
|
interval?: number;
|
|
47
|
+
/** Per-chain polling configuration */
|
|
48
|
+
chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;
|
|
30
49
|
state?: Partial<TokenBalancesControllerState>;
|
|
31
50
|
/** When `true`, balances for *all* known accounts are queried. */
|
|
32
51
|
queryMultipleAccounts?: boolean;
|
|
@@ -66,14 +85,46 @@ declare const TokenBalancesController_base: (abstract new (...args: any[]) => {
|
|
|
66
85
|
}) & typeof import("@metamask/base-controller").BaseController;
|
|
67
86
|
export declare class TokenBalancesController extends TokenBalancesController_base<typeof CONTROLLER, TokenBalancesControllerState, TokenBalancesControllerMessenger> {
|
|
68
87
|
#private;
|
|
69
|
-
constructor({ messenger, interval, state, queryMultipleAccounts, useAccountsAPI, allowExternalServices, }: TokenBalancesControllerOptions);
|
|
88
|
+
constructor({ messenger, interval, chainPollingIntervals, state, queryMultipleAccounts, useAccountsAPI, allowExternalServices, }: TokenBalancesControllerOptions);
|
|
89
|
+
/**
|
|
90
|
+
* Override to support per-chain polling intervals by grouping chains by interval
|
|
91
|
+
*
|
|
92
|
+
* @param options0 - The polling options
|
|
93
|
+
* @param options0.chainIds - Chain IDs to start polling for
|
|
94
|
+
*/
|
|
95
|
+
_startPolling({ chainIds }: {
|
|
96
|
+
chainIds: ChainIdHex[];
|
|
97
|
+
}): void;
|
|
98
|
+
/**
|
|
99
|
+
* Override to handle our custom polling approach
|
|
100
|
+
*/
|
|
101
|
+
_stopPollingByPollingTokenSetId(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Get polling configuration for a chain (includes default fallback)
|
|
104
|
+
*
|
|
105
|
+
* @param chainId - The chain ID to get config for
|
|
106
|
+
* @returns The polling configuration for the chain
|
|
107
|
+
*/
|
|
108
|
+
getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig;
|
|
70
109
|
_executePoll({ chainIds }: {
|
|
71
110
|
chainIds: ChainIdHex[];
|
|
72
111
|
}): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Update multiple chain polling configurations at once
|
|
114
|
+
*
|
|
115
|
+
* @param configs - Object mapping chain IDs to polling configurations
|
|
116
|
+
* @param options - Optional configuration for the update behavior
|
|
117
|
+
* @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
|
|
118
|
+
*/
|
|
119
|
+
updateChainPollingConfigs(configs: Record<ChainIdHex, ChainPollingConfig>, options?: UpdateChainPollingConfigsOptions): void;
|
|
73
120
|
updateBalances({ chainIds }?: {
|
|
74
121
|
chainIds?: ChainIdHex[];
|
|
75
122
|
}): Promise<void>;
|
|
76
123
|
resetState(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Clean up all timers and resources when controller is destroyed
|
|
126
|
+
*/
|
|
127
|
+
destroy(): void;
|
|
77
128
|
}
|
|
78
129
|
export default TokenBalancesController;
|
|
79
130
|
//# sourceMappingURL=TokenBalancesController.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AAOnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,qCAAqC;AACzF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAC/B,iCAAiC,EAElC,qCAAqC;AAEtC,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACtC,yCAAyC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,uCAAmC;AAQpC,OAAO,KAAK,EACV,8BAA8B,EAE9B,gCAAgC,EACjC,+BAA2B;AAE5B,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,QAAA,MAAM,UAAU,2BAAqC,CAAC;AAQtD,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,eAAe,EACf,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,UAAU,EACjB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.cts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,15 @@ export type TokenBalancesControllerState = {
|
|
|
14
14
|
tokenBalances: TokenBalances;
|
|
15
15
|
};
|
|
16
16
|
export type TokenBalancesControllerGetStateAction = ControllerGetStateAction<typeof CONTROLLER, TokenBalancesControllerState>;
|
|
17
|
-
export type
|
|
17
|
+
export type TokenBalancesControllerUpdateChainPollingConfigsAction = {
|
|
18
|
+
type: `TokenBalancesController:updateChainPollingConfigs`;
|
|
19
|
+
handler: TokenBalancesController['updateChainPollingConfigs'];
|
|
20
|
+
};
|
|
21
|
+
export type TokenBalancesControllerGetChainPollingConfigAction = {
|
|
22
|
+
type: `TokenBalancesController:getChainPollingConfig`;
|
|
23
|
+
handler: TokenBalancesController['getChainPollingConfig'];
|
|
24
|
+
};
|
|
25
|
+
export type TokenBalancesControllerActions = TokenBalancesControllerGetStateAction | TokenBalancesControllerUpdateChainPollingConfigsAction | TokenBalancesControllerGetChainPollingConfigAction;
|
|
18
26
|
export type TokenBalancesControllerStateChangeEvent = ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;
|
|
19
27
|
export type NativeBalanceEvent = {
|
|
20
28
|
type: `${typeof CONTROLLER}:updatedNativeBalance`;
|
|
@@ -24,9 +32,20 @@ export type TokenBalancesControllerEvents = TokenBalancesControllerStateChangeEv
|
|
|
24
32
|
export type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | TokensControllerGetStateAction | PreferencesControllerGetStateAction | AccountsControllerGetSelectedAccountAction | AccountsControllerListAccountsAction | AccountTrackerUpdateNativeBalancesAction | AccountTrackerUpdateStakedBalancesAction;
|
|
25
33
|
export type AllowedEvents = TokensControllerStateChangeEvent | PreferencesControllerStateChangeEvent | NetworkControllerStateChangeEvent | KeyringControllerAccountRemovedEvent;
|
|
26
34
|
export type TokenBalancesControllerMessenger = RestrictedMessenger<typeof CONTROLLER, TokenBalancesControllerActions | AllowedActions, TokenBalancesControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
|
|
35
|
+
export type ChainPollingConfig = {
|
|
36
|
+
/** Polling interval in milliseconds for this chain */
|
|
37
|
+
interval: number;
|
|
38
|
+
};
|
|
39
|
+
export type UpdateChainPollingConfigsOptions = {
|
|
40
|
+
/** Whether to immediately fetch balances after updating configs (default: true) */
|
|
41
|
+
immediateUpdate?: boolean;
|
|
42
|
+
};
|
|
27
43
|
export type TokenBalancesControllerOptions = {
|
|
28
44
|
messenger: TokenBalancesControllerMessenger;
|
|
45
|
+
/** Default interval for chains not specified in chainPollingIntervals */
|
|
29
46
|
interval?: number;
|
|
47
|
+
/** Per-chain polling configuration */
|
|
48
|
+
chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;
|
|
30
49
|
state?: Partial<TokenBalancesControllerState>;
|
|
31
50
|
/** When `true`, balances for *all* known accounts are queried. */
|
|
32
51
|
queryMultipleAccounts?: boolean;
|
|
@@ -66,14 +85,46 @@ declare const TokenBalancesController_base: (abstract new (...args: any[]) => {
|
|
|
66
85
|
}) & typeof import("@metamask/base-controller").BaseController;
|
|
67
86
|
export declare class TokenBalancesController extends TokenBalancesController_base<typeof CONTROLLER, TokenBalancesControllerState, TokenBalancesControllerMessenger> {
|
|
68
87
|
#private;
|
|
69
|
-
constructor({ messenger, interval, state, queryMultipleAccounts, useAccountsAPI, allowExternalServices, }: TokenBalancesControllerOptions);
|
|
88
|
+
constructor({ messenger, interval, chainPollingIntervals, state, queryMultipleAccounts, useAccountsAPI, allowExternalServices, }: TokenBalancesControllerOptions);
|
|
89
|
+
/**
|
|
90
|
+
* Override to support per-chain polling intervals by grouping chains by interval
|
|
91
|
+
*
|
|
92
|
+
* @param options0 - The polling options
|
|
93
|
+
* @param options0.chainIds - Chain IDs to start polling for
|
|
94
|
+
*/
|
|
95
|
+
_startPolling({ chainIds }: {
|
|
96
|
+
chainIds: ChainIdHex[];
|
|
97
|
+
}): void;
|
|
98
|
+
/**
|
|
99
|
+
* Override to handle our custom polling approach
|
|
100
|
+
*/
|
|
101
|
+
_stopPollingByPollingTokenSetId(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Get polling configuration for a chain (includes default fallback)
|
|
104
|
+
*
|
|
105
|
+
* @param chainId - The chain ID to get config for
|
|
106
|
+
* @returns The polling configuration for the chain
|
|
107
|
+
*/
|
|
108
|
+
getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig;
|
|
70
109
|
_executePoll({ chainIds }: {
|
|
71
110
|
chainIds: ChainIdHex[];
|
|
72
111
|
}): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Update multiple chain polling configurations at once
|
|
114
|
+
*
|
|
115
|
+
* @param configs - Object mapping chain IDs to polling configurations
|
|
116
|
+
* @param options - Optional configuration for the update behavior
|
|
117
|
+
* @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
|
|
118
|
+
*/
|
|
119
|
+
updateChainPollingConfigs(configs: Record<ChainIdHex, ChainPollingConfig>, options?: UpdateChainPollingConfigsOptions): void;
|
|
73
120
|
updateBalances({ chainIds }?: {
|
|
74
121
|
chainIds?: ChainIdHex[];
|
|
75
122
|
}): Promise<void>;
|
|
76
123
|
resetState(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Clean up all timers and resources when controller is destroyed
|
|
126
|
+
*/
|
|
127
|
+
destroy(): void;
|
|
77
128
|
}
|
|
78
129
|
export default TokenBalancesController;
|
|
79
130
|
//# sourceMappingURL=TokenBalancesController.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EACpB,kCAAkC;AAOnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,qCAAqC;AACzF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAC/B,iCAAiC,EAElC,qCAAqC;AAEtC,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EACtC,yCAAyC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,wCAAwC,EACxC,wCAAwC,EACzC,uCAAmC;AAQpC,OAAO,KAAK,EACV,8BAA8B,EAE9B,gCAAgC,EACjC,+BAA2B;AAE5B,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,QAAA,MAAM,UAAU,2BAAqC,CAAC;AAQtD,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,eAAe,EACf,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CACjD,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,UAAU,EACjB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ 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_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
12
|
+
var _TokenBalancesController_instances, _TokenBalancesController_queryAllAccounts, _TokenBalancesController_balanceFetchers, _TokenBalancesController_allTokens, _TokenBalancesController_detectedTokens, _TokenBalancesController_defaultInterval, _TokenBalancesController_chainPollingConfig, _TokenBalancesController_intervalPollingTimers, _TokenBalancesController_isControllerPollingActive, _TokenBalancesController_requestedChainIds, _TokenBalancesController_chainIdsWithTokens, _TokenBalancesController_getProvider, _TokenBalancesController_getNetworkClient, _TokenBalancesController_startIntervalGroupPolling, _TokenBalancesController_startPollingForInterval, _TokenBalancesController_setPollingTimer, _TokenBalancesController_onTokensChanged, _TokenBalancesController_onNetworkChanged, _TokenBalancesController_onAccountRemoved;
|
|
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";
|
|
@@ -35,7 +35,7 @@ const checksum = (addr) => toChecksumHexAddress(addr);
|
|
|
35
35
|
// ────────────────────────────────────────────────────────────────────────────
|
|
36
36
|
// region: Main controller
|
|
37
37
|
export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
38
|
-
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, state = {}, queryMultipleAccounts = true, useAccountsAPI = false, allowExternalServices = () => true, }) {
|
|
38
|
+
constructor({ messenger, interval = DEFAULT_INTERVAL_MS, chainPollingIntervals = {}, state = {}, queryMultipleAccounts = true, useAccountsAPI = false, allowExternalServices = () => true, }) {
|
|
39
39
|
super({
|
|
40
40
|
name: CONTROLLER,
|
|
41
41
|
messenger,
|
|
@@ -47,6 +47,16 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
47
47
|
_TokenBalancesController_balanceFetchers.set(this, void 0);
|
|
48
48
|
_TokenBalancesController_allTokens.set(this, {});
|
|
49
49
|
_TokenBalancesController_detectedTokens.set(this, {});
|
|
50
|
+
/** Default polling interval for chains without specific configuration */
|
|
51
|
+
_TokenBalancesController_defaultInterval.set(this, void 0);
|
|
52
|
+
/** Per-chain polling configuration */
|
|
53
|
+
_TokenBalancesController_chainPollingConfig.set(this, void 0);
|
|
54
|
+
/** Active polling timers grouped by interval */
|
|
55
|
+
_TokenBalancesController_intervalPollingTimers.set(this, new Map());
|
|
56
|
+
/** Track if controller-level polling is active */
|
|
57
|
+
_TokenBalancesController_isControllerPollingActive.set(this, false);
|
|
58
|
+
/** Store original chainIds from startPolling to preserve intent */
|
|
59
|
+
_TokenBalancesController_requestedChainIds.set(this, []);
|
|
50
60
|
_TokenBalancesController_getProvider.set(this, (chainId) => {
|
|
51
61
|
const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
|
|
52
62
|
const cfg = networkConfigurationsByChainId[chainId];
|
|
@@ -174,6 +184,8 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
174
184
|
});
|
|
175
185
|
});
|
|
176
186
|
__classPrivateFieldSet(this, _TokenBalancesController_queryAllAccounts, queryMultipleAccounts, "f");
|
|
187
|
+
__classPrivateFieldSet(this, _TokenBalancesController_defaultInterval, interval, "f");
|
|
188
|
+
__classPrivateFieldSet(this, _TokenBalancesController_chainPollingConfig, { ...chainPollingIntervals }, "f");
|
|
177
189
|
// Strategy order: API first, then RPC fallback
|
|
178
190
|
__classPrivateFieldSet(this, _TokenBalancesController_balanceFetchers, [
|
|
179
191
|
...(useAccountsAPI && allowExternalServices()
|
|
@@ -196,10 +208,61 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
196
208
|
});
|
|
197
209
|
this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onNetworkChanged, "f"));
|
|
198
210
|
this.messagingSystem.subscribe('KeyringController:accountRemoved', __classPrivateFieldGet(this, _TokenBalancesController_onAccountRemoved, "f"));
|
|
211
|
+
// Register action handlers for polling interval control
|
|
212
|
+
this.messagingSystem.registerActionHandler(`TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this));
|
|
213
|
+
this.messagingSystem.registerActionHandler(`TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this));
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Override to support per-chain polling intervals by grouping chains by interval
|
|
217
|
+
*
|
|
218
|
+
* @param options0 - The polling options
|
|
219
|
+
* @param options0.chainIds - Chain IDs to start polling for
|
|
220
|
+
*/
|
|
221
|
+
_startPolling({ chainIds }) {
|
|
222
|
+
// Store the original chainIds to preserve intent across config updates
|
|
223
|
+
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [...chainIds], "f");
|
|
224
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, true, "f");
|
|
225
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, chainIds, true);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Override to handle our custom polling approach
|
|
229
|
+
*/
|
|
230
|
+
_stopPollingByPollingTokenSetId() {
|
|
231
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
232
|
+
__classPrivateFieldSet(this, _TokenBalancesController_requestedChainIds, [], "f"); // Clear original intent when stopping
|
|
233
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
234
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get polling configuration for a chain (includes default fallback)
|
|
238
|
+
*
|
|
239
|
+
* @param chainId - The chain ID to get config for
|
|
240
|
+
* @returns The polling configuration for the chain
|
|
241
|
+
*/
|
|
242
|
+
getChainPollingConfig(chainId) {
|
|
243
|
+
return (__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f")[chainId] ?? {
|
|
244
|
+
interval: __classPrivateFieldGet(this, _TokenBalancesController_defaultInterval, "f"),
|
|
245
|
+
});
|
|
199
246
|
}
|
|
200
247
|
async _executePoll({ chainIds }) {
|
|
248
|
+
// This won't be called with our custom implementation, but keep for compatibility
|
|
201
249
|
await this.updateBalances({ chainIds });
|
|
202
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Update multiple chain polling configurations at once
|
|
253
|
+
*
|
|
254
|
+
* @param configs - Object mapping chain IDs to polling configurations
|
|
255
|
+
* @param options - Optional configuration for the update behavior
|
|
256
|
+
* @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)
|
|
257
|
+
*/
|
|
258
|
+
updateChainPollingConfigs(configs, options = { immediateUpdate: true }) {
|
|
259
|
+
Object.assign(__classPrivateFieldGet(this, _TokenBalancesController_chainPollingConfig, "f"), configs);
|
|
260
|
+
// If polling is currently active, restart with new interval groupings
|
|
261
|
+
if (__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
|
|
262
|
+
// Restart polling with immediate fetch by default, unless explicitly disabled
|
|
263
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startIntervalGroupPolling).call(this, __classPrivateFieldGet(this, _TokenBalancesController_requestedChainIds, "f"), options.immediateUpdate);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
203
266
|
async updateBalances({ chainIds } = {}) {
|
|
204
267
|
const targetChains = chainIds ?? __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_chainIdsWithTokens).call(this);
|
|
205
268
|
if (!targetChains.length) {
|
|
@@ -310,14 +373,74 @@ export class TokenBalancesController extends StaticIntervalPollingController() {
|
|
|
310
373
|
resetState() {
|
|
311
374
|
this.update(() => ({ tokenBalances: {} }));
|
|
312
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Clean up all timers and resources when controller is destroyed
|
|
378
|
+
*/
|
|
379
|
+
destroy() {
|
|
380
|
+
__classPrivateFieldSet(this, _TokenBalancesController_isControllerPollingActive, false, "f");
|
|
381
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
382
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
383
|
+
// Unregister action handlers
|
|
384
|
+
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:updateChainPollingConfigs`);
|
|
385
|
+
this.messagingSystem.unregisterActionHandler(`TokenBalancesController:getChainPollingConfig`);
|
|
386
|
+
super.destroy();
|
|
387
|
+
}
|
|
313
388
|
}
|
|
314
|
-
_TokenBalancesController_queryAllAccounts = new WeakMap(), _TokenBalancesController_balanceFetchers = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_detectedTokens = new WeakMap(), _TokenBalancesController_getProvider = new WeakMap(), _TokenBalancesController_getNetworkClient = new WeakMap(), _TokenBalancesController_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
389
|
+
_TokenBalancesController_queryAllAccounts = 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_onTokensChanged = new WeakMap(), _TokenBalancesController_onNetworkChanged = new WeakMap(), _TokenBalancesController_onAccountRemoved = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_chainIdsWithTokens = function _TokenBalancesController_chainIdsWithTokens() {
|
|
315
390
|
return [
|
|
316
391
|
...new Set([
|
|
317
392
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")),
|
|
318
393
|
...Object.keys(__classPrivateFieldGet(this, _TokenBalancesController_detectedTokens, "f")),
|
|
319
394
|
]),
|
|
320
395
|
];
|
|
396
|
+
}, _TokenBalancesController_startIntervalGroupPolling = function _TokenBalancesController_startIntervalGroupPolling(chainIds, immediate = true) {
|
|
397
|
+
// Stop any existing interval timers
|
|
398
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").forEach((timer) => clearInterval(timer));
|
|
399
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").clear();
|
|
400
|
+
// Group chains by their polling intervals
|
|
401
|
+
const intervalGroups = new Map();
|
|
402
|
+
for (const chainId of chainIds) {
|
|
403
|
+
const config = this.getChainPollingConfig(chainId);
|
|
404
|
+
const existing = intervalGroups.get(config.interval) || [];
|
|
405
|
+
existing.push(chainId);
|
|
406
|
+
intervalGroups.set(config.interval, existing);
|
|
407
|
+
}
|
|
408
|
+
// Start separate polling loop for each interval group
|
|
409
|
+
for (const [interval, chainIdsGroup] of intervalGroups) {
|
|
410
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_startPollingForInterval).call(this, interval, chainIdsGroup, immediate);
|
|
411
|
+
}
|
|
412
|
+
}, _TokenBalancesController_startPollingForInterval = function _TokenBalancesController_startPollingForInterval(interval, chainIds, immediate = true) {
|
|
413
|
+
const pollFunction = async () => {
|
|
414
|
+
if (!__classPrivateFieldGet(this, _TokenBalancesController_isControllerPollingActive, "f")) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
await this._executePoll({ chainIds });
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
console.warn(`Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`, error);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
// Poll immediately first if requested
|
|
425
|
+
if (immediate) {
|
|
426
|
+
pollFunction().catch((error) => {
|
|
427
|
+
console.warn(`Immediate polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
// Then start regular interval polling
|
|
431
|
+
__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_setPollingTimer).call(this, interval, chainIds, pollFunction);
|
|
432
|
+
}, _TokenBalancesController_setPollingTimer = function _TokenBalancesController_setPollingTimer(interval, chainIds, pollFunction) {
|
|
433
|
+
// Clear any existing timer for this interval first
|
|
434
|
+
const existingTimer = __classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").get(interval);
|
|
435
|
+
if (existingTimer) {
|
|
436
|
+
clearInterval(existingTimer);
|
|
437
|
+
}
|
|
438
|
+
const timer = setInterval(() => {
|
|
439
|
+
pollFunction().catch((error) => {
|
|
440
|
+
console.warn(`Interval polling failed for chains ${chainIds.join(', ')}:`, error);
|
|
441
|
+
});
|
|
442
|
+
}, interval);
|
|
443
|
+
__classPrivateFieldGet(this, _TokenBalancesController_intervalPollingTimers, "f").set(interval, timer);
|
|
321
444
|
};
|
|
322
445
|
export default TokenBalancesController;
|
|
323
446
|
//# sourceMappingURL=TokenBalancesController.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n interval?: number;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n await this.updateBalances({ chainIds });\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n}\n\nexport default TokenBalancesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n BNToHex,\n isValidHexAddress,\n toChecksumHexAddress,\n toHex,\n} from '@metamask/controller-utils';\nimport type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport { isStrictHexString } from '@metamask/utils';\nimport { produce } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type {\n AccountTrackerUpdateNativeBalancesAction,\n AccountTrackerUpdateStakedBalancesAction,\n} from './AccountTrackerController';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from './AssetsContractController';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\nimport { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst CONTROLLER = 'TokenBalancesController' as const;\nconst DEFAULT_INTERVAL_MS = 180_000; // 3 minutes\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n// account → chain → token → balance\nexport type TokenBalances = Record<\n ChecksumAddress,\n Record<ChainIdHex, Record<ChecksumAddress, Hex>>\n>;\n\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof CONTROLLER,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerUpdateChainPollingConfigsAction = {\n type: `TokenBalancesController:updateChainPollingConfigs`;\n handler: TokenBalancesController['updateChainPollingConfigs'];\n};\n\nexport type TokenBalancesControllerGetChainPollingConfigAction = {\n type: `TokenBalancesController:getChainPollingConfig`;\n handler: TokenBalancesController['getChainPollingConfig'];\n};\n\nexport type TokenBalancesControllerActions =\n | TokenBalancesControllerGetStateAction\n | TokenBalancesControllerUpdateChainPollingConfigsAction\n | TokenBalancesControllerGetChainPollingConfigAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<typeof CONTROLLER, TokenBalancesControllerState>;\n\nexport type NativeBalanceEvent = {\n type: `${typeof CONTROLLER}:updatedNativeBalance`;\n payload: unknown[];\n};\n\nexport type TokenBalancesControllerEvents =\n | TokenBalancesControllerStateChangeEvent\n | NativeBalanceEvent;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent\n | KeyringControllerAccountRemovedEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedMessenger<\n typeof CONTROLLER,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nexport type ChainPollingConfig = {\n /** Polling interval in milliseconds for this chain */\n interval: number;\n};\n\nexport type UpdateChainPollingConfigsOptions = {\n /** Whether to immediately fetch balances after updating configs (default: true) */\n immediateUpdate?: boolean;\n};\n\nexport type TokenBalancesControllerOptions = {\n messenger: TokenBalancesControllerMessenger;\n /** Default interval for chains not specified in chainPollingIntervals */\n interval?: number;\n /** Per-chain polling configuration */\n chainPollingIntervals?: Record<ChainIdHex, ChainPollingConfig>;\n state?: Partial<TokenBalancesControllerState>;\n /** When `true`, balances for *all* known accounts are queried. */\n queryMultipleAccounts?: boolean;\n /** Enable Accounts‑API strategy (if supported chain). */\n useAccountsAPI?: boolean;\n /** Disable external HTTP calls (privacy / offline mode). */\n allowExternalServices?: () => boolean;\n /** Custom logger. */\n log?: (...args: unknown[]) => void;\n};\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Helper utilities\nconst draft = <T>(base: T, fn: (d: T) => void): T => produce(base, fn);\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n// endregion\n\n// ────────────────────────────────────────────────────────────────────────────\n// region: Main controller\nexport class TokenBalancesController extends StaticIntervalPollingController<{\n chainIds: ChainIdHex[];\n}>()<\n typeof CONTROLLER,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n readonly #queryAllAccounts: boolean;\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n #allTokens: TokensControllerState['allTokens'] = {};\n\n #detectedTokens: TokensControllerState['allDetectedTokens'] = {};\n\n /** Default polling interval for chains without specific configuration */\n readonly #defaultInterval: number;\n\n /** Per-chain polling configuration */\n readonly #chainPollingConfig: Record<ChainIdHex, ChainPollingConfig>;\n\n /** Active polling timers grouped by interval */\n readonly #intervalPollingTimers: Map<number, NodeJS.Timeout> = new Map();\n\n /** Track if controller-level polling is active */\n #isControllerPollingActive = false;\n\n /** Store original chainIds from startPolling to preserve intent */\n #requestedChainIds: ChainIdHex[] = [];\n\n constructor({\n messenger,\n interval = DEFAULT_INTERVAL_MS,\n chainPollingIntervals = {},\n state = {},\n queryMultipleAccounts = true,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: TokenBalancesControllerOptions) {\n super({\n name: CONTROLLER,\n messenger,\n metadata,\n state: { tokenBalances: {}, ...state },\n });\n\n this.#queryAllAccounts = queryMultipleAccounts;\n this.#defaultInterval = interval;\n this.#chainPollingConfig = { ...chainPollingIntervals };\n\n // Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new RpcBalanceFetcher(this.#getProvider, this.#getNetworkClient, () => ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#detectedTokens,\n })),\n ];\n\n this.setIntervalLength(interval);\n\n // initial token state & subscriptions\n const { allTokens, allDetectedTokens } = this.messagingSystem.call(\n 'TokensController:getState',\n );\n this.#allTokens = allTokens;\n this.#detectedTokens = allDetectedTokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n (tokensState: TokensControllerState) => {\n this.#onTokensChanged(tokensState).catch((error) => {\n console.warn('Error handling token state change:', error);\n });\n },\n );\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkChanged,\n );\n this.messagingSystem.subscribe(\n 'KeyringController:accountRemoved',\n this.#onAccountRemoved,\n );\n\n // Register action handlers for polling interval control\n this.messagingSystem.registerActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n this.updateChainPollingConfigs.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n this.getChainPollingConfig.bind(this),\n );\n }\n\n #chainIdsWithTokens(): ChainIdHex[] {\n return [\n ...new Set([\n ...Object.keys(this.#allTokens),\n ...Object.keys(this.#detectedTokens),\n ]),\n ] as ChainIdHex[];\n }\n\n readonly #getProvider = (chainId: ChainIdHex): Web3Provider => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n const client = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return new Web3Provider(client.provider);\n };\n\n readonly #getNetworkClient = (chainId: ChainIdHex) => {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const cfg = networkConfigurationsByChainId[chainId];\n const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];\n return this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n };\n\n /**\n * Override to support per-chain polling intervals by grouping chains by interval\n *\n * @param options0 - The polling options\n * @param options0.chainIds - Chain IDs to start polling for\n */\n override _startPolling({ chainIds }: { chainIds: ChainIdHex[] }) {\n // Store the original chainIds to preserve intent across config updates\n this.#requestedChainIds = [...chainIds];\n this.#isControllerPollingActive = true;\n this.#startIntervalGroupPolling(chainIds, true);\n }\n\n /**\n * Start or restart interval-based polling for multiple chains\n *\n * @param chainIds - Chain IDs to start polling for\n * @param immediate - Whether to poll immediately before starting timers (default: true)\n */\n #startIntervalGroupPolling(chainIds: ChainIdHex[], immediate = true) {\n // Stop any existing interval timers\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Group chains by their polling intervals\n const intervalGroups = new Map<number, ChainIdHex[]>();\n\n for (const chainId of chainIds) {\n const config = this.getChainPollingConfig(chainId);\n const existing = intervalGroups.get(config.interval) || [];\n existing.push(chainId);\n intervalGroups.set(config.interval, existing);\n }\n\n // Start separate polling loop for each interval group\n for (const [interval, chainIdsGroup] of intervalGroups) {\n this.#startPollingForInterval(interval, chainIdsGroup, immediate);\n }\n }\n\n /**\n * Start polling loop for chains that share the same interval\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs that share this interval\n * @param immediate - Whether to poll immediately before starting the timer (default: true)\n */\n #startPollingForInterval(\n interval: number,\n chainIds: ChainIdHex[],\n immediate = true,\n ) {\n const pollFunction = async () => {\n if (!this.#isControllerPollingActive) {\n return;\n }\n try {\n await this._executePoll({ chainIds });\n } catch (error) {\n console.warn(\n `Polling failed for chains ${chainIds.join(', ')} with interval ${interval}:`,\n error,\n );\n }\n };\n\n // Poll immediately first if requested\n if (immediate) {\n pollFunction().catch((error) => {\n console.warn(\n `Immediate polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }\n\n // Then start regular interval polling\n this.#setPollingTimer(interval, chainIds, pollFunction);\n }\n\n /**\n * Helper method to set up polling timer\n *\n * @param interval - The polling interval in milliseconds\n * @param chainIds - Chain IDs for this interval\n * @param pollFunction - The function to call on each poll\n */\n #setPollingTimer(\n interval: number,\n chainIds: ChainIdHex[],\n pollFunction: () => Promise<void>,\n ) {\n // Clear any existing timer for this interval first\n const existingTimer = this.#intervalPollingTimers.get(interval);\n if (existingTimer) {\n clearInterval(existingTimer);\n }\n\n const timer = setInterval(() => {\n pollFunction().catch((error) => {\n console.warn(\n `Interval polling failed for chains ${chainIds.join(', ')}:`,\n error,\n );\n });\n }, interval);\n this.#intervalPollingTimers.set(interval, timer);\n }\n\n /**\n * Override to handle our custom polling approach\n */\n override _stopPollingByPollingTokenSetId() {\n this.#isControllerPollingActive = false;\n this.#requestedChainIds = []; // Clear original intent when stopping\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n }\n\n /**\n * Get polling configuration for a chain (includes default fallback)\n *\n * @param chainId - The chain ID to get config for\n * @returns The polling configuration for the chain\n */\n getChainPollingConfig(chainId: ChainIdHex): ChainPollingConfig {\n return (\n this.#chainPollingConfig[chainId] ?? {\n interval: this.#defaultInterval,\n }\n );\n }\n\n override async _executePoll({ chainIds }: { chainIds: ChainIdHex[] }) {\n // This won't be called with our custom implementation, but keep for compatibility\n await this.updateBalances({ chainIds });\n }\n\n /**\n * Update multiple chain polling configurations at once\n *\n * @param configs - Object mapping chain IDs to polling configurations\n * @param options - Optional configuration for the update behavior\n * @param options.immediateUpdate - Whether to immediately fetch balances after updating configs (default: true)\n */\n updateChainPollingConfigs(\n configs: Record<ChainIdHex, ChainPollingConfig>,\n options: UpdateChainPollingConfigsOptions = { immediateUpdate: true },\n ): void {\n Object.assign(this.#chainPollingConfig, configs);\n\n // If polling is currently active, restart with new interval groupings\n if (this.#isControllerPollingActive) {\n // Restart polling with immediate fetch by default, unless explicitly disabled\n this.#startIntervalGroupPolling(\n this.#requestedChainIds,\n options.immediateUpdate,\n );\n }\n }\n\n async updateBalances({ chainIds }: { chainIds?: ChainIdHex[] } = {}) {\n const targetChains = chainIds ?? this.#chainIdsWithTokens();\n if (!targetChains.length) {\n return;\n }\n\n const { address: selected } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...targetChains];\n\n // Try each fetcher in order, removing successfully processed chains\n for (const fetcher of this.#balanceFetchers) {\n const supportedChains = remainingChains.filter((c) =>\n fetcher.supports(c),\n );\n if (!supportedChains.length) {\n continue;\n }\n\n try {\n const balances = await fetcher.fetch({\n chainIds: supportedChains,\n queryAllAccounts: this.#queryAllAccounts,\n selectedAccount: selected as ChecksumAddress,\n allAccounts,\n });\n\n if (balances && balances.length > 0) {\n aggregated.push(...balances);\n // Remove chains that were successfully processed\n const processedChains = new Set(balances.map((b) => b.chainId));\n remainingChains = remainingChains.filter(\n (chain) => !processedChains.has(chain),\n );\n }\n } catch (error) {\n console.warn(\n `Balance fetcher failed for chains ${supportedChains.join(', ')}: ${String(error)}`,\n );\n // Continue to next fetcher (fallback)\n }\n\n // If all chains have been processed, break early\n if (remainingChains.length === 0) {\n break;\n }\n }\n\n // Determine which accounts to process\n const accountsToProcess = this.#queryAllAccounts\n ? allAccounts.map((a) => a.address as ChecksumAddress)\n : [selected as ChecksumAddress];\n\n const prev = this.state;\n const next = draft(prev, (d) => {\n // First, initialize all tokens from allTokens state with balance 0\n // for the accounts and chains we're processing\n for (const chainId of targetChains) {\n for (const account of accountsToProcess) {\n // Initialize tokens from allTokens\n const chainTokens = this.#allTokens[chainId];\n if (chainTokens?.[account]) {\n Object.values(chainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n\n // Initialize tokens from allDetectedTokens\n const detectedChainTokens = this.#detectedTokens[chainId];\n if (detectedChainTokens?.[account]) {\n Object.values(detectedChainTokens[account]).forEach(\n (token: { address: string }) => {\n const tokenAddress = checksum(token.address);\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = '0x0';\n },\n );\n }\n }\n }\n\n // Then update with actual fetched balances where available\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n ((d.tokenBalances[account] ??= {})[chainId] ??= {})[checksum(token)] =\n toHex(value);\n }\n });\n });\n\n if (!isEqual(prev, next)) {\n this.update(() => next);\n\n const nativeBalances = aggregated.filter(\n (r) => r.success && r.token === ZERO_ADDRESS,\n );\n\n // Update native token balances in a single batch operation for better performance\n if (nativeBalances.length > 0) {\n const balanceUpdates = nativeBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n balance: balance.value ? BNToHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateNativeBalances',\n balanceUpdates,\n );\n }\n\n // Get staking contract addresses for filtering\n const stakingContractAddresses = Object.values(\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n ).map((addr) => addr.toLowerCase());\n\n // Filter and update staked balances in a single batch operation for better performance\n const stakedBalances = aggregated.filter((r) => {\n return (\n r.success &&\n r.token !== ZERO_ADDRESS &&\n stakingContractAddresses.includes(r.token.toLowerCase())\n );\n });\n\n if (stakedBalances.length > 0) {\n const stakedBalanceUpdates = stakedBalances.map((balance) => ({\n address: balance.account,\n chainId: balance.chainId,\n stakedBalance: balance.value ? toHex(balance.value) : '0x0',\n }));\n\n this.messagingSystem.call(\n 'AccountTrackerController:updateStakedBalances',\n stakedBalanceUpdates,\n );\n }\n }\n }\n\n resetState() {\n this.update(() => ({ tokenBalances: {} }));\n }\n\n readonly #onTokensChanged = async (state: TokensControllerState) => {\n const changed: ChainIdHex[] = [];\n let hasChanges = false;\n\n // Get chains that have existing balances\n const chainsWithBalances = new Set<ChainIdHex>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const chainId of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n chainsWithBalances.add(chainId as ChainIdHex);\n }\n }\n\n // Only process chains that are explicitly mentioned in the incoming state change\n const incomingChainIds = new Set([\n ...Object.keys(state.allTokens),\n ...Object.keys(state.allDetectedTokens),\n ]);\n\n // Only proceed if there are actual changes to chains that have balances or are being added\n const relevantChainIds = Array.from(incomingChainIds).filter((chainId) => {\n const id = chainId as ChainIdHex;\n\n const hasTokensNow =\n (state.allTokens[id] && Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] && Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n // Check if there's an actual change in token state\n const hasTokenChange =\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id]);\n\n // Process chains that have actual changes OR are new chains getting tokens\n return hasTokenChange || (!hadTokensBefore && hasTokensNow);\n });\n\n if (relevantChainIds.length === 0) {\n // No relevant changes, just update internal state\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n return;\n }\n\n // Handle both cleanup and updates in a single state update\n this.update((s) => {\n for (const chainId of relevantChainIds) {\n const id = chainId as ChainIdHex;\n const hasTokensNow =\n (state.allTokens[id] &&\n Object.keys(state.allTokens[id]).length > 0) ||\n (state.allDetectedTokens[id] &&\n Object.keys(state.allDetectedTokens[id]).length > 0);\n const hadTokensBefore =\n (this.#allTokens[id] &&\n Object.keys(this.#allTokens[id]).length > 0) ||\n (this.#detectedTokens[id] &&\n Object.keys(this.#detectedTokens[id]).length > 0);\n\n if (\n !isEqual(state.allTokens[id], this.#allTokens[id]) ||\n !isEqual(state.allDetectedTokens[id], this.#detectedTokens[id])\n ) {\n if (hasTokensNow) {\n // Chain still has tokens - mark for async balance update\n changed.push(id);\n } else if (hadTokensBefore) {\n // Chain had tokens before but doesn't now - clean up balances immediately\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n if (s.tokenBalances[addressKey]?.[id]) {\n s.tokenBalances[addressKey][id] = {};\n hasChanges = true;\n }\n }\n }\n }\n }\n });\n\n this.#allTokens = state.allTokens;\n this.#detectedTokens = state.allDetectedTokens;\n\n // Only update balances for chains that still have tokens (and only if we haven't already updated state)\n if (changed.length && !hasChanges) {\n this.updateBalances({ chainIds: changed }).catch((error) => {\n console.warn('Error updating balances after token change:', error);\n });\n }\n };\n\n readonly #onNetworkChanged = (state: NetworkState) => {\n // Check if any networks were removed by comparing with previous state\n const currentNetworks = new Set(\n Object.keys(state.networkConfigurationsByChainId),\n );\n\n // Get all networks that currently have balances\n const networksWithBalances = new Set<string>();\n for (const address of Object.keys(this.state.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const network of Object.keys(\n this.state.tokenBalances[addressKey] || {},\n )) {\n networksWithBalances.add(network);\n }\n }\n\n // Find networks that were removed\n const removedNetworks = Array.from(networksWithBalances).filter(\n (network) => !currentNetworks.has(network),\n );\n\n if (removedNetworks.length > 0) {\n this.update((s) => {\n // Remove balances for all accounts on the deleted networks\n for (const address of Object.keys(s.tokenBalances)) {\n const addressKey = address as ChecksumAddress;\n for (const removedNetwork of removedNetworks) {\n const networkKey = removedNetwork as ChainIdHex;\n if (s.tokenBalances[addressKey]?.[networkKey]) {\n delete s.tokenBalances[addressKey][networkKey];\n }\n }\n }\n });\n }\n };\n\n readonly #onAccountRemoved = (addr: string) => {\n if (!isStrictHexString(addr) || !isValidHexAddress(addr)) {\n return;\n }\n this.update((s) => {\n delete s.tokenBalances[addr as ChecksumAddress];\n });\n };\n\n /**\n * Clean up all timers and resources when controller is destroyed\n */\n override destroy(): void {\n this.#isControllerPollingActive = false;\n this.#intervalPollingTimers.forEach((timer) => clearInterval(timer));\n this.#intervalPollingTimers.clear();\n\n // Unregister action handlers\n this.messagingSystem.unregisterActionHandler(\n `TokenBalancesController:updateChainPollingConfigs`,\n );\n this.messagingSystem.unregisterActionHandler(\n `TokenBalancesController:getChainPollingConfig`,\n );\n\n super.destroy();\n }\n}\n\nexport default TokenBalancesController;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/assets-controllers",
|
|
3
|
-
"version": "74.3.
|
|
3
|
+
"version": "74.3.3",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@ethersproject/contracts": "^5.7.0",
|
|
55
55
|
"@ethersproject/providers": "^5.7.0",
|
|
56
56
|
"@metamask/abi-utils": "^2.0.3",
|
|
57
|
-
"@metamask/base-controller": "^8.
|
|
57
|
+
"@metamask/base-controller": "^8.3.0",
|
|
58
58
|
"@metamask/contract-metadata": "^2.4.0",
|
|
59
59
|
"@metamask/controller-utils": "^11.12.0",
|
|
60
60
|
"@metamask/eth-query": "^4.0.0",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@babel/runtime": "^7.23.9",
|
|
82
82
|
"@metamask/account-api": "^0.9.0",
|
|
83
|
-
"@metamask/account-tree-controller": "^0.12.
|
|
83
|
+
"@metamask/account-tree-controller": "^0.12.1",
|
|
84
84
|
"@metamask/accounts-controller": "^33.0.0",
|
|
85
85
|
"@metamask/approval-controller": "^7.1.3",
|
|
86
86
|
"@metamask/auto-changelog": "^3.4.4",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"@metamask/preferences-controller": "^19.0.0",
|
|
96
96
|
"@metamask/providers": "^22.1.0",
|
|
97
97
|
"@metamask/snaps-controllers": "^14.0.1",
|
|
98
|
-
"@metamask/transaction-controller": "^60.
|
|
98
|
+
"@metamask/transaction-controller": "^60.2.0",
|
|
99
99
|
"@types/jest": "^27.4.1",
|
|
100
100
|
"@types/lodash": "^4.14.191",
|
|
101
101
|
"@types/node": "^16.18.54",
|