@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
@@ -51,10 +51,19 @@ class AccountsApiBalanceFetcher {
51
51
  if (!caipAddrs.length) {
52
52
  return [];
53
53
  }
54
- const [balances, stakedBalances] = await Promise.all([
55
- (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchBalances).call(this, caipAddrs)),
56
- __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchStakedBalances).call(this, caipAddrs),
57
- ]);
54
+ // Don't use safelyExecute here - let real errors propagate
55
+ let balances;
56
+ let apiError = false;
57
+ try {
58
+ balances = await __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchBalances).call(this, caipAddrs);
59
+ }
60
+ catch (error) {
61
+ // Mark that we had an API error so we don't add fake zero balances
62
+ apiError = true;
63
+ console.error('Failed to fetch balances from API:', error);
64
+ balances = undefined;
65
+ }
66
+ const stakedBalances = await __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchStakedBalances).call(this, caipAddrs);
58
67
  const results = [];
59
68
  // Collect all unique addresses and chains from the CAIP addresses
60
69
  const addressChainMap = new Map();
@@ -81,7 +90,17 @@ class AccountsApiBalanceFetcher {
81
90
  const chainId = (0, controller_utils_1.toHex)(b.chainId);
82
91
  let value;
83
92
  try {
84
- value = new bn_js_1.default((parseFloat(b.balance) * 10 ** b.decimals).toFixed(0));
93
+ // Convert string balance to BN avoiding floating point precision issues
94
+ const { balance: balanceStr, decimals } = b;
95
+ // Split the balance string into integer and decimal parts
96
+ const [integerPart = '0', decimalPart = ''] = balanceStr.split('.');
97
+ // Pad or truncate decimal part to match token decimals
98
+ const paddedDecimalPart = decimalPart
99
+ .padEnd(decimals, '0')
100
+ .slice(0, decimals);
101
+ // Combine and create BN
102
+ const fullIntegerStr = integerPart + paddedDecimalPart;
103
+ value = new bn_js_1.default(fullIntegerStr);
85
104
  }
86
105
  catch {
87
106
  value = undefined;
@@ -102,25 +121,45 @@ class AccountsApiBalanceFetcher {
102
121
  });
103
122
  results.push(...apiBalances);
104
123
  }
105
- // Ensure native token entries exist for all addresses/chains, even if not returned by API
106
- addressChainMap.forEach((chains, address) => {
107
- chains.forEach((chainId) => {
108
- const key = `${address}-${chainId}`;
109
- const existingBalance = nativeBalancesFromAPI.get(key);
110
- if (!existingBalance) {
111
- // Add zero native balance entry if API didn't return one
124
+ // Only add zero native balance entries if API succeeded but didn't return balances
125
+ // Don't add fake zero balances if the API failed entirely
126
+ if (!apiError) {
127
+ addressChainMap.forEach((chains, address) => {
128
+ chains.forEach((chainId) => {
129
+ const key = `${address}-${chainId}`;
130
+ const existingBalance = nativeBalancesFromAPI.get(key);
131
+ if (!existingBalance) {
132
+ // Add zero native balance entry if API succeeded but didn't return one
133
+ results.push({
134
+ success: true,
135
+ value: new bn_js_1.default('0'),
136
+ account: address,
137
+ token: ZERO_ADDRESS,
138
+ chainId,
139
+ });
140
+ }
141
+ });
142
+ });
143
+ }
144
+ else {
145
+ // If API failed, add error entries for all requested addresses/chains
146
+ addressChainMap.forEach((chains, address) => {
147
+ chains.forEach((chainId) => {
112
148
  results.push({
113
- success: true,
114
- value: new bn_js_1.default('0'),
149
+ success: false,
115
150
  account: address,
116
151
  token: ZERO_ADDRESS,
117
152
  chainId,
118
153
  });
119
- }
154
+ });
120
155
  });
121
- });
156
+ }
122
157
  // Add staked balances
123
158
  results.push(...stakedBalances);
159
+ // If we had an API error and no successful results, throw the error
160
+ if (apiError && results.every((r) => !r.success)) {
161
+ throw new Error('Failed to fetch any balance data due to API error');
162
+ }
124
163
  return results;
125
164
  }
126
165
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-balance-fetcher.cjs","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport type { Web3Provider } from '@ethersproject/providers';\nimport {\n safelyExecute,\n toHex,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { CaipAccountAddress, Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { fetchMultiChainBalancesV4 } from './multi-chain-accounts';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport {\n accountAddressToCaipReference,\n reduceInBatchesSerially,\n SupportedStakedBalanceNetworks,\n} from '../assetsUtil';\nimport { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from '../constants';\n\n// Maximum number of account addresses that can be sent to the accounts API in a single request\nconst ACCOUNTS_API_BATCH_SIZE = 50;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<ProcessedBalance[]>;\n};\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nconst toCaipAccount = (\n chainId: ChainIdHex,\n account: ChecksumAddress,\n): CaipAccountAddress => accountAddressToCaipReference(chainId, account);\n\nexport type GetProviderFunction = (chainId: ChainIdHex) => Web3Provider;\n\nexport class AccountsApiBalanceFetcher implements BalanceFetcher {\n readonly #platform: 'extension' | 'mobile' = 'extension';\n\n readonly #getProvider?: GetProviderFunction;\n\n constructor(\n platform: 'extension' | 'mobile' = 'extension',\n getProvider?: GetProviderFunction,\n ) {\n this.#platform = platform;\n this.#getProvider = getProvider;\n }\n\n supports(chainId: ChainIdHex): boolean {\n return SUPPORTED_NETWORKS_ACCOUNTS_API_V4.includes(chainId);\n }\n\n async #fetchStakedBalances(\n addrs: CaipAccountAddress[],\n ): Promise<ProcessedBalance[]> {\n // Return empty array if no provider is available for blockchain calls\n if (!this.#getProvider) {\n return [];\n }\n\n const results: ProcessedBalance[] = [];\n\n // Group addresses by chain ID\n const addressesByChain: Record<ChainIdHex, ChecksumAddress[]> = {};\n\n for (const caipAddr of addrs) {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressesByChain[chainId]) {\n addressesByChain[chainId] = [];\n }\n addressesByChain[chainId].push(checksumAddress);\n }\n\n // Process each supported chain\n for (const [chainId, addresses] of Object.entries(addressesByChain)) {\n const chainIdHex = chainId as ChainIdHex;\n\n // Only fetch staked balance on supported networks (mainnet and hoodi)\n if (\n ![\n SupportedStakedBalanceNetworks.mainnet,\n SupportedStakedBalanceNetworks.hoodi,\n ].includes(chainIdHex as SupportedStakedBalanceNetworks)\n ) {\n continue;\n }\n\n // Only fetch staked balance if contract address exists\n if (!(chainIdHex in STAKING_CONTRACT_ADDRESS_BY_CHAINID)) {\n continue;\n }\n\n const contractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n chainIdHex as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n const provider = this.#getProvider(chainIdHex);\n\n const abi = [\n {\n inputs: [\n { internalType: 'address', name: 'account', type: 'address' },\n ],\n name: 'getShares',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [\n { internalType: 'uint256', name: 'shares', type: 'uint256' },\n ],\n name: 'convertToAssets',\n outputs: [\n { internalType: 'uint256', name: 'assets', type: 'uint256' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ];\n\n try {\n const contract = new Contract(contractAddress, abi, provider);\n\n // Get shares for each address\n for (const address of addresses) {\n try {\n const shares = await safelyExecute(() =>\n contract.getShares(address),\n );\n\n if (shares && (shares as BigNumber).gt(0)) {\n // Convert shares to assets (actual staked ETH amount)\n const assets = await safelyExecute(() =>\n contract.convertToAssets(shares),\n );\n\n if (assets) {\n results.push({\n success: true,\n value: new BN((assets as BigNumber).toString()),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } else {\n // Return zero balance for accounts with no staked assets\n results.push({\n success: true,\n value: new BN('0'),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } catch (error) {\n // Log error and continue with next address\n console.error(\n `Error fetching staked balance for ${address}:`,\n error,\n );\n results.push({\n success: false,\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n }\n } catch (error) {\n console.error(\n `Error setting up staking contract for chain ${chainId}:`,\n error,\n );\n }\n }\n\n return results;\n }\n\n async #fetchBalances(addrs: CaipAccountAddress[]) {\n // If we have fewer than or equal to the batch size, make a single request\n if (addrs.length <= ACCOUNTS_API_BATCH_SIZE) {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: addrs },\n this.#platform,\n );\n return balances;\n }\n\n // Otherwise, batch the requests to respect the 50-element limit\n type BalanceData = Awaited<\n ReturnType<typeof fetchMultiChainBalancesV4>\n >['balances'][number];\n\n const allBalances = await reduceInBatchesSerially<\n CaipAccountAddress,\n BalanceData[]\n >({\n values: addrs,\n batchSize: ACCOUNTS_API_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: batch },\n this.#platform,\n );\n return [...(workingResult || []), ...balances];\n },\n initialResult: [],\n });\n\n return allBalances;\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n const caipAddrs: CaipAccountAddress[] = [];\n\n for (const chainId of chainIds.filter((c) => this.supports(c))) {\n if (queryAllAccounts) {\n allAccounts.forEach((a) =>\n caipAddrs.push(toCaipAccount(chainId, a.address as ChecksumAddress)),\n );\n } else {\n caipAddrs.push(toCaipAccount(chainId, selectedAccount));\n }\n }\n\n if (!caipAddrs.length) {\n return [];\n }\n\n const [balances, stakedBalances] = await Promise.all([\n safelyExecute(() => this.#fetchBalances(caipAddrs)),\n this.#fetchStakedBalances(caipAddrs),\n ]);\n\n const results: ProcessedBalance[] = [];\n\n // Collect all unique addresses and chains from the CAIP addresses\n const addressChainMap = new Map<string, Set<ChainIdHex>>();\n caipAddrs.forEach((caipAddr) => {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressChainMap.has(checksumAddress)) {\n addressChainMap.set(checksumAddress, new Set());\n }\n addressChainMap.get(checksumAddress)?.add(chainId);\n });\n\n // Ensure native token entries exist for all addresses on all requested chains\n const ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n const nativeBalancesFromAPI = new Map<string, BN>(); // key: `${address}-${chainId}`\n\n // Process regular API balances\n if (balances) {\n const apiBalances = balances.flatMap((b) => {\n const account = b.accountAddress?.split(':')[2] as ChecksumAddress;\n if (!account) {\n return [];\n }\n const token = checksum(b.address);\n const chainId = toHex(b.chainId) as ChainIdHex;\n\n let value: BN | undefined;\n try {\n value = new BN((parseFloat(b.balance) * 10 ** b.decimals).toFixed(0));\n } catch {\n value = undefined;\n }\n\n // Track native balances for later\n if (token === ZERO_ADDRESS && value !== undefined) {\n nativeBalancesFromAPI.set(`${account}-${chainId}`, value);\n }\n\n return [\n {\n success: value !== undefined,\n value,\n account,\n token,\n chainId,\n },\n ];\n });\n results.push(...apiBalances);\n }\n\n // Ensure native token entries exist for all addresses/chains, even if not returned by API\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n const key = `${address}-${chainId}`;\n const existingBalance = nativeBalancesFromAPI.get(key);\n\n if (!existingBalance) {\n // Add zero native balance entry if API didn't return one\n results.push({\n success: true,\n value: new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n }\n });\n });\n\n // Add staked balances\n results.push(...stakedBalances);\n\n return results;\n }\n}\n"]}
1
+ {"version":3,"file":"api-balance-fetcher.cjs","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport type { Web3Provider } from '@ethersproject/providers';\nimport {\n safelyExecute,\n toHex,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { CaipAccountAddress, Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { fetchMultiChainBalancesV4 } from './multi-chain-accounts';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport {\n accountAddressToCaipReference,\n reduceInBatchesSerially,\n SupportedStakedBalanceNetworks,\n} from '../assetsUtil';\nimport { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from '../constants';\n\n// Maximum number of account addresses that can be sent to the accounts API in a single request\nconst ACCOUNTS_API_BATCH_SIZE = 50;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<ProcessedBalance[]>;\n};\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nconst toCaipAccount = (\n chainId: ChainIdHex,\n account: ChecksumAddress,\n): CaipAccountAddress => accountAddressToCaipReference(chainId, account);\n\nexport type GetProviderFunction = (chainId: ChainIdHex) => Web3Provider;\n\nexport class AccountsApiBalanceFetcher implements BalanceFetcher {\n readonly #platform: 'extension' | 'mobile' = 'extension';\n\n readonly #getProvider?: GetProviderFunction;\n\n constructor(\n platform: 'extension' | 'mobile' = 'extension',\n getProvider?: GetProviderFunction,\n ) {\n this.#platform = platform;\n this.#getProvider = getProvider;\n }\n\n supports(chainId: ChainIdHex): boolean {\n return SUPPORTED_NETWORKS_ACCOUNTS_API_V4.includes(chainId);\n }\n\n async #fetchStakedBalances(\n addrs: CaipAccountAddress[],\n ): Promise<ProcessedBalance[]> {\n // Return empty array if no provider is available for blockchain calls\n if (!this.#getProvider) {\n return [];\n }\n\n const results: ProcessedBalance[] = [];\n\n // Group addresses by chain ID\n const addressesByChain: Record<ChainIdHex, ChecksumAddress[]> = {};\n\n for (const caipAddr of addrs) {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressesByChain[chainId]) {\n addressesByChain[chainId] = [];\n }\n addressesByChain[chainId].push(checksumAddress);\n }\n\n // Process each supported chain\n for (const [chainId, addresses] of Object.entries(addressesByChain)) {\n const chainIdHex = chainId as ChainIdHex;\n\n // Only fetch staked balance on supported networks (mainnet and hoodi)\n if (\n ![\n SupportedStakedBalanceNetworks.mainnet,\n SupportedStakedBalanceNetworks.hoodi,\n ].includes(chainIdHex as SupportedStakedBalanceNetworks)\n ) {\n continue;\n }\n\n // Only fetch staked balance if contract address exists\n if (!(chainIdHex in STAKING_CONTRACT_ADDRESS_BY_CHAINID)) {\n continue;\n }\n\n const contractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n chainIdHex as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n const provider = this.#getProvider(chainIdHex);\n\n const abi = [\n {\n inputs: [\n { internalType: 'address', name: 'account', type: 'address' },\n ],\n name: 'getShares',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [\n { internalType: 'uint256', name: 'shares', type: 'uint256' },\n ],\n name: 'convertToAssets',\n outputs: [\n { internalType: 'uint256', name: 'assets', type: 'uint256' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ];\n\n try {\n const contract = new Contract(contractAddress, abi, provider);\n\n // Get shares for each address\n for (const address of addresses) {\n try {\n const shares = await safelyExecute(() =>\n contract.getShares(address),\n );\n\n if (shares && (shares as BigNumber).gt(0)) {\n // Convert shares to assets (actual staked ETH amount)\n const assets = await safelyExecute(() =>\n contract.convertToAssets(shares),\n );\n\n if (assets) {\n results.push({\n success: true,\n value: new BN((assets as BigNumber).toString()),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } else {\n // Return zero balance for accounts with no staked assets\n results.push({\n success: true,\n value: new BN('0'),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } catch (error) {\n // Log error and continue with next address\n console.error(\n `Error fetching staked balance for ${address}:`,\n error,\n );\n results.push({\n success: false,\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n }\n } catch (error) {\n console.error(\n `Error setting up staking contract for chain ${chainId}:`,\n error,\n );\n }\n }\n\n return results;\n }\n\n async #fetchBalances(addrs: CaipAccountAddress[]) {\n // If we have fewer than or equal to the batch size, make a single request\n if (addrs.length <= ACCOUNTS_API_BATCH_SIZE) {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: addrs },\n this.#platform,\n );\n return balances;\n }\n\n // Otherwise, batch the requests to respect the 50-element limit\n type BalanceData = Awaited<\n ReturnType<typeof fetchMultiChainBalancesV4>\n >['balances'][number];\n\n const allBalances = await reduceInBatchesSerially<\n CaipAccountAddress,\n BalanceData[]\n >({\n values: addrs,\n batchSize: ACCOUNTS_API_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: batch },\n this.#platform,\n );\n return [...(workingResult || []), ...balances];\n },\n initialResult: [],\n });\n\n return allBalances;\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n const caipAddrs: CaipAccountAddress[] = [];\n\n for (const chainId of chainIds.filter((c) => this.supports(c))) {\n if (queryAllAccounts) {\n allAccounts.forEach((a) =>\n caipAddrs.push(toCaipAccount(chainId, a.address as ChecksumAddress)),\n );\n } else {\n caipAddrs.push(toCaipAccount(chainId, selectedAccount));\n }\n }\n\n if (!caipAddrs.length) {\n return [];\n }\n\n // Don't use safelyExecute here - let real errors propagate\n let balances;\n let apiError = false;\n\n try {\n balances = await this.#fetchBalances(caipAddrs);\n } catch (error) {\n // Mark that we had an API error so we don't add fake zero balances\n apiError = true;\n console.error('Failed to fetch balances from API:', error);\n balances = undefined;\n }\n\n const stakedBalances = await this.#fetchStakedBalances(caipAddrs);\n\n const results: ProcessedBalance[] = [];\n\n // Collect all unique addresses and chains from the CAIP addresses\n const addressChainMap = new Map<string, Set<ChainIdHex>>();\n caipAddrs.forEach((caipAddr) => {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressChainMap.has(checksumAddress)) {\n addressChainMap.set(checksumAddress, new Set());\n }\n addressChainMap.get(checksumAddress)?.add(chainId);\n });\n\n // Ensure native token entries exist for all addresses on all requested chains\n const ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n const nativeBalancesFromAPI = new Map<string, BN>(); // key: `${address}-${chainId}`\n\n // Process regular API balances\n if (balances) {\n const apiBalances = balances.flatMap((b) => {\n const account = b.accountAddress?.split(':')[2] as ChecksumAddress;\n if (!account) {\n return [];\n }\n const token = checksum(b.address);\n const chainId = toHex(b.chainId) as ChainIdHex;\n\n let value: BN | undefined;\n try {\n // Convert string balance to BN avoiding floating point precision issues\n const { balance: balanceStr, decimals } = b;\n\n // Split the balance string into integer and decimal parts\n const [integerPart = '0', decimalPart = ''] = balanceStr.split('.');\n\n // Pad or truncate decimal part to match token decimals\n const paddedDecimalPart = decimalPart\n .padEnd(decimals, '0')\n .slice(0, decimals);\n\n // Combine and create BN\n const fullIntegerStr = integerPart + paddedDecimalPart;\n value = new BN(fullIntegerStr);\n } catch {\n value = undefined;\n }\n\n // Track native balances for later\n if (token === ZERO_ADDRESS && value !== undefined) {\n nativeBalancesFromAPI.set(`${account}-${chainId}`, value);\n }\n\n return [\n {\n success: value !== undefined,\n value,\n account,\n token,\n chainId,\n },\n ];\n });\n results.push(...apiBalances);\n }\n\n // Only add zero native balance entries if API succeeded but didn't return balances\n // Don't add fake zero balances if the API failed entirely\n if (!apiError) {\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n const key = `${address}-${chainId}`;\n const existingBalance = nativeBalancesFromAPI.get(key);\n\n if (!existingBalance) {\n // Add zero native balance entry if API succeeded but didn't return one\n results.push({\n success: true,\n value: new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n }\n });\n });\n } else {\n // If API failed, add error entries for all requested addresses/chains\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n results.push({\n success: false,\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n });\n });\n }\n\n // Add staked balances\n results.push(...stakedBalances);\n\n // If we had an API error and no successful results, throw the error\n if (apiError && results.every((r) => !r.success)) {\n throw new Error('Failed to fetch any balance data due to API error');\n }\n\n return results;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"api-balance-fetcher.d.cts","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAM7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAsB,GAAG,EAAE,wBAAwB;AAC/D,OAAO,EAAE,cAAc;AAcvB,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;KAChC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACjC,CAAC;AAUF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;AAExE,qBAAa,yBAA0B,YAAW,cAAc;;gBAM5D,QAAQ,GAAE,WAAW,GAAG,QAAsB,EAC9C,WAAW,CAAC,EAAE,mBAAmB;IAMnC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO;IA0KhC,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,GACZ,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAqGxE"}
1
+ {"version":3,"file":"api-balance-fetcher.d.cts","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAM7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAsB,GAAG,EAAE,wBAAwB;AAC/D,OAAO,EAAE,cAAc;AAcvB,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;KAChC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACjC,CAAC;AAUF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;AAExE,qBAAa,yBAA0B,YAAW,cAAc;;gBAM5D,QAAQ,GAAE,WAAW,GAAG,QAAsB,EAC9C,WAAW,CAAC,EAAE,mBAAmB;IAMnC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO;IA0KhC,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,GACZ,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAgJxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-balance-fetcher.d.mts","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAM7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAsB,GAAG,EAAE,wBAAwB;AAC/D,OAAO,EAAE,cAAc;AAcvB,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;KAChC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACjC,CAAC;AAUF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;AAExE,qBAAa,yBAA0B,YAAW,cAAc;;gBAM5D,QAAQ,GAAE,WAAW,GAAG,QAAsB,EAC9C,WAAW,CAAC,EAAE,mBAAmB;IAMnC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO;IA0KhC,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,GACZ,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAqGxE"}
1
+ {"version":3,"file":"api-balance-fetcher.d.mts","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iCAAiC;AAM7D,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAsB,GAAG,EAAE,wBAAwB;AAC/D,OAAO,EAAE,cAAc;AAcvB,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,EAAE,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QACX,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,eAAe,EAAE,CAAC;KAChC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACjC,CAAC;AAUF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;AAExE,qBAAa,yBAA0B,YAAW,cAAc;;gBAM5D,QAAQ,GAAE,WAAW,GAAG,QAAsB,EAC9C,WAAW,CAAC,EAAE,mBAAmB;IAMnC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO;IA0KhC,KAAK,CAAC,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,WAAW,GACZ,EAAE,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAgJxE"}
@@ -52,10 +52,19 @@ export class AccountsApiBalanceFetcher {
52
52
  if (!caipAddrs.length) {
53
53
  return [];
54
54
  }
55
- const [balances, stakedBalances] = await Promise.all([
56
- safelyExecute(() => __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchBalances).call(this, caipAddrs)),
57
- __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchStakedBalances).call(this, caipAddrs),
58
- ]);
55
+ // Don't use safelyExecute here - let real errors propagate
56
+ let balances;
57
+ let apiError = false;
58
+ try {
59
+ balances = await __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchBalances).call(this, caipAddrs);
60
+ }
61
+ catch (error) {
62
+ // Mark that we had an API error so we don't add fake zero balances
63
+ apiError = true;
64
+ console.error('Failed to fetch balances from API:', error);
65
+ balances = undefined;
66
+ }
67
+ const stakedBalances = await __classPrivateFieldGet(this, _AccountsApiBalanceFetcher_instances, "m", _AccountsApiBalanceFetcher_fetchStakedBalances).call(this, caipAddrs);
59
68
  const results = [];
60
69
  // Collect all unique addresses and chains from the CAIP addresses
61
70
  const addressChainMap = new Map();
@@ -82,7 +91,17 @@ export class AccountsApiBalanceFetcher {
82
91
  const chainId = toHex(b.chainId);
83
92
  let value;
84
93
  try {
85
- value = new BN((parseFloat(b.balance) * 10 ** b.decimals).toFixed(0));
94
+ // Convert string balance to BN avoiding floating point precision issues
95
+ const { balance: balanceStr, decimals } = b;
96
+ // Split the balance string into integer and decimal parts
97
+ const [integerPart = '0', decimalPart = ''] = balanceStr.split('.');
98
+ // Pad or truncate decimal part to match token decimals
99
+ const paddedDecimalPart = decimalPart
100
+ .padEnd(decimals, '0')
101
+ .slice(0, decimals);
102
+ // Combine and create BN
103
+ const fullIntegerStr = integerPart + paddedDecimalPart;
104
+ value = new BN(fullIntegerStr);
86
105
  }
87
106
  catch {
88
107
  value = undefined;
@@ -103,25 +122,45 @@ export class AccountsApiBalanceFetcher {
103
122
  });
104
123
  results.push(...apiBalances);
105
124
  }
106
- // Ensure native token entries exist for all addresses/chains, even if not returned by API
107
- addressChainMap.forEach((chains, address) => {
108
- chains.forEach((chainId) => {
109
- const key = `${address}-${chainId}`;
110
- const existingBalance = nativeBalancesFromAPI.get(key);
111
- if (!existingBalance) {
112
- // Add zero native balance entry if API didn't return one
125
+ // Only add zero native balance entries if API succeeded but didn't return balances
126
+ // Don't add fake zero balances if the API failed entirely
127
+ if (!apiError) {
128
+ addressChainMap.forEach((chains, address) => {
129
+ chains.forEach((chainId) => {
130
+ const key = `${address}-${chainId}`;
131
+ const existingBalance = nativeBalancesFromAPI.get(key);
132
+ if (!existingBalance) {
133
+ // Add zero native balance entry if API succeeded but didn't return one
134
+ results.push({
135
+ success: true,
136
+ value: new BN('0'),
137
+ account: address,
138
+ token: ZERO_ADDRESS,
139
+ chainId,
140
+ });
141
+ }
142
+ });
143
+ });
144
+ }
145
+ else {
146
+ // If API failed, add error entries for all requested addresses/chains
147
+ addressChainMap.forEach((chains, address) => {
148
+ chains.forEach((chainId) => {
113
149
  results.push({
114
- success: true,
115
- value: new BN('0'),
150
+ success: false,
116
151
  account: address,
117
152
  token: ZERO_ADDRESS,
118
153
  chainId,
119
154
  });
120
- }
155
+ });
121
156
  });
122
- });
157
+ }
123
158
  // Add staked balances
124
159
  results.push(...stakedBalances);
160
+ // If we had an API error and no successful results, throw the error
161
+ if (apiError && results.every((r) => !r.success)) {
162
+ throw new Error('Failed to fetch any balance data due to API error');
163
+ }
125
164
  return results;
126
165
  }
127
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-balance-fetcher.mjs","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport type { Web3Provider } from '@ethersproject/providers';\nimport {\n safelyExecute,\n toHex,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { CaipAccountAddress, Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { fetchMultiChainBalancesV4 } from './multi-chain-accounts';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport {\n accountAddressToCaipReference,\n reduceInBatchesSerially,\n SupportedStakedBalanceNetworks,\n} from '../assetsUtil';\nimport { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from '../constants';\n\n// Maximum number of account addresses that can be sent to the accounts API in a single request\nconst ACCOUNTS_API_BATCH_SIZE = 50;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<ProcessedBalance[]>;\n};\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nconst toCaipAccount = (\n chainId: ChainIdHex,\n account: ChecksumAddress,\n): CaipAccountAddress => accountAddressToCaipReference(chainId, account);\n\nexport type GetProviderFunction = (chainId: ChainIdHex) => Web3Provider;\n\nexport class AccountsApiBalanceFetcher implements BalanceFetcher {\n readonly #platform: 'extension' | 'mobile' = 'extension';\n\n readonly #getProvider?: GetProviderFunction;\n\n constructor(\n platform: 'extension' | 'mobile' = 'extension',\n getProvider?: GetProviderFunction,\n ) {\n this.#platform = platform;\n this.#getProvider = getProvider;\n }\n\n supports(chainId: ChainIdHex): boolean {\n return SUPPORTED_NETWORKS_ACCOUNTS_API_V4.includes(chainId);\n }\n\n async #fetchStakedBalances(\n addrs: CaipAccountAddress[],\n ): Promise<ProcessedBalance[]> {\n // Return empty array if no provider is available for blockchain calls\n if (!this.#getProvider) {\n return [];\n }\n\n const results: ProcessedBalance[] = [];\n\n // Group addresses by chain ID\n const addressesByChain: Record<ChainIdHex, ChecksumAddress[]> = {};\n\n for (const caipAddr of addrs) {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressesByChain[chainId]) {\n addressesByChain[chainId] = [];\n }\n addressesByChain[chainId].push(checksumAddress);\n }\n\n // Process each supported chain\n for (const [chainId, addresses] of Object.entries(addressesByChain)) {\n const chainIdHex = chainId as ChainIdHex;\n\n // Only fetch staked balance on supported networks (mainnet and hoodi)\n if (\n ![\n SupportedStakedBalanceNetworks.mainnet,\n SupportedStakedBalanceNetworks.hoodi,\n ].includes(chainIdHex as SupportedStakedBalanceNetworks)\n ) {\n continue;\n }\n\n // Only fetch staked balance if contract address exists\n if (!(chainIdHex in STAKING_CONTRACT_ADDRESS_BY_CHAINID)) {\n continue;\n }\n\n const contractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n chainIdHex as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n const provider = this.#getProvider(chainIdHex);\n\n const abi = [\n {\n inputs: [\n { internalType: 'address', name: 'account', type: 'address' },\n ],\n name: 'getShares',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [\n { internalType: 'uint256', name: 'shares', type: 'uint256' },\n ],\n name: 'convertToAssets',\n outputs: [\n { internalType: 'uint256', name: 'assets', type: 'uint256' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ];\n\n try {\n const contract = new Contract(contractAddress, abi, provider);\n\n // Get shares for each address\n for (const address of addresses) {\n try {\n const shares = await safelyExecute(() =>\n contract.getShares(address),\n );\n\n if (shares && (shares as BigNumber).gt(0)) {\n // Convert shares to assets (actual staked ETH amount)\n const assets = await safelyExecute(() =>\n contract.convertToAssets(shares),\n );\n\n if (assets) {\n results.push({\n success: true,\n value: new BN((assets as BigNumber).toString()),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } else {\n // Return zero balance for accounts with no staked assets\n results.push({\n success: true,\n value: new BN('0'),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } catch (error) {\n // Log error and continue with next address\n console.error(\n `Error fetching staked balance for ${address}:`,\n error,\n );\n results.push({\n success: false,\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n }\n } catch (error) {\n console.error(\n `Error setting up staking contract for chain ${chainId}:`,\n error,\n );\n }\n }\n\n return results;\n }\n\n async #fetchBalances(addrs: CaipAccountAddress[]) {\n // If we have fewer than or equal to the batch size, make a single request\n if (addrs.length <= ACCOUNTS_API_BATCH_SIZE) {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: addrs },\n this.#platform,\n );\n return balances;\n }\n\n // Otherwise, batch the requests to respect the 50-element limit\n type BalanceData = Awaited<\n ReturnType<typeof fetchMultiChainBalancesV4>\n >['balances'][number];\n\n const allBalances = await reduceInBatchesSerially<\n CaipAccountAddress,\n BalanceData[]\n >({\n values: addrs,\n batchSize: ACCOUNTS_API_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: batch },\n this.#platform,\n );\n return [...(workingResult || []), ...balances];\n },\n initialResult: [],\n });\n\n return allBalances;\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n const caipAddrs: CaipAccountAddress[] = [];\n\n for (const chainId of chainIds.filter((c) => this.supports(c))) {\n if (queryAllAccounts) {\n allAccounts.forEach((a) =>\n caipAddrs.push(toCaipAccount(chainId, a.address as ChecksumAddress)),\n );\n } else {\n caipAddrs.push(toCaipAccount(chainId, selectedAccount));\n }\n }\n\n if (!caipAddrs.length) {\n return [];\n }\n\n const [balances, stakedBalances] = await Promise.all([\n safelyExecute(() => this.#fetchBalances(caipAddrs)),\n this.#fetchStakedBalances(caipAddrs),\n ]);\n\n const results: ProcessedBalance[] = [];\n\n // Collect all unique addresses and chains from the CAIP addresses\n const addressChainMap = new Map<string, Set<ChainIdHex>>();\n caipAddrs.forEach((caipAddr) => {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressChainMap.has(checksumAddress)) {\n addressChainMap.set(checksumAddress, new Set());\n }\n addressChainMap.get(checksumAddress)?.add(chainId);\n });\n\n // Ensure native token entries exist for all addresses on all requested chains\n const ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n const nativeBalancesFromAPI = new Map<string, BN>(); // key: `${address}-${chainId}`\n\n // Process regular API balances\n if (balances) {\n const apiBalances = balances.flatMap((b) => {\n const account = b.accountAddress?.split(':')[2] as ChecksumAddress;\n if (!account) {\n return [];\n }\n const token = checksum(b.address);\n const chainId = toHex(b.chainId) as ChainIdHex;\n\n let value: BN | undefined;\n try {\n value = new BN((parseFloat(b.balance) * 10 ** b.decimals).toFixed(0));\n } catch {\n value = undefined;\n }\n\n // Track native balances for later\n if (token === ZERO_ADDRESS && value !== undefined) {\n nativeBalancesFromAPI.set(`${account}-${chainId}`, value);\n }\n\n return [\n {\n success: value !== undefined,\n value,\n account,\n token,\n chainId,\n },\n ];\n });\n results.push(...apiBalances);\n }\n\n // Ensure native token entries exist for all addresses/chains, even if not returned by API\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n const key = `${address}-${chainId}`;\n const existingBalance = nativeBalancesFromAPI.get(key);\n\n if (!existingBalance) {\n // Add zero native balance entry if API didn't return one\n results.push({\n success: true,\n value: new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n }\n });\n });\n\n // Add staked balances\n results.push(...stakedBalances);\n\n return results;\n }\n}\n"]}
1
+ {"version":3,"file":"api-balance-fetcher.mjs","sourceRoot":"","sources":["../../src/multi-chain-accounts-service/api-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BigNumber } from '@ethersproject/bignumber';\nimport { Contract } from '@ethersproject/contracts';\nimport type { Web3Provider } from '@ethersproject/providers';\nimport {\n safelyExecute,\n toHex,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { CaipAccountAddress, Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { fetchMultiChainBalancesV4 } from './multi-chain-accounts';\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport {\n accountAddressToCaipReference,\n reduceInBatchesSerially,\n SupportedStakedBalanceNetworks,\n} from '../assetsUtil';\nimport { SUPPORTED_NETWORKS_ACCOUNTS_API_V4 } from '../constants';\n\n// Maximum number of account addresses that can be sent to the accounts API in a single request\nconst ACCOUNTS_API_BATCH_SIZE = 50;\n\nexport type ChainIdHex = Hex;\nexport type ChecksumAddress = Hex;\n\nexport type ProcessedBalance = {\n success: boolean;\n value?: BN;\n account: ChecksumAddress;\n token: ChecksumAddress;\n chainId: ChainIdHex;\n};\n\nexport type BalanceFetcher = {\n supports(chainId: ChainIdHex): boolean;\n fetch(input: {\n chainIds: ChainIdHex[];\n queryAllAccounts: boolean;\n selectedAccount: ChecksumAddress;\n allAccounts: InternalAccount[];\n }): Promise<ProcessedBalance[]>;\n};\n\nconst checksum = (addr: string): ChecksumAddress =>\n toChecksumHexAddress(addr) as ChecksumAddress;\n\nconst toCaipAccount = (\n chainId: ChainIdHex,\n account: ChecksumAddress,\n): CaipAccountAddress => accountAddressToCaipReference(chainId, account);\n\nexport type GetProviderFunction = (chainId: ChainIdHex) => Web3Provider;\n\nexport class AccountsApiBalanceFetcher implements BalanceFetcher {\n readonly #platform: 'extension' | 'mobile' = 'extension';\n\n readonly #getProvider?: GetProviderFunction;\n\n constructor(\n platform: 'extension' | 'mobile' = 'extension',\n getProvider?: GetProviderFunction,\n ) {\n this.#platform = platform;\n this.#getProvider = getProvider;\n }\n\n supports(chainId: ChainIdHex): boolean {\n return SUPPORTED_NETWORKS_ACCOUNTS_API_V4.includes(chainId);\n }\n\n async #fetchStakedBalances(\n addrs: CaipAccountAddress[],\n ): Promise<ProcessedBalance[]> {\n // Return empty array if no provider is available for blockchain calls\n if (!this.#getProvider) {\n return [];\n }\n\n const results: ProcessedBalance[] = [];\n\n // Group addresses by chain ID\n const addressesByChain: Record<ChainIdHex, ChecksumAddress[]> = {};\n\n for (const caipAddr of addrs) {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressesByChain[chainId]) {\n addressesByChain[chainId] = [];\n }\n addressesByChain[chainId].push(checksumAddress);\n }\n\n // Process each supported chain\n for (const [chainId, addresses] of Object.entries(addressesByChain)) {\n const chainIdHex = chainId as ChainIdHex;\n\n // Only fetch staked balance on supported networks (mainnet and hoodi)\n if (\n ![\n SupportedStakedBalanceNetworks.mainnet,\n SupportedStakedBalanceNetworks.hoodi,\n ].includes(chainIdHex as SupportedStakedBalanceNetworks)\n ) {\n continue;\n }\n\n // Only fetch staked balance if contract address exists\n if (!(chainIdHex in STAKING_CONTRACT_ADDRESS_BY_CHAINID)) {\n continue;\n }\n\n const contractAddress =\n STAKING_CONTRACT_ADDRESS_BY_CHAINID[\n chainIdHex as keyof typeof STAKING_CONTRACT_ADDRESS_BY_CHAINID\n ];\n const provider = this.#getProvider(chainIdHex);\n\n const abi = [\n {\n inputs: [\n { internalType: 'address', name: 'account', type: 'address' },\n ],\n name: 'getShares',\n outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],\n stateMutability: 'view',\n type: 'function',\n },\n {\n inputs: [\n { internalType: 'uint256', name: 'shares', type: 'uint256' },\n ],\n name: 'convertToAssets',\n outputs: [\n { internalType: 'uint256', name: 'assets', type: 'uint256' },\n ],\n stateMutability: 'view',\n type: 'function',\n },\n ];\n\n try {\n const contract = new Contract(contractAddress, abi, provider);\n\n // Get shares for each address\n for (const address of addresses) {\n try {\n const shares = await safelyExecute(() =>\n contract.getShares(address),\n );\n\n if (shares && (shares as BigNumber).gt(0)) {\n // Convert shares to assets (actual staked ETH amount)\n const assets = await safelyExecute(() =>\n contract.convertToAssets(shares),\n );\n\n if (assets) {\n results.push({\n success: true,\n value: new BN((assets as BigNumber).toString()),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } else {\n // Return zero balance for accounts with no staked assets\n results.push({\n success: true,\n value: new BN('0'),\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n } catch (error) {\n // Log error and continue with next address\n console.error(\n `Error fetching staked balance for ${address}:`,\n error,\n );\n results.push({\n success: false,\n account: address,\n token: checksum(contractAddress) as ChecksumAddress,\n chainId: chainIdHex,\n });\n }\n }\n } catch (error) {\n console.error(\n `Error setting up staking contract for chain ${chainId}:`,\n error,\n );\n }\n }\n\n return results;\n }\n\n async #fetchBalances(addrs: CaipAccountAddress[]) {\n // If we have fewer than or equal to the batch size, make a single request\n if (addrs.length <= ACCOUNTS_API_BATCH_SIZE) {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: addrs },\n this.#platform,\n );\n return balances;\n }\n\n // Otherwise, batch the requests to respect the 50-element limit\n type BalanceData = Awaited<\n ReturnType<typeof fetchMultiChainBalancesV4>\n >['balances'][number];\n\n const allBalances = await reduceInBatchesSerially<\n CaipAccountAddress,\n BalanceData[]\n >({\n values: addrs,\n batchSize: ACCOUNTS_API_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const { balances } = await fetchMultiChainBalancesV4(\n { accountAddresses: batch },\n this.#platform,\n );\n return [...(workingResult || []), ...balances];\n },\n initialResult: [],\n });\n\n return allBalances;\n }\n\n async fetch({\n chainIds,\n queryAllAccounts,\n selectedAccount,\n allAccounts,\n }: Parameters<BalanceFetcher['fetch']>[0]): Promise<ProcessedBalance[]> {\n const caipAddrs: CaipAccountAddress[] = [];\n\n for (const chainId of chainIds.filter((c) => this.supports(c))) {\n if (queryAllAccounts) {\n allAccounts.forEach((a) =>\n caipAddrs.push(toCaipAccount(chainId, a.address as ChecksumAddress)),\n );\n } else {\n caipAddrs.push(toCaipAccount(chainId, selectedAccount));\n }\n }\n\n if (!caipAddrs.length) {\n return [];\n }\n\n // Don't use safelyExecute here - let real errors propagate\n let balances;\n let apiError = false;\n\n try {\n balances = await this.#fetchBalances(caipAddrs);\n } catch (error) {\n // Mark that we had an API error so we don't add fake zero balances\n apiError = true;\n console.error('Failed to fetch balances from API:', error);\n balances = undefined;\n }\n\n const stakedBalances = await this.#fetchStakedBalances(caipAddrs);\n\n const results: ProcessedBalance[] = [];\n\n // Collect all unique addresses and chains from the CAIP addresses\n const addressChainMap = new Map<string, Set<ChainIdHex>>();\n caipAddrs.forEach((caipAddr) => {\n const [, chainRef, address] = caipAddr.split(':');\n const chainId = toHex(parseInt(chainRef, 10)) as ChainIdHex;\n const checksumAddress = checksum(address);\n\n if (!addressChainMap.has(checksumAddress)) {\n addressChainMap.set(checksumAddress, new Set());\n }\n addressChainMap.get(checksumAddress)?.add(chainId);\n });\n\n // Ensure native token entries exist for all addresses on all requested chains\n const ZERO_ADDRESS =\n '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n const nativeBalancesFromAPI = new Map<string, BN>(); // key: `${address}-${chainId}`\n\n // Process regular API balances\n if (balances) {\n const apiBalances = balances.flatMap((b) => {\n const account = b.accountAddress?.split(':')[2] as ChecksumAddress;\n if (!account) {\n return [];\n }\n const token = checksum(b.address);\n const chainId = toHex(b.chainId) as ChainIdHex;\n\n let value: BN | undefined;\n try {\n // Convert string balance to BN avoiding floating point precision issues\n const { balance: balanceStr, decimals } = b;\n\n // Split the balance string into integer and decimal parts\n const [integerPart = '0', decimalPart = ''] = balanceStr.split('.');\n\n // Pad or truncate decimal part to match token decimals\n const paddedDecimalPart = decimalPart\n .padEnd(decimals, '0')\n .slice(0, decimals);\n\n // Combine and create BN\n const fullIntegerStr = integerPart + paddedDecimalPart;\n value = new BN(fullIntegerStr);\n } catch {\n value = undefined;\n }\n\n // Track native balances for later\n if (token === ZERO_ADDRESS && value !== undefined) {\n nativeBalancesFromAPI.set(`${account}-${chainId}`, value);\n }\n\n return [\n {\n success: value !== undefined,\n value,\n account,\n token,\n chainId,\n },\n ];\n });\n results.push(...apiBalances);\n }\n\n // Only add zero native balance entries if API succeeded but didn't return balances\n // Don't add fake zero balances if the API failed entirely\n if (!apiError) {\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n const key = `${address}-${chainId}`;\n const existingBalance = nativeBalancesFromAPI.get(key);\n\n if (!existingBalance) {\n // Add zero native balance entry if API succeeded but didn't return one\n results.push({\n success: true,\n value: new BN('0'),\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n }\n });\n });\n } else {\n // If API failed, add error entries for all requested addresses/chains\n addressChainMap.forEach((chains, address) => {\n chains.forEach((chainId) => {\n results.push({\n success: false,\n account: address as ChecksumAddress,\n token: ZERO_ADDRESS,\n chainId,\n });\n });\n });\n }\n\n // Add staked balances\n results.push(...stakedBalances);\n\n // If we had an API error and no successful results, throw the error\n if (apiError && results.every((r) => !r.success)) {\n throw new Error('Failed to fetch any balance data due to API error');\n }\n\n return results;\n }\n}\n"]}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // From https://github.com/MetaMask/eth-token-tracker/blob/main/lib/util.js
3
+ // Ensures backwards compatibility with display formatting.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.stringifyBalanceWithDecimals = void 0;
6
+ /**
7
+ * @param balance - The balance to stringify as a decimal string
8
+ * @param decimals - The number of decimals of the balance
9
+ * @param balanceDecimals - The number of decimals to display
10
+ * @returns The stringified balance with the specified number of decimals
11
+ */
12
+ function stringifyBalanceWithDecimals(balance, decimals, balanceDecimals = 5) {
13
+ if (balance === 0n || decimals === 0) {
14
+ return balance.toString();
15
+ }
16
+ let bal = balance.toString();
17
+ let len = bal.length;
18
+ let decimalIndex = len - decimals;
19
+ let prefix = '';
20
+ if (decimalIndex <= 0) {
21
+ while (prefix.length <= decimalIndex * -1) {
22
+ prefix += '0';
23
+ len += 1;
24
+ }
25
+ bal = prefix + bal;
26
+ decimalIndex = 1;
27
+ }
28
+ const whole = bal.slice(0, len - decimals);
29
+ if (balanceDecimals === 0) {
30
+ return whole;
31
+ }
32
+ const fractional = bal.slice(decimalIndex, decimalIndex + balanceDecimals);
33
+ if (/0+$/u.test(fractional)) {
34
+ let withOnlySigZeroes = bal.slice(decimalIndex).replace(/0+$/u, '');
35
+ if (withOnlySigZeroes.length > 0) {
36
+ withOnlySigZeroes = `.${withOnlySigZeroes}`;
37
+ }
38
+ return `${whole}${withOnlySigZeroes}`;
39
+ }
40
+ return `${whole}.${fractional}`;
41
+ }
42
+ exports.stringifyBalanceWithDecimals = stringifyBalanceWithDecimals;
43
+ //# sourceMappingURL=stringify-balance.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify-balance.cjs","sourceRoot":"","sources":["../../src/selectors/stringify-balance.ts"],"names":[],"mappings":";AAAA,2EAA2E;AAC3E,2DAA2D;;;AAE3D;;;;;GAKG;AACH,SAAgB,4BAA4B,CAC1C,OAAe,EACf,QAAgB,EAChB,eAAe,GAAG,CAAC;IAEnB,IAAI,OAAO,KAAK,EAAE,IAAI,QAAQ,KAAK,CAAC,EAAE;QACpC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;KAC3B;IAED,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,IAAI,YAAY,GAAG,GAAG,GAAG,QAAQ,CAAC;IAClC,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,IAAI,YAAY,IAAI,CAAC,EAAE;QACrB,OAAO,MAAM,CAAC,MAAM,IAAI,YAAY,GAAG,CAAC,CAAC,EAAE;YACzC,MAAM,IAAI,GAAG,CAAC;YACd,GAAG,IAAI,CAAC,CAAC;SACV;QACD,GAAG,GAAG,MAAM,GAAG,GAAG,CAAC;QACnB,YAAY,GAAG,CAAC,CAAC;KAClB;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC;IAE3C,IAAI,eAAe,KAAK,CAAC,EAAE;QACzB,OAAO,KAAK,CAAC;KACd;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,eAAe,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE;QAC3B,IAAI,iBAAiB,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE;YAChC,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;SAC7C;QACD,OAAO,GAAG,KAAK,GAAG,iBAAiB,EAAE,CAAC;KACvC;IACD,OAAO,GAAG,KAAK,IAAI,UAAU,EAAE,CAAC;AAClC,CAAC;AAtCD,oEAsCC","sourcesContent":["// From https://github.com/MetaMask/eth-token-tracker/blob/main/lib/util.js\n// Ensures backwards compatibility with display formatting.\n\n/**\n * @param balance - The balance to stringify as a decimal string\n * @param decimals - The number of decimals of the balance\n * @param balanceDecimals - The number of decimals to display\n * @returns The stringified balance with the specified number of decimals\n */\nexport function stringifyBalanceWithDecimals(\n balance: bigint,\n decimals: number,\n balanceDecimals = 5,\n) {\n if (balance === 0n || decimals === 0) {\n return balance.toString();\n }\n\n let bal = balance.toString();\n let len = bal.length;\n let decimalIndex = len - decimals;\n let prefix = '';\n\n if (decimalIndex <= 0) {\n while (prefix.length <= decimalIndex * -1) {\n prefix += '0';\n len += 1;\n }\n bal = prefix + bal;\n decimalIndex = 1;\n }\n\n const whole = bal.slice(0, len - decimals);\n\n if (balanceDecimals === 0) {\n return whole;\n }\n\n const fractional = bal.slice(decimalIndex, decimalIndex + balanceDecimals);\n if (/0+$/u.test(fractional)) {\n let withOnlySigZeroes = bal.slice(decimalIndex).replace(/0+$/u, '');\n if (withOnlySigZeroes.length > 0) {\n withOnlySigZeroes = `.${withOnlySigZeroes}`;\n }\n return `${whole}${withOnlySigZeroes}`;\n }\n return `${whole}.${fractional}`;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param balance - The balance to stringify as a decimal string
3
+ * @param decimals - The number of decimals of the balance
4
+ * @param balanceDecimals - The number of decimals to display
5
+ * @returns The stringified balance with the specified number of decimals
6
+ */
7
+ export declare function stringifyBalanceWithDecimals(balance: bigint, decimals: number, balanceDecimals?: number): string;
8
+ //# sourceMappingURL=stringify-balance.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify-balance.d.cts","sourceRoot":"","sources":["../../src/selectors/stringify-balance.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,eAAe,SAAI,UAmCpB"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param balance - The balance to stringify as a decimal string
3
+ * @param decimals - The number of decimals of the balance
4
+ * @param balanceDecimals - The number of decimals to display
5
+ * @returns The stringified balance with the specified number of decimals
6
+ */
7
+ export declare function stringifyBalanceWithDecimals(balance: bigint, decimals: number, balanceDecimals?: number): string;
8
+ //# sourceMappingURL=stringify-balance.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify-balance.d.mts","sourceRoot":"","sources":["../../src/selectors/stringify-balance.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,eAAe,SAAI,UAmCpB"}
@@ -0,0 +1,39 @@
1
+ // From https://github.com/MetaMask/eth-token-tracker/blob/main/lib/util.js
2
+ // Ensures backwards compatibility with display formatting.
3
+ /**
4
+ * @param balance - The balance to stringify as a decimal string
5
+ * @param decimals - The number of decimals of the balance
6
+ * @param balanceDecimals - The number of decimals to display
7
+ * @returns The stringified balance with the specified number of decimals
8
+ */
9
+ export function stringifyBalanceWithDecimals(balance, decimals, balanceDecimals = 5) {
10
+ if (balance === 0n || decimals === 0) {
11
+ return balance.toString();
12
+ }
13
+ let bal = balance.toString();
14
+ let len = bal.length;
15
+ let decimalIndex = len - decimals;
16
+ let prefix = '';
17
+ if (decimalIndex <= 0) {
18
+ while (prefix.length <= decimalIndex * -1) {
19
+ prefix += '0';
20
+ len += 1;
21
+ }
22
+ bal = prefix + bal;
23
+ decimalIndex = 1;
24
+ }
25
+ const whole = bal.slice(0, len - decimals);
26
+ if (balanceDecimals === 0) {
27
+ return whole;
28
+ }
29
+ const fractional = bal.slice(decimalIndex, decimalIndex + balanceDecimals);
30
+ if (/0+$/u.test(fractional)) {
31
+ let withOnlySigZeroes = bal.slice(decimalIndex).replace(/0+$/u, '');
32
+ if (withOnlySigZeroes.length > 0) {
33
+ withOnlySigZeroes = `.${withOnlySigZeroes}`;
34
+ }
35
+ return `${whole}${withOnlySigZeroes}`;
36
+ }
37
+ return `${whole}.${fractional}`;
38
+ }
39
+ //# sourceMappingURL=stringify-balance.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify-balance.mjs","sourceRoot":"","sources":["../../src/selectors/stringify-balance.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2DAA2D;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAe,EACf,QAAgB,EAChB,eAAe,GAAG,CAAC;IAEnB,IAAI,OAAO,KAAK,EAAE,IAAI,QAAQ,KAAK,CAAC,EAAE;QACpC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;KAC3B;IAED,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC7B,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,IAAI,YAAY,GAAG,GAAG,GAAG,QAAQ,CAAC;IAClC,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,IAAI,YAAY,IAAI,CAAC,EAAE;QACrB,OAAO,MAAM,CAAC,MAAM,IAAI,YAAY,GAAG,CAAC,CAAC,EAAE;YACzC,MAAM,IAAI,GAAG,CAAC;YACd,GAAG,IAAI,CAAC,CAAC;SACV;QACD,GAAG,GAAG,MAAM,GAAG,GAAG,CAAC;QACnB,YAAY,GAAG,CAAC,CAAC;KAClB;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC;IAE3C,IAAI,eAAe,KAAK,CAAC,EAAE;QACzB,OAAO,KAAK,CAAC;KACd;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,YAAY,GAAG,eAAe,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE;QAC3B,IAAI,iBAAiB,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE;YAChC,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;SAC7C;QACD,OAAO,GAAG,KAAK,GAAG,iBAAiB,EAAE,CAAC;KACvC;IACD,OAAO,GAAG,KAAK,IAAI,UAAU,EAAE,CAAC;AAClC,CAAC","sourcesContent":["// From https://github.com/MetaMask/eth-token-tracker/blob/main/lib/util.js\n// Ensures backwards compatibility with display formatting.\n\n/**\n * @param balance - The balance to stringify as a decimal string\n * @param decimals - The number of decimals of the balance\n * @param balanceDecimals - The number of decimals to display\n * @returns The stringified balance with the specified number of decimals\n */\nexport function stringifyBalanceWithDecimals(\n balance: bigint,\n decimals: number,\n balanceDecimals = 5,\n) {\n if (balance === 0n || decimals === 0) {\n return balance.toString();\n }\n\n let bal = balance.toString();\n let len = bal.length;\n let decimalIndex = len - decimals;\n let prefix = '';\n\n if (decimalIndex <= 0) {\n while (prefix.length <= decimalIndex * -1) {\n prefix += '0';\n len += 1;\n }\n bal = prefix + bal;\n decimalIndex = 1;\n }\n\n const whole = bal.slice(0, len - decimals);\n\n if (balanceDecimals === 0) {\n return whole;\n }\n\n const fractional = bal.slice(decimalIndex, decimalIndex + balanceDecimals);\n if (/0+$/u.test(fractional)) {\n let withOnlySigZeroes = bal.slice(decimalIndex).replace(/0+$/u, '');\n if (withOnlySigZeroes.length > 0) {\n withOnlySigZeroes = `.${withOnlySigZeroes}`;\n }\n return `${whole}${withOnlySigZeroes}`;\n }\n return `${whole}.${fractional}`;\n}\n"]}