@metamask/assets-controllers 73.1.0 → 73.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 +29 -1
- package/dist/AccountTrackerController.cjs +51 -1
- package/dist/AccountTrackerController.cjs.map +1 -1
- package/dist/AccountTrackerController.d.cts +40 -1
- package/dist/AccountTrackerController.d.cts.map +1 -1
- package/dist/AccountTrackerController.d.mts +40 -1
- package/dist/AccountTrackerController.d.mts.map +1 -1
- package/dist/AccountTrackerController.mjs +51 -1
- package/dist/AccountTrackerController.mjs.map +1 -1
- package/dist/TokenBalancesController.cjs +278 -319
- package/dist/TokenBalancesController.cjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +51 -93
- package/dist/TokenBalancesController.d.cts.map +1 -1
- package/dist/TokenBalancesController.d.mts +51 -93
- package/dist/TokenBalancesController.d.mts.map +1 -1
- package/dist/TokenBalancesController.mjs +277 -317
- package/dist/TokenBalancesController.mjs.map +1 -1
- package/dist/assetsUtil.cjs +13 -1
- package/dist/assetsUtil.cjs.map +1 -1
- package/dist/assetsUtil.d.cts +8 -0
- package/dist/assetsUtil.d.cts.map +1 -1
- package/dist/assetsUtil.d.mts +8 -0
- package/dist/assetsUtil.d.mts.map +1 -1
- package/dist/assetsUtil.mjs +12 -1
- package/dist/assetsUtil.mjs.map +1 -1
- package/dist/balances.cjs +447 -0
- package/dist/balances.cjs.map +1 -0
- package/dist/balances.d.cts +88 -0
- package/dist/balances.d.cts.map +1 -0
- package/dist/balances.d.mts +88 -0
- package/dist/balances.d.mts.map +1 -0
- package/dist/balances.mjs +441 -0
- package/dist/balances.mjs.map +1 -0
- package/dist/constants.cjs +13 -1
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +1 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +12 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +6 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +6 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +247 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts +30 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts +30 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +247 -0
- package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs +35 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts +16 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts +16 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs +33 -0
- package/dist/multi-chain-accounts-service/multi-chain-accounts.mjs.map +1 -1
- package/dist/multi-chain-accounts-service/types.cjs.map +1 -1
- package/dist/multi-chain-accounts-service/types.d.cts +8 -0
- package/dist/multi-chain-accounts-service/types.d.cts.map +1 -1
- package/dist/multi-chain-accounts-service/types.d.mts +8 -0
- package/dist/multi-chain-accounts-service/types.d.mts.map +1 -1
- package/dist/multi-chain-accounts-service/types.mjs.map +1 -1
- package/dist/multicall.cjs +457 -1
- package/dist/multicall.cjs.map +1 -1
- package/dist/multicall.d.cts +51 -0
- package/dist/multicall.d.cts.map +1 -1
- package/dist/multicall.d.mts +51 -0
- package/dist/multicall.d.mts.map +1 -1
- package/dist/multicall.mjs +457 -0
- package/dist/multicall.mjs.map +1 -1
- package/dist/rpc-service/rpc-balance-fetcher.cjs +184 -0
- package/dist/rpc-service/rpc-balance-fetcher.cjs.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.cts +34 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.cts.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.mts +34 -0
- package/dist/rpc-service/rpc-balance-fetcher.d.mts.map +1 -0
- package/dist/rpc-service/rpc-balance-fetcher.mjs +184 -0
- package/dist/rpc-service/rpc-balance-fetcher.mjs.map +1 -0
- package/package.json +9 -9
- package/dist/selectors/balanceSelectors.cjs +0 -328
- package/dist/selectors/balanceSelectors.cjs.map +0 -1
- package/dist/selectors/balanceSelectors.d.cts +0 -1676
- package/dist/selectors/balanceSelectors.d.cts.map +0 -1
- package/dist/selectors/balanceSelectors.d.mts +0 -1676
- package/dist/selectors/balanceSelectors.d.mts.map +0 -1
- package/dist/selectors/balanceSelectors.mjs +0 -321
- package/dist/selectors/balanceSelectors.mjs.map +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
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
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _RpcBalanceFetcher_instances, _RpcBalanceFetcher_getProvider, _RpcBalanceFetcher_getNetworkClient, _RpcBalanceFetcher_getTokensState, _RpcBalanceFetcher_getStakingContractAddress, _RpcBalanceFetcher_ensureFreshBlockData;
|
|
13
|
+
function $importDefault(module) {
|
|
14
|
+
if (module?.__esModule) {
|
|
15
|
+
return module.default;
|
|
16
|
+
}
|
|
17
|
+
return module;
|
|
18
|
+
}
|
|
19
|
+
import { toChecksumHexAddress } from "@metamask/controller-utils";
|
|
20
|
+
import $BN from "bn.js";
|
|
21
|
+
const BN = $importDefault($BN);
|
|
22
|
+
import { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from "../AssetsContractController.mjs";
|
|
23
|
+
import { getTokenBalancesForMultipleAddresses } from "../multicall.mjs";
|
|
24
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
25
|
+
const checksum = (addr) => toChecksumHexAddress(addr);
|
|
26
|
+
export class RpcBalanceFetcher {
|
|
27
|
+
constructor(getProvider, getNetworkClient, getTokensState) {
|
|
28
|
+
_RpcBalanceFetcher_instances.add(this);
|
|
29
|
+
_RpcBalanceFetcher_getProvider.set(this, void 0);
|
|
30
|
+
_RpcBalanceFetcher_getNetworkClient.set(this, void 0);
|
|
31
|
+
_RpcBalanceFetcher_getTokensState.set(this, void 0);
|
|
32
|
+
__classPrivateFieldSet(this, _RpcBalanceFetcher_getProvider, getProvider, "f");
|
|
33
|
+
__classPrivateFieldSet(this, _RpcBalanceFetcher_getNetworkClient, getNetworkClient, "f");
|
|
34
|
+
__classPrivateFieldSet(this, _RpcBalanceFetcher_getTokensState, getTokensState, "f");
|
|
35
|
+
}
|
|
36
|
+
supports() {
|
|
37
|
+
return true; // fallback – supports every chain
|
|
38
|
+
}
|
|
39
|
+
async fetch({ chainIds, queryAllAccounts, selectedAccount, allAccounts, }) {
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const chainId of chainIds) {
|
|
42
|
+
const tokensState = __classPrivateFieldGet(this, _RpcBalanceFetcher_getTokensState, "f").call(this);
|
|
43
|
+
const accountTokenGroups = buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, tokensState.allTokens, tokensState.allDetectedTokens);
|
|
44
|
+
if (!accountTokenGroups.length) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const provider = __classPrivateFieldGet(this, _RpcBalanceFetcher_getProvider, "f").call(this, chainId);
|
|
48
|
+
await __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_ensureFreshBlockData).call(this, chainId);
|
|
49
|
+
const { tokenBalances, stakedBalances } = await getTokenBalancesForMultipleAddresses(accountTokenGroups, chainId, provider, true, // include native
|
|
50
|
+
true);
|
|
51
|
+
// Add native token entries for all addresses being processed
|
|
52
|
+
const allAddressesForNative = new Set();
|
|
53
|
+
accountTokenGroups.forEach((group) => {
|
|
54
|
+
allAddressesForNative.add(group.accountAddress);
|
|
55
|
+
});
|
|
56
|
+
// Ensure native token entries exist for all addresses
|
|
57
|
+
allAddressesForNative.forEach((address) => {
|
|
58
|
+
const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;
|
|
59
|
+
results.push({
|
|
60
|
+
success: true,
|
|
61
|
+
value: nativeBalance ? nativeBalance : new BN('0'),
|
|
62
|
+
account: address,
|
|
63
|
+
token: ZERO_ADDRESS,
|
|
64
|
+
chainId,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// Add other token balances
|
|
68
|
+
Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {
|
|
69
|
+
// Skip native token since we handled it explicitly above
|
|
70
|
+
if (tokenAddr === ZERO_ADDRESS) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
Object.entries(balances).forEach(([acct, bn]) => {
|
|
74
|
+
results.push({
|
|
75
|
+
success: bn !== null,
|
|
76
|
+
value: bn,
|
|
77
|
+
account: acct,
|
|
78
|
+
token: checksum(tokenAddr),
|
|
79
|
+
chainId,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// Add staked balances for all addresses being processed
|
|
84
|
+
const stakingContractAddress = __classPrivateFieldGet(this, _RpcBalanceFetcher_instances, "m", _RpcBalanceFetcher_getStakingContractAddress).call(this, chainId);
|
|
85
|
+
if (stakingContractAddress) {
|
|
86
|
+
// Get all unique addresses being processed for this chain
|
|
87
|
+
const allAddresses = new Set();
|
|
88
|
+
accountTokenGroups.forEach((group) => {
|
|
89
|
+
allAddresses.add(group.accountAddress);
|
|
90
|
+
});
|
|
91
|
+
// Add staked balance entry for each address
|
|
92
|
+
const checksummedStakingAddress = checksum(stakingContractAddress);
|
|
93
|
+
allAddresses.forEach((address) => {
|
|
94
|
+
const stakedBalance = stakedBalances?.[address] || null;
|
|
95
|
+
results.push({
|
|
96
|
+
success: true,
|
|
97
|
+
value: stakedBalance ? stakedBalance : new BN('0'),
|
|
98
|
+
account: address,
|
|
99
|
+
token: checksummedStakingAddress,
|
|
100
|
+
chainId,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
_RpcBalanceFetcher_getProvider = new WeakMap(), _RpcBalanceFetcher_getNetworkClient = new WeakMap(), _RpcBalanceFetcher_getTokensState = new WeakMap(), _RpcBalanceFetcher_instances = new WeakSet(), _RpcBalanceFetcher_getStakingContractAddress = function _RpcBalanceFetcher_getStakingContractAddress(chainId) {
|
|
109
|
+
return STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId];
|
|
110
|
+
}, _RpcBalanceFetcher_ensureFreshBlockData =
|
|
111
|
+
/**
|
|
112
|
+
* Ensures that the block tracker has the latest block data before performing multicall operations.
|
|
113
|
+
* This is a temporary fix to ensure that the block number is up to date.
|
|
114
|
+
*
|
|
115
|
+
* @param chainId - The chain id to update block data for.
|
|
116
|
+
*/
|
|
117
|
+
async function _RpcBalanceFetcher_ensureFreshBlockData(chainId) {
|
|
118
|
+
// Force fresh block data before multicall
|
|
119
|
+
// TODO: This is a temporary fix to ensure that the block number is up to date.
|
|
120
|
+
// We should remove this once we have a better solution for this on the block tracker controller.
|
|
121
|
+
const networkClient = __classPrivateFieldGet(this, _RpcBalanceFetcher_getNetworkClient, "f").call(this, chainId);
|
|
122
|
+
await networkClient.blockTracker?.checkForLatestBlock?.();
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Merges imported & detected tokens for the requested chain and returns a list
|
|
126
|
+
* of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.
|
|
127
|
+
*
|
|
128
|
+
* @param chainId - The chain ID to build account token groups for
|
|
129
|
+
* @param queryAllAccounts - Whether to query all accounts or just the selected one
|
|
130
|
+
* @param selectedAccount - The currently selected account
|
|
131
|
+
* @param allAccounts - All available accounts
|
|
132
|
+
* @param allTokens - All tokens from TokensController
|
|
133
|
+
* @param allDetectedTokens - All detected tokens from TokensController
|
|
134
|
+
* @returns Array of account/token groups for multicall
|
|
135
|
+
*/
|
|
136
|
+
function buildAccountTokenGroupsStatic(chainId, queryAllAccounts, selectedAccount, allAccounts, allTokens, allDetectedTokens) {
|
|
137
|
+
const pairs = [];
|
|
138
|
+
const add = ([account, tokens]) => {
|
|
139
|
+
const shouldInclude = queryAllAccounts || checksum(account) === checksum(selectedAccount);
|
|
140
|
+
if (!shouldInclude) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
tokens.forEach((t) => pairs.push({
|
|
144
|
+
accountAddress: account,
|
|
145
|
+
tokenAddress: checksum(t.address),
|
|
146
|
+
}));
|
|
147
|
+
};
|
|
148
|
+
Object.entries(allTokens[chainId] ?? {}).forEach(add);
|
|
149
|
+
Object.entries(allDetectedTokens[chainId] ?? {}).forEach(add);
|
|
150
|
+
// Always include native token for relevant accounts
|
|
151
|
+
if (queryAllAccounts) {
|
|
152
|
+
allAccounts.forEach((a) => {
|
|
153
|
+
pairs.push({
|
|
154
|
+
accountAddress: a.address,
|
|
155
|
+
tokenAddress: ZERO_ADDRESS,
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
pairs.push({
|
|
161
|
+
accountAddress: selectedAccount,
|
|
162
|
+
tokenAddress: ZERO_ADDRESS,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (!pairs.length) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
// group by account
|
|
169
|
+
const map = new Map();
|
|
170
|
+
pairs.forEach(({ accountAddress, tokenAddress }) => {
|
|
171
|
+
if (!map.has(accountAddress)) {
|
|
172
|
+
map.set(accountAddress, []);
|
|
173
|
+
}
|
|
174
|
+
const tokens = map.get(accountAddress);
|
|
175
|
+
if (tokens) {
|
|
176
|
+
tokens.push(tokenAddress);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return Array.from(map.entries()).map(([accountAddress, tokenAddresses]) => ({
|
|
180
|
+
accountAddress,
|
|
181
|
+
tokenAddresses,
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=rpc-balance-fetcher.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-balance-fetcher.mjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Web3Provider } from '@ethersproject/providers';\nimport { toChecksumHexAddress } 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\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 BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<ProcessedBalance[]>;\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[\n chainId as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n const results: ProcessedBalance[] = [];\n\n for (const chainId of chainIds) {\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 continue;\n }\n\n const provider = this.#getProvider(chainId);\n await this.#ensureFreshBlockData(chainId);\n\n const { tokenBalances, stakedBalances } =\n await getTokenBalancesForMultipleAddresses(\n accountTokenGroups,\n chainId,\n provider,\n true, // include native\n true, // include staked\n );\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 results.push({\n success: true,\n value: nativeBalance ? (nativeBalance as BN) : 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 results.push({\n success: bn !== null,\n value: bn as 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 results.push({\n success: true,\n value: stakedBalance ? (stakedBalance as BN) : new BN('0'),\n account: address as ChecksumAddress,\n token: checksummedStakingAddress,\n chainId,\n });\n });\n }\n }\n\n return 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 as unknown[]).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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/assets-controllers",
|
|
3
|
-
"version": "73.
|
|
3
|
+
"version": "73.2.0",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"@ethersproject/contracts": "^5.7.0",
|
|
55
55
|
"@ethersproject/providers": "^5.7.0",
|
|
56
56
|
"@metamask/abi-utils": "^2.0.3",
|
|
57
|
-
"@metamask/base-controller": "^8.0
|
|
57
|
+
"@metamask/base-controller": "^8.1.0",
|
|
58
58
|
"@metamask/contract-metadata": "^2.4.0",
|
|
59
|
-
"@metamask/controller-utils": "^11.
|
|
59
|
+
"@metamask/controller-utils": "^11.12.0",
|
|
60
60
|
"@metamask/eth-query": "^4.0.0",
|
|
61
61
|
"@metamask/keyring-api": "^20.0.0",
|
|
62
62
|
"@metamask/metamask-eth-abis": "^3.1.1",
|
|
@@ -80,22 +80,22 @@
|
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@babel/runtime": "^7.23.9",
|
|
82
82
|
"@metamask/account-api": "^0.9.0",
|
|
83
|
-
"@metamask/account-tree-controller": "^0.
|
|
84
|
-
"@metamask/accounts-controller": "^32.0.
|
|
83
|
+
"@metamask/account-tree-controller": "^0.8.0",
|
|
84
|
+
"@metamask/accounts-controller": "^32.0.2",
|
|
85
85
|
"@metamask/approval-controller": "^7.1.3",
|
|
86
86
|
"@metamask/auto-changelog": "^3.4.4",
|
|
87
87
|
"@metamask/ethjs-provider-http": "^0.3.0",
|
|
88
|
-
"@metamask/keyring-controller": "^22.1.
|
|
88
|
+
"@metamask/keyring-controller": "^22.1.1",
|
|
89
89
|
"@metamask/keyring-internal-api": "^8.0.0",
|
|
90
90
|
"@metamask/keyring-snap-client": "^7.0.0",
|
|
91
|
-
"@metamask/multichain-account-service": "^0.
|
|
92
|
-
"@metamask/network-controller": "^24.0
|
|
91
|
+
"@metamask/multichain-account-service": "^0.4.0",
|
|
92
|
+
"@metamask/network-controller": "^24.1.0",
|
|
93
93
|
"@metamask/permission-controller": "^11.0.6",
|
|
94
94
|
"@metamask/phishing-controller": "^13.1.0",
|
|
95
95
|
"@metamask/preferences-controller": "^18.4.1",
|
|
96
96
|
"@metamask/providers": "^22.1.0",
|
|
97
97
|
"@metamask/snaps-controllers": "^14.0.1",
|
|
98
|
-
"@metamask/transaction-controller": "^59.
|
|
98
|
+
"@metamask/transaction-controller": "^59.2.0",
|
|
99
99
|
"@types/jest": "^27.4.1",
|
|
100
100
|
"@types/lodash": "^4.14.191",
|
|
101
101
|
"@types/node": "^16.18.54",
|
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.balanceSelectors = exports.selectBalanceForSelectedAccountGroup = exports.selectBalanceByWallet = exports.selectBalanceByAccountGroup = exports.selectBalanceForAllWallets = void 0;
|
|
4
|
-
const keyring_api_1 = require("@metamask/keyring-api");
|
|
5
|
-
const reselect_1 = require("reselect");
|
|
6
|
-
/**
|
|
7
|
-
* Individual controller state selectors using direct state access
|
|
8
|
-
* This avoids new object creation and provides stable references
|
|
9
|
-
* Supports both mobile (state.engine.backgroundState) and extension (state.metamask) structures
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Helper function to get controller state from different state structures
|
|
13
|
-
*
|
|
14
|
-
* @param state - The application state
|
|
15
|
-
* @param controllerName - The name of the controller
|
|
16
|
-
* @returns The controller state or undefined if not found
|
|
17
|
-
*/
|
|
18
|
-
const getControllerState = (
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
-
state, controllerName) => {
|
|
21
|
-
// Mobile structure: state.engine.backgroundState.ControllerName
|
|
22
|
-
if (state?.engine?.backgroundState?.[controllerName]) {
|
|
23
|
-
return state.engine.backgroundState[controllerName];
|
|
24
|
-
}
|
|
25
|
-
// Extension structure: state.metamask.ControllerName
|
|
26
|
-
if (state?.metamask?.[controllerName]) {
|
|
27
|
-
return state.metamask[controllerName];
|
|
28
|
-
}
|
|
29
|
-
// Flat structure (default assets-controllers structure)
|
|
30
|
-
if (state?.[controllerName]) {
|
|
31
|
-
return state[controllerName];
|
|
32
|
-
}
|
|
33
|
-
// Since controllers always have default states, this should never happen
|
|
34
|
-
// but we need to return something for TypeScript
|
|
35
|
-
return state?.[controllerName];
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Selector for AccountTreeController state using direct state access
|
|
39
|
-
*
|
|
40
|
-
* @param state - The application state
|
|
41
|
-
* @returns AccountTreeController state
|
|
42
|
-
*/
|
|
43
|
-
const selectAccountTreeControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'AccountTreeController'));
|
|
44
|
-
/**
|
|
45
|
-
* Selector for AccountsController state using direct state access
|
|
46
|
-
*
|
|
47
|
-
* @param state - The application state
|
|
48
|
-
* @returns AccountsController state
|
|
49
|
-
*/
|
|
50
|
-
const selectAccountsControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'AccountsController'));
|
|
51
|
-
/**
|
|
52
|
-
* Selector for TokenBalancesController state using direct state access
|
|
53
|
-
*
|
|
54
|
-
* @param state - The application state
|
|
55
|
-
* @returns TokenBalancesController state
|
|
56
|
-
*/
|
|
57
|
-
const selectTokenBalancesControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'TokenBalancesController'));
|
|
58
|
-
/**
|
|
59
|
-
* Selector for TokenRatesController state using direct state access
|
|
60
|
-
*
|
|
61
|
-
* @param state - The application state
|
|
62
|
-
* @returns TokenRatesController state
|
|
63
|
-
*/
|
|
64
|
-
const selectTokenRatesControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'TokenRatesController'));
|
|
65
|
-
/**
|
|
66
|
-
* Selector for MultichainAssetsRatesController state using direct state access
|
|
67
|
-
*
|
|
68
|
-
* @param state - The application state
|
|
69
|
-
* @returns MultichainAssetsRatesController state
|
|
70
|
-
*/
|
|
71
|
-
const selectMultichainAssetsRatesControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'MultichainAssetsRatesController'));
|
|
72
|
-
/**
|
|
73
|
-
* Selector for MultichainBalancesController state using direct state access
|
|
74
|
-
*
|
|
75
|
-
* @param state - The application state
|
|
76
|
-
* @returns MultichainBalancesController state
|
|
77
|
-
*/
|
|
78
|
-
const selectMultichainBalancesControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'MultichainBalancesController'));
|
|
79
|
-
/**
|
|
80
|
-
* Selector for TokensController state using direct state access
|
|
81
|
-
*
|
|
82
|
-
* @param state - The application state
|
|
83
|
-
* @returns TokensController state
|
|
84
|
-
*/
|
|
85
|
-
const selectTokensControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'TokensController'));
|
|
86
|
-
/**
|
|
87
|
-
* Selector for CurrencyRateController state using direct state access
|
|
88
|
-
*
|
|
89
|
-
* @param state - The application state
|
|
90
|
-
* @returns CurrencyRateController state
|
|
91
|
-
*/
|
|
92
|
-
const selectCurrencyRateControllerState = (0, reselect_1.createSelector)([(state) => state], (state) => getControllerState(state, 'CurrencyRateController'));
|
|
93
|
-
/**
|
|
94
|
-
* Helper function to get internal accounts for a specific group.
|
|
95
|
-
* Uses AccountTreeController state to find accounts.
|
|
96
|
-
*
|
|
97
|
-
* @param accountTreeState - AccountTreeController state
|
|
98
|
-
* @param accountsState - AccountsController state
|
|
99
|
-
* @param groupId - The account group ID (format: "walletId/groupIndex")
|
|
100
|
-
* @returns Array of internal accounts in the group
|
|
101
|
-
*/
|
|
102
|
-
const getInternalAccountsForGroup = (accountTreeState, accountsState, groupId) => {
|
|
103
|
-
// Extract walletId from groupId (format: "walletId/groupIndex")
|
|
104
|
-
const walletId = groupId.split('/')[0];
|
|
105
|
-
const wallet = accountTreeState.accountTree.wallets[walletId];
|
|
106
|
-
if (!wallet) {
|
|
107
|
-
return [];
|
|
108
|
-
}
|
|
109
|
-
const group = wallet.groups[groupId];
|
|
110
|
-
if (!group) {
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
113
|
-
// Map account IDs to actual account objects
|
|
114
|
-
return group.accounts
|
|
115
|
-
.map((accountId) => accountsState.internalAccounts.accounts[accountId])
|
|
116
|
-
.filter(Boolean);
|
|
117
|
-
};
|
|
118
|
-
/**
|
|
119
|
-
* Comprehensive selector that calculates all balances for all wallets and groups.
|
|
120
|
-
* This is the single source of truth for all balance calculations.
|
|
121
|
-
* Other selectors will derive from this to ensure proper memoization.
|
|
122
|
-
*
|
|
123
|
-
* @returns Aggregated balance for all wallets
|
|
124
|
-
*/
|
|
125
|
-
const selectBalanceForAllWallets = () => (0, reselect_1.createSelector)([
|
|
126
|
-
selectAccountTreeControllerState,
|
|
127
|
-
selectAccountsControllerState,
|
|
128
|
-
selectTokenBalancesControllerState,
|
|
129
|
-
selectTokenRatesControllerState,
|
|
130
|
-
selectMultichainAssetsRatesControllerState,
|
|
131
|
-
selectMultichainBalancesControllerState,
|
|
132
|
-
selectTokensControllerState,
|
|
133
|
-
selectCurrencyRateControllerState,
|
|
134
|
-
], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
|
|
135
|
-
const walletBalances = {};
|
|
136
|
-
let totalBalanceInUserCurrency = 0;
|
|
137
|
-
const walletIds = Object.keys(accountTreeState.accountTree.wallets);
|
|
138
|
-
for (const walletId of walletIds) {
|
|
139
|
-
const wallet = accountTreeState.accountTree.wallets[walletId];
|
|
140
|
-
if (!wallet) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const groupBalances = {};
|
|
144
|
-
let walletTotalBalance = 0;
|
|
145
|
-
const groups = Object.keys(wallet.groups || {});
|
|
146
|
-
for (const groupId of groups) {
|
|
147
|
-
const accounts = getInternalAccountsForGroup(accountTreeState, accountsState, groupId);
|
|
148
|
-
if (accounts.length === 0) {
|
|
149
|
-
groupBalances[groupId] = {
|
|
150
|
-
walletId,
|
|
151
|
-
groupId,
|
|
152
|
-
totalBalanceInUserCurrency: 0,
|
|
153
|
-
userCurrency: currencyRateState.currentCurrency,
|
|
154
|
-
};
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
let groupTotalBalance = 0;
|
|
158
|
-
// Process each account's balances
|
|
159
|
-
for (const account of accounts) {
|
|
160
|
-
const isEvmAccount = (0, keyring_api_1.isEvmAccountType)(account.type);
|
|
161
|
-
if (isEvmAccount) {
|
|
162
|
-
// Handle EVM account balances from TokenBalancesController
|
|
163
|
-
const accountBalances = tokenBalancesState.tokenBalances[account.address];
|
|
164
|
-
if (accountBalances) {
|
|
165
|
-
for (const [chainId, chainBalances] of Object.entries(accountBalances)) {
|
|
166
|
-
for (const [tokenAddress, balance] of Object.entries(chainBalances)) {
|
|
167
|
-
// Find token in TokensController state
|
|
168
|
-
const chainTokens = tokensState.allTokens[chainId];
|
|
169
|
-
const accountTokens = chainTokens?.[account.address];
|
|
170
|
-
const token = accountTokens?.find((t) => t.address === tokenAddress);
|
|
171
|
-
if (!token) {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
// Use nullish coalescing to handle 0 decimals correctly
|
|
175
|
-
// and ensure decimals is a valid number to prevent NaN propagation
|
|
176
|
-
const decimals = typeof token.decimals === 'number' &&
|
|
177
|
-
!Number.isNaN(token.decimals)
|
|
178
|
-
? token.decimals
|
|
179
|
-
: 18;
|
|
180
|
-
const balanceInSmallestUnit = parseInt(balance, 16);
|
|
181
|
-
// Skip invalid balance values to prevent NaN propagation
|
|
182
|
-
if (Number.isNaN(balanceInSmallestUnit)) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
const balanceInTokenUnits = balanceInSmallestUnit / Math.pow(10, decimals);
|
|
186
|
-
// Get token rate in native currency from TokenRatesController
|
|
187
|
-
const chainMarketData = tokenRatesState.marketData[chainId];
|
|
188
|
-
const tokenMarketData = chainMarketData?.[tokenAddress];
|
|
189
|
-
if (tokenMarketData?.price) {
|
|
190
|
-
// Convert token price to user currency using native currency conversion rate
|
|
191
|
-
const nativeCurrency = tokenMarketData.currency;
|
|
192
|
-
const nativeToUserRate = currencyRateState.currencyRates[nativeCurrency]
|
|
193
|
-
?.conversionRate;
|
|
194
|
-
if (nativeToUserRate) {
|
|
195
|
-
// Convert token price to user currency: tokenPrice * nativeToUserRate
|
|
196
|
-
const tokenPriceInUserCurrency = tokenMarketData.price * nativeToUserRate;
|
|
197
|
-
const balanceInUserCurrency = balanceInTokenUnits * tokenPriceInUserCurrency;
|
|
198
|
-
groupTotalBalance += balanceInUserCurrency;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
// Handle non-EVM account balances from MultichainBalancesController
|
|
207
|
-
const accountBalances = multichainBalancesState.balances[account.id];
|
|
208
|
-
if (accountBalances) {
|
|
209
|
-
for (const [assetId, balanceData] of Object.entries(accountBalances)) {
|
|
210
|
-
const balanceAmount = parseFloat(balanceData.amount);
|
|
211
|
-
// Skip invalid balance values to prevent NaN propagation
|
|
212
|
-
if (Number.isNaN(balanceAmount)) {
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
// Get conversion rate for this asset (already in user currency)
|
|
216
|
-
const conversionRate = multichainRatesState.conversionRates[assetId];
|
|
217
|
-
if (conversionRate) {
|
|
218
|
-
const conversionRateValue = parseFloat(conversionRate.rate);
|
|
219
|
-
// Skip invalid conversion rate values to prevent NaN propagation
|
|
220
|
-
if (Number.isNaN(conversionRateValue)) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
// MultichainAssetsRatesController already provides rates in user currency
|
|
224
|
-
const balanceInUserCurrency = balanceAmount * conversionRateValue;
|
|
225
|
-
groupTotalBalance += balanceInUserCurrency;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
groupBalances[groupId] = {
|
|
232
|
-
walletId,
|
|
233
|
-
groupId,
|
|
234
|
-
totalBalanceInUserCurrency: groupTotalBalance,
|
|
235
|
-
userCurrency: currencyRateState.currentCurrency,
|
|
236
|
-
};
|
|
237
|
-
walletTotalBalance += groupTotalBalance;
|
|
238
|
-
}
|
|
239
|
-
walletBalances[walletId] = {
|
|
240
|
-
walletId,
|
|
241
|
-
groups: groupBalances,
|
|
242
|
-
totalBalanceInUserCurrency: walletTotalBalance,
|
|
243
|
-
userCurrency: currencyRateState.currentCurrency,
|
|
244
|
-
};
|
|
245
|
-
totalBalanceInUserCurrency += walletTotalBalance;
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
wallets: walletBalances,
|
|
249
|
-
totalBalanceInUserCurrency,
|
|
250
|
-
userCurrency: currencyRateState.currentCurrency,
|
|
251
|
-
};
|
|
252
|
-
});
|
|
253
|
-
exports.selectBalanceForAllWallets = selectBalanceForAllWallets;
|
|
254
|
-
/**
|
|
255
|
-
* Selector to get aggregated balances for a specific account group.
|
|
256
|
-
* Derives from the comprehensive selector to ensure proper memoization.
|
|
257
|
-
*
|
|
258
|
-
* @param groupId - The account group ID (format: "walletId/groupIndex", e.g., "entropy:entropy-source-1/0")
|
|
259
|
-
* @returns Aggregated balance for the account group
|
|
260
|
-
*/
|
|
261
|
-
const selectBalanceByAccountGroup = (groupId) => (0, reselect_1.createSelector)([(0, exports.selectBalanceForAllWallets)()], (allBalances) => {
|
|
262
|
-
const walletId = groupId.split('/')[0];
|
|
263
|
-
const wallet = allBalances.wallets[walletId];
|
|
264
|
-
if (!wallet || !wallet.groups[groupId]) {
|
|
265
|
-
return {
|
|
266
|
-
walletId,
|
|
267
|
-
groupId,
|
|
268
|
-
totalBalanceInUserCurrency: 0,
|
|
269
|
-
userCurrency: allBalances.userCurrency,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
return wallet.groups[groupId];
|
|
273
|
-
});
|
|
274
|
-
exports.selectBalanceByAccountGroup = selectBalanceByAccountGroup;
|
|
275
|
-
/**
|
|
276
|
-
* Selector to get aggregated balances for all account groups in a wallet.
|
|
277
|
-
* Derives from the comprehensive selector to ensure proper memoization.
|
|
278
|
-
*
|
|
279
|
-
* @param walletId - The wallet ID (entropy source)
|
|
280
|
-
* @returns Aggregated balance for all groups in the wallet
|
|
281
|
-
*/
|
|
282
|
-
const selectBalanceByWallet = (walletId) => (0, reselect_1.createSelector)([(0, exports.selectBalanceForAllWallets)()], (allBalances) => {
|
|
283
|
-
const wallet = allBalances.wallets[walletId];
|
|
284
|
-
if (!wallet) {
|
|
285
|
-
return {
|
|
286
|
-
walletId,
|
|
287
|
-
groups: {},
|
|
288
|
-
totalBalanceInUserCurrency: 0,
|
|
289
|
-
userCurrency: allBalances.userCurrency,
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
return wallet;
|
|
293
|
-
});
|
|
294
|
-
exports.selectBalanceByWallet = selectBalanceByWallet;
|
|
295
|
-
/**
|
|
296
|
-
* Selector to get aggregated balances for the currently selected account group.
|
|
297
|
-
* Derives from the comprehensive selector to ensure proper memoization.
|
|
298
|
-
*
|
|
299
|
-
* @returns Aggregated balance for the currently selected group
|
|
300
|
-
*/
|
|
301
|
-
const selectBalanceForSelectedAccountGroup = () => (0, reselect_1.createSelector)([selectAccountTreeControllerState, (0, exports.selectBalanceForAllWallets)()], (accountTreeState, allBalances) => {
|
|
302
|
-
const selectedGroupId = accountTreeState.accountTree.selectedAccountGroup;
|
|
303
|
-
if (!selectedGroupId) {
|
|
304
|
-
return null;
|
|
305
|
-
}
|
|
306
|
-
const walletId = selectedGroupId.split('/')[0];
|
|
307
|
-
const wallet = allBalances.wallets[walletId];
|
|
308
|
-
if (!wallet || !wallet.groups[selectedGroupId]) {
|
|
309
|
-
return {
|
|
310
|
-
walletId,
|
|
311
|
-
groupId: selectedGroupId,
|
|
312
|
-
totalBalanceInUserCurrency: 0,
|
|
313
|
-
userCurrency: allBalances.userCurrency,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
return wallet.groups[selectedGroupId];
|
|
317
|
-
});
|
|
318
|
-
exports.selectBalanceForSelectedAccountGroup = selectBalanceForSelectedAccountGroup;
|
|
319
|
-
/**
|
|
320
|
-
* Collection of balance-related selectors for assets controllers
|
|
321
|
-
*/
|
|
322
|
-
exports.balanceSelectors = {
|
|
323
|
-
selectBalanceByAccountGroup: exports.selectBalanceByAccountGroup,
|
|
324
|
-
selectBalanceByWallet: exports.selectBalanceByWallet,
|
|
325
|
-
selectBalanceForAllWallets: exports.selectBalanceForAllWallets,
|
|
326
|
-
selectBalanceForSelectedAccountGroup: exports.selectBalanceForSelectedAccountGroup,
|
|
327
|
-
};
|
|
328
|
-
//# sourceMappingURL=balanceSelectors.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"balanceSelectors.cjs","sourceRoot":"","sources":["../../src/selectors/balanceSelectors.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountTreeControllerState } from '@metamask/account-tree-controller';\nimport type { AccountWalletObject } from '@metamask/account-tree-controller';\nimport type { AccountGroupObject } from '@metamask/account-tree-controller';\nimport type { AccountsControllerState } from '@metamask/accounts-controller';\nimport type { EntropySourceId } from '@metamask/keyring-api';\nimport { isEvmAccountType } from '@metamask/keyring-api';\nimport type { Hex } from '@metamask/utils';\nimport type { CaipAssetType } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport type { CurrencyRateState } from '../CurrencyRateController';\nimport type { MultichainAssetsRatesControllerState } from '../MultichainAssetsRatesController';\nimport type { MultichainBalancesControllerState } from '../MultichainBalancesController';\nimport type { TokenBalancesControllerState } from '../TokenBalancesController';\nimport type { TokenRatesControllerState } from '../TokenRatesController';\nimport type { TokensControllerState } from '../TokensController';\n\n/**\n * Individual controller state selectors using direct state access\n * This avoids new object creation and provides stable references\n * Supports both mobile (state.engine.backgroundState) and extension (state.metamask) structures\n */\n\n/**\n * Helper function to get controller state from different state structures\n *\n * @param state - The application state\n * @param controllerName - The name of the controller\n * @returns The controller state or undefined if not found\n */\n\nconst getControllerState = <T>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n state: any,\n controllerName: string,\n): T => {\n // Mobile structure: state.engine.backgroundState.ControllerName\n if (state?.engine?.backgroundState?.[controllerName]) {\n return state.engine.backgroundState[controllerName];\n }\n\n // Extension structure: state.metamask.ControllerName\n if (state?.metamask?.[controllerName]) {\n return state.metamask[controllerName];\n }\n\n // Flat structure (default assets-controllers structure)\n if (state?.[controllerName]) {\n return state[controllerName];\n }\n\n // Since controllers always have default states, this should never happen\n // but we need to return something for TypeScript\n return state?.[controllerName] as T;\n};\n\n/**\n * Selector for AccountTreeController state using direct state access\n *\n * @param state - The application state\n * @returns AccountTreeController state\n */\nconst selectAccountTreeControllerState = createSelector(\n [(state: unknown) => state],\n (state): AccountTreeControllerState =>\n getControllerState<AccountTreeControllerState>(\n state,\n 'AccountTreeController',\n ),\n);\n\n/**\n * Selector for AccountsController state using direct state access\n *\n * @param state - The application state\n * @returns AccountsController state\n */\nconst selectAccountsControllerState = createSelector(\n [(state: unknown) => state],\n (state): AccountsControllerState =>\n getControllerState<AccountsControllerState>(state, 'AccountsController'),\n);\n\n/**\n * Selector for TokenBalancesController state using direct state access\n *\n * @param state - The application state\n * @returns TokenBalancesController state\n */\nconst selectTokenBalancesControllerState = createSelector(\n [(state: unknown) => state],\n (state): TokenBalancesControllerState =>\n getControllerState<TokenBalancesControllerState>(\n state,\n 'TokenBalancesController',\n ),\n);\n\n/**\n * Selector for TokenRatesController state using direct state access\n *\n * @param state - The application state\n * @returns TokenRatesController state\n */\nconst selectTokenRatesControllerState = createSelector(\n [(state: unknown) => state],\n (state): TokenRatesControllerState =>\n getControllerState<TokenRatesControllerState>(\n state,\n 'TokenRatesController',\n ),\n);\n\n/**\n * Selector for MultichainAssetsRatesController state using direct state access\n *\n * @param state - The application state\n * @returns MultichainAssetsRatesController state\n */\nconst selectMultichainAssetsRatesControllerState = createSelector(\n [(state: unknown) => state],\n (state): MultichainAssetsRatesControllerState =>\n getControllerState<MultichainAssetsRatesControllerState>(\n state,\n 'MultichainAssetsRatesController',\n ),\n);\n\n/**\n * Selector for MultichainBalancesController state using direct state access\n *\n * @param state - The application state\n * @returns MultichainBalancesController state\n */\nconst selectMultichainBalancesControllerState = createSelector(\n [(state: unknown) => state],\n (state): MultichainBalancesControllerState =>\n getControllerState<MultichainBalancesControllerState>(\n state,\n 'MultichainBalancesController',\n ),\n);\n\n/**\n * Selector for TokensController state using direct state access\n *\n * @param state - The application state\n * @returns TokensController state\n */\nconst selectTokensControllerState = createSelector(\n [(state: unknown) => state],\n (state): TokensControllerState =>\n getControllerState<TokensControllerState>(state, 'TokensController'),\n);\n\n/**\n * Selector for CurrencyRateController state using direct state access\n *\n * @param state - The application state\n * @returns CurrencyRateController state\n */\nconst selectCurrencyRateControllerState = createSelector(\n [(state: unknown) => state],\n (state): CurrencyRateState =>\n getControllerState<CurrencyRateState>(state, 'CurrencyRateController'),\n);\n\n/**\n * Helper function to get internal accounts for a specific group.\n * Uses AccountTreeController state to find accounts.\n *\n * @param accountTreeState - AccountTreeController state\n * @param accountsState - AccountsController state\n * @param groupId - The account group ID (format: \"walletId/groupIndex\")\n * @returns Array of internal accounts in the group\n */\nconst getInternalAccountsForGroup = (\n accountTreeState: AccountTreeControllerState,\n accountsState: AccountsControllerState,\n groupId: string,\n) => {\n // Extract walletId from groupId (format: \"walletId/groupIndex\")\n const walletId = groupId.split('/')[0] as EntropySourceId;\n\n const wallet = (\n accountTreeState.accountTree.wallets as Record<string, AccountWalletObject>\n )[walletId];\n if (!wallet) {\n return [];\n }\n\n const group = (wallet.groups as Record<string, AccountGroupObject>)[groupId];\n if (!group) {\n return [];\n }\n\n // Map account IDs to actual account objects\n return group.accounts\n .map(\n (accountId: string) => accountsState.internalAccounts.accounts[accountId],\n )\n .filter(Boolean);\n};\n\n/**\n * Comprehensive selector that calculates all balances for all wallets and groups.\n * This is the single source of truth for all balance calculations.\n * Other selectors will derive from this to ensure proper memoization.\n *\n * @returns Aggregated balance for all wallets\n */\nexport const selectBalanceForAllWallets = () =>\n createSelector(\n [\n selectAccountTreeControllerState,\n selectAccountsControllerState,\n selectTokenBalancesControllerState,\n selectTokenRatesControllerState,\n selectMultichainAssetsRatesControllerState,\n selectMultichainBalancesControllerState,\n selectTokensControllerState,\n selectCurrencyRateControllerState,\n ],\n (\n accountTreeState,\n accountsState,\n tokenBalancesState,\n tokenRatesState,\n multichainRatesState,\n multichainBalancesState,\n tokensState,\n currencyRateState,\n ): AllWalletsBalance => {\n const walletBalances: Record<string, WalletBalance> = {};\n let totalBalanceInUserCurrency = 0;\n\n const walletIds = Object.keys(\n accountTreeState.accountTree.wallets,\n ) as string[];\n\n for (const walletId of walletIds) {\n const wallet = (\n accountTreeState.accountTree.wallets as Record<\n string,\n AccountWalletObject\n >\n )[walletId];\n if (!wallet) {\n continue;\n }\n\n const groupBalances: Record<string, AccountGroupBalance> = {};\n let walletTotalBalance = 0;\n\n const groups = Object.keys(wallet.groups || {}) as string[];\n\n for (const groupId of groups) {\n const accounts = getInternalAccountsForGroup(\n accountTreeState,\n accountsState,\n groupId,\n );\n\n if (accounts.length === 0) {\n groupBalances[groupId] = {\n walletId,\n groupId,\n totalBalanceInUserCurrency: 0,\n userCurrency: currencyRateState.currentCurrency,\n };\n continue;\n }\n\n let groupTotalBalance = 0;\n\n // Process each account's balances\n for (const account of accounts) {\n const isEvmAccount = isEvmAccountType(account.type);\n\n if (isEvmAccount) {\n // Handle EVM account balances from TokenBalancesController\n const accountBalances =\n tokenBalancesState.tokenBalances[account.address as Hex];\n if (accountBalances) {\n for (const [chainId, chainBalances] of Object.entries(\n accountBalances,\n )) {\n for (const [tokenAddress, balance] of Object.entries(\n chainBalances,\n )) {\n // Find token in TokensController state\n const chainTokens = tokensState.allTokens[chainId as Hex];\n const accountTokens = chainTokens?.[account.address];\n const token = accountTokens?.find(\n (t) => t.address === tokenAddress,\n );\n if (!token) {\n continue;\n }\n\n // Use nullish coalescing to handle 0 decimals correctly\n // and ensure decimals is a valid number to prevent NaN propagation\n const decimals =\n typeof token.decimals === 'number' &&\n !Number.isNaN(token.decimals)\n ? token.decimals\n : 18;\n const balanceInSmallestUnit = parseInt(\n balance as string,\n 16,\n );\n\n // Skip invalid balance values to prevent NaN propagation\n if (Number.isNaN(balanceInSmallestUnit)) {\n continue;\n }\n\n const balanceInTokenUnits =\n balanceInSmallestUnit / Math.pow(10, decimals);\n\n // Get token rate in native currency from TokenRatesController\n const chainMarketData =\n tokenRatesState.marketData[chainId as Hex];\n const tokenMarketData =\n chainMarketData?.[tokenAddress as Hex];\n if (tokenMarketData?.price) {\n // Convert token price to user currency using native currency conversion rate\n const nativeCurrency = tokenMarketData.currency;\n const nativeToUserRate =\n currencyRateState.currencyRates[nativeCurrency]\n ?.conversionRate;\n\n if (nativeToUserRate) {\n // Convert token price to user currency: tokenPrice * nativeToUserRate\n const tokenPriceInUserCurrency =\n tokenMarketData.price * nativeToUserRate;\n const balanceInUserCurrency =\n balanceInTokenUnits * tokenPriceInUserCurrency;\n groupTotalBalance += balanceInUserCurrency;\n }\n }\n }\n }\n }\n } else {\n // Handle non-EVM account balances from MultichainBalancesController\n const accountBalances =\n multichainBalancesState.balances[account.id];\n if (accountBalances) {\n for (const [assetId, balanceData] of Object.entries(\n accountBalances,\n )) {\n const balanceAmount = parseFloat(balanceData.amount);\n\n // Skip invalid balance values to prevent NaN propagation\n if (Number.isNaN(balanceAmount)) {\n continue;\n }\n\n // Get conversion rate for this asset (already in user currency)\n const conversionRate =\n multichainRatesState.conversionRates[\n assetId as CaipAssetType\n ];\n if (conversionRate) {\n const conversionRateValue = parseFloat(conversionRate.rate);\n\n // Skip invalid conversion rate values to prevent NaN propagation\n if (Number.isNaN(conversionRateValue)) {\n continue;\n }\n\n // MultichainAssetsRatesController already provides rates in user currency\n const balanceInUserCurrency =\n balanceAmount * conversionRateValue;\n groupTotalBalance += balanceInUserCurrency;\n }\n }\n }\n }\n }\n\n groupBalances[groupId] = {\n walletId,\n groupId,\n totalBalanceInUserCurrency: groupTotalBalance,\n userCurrency: currencyRateState.currentCurrency,\n };\n walletTotalBalance += groupTotalBalance;\n }\n\n walletBalances[walletId] = {\n walletId,\n groups: groupBalances,\n totalBalanceInUserCurrency: walletTotalBalance,\n userCurrency: currencyRateState.currentCurrency,\n };\n totalBalanceInUserCurrency += walletTotalBalance;\n }\n\n return {\n wallets: walletBalances,\n totalBalanceInUserCurrency,\n userCurrency: currencyRateState.currentCurrency,\n };\n },\n );\n\n/**\n * Aggregated balance for an account group\n */\nexport type AccountGroupBalance = {\n walletId: string;\n groupId: string;\n totalBalanceInUserCurrency: number; // not formatted\n userCurrency: string;\n};\n\n/**\n * Aggregated balance for a wallet (all groups)\n */\nexport type WalletBalance = {\n walletId: string;\n groups: Record<string, AccountGroupBalance>;\n totalBalanceInUserCurrency: number; // not formatted\n userCurrency: string;\n};\n\n/**\n * Aggregated balance for all wallets\n */\nexport type AllWalletsBalance = {\n wallets: Record<string, WalletBalance>;\n totalBalanceInUserCurrency: number; // not formatted\n userCurrency: string;\n};\n\n/**\n * Selector to get aggregated balances for a specific account group.\n * Derives from the comprehensive selector to ensure proper memoization.\n *\n * @param groupId - The account group ID (format: \"walletId/groupIndex\", e.g., \"entropy:entropy-source-1/0\")\n * @returns Aggregated balance for the account group\n */\nexport const selectBalanceByAccountGroup = (groupId: string) =>\n createSelector(\n [selectBalanceForAllWallets()],\n (allBalances): AccountGroupBalance => {\n const walletId = groupId.split('/')[0] as EntropySourceId;\n const wallet = allBalances.wallets[walletId];\n\n if (!wallet || !wallet.groups[groupId]) {\n return {\n walletId,\n groupId,\n totalBalanceInUserCurrency: 0,\n userCurrency: allBalances.userCurrency,\n };\n }\n\n return wallet.groups[groupId];\n },\n );\n\n/**\n * Selector to get aggregated balances for all account groups in a wallet.\n * Derives from the comprehensive selector to ensure proper memoization.\n *\n * @param walletId - The wallet ID (entropy source)\n * @returns Aggregated balance for all groups in the wallet\n */\nexport const selectBalanceByWallet = (walletId: EntropySourceId) =>\n createSelector(\n [selectBalanceForAllWallets()],\n (allBalances): WalletBalance => {\n const wallet = allBalances.wallets[walletId];\n\n if (!wallet) {\n return {\n walletId,\n groups: {},\n totalBalanceInUserCurrency: 0,\n userCurrency: allBalances.userCurrency,\n };\n }\n\n return wallet;\n },\n );\n\n/**\n * Selector to get aggregated balances for the currently selected account group.\n * Derives from the comprehensive selector to ensure proper memoization.\n *\n * @returns Aggregated balance for the currently selected group\n */\nexport const selectBalanceForSelectedAccountGroup = () =>\n createSelector(\n [selectAccountTreeControllerState, selectBalanceForAllWallets()],\n (accountTreeState, allBalances): AccountGroupBalance | null => {\n const selectedGroupId = accountTreeState.accountTree.selectedAccountGroup;\n\n if (!selectedGroupId) {\n return null;\n }\n\n const walletId = selectedGroupId.split('/')[0] as EntropySourceId;\n const wallet = allBalances.wallets[walletId];\n\n if (!wallet || !wallet.groups[selectedGroupId]) {\n return {\n walletId,\n groupId: selectedGroupId,\n totalBalanceInUserCurrency: 0,\n userCurrency: allBalances.userCurrency,\n };\n }\n\n return wallet.groups[selectedGroupId];\n },\n );\n\n/**\n * Collection of balance-related selectors for assets controllers\n */\nexport const balanceSelectors = {\n selectBalanceByAccountGroup,\n selectBalanceByWallet,\n selectBalanceForAllWallets,\n selectBalanceForSelectedAccountGroup,\n};\n"]}
|