@metamask/assets-controllers 100.1.0 → 100.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/dist/AccountTrackerController.cjs +25 -4
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +5 -2
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +5 -2
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +26 -5
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/NftController.cjs +198 -150
- package/dist/NftController.cjs.map +1 -1
- package/dist/NftController.d.cts.map +1 -1
- package/dist/NftController.d.mts.map +1 -1
- package/dist/NftController.mjs +198 -150
- package/dist/NftController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +51 -12
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +51 -12
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/TokenDetectionController.cjs +0 -2
- package/dist/TokenDetectionController.cjs.map +1 -1
- package/dist/TokenDetectionController.d.cts.map +1 -1
- package/dist/TokenDetectionController.d.mts.map +1 -1
- package/dist/TokenDetectionController.mjs +0 -2
- package/dist/TokenDetectionController.mjs.map +1 -1
- package/dist/TokenListController.cjs +38 -5
- package/dist/TokenListController.cjs.map +1 -1
- package/dist/TokenListController.d.cts.map +1 -1
- package/dist/TokenListController.d.mts.map +1 -1
- package/dist/TokenListController.mjs +38 -5
- package/dist/TokenListController.mjs.map +1 -1
- package/dist/balances.cjs +46 -12
- package/dist/balances.cjs.map +1 -1
- package/dist/balances.d.cts +14 -3
- package/dist/balances.d.cts.map +1 -1
- package/dist/balances.d.mts +14 -3
- package/dist/balances.d.mts.map +1 -1
- package/dist/balances.mjs +46 -12
- package/dist/balances.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +18 -9
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts +10 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts +10 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +18 -9
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.cjs +99 -58
- package/dist/rpc-service/rpc-balance-fetcher.cjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.cts +4 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.cts.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.mts +4 -1
- package/dist/rpc-service/rpc-balance-fetcher.d.mts.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.mjs +99 -58
- package/dist/rpc-service/rpc-balance-fetcher.mjs.map +1 -1
- package/package.json +7 -7
|
@@ -36,19 +36,27 @@ class RpcBalanceFetcher {
|
|
|
36
36
|
supports() {
|
|
37
37
|
return true; // fallback – supports every chain
|
|
38
38
|
}
|
|
39
|
-
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }) {
|
|
39
|
+
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, unprocessedTokens, }) {
|
|
40
40
|
// Process all chains in parallel for better performance
|
|
41
41
|
const chainProcessingPromises = chainIds.map(async (chainId) => {
|
|
42
|
+
// if there are unprocessed tokens for a chain, it means the chain was partially processed.
|
|
43
|
+
// because of this, we need to build distinct account <-> token groups to process
|
|
44
|
+
const hasUnprocessedTokensForChain = queryAllAccounts
|
|
45
|
+
? Object.values(unprocessedTokens ?? {}).some((chainMap) => Boolean(chainMap[chainId] && chainMap[chainId].length > 0))
|
|
46
|
+
: Boolean(unprocessedTokens?.[selectedAccount.toLowerCase()]?.[chainId] &&
|
|
47
|
+
unprocessedTokens[selectedAccount.toLowerCase()][chainId].length >
|
|
48
|
+
0);
|
|
42
49
|
const tokensState = __classPrivateFieldGet(this, _RpcBalanceFetcher_getTokensState, "f").call(this);
|
|
43
|
-
const accountTokenGroups
|
|
50
|
+
const { accountTokenGroups, includeNativeAndStaked } = hasUnprocessedTokensForChain
|
|
51
|
+
? buildUnprocessedAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, unprocessedTokens)
|
|
52
|
+
: buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, tokensState.allTokens, tokensState.allDetectedTokens);
|
|
44
53
|
if (!accountTokenGroups.length) {
|
|
45
54
|
return [];
|
|
46
55
|
}
|
|
47
56
|
const provider = __classPrivateFieldGet(this, _RpcBalanceFetcher_getProvider, "f").call(this, chainId);
|
|
48
57
|
await __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_ensureFreshBlockData).call(this, chainId);
|
|
49
58
|
const balanceResult = await (0, controller_utils_1.safelyExecuteWithTimeout)(async () => {
|
|
50
|
-
return await (0, multicall_1.getTokenBalancesForMultipleAddresses)(accountTokenGroups, chainId, provider,
|
|
51
|
-
true);
|
|
59
|
+
return await (0, multicall_1.getTokenBalancesForMultipleAddresses)(accountTokenGroups, chainId, provider, includeNativeAndStaked, includeNativeAndStaked);
|
|
52
60
|
}, true, RPC_TIMEOUT_MS);
|
|
53
61
|
// If timeout or error occurred, return empty array for this chain
|
|
54
62
|
if (!balanceResult) {
|
|
@@ -56,22 +64,24 @@ class RpcBalanceFetcher {
|
|
|
56
64
|
}
|
|
57
65
|
const { tokenBalances, stakedBalances } = balanceResult;
|
|
58
66
|
const chainResults = [];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// Ensure native token entries exist for all addresses
|
|
65
|
-
allAddressesForNative.forEach((address) => {
|
|
66
|
-
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
67
|
-
chainResults.push({
|
|
68
|
-
success: true,
|
|
69
|
-
value: nativeBalance || new bn_js_1.default('0'),
|
|
70
|
-
account: address,
|
|
71
|
-
token: ZERO_ADDRESS,
|
|
72
|
-
chainId,
|
|
67
|
+
if (includeNativeAndStaked) {
|
|
68
|
+
// Add native token entries for all addresses being processed
|
|
69
|
+
const allAddressesForNative = new Set();
|
|
70
|
+
accountTokenGroups.forEach((group) => {
|
|
71
|
+
allAddressesForNative.add(group.accountAddress);
|
|
73
72
|
});
|
|
74
|
-
|
|
73
|
+
// Ensure native token entries exist for all addresses
|
|
74
|
+
allAddressesForNative.forEach((address) => {
|
|
75
|
+
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
76
|
+
chainResults.push({
|
|
77
|
+
success: true,
|
|
78
|
+
value: nativeBalance || new bn_js_1.default('0'),
|
|
79
|
+
account: address,
|
|
80
|
+
token: ZERO_ADDRESS,
|
|
81
|
+
chainId,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
75
85
|
// Add other token balances
|
|
76
86
|
Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {
|
|
77
87
|
// Skip native token since we handled it explicitly above
|
|
@@ -90,7 +100,7 @@ class RpcBalanceFetcher {
|
|
|
90
100
|
});
|
|
91
101
|
// Add staked balances for all addresses being processed
|
|
92
102
|
const stakingContractAddress = __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_getStakingContractAddress).call(this, chainId);
|
|
93
|
-
if (stakingContractAddress) {
|
|
103
|
+
if (includeNativeAndStaked && stakingContractAddress) {
|
|
94
104
|
// Get all unique addresses being processed for this chain
|
|
95
105
|
const allAddresses = new Set();
|
|
96
106
|
accountTokenGroups.forEach((group) => {
|
|
@@ -99,10 +109,10 @@ class RpcBalanceFetcher {
|
|
|
99
109
|
// Add staked balance entry for each address
|
|
100
110
|
const checksummedStakingAddress = checksum(stakingContractAddress);
|
|
101
111
|
allAddresses.forEach((address) => {
|
|
102
|
-
const stakedBalance = stakedBalances?.[address]
|
|
112
|
+
const stakedBalance = stakedBalances?.[address] ?? null;
|
|
103
113
|
chainResults.push({
|
|
104
114
|
success: true,
|
|
105
|
-
value: stakedBalance
|
|
115
|
+
value: stakedBalance ?? new bn_js_1.default('0'),
|
|
106
116
|
account: address,
|
|
107
117
|
token: checksummedStakingAddress,
|
|
108
118
|
chainId,
|
|
@@ -139,50 +149,20 @@ async function _RpcBalanceFetcher_ensureFreshBlockData(chainId) {
|
|
|
139
149
|
const networkClient = __classPrivateFieldGet(this, _RpcBalanceFetcher_getNetworkClient, "f").call(this, chainId);
|
|
140
150
|
await networkClient.blockTracker?.checkForLatestBlock?.();
|
|
141
151
|
};
|
|
142
|
-
|
|
143
|
-
* Merges imported & detected tokens for the requested chain and returns a list
|
|
144
|
-
* of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.
|
|
145
|
-
*
|
|
146
|
-
* @param chainId - The chain ID to build account token groups for
|
|
147
|
-
* @param queryAllAccounts - Whether to query all accounts or just the selected one
|
|
148
|
-
* @param selectedAccount - The currently selected account
|
|
149
|
-
* @param allAccounts - All available accounts
|
|
150
|
-
* @param allTokens - All tokens from TokensController
|
|
151
|
-
* @param allDetectedTokens - All detected tokens from TokensController
|
|
152
|
-
* @returns Array of account/token groups for multicall
|
|
153
|
-
*/
|
|
154
|
-
function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, allTokens, allDetectedTokens) {
|
|
152
|
+
function buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap) {
|
|
155
153
|
const pairs = [];
|
|
156
154
|
const add = ([account, tokens]) => {
|
|
157
|
-
const
|
|
155
|
+
const checksumAccount = checksum(account);
|
|
156
|
+
const shouldInclude = queryAllAccounts || checksumAccount === checksum(selectedAccount);
|
|
158
157
|
if (!shouldInclude) {
|
|
159
158
|
return;
|
|
160
159
|
}
|
|
161
|
-
tokens.forEach((
|
|
160
|
+
tokens.forEach((token) => pairs.push({
|
|
162
161
|
accountAddress: account,
|
|
163
|
-
tokenAddress: checksum(
|
|
162
|
+
tokenAddress: checksum(token),
|
|
164
163
|
}));
|
|
165
164
|
};
|
|
166
|
-
Object.entries(
|
|
167
|
-
Object.entries(allDetectedTokens[chainId] ?? {}).forEach(add);
|
|
168
|
-
// Always include native token for relevant accounts
|
|
169
|
-
if (queryAllAccounts) {
|
|
170
|
-
allAccounts.forEach((a) => {
|
|
171
|
-
pairs.push({
|
|
172
|
-
accountAddress: a.address,
|
|
173
|
-
tokenAddress: ZERO_ADDRESS,
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
pairs.push({
|
|
179
|
-
accountAddress: selectedAccount,
|
|
180
|
-
tokenAddress: ZERO_ADDRESS,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (!pairs.length) {
|
|
184
|
-
return [];
|
|
185
|
-
}
|
|
165
|
+
Object.entries(accountTokenMap).forEach(add);
|
|
186
166
|
// group by account
|
|
187
167
|
const map = new Map();
|
|
188
168
|
pairs.forEach(({ accountAddress, tokenAddress }) => {
|
|
@@ -199,4 +179,65 @@ function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccoun
|
|
|
199
179
|
tokenAddresses,
|
|
200
180
|
}));
|
|
201
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Merges imported & detected tokens for the requested chain and returns a list
|
|
184
|
+
* of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.
|
|
185
|
+
*
|
|
186
|
+
* @param chainId - The chain ID to build account token groups for
|
|
187
|
+
* @param queryAllAccounts - Whether to query all accounts or just the selected one
|
|
188
|
+
* @param selectedAccount - The currently selected account
|
|
189
|
+
* @param allAccounts - All available accounts
|
|
190
|
+
* @param allTokens - All tokens from TokensController
|
|
191
|
+
* @param allDetectedTokens - All detected tokens from TokensController
|
|
192
|
+
* @returns Array of account/token groups for multicall
|
|
193
|
+
*/
|
|
194
|
+
function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, allTokens, allDetectedTokens) {
|
|
195
|
+
const accountTokenMap = {};
|
|
196
|
+
// Add all tokens
|
|
197
|
+
Object.entries(allTokens[chainId] ?? {}).forEach(([account, tokens]) => {
|
|
198
|
+
accountTokenMap[account] = tokens.map((token) => token.address);
|
|
199
|
+
});
|
|
200
|
+
// Add all detected tokens
|
|
201
|
+
Object.entries(allDetectedTokens[chainId] ?? {}).forEach(([account, tokens]) => {
|
|
202
|
+
if (!accountTokenMap[account]) {
|
|
203
|
+
accountTokenMap[account] = [];
|
|
204
|
+
}
|
|
205
|
+
accountTokenMap[account] = Array.from(new Set([
|
|
206
|
+
...accountTokenMap[account],
|
|
207
|
+
...tokens.map((token) => token.address),
|
|
208
|
+
]));
|
|
209
|
+
});
|
|
210
|
+
// Add native tokens
|
|
211
|
+
if (queryAllAccounts) {
|
|
212
|
+
allAccounts.forEach((a) => {
|
|
213
|
+
var _a;
|
|
214
|
+
accountTokenMap[_a = a.address] ?? (accountTokenMap[_a] = []);
|
|
215
|
+
accountTokenMap[a.address].push(ZERO_ADDRESS);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
accountTokenMap[selectedAccount] ?? (accountTokenMap[selectedAccount] = []);
|
|
220
|
+
accountTokenMap[selectedAccount].push(ZERO_ADDRESS);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
accountTokenGroups: buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap),
|
|
224
|
+
includeNativeAndStaked: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function buildUnprocessedAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, unprocessedTokens) {
|
|
228
|
+
const accountTokenMap = {};
|
|
229
|
+
Object.entries(unprocessedTokens).forEach(([account, tokens]) => {
|
|
230
|
+
const lowercaseAccount = account.toLowerCase();
|
|
231
|
+
if (queryAllAccounts ||
|
|
232
|
+
lowercaseAccount === selectedAccount.toLowerCase()) {
|
|
233
|
+
const tokenAddresses = tokens?.[chainId]?.map((tokenAddress) => tokenAddress.toLowerCase()) ??
|
|
234
|
+
[];
|
|
235
|
+
accountTokenMap[lowercaseAccount] = tokenAddresses;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
accountTokenGroups: buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap),
|
|
240
|
+
includeNativeAndStaked: false,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
202
243
|
//# sourceMappingURL=rpc-balance-fetcher.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-balance-fetcher.cjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Web3Provider } from '@ethersproject/providers';\nimport {\n toChecksumHexAddress,\n safelyExecuteWithTimeout,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { NetworkClient } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport { getTokenBalancesForMultipleAddresses } from '../multicall';\nimport type { TokensControllerState } from '../TokensController';\n\nconst RPC_TIMEOUT_MS = 30000;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetchResult = {\n balances: ProcessedBalance[];\n unprocessedChainIds?: ChainIdHex[];\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<BalanceFetchResult>;\n};\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nexport class RpcBalanceFetcher implements BalanceFetcher {\n readonly #getProvider: (chainId: ChainIdHex) => Web3Provider;\n\n readonly #getNetworkClient: (chainId: ChainIdHex) => NetworkClient;\n\n readonly #getTokensState: () => {\n allTokens: TokensControllerState['allTokens'];\n allDetectedTokens: TokensControllerState['allDetectedTokens'];\n };\n\n constructor(\n getProvider: (chainId: ChainIdHex) => Web3Provider,\n getNetworkClient: (chainId: ChainIdHex) => NetworkClient,\n getTokensState: () => {\n allTokens: TokensControllerState['allTokens'];\n allDetectedTokens: TokensControllerState['allDetectedTokens'];\n },\n ) {\n this.#getProvider = getProvider;\n this.#getNetworkClient = getNetworkClient;\n this.#getTokensState = getTokensState;\n }\n\n supports(): boolean {\n return true; // fallback – supports every chain\n }\n\n #getStakingContractAddress(chainId: ChainIdHex): string | undefined {\n return STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId];\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult> {\n // Process all chains in parallel for better performance\n const chainProcessingPromises = chainIds.map(async (chainId) => {\n const tokensState = this.#getTokensState();\n const accountTokenGroups = buildAccountTokenGroupsStatic(\n chainId,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n tokensState.allTokens,\n tokensState.allDetectedTokens,\n );\n if (!accountTokenGroups.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n await this.#ensureFreshBlockData(chainId);\n\n const balanceResult = await safelyExecuteWithTimeout(\n async () => {\n return await getTokenBalancesForMultipleAddresses(\n accountTokenGroups,\n chainId,\n provider,\n true, // include native\n true, // include staked\n );\n },\n true,\n RPC_TIMEOUT_MS,\n );\n\n // If timeout or error occurred, return empty array for this chain\n if (!balanceResult) {\n return [];\n }\n\n const { tokenBalances, stakedBalances } = balanceResult;\n const chainResults: ProcessedBalance[] = [];\n\n // Add native token entries for all addresses being processed\n const allAddressesForNative = new Set<string>();\n accountTokenGroups.forEach((group) => {\n allAddressesForNative.add(group.accountAddress);\n });\n\n // Ensure native token entries exist for all addresses\n allAddressesForNative.forEach((address) => {\n const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;\n chainResults.push({\n success: true,\n value: nativeBalance || new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n });\n\n // Add other token balances\n Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {\n // Skip native token since we handled it explicitly above\n if (tokenAddr === ZERO_ADDRESS) {\n return;\n }\n Object.entries(balances).forEach(([acct, bn]) => {\n chainResults.push({\n success: bn !== null,\n value: bn,\n account: acct as ChecksumAddress,\n token: checksum(tokenAddr),\n chainId,\n });\n });\n });\n\n // Add staked balances for all addresses being processed\n const stakingContractAddress = this.#getStakingContractAddress(chainId);\n if (stakingContractAddress) {\n // Get all unique addresses being processed for this chain\n const allAddresses = new Set<string>();\n accountTokenGroups.forEach((group) => {\n allAddresses.add(group.accountAddress);\n });\n\n // Add staked balance entry for each address\n const checksummedStakingAddress = checksum(stakingContractAddress);\n allAddresses.forEach((address) => {\n const stakedBalance = stakedBalances?.[address] || null;\n chainResults.push({\n success: true,\n value: stakedBalance || new BN('0'),\n account: address as ChecksumAddress,\n token: checksummedStakingAddress,\n chainId,\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 }\n });\n\n return { balances: results };\n }\n\n /**\n * Ensures that the block tracker has the latest block data before performing multicall operations.\n * This is a temporary fix to ensure that the block number is up to date.\n *\n * @param chainId - The chain id to update block data for.\n */\n async #ensureFreshBlockData(chainId: Hex): Promise<void> {\n // Force fresh block data before multicall\n // TODO: This is a temporary fix to ensure that the block number is up to date.\n // We should remove this once we have a better solution for this on the block tracker controller.\n const networkClient = this.#getNetworkClient(chainId);\n await networkClient.blockTracker?.checkForLatestBlock?.();\n }\n}\n\n/**\n * Merges imported & detected tokens for the requested chain and returns a list\n * of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.\n *\n * @param chainId - The chain ID to build account token groups for\n * @param queryAllAccounts - Whether to query all accounts or just the selected one\n * @param selectedAccount - The currently selected account\n * @param allAccounts - All available accounts\n * @param allTokens - All tokens from TokensController\n * @param allDetectedTokens - All detected tokens from TokensController\n * @returns Array of account/token groups for multicall\n */\nfunction buildAccountTokenGroupsStatic(\n chainId: ChainIdHex,\n queryAllAccounts: boolean,\n selectedAccount: ChecksumAddress,\n allAccounts: InternalAccount[],\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n): { accountAddress: ChecksumAddress; tokenAddresses: ChecksumAddress[] }[] {\n const pairs: {\n accountAddress: ChecksumAddress;\n tokenAddress: ChecksumAddress;\n }[] = [];\n\n const add = ([account, tokens]: [string, unknown[]]) => {\n const shouldInclude =\n queryAllAccounts || checksum(account) === checksum(selectedAccount);\n if (!shouldInclude) {\n return;\n }\n tokens.forEach((t: unknown) =>\n pairs.push({\n accountAddress: account as ChecksumAddress,\n tokenAddress: checksum((t as { address: string }).address),\n }),\n );\n };\n\n Object.entries(allTokens[chainId] ?? {}).forEach(\n add as (entry: [string, unknown]) => void,\n );\n Object.entries(allDetectedTokens[chainId] ?? {}).forEach(\n add as (entry: [string, unknown]) => void,\n );\n\n // Always include native token for relevant accounts\n if (queryAllAccounts) {\n allAccounts.forEach((a) => {\n pairs.push({\n accountAddress: a.address as ChecksumAddress,\n tokenAddress: ZERO_ADDRESS,\n });\n });\n } else {\n pairs.push({\n accountAddress: selectedAccount,\n tokenAddress: ZERO_ADDRESS,\n });\n }\n\n if (!pairs.length) {\n return [];\n }\n\n // group by account\n const map = new Map<ChecksumAddress, ChecksumAddress[]>();\n pairs.forEach(({ accountAddress, tokenAddress }) => {\n if (!map.has(accountAddress)) {\n map.set(accountAddress, []);\n }\n const tokens = map.get(accountAddress);\n if (tokens) {\n tokens.push(tokenAddress);\n }\n });\n\n return Array.from(map.entries()).map(([accountAddress, tokenAddresses]) => ({\n accountAddress,\n tokenAddresses,\n }));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rpc-balance-fetcher.cjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Web3Provider } from '@ethersproject/providers';\nimport {\n toChecksumHexAddress,\n safelyExecuteWithTimeout,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { NetworkClient } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport type { UnprocessedTokens } from '../multi-chain-accounts-service/api-balance-fetcher';\nimport { getTokenBalancesForMultipleAddresses } from '../multicall';\nimport type { TokensControllerState } from '../TokensController';\n\nconst RPC_TIMEOUT_MS = 30000;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetchResult = {\n balances: ProcessedBalance[];\n unprocessedChainIds?: ChainIdHex[];\n unprocessedTokens?: UnprocessedTokens;\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n unprocessedTokens?: UnprocessedTokens;\n }): Promise<BalanceFetchResult>;\n};\n\nconst ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nexport class RpcBalanceFetcher implements BalanceFetcher {\n readonly #getProvider: (chainId: ChainIdHex) => Web3Provider;\n\n readonly #getNetworkClient: (chainId: ChainIdHex) => NetworkClient;\n\n readonly #getTokensState: () => {\n allTokens: TokensControllerState['allTokens'];\n allDetectedTokens: TokensControllerState['allDetectedTokens'];\n };\n\n constructor(\n getProvider: (chainId: ChainIdHex) => Web3Provider,\n getNetworkClient: (chainId: ChainIdHex) => NetworkClient,\n getTokensState: () => {\n allTokens: TokensControllerState['allTokens'];\n allDetectedTokens: TokensControllerState['allDetectedTokens'];\n },\n ) {\n this.#getProvider = getProvider;\n this.#getNetworkClient = getNetworkClient;\n this.#getTokensState = getTokensState;\n }\n\n supports(): boolean {\n return true; // fallback – supports every chain\n }\n\n #getStakingContractAddress(chainId: ChainIdHex): string | undefined {\n return STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId];\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n unprocessedTokens,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult> {\n // Process all chains in parallel for better performance\n const chainProcessingPromises = chainIds.map(async (chainId) => {\n // if there are unprocessed tokens for a chain, it means the chain was partially processed.\n // because of this, we need to build distinct account <-> token groups to process\n const hasUnprocessedTokensForChain = queryAllAccounts\n ? Object.values(unprocessedTokens ?? {}).some((chainMap) =>\n Boolean(chainMap[chainId] && chainMap[chainId].length > 0),\n )\n : Boolean(\n unprocessedTokens?.[selectedAccount.toLowerCase()]?.[chainId] &&\n unprocessedTokens[selectedAccount.toLowerCase()][chainId].length >\n 0,\n );\n\n const tokensState = this.#getTokensState();\n const { accountTokenGroups, includeNativeAndStaked } =\n hasUnprocessedTokensForChain\n ? buildUnprocessedAccountTokenGroupsStatic(\n chainId,\n queryAllAccounts,\n selectedAccount,\n unprocessedTokens as UnprocessedTokens,\n )\n : buildAccountTokenGroupsStatic(\n chainId,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n tokensState.allTokens,\n tokensState.allDetectedTokens,\n );\n\n if (!accountTokenGroups.length) {\n return [];\n }\n\n const provider = this.#getProvider(chainId);\n await this.#ensureFreshBlockData(chainId);\n\n const balanceResult = await safelyExecuteWithTimeout(\n async () => {\n return await getTokenBalancesForMultipleAddresses(\n accountTokenGroups,\n chainId,\n provider,\n includeNativeAndStaked,\n includeNativeAndStaked,\n );\n },\n true,\n RPC_TIMEOUT_MS,\n );\n\n // If timeout or error occurred, return empty array for this chain\n if (!balanceResult) {\n return [];\n }\n\n const { tokenBalances, stakedBalances } = balanceResult;\n const chainResults: ProcessedBalance[] = [];\n\n if (includeNativeAndStaked) {\n // Add native token entries for all addresses being processed\n const allAddressesForNative = new Set<string>();\n accountTokenGroups.forEach((group) => {\n allAddressesForNative.add(group.accountAddress);\n });\n\n // Ensure native token entries exist for all addresses\n allAddressesForNative.forEach((address) => {\n const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;\n chainResults.push({\n success: true,\n value: nativeBalance || new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n });\n }\n\n // Add other token balances\n Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {\n // Skip native token since we handled it explicitly above\n if (tokenAddr === ZERO_ADDRESS) {\n return;\n }\n Object.entries(balances).forEach(([acct, bn]) => {\n chainResults.push({\n success: bn !== null,\n value: bn,\n account: acct as ChecksumAddress,\n token: checksum(tokenAddr),\n chainId,\n });\n });\n });\n\n // Add staked balances for all addresses being processed\n const stakingContractAddress = this.#getStakingContractAddress(chainId);\n if (includeNativeAndStaked && stakingContractAddress) {\n // Get all unique addresses being processed for this chain\n const allAddresses = new Set<string>();\n accountTokenGroups.forEach((group) => {\n allAddresses.add(group.accountAddress);\n });\n\n // Add staked balance entry for each address\n const checksummedStakingAddress = checksum(stakingContractAddress);\n allAddresses.forEach((address) => {\n const stakedBalance = stakedBalances?.[address] ?? null;\n chainResults.push({\n success: true,\n value: stakedBalance ?? new BN('0'),\n account: address as ChecksumAddress,\n token: checksummedStakingAddress,\n chainId,\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 }\n });\n\n return { balances: results };\n }\n\n /**\n * Ensures that the block tracker has the latest block data before performing multicall operations.\n * This is a temporary fix to ensure that the block number is up to date.\n *\n * @param chainId - The chain id to update block data for.\n */\n async #ensureFreshBlockData(chainId: Hex): Promise<void> {\n // Force fresh block data before multicall\n // TODO: This is a temporary fix to ensure that the block number is up to date.\n // We should remove this once we have a better solution for this on the block tracker controller.\n const networkClient = this.#getNetworkClient(chainId);\n await networkClient.blockTracker?.checkForLatestBlock?.();\n }\n}\n\ntype AccountTokenGroup = {\n accountAddress: ChecksumAddress;\n tokenAddresses: ChecksumAddress[];\n};\n\nfunction buildAccountTokenGroups(\n queryAllAccounts: boolean,\n selectedAccount: ChecksumAddress,\n accountTokenMap: { [account: string]: string[] },\n): AccountTokenGroup[] {\n const pairs: {\n accountAddress: ChecksumAddress;\n tokenAddress: ChecksumAddress;\n }[] = [];\n\n const add = ([account, tokens]: [string, string[]]): void => {\n const checksumAccount = checksum(account);\n const shouldInclude =\n queryAllAccounts || checksumAccount === checksum(selectedAccount);\n if (!shouldInclude) {\n return;\n }\n tokens.forEach((token: string) =>\n pairs.push({\n accountAddress: account as ChecksumAddress,\n tokenAddress: checksum(token),\n }),\n );\n };\n\n Object.entries(accountTokenMap).forEach(add);\n\n // group by account\n const map = new Map<ChecksumAddress, ChecksumAddress[]>();\n pairs.forEach(({ accountAddress, tokenAddress }) => {\n if (!map.has(accountAddress)) {\n map.set(accountAddress, []);\n }\n const tokens = map.get(accountAddress);\n if (tokens) {\n tokens.push(tokenAddress);\n }\n });\n\n return Array.from(map.entries()).map(([accountAddress, tokenAddresses]) => ({\n accountAddress,\n tokenAddresses,\n }));\n}\n\n/**\n * Merges imported & detected tokens for the requested chain and returns a list\n * of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.\n *\n * @param chainId - The chain ID to build account token groups for\n * @param queryAllAccounts - Whether to query all accounts or just the selected one\n * @param selectedAccount - The currently selected account\n * @param allAccounts - All available accounts\n * @param allTokens - All tokens from TokensController\n * @param allDetectedTokens - All detected tokens from TokensController\n * @returns Array of account/token groups for multicall\n */\nfunction buildAccountTokenGroupsStatic(\n chainId: ChainIdHex,\n queryAllAccounts: boolean,\n selectedAccount: ChecksumAddress,\n allAccounts: InternalAccount[],\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n): {\n accountTokenGroups: AccountTokenGroup[];\n includeNativeAndStaked: true;\n} {\n const accountTokenMap: { [account: string]: string[] } = {};\n\n // Add all tokens\n Object.entries(allTokens[chainId] ?? {}).forEach(([account, tokens]) => {\n accountTokenMap[account] = tokens.map((token) => token.address);\n });\n\n // Add all detected tokens\n Object.entries(allDetectedTokens[chainId] ?? {}).forEach(\n ([account, tokens]) => {\n if (!accountTokenMap[account]) {\n accountTokenMap[account] = [];\n }\n accountTokenMap[account] = Array.from(\n new Set([\n ...accountTokenMap[account],\n ...tokens.map((token) => token.address),\n ]),\n );\n },\n );\n\n // Add native tokens\n if (queryAllAccounts) {\n allAccounts.forEach((a) => {\n accountTokenMap[a.address] ??= [];\n accountTokenMap[a.address].push(ZERO_ADDRESS);\n });\n } else {\n accountTokenMap[selectedAccount] ??= [];\n accountTokenMap[selectedAccount].push(ZERO_ADDRESS);\n }\n\n return {\n accountTokenGroups: buildAccountTokenGroups(\n queryAllAccounts,\n selectedAccount,\n accountTokenMap,\n ),\n includeNativeAndStaked: true,\n };\n}\n\nfunction buildUnprocessedAccountTokenGroupsStatic(\n chainId: ChainIdHex,\n queryAllAccounts: boolean,\n selectedAccount: ChecksumAddress,\n unprocessedTokens: UnprocessedTokens,\n): {\n accountTokenGroups: AccountTokenGroup[];\n includeNativeAndStaked: false;\n} {\n const accountTokenMap: { [account: string]: string[] } = {};\n Object.entries(unprocessedTokens).forEach(([account, tokens]) => {\n const lowercaseAccount = account.toLowerCase();\n if (\n queryAllAccounts ||\n lowercaseAccount === selectedAccount.toLowerCase()\n ) {\n const tokenAddresses =\n tokens?.[chainId]?.map((tokenAddress) => tokenAddress.toLowerCase()) ??\n [];\n accountTokenMap[lowercaseAccount] = tokenAddresses;\n }\n });\n\n return {\n accountTokenGroups: buildAccountTokenGroups(\n queryAllAccounts,\n selectedAccount,\n accountTokenMap,\n ),\n includeNativeAndStaked: false,\n };\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import type { InternalAccount } from "@metamask/keyring-internal-api";
|
|
|
3
3
|
import type { NetworkClient } from "@metamask/network-controller";
|
|
4
4
|
import type { Hex } from "@metamask/utils";
|
|
5
5
|
import BN from "bn.js";
|
|
6
|
+
import type { UnprocessedTokens } from "../multi-chain-accounts-service/api-balance-fetcher.cjs";
|
|
6
7
|
import type { TokensControllerState } from "../TokensController.cjs";
|
|
7
8
|
export type ChainIdHex = Hex;
|
|
8
9
|
export type ChecksumAddress = Hex;
|
|
@@ -16,6 +17,7 @@ export type ProcessedBalance = {
|
|
|
16
17
|
export type BalanceFetchResult = {
|
|
17
18
|
balances: ProcessedBalance[];
|
|
18
19
|
unprocessedChainIds?: ChainIdHex[];
|
|
20
|
+
unprocessedTokens?: UnprocessedTokens;
|
|
19
21
|
};
|
|
20
22
|
export type BalanceFetcher = {
|
|
21
23
|
supports(chainId: ChainIdHex): boolean;
|
|
@@ -24,6 +26,7 @@ export type BalanceFetcher = {
|
|
|
24
26
|
queryAllAccounts: boolean;
|
|
25
27
|
selectedAccount: ChecksumAddress;
|
|
26
28
|
allAccounts: InternalAccount[];
|
|
29
|
+
unprocessedTokens?: UnprocessedTokens;
|
|
27
30
|
}): Promise<BalanceFetchResult>;
|
|
28
31
|
};
|
|
29
32
|
export declare class RpcBalanceFetcher implements BalanceFetcher {
|
|
@@ -33,6 +36,6 @@ export declare class RpcBalanceFetcher implements BalanceFetcher {
|
|
|
33
36
|
allDetectedTokens: TokensControllerState['allDetectedTokens'];
|
|
34
37
|
});
|
|
35
38
|
supports(): boolean;
|
|
36
|
-
fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult>;
|
|
39
|
+
fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, unprocessedTokens, }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult>;
|
|
37
40
|
}
|
|
38
41
|
//# sourceMappingURL=rpc-balance-fetcher.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-balance-fetcher.d.cts","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAK7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,qCAAqC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAC3C,OAAO,EAAE,cAAc;
|
|
1
|
+
{"version":3,"file":"rpc-balance-fetcher.d.cts","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAK7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,qCAAqC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAC3C,OAAO,EAAE,cAAc;AAGvB,OAAO,KAAK,EAAE,iBAAiB,EAAE,gEAA4D;AAE7F,OAAO,KAAK,EAAE,qBAAqB,EAAE,gCAA4B;AAIjE,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC;IACnC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;QAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;KACvC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACjC,CAAC;AAQF,qBAAa,iBAAkB,YAAW,cAAc;;gBAWpD,WAAW,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,EAClD,gBAAgB,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,aAAa,EACxD,cAAc,EAAE,MAAM;QACpB,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC9C,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;KAC/D;IAOH,QAAQ,IAAI,OAAO;IAQb,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,GAClB,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAuJxE"}
|
|
@@ -3,6 +3,7 @@ import type { InternalAccount } from "@metamask/keyring-internal-api";
|
|
|
3
3
|
import type { NetworkClient } from "@metamask/network-controller";
|
|
4
4
|
import type { Hex } from "@metamask/utils";
|
|
5
5
|
import BN from "bn.js";
|
|
6
|
+
import type { UnprocessedTokens } from "../multi-chain-accounts-service/api-balance-fetcher.mjs";
|
|
6
7
|
import type { TokensControllerState } from "../TokensController.mjs";
|
|
7
8
|
export type ChainIdHex = Hex;
|
|
8
9
|
export type ChecksumAddress = Hex;
|
|
@@ -16,6 +17,7 @@ export type ProcessedBalance = {
|
|
|
16
17
|
export type BalanceFetchResult = {
|
|
17
18
|
balances: ProcessedBalance[];
|
|
18
19
|
unprocessedChainIds?: ChainIdHex[];
|
|
20
|
+
unprocessedTokens?: UnprocessedTokens;
|
|
19
21
|
};
|
|
20
22
|
export type BalanceFetcher = {
|
|
21
23
|
supports(chainId: ChainIdHex): boolean;
|
|
@@ -24,6 +26,7 @@ export type BalanceFetcher = {
|
|
|
24
26
|
queryAllAccounts: boolean;
|
|
25
27
|
selectedAccount: ChecksumAddress;
|
|
26
28
|
allAccounts: InternalAccount[];
|
|
29
|
+
unprocessedTokens?: UnprocessedTokens;
|
|
27
30
|
}): Promise<BalanceFetchResult>;
|
|
28
31
|
};
|
|
29
32
|
export declare class RpcBalanceFetcher implements BalanceFetcher {
|
|
@@ -33,6 +36,6 @@ export declare class RpcBalanceFetcher implements BalanceFetcher {
|
|
|
33
36
|
allDetectedTokens: TokensControllerState['allDetectedTokens'];
|
|
34
37
|
});
|
|
35
38
|
supports(): boolean;
|
|
36
|
-
fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult>;
|
|
39
|
+
fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, unprocessedTokens, }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult>;
|
|
37
40
|
}
|
|
38
41
|
//# sourceMappingURL=rpc-balance-fetcher.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-balance-fetcher.d.mts","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAK7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,qCAAqC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAC3C,OAAO,EAAE,cAAc;
|
|
1
|
+
{"version":3,"file":"rpc-balance-fetcher.d.mts","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAK7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,qCAAqC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAC3C,OAAO,EAAE,cAAc;AAGvB,OAAO,KAAK,EAAE,iBAAiB,EAAE,gEAA4D;AAE7F,OAAO,KAAK,EAAE,qBAAqB,EAAE,gCAA4B;AAIjE,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC;IACnC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;QAC/B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;KACvC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACjC,CAAC;AAQF,qBAAa,iBAAkB,YAAW,cAAc;;gBAWpD,WAAW,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,EAClD,gBAAgB,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,aAAa,EACxD,cAAc,EAAE,MAAM;QACpB,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC9C,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;KAC/D;IAOH,QAAQ,IAAI,OAAO;IAQb,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,GAClB,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAuJxE"}
|
|
@@ -37,19 +37,27 @@ export class RpcBalanceFetcher {
|
|
|
37
37
|
supports() {
|
|
38
38
|
return true; // fallback – supports every chain
|
|
39
39
|
}
|
|
40
|
-
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }) {
|
|
40
|
+
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, unprocessedTokens, }) {
|
|
41
41
|
// Process all chains in parallel for better performance
|
|
42
42
|
const chainProcessingPromises = chainIds.map(async (chainId) => {
|
|
43
|
+
// if there are unprocessed tokens for a chain, it means the chain was partially processed.
|
|
44
|
+
// because of this, we need to build distinct account <-> token groups to process
|
|
45
|
+
const hasUnprocessedTokensForChain = queryAllAccounts
|
|
46
|
+
? Object.values(unprocessedTokens ?? {}).some((chainMap) => Boolean(chainMap[chainId] && chainMap[chainId].length > 0))
|
|
47
|
+
: Boolean(unprocessedTokens?.[selectedAccount.toLowerCase()]?.[chainId] &&
|
|
48
|
+
unprocessedTokens[selectedAccount.toLowerCase()][chainId].length >
|
|
49
|
+
0);
|
|
43
50
|
const tokensState = __classPrivateFieldGet(this, _RpcBalanceFetcher_getTokensState, "f").call(this);
|
|
44
|
-
const accountTokenGroups
|
|
51
|
+
const { accountTokenGroups, includeNativeAndStaked } = hasUnprocessedTokensForChain
|
|
52
|
+
? buildUnprocessedAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, unprocessedTokens)
|
|
53
|
+
: buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, tokensState.allTokens, tokensState.allDetectedTokens);
|
|
45
54
|
if (!accountTokenGroups.length) {
|
|
46
55
|
return [];
|
|
47
56
|
}
|
|
48
57
|
const provider = __classPrivateFieldGet(this, _RpcBalanceFetcher_getProvider, "f").call(this, chainId);
|
|
49
58
|
await __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_ensureFreshBlockData).call(this, chainId);
|
|
50
59
|
const balanceResult = await safelyExecuteWithTimeout(async () => {
|
|
51
|
-
return await getTokenBalancesForMultipleAddresses(accountTokenGroups, chainId, provider,
|
|
52
|
-
true);
|
|
60
|
+
return await getTokenBalancesForMultipleAddresses(accountTokenGroups, chainId, provider, includeNativeAndStaked, includeNativeAndStaked);
|
|
53
61
|
}, true, RPC_TIMEOUT_MS);
|
|
54
62
|
// If timeout or error occurred, return empty array for this chain
|
|
55
63
|
if (!balanceResult) {
|
|
@@ -57,22 +65,24 @@ export class RpcBalanceFetcher {
|
|
|
57
65
|
}
|
|
58
66
|
const { tokenBalances, stakedBalances } = balanceResult;
|
|
59
67
|
const chainResults = [];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Ensure native token entries exist for all addresses
|
|
66
|
-
allAddressesForNative.forEach((address) => {
|
|
67
|
-
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
68
|
-
chainResults.push({
|
|
69
|
-
success: true,
|
|
70
|
-
value: nativeBalance || new BN('0'),
|
|
71
|
-
account: address,
|
|
72
|
-
token: ZERO_ADDRESS,
|
|
73
|
-
chainId,
|
|
68
|
+
if (includeNativeAndStaked) {
|
|
69
|
+
// Add native token entries for all addresses being processed
|
|
70
|
+
const allAddressesForNative = new Set();
|
|
71
|
+
accountTokenGroups.forEach((group) => {
|
|
72
|
+
allAddressesForNative.add(group.accountAddress);
|
|
74
73
|
});
|
|
75
|
-
|
|
74
|
+
// Ensure native token entries exist for all addresses
|
|
75
|
+
allAddressesForNative.forEach((address) => {
|
|
76
|
+
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
77
|
+
chainResults.push({
|
|
78
|
+
success: true,
|
|
79
|
+
value: nativeBalance || new BN('0'),
|
|
80
|
+
account: address,
|
|
81
|
+
token: ZERO_ADDRESS,
|
|
82
|
+
chainId,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
76
86
|
// Add other token balances
|
|
77
87
|
Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {
|
|
78
88
|
// Skip native token since we handled it explicitly above
|
|
@@ -91,7 +101,7 @@ export class RpcBalanceFetcher {
|
|
|
91
101
|
});
|
|
92
102
|
// Add staked balances for all addresses being processed
|
|
93
103
|
const stakingContractAddress = __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_getStakingContractAddress).call(this, chainId);
|
|
94
|
-
if (stakingContractAddress) {
|
|
104
|
+
if (includeNativeAndStaked && stakingContractAddress) {
|
|
95
105
|
// Get all unique addresses being processed for this chain
|
|
96
106
|
const allAddresses = new Set();
|
|
97
107
|
accountTokenGroups.forEach((group) => {
|
|
@@ -100,10 +110,10 @@ export class RpcBalanceFetcher {
|
|
|
100
110
|
// Add staked balance entry for each address
|
|
101
111
|
const checksummedStakingAddress = checksum(stakingContractAddress);
|
|
102
112
|
allAddresses.forEach((address) => {
|
|
103
|
-
const stakedBalance = stakedBalances?.[address]
|
|
113
|
+
const stakedBalance = stakedBalances?.[address] ?? null;
|
|
104
114
|
chainResults.push({
|
|
105
115
|
success: true,
|
|
106
|
-
value: stakedBalance
|
|
116
|
+
value: stakedBalance ?? new BN('0'),
|
|
107
117
|
account: address,
|
|
108
118
|
token: checksummedStakingAddress,
|
|
109
119
|
chainId,
|
|
@@ -139,50 +149,20 @@ async function _RpcBalanceFetcher_ensureFreshBlockData(chainId) {
|
|
|
139
149
|
const networkClient = __classPrivateFieldGet(this, _RpcBalanceFetcher_getNetworkClient, "f").call(this, chainId);
|
|
140
150
|
await networkClient.blockTracker?.checkForLatestBlock?.();
|
|
141
151
|
};
|
|
142
|
-
|
|
143
|
-
* Merges imported & detected tokens for the requested chain and returns a list
|
|
144
|
-
* of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.
|
|
145
|
-
*
|
|
146
|
-
* @param chainId - The chain ID to build account token groups for
|
|
147
|
-
* @param queryAllAccounts - Whether to query all accounts or just the selected one
|
|
148
|
-
* @param selectedAccount - The currently selected account
|
|
149
|
-
* @param allAccounts - All available accounts
|
|
150
|
-
* @param allTokens - All tokens from TokensController
|
|
151
|
-
* @param allDetectedTokens - All detected tokens from TokensController
|
|
152
|
-
* @returns Array of account/token groups for multicall
|
|
153
|
-
*/
|
|
154
|
-
function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, allTokens, allDetectedTokens) {
|
|
152
|
+
function buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap) {
|
|
155
153
|
const pairs = [];
|
|
156
154
|
const add = ([account, tokens]) => {
|
|
157
|
-
const
|
|
155
|
+
const checksumAccount = checksum(account);
|
|
156
|
+
const shouldInclude = queryAllAccounts || checksumAccount === checksum(selectedAccount);
|
|
158
157
|
if (!shouldInclude) {
|
|
159
158
|
return;
|
|
160
159
|
}
|
|
161
|
-
tokens.forEach((
|
|
160
|
+
tokens.forEach((token) => pairs.push({
|
|
162
161
|
accountAddress: account,
|
|
163
|
-
tokenAddress: checksum(
|
|
162
|
+
tokenAddress: checksum(token),
|
|
164
163
|
}));
|
|
165
164
|
};
|
|
166
|
-
Object.entries(
|
|
167
|
-
Object.entries(allDetectedTokens[chainId] ?? {}).forEach(add);
|
|
168
|
-
// Always include native token for relevant accounts
|
|
169
|
-
if (queryAllAccounts) {
|
|
170
|
-
allAccounts.forEach((a) => {
|
|
171
|
-
pairs.push({
|
|
172
|
-
accountAddress: a.address,
|
|
173
|
-
tokenAddress: ZERO_ADDRESS,
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
pairs.push({
|
|
179
|
-
accountAddress: selectedAccount,
|
|
180
|
-
tokenAddress: ZERO_ADDRESS,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (!pairs.length) {
|
|
184
|
-
return [];
|
|
185
|
-
}
|
|
165
|
+
Object.entries(accountTokenMap).forEach(add);
|
|
186
166
|
// group by account
|
|
187
167
|
const map = new Map();
|
|
188
168
|
pairs.forEach(({ accountAddress, tokenAddress }) => {
|
|
@@ -199,4 +179,65 @@ function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccoun
|
|
|
199
179
|
tokenAddresses,
|
|
200
180
|
}));
|
|
201
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Merges imported & detected tokens for the requested chain and returns a list
|
|
184
|
+
* of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.
|
|
185
|
+
*
|
|
186
|
+
* @param chainId - The chain ID to build account token groups for
|
|
187
|
+
* @param queryAllAccounts - Whether to query all accounts or just the selected one
|
|
188
|
+
* @param selectedAccount - The currently selected account
|
|
189
|
+
* @param allAccounts - All available accounts
|
|
190
|
+
* @param allTokens - All tokens from TokensController
|
|
191
|
+
* @param allDetectedTokens - All detected tokens from TokensController
|
|
192
|
+
* @returns Array of account/token groups for multicall
|
|
193
|
+
*/
|
|
194
|
+
function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, allTokens, allDetectedTokens) {
|
|
195
|
+
const accountTokenMap = {};
|
|
196
|
+
// Add all tokens
|
|
197
|
+
Object.entries(allTokens[chainId] ?? {}).forEach(([account, tokens]) => {
|
|
198
|
+
accountTokenMap[account] = tokens.map((token) => token.address);
|
|
199
|
+
});
|
|
200
|
+
// Add all detected tokens
|
|
201
|
+
Object.entries(allDetectedTokens[chainId] ?? {}).forEach(([account, tokens]) => {
|
|
202
|
+
if (!accountTokenMap[account]) {
|
|
203
|
+
accountTokenMap[account] = [];
|
|
204
|
+
}
|
|
205
|
+
accountTokenMap[account] = Array.from(new Set([
|
|
206
|
+
...accountTokenMap[account],
|
|
207
|
+
...tokens.map((token) => token.address),
|
|
208
|
+
]));
|
|
209
|
+
});
|
|
210
|
+
// Add native tokens
|
|
211
|
+
if (queryAllAccounts) {
|
|
212
|
+
allAccounts.forEach((a) => {
|
|
213
|
+
var _a;
|
|
214
|
+
accountTokenMap[_a = a.address] ?? (accountTokenMap[_a] = []);
|
|
215
|
+
accountTokenMap[a.address].push(ZERO_ADDRESS);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
accountTokenMap[selectedAccount] ?? (accountTokenMap[selectedAccount] = []);
|
|
220
|
+
accountTokenMap[selectedAccount].push(ZERO_ADDRESS);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
accountTokenGroups: buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap),
|
|
224
|
+
includeNativeAndStaked: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function buildUnprocessedAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, unprocessedTokens) {
|
|
228
|
+
const accountTokenMap = {};
|
|
229
|
+
Object.entries(unprocessedTokens).forEach(([account, tokens]) => {
|
|
230
|
+
const lowercaseAccount = account.toLowerCase();
|
|
231
|
+
if (queryAllAccounts ||
|
|
232
|
+
lowercaseAccount === selectedAccount.toLowerCase()) {
|
|
233
|
+
const tokenAddresses = tokens?.[chainId]?.map((tokenAddress) => tokenAddress.toLowerCase()) ??
|
|
234
|
+
[];
|
|
235
|
+
accountTokenMap[lowercaseAccount] = tokenAddresses;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
accountTokenGroups: buildAccountTokenGroups(queryAllAccounts, selectedAccount, accountTokenMap),
|
|
240
|
+
includeNativeAndStaked: false,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
202
243
|
//# sourceMappingURL=rpc-balance-fetcher.mjs.map
|