@metamask/assets-controllers 73.2.0 → 74.0.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/dist/TokenBalancesController.cjs +1 -1
  3. package/dist/TokenBalancesController.cjs.map +1 -1
  4. package/dist/TokenBalancesController.d.cts.map +1 -1
  5. package/dist/TokenBalancesController.d.mts.map +1 -1
  6. package/dist/TokenBalancesController.mjs +1 -1
  7. package/dist/TokenBalancesController.mjs.map +1 -1
  8. package/dist/index.cjs +3 -1
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2 -0
  11. package/dist/index.d.cts.map +1 -1
  12. package/dist/index.d.mts +2 -0
  13. package/dist/index.d.mts.map +1 -1
  14. package/dist/index.mjs +1 -0
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs +55 -16
  17. package/dist/multi-chain-accounts-service/api-balance-fetcher.cjs.map +1 -1
  18. package/dist/multi-chain-accounts-service/api-balance-fetcher.d.cts.map +1 -1
  19. package/dist/multi-chain-accounts-service/api-balance-fetcher.d.mts.map +1 -1
  20. package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs +55 -16
  21. package/dist/multi-chain-accounts-service/api-balance-fetcher.mjs.map +1 -1
  22. package/dist/selectors/stringify-balance.cjs +43 -0
  23. package/dist/selectors/stringify-balance.cjs.map +1 -0
  24. package/dist/selectors/stringify-balance.d.cts +8 -0
  25. package/dist/selectors/stringify-balance.d.cts.map +1 -0
  26. package/dist/selectors/stringify-balance.d.mts +8 -0
  27. package/dist/selectors/stringify-balance.d.mts.map +1 -0
  28. package/dist/selectors/stringify-balance.mjs +39 -0
  29. package/dist/selectors/stringify-balance.mjs.map +1 -0
  30. package/dist/selectors/token-selectors.cjs +275 -0
  31. package/dist/selectors/token-selectors.cjs.map +1 -0
  32. package/dist/selectors/token-selectors.d.cts +734 -0
  33. package/dist/selectors/token-selectors.d.cts.map +1 -0
  34. package/dist/selectors/token-selectors.d.mts +734 -0
  35. package/dist/selectors/token-selectors.d.mts.map +1 -0
  36. package/dist/selectors/token-selectors.mjs +272 -0
  37. package/dist/selectors/token-selectors.mjs.map +1 -0
  38. package/package.json +13 -13
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectAssetsBySelectedAccountGroup = void 0;
4
+ const controller_utils_1 = require("@metamask/controller-utils");
5
+ const utils_1 = require("@metamask/utils");
6
+ const reselect_1 = require("reselect");
7
+ const stringify_balance_1 = require("./stringify-balance.cjs");
8
+ const codefi_v2_1 = require("../token-prices-service/codefi-v2.cjs");
9
+ // If this gets out of hand with other chains, we should probably have a permanent object that defines them
10
+ const MULTICHAIN_NATIVE_ASSET_IDS = [
11
+ `bip122:000000000019d6689c085ae165831e93/slip44:0`,
12
+ `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501`,
13
+ ];
14
+ const createAssetListSelector = reselect_1.createSelector.withTypes();
15
+ const selectAccountsToGroupIdMap = createAssetListSelector([(state) => state.accountTree, (state) => state.internalAccounts], (accountTree, internalAccounts) => {
16
+ const accountsMap = {};
17
+ for (const { groups } of Object.values(accountTree.wallets)) {
18
+ for (const { id: accountGroupId, accounts } of Object.values(groups)) {
19
+ for (const accountId of accounts) {
20
+ const internalAccount = internalAccounts.accounts[accountId];
21
+ accountsMap[
22
+ // TODO: We would not need internalAccounts if evmTokens state had the accountId
23
+ internalAccount.type.startsWith('eip155')
24
+ ? internalAccount.address
25
+ : accountId] = accountGroupId;
26
+ }
27
+ }
28
+ }
29
+ return accountsMap;
30
+ });
31
+ // TODO: This selector will not be needed once the native balances are part of the evm tokens state
32
+ const selectAllEvmAccountNativeBalances = createAssetListSelector([
33
+ selectAccountsToGroupIdMap,
34
+ (state) => state.accountsByChainId,
35
+ (state) => state.marketData,
36
+ (state) => state.currencyRates,
37
+ (state) => state.currentCurrency,
38
+ (state) => state.networkConfigurationsByChainId,
39
+ ], (accountsMap, accountsByChainId, marketData, currencyRates, currentCurrency, networkConfigurationsByChainId) => {
40
+ var _a;
41
+ const groupAssets = {};
42
+ for (const [chainId, chainAccounts] of Object.entries(accountsByChainId)) {
43
+ for (const [accountAddress, accountBalance] of Object.entries(chainAccounts)) {
44
+ const accountGroupId = accountsMap[accountAddress];
45
+ groupAssets[accountGroupId] ?? (groupAssets[accountGroupId] = {});
46
+ (_a = groupAssets[accountGroupId])[chainId] ?? (_a[chainId] = []);
47
+ const groupChainAssets = groupAssets[accountGroupId][chainId];
48
+ const rawBalance = accountBalance.balance || '0x0';
49
+ const nativeCurrency = networkConfigurationsByChainId[chainId]?.nativeCurrency || 'NATIVE';
50
+ const nativeToken = {
51
+ address: (0, codefi_v2_1.getNativeTokenAddress)(chainId),
52
+ decimals: 18,
53
+ name: nativeCurrency === 'ETH' ? 'Ethereum' : nativeCurrency,
54
+ symbol: nativeCurrency,
55
+ // This field need to be filled at client level for now
56
+ image: '',
57
+ };
58
+ const fiatData = getFiatBalanceForEvmToken(rawBalance, nativeToken.decimals, marketData, currencyRates, chainId, nativeToken.address);
59
+ groupChainAssets.push({
60
+ type: 'evm',
61
+ assetId: nativeToken.address,
62
+ isNative: true,
63
+ address: nativeToken.address,
64
+ image: nativeToken.image,
65
+ name: nativeToken.name,
66
+ symbol: nativeToken.symbol,
67
+ decimals: nativeToken.decimals,
68
+ balance: (0, stringify_balance_1.stringifyBalanceWithDecimals)((0, utils_1.hexToBigInt)(rawBalance), nativeToken.decimals),
69
+ fiat: fiatData
70
+ ? {
71
+ balance: fiatData.balance,
72
+ currency: currentCurrency,
73
+ conversionRate: fiatData.conversionRate,
74
+ }
75
+ : undefined,
76
+ chainId,
77
+ });
78
+ }
79
+ }
80
+ return groupAssets;
81
+ });
82
+ const selectAllEvmAssets = createAssetListSelector([
83
+ selectAccountsToGroupIdMap,
84
+ (state) => state.allTokens,
85
+ (state) => state.allIgnoredTokens,
86
+ (state) => state.tokenBalances,
87
+ (state) => state.marketData,
88
+ (state) => state.currencyRates,
89
+ (state) => state.currentCurrency,
90
+ ], (accountsMap, evmTokens, ignoredEvmTokens, tokenBalances, marketData, currencyRates, currentCurrency) => {
91
+ var _a;
92
+ const groupAssets = {};
93
+ for (const [chainId, chainTokens] of Object.entries(evmTokens)) {
94
+ for (const [accountAddress, addressTokens] of Object.entries(chainTokens)) {
95
+ for (const token of addressTokens) {
96
+ const tokenAddress = token.address;
97
+ const accountGroupId = accountsMap[accountAddress];
98
+ if (ignoredEvmTokens[chainId]?.[accountAddress]?.includes(tokenAddress)) {
99
+ continue;
100
+ }
101
+ const rawBalance = tokenBalances[accountAddress]?.[chainId]?.[tokenAddress];
102
+ if (!rawBalance) {
103
+ continue;
104
+ }
105
+ groupAssets[accountGroupId] ?? (groupAssets[accountGroupId] = {});
106
+ (_a = groupAssets[accountGroupId])[chainId] ?? (_a[chainId] = []);
107
+ const groupChainAssets = groupAssets[accountGroupId][chainId];
108
+ const fiatData = getFiatBalanceForEvmToken(rawBalance, token.decimals, marketData, currencyRates, chainId, tokenAddress);
109
+ groupChainAssets.push({
110
+ type: 'evm',
111
+ assetId: tokenAddress,
112
+ isNative: false,
113
+ address: tokenAddress,
114
+ image: token.image ?? '',
115
+ name: token.name ?? token.symbol,
116
+ symbol: token.symbol,
117
+ decimals: token.decimals,
118
+ balance: (0, stringify_balance_1.stringifyBalanceWithDecimals)((0, utils_1.hexToBigInt)(rawBalance), token.decimals),
119
+ fiat: fiatData
120
+ ? {
121
+ balance: fiatData.balance,
122
+ currency: currentCurrency,
123
+ conversionRate: fiatData.conversionRate,
124
+ }
125
+ : undefined,
126
+ chainId,
127
+ });
128
+ }
129
+ }
130
+ }
131
+ return groupAssets;
132
+ });
133
+ const selectAllMultichainAssets = createAssetListSelector([
134
+ selectAccountsToGroupIdMap,
135
+ (state) => state.accountsAssets,
136
+ (state) => state.assetsMetadata,
137
+ (state) => state.balances,
138
+ (state) => state.conversionRates,
139
+ (state) => state.currentCurrency,
140
+ ], (accountsMap, multichainTokens, multichainAssetsMetadata, multichainBalances, multichainConversionRates, currentCurrency) => {
141
+ var _a;
142
+ const groupAssets = {};
143
+ for (const [accountId, accountAssets] of Object.entries(multichainTokens)) {
144
+ for (const assetId of accountAssets) {
145
+ let caipAsset;
146
+ try {
147
+ caipAsset = (0, utils_1.parseCaipAssetType)(assetId);
148
+ }
149
+ catch {
150
+ // TODO: We should log this error when we have the ability to inject a logger from the client
151
+ continue;
152
+ }
153
+ const { chainId } = caipAsset;
154
+ const asset = `${caipAsset.assetNamespace}:${caipAsset.assetReference}`;
155
+ const accountGroupId = accountsMap[accountId];
156
+ const assetMetadata = multichainAssetsMetadata[assetId];
157
+ if (!accountGroupId || !assetMetadata) {
158
+ continue;
159
+ }
160
+ groupAssets[accountGroupId] ?? (groupAssets[accountGroupId] = {});
161
+ (_a = groupAssets[accountGroupId])[chainId] ?? (_a[chainId] = []);
162
+ const groupChainAssets = groupAssets[accountGroupId][chainId];
163
+ const balance = multichainBalances[accountId]?.[assetId];
164
+ if (!balance) {
165
+ continue;
166
+ }
167
+ const fiatData = getFiatBalanceForMultichainAsset(balance, multichainConversionRates, assetId);
168
+ // TODO: We shouldn't have to rely on fallbacks for name and symbol, they should not be optional
169
+ groupChainAssets.push({
170
+ type: 'multichain',
171
+ assetId,
172
+ isNative: MULTICHAIN_NATIVE_ASSET_IDS.includes(assetId),
173
+ image: assetMetadata.iconUrl,
174
+ name: assetMetadata.name ?? assetMetadata.symbol ?? asset,
175
+ symbol: assetMetadata.symbol ?? asset,
176
+ decimals: assetMetadata.units.find((unit) => unit.name === assetMetadata.name &&
177
+ unit.symbol === assetMetadata.symbol)?.decimals ?? 0,
178
+ balance: balance.amount,
179
+ fiat: fiatData
180
+ ? {
181
+ balance: fiatData.balance,
182
+ currency: currentCurrency,
183
+ conversionRate: fiatData.conversionRate,
184
+ }
185
+ : undefined,
186
+ chainId,
187
+ });
188
+ }
189
+ }
190
+ return groupAssets;
191
+ });
192
+ const selectAllAssets = createAssetListSelector([
193
+ selectAllEvmAssets,
194
+ selectAllMultichainAssets,
195
+ selectAllEvmAccountNativeBalances,
196
+ ], (evmAssets, multichainAssets, evmAccountNativeBalances) => {
197
+ const groupAssets = {};
198
+ mergeAssets(groupAssets, evmAssets);
199
+ mergeAssets(groupAssets, multichainAssets);
200
+ mergeAssets(groupAssets, evmAccountNativeBalances);
201
+ return groupAssets;
202
+ });
203
+ exports.selectAssetsBySelectedAccountGroup = createAssetListSelector([selectAllAssets, (state) => state.accountTree], (groupAssets, accountTree) => {
204
+ const { selectedAccountGroup } = accountTree;
205
+ if (!selectedAccountGroup) {
206
+ return {};
207
+ }
208
+ return groupAssets[selectedAccountGroup] || {};
209
+ });
210
+ // TODO: Once native assets are part of the evm tokens state, this function can be simplified as chains will always be unique
211
+ /**
212
+ * Merges the new assets into the existing assets
213
+ *
214
+ * @param existingAssets - The existing assets
215
+ * @param newAssets - The new assets
216
+ */
217
+ function mergeAssets(existingAssets, newAssets) {
218
+ for (const [accountGroupId, accountAssets] of Object.entries(newAssets)) {
219
+ const existingAccountGroupAssets = existingAssets[accountGroupId];
220
+ if (!existingAccountGroupAssets) {
221
+ existingAssets[accountGroupId] = accountAssets;
222
+ }
223
+ else {
224
+ for (const [network, chainAssets] of Object.entries(accountAssets)) {
225
+ existingAccountGroupAssets[network] ?? (existingAccountGroupAssets[network] = []);
226
+ existingAccountGroupAssets[network].push(...chainAssets);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ /**
232
+ * @param rawBalance - The balance of the token
233
+ * @param decimals - The decimals of the token
234
+ * @param marketData - The market data for the token
235
+ * @param currencyRates - The currency rates for the token
236
+ * @param chainId - The chain id of the token
237
+ * @param tokenAddress - The address of the token
238
+ * @returns The price and currency of the token in the current currency. Returns undefined if the asset is not found in the market data or currency rates.
239
+ */
240
+ function getFiatBalanceForEvmToken(rawBalance, decimals, marketData, currencyRates, chainId, tokenAddress) {
241
+ const tokenMarketData = marketData[chainId]?.[tokenAddress];
242
+ if (!tokenMarketData) {
243
+ return undefined;
244
+ }
245
+ const currencyRate = currencyRates[tokenMarketData.currency];
246
+ if (!currencyRate?.conversionRate) {
247
+ return undefined;
248
+ }
249
+ const fiatBalance = ((0, controller_utils_1.convertHexToDecimal)(rawBalance) / 10 ** decimals) *
250
+ tokenMarketData.price *
251
+ currencyRate.conversionRate;
252
+ return {
253
+ balance: fiatBalance,
254
+ conversionRate: currencyRate.conversionRate,
255
+ };
256
+ }
257
+ /**
258
+ * @param balance - The balance of the asset, in the format { amount: string; unit: string }
259
+ * @param balance.amount - The amount of the balance
260
+ * @param balance.unit - The unit of the balance
261
+ * @param multichainConversionRates - The conversion rates for the multichain asset
262
+ * @param assetId - The asset id of the asset
263
+ * @returns The price and currency of the token in the current currency. Returns undefined if the asset is not found in the conversion rates.
264
+ */
265
+ function getFiatBalanceForMultichainAsset(balance, multichainConversionRates, assetId) {
266
+ const assetMarketData = multichainConversionRates[assetId];
267
+ if (!assetMarketData?.rate) {
268
+ return undefined;
269
+ }
270
+ return {
271
+ balance: Number(balance.amount) * Number(assetMarketData.rate),
272
+ conversionRate: Number(assetMarketData.rate),
273
+ };
274
+ }
275
+ //# sourceMappingURL=token-selectors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-selectors.cjs","sourceRoot":"","sources":["../../src/selectors/token-selectors.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId } from '@metamask/account-api';\nimport type { AccountTreeControllerState } from '@metamask/account-tree-controller';\nimport type { AccountsControllerState } from '@metamask/accounts-controller';\nimport { convertHexToDecimal } from '@metamask/controller-utils';\nimport type { NetworkState } from '@metamask/network-controller';\nimport { hexToBigInt, parseCaipAssetType, type Hex } from '@metamask/utils';\nimport { createSelector } from 'reselect';\n\nimport { stringifyBalanceWithDecimals } from './stringify-balance';\nimport type { CurrencyRateState } from '../CurrencyRateController';\nimport type { MultichainAssetsControllerState } from '../MultichainAssetsController';\nimport type { MultichainAssetsRatesControllerState } from '../MultichainAssetsRatesController';\nimport type { MultichainBalancesControllerState } from '../MultichainBalancesController';\nimport { getNativeTokenAddress } from '../token-prices-service/codefi-v2';\nimport type { TokenBalancesControllerState } from '../TokenBalancesController';\nimport type { Token, TokenRatesControllerState } from '../TokenRatesController';\nimport type { TokensControllerState } from '../TokensController';\n\ntype AssetsByAccountGroup = {\n [accountGroupId: AccountGroupId]: AccountGroupAssets;\n};\n\nexport type AccountGroupAssets = {\n [network: string]: Asset[];\n};\n\n// If this gets out of hand with other chains, we should probably have a permanent object that defines them\nconst MULTICHAIN_NATIVE_ASSET_IDS = [\n `bip122:000000000019d6689c085ae165831e93/slip44:0`,\n `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501`,\n];\n\nexport type Asset = (\n | {\n type: 'evm';\n assetId: Hex; // This is also the address for EVM tokens\n address: Hex;\n chainId: Hex;\n }\n | {\n type: 'multichain';\n assetId: `${string}:${string}/${string}:${string}`;\n chainId: `${string}:${string}`;\n }\n) & {\n image: string;\n name: string;\n symbol: string;\n decimals: number;\n isNative: boolean;\n balance: string;\n fiat:\n | {\n balance: number;\n currency: string;\n conversionRate: number;\n }\n | undefined;\n};\n\nexport type AssetListState = {\n accountTree: AccountTreeControllerState['accountTree'];\n internalAccounts: AccountsControllerState['internalAccounts'];\n allTokens: TokensControllerState['allTokens'];\n allIgnoredTokens: TokensControllerState['allIgnoredTokens'];\n tokenBalances: TokenBalancesControllerState['tokenBalances'];\n marketData: TokenRatesControllerState['marketData'];\n currencyRates: CurrencyRateState['currencyRates'];\n accountsAssets: MultichainAssetsControllerState['accountsAssets'];\n assetsMetadata: MultichainAssetsControllerState['assetsMetadata'];\n balances: MultichainBalancesControllerState['balances'];\n conversionRates: MultichainAssetsRatesControllerState['conversionRates'];\n currentCurrency: CurrencyRateState['currentCurrency'];\n networkConfigurationsByChainId: NetworkState['networkConfigurationsByChainId'];\n // This is the state from AccountTrackerController. The state is different on mobile and extension\n // accountsByChainId with a balance is the only field that both clients have in common\n // This field could be removed once TokenBalancesController returns native balances\n accountsByChainId: Record<\n Hex,\n Record<\n Hex,\n {\n balance: Hex | null;\n }\n >\n >;\n};\n\nconst createAssetListSelector = createSelector.withTypes<AssetListState>();\n\nconst selectAccountsToGroupIdMap = createAssetListSelector(\n [(state) => state.accountTree, (state) => state.internalAccounts],\n (accountTree, internalAccounts) => {\n const accountsMap: Record<string, AccountGroupId> = {};\n for (const { groups } of Object.values(accountTree.wallets)) {\n for (const { id: accountGroupId, accounts } of Object.values(groups)) {\n for (const accountId of accounts) {\n const internalAccount = internalAccounts.accounts[accountId];\n\n accountsMap[\n // TODO: We would not need internalAccounts if evmTokens state had the accountId\n internalAccount.type.startsWith('eip155')\n ? internalAccount.address\n : accountId\n ] = accountGroupId;\n }\n }\n }\n\n return accountsMap;\n },\n);\n\n// TODO: This selector will not be needed once the native balances are part of the evm tokens state\nconst selectAllEvmAccountNativeBalances = createAssetListSelector(\n [\n selectAccountsToGroupIdMap,\n (state) => state.accountsByChainId,\n (state) => state.marketData,\n (state) => state.currencyRates,\n (state) => state.currentCurrency,\n (state) => state.networkConfigurationsByChainId,\n ],\n (\n accountsMap,\n accountsByChainId,\n marketData,\n currencyRates,\n currentCurrency,\n networkConfigurationsByChainId,\n ) => {\n const groupAssets: AssetsByAccountGroup = {};\n\n for (const [chainId, chainAccounts] of Object.entries(\n accountsByChainId,\n ) as [Hex, Record<Hex, { balance: Hex | null }>][]) {\n for (const [accountAddress, accountBalance] of Object.entries(\n chainAccounts,\n )) {\n const accountGroupId = accountsMap[accountAddress];\n groupAssets[accountGroupId] ??= {};\n groupAssets[accountGroupId][chainId] ??= [];\n const groupChainAssets = groupAssets[accountGroupId][chainId];\n\n const rawBalance = accountBalance.balance || '0x0';\n\n const nativeCurrency =\n networkConfigurationsByChainId[chainId]?.nativeCurrency || 'NATIVE';\n\n const nativeToken = {\n address: getNativeTokenAddress(chainId),\n decimals: 18,\n name: nativeCurrency === 'ETH' ? 'Ethereum' : nativeCurrency,\n symbol: nativeCurrency,\n // This field need to be filled at client level for now\n image: '',\n };\n\n const fiatData = getFiatBalanceForEvmToken(\n rawBalance,\n nativeToken.decimals,\n marketData,\n currencyRates,\n chainId,\n nativeToken.address,\n );\n\n groupChainAssets.push({\n type: 'evm',\n assetId: nativeToken.address,\n isNative: true,\n address: nativeToken.address,\n image: nativeToken.image,\n name: nativeToken.name,\n symbol: nativeToken.symbol,\n decimals: nativeToken.decimals,\n balance: stringifyBalanceWithDecimals(\n hexToBigInt(rawBalance),\n nativeToken.decimals,\n ),\n fiat: fiatData\n ? {\n balance: fiatData.balance,\n currency: currentCurrency,\n conversionRate: fiatData.conversionRate,\n }\n : undefined,\n chainId,\n });\n }\n }\n\n return groupAssets;\n },\n);\n\nconst selectAllEvmAssets = createAssetListSelector(\n [\n selectAccountsToGroupIdMap,\n (state) => state.allTokens,\n (state) => state.allIgnoredTokens,\n (state) => state.tokenBalances,\n (state) => state.marketData,\n (state) => state.currencyRates,\n (state) => state.currentCurrency,\n ],\n (\n accountsMap,\n evmTokens,\n ignoredEvmTokens,\n tokenBalances,\n marketData,\n currencyRates,\n currentCurrency,\n ) => {\n const groupAssets: AssetsByAccountGroup = {};\n\n for (const [chainId, chainTokens] of Object.entries(evmTokens) as [\n Hex,\n { [key: string]: Token[] },\n ][]) {\n for (const [accountAddress, addressTokens] of Object.entries(\n chainTokens,\n ) as [Hex, Token[]][]) {\n for (const token of addressTokens) {\n const tokenAddress = token.address as Hex;\n const accountGroupId = accountsMap[accountAddress];\n\n if (\n ignoredEvmTokens[chainId]?.[accountAddress]?.includes(tokenAddress)\n ) {\n continue;\n }\n\n const rawBalance =\n tokenBalances[accountAddress]?.[chainId]?.[tokenAddress];\n\n if (!rawBalance) {\n continue;\n }\n\n groupAssets[accountGroupId] ??= {};\n groupAssets[accountGroupId][chainId] ??= [];\n const groupChainAssets = groupAssets[accountGroupId][chainId];\n\n const fiatData = getFiatBalanceForEvmToken(\n rawBalance,\n token.decimals,\n marketData,\n currencyRates,\n chainId,\n tokenAddress,\n );\n\n groupChainAssets.push({\n type: 'evm',\n assetId: tokenAddress,\n isNative: false,\n address: tokenAddress,\n image: token.image ?? '',\n name: token.name ?? token.symbol,\n symbol: token.symbol,\n decimals: token.decimals,\n balance: stringifyBalanceWithDecimals(\n hexToBigInt(rawBalance),\n token.decimals,\n ),\n fiat: fiatData\n ? {\n balance: fiatData.balance,\n currency: currentCurrency,\n conversionRate: fiatData.conversionRate,\n }\n : undefined,\n chainId,\n });\n }\n }\n }\n\n return groupAssets;\n },\n);\n\nconst selectAllMultichainAssets = createAssetListSelector(\n [\n selectAccountsToGroupIdMap,\n (state) => state.accountsAssets,\n (state) => state.assetsMetadata,\n (state) => state.balances,\n (state) => state.conversionRates,\n (state) => state.currentCurrency,\n ],\n (\n accountsMap,\n multichainTokens,\n multichainAssetsMetadata,\n multichainBalances,\n multichainConversionRates,\n currentCurrency,\n ) => {\n const groupAssets: AssetsByAccountGroup = {};\n\n for (const [accountId, accountAssets] of Object.entries(multichainTokens)) {\n for (const assetId of accountAssets) {\n let caipAsset: ReturnType<typeof parseCaipAssetType>;\n try {\n caipAsset = parseCaipAssetType(assetId);\n } catch {\n // TODO: We should log this error when we have the ability to inject a logger from the client\n continue;\n }\n\n const { chainId } = caipAsset;\n const asset = `${caipAsset.assetNamespace}:${caipAsset.assetReference}`;\n\n const accountGroupId = accountsMap[accountId];\n const assetMetadata = multichainAssetsMetadata[assetId];\n if (!accountGroupId || !assetMetadata) {\n continue;\n }\n\n groupAssets[accountGroupId] ??= {};\n groupAssets[accountGroupId][chainId] ??= [];\n const groupChainAssets = groupAssets[accountGroupId][chainId];\n\n const balance:\n | {\n amount: string;\n unit: string;\n }\n | undefined = multichainBalances[accountId]?.[assetId];\n\n if (!balance) {\n continue;\n }\n\n const fiatData = getFiatBalanceForMultichainAsset(\n balance,\n multichainConversionRates,\n assetId,\n );\n\n // TODO: We shouldn't have to rely on fallbacks for name and symbol, they should not be optional\n groupChainAssets.push({\n type: 'multichain',\n assetId,\n isNative: MULTICHAIN_NATIVE_ASSET_IDS.includes(assetId),\n image: assetMetadata.iconUrl,\n name: assetMetadata.name ?? assetMetadata.symbol ?? asset,\n symbol: assetMetadata.symbol ?? asset,\n decimals:\n assetMetadata.units.find(\n (unit) =>\n unit.name === assetMetadata.name &&\n unit.symbol === assetMetadata.symbol,\n )?.decimals ?? 0,\n balance: balance.amount,\n fiat: fiatData\n ? {\n balance: fiatData.balance,\n currency: currentCurrency,\n conversionRate: fiatData.conversionRate,\n }\n : undefined,\n chainId,\n });\n }\n }\n\n return groupAssets;\n },\n);\n\nconst selectAllAssets = createAssetListSelector(\n [\n selectAllEvmAssets,\n selectAllMultichainAssets,\n selectAllEvmAccountNativeBalances,\n ],\n (evmAssets, multichainAssets, evmAccountNativeBalances) => {\n const groupAssets: AssetsByAccountGroup = {};\n\n mergeAssets(groupAssets, evmAssets);\n\n mergeAssets(groupAssets, multichainAssets);\n\n mergeAssets(groupAssets, evmAccountNativeBalances);\n\n return groupAssets;\n },\n);\n\nexport const selectAssetsBySelectedAccountGroup = createAssetListSelector(\n [selectAllAssets, (state) => state.accountTree],\n (groupAssets, accountTree) => {\n const { selectedAccountGroup } = accountTree;\n if (!selectedAccountGroup) {\n return {};\n }\n return groupAssets[selectedAccountGroup] || {};\n },\n);\n\n// TODO: Once native assets are part of the evm tokens state, this function can be simplified as chains will always be unique\n/**\n * Merges the new assets into the existing assets\n *\n * @param existingAssets - The existing assets\n * @param newAssets - The new assets\n */\nfunction mergeAssets(\n existingAssets: AssetsByAccountGroup,\n newAssets: AssetsByAccountGroup,\n) {\n for (const [accountGroupId, accountAssets] of Object.entries(newAssets) as [\n AccountGroupId,\n AccountGroupAssets,\n ][]) {\n const existingAccountGroupAssets = existingAssets[accountGroupId];\n\n if (!existingAccountGroupAssets) {\n existingAssets[accountGroupId] = accountAssets;\n } else {\n for (const [network, chainAssets] of Object.entries(accountAssets)) {\n existingAccountGroupAssets[network] ??= [];\n existingAccountGroupAssets[network].push(...chainAssets);\n }\n }\n }\n}\n\n/**\n * @param rawBalance - The balance of the token\n * @param decimals - The decimals of the token\n * @param marketData - The market data for the token\n * @param currencyRates - The currency rates for the token\n * @param chainId - The chain id of the token\n * @param tokenAddress - The address of the token\n * @returns The price and currency of the token in the current currency. Returns undefined if the asset is not found in the market data or currency rates.\n */\nfunction getFiatBalanceForEvmToken(\n rawBalance: Hex,\n decimals: number,\n marketData: TokenRatesControllerState['marketData'],\n currencyRates: CurrencyRateState['currencyRates'],\n chainId: Hex,\n tokenAddress: Hex,\n) {\n const tokenMarketData = marketData[chainId]?.[tokenAddress];\n\n if (!tokenMarketData) {\n return undefined;\n }\n\n const currencyRate = currencyRates[tokenMarketData.currency];\n\n if (!currencyRate?.conversionRate) {\n return undefined;\n }\n\n const fiatBalance =\n (convertHexToDecimal(rawBalance) / 10 ** decimals) *\n tokenMarketData.price *\n currencyRate.conversionRate;\n\n return {\n balance: fiatBalance,\n conversionRate: currencyRate.conversionRate,\n };\n}\n\n/**\n * @param balance - The balance of the asset, in the format { amount: string; unit: string }\n * @param balance.amount - The amount of the balance\n * @param balance.unit - The unit of the balance\n * @param multichainConversionRates - The conversion rates for the multichain asset\n * @param assetId - The asset id of the asset\n * @returns The price and currency of the token in the current currency. Returns undefined if the asset is not found in the conversion rates.\n */\nfunction getFiatBalanceForMultichainAsset(\n balance: { amount: string; unit: string },\n multichainConversionRates: MultichainAssetsRatesControllerState['conversionRates'],\n assetId: `${string}:${string}/${string}:${string}`,\n) {\n const assetMarketData = multichainConversionRates[assetId];\n\n if (!assetMarketData?.rate) {\n return undefined;\n }\n\n return {\n balance: Number(balance.amount) * Number(assetMarketData.rate),\n conversionRate: Number(assetMarketData.rate),\n };\n}\n"]}