@metamask-previews/assets-controllers 73.0.2-preview-115d10e → 73.0.2-preview-b31794c

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,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.balanceSelectors = exports.selectBalanceForSelectedAccountGroup = exports.selectBalanceForAllWallets = exports.selectBalanceByWallet = exports.selectBalanceByAccountGroup = void 0;
3
+ exports.balanceSelectors = exports.selectBalanceForSelectedAccountGroup = exports.selectBalanceByWallet = exports.selectBalanceByAccountGroup = exports.selectBalanceForAllWallets = void 0;
4
4
  const keyring_api_1 = require("@metamask/keyring-api");
5
5
  const reselect_1 = require("reselect");
6
6
  /**
@@ -116,13 +116,13 @@ const getInternalAccountsForGroup = (accountTreeState, accountsState, groupId) =
116
116
  .filter(Boolean);
117
117
  };
118
118
  /**
119
- * Selector to get aggregated balances for a specific account group.
120
- * Returns total balance in user's selected currency, aggregating all tokens across accounts in the group.
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.
121
122
  *
122
- * @param groupId - The account group ID (format: "walletId/groupIndex", e.g., "entropy:entropy-source-1/0")
123
- * @returns Aggregated balance for the account group
123
+ * @returns Aggregated balance for all wallets
124
124
  */
125
- const selectBalanceByAccountGroup = (groupId) => (0, reselect_1.createSelector)([
125
+ const selectBalanceForAllWallets = () => (0, reselect_1.createSelector)([
126
126
  selectAccountTreeControllerState,
127
127
  selectAccountsControllerState,
128
128
  selectTokenBalancesControllerState,
@@ -132,217 +132,188 @@ const selectBalanceByAccountGroup = (groupId) => (0, reselect_1.createSelector)(
132
132
  selectTokensControllerState,
133
133
  selectCurrencyRateControllerState,
134
134
  ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
135
- // Extract walletId from groupId
136
- const walletId = groupId.split('/')[0];
137
- const accounts = getInternalAccountsForGroup(accountTreeState, accountsState, groupId);
138
- if (accounts.length === 0) {
139
- return {
140
- walletId,
141
- groupId,
142
- totalBalanceInUserCurrency: 0,
143
- userCurrency: currencyRateState.currentCurrency,
144
- };
145
- }
135
+ const walletBalances = {};
146
136
  let totalBalanceInUserCurrency = 0;
147
- // Process each account's balances
148
- for (const account of accounts) {
149
- const isEvmAccount = (0, keyring_api_1.isEvmAccountType)(account.type);
150
- if (isEvmAccount) {
151
- // Handle EVM account balances from TokenBalancesController
152
- // Structure: tokenBalances[chainId][accountAddress][tokenAddress] = balance
153
- for (const [chainId, chainBalances] of Object.entries(tokenBalancesState.tokenBalances)) {
154
- const accountBalances = chainBalances[account.address];
155
- if (accountBalances) {
156
- for (const [tokenAddress, balance] of Object.entries(accountBalances)) {
157
- // Find token in TokensController state
158
- const chainTokens = tokensState.allTokens[chainId];
159
- const accountTokens = chainTokens?.[account.address];
160
- const token = accountTokens?.find((t) => t.address === tokenAddress);
161
- if (!token) {
162
- continue;
163
- }
164
- const decimals = token.decimals || 18;
165
- const balanceInSmallestUnit = parseInt(balance, 16);
166
- // Skip invalid balance values to prevent NaN propagation
167
- if (Number.isNaN(balanceInSmallestUnit)) {
168
- continue;
169
- }
170
- const balanceInTokenUnits = balanceInSmallestUnit / Math.pow(10, decimals);
171
- // Get token rate in native currency from TokenRatesController
172
- const chainMarketData = tokenRatesState.marketData[chainId];
173
- const tokenMarketData = chainMarketData?.[tokenAddress];
174
- if (tokenMarketData?.price) {
175
- // Convert token price to user currency using native currency conversion rate
176
- const nativeCurrency = tokenMarketData.currency;
177
- const nativeToUserRate = currencyRateState.currencyRates[nativeCurrency]
178
- ?.conversionRate;
179
- if (nativeToUserRate) {
180
- // Convert token price to user currency: tokenPrice * nativeToUserRate
181
- const tokenPriceInUserCurrency = tokenMarketData.price * nativeToUserRate;
182
- const balanceInUserCurrency = balanceInTokenUnits * tokenPriceInUserCurrency;
183
- totalBalanceInUserCurrency += balanceInUserCurrency;
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
+ }
184
201
  }
185
202
  }
186
203
  }
187
204
  }
188
- }
189
- }
190
- else {
191
- // Handle non-EVM account balances from MultichainBalancesController
192
- const accountBalances = multichainBalancesState.balances[account.id];
193
- if (accountBalances) {
194
- for (const [assetId, balanceData] of Object.entries(accountBalances)) {
195
- const balanceAmount = parseFloat(balanceData.amount);
196
- // Skip invalid balance values to prevent NaN propagation
197
- if (Number.isNaN(balanceAmount)) {
198
- continue;
199
- }
200
- // Get conversion rate for this asset (already in user currency)
201
- const conversionRate = multichainRatesState.conversionRates[assetId];
202
- if (conversionRate) {
203
- const conversionRateValue = parseFloat(conversionRate.rate);
204
- // Skip invalid conversion rate values to prevent NaN propagation
205
- if (Number.isNaN(conversionRateValue)) {
206
- continue;
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
+ }
207
227
  }
208
- // MultichainAssetsRatesController already provides rates in user currency
209
- const balanceInUserCurrency = balanceAmount * conversionRateValue;
210
- totalBalanceInUserCurrency += balanceInUserCurrency;
211
228
  }
212
229
  }
213
230
  }
231
+ groupBalances[groupId] = {
232
+ walletId,
233
+ groupId,
234
+ totalBalanceInUserCurrency: groupTotalBalance,
235
+ userCurrency: currencyRateState.currentCurrency,
236
+ };
237
+ walletTotalBalance += groupTotalBalance;
214
238
  }
239
+ walletBalances[walletId] = {
240
+ walletId,
241
+ groups: groupBalances,
242
+ totalBalanceInUserCurrency: walletTotalBalance,
243
+ userCurrency: currencyRateState.currentCurrency,
244
+ };
245
+ totalBalanceInUserCurrency += walletTotalBalance;
215
246
  }
216
247
  return {
217
- walletId,
218
- groupId,
248
+ wallets: walletBalances,
219
249
  totalBalanceInUserCurrency,
220
250
  userCurrency: currencyRateState.currentCurrency,
221
251
  };
222
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
+ });
223
274
  exports.selectBalanceByAccountGroup = selectBalanceByAccountGroup;
224
275
  /**
225
276
  * Selector to get aggregated balances for all account groups in a wallet.
226
- * Returns total balance in user's selected currency, aggregating all tokens across all groups in the wallet.
277
+ * Derives from the comprehensive selector to ensure proper memoization.
227
278
  *
228
279
  * @param walletId - The wallet ID (entropy source)
229
280
  * @returns Aggregated balance for all groups in the wallet
230
281
  */
231
- const selectBalanceByWallet = (walletId) => (0, reselect_1.createSelector)([
232
- selectAccountTreeControllerState,
233
- selectAccountsControllerState,
234
- selectTokenBalancesControllerState,
235
- selectTokenRatesControllerState,
236
- selectMultichainAssetsRatesControllerState,
237
- selectMultichainBalancesControllerState,
238
- selectTokensControllerState,
239
- selectCurrencyRateControllerState,
240
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
241
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
242
- const wallet = accountTreeState.accountTree.wallets[walletId];
282
+ const selectBalanceByWallet = (walletId) => (0, reselect_1.createSelector)([(0, exports.selectBalanceForAllWallets)()], (allBalances) => {
283
+ const wallet = allBalances.wallets[walletId];
243
284
  if (!wallet) {
244
285
  return {
245
286
  walletId,
246
287
  groups: {},
247
288
  totalBalanceInUserCurrency: 0,
248
- userCurrency: currencyRateState.currentCurrency,
289
+ userCurrency: allBalances.userCurrency,
249
290
  };
250
291
  }
251
- const groupBalances = {};
252
- let totalBalanceInUserCurrency = 0;
253
- const groups = Object.keys(wallet.groups || {});
254
- for (const groupId of groups) {
255
- const groupBalance = (0, exports.selectBalanceByAccountGroup)(groupId)({
256
- AccountTreeController: accountTreeState,
257
- AccountsController: accountsState,
258
- TokenBalancesController: tokenBalancesState,
259
- TokenRatesController: tokenRatesState,
260
- MultichainAssetsRatesController: multichainRatesState,
261
- MultichainBalancesController: multichainBalancesState,
262
- TokensController: tokensState,
263
- CurrencyRateController: currencyRateState,
264
- });
265
- groupBalances[groupId] = groupBalance;
266
- totalBalanceInUserCurrency += groupBalance.totalBalanceInUserCurrency;
267
- }
268
- return {
269
- walletId,
270
- groups: groupBalances,
271
- totalBalanceInUserCurrency,
272
- userCurrency: currencyRateState.currentCurrency,
273
- };
292
+ return wallet;
274
293
  });
275
294
  exports.selectBalanceByWallet = selectBalanceByWallet;
276
- /**
277
- * Selector to get aggregated balances for all wallets and their account groups.
278
- * Returns total balance in user's selected currency, aggregating all tokens across all wallets.
279
- *
280
- * @returns Aggregated balance for all wallets
281
- */
282
- const selectBalanceForAllWallets = () => (0, reselect_1.createSelector)([
283
- selectAccountTreeControllerState,
284
- selectAccountsControllerState,
285
- selectTokenBalancesControllerState,
286
- selectTokenRatesControllerState,
287
- selectMultichainAssetsRatesControllerState,
288
- selectMultichainBalancesControllerState,
289
- selectTokensControllerState,
290
- selectCurrencyRateControllerState,
291
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
292
- const walletBalances = {};
293
- let totalBalanceInUserCurrency = 0;
294
- const walletIds = Object.keys(accountTreeState.accountTree.wallets);
295
- for (const walletId of walletIds) {
296
- const walletBalance = (0, exports.selectBalanceByWallet)(walletId)({
297
- AccountTreeController: accountTreeState,
298
- AccountsController: accountsState,
299
- TokenBalancesController: tokenBalancesState,
300
- TokenRatesController: tokenRatesState,
301
- MultichainAssetsRatesController: multichainRatesState,
302
- MultichainBalancesController: multichainBalancesState,
303
- TokensController: tokensState,
304
- CurrencyRateController: currencyRateState,
305
- });
306
- walletBalances[walletId] = walletBalance;
307
- totalBalanceInUserCurrency += walletBalance.totalBalanceInUserCurrency;
308
- }
309
- return {
310
- wallets: walletBalances,
311
- totalBalanceInUserCurrency,
312
- userCurrency: currencyRateState.currentCurrency,
313
- };
314
- });
315
- exports.selectBalanceForAllWallets = selectBalanceForAllWallets;
316
295
  /**
317
296
  * Selector to get aggregated balances for the currently selected account group.
318
- * Returns total balance in user's selected currency, aggregating all tokens in the selected group.
297
+ * Derives from the comprehensive selector to ensure proper memoization.
319
298
  *
320
299
  * @returns Aggregated balance for the currently selected group
321
300
  */
322
- const selectBalanceForSelectedAccountGroup = () => (0, reselect_1.createSelector)([
323
- selectAccountTreeControllerState,
324
- selectAccountsControllerState,
325
- selectTokenBalancesControllerState,
326
- selectTokenRatesControllerState,
327
- selectMultichainAssetsRatesControllerState,
328
- selectMultichainBalancesControllerState,
329
- selectTokensControllerState,
330
- selectCurrencyRateControllerState,
331
- ], (accountTreeState, accountsState, tokenBalancesState, tokenRatesState, multichainRatesState, multichainBalancesState, tokensState, currencyRateState) => {
301
+ const selectBalanceForSelectedAccountGroup = () => (0, reselect_1.createSelector)([selectAccountTreeControllerState, (0, exports.selectBalanceForAllWallets)()], (accountTreeState, allBalances) => {
332
302
  const selectedGroupId = accountTreeState.accountTree.selectedAccountGroup;
333
303
  if (!selectedGroupId) {
334
304
  return null;
335
305
  }
336
- return (0, exports.selectBalanceByAccountGroup)(selectedGroupId)({
337
- AccountTreeController: accountTreeState,
338
- AccountsController: accountsState,
339
- TokenBalancesController: tokenBalancesState,
340
- TokenRatesController: tokenRatesState,
341
- MultichainAssetsRatesController: multichainRatesState,
342
- MultichainBalancesController: multichainBalancesState,
343
- TokensController: tokensState,
344
- CurrencyRateController: currencyRateState,
345
- });
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];
346
317
  });
347
318
  exports.selectBalanceForSelectedAccountGroup = selectBalanceForSelectedAccountGroup;
348
319
  /**
@@ -1 +1 @@
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 * 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[chainId][accountAddress][tokenAddress] = balance\n for (const [chainId, chainBalances] of Object.entries(\n tokenBalancesState.tokenBalances,\n )) {\n const accountBalances = chainBalances[account.address as Hex];\n if (accountBalances) {\n for (const [tokenAddress, balance] of Object.entries(\n accountBalances,\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"]}
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"]}