@metamask-previews/assets-controllers 73.0.2-preview-d8fe3a3 → 73.0.2-preview-4375dc2

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"balanceSelectors.d.mts","sourceRoot":"","sources":["../../src/selectors/balanceSelectors.ts"],"names":[],"mappings":"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;;;;;;;;;;sCAuO8B,eAAe;;;;;;;;;;;;;;;;;;;;;;;;mCA1UpD,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAnGP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAnGP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAeP,OAAO;;;;;;;;;;;;;;;;;;;;mCAYP,OAAO;;;;;;;;;;;;;;;;;CAwZjB,CAAC"}
@@ -1,350 +0,0 @@
1
- import { isEvmAccountType } from "@metamask/keyring-api";
2
- import { createSelector } from "reselect";
3
- /**
4
- * Individual controller state selectors using direct state access
5
- * This avoids new object creation and provides stable references
6
- * Supports both mobile (state.engine.backgroundState) and extension (state.metamask) structures
7
- */
8
- /**
9
- * Helper function to get controller state from different state structures
10
- *
11
- * @param state - The application state
12
- * @param controllerName - The name of the controller
13
- * @returns The controller state or undefined if not found
14
- */
15
- const getControllerState = (
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- state, controllerName) => {
18
- // Mobile structure: state.engine.backgroundState.ControllerName
19
- if (state?.engine?.backgroundState?.[controllerName]) {
20
- return state.engine.backgroundState[controllerName];
21
- }
22
- // Extension structure: state.metamask.ControllerName
23
- if (state?.metamask?.[controllerName]) {
24
- return state.metamask[controllerName];
25
- }
26
- // Flat structure (default assets-controllers structure)
27
- if (state?.[controllerName]) {
28
- return state[controllerName];
29
- }
30
- // Since controllers always have default states, this should never happen
31
- // but we need to return something for TypeScript
32
- return state?.[controllerName];
33
- };
34
- /**
35
- * Selector for AccountTreeController state using direct state access
36
- *
37
- * @param state - The application state
38
- * @returns AccountTreeController state
39
- */
40
- const selectAccountTreeControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'AccountTreeController'));
41
- /**
42
- * Selector for AccountsController state using direct state access
43
- *
44
- * @param state - The application state
45
- * @returns AccountsController state
46
- */
47
- const selectAccountsControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'AccountsController'));
48
- /**
49
- * Selector for TokenBalancesController state using direct state access
50
- *
51
- * @param state - The application state
52
- * @returns TokenBalancesController state
53
- */
54
- const selectTokenBalancesControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'TokenBalancesController'));
55
- /**
56
- * Selector for TokenRatesController state using direct state access
57
- *
58
- * @param state - The application state
59
- * @returns TokenRatesController state
60
- */
61
- const selectTokenRatesControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'TokenRatesController'));
62
- /**
63
- * Selector for MultichainAssetsRatesController state using direct state access
64
- *
65
- * @param state - The application state
66
- * @returns MultichainAssetsRatesController state
67
- */
68
- const selectMultichainAssetsRatesControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'MultichainAssetsRatesController'));
69
- /**
70
- * Selector for MultichainBalancesController state using direct state access
71
- *
72
- * @param state - The application state
73
- * @returns MultichainBalancesController state
74
- */
75
- const selectMultichainBalancesControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'MultichainBalancesController'));
76
- /**
77
- * Selector for TokensController state using direct state access
78
- *
79
- * @param state - The application state
80
- * @returns TokensController state
81
- */
82
- const selectTokensControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'TokensController'));
83
- /**
84
- * Selector for CurrencyRateController state using direct state access
85
- *
86
- * @param state - The application state
87
- * @returns CurrencyRateController state
88
- */
89
- const selectCurrencyRateControllerState = createSelector([(state) => state], (state) => getControllerState(state, 'CurrencyRateController'));
90
- /**
91
- * Helper function to get internal accounts for a specific group.
92
- * Uses AccountTreeController state to find accounts.
93
- *
94
- * @param accountTreeState - AccountTreeController state
95
- * @param accountsState - AccountsController state
96
- * @param groupId - The account group ID (format: "walletId/groupIndex")
97
- * @returns Array of internal accounts in the group
98
- */
99
- const getInternalAccountsForGroup = (accountTreeState, accountsState, groupId) => {
100
- // Extract walletId from groupId (format: "walletId/groupIndex")
101
- const walletId = groupId.split('/')[0];
102
- const wallet = accountTreeState.accountTree.wallets[walletId];
103
- if (!wallet) {
104
- return [];
105
- }
106
- const group = wallet.groups[groupId];
107
- if (!group) {
108
- return [];
109
- }
110
- // Map account IDs to actual account objects
111
- return group.accounts
112
- .map((accountId) => accountsState.internalAccounts.accounts[accountId])
113
- .filter(Boolean);
114
- };
115
- /**
116
- * Selector to get aggregated balances for a specific account group.
117
- * Returns total balance in user's selected currency, aggregating all tokens across accounts in the group.
118
- *
119
- * @param groupId - The account group ID (format: "walletId/groupIndex", e.g., "entropy:entropy-source-1/0")
120
- * @returns Aggregated balance for the account group
121
- */
122
- export const selectBalanceByAccountGroup = (groupId) => createSelector([
123
- selectAccountTreeControllerState,
124
- selectAccountsControllerState,
125
- selectTokenBalancesControllerState,
126
- selectTokenRatesControllerState,
127
- selectMultichainAssetsRatesControllerState,
128
- selectMultichainBalancesControllerState,
129
- selectTokensControllerState,
130
- selectCurrencyRateControllerState,
131
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
132
- // Extract walletId from groupId
133
- const walletId = groupId.split('/')[0];
134
- const accounts = getInternalAccountsForGroup(accountTreeState, accountsState, groupId);
135
- if (accounts.length === 0) {
136
- return {
137
- walletId,
138
- groupId,
139
- totalBalanceInUserCurrency: 0,
140
- userCurrency: currencyRateState.currentCurrency,
141
- };
142
- }
143
- let totalBalanceInUserCurrency = 0;
144
- // Process each account's balances
145
- for (const account of accounts) {
146
- const isEvmAccount = isEvmAccountType(account.type);
147
- if (isEvmAccount) {
148
- // Handle EVM account balances from TokenBalancesController
149
- // Structure: tokenBalances[accountAddress][chainId][tokenAddress] = balance
150
- const accountBalances = tokenBalancesState.tokenBalances[account.address];
151
- if (accountBalances) {
152
- for (const [chainId, chainBalances] of Object.entries(accountBalances)) {
153
- for (const [tokenAddress, balance] of Object.entries(chainBalances)) {
154
- // Find token in TokensController state
155
- const chainTokens = tokensState.allTokens[chainId];
156
- const accountTokens = chainTokens?.[account.address];
157
- const token = accountTokens?.find((t) => t.address === tokenAddress);
158
- if (!token) {
159
- continue;
160
- }
161
- const decimals = token.decimals || 18;
162
- const balanceInSmallestUnit = parseInt(balance, 16);
163
- // Skip invalid balance values to prevent NaN propagation
164
- if (Number.isNaN(balanceInSmallestUnit)) {
165
- continue;
166
- }
167
- const balanceInTokenUnits = balanceInSmallestUnit / Math.pow(10, decimals);
168
- // Get token rate in native currency from TokenRatesController
169
- const chainMarketData = tokenRatesState.marketData[chainId];
170
- const tokenMarketData = chainMarketData?.[tokenAddress];
171
- if (tokenMarketData?.price) {
172
- // Convert token price to user currency using native currency conversion rate
173
- const nativeCurrency = tokenMarketData.currency;
174
- const nativeToUserRate = currencyRateState.currencyRates[nativeCurrency]
175
- ?.conversionRate;
176
- if (nativeToUserRate) {
177
- // Convert token price to user currency: tokenPrice * nativeToUserRate
178
- const tokenPriceInUserCurrency = tokenMarketData.price * nativeToUserRate;
179
- const balanceInUserCurrency = balanceInTokenUnits * tokenPriceInUserCurrency;
180
- totalBalanceInUserCurrency += balanceInUserCurrency;
181
- }
182
- }
183
- }
184
- }
185
- }
186
- }
187
- else {
188
- // Handle non-EVM account balances from MultichainBalancesController
189
- const accountBalances = multichainBalancesState.balances[account.id];
190
- if (accountBalances) {
191
- for (const [assetId, balanceData] of Object.entries(accountBalances)) {
192
- const balanceAmount = parseFloat(balanceData.amount);
193
- // Skip invalid balance values to prevent NaN propagation
194
- if (Number.isNaN(balanceAmount)) {
195
- continue;
196
- }
197
- // Get conversion rate for this asset (already in user currency)
198
- const conversionRate = multichainRatesState.conversionRates[assetId];
199
- if (conversionRate) {
200
- const conversionRateValue = parseFloat(conversionRate.rate);
201
- // Skip invalid conversion rate values to prevent NaN propagation
202
- if (Number.isNaN(conversionRateValue)) {
203
- continue;
204
- }
205
- // MultichainAssetsRatesController already provides rates in user currency
206
- const balanceInUserCurrency = balanceAmount * conversionRateValue;
207
- totalBalanceInUserCurrency += balanceInUserCurrency;
208
- }
209
- }
210
- }
211
- }
212
- }
213
- return {
214
- walletId,
215
- groupId,
216
- totalBalanceInUserCurrency,
217
- userCurrency: currencyRateState.currentCurrency,
218
- };
219
- });
220
- /**
221
- * Selector to get aggregated balances for all account groups in a wallet.
222
- * Returns total balance in user's selected currency, aggregating all tokens across all groups in the wallet.
223
- *
224
- * @param walletId - The wallet ID (entropy source)
225
- * @returns Aggregated balance for all groups in the wallet
226
- */
227
- export const selectBalanceByWallet = (walletId) => createSelector([
228
- selectAccountTreeControllerState,
229
- selectAccountsControllerState,
230
- selectTokenBalancesControllerState,
231
- selectTokenRatesControllerState,
232
- selectMultichainAssetsRatesControllerState,
233
- selectMultichainBalancesControllerState,
234
- selectTokensControllerState,
235
- selectCurrencyRateControllerState,
236
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
237
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
238
- const wallet = accountTreeState.accountTree.wallets[walletId];
239
- if (!wallet) {
240
- return {
241
- walletId,
242
- groups: {},
243
- totalBalanceInUserCurrency: 0,
244
- userCurrency: currencyRateState.currentCurrency,
245
- };
246
- }
247
- const groupBalances = {};
248
- let totalBalanceInUserCurrency = 0;
249
- const groups = Object.keys(wallet.groups || {});
250
- for (const groupId of groups) {
251
- const groupBalance = selectBalanceByAccountGroup(groupId)({
252
- AccountTreeController: accountTreeState,
253
- AccountsController: accountsState,
254
- TokenBalancesController: tokenBalancesState,
255
- TokenRatesController: tokenRatesState,
256
- MultichainAssetsRatesController: multichainRatesState,
257
- MultichainBalancesController: multichainBalancesState,
258
- TokensController: tokensState,
259
- CurrencyRateController: currencyRateState,
260
- });
261
- groupBalances[groupId] = groupBalance;
262
- totalBalanceInUserCurrency += groupBalance.totalBalanceInUserCurrency;
263
- }
264
- return {
265
- walletId,
266
- groups: groupBalances,
267
- totalBalanceInUserCurrency,
268
- userCurrency: currencyRateState.currentCurrency,
269
- };
270
- });
271
- /**
272
- * Selector to get aggregated balances for all wallets and their account groups.
273
- * Returns total balance in user's selected currency, aggregating all tokens across all wallets.
274
- *
275
- * @returns Aggregated balance for all wallets
276
- */
277
- export const selectBalanceForAllWallets = () => createSelector([
278
- selectAccountTreeControllerState,
279
- selectAccountsControllerState,
280
- selectTokenBalancesControllerState,
281
- selectTokenRatesControllerState,
282
- selectMultichainAssetsRatesControllerState,
283
- selectMultichainBalancesControllerState,
284
- selectTokensControllerState,
285
- selectCurrencyRateControllerState,
286
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
287
- const walletBalances = {};
288
- let totalBalanceInUserCurrency = 0;
289
- const walletIds = Object.keys(accountTreeState.accountTree.wallets);
290
- for (const walletId of walletIds) {
291
- const walletBalance = selectBalanceByWallet(walletId)({
292
- AccountTreeController: accountTreeState,
293
- AccountsController: accountsState,
294
- TokenBalancesController: tokenBalancesState,
295
- TokenRatesController: tokenRatesState,
296
- MultichainAssetsRatesController: multichainRatesState,
297
- MultichainBalancesController: multichainBalancesState,
298
- TokensController: tokensState,
299
- CurrencyRateController: currencyRateState,
300
- });
301
- walletBalances[walletId] = walletBalance;
302
- totalBalanceInUserCurrency += walletBalance.totalBalanceInUserCurrency;
303
- }
304
- return {
305
- wallets: walletBalances,
306
- totalBalanceInUserCurrency,
307
- userCurrency: currencyRateState.currentCurrency,
308
- };
309
- });
310
- /**
311
- * Selector to get aggregated balances for the currently selected account group.
312
- * Returns total balance in user's selected currency, aggregating all tokens in the selected group.
313
- *
314
- * @returns Aggregated balance for the currently selected group
315
- */
316
- export const selectBalanceForSelectedAccountGroup = () => createSelector([
317
- selectAccountTreeControllerState,
318
- selectAccountsControllerState,
319
- selectTokenBalancesControllerState,
320
- selectTokenRatesControllerState,
321
- selectMultichainAssetsRatesControllerState,
322
- selectMultichainBalancesControllerState,
323
- selectTokensControllerState,
324
- selectCurrencyRateControllerState,
325
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
326
- const selectedGroupId = accountTreeState.accountTree.selectedAccountGroup;
327
- if (!selectedGroupId) {
328
- return null;
329
- }
330
- return selectBalanceByAccountGroup(selectedGroupId)({
331
- AccountTreeController: accountTreeState,
332
- AccountsController: accountsState,
333
- TokenBalancesController: tokenBalancesState,
334
- TokenRatesController: tokenRatesState,
335
- MultichainAssetsRatesController: multichainRatesState,
336
- MultichainBalancesController: multichainBalancesState,
337
- TokensController: tokensState,
338
- CurrencyRateController: currencyRateState,
339
- });
340
- });
341
- /**
342
- * Collection of balance-related selectors for assets controllers
343
- */
344
- export const balanceSelectors = {
345
- selectBalanceByAccountGroup,
346
- selectBalanceByWallet,
347
- selectBalanceForAllWallets,
348
- selectBalanceForSelectedAccountGroup,
349
- };
350
- //# sourceMappingURL=balanceSelectors.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"balanceSelectors.mjs","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 * Selector to get aggregated balances for a specific account group.\n * Returns total balance in user's selected currency, aggregating all tokens across accounts in the group.\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 [\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 ): AccountGroupBalance => {\n // Extract walletId from groupId\n const walletId = groupId.split('/')[0] as EntropySourceId;\n\n const accounts = getInternalAccountsForGroup(\n accountTreeState,\n accountsState,\n groupId,\n );\n\n if (accounts.length === 0) {\n return {\n walletId,\n groupId,\n totalBalanceInUserCurrency: 0,\n userCurrency: currencyRateState.currentCurrency,\n };\n }\n\n let totalBalanceInUserCurrency = 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 // Structure: tokenBalances[accountAddress][chainId][tokenAddress] = balance\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 const decimals = token.decimals || 18;\n const balanceInSmallestUnit = parseInt(balance as string, 16);\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 = 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 totalBalanceInUserCurrency += balanceInUserCurrency;\n }\n }\n }\n }\n }\n } else {\n // Handle non-EVM account balances from MultichainBalancesController\n const accountBalances = 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[assetId as CaipAssetType];\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 totalBalanceInUserCurrency += balanceInUserCurrency;\n }\n }\n }\n }\n }\n\n return {\n walletId,\n groupId,\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 all account groups in a wallet.\n * Returns total balance in user's selected currency, aggregating all tokens across all groups in the wallet.\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 [\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 ): WalletBalance => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const wallet = (accountTreeState.accountTree.wallets as any)[walletId];\n if (!wallet) {\n return {\n walletId,\n groups: {},\n totalBalanceInUserCurrency: 0,\n userCurrency: currencyRateState.currentCurrency,\n };\n }\n\n const groupBalances: Record<string, AccountGroupBalance> = {};\n let totalBalanceInUserCurrency = 0;\n\n const groups = Object.keys(wallet.groups || {}) as string[];\n\n for (const groupId of groups) {\n const groupBalance = selectBalanceByAccountGroup(groupId)({\n AccountTreeController: accountTreeState,\n AccountsController: accountsState,\n TokenBalancesController: tokenBalancesState,\n TokenRatesController: tokenRatesState,\n MultichainAssetsRatesController: multichainRatesState,\n MultichainBalancesController: multichainBalancesState,\n TokensController: tokensState,\n CurrencyRateController: currencyRateState,\n });\n\n groupBalances[groupId] = groupBalance;\n totalBalanceInUserCurrency += groupBalance.totalBalanceInUserCurrency;\n }\n\n return {\n walletId,\n groups: groupBalances,\n totalBalanceInUserCurrency,\n userCurrency: currencyRateState.currentCurrency,\n };\n },\n );\n\n/**\n * Selector to get aggregated balances for all wallets and their account groups.\n * Returns total balance in user's selected currency, aggregating all tokens across all wallets.\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 walletBalance = selectBalanceByWallet(walletId)({\n AccountTreeController: accountTreeState,\n AccountsController: accountsState,\n TokenBalancesController: tokenBalancesState,\n TokenRatesController: tokenRatesState,\n MultichainAssetsRatesController: multichainRatesState,\n MultichainBalancesController: multichainBalancesState,\n TokensController: tokensState,\n CurrencyRateController: currencyRateState,\n });\n\n walletBalances[walletId] = walletBalance;\n totalBalanceInUserCurrency += walletBalance.totalBalanceInUserCurrency;\n }\n\n return {\n wallets: walletBalances,\n totalBalanceInUserCurrency,\n userCurrency: currencyRateState.currentCurrency,\n };\n },\n );\n\n/**\n * Selector to get aggregated balances for the currently selected account group.\n * Returns total balance in user's selected currency, aggregating all tokens in the selected group.\n *\n * @returns Aggregated balance for the currently selected group\n */\nexport const selectBalanceForSelectedAccountGroup = () =>\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 ): AccountGroupBalance | null => {\n const selectedGroupId = accountTreeState.accountTree.selectedAccountGroup;\n\n if (!selectedGroupId) {\n return null;\n }\n\n return selectBalanceByAccountGroup(selectedGroupId)({\n AccountTreeController: accountTreeState,\n AccountsController: accountsState,\n TokenBalancesController: tokenBalancesState,\n TokenRatesController: tokenRatesState,\n MultichainAssetsRatesController: multichainRatesState,\n MultichainBalancesController: multichainBalancesState,\n TokensController: tokensState,\n CurrencyRateController: currencyRateState,\n });\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"]}