@metamask/assets-controllers 74.3.1 → 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 +16 -1
- package/dist/AccountTrackerController.cjs +37 -135
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +1 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +1 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +38 -138
- package/dist/AccountTrackerController.mjs.map +1 -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 +5 -5
|
@@ -9,167 +9,60 @@ 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
|
|
12
|
+
var _AccountTrackerController_instances, _AccountTrackerController_refreshMutex, _AccountTrackerController_includeStakedAssets, _AccountTrackerController_getStakedBalanceForChain, _AccountTrackerController_balanceFetchers, _AccountTrackerController_getProvider, _AccountTrackerController_getNetworkClient, _AccountTrackerController_getCorrectNetworkClient, _AccountTrackerController_getNetworkClientIds, _AccountTrackerController_registerMessageHandlers;
|
|
13
13
|
function $importDefault(module) {
|
|
14
14
|
if (module?.__esModule) {
|
|
15
15
|
return module.default;
|
|
16
16
|
}
|
|
17
17
|
return module;
|
|
18
18
|
}
|
|
19
|
-
import { Contract } from "@ethersproject/contracts";
|
|
20
19
|
import { Web3Provider } from "@ethersproject/providers";
|
|
21
20
|
import { query, safelyExecuteWithTimeout, toChecksumHexAddress } from "@metamask/controller-utils";
|
|
22
21
|
import $EthQuery from "@metamask/eth-query";
|
|
23
22
|
const EthQuery = $importDefault($EthQuery);
|
|
24
23
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
25
|
-
import { assert
|
|
24
|
+
import { assert } from "@metamask/utils";
|
|
26
25
|
import { Mutex } from "async-mutex";
|
|
27
|
-
import $BN from "bn.js";
|
|
28
|
-
const BN = $importDefault($BN);
|
|
29
26
|
import $lodash from "lodash";
|
|
30
27
|
const { cloneDeep, isEqual } = $lodash;
|
|
31
|
-
import $abiSingleCallBalancesContract from "single-call-balance-checker-abi";
|
|
32
|
-
const abiSingleCallBalancesContract = $importDefault($abiSingleCallBalancesContract);
|
|
33
|
-
import { SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, STAKING_CONTRACT_ADDRESS_BY_CHAINID } from "./AssetsContractController.mjs";
|
|
34
|
-
import { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from "./assetsUtil.mjs";
|
|
35
28
|
import { AccountsApiBalanceFetcher } from "./multi-chain-accounts-service/api-balance-fetcher.mjs";
|
|
29
|
+
import { RpcBalanceFetcher } from "./rpc-service/rpc-balance-fetcher.mjs";
|
|
36
30
|
/**
|
|
37
31
|
* The name of the {@link AccountTrackerController}.
|
|
38
32
|
*/
|
|
39
33
|
const controllerName = 'AccountTrackerController';
|
|
40
34
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
41
35
|
/**
|
|
42
|
-
* RPC
|
|
43
|
-
*
|
|
44
|
-
*/
|
|
45
|
-
class AccountTrackerRpcBalanceFetcher {
|
|
46
|
-
constructor(getProvider, getNetworkClient, includeStakedAssets, getStakedBalanceForChain) {
|
|
47
|
-
_AccountTrackerRpcBalanceFetcher_instances.add(this);
|
|
48
|
-
_AccountTrackerRpcBalanceFetcher_getProvider.set(this, void 0);
|
|
49
|
-
_AccountTrackerRpcBalanceFetcher_getNetworkClient.set(this, void 0);
|
|
50
|
-
_AccountTrackerRpcBalanceFetcher_includeStakedAssets.set(this, void 0);
|
|
51
|
-
_AccountTrackerRpcBalanceFetcher_getStakedBalanceForChain.set(this, void 0);
|
|
52
|
-
__classPrivateFieldSet(this, _AccountTrackerRpcBalanceFetcher_getProvider, getProvider, "f");
|
|
53
|
-
__classPrivateFieldSet(this, _AccountTrackerRpcBalanceFetcher_getNetworkClient, getNetworkClient, "f");
|
|
54
|
-
__classPrivateFieldSet(this, _AccountTrackerRpcBalanceFetcher_includeStakedAssets, includeStakedAssets, "f");
|
|
55
|
-
__classPrivateFieldSet(this, _AccountTrackerRpcBalanceFetcher_getStakedBalanceForChain, getStakedBalanceForChain, "f");
|
|
56
|
-
}
|
|
57
|
-
supports() {
|
|
58
|
-
return true; // fallback – supports every chain
|
|
59
|
-
}
|
|
60
|
-
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }) {
|
|
61
|
-
// Process all chains in parallel for better performance
|
|
62
|
-
const chainProcessingPromises = chainIds.map(async (chainId) => {
|
|
63
|
-
const accountsToUpdate = queryAllAccounts
|
|
64
|
-
? Object.values(allAccounts).map((account) => toChecksumHexAddress(account.address))
|
|
65
|
-
: [selectedAccount];
|
|
66
|
-
const { provider, blockTracker } = __classPrivateFieldGet(this, _AccountTrackerRpcBalanceFetcher_getNetworkClient, "f").call(this, chainId);
|
|
67
|
-
const ethQuery = new EthQuery(provider);
|
|
68
|
-
const chainResults = [];
|
|
69
|
-
// Force fresh block data before multicall
|
|
70
|
-
await safelyExecuteWithTimeout(() => blockTracker?.checkForLatestBlock?.());
|
|
71
|
-
// Fetch native balances
|
|
72
|
-
if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {
|
|
73
|
-
const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[chainId];
|
|
74
|
-
const contract = new Contract(contractAddress, abiSingleCallBalancesContract, __classPrivateFieldGet(this, _AccountTrackerRpcBalanceFetcher_getProvider, "f").call(this, chainId));
|
|
75
|
-
const nativeBalances = await safelyExecuteWithTimeout(() => contract.balances(accountsToUpdate, [ZERO_ADDRESS]), false, 3000);
|
|
76
|
-
if (nativeBalances) {
|
|
77
|
-
accountsToUpdate.forEach((address, index) => {
|
|
78
|
-
chainResults.push({
|
|
79
|
-
success: true,
|
|
80
|
-
value: new BN(nativeBalances[index].toString()),
|
|
81
|
-
account: address,
|
|
82
|
-
token: ZERO_ADDRESS,
|
|
83
|
-
chainId,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// Process accounts in batches using reduceInBatchesSerially
|
|
90
|
-
await reduceInBatchesSerially({
|
|
91
|
-
values: accountsToUpdate,
|
|
92
|
-
batchSize: TOKEN_PRICES_BATCH_SIZE,
|
|
93
|
-
initialResult: undefined,
|
|
94
|
-
eachBatch: async (workingResult, batch) => {
|
|
95
|
-
const balancePromises = batch.map(async (address) => {
|
|
96
|
-
const balanceResult = await __classPrivateFieldGet(this, _AccountTrackerRpcBalanceFetcher_instances, "m", _AccountTrackerRpcBalanceFetcher_getBalanceFromChain).call(this, address, ethQuery).catch(() => null);
|
|
97
|
-
if (balanceResult) {
|
|
98
|
-
chainResults.push({
|
|
99
|
-
success: true,
|
|
100
|
-
value: new BN(balanceResult.replace('0x', ''), 16),
|
|
101
|
-
account: address,
|
|
102
|
-
token: ZERO_ADDRESS,
|
|
103
|
-
chainId,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
chainResults.push({
|
|
108
|
-
success: false,
|
|
109
|
-
account: address,
|
|
110
|
-
token: ZERO_ADDRESS,
|
|
111
|
-
chainId,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
await Promise.allSettled(balancePromises);
|
|
116
|
-
return workingResult;
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
// Fetch staked balances if enabled
|
|
121
|
-
if (__classPrivateFieldGet(this, _AccountTrackerRpcBalanceFetcher_includeStakedAssets, "f")) {
|
|
122
|
-
const stakedBalancesPromise = __classPrivateFieldGet(this, _AccountTrackerRpcBalanceFetcher_getStakedBalanceForChain, "f").call(this, accountsToUpdate, chainId);
|
|
123
|
-
const stakedBalanceResult = await safelyExecuteWithTimeout(async () => (await stakedBalancesPromise));
|
|
124
|
-
if (stakedBalanceResult) {
|
|
125
|
-
// Find the staking contract address for this chain
|
|
126
|
-
const stakingContractAddress = STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId];
|
|
127
|
-
if (stakingContractAddress) {
|
|
128
|
-
Object.entries(stakedBalanceResult).forEach(([address, balance]) => {
|
|
129
|
-
chainResults.push({
|
|
130
|
-
success: true,
|
|
131
|
-
value: balance
|
|
132
|
-
? new BN(balance.replace('0x', ''), 16)
|
|
133
|
-
: new BN('0'),
|
|
134
|
-
account: address,
|
|
135
|
-
token: toChecksumHexAddress(stakingContractAddress),
|
|
136
|
-
chainId,
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return chainResults;
|
|
143
|
-
});
|
|
144
|
-
// Wait for all chains to complete (or fail) and collect results
|
|
145
|
-
const chainResultsArray = await Promise.allSettled(chainProcessingPromises);
|
|
146
|
-
const results = [];
|
|
147
|
-
chainResultsArray.forEach((chainResult) => {
|
|
148
|
-
if (chainResult.status === 'fulfilled') {
|
|
149
|
-
results.push(...chainResult.value);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
// Log error but continue with other chains
|
|
153
|
-
console.warn('Chain processing failed:', chainResult.reason);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
return results;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
_AccountTrackerRpcBalanceFetcher_getProvider = new WeakMap(), _AccountTrackerRpcBalanceFetcher_getNetworkClient = new WeakMap(), _AccountTrackerRpcBalanceFetcher_includeStakedAssets = new WeakMap(), _AccountTrackerRpcBalanceFetcher_getStakedBalanceForChain = new WeakMap(), _AccountTrackerRpcBalanceFetcher_instances = new WeakSet(), _AccountTrackerRpcBalanceFetcher_getBalanceFromChain =
|
|
160
|
-
/**
|
|
161
|
-
* Fetches the balance of a given address from the blockchain.
|
|
36
|
+
* Creates an RPC balance fetcher configured for AccountTracker use case.
|
|
37
|
+
* Returns only native balances and staked balances (no token balances).
|
|
162
38
|
*
|
|
163
|
-
* @param
|
|
164
|
-
* @param
|
|
165
|
-
* @
|
|
39
|
+
* @param getProvider - Function to get Web3Provider for a given chain ID
|
|
40
|
+
* @param getNetworkClient - Function to get NetworkClient for a given chain ID
|
|
41
|
+
* @param includeStakedAssets - Whether to include staked assets in the fetch
|
|
42
|
+
* @returns BalanceFetcher configured to fetch only native and optionally staked balances
|
|
166
43
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
44
|
+
function createAccountTrackerRpcBalanceFetcher(getProvider, getNetworkClient, includeStakedAssets) {
|
|
45
|
+
// Provide empty tokens state to ensure only native and staked balances are fetched
|
|
46
|
+
const getEmptyTokensState = () => ({
|
|
47
|
+
allTokens: {},
|
|
48
|
+
allDetectedTokens: {},
|
|
171
49
|
});
|
|
172
|
-
|
|
50
|
+
const rpcBalanceFetcher = new RpcBalanceFetcher(getProvider, getNetworkClient, getEmptyTokensState);
|
|
51
|
+
// Wrap the RpcBalanceFetcher to filter staked balances when not needed
|
|
52
|
+
return {
|
|
53
|
+
supports(_chainId) {
|
|
54
|
+
return rpcBalanceFetcher.supports();
|
|
55
|
+
},
|
|
56
|
+
async fetch(params) {
|
|
57
|
+
const balances = await rpcBalanceFetcher.fetch(params);
|
|
58
|
+
if (!includeStakedAssets) {
|
|
59
|
+
// Filter out staked balances from the results
|
|
60
|
+
return balances.filter((balance) => balance.token === ZERO_ADDRESS);
|
|
61
|
+
}
|
|
62
|
+
return balances;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
173
66
|
const accountTrackerMetadata = {
|
|
174
67
|
accountsByChainId: {
|
|
175
68
|
persist: true,
|
|
@@ -231,7 +124,7 @@ export class AccountTrackerController extends StaticIntervalPollingController()
|
|
|
231
124
|
...(useAccountsAPI && allowExternalServices()
|
|
232
125
|
? [new AccountsApiBalanceFetcher('extension', __classPrivateFieldGet(this, _AccountTrackerController_getProvider, "f"))]
|
|
233
126
|
: []),
|
|
234
|
-
|
|
127
|
+
createAccountTrackerRpcBalanceFetcher(__classPrivateFieldGet(this, _AccountTrackerController_getProvider, "f"), __classPrivateFieldGet(this, _AccountTrackerController_getNetworkClient, "f"), __classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")),
|
|
235
128
|
], "f");
|
|
236
129
|
this.setIntervalLength(interval);
|
|
237
130
|
this.messagingSystem.subscribe('AccountsController:selectedEvmAccountChange', (newAddress, prevAddress) => {
|
|
@@ -374,6 +267,13 @@ export class AccountTrackerController extends StaticIntervalPollingController()
|
|
|
374
267
|
// Apply staked balances
|
|
375
268
|
Object.entries(stakedBalancesByChainAndAddress).forEach(([chainId, balancesByAddress]) => {
|
|
376
269
|
Object.entries(balancesByAddress).forEach(([address, stakedBalance]) => {
|
|
270
|
+
// Ensure account structure exists
|
|
271
|
+
if (!nextAccountsByChainId[chainId]) {
|
|
272
|
+
nextAccountsByChainId[chainId] = {};
|
|
273
|
+
}
|
|
274
|
+
if (!nextAccountsByChainId[chainId][address]) {
|
|
275
|
+
nextAccountsByChainId[chainId][address] = { balance: '0x0' };
|
|
276
|
+
}
|
|
377
277
|
if (nextAccountsByChainId[chainId][address].stakedBalance !==
|
|
378
278
|
stakedBalance) {
|
|
379
279
|
nextAccountsByChainId[chainId][address].stakedBalance =
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountTrackerController.mjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClient,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, hasProperty, type Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport BN from 'bn.js';\nimport { cloneDeep, isEqual } from 'lodash';\nimport abiSingleCallBalancesContract from 'single-call-balance-checker-abi';\n\nimport {\n SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID,\n STAKING_CONTRACT_ADDRESS_BY_CHAINID,\n type AssetsContractController,\n type StakedBalance,\n} from './AssetsContractController';\nimport { reduceInBatchesSerially, TOKEN_PRICES_BATCH_SIZE } from './assetsUtil';\nimport {\n AccountsApiBalanceFetcher,\n type BalanceFetcher,\n type ProcessedBalance,\n} from './multi-chain-accounts-service/api-balance-fetcher';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\n/**\n * RPC-based balance fetcher for AccountTrackerController.\n * Fetches only native balances and staked balances (no token balances).\n */\nclass AccountTrackerRpcBalanceFetcher implements BalanceFetcher {\n readonly #getProvider: (chainId: Hex) => Web3Provider;\n\n readonly #getNetworkClient: (chainId: Hex) => NetworkClient;\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n constructor(\n getProvider: (chainId: Hex) => Web3Provider,\n getNetworkClient: (chainId: Hex) => NetworkClient,\n includeStakedAssets: boolean,\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'],\n ) {\n this.#getProvider = getProvider;\n this.#getNetworkClient = getNetworkClient;\n this.#includeStakedAssets = includeStakedAssets;\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n }\n\n supports(): boolean {\n return true; // fallback – supports every chain\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n // Process all chains in parallel for better performance\n const chainProcessingPromises = chainIds.map(async (chainId) => {\n const accountsToUpdate = queryAllAccounts\n ? Object.values(allAccounts).map(\n (account) =>\n toChecksumHexAddress(account.address) as ChecksumAddress,\n )\n : [selectedAccount];\n\n const { provider, blockTracker } = this.#getNetworkClient(chainId);\n const ethQuery = new EthQuery(provider);\n const chainResults: ProcessedBalance[] = [];\n\n // Force fresh block data before multicall\n await safelyExecuteWithTimeout(() =>\n blockTracker?.checkForLatestBlock?.(),\n );\n\n // Fetch native balances\n if (hasProperty(SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID, chainId)) {\n const contractAddress = SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID[\n chainId\n ] as string;\n\n const contract = new Contract(\n contractAddress,\n abiSingleCallBalancesContract,\n this.#getProvider(chainId),\n );\n\n const nativeBalances = await safelyExecuteWithTimeout(\n () =>\n contract.balances(accountsToUpdate, [ZERO_ADDRESS]) as Promise<\n BigNumber[]\n >,\n false,\n 3_000, // 3s max call for multicall contract call\n );\n\n if (nativeBalances) {\n accountsToUpdate.forEach((address, index) => {\n chainResults.push({\n success: true,\n value: new BN(nativeBalances[index].toString()),\n account: address,\n token: ZERO_ADDRESS,\n chainId,\n });\n });\n }\n } else {\n // Process accounts in batches using reduceInBatchesSerially\n await reduceInBatchesSerially<string, void>({\n values: accountsToUpdate,\n batchSize: TOKEN_PRICES_BATCH_SIZE,\n initialResult: undefined,\n eachBatch: async (workingResult: void, batch: string[]) => {\n const balancePromises = batch.map(async (address: string) => {\n const balanceResult = await this.#getBalanceFromChain(\n address,\n ethQuery,\n ).catch(() => null);\n\n if (balanceResult) {\n chainResults.push({\n success: true,\n value: new BN(balanceResult.replace('0x', ''), 16),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n } else {\n chainResults.push({\n success: false,\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n }\n });\n\n await Promise.allSettled(balancePromises);\n return workingResult;\n },\n });\n }\n\n // Fetch staked balances if enabled\n if (this.#includeStakedAssets) {\n const stakedBalancesPromise = this.#getStakedBalanceForChain(\n accountsToUpdate,\n chainId,\n );\n\n const stakedBalanceResult = await safelyExecuteWithTimeout(\n async () =>\n (await stakedBalancesPromise) as Record<string, StakedBalance>,\n );\n\n if (stakedBalanceResult) {\n // Find the staking contract address for this chain\n const stakingContractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n chainId as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n\n if (stakingContractAddress) {\n Object.entries(stakedBalanceResult).forEach(\n ([address, balance]) => {\n chainResults.push({\n success: true,\n value: balance\n ? new BN(balance.replace('0x', ''), 16)\n : new BN('0'),\n account: address as ChecksumAddress,\n token: toChecksumHexAddress(\n stakingContractAddress,\n ) as ChecksumAddress,\n chainId,\n });\n },\n );\n }\n }\n }\n\n return chainResults;\n });\n\n // Wait for all chains to complete (or fail) and collect results\n const chainResultsArray = await Promise.allSettled(chainProcessingPromises);\n const results: ProcessedBalance[] = [];\n\n chainResultsArray.forEach((chainResult) => {\n if (chainResult.status === 'fulfilled') {\n results.push(...chainResult.value);\n } else {\n // Log error but continue with other chains\n console.warn('Chain processing failed:', chainResult.reason);\n }\n });\n\n return results;\n }\n\n /**\n * Fetches the balance of a given address from the blockchain.\n *\n * @param address - The account address to fetch the balance for.\n * @param ethQuery - The EthQuery instance to query getBalance with.\n * @returns A promise that resolves to the balance in a hex string format.\n */\n async #getBalanceFromChain(\n address: string,\n ethQuery?: EthQuery,\n ): Promise<string | undefined> {\n return await safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n return await query(ethQuery, 'getBalance', [address]);\n });\n }\n}\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The action that can be performed to update multiple native token balances in batch.\n */\nexport type AccountTrackerUpdateNativeBalancesAction = {\n type: `${typeof controllerName}:updateNativeBalances`;\n handler: AccountTrackerController['updateNativeBalances'];\n};\n\n/**\n * The action that can be performed to update multiple staked balances in batch.\n */\nexport type AccountTrackerUpdateStakedBalancesAction = {\n type: `${typeof controllerName}:updateStakedBalances`;\n handler: AccountTrackerController['updateStakedBalances'];\n};\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n | AccountTrackerControllerGetStateAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n * @param options.useAccountsAPI - Enable Accounts‑API strategy (if supported chain).\n * @param options.allowExternalServices - Disable external HTTP calls (privacy / offline mode).\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n useAccountsAPI?: boolean;\n allowExternalServices?: () => boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n // Initialize balance fetchers - Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n new AccountTrackerRpcBalanceFetcher(\n this.#getProvider,\n this.#getNetworkClient,\n includeStakedAssets,\n getStakedBalanceForChain,\n ),\n ];\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n\n this.#registerMessageHandlers();\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n readonly #getProvider = (chainId: Hex): 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: Hex) => {\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 * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId) {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n blockTracker,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n blockTracker,\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Use balance fetchers with fallback strategy\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...chainIds] as ChainIdHex[];\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: isMultiAccountBalancesEnabled,\n selectedAccount: toChecksumHexAddress(\n selectedAccount.address,\n ) 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 // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n // Process the aggregated balance results\n const stakedBalancesByChainAndAddress: Record<\n string,\n Record<string, string>\n > = {};\n\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n const checksumAddress = toChecksumHexAddress(account);\n const hexValue = `0x${value.toString(16)}`;\n\n if (token === ZERO_ADDRESS) {\n // Native balance\n if (\n nextAccountsByChainId[chainId][checksumAddress].balance !==\n hexValue\n ) {\n nextAccountsByChainId[chainId][checksumAddress].balance =\n hexValue;\n hasChanges = true;\n }\n } else {\n // Staked balance (from staking contract address)\n if (!stakedBalancesByChainAndAddress[chainId]) {\n stakedBalancesByChainAndAddress[chainId] = {};\n }\n stakedBalancesByChainAndAddress[chainId][checksumAddress] =\n hexValue;\n }\n }\n });\n\n // Apply staked balances\n Object.entries(stakedBalancesByChainAndAddress).forEach(\n ([chainId, balancesByAddress]) => {\n Object.entries(balancesByAddress).forEach(\n ([address, stakedBalance]) => {\n if (\n nextAccountsByChainId[chainId][address].stakedBalance !==\n stakedBalance\n ) {\n nextAccountsByChainId[chainId][address].stakedBalance =\n stakedBalance;\n hasChanges = true;\n }\n },\n );\n },\n );\n\n // Only update state if something changed\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n\n /**\n * Updates the balances of multiple native tokens in a single batch operation.\n * This is more efficient than calling updateNativeToken multiple times as it\n * triggers only one state update.\n *\n * @param balances - Array of balance updates, each containing address, chainId, and balance.\n */\n updateNativeBalances(\n balances: { address: string; chainId: Hex; balance: Hex }[],\n ) {\n this.update((state) => {\n balances.forEach(({ address, chainId, balance }) => {\n const checksumAddress = toChecksumHexAddress(address);\n\n // Ensure the chainId exists in the state\n if (!state.accountsByChainId[chainId]) {\n state.accountsByChainId[chainId] = {};\n }\n\n // Ensure the address exists for this chain\n if (!state.accountsByChainId[chainId][checksumAddress]) {\n state.accountsByChainId[chainId][checksumAddress] = {\n balance: '0x0',\n };\n }\n\n // Update the balance\n state.accountsByChainId[chainId][checksumAddress].balance = balance;\n });\n });\n }\n\n /**\n * Updates the staked balances of multiple accounts in a single batch operation.\n * This is more efficient than updating staked balances individually as it\n * triggers only one state update.\n *\n * @param stakedBalances - Array of staked balance updates, each containing address, chainId, and stakedBalance.\n */\n updateStakedBalances(\n stakedBalances: {\n address: string;\n chainId: Hex;\n stakedBalance: StakedBalance;\n }[],\n ) {\n this.update((state) => {\n stakedBalances.forEach(({ address, chainId, stakedBalance }) => {\n const checksumAddress = toChecksumHexAddress(address);\n\n // Ensure the chainId exists in the state\n if (!state.accountsByChainId[chainId]) {\n state.accountsByChainId[chainId] = {};\n }\n\n // Ensure the address exists for this chain\n if (!state.accountsByChainId[chainId][checksumAddress]) {\n state.accountsByChainId[chainId][checksumAddress] = {\n balance: '0x0',\n };\n }\n\n // Update the staked balance\n state.accountsByChainId[chainId][checksumAddress].stakedBalance =\n stakedBalance;\n });\n });\n }\n\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:updateNativeBalances` as const,\n this.updateNativeBalances.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:updateStakedBalances` as const,\n this.updateStakedBalances.bind(this),\n );\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
1
|
+
{"version":3,"file":"AccountTrackerController.mjs","sourceRoot":"","sources":["../src/AccountTrackerController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,iCAAiC;AAYxD,OAAO,EACL,KAAK,EACL,wBAAwB,EACxB,oBAAoB,EACrB,mCAAmC;AACpC,OAAO,SAAQ,4BAA4B;;AAO3C,OAAO,EAAE,+BAA+B,EAAE,qCAAqC;AAE/E,OAAO,EAAE,MAAM,EAAY,wBAAwB;AACnD,OAAO,EAAE,KAAK,EAAE,oBAAoB;;;AAOpC,OAAO,EACL,yBAAyB,EAG1B,+DAA2D;AAC5D,OAAO,EAAE,iBAAiB,EAAE,8CAA0C;AAEtE;;GAEG;AACH,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAKlD,MAAM,YAAY,GAChB,4CAA+D,CAAC;AAElE;;;;;;;;GAQG;AACH,SAAS,qCAAqC,CAC5C,WAA2C,EAC3C,gBAAiD,EACjD,mBAA4B;IAE5B,mFAAmF;IACnF,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC,CAAC;QACjC,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,EAAE;KACtB,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAC7C,WAAW,EACX,gBAAgB,EAChB,mBAAmB,CACpB,CAAC;IAEF,uEAAuE;IACvE,OAAO;QACL,QAAQ,CAAC,QAAoB;YAC3B,OAAO,iBAAiB,CAAC,QAAQ,EAAE,CAAC;QACtC,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,MAAM;YAChB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAEvD,IAAI,CAAC,mBAAmB,EAAE;gBACxB,8CAA8C;gBAC9C,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;aACrE;YAED,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AA2BD,MAAM,sBAAsB,GAAG;IAC7B,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAkFF;;GAEG;AACH,MAAM,OAAO,wBAAyB,SAAQ,+BAA+B,EAI5E;IASC;;;;;;;;;;;OAWG;IACH,YAAY,EACV,QAAQ,GAAG,KAAK,EAChB,KAAK,EACL,SAAS,EACT,wBAAwB,EACxB,mBAAmB,GAAG,KAAK,EAC3B,cAAc,GAAG,KAAK,EACtB,qBAAqB,GAAG,GAAG,EAAE,CAAC,IAAI,GASnC;QACC,MAAM,EAAE,uBAAuB,EAAE,GAAG,SAAS,CAAC,IAAI,CAChD,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,GAC3B,GAAG,SAAS,CAAC,IAAI,CAChB,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QACF,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,SAAS;YACT,KAAK,EAAE;gBACL,iBAAiB,EAAE;oBACjB,CAAC,OAAO,CAAC,EAAE,EAAE;iBACd;gBACD,GAAG,KAAK;aACT;YACD,QAAQ,EAAE,sBAAsB;SACjC,CAAC,CAAC;;QAxDI,iDAAgB,IAAI,KAAK,EAAE,EAAC;QAE5B,gEAA8B;QAE9B,qEAAgF;QAEhF,4DAAmC;QAgJnC,gDAAe,CAAC,OAAY,EAAgB,EAAE;YACrD,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;YACF,MAAM,GAAG,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACtC,wCAAwC,EACxC,eAAe,CAChB,CAAC;YACF,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,EAAC;QAEO,qDAAoB,CAAC,OAAY,EAAE,EAAE;YAC5C,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;YACF,MAAM,GAAG,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,wCAAwC,EACxC,eAAe,CAChB,CAAC;QACJ,CAAC,EAAC;QApHA,uBAAA,IAAI,sDAA6B,wBAAwB,MAAA,CAAC;QAE1D,uBAAA,IAAI,iDAAwB,mBAAmB,MAAA,CAAC;QAEhD,6EAA6E;QAC7E,uBAAA,IAAI,6CAAoB;YACtB,GAAG,CAAC,cAAc,IAAI,qBAAqB,EAAE;gBAC3C,CAAC,CAAC,CAAC,IAAI,yBAAyB,CAAC,WAAW,EAAE,uBAAA,IAAI,6CAAa,CAAC,CAAC;gBACjE,CAAC,CAAC,EAAE,CAAC;YACP,qCAAqC,CACnC,uBAAA,IAAI,6CAAa,EACjB,uBAAA,IAAI,kDAAkB,EACtB,uBAAA,IAAI,qDAAqB,CAC1B;SACF,MAAA,CAAC;QAEF,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEjC,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,6CAA6C,EAC7C,CAAC,UAAU,EAAE,WAAW,EAAE,EAAE;YAC1B,IAAI,UAAU,KAAK,WAAW,EAAE;gBAC9B,0CAA0C;gBAC1C,mEAAmE;gBACnE,IAAI,CAAC,OAAO,CAAC,uBAAA,IAAI,0FAAqB,MAAzB,IAAI,CAAuB,CAAC,CAAC;aAC3C;QACH,CAAC,EACD,CAAC,KAAK,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CACjC,CAAC;QAEF,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,CAA2B,CAAC;IAClC,CAAC;IAEO,YAAY,CAAC,WAAqB;QACxC,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAClE,MAAM,EAAE,uBAAuB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3D,4BAA4B,CAC7B,CAAC;QACF,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,GAC3C,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;QAExE,+CAA+C;QAC/C,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;YACjC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE;gBAClC,iBAAiB,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;gBACnC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBAC3B,iBAAiB,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;gBAC9D,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,CAAC,CAAC;QAEH,oEAAoE;QACpE,4DAA4D;QAC5D,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAC7B,IAAI,CAAC,eAAe;aACjB,IAAI,CAAC,iCAAiC,CAAC;aACvC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE,CACvB,oBAAoB,CAAC,eAAe,CAAC,OAAO,CAAC,CAC9C,CACJ,CAAC;QACF,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CACnC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CACzC,CAAC;QACF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAClC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1C,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG;oBACpC,OAAO,EAAE,KAAK;iBACf,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACjD,YAAY,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC/B,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,EAAE;YAC7D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;YAC9C,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAyED;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,EACjB,gBAAgB,GACW;QAC3B,gFAAgF;QAChF,mEAAmE;QACnE,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,gBAAmC;QAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC/C,uCAAuC,CACxC,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3C,iCAAiC,CAClC,CAAC;QACF,MAAM,EAAE,6BAA6B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACjE,gCAAgC,CACjC,CAAC;QAEF,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,8CAAc,CAAC,OAAO,EAAE,CAAC;QACvD,IAAI;YACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,eAAe,EAAE,EAAE;gBACxD,MAAM,EAAE,OAAO,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;gBACnE,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAE5B,8CAA8C;YAC9C,MAAM,UAAU,GAAuB,EAAE,CAAC;YAC1C,IAAI,eAAe,GAAG,CAAC,GAAG,QAAQ,CAAiB,CAAC;YAEpD,oEAAoE;YACpE,KAAK,MAAM,OAAO,IAAI,uBAAA,IAAI,iDAAiB,EAAE;gBAC3C,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACnD,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CACpB,CAAC;gBACF,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE;oBAC3B,SAAS;iBACV;gBAED,IAAI;oBACF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;wBACnC,QAAQ,EAAE,eAAe;wBACzB,gBAAgB,EAAE,6BAA6B;wBAC/C,eAAe,EAAE,oBAAoB,CACnC,eAAe,CAAC,OAAO,CACL;wBACpB,WAAW;qBACZ,CAAC,CAAC;oBAEH,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;wBACnC,UAAU,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;wBAC7B,iDAAiD;wBACjD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;wBAChE,eAAe,GAAG,eAAe,CAAC,MAAM,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CACvC,CAAC;qBACH;iBACF;gBAAC,OAAO,KAAK,EAAE;oBACd,OAAO,CAAC,IAAI,CACV,qCAAqC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CACpF,CAAC;oBACF,sCAAsC;iBACvC;gBAED,iDAAiD;gBACjD,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE;oBAChC,MAAM;iBACP;aACF;YAED,yEAAyE;YACzE,MAAM,qBAAqB,GACzB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1C,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,yCAAyC;YACzC,MAAM,+BAA+B,GAGjC,EAAE,CAAC;YAEP,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;gBACjE,IAAI,OAAO,IAAI,KAAK,KAAK,SAAS,EAAE;oBAClC,MAAM,eAAe,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;oBACtD,MAAM,QAAQ,GAAG,KAAK,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBAE3C,IAAI,KAAK,KAAK,YAAY,EAAE;wBAC1B,iBAAiB;wBACjB,IACE,qBAAqB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO;4BACvD,QAAQ,EACR;4BACA,qBAAqB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO;gCACrD,QAAQ,CAAC;4BACX,UAAU,GAAG,IAAI,CAAC;yBACnB;qBACF;yBAAM;wBACL,iDAAiD;wBACjD,IAAI,CAAC,+BAA+B,CAAC,OAAO,CAAC,EAAE;4BAC7C,+BAA+B,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;yBAC/C;wBACD,+BAA+B,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC;4BACvD,QAAQ,CAAC;qBACZ;iBACF;YACH,CAAC,CAAC,CAAC;YAEH,wBAAwB;YACxB,MAAM,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,OAAO,CACrD,CAAC,CAAC,OAAO,EAAE,iBAAiB,CAAC,EAAE,EAAE;gBAC/B,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CACvC,CAAC,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,EAAE;oBAC3B,kCAAkC;oBAClC,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE;wBACnC,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;qBACrC;oBACD,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE;wBAC5C,qBAAqB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;qBAC9D;oBACD,IACE,qBAAqB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa;wBACrD,aAAa,EACb;wBACA,qBAAqB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa;4BACnD,aAAa,CAAC;wBAChB,UAAU,GAAG,IAAI,CAAC;qBACnB;gBACH,CAAC,CACF,CAAC;YACJ,CAAC,CACF,CAAC;YAEF,yCAAyC;YACzC,IAAI,UAAU,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,KAAK,CAAC,iBAAiB,GAAG,qBAAqB,CAAC;gBAClD,CAAC,CAAC,CAAC;aACJ;SACF;gBAAS;YACR,WAAW,EAAE,CAAC;SACf;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,wBAAwB,CAC5B,SAAmB,EACnB,eAAiC;QAIjC,MAAM,EAAE,QAAQ,EAAE,GAAG,uBAAA,IAAI,8FAAyB,MAA7B,IAAI,EAA0B,eAAe,CAAC,CAAC;QAEpE,4DAA4D;QAC5D,OAAO,MAAM,OAAO,CAAC,GAAG,CACtB,SAAS,CAAC,GAAG,CACX,CAAC,OAAO,EAAwD,EAAE;YAChE,OAAO,wBAAwB,CAAC,KAAK,IAAI,EAAE;gBACzC,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;gBACtC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;gBAE/D,IAAI,aAA4B,CAAC;gBACjC,IAAI,uBAAA,IAAI,qDAAqB,EAAE;oBAC7B,aAAa,GAAG,CACd,MAAM,uBAAA,IAAI,0DAA0B,MAA9B,IAAI,EAA2B,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,CACjE,CAAC,OAAO,CAAC,CAAC;iBACZ;gBACD,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACL,CAAC,CACF,CACF,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBAChC,IAAI,CAAC,IAAI,EAAE;oBACT,OAAO,GAAG,CAAC;iBACZ;gBAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,GAAG,IAAI,CAAC;gBAC/C,OAAO;oBACL,GAAG,GAAG;oBACN,CAAC,OAAO,CAAC,EAAE;wBACT,OAAO;wBACP,aAAa;qBACd;iBACF,CAAC;YACJ,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB,CAClB,QAA2D;QAE3D,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;gBACjD,MAAM,eAAe,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;gBAEtD,yCAAyC;gBACzC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE;oBACrC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;iBACvC;gBAED,2CAA2C;gBAC3C,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,EAAE;oBACtD,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,GAAG;wBAClD,OAAO,EAAE,KAAK;qBACf,CAAC;iBACH;gBAED,qBAAqB;gBACrB,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC;YACtE,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB,CAClB,cAIG;QAEH,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,cAAc,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE;gBAC7D,MAAM,eAAe,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;gBAEtD,yCAAyC;gBACzC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE;oBACrC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;iBACvC;gBAED,2CAA2C;gBAC3C,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,EAAE;oBACtD,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,GAAG;wBAClD,OAAO,EAAE,KAAK;qBACf,CAAC;iBACH;gBAED,4BAA4B;gBAC5B,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,aAAa;oBAC7D,aAAa,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CAaF;6gBAxU0B,eAAiC;IACxD,MAAM,uBAAuB,GAC3B,eAAe;QACf,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,4BAA4B,CAAC;aACpD,uBAAuB,CAAC;IAC7B,MAAM,EACJ,aAAa,EAAE,EAAE,OAAO,EAAE,EAC1B,QAAQ,EACR,YAAY,GACb,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3B,wCAAwC,EACxC,uBAAuB,CACxB,CAAC;IAEF,OAAO;QACL,OAAO;QACP,QAAQ;QACR,QAAQ,EAAE,IAAI,QAAQ,CAAC,QAAQ,CAAC;QAChC,YAAY;KACb,CAAC;AACJ,CAAC;IAQC,MAAM,EAAE,8BAA8B,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAClE,4BAA4B,CAC7B,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAC1D,CAAC,oBAAoB,EAAE,EAAE,CACvB,oBAAoB,CAAC,YAAY,CAAC,GAAG,CACnC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,eAAe,CAC7C,CACJ,CAAC;AACJ,CAAC;IAyRC,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,uBAAgC,EACjD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CACrC,CAAC;IAEF,IAAI,CAAC,eAAe,CAAC,qBAAqB,CACxC,GAAG,cAAc,uBAAgC,EACjD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CACrC,CAAC;AACJ,CAAC;AAGH,eAAe,wBAAwB,CAAC","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerSelectedEvmAccountChangeEvent,\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerListAccountsAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerStateChangeEvent,\n ControllerGetStateAction,\n RestrictedMessenger,\n} from '@metamask/base-controller';\nimport {\n query,\n safelyExecuteWithTimeout,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport EthQuery from '@metamask/eth-query';\nimport type {\n NetworkClient,\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller';\nimport { assert, type Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport { cloneDeep, isEqual } from 'lodash';\n\nimport type {\n AssetsContractController,\n StakedBalance,\n} 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';\n\n/**\n * The name of the {@link AccountTrackerController}.\n */\nconst controllerName = 'AccountTrackerController';\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\n/**\n * Creates an RPC balance fetcher configured for AccountTracker use case.\n * Returns only native balances and staked balances (no token balances).\n *\n * @param getProvider - Function to get Web3Provider for a given chain ID\n * @param getNetworkClient - Function to get NetworkClient for a given chain ID\n * @param includeStakedAssets - Whether to include staked assets in the fetch\n * @returns BalanceFetcher configured to fetch only native and optionally staked balances\n */\nfunction createAccountTrackerRpcBalanceFetcher(\n getProvider: (chainId: Hex) => Web3Provider,\n getNetworkClient: (chainId: Hex) => NetworkClient,\n includeStakedAssets: boolean,\n): BalanceFetcher {\n // Provide empty tokens state to ensure only native and staked balances are fetched\n const getEmptyTokensState = () => ({\n allTokens: {},\n allDetectedTokens: {},\n });\n\n const rpcBalanceFetcher = new RpcBalanceFetcher(\n getProvider,\n getNetworkClient,\n getEmptyTokensState,\n );\n\n // Wrap the RpcBalanceFetcher to filter staked balances when not needed\n return {\n supports(_chainId: ChainIdHex): boolean {\n return rpcBalanceFetcher.supports();\n },\n\n async fetch(params) {\n const balances = await rpcBalanceFetcher.fetch(params);\n\n if (!includeStakedAssets) {\n // Filter out staked balances from the results\n return balances.filter((balance) => balance.token === ZERO_ADDRESS);\n }\n\n return balances;\n },\n };\n}\n\n/**\n * AccountInformation\n *\n * Account information object\n *\n * balance - Hex string of an account balance in wei\n *\n * stakedBalance - Hex string of an account staked balance in wei\n */\nexport type AccountInformation = {\n balance: string;\n stakedBalance?: string;\n};\n\n/**\n * AccountTrackerControllerState\n *\n * Account tracker controller state\n *\n * accountsByChainId - Map of addresses to account information by chain\n */\nexport type AccountTrackerControllerState = {\n accountsByChainId: Record<string, { [address: string]: AccountInformation }>;\n};\n\nconst accountTrackerMetadata = {\n accountsByChainId: {\n persist: true,\n anonymous: false,\n },\n};\n\n/**\n * The action that can be performed to get the state of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTrackerControllerState\n>;\n\n/**\n * The action that can be performed to update multiple native token balances in batch.\n */\nexport type AccountTrackerUpdateNativeBalancesAction = {\n type: `${typeof controllerName}:updateNativeBalances`;\n handler: AccountTrackerController['updateNativeBalances'];\n};\n\n/**\n * The action that can be performed to update multiple staked balances in batch.\n */\nexport type AccountTrackerUpdateStakedBalancesAction = {\n type: `${typeof controllerName}:updateStakedBalances`;\n handler: AccountTrackerController['updateStakedBalances'];\n};\n\n/**\n * The actions that can be performed using the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerActions =\n | AccountTrackerControllerGetStateAction\n | AccountTrackerUpdateNativeBalancesAction\n | AccountTrackerUpdateStakedBalancesAction;\n\n/**\n * The messenger of the {@link AccountTrackerController} for communication.\n */\nexport type AllowedActions =\n | AccountsControllerListAccountsAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction\n | NetworkControllerGetStateAction\n | NetworkControllerGetNetworkClientByIdAction;\n\n/**\n * The event that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n AccountTrackerControllerState\n >;\n\n/**\n * The events that {@link AccountTrackerController} can emit.\n */\nexport type AccountTrackerControllerEvents =\n AccountTrackerControllerStateChangeEvent;\n\n/**\n * The external events available to the {@link AccountTrackerController}.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedEvmAccountChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link AccountTrackerController}.\n */\nexport type AccountTrackerControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTrackerControllerActions | AllowedActions,\n AccountTrackerControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/** The input to start polling for the {@link AccountTrackerController} */\ntype AccountTrackerPollingInput = {\n networkClientIds: NetworkClientId[];\n};\n\n/**\n * Controller that tracks the network balances for all user accounts.\n */\nexport class AccountTrackerController extends StaticIntervalPollingController<AccountTrackerPollingInput>()<\n typeof controllerName,\n AccountTrackerControllerState,\n AccountTrackerControllerMessenger\n> {\n readonly #refreshMutex = new Mutex();\n\n readonly #includeStakedAssets: boolean;\n\n readonly #getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n\n readonly #balanceFetchers: BalanceFetcher[];\n\n /**\n * Creates an AccountTracker instance.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new account balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller messaging system.\n * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.\n * @param options.includeStakedAssets - Whether to include staked assets in the account balances.\n * @param options.useAccountsAPI - Enable Accounts‑API strategy (if supported chain).\n * @param options.allowExternalServices - Disable external HTTP calls (privacy / offline mode).\n */\n constructor({\n interval = 10000,\n state,\n messenger,\n getStakedBalanceForChain,\n includeStakedAssets = false,\n useAccountsAPI = false,\n allowExternalServices = () => true,\n }: {\n interval?: number;\n state?: Partial<AccountTrackerControllerState>;\n messenger: AccountTrackerControllerMessenger;\n getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain'];\n includeStakedAssets?: boolean;\n useAccountsAPI?: boolean;\n allowExternalServices?: () => boolean;\n }) {\n const { selectedNetworkClientId } = messenger.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = messenger.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n super({\n name: controllerName,\n messenger,\n state: {\n accountsByChainId: {\n [chainId]: {},\n },\n ...state,\n },\n metadata: accountTrackerMetadata,\n });\n this.#getStakedBalanceForChain = getStakedBalanceForChain;\n\n this.#includeStakedAssets = includeStakedAssets;\n\n // Initialize balance fetchers - Strategy order: API first, then RPC fallback\n this.#balanceFetchers = [\n ...(useAccountsAPI && allowExternalServices()\n ? [new AccountsApiBalanceFetcher('extension', this.#getProvider)]\n : []),\n createAccountTrackerRpcBalanceFetcher(\n this.#getProvider,\n this.#getNetworkClient,\n this.#includeStakedAssets,\n ),\n ];\n\n this.setIntervalLength(interval);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedEvmAccountChange',\n (newAddress, prevAddress) => {\n if (newAddress !== prevAddress) {\n // Making an async call for this new event\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(this.#getNetworkClientIds());\n }\n },\n (event): string => event.address,\n );\n\n this.#registerMessageHandlers();\n }\n\n private syncAccounts(newChainIds: string[]) {\n const accountsByChainId = cloneDeep(this.state.accountsByChainId);\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId: currentChainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n const existing = Object.keys(accountsByChainId?.[currentChainId] ?? {});\n\n // Initialize new chain IDs if they don't exist\n newChainIds.forEach((newChainId) => {\n if (!accountsByChainId[newChainId]) {\n accountsByChainId[newChainId] = {};\n existing.forEach((address) => {\n accountsByChainId[newChainId][address] = { balance: '0x0' };\n });\n }\n });\n\n // Note: The address from the preferences controller are checksummed\n // The addresses from the accounts controller are lowercased\n const addresses = Object.values(\n this.messagingSystem\n .call('AccountsController:listAccounts')\n .map((internalAccount) =>\n toChecksumHexAddress(internalAccount.address),\n ),\n );\n const newAddresses = addresses.filter(\n (address) => !existing.includes(address),\n );\n const oldAddresses = existing.filter(\n (address) => !addresses.includes(address),\n );\n Object.keys(accountsByChainId).forEach((chainId) => {\n newAddresses.forEach((address) => {\n accountsByChainId[chainId][address] = {\n balance: '0x0',\n };\n });\n });\n\n Object.keys(accountsByChainId).forEach((chainId) => {\n oldAddresses.forEach((address) => {\n delete accountsByChainId[chainId][address];\n });\n });\n\n if (!isEqual(this.state.accountsByChainId, accountsByChainId)) {\n this.update((state) => {\n state.accountsByChainId = accountsByChainId;\n });\n }\n }\n\n readonly #getProvider = (chainId: Hex): 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: Hex) => {\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 * Resolves a networkClientId to a network client config\n * or globally selected network config if not provided\n *\n * @param networkClientId - Optional networkClientId to fetch a network client with\n * @returns network client config\n */\n #getCorrectNetworkClient(networkClientId?: NetworkClientId) {\n const selectedNetworkClientId =\n networkClientId ??\n this.messagingSystem.call('NetworkController:getState')\n .selectedNetworkClientId;\n const {\n configuration: { chainId },\n provider,\n blockTracker,\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n return {\n chainId,\n provider,\n ethQuery: new EthQuery(provider),\n blockTracker,\n };\n }\n\n /**\n * Retrieves the list of network client IDs.\n *\n * @returns An array of network client IDs.\n */\n #getNetworkClientIds(): NetworkClientId[] {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n return Object.values(networkConfigurationsByChainId).flatMap(\n (networkConfiguration) =>\n networkConfiguration.rpcEndpoints.map(\n (rpcEndpoint) => rpcEndpoint.networkClientId,\n ),\n );\n }\n\n /**\n * Refreshes the balances of the accounts using the networkClientId\n *\n * @param input - The input for the poll.\n * @param input.networkClientIds - The network client IDs used to get balances.\n */\n async _executePoll({\n networkClientIds,\n }: AccountTrackerPollingInput): Promise<void> {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh(networkClientIds);\n }\n\n /**\n * Refreshes the balances of the accounts depending on the multi-account setting.\n * If multi-account is disabled, only updates the selected account balance.\n * If multi-account is enabled, updates balances for all accounts.\n *\n * @param networkClientIds - Optional network client IDs to fetch a network client with\n */\n async refresh(networkClientIds: NetworkClientId[]) {\n const selectedAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n const allAccounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(\n 'PreferencesController:getState',\n );\n\n const releaseLock = await this.#refreshMutex.acquire();\n try {\n const chainIds = networkClientIds.map((networkClientId) => {\n const { chainId } = this.#getCorrectNetworkClient(networkClientId);\n return chainId;\n });\n\n this.syncAccounts(chainIds);\n\n // Use balance fetchers with fallback strategy\n const aggregated: ProcessedBalance[] = [];\n let remainingChains = [...chainIds] as ChainIdHex[];\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: isMultiAccountBalancesEnabled,\n selectedAccount: toChecksumHexAddress(\n selectedAccount.address,\n ) 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 // Build a _copy_ of the current state and track whether anything changed\n const nextAccountsByChainId: AccountTrackerControllerState['accountsByChainId'] =\n cloneDeep(this.state.accountsByChainId);\n let hasChanges = false;\n\n // Process the aggregated balance results\n const stakedBalancesByChainAndAddress: Record<\n string,\n Record<string, string>\n > = {};\n\n aggregated.forEach(({ success, value, account, token, chainId }) => {\n if (success && value !== undefined) {\n const checksumAddress = toChecksumHexAddress(account);\n const hexValue = `0x${value.toString(16)}`;\n\n if (token === ZERO_ADDRESS) {\n // Native balance\n if (\n nextAccountsByChainId[chainId][checksumAddress].balance !==\n hexValue\n ) {\n nextAccountsByChainId[chainId][checksumAddress].balance =\n hexValue;\n hasChanges = true;\n }\n } else {\n // Staked balance (from staking contract address)\n if (!stakedBalancesByChainAndAddress[chainId]) {\n stakedBalancesByChainAndAddress[chainId] = {};\n }\n stakedBalancesByChainAndAddress[chainId][checksumAddress] =\n hexValue;\n }\n }\n });\n\n // Apply staked balances\n Object.entries(stakedBalancesByChainAndAddress).forEach(\n ([chainId, balancesByAddress]) => {\n Object.entries(balancesByAddress).forEach(\n ([address, stakedBalance]) => {\n // Ensure account structure exists\n if (!nextAccountsByChainId[chainId]) {\n nextAccountsByChainId[chainId] = {};\n }\n if (!nextAccountsByChainId[chainId][address]) {\n nextAccountsByChainId[chainId][address] = { balance: '0x0' };\n }\n if (\n nextAccountsByChainId[chainId][address].stakedBalance !==\n stakedBalance\n ) {\n nextAccountsByChainId[chainId][address].stakedBalance =\n stakedBalance;\n hasChanges = true;\n }\n },\n );\n },\n );\n\n // Only update state if something changed\n if (hasChanges) {\n this.update((state) => {\n state.accountsByChainId = nextAccountsByChainId;\n });\n }\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Sync accounts balances with some additional addresses.\n *\n * @param addresses - the additional addresses, may be hardware wallet addresses.\n * @param networkClientId - Optional networkClientId to fetch a network client with.\n * @returns accounts - addresses with synced balance\n */\n async syncBalanceWithAddresses(\n addresses: string[],\n networkClientId?: NetworkClientId,\n ): Promise<\n Record<string, { balance: string; stakedBalance?: StakedBalance }>\n > {\n const { ethQuery } = this.#getCorrectNetworkClient(networkClientId);\n\n // TODO: This should use multicall when enabled by the user.\n return await Promise.all(\n addresses.map(\n (address): Promise<[string, string, StakedBalance] | undefined> => {\n return safelyExecuteWithTimeout(async () => {\n assert(ethQuery, 'Provider not set.');\n const balance = await query(ethQuery, 'getBalance', [address]);\n\n let stakedBalance: StakedBalance;\n if (this.#includeStakedAssets) {\n stakedBalance = (\n await this.#getStakedBalanceForChain([address], networkClientId)\n )[address];\n }\n return [address, balance, stakedBalance];\n });\n },\n ),\n ).then((value) => {\n return value.reduce((obj, item) => {\n if (!item) {\n return obj;\n }\n\n const [address, balance, stakedBalance] = item;\n return {\n ...obj,\n [address]: {\n balance,\n stakedBalance,\n },\n };\n }, {});\n });\n }\n\n /**\n * Updates the balances of multiple native tokens in a single batch operation.\n * This is more efficient than calling updateNativeToken multiple times as it\n * triggers only one state update.\n *\n * @param balances - Array of balance updates, each containing address, chainId, and balance.\n */\n updateNativeBalances(\n balances: { address: string; chainId: Hex; balance: Hex }[],\n ) {\n this.update((state) => {\n balances.forEach(({ address, chainId, balance }) => {\n const checksumAddress = toChecksumHexAddress(address);\n\n // Ensure the chainId exists in the state\n if (!state.accountsByChainId[chainId]) {\n state.accountsByChainId[chainId] = {};\n }\n\n // Ensure the address exists for this chain\n if (!state.accountsByChainId[chainId][checksumAddress]) {\n state.accountsByChainId[chainId][checksumAddress] = {\n balance: '0x0',\n };\n }\n\n // Update the balance\n state.accountsByChainId[chainId][checksumAddress].balance = balance;\n });\n });\n }\n\n /**\n * Updates the staked balances of multiple accounts in a single batch operation.\n * This is more efficient than updating staked balances individually as it\n * triggers only one state update.\n *\n * @param stakedBalances - Array of staked balance updates, each containing address, chainId, and stakedBalance.\n */\n updateStakedBalances(\n stakedBalances: {\n address: string;\n chainId: Hex;\n stakedBalance: StakedBalance;\n }[],\n ) {\n this.update((state) => {\n stakedBalances.forEach(({ address, chainId, stakedBalance }) => {\n const checksumAddress = toChecksumHexAddress(address);\n\n // Ensure the chainId exists in the state\n if (!state.accountsByChainId[chainId]) {\n state.accountsByChainId[chainId] = {};\n }\n\n // Ensure the address exists for this chain\n if (!state.accountsByChainId[chainId][checksumAddress]) {\n state.accountsByChainId[chainId][checksumAddress] = {\n balance: '0x0',\n };\n }\n\n // Update the staked balance\n state.accountsByChainId[chainId][checksumAddress].stakedBalance =\n stakedBalance;\n });\n });\n }\n\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:updateNativeBalances` as const,\n this.updateNativeBalances.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:updateStakedBalances` as const,\n this.updateStakedBalances.bind(this),\n );\n }\n}\n\nexport default AccountTrackerController;\n"]}
|
|
@@ -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
|