{"version":3,"file":"rpc-balance-fetcher.mjs","sourceRoot":"","sources":["../../src/rpc-service/rpc-balance-fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Web3Provider } from '@ethersproject/providers';\nimport {\n  toChecksumHexAddress,\n  safelyExecuteWithTimeout,\n} from '@metamask/controller-utils';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { NetworkClient } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\nimport BN from 'bn.js';\n\nimport { STAKING_CONTRACT_ADDRESS_BY_CHAINID } from '../AssetsContractController';\nimport { shouldIncludeNativeToken } from '../constants';\nimport type { UnprocessedTokens } from '../multi-chain-accounts-service/api-balance-fetcher';\nimport { getTokenBalancesForMultipleAddresses } from '../multicall';\nimport type { TokensControllerState } from '../TokensController';\n\nconst RPC_TIMEOUT_MS = 30000;\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 BalanceFetchResult = {\n  balances: ProcessedBalance[];\n  unprocessedChainIds?: ChainIdHex[];\n  unprocessedTokens?: UnprocessedTokens;\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    unprocessedTokens?: UnprocessedTokens;\n  }): Promise<BalanceFetchResult>;\n};\n\nconst ZERO_ADDRESS =\n  '0x0000000000000000000000000000000000000000' as ChecksumAddress;\n\nconst checksum = (addr: string): ChecksumAddress =>\n  toChecksumHexAddress(addr) as ChecksumAddress;\n\nexport class RpcBalanceFetcher implements BalanceFetcher {\n  readonly #getProvider: (chainId: ChainIdHex) => Web3Provider;\n\n  readonly #getNetworkClient: (chainId: ChainIdHex) => NetworkClient;\n\n  readonly #getTokensState: () => {\n    allTokens: TokensControllerState['allTokens'];\n    allDetectedTokens: TokensControllerState['allDetectedTokens'];\n  };\n\n  constructor(\n    getProvider: (chainId: ChainIdHex) => Web3Provider,\n    getNetworkClient: (chainId: ChainIdHex) => NetworkClient,\n    getTokensState: () => {\n      allTokens: TokensControllerState['allTokens'];\n      allDetectedTokens: TokensControllerState['allDetectedTokens'];\n    },\n  ) {\n    this.#getProvider = getProvider;\n    this.#getNetworkClient = getNetworkClient;\n    this.#getTokensState = getTokensState;\n  }\n\n  supports(): boolean {\n    return true; // fallback – supports every chain\n  }\n\n  #getStakingContractAddress(chainId: ChainIdHex): string | undefined {\n    return STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId];\n  }\n\n  async fetch({\n    chainIds,\n    queryAllAccounts,\n    selectedAccount,\n    allAccounts,\n    unprocessedTokens,\n  }: Parameters<BalanceFetcher['fetch']>[0]): Promise<BalanceFetchResult> {\n    // Process all chains in parallel for better performance\n    const chainProcessingPromises = chainIds.map(async (chainId) => {\n      // if there are unprocessed tokens for a chain, it means the chain was partially processed.\n      // because of this, we need to build distinct account <-> token groups to process\n      const hasUnprocessedTokensForChain = queryAllAccounts\n        ? Object.values(unprocessedTokens ?? {}).some((chainMap) =>\n            Boolean(chainMap[chainId] && chainMap[chainId].length > 0),\n          )\n        : Boolean(\n            unprocessedTokens?.[selectedAccount.toLowerCase()]?.[chainId] &&\n            unprocessedTokens[selectedAccount.toLowerCase()][chainId].length >\n              0,\n          );\n\n      const tokensState = this.#getTokensState();\n      const { accountTokenGroups, includeNativeAndStaked } =\n        hasUnprocessedTokensForChain\n          ? buildUnprocessedAccountTokenGroupsStatic(\n              chainId,\n              queryAllAccounts,\n              selectedAccount,\n              unprocessedTokens as UnprocessedTokens,\n            )\n          : buildAccountTokenGroupsStatic(\n              chainId,\n              queryAllAccounts,\n              selectedAccount,\n              allAccounts,\n              tokensState.allTokens,\n              tokensState.allDetectedTokens,\n            );\n\n      if (!accountTokenGroups.length) {\n        return [];\n      }\n\n      const provider = this.#getProvider(chainId);\n      await this.#ensureFreshBlockData(chainId);\n\n      // Skip native token fetching for chains that return arbitrary large numbers\n      const includeNative = shouldIncludeNativeToken(chainId);\n\n      const balanceResult = await safelyExecuteWithTimeout(\n        async () => {\n          return await getTokenBalancesForMultipleAddresses(\n            accountTokenGroups,\n            chainId,\n            provider,\n            // Skip native for Tempo chains\n            includeNative && includeNativeAndStaked,\n            includeNativeAndStaked,\n          );\n        },\n        true,\n        RPC_TIMEOUT_MS,\n      );\n\n      // If timeout or error occurred, return empty array for this chain\n      if (!balanceResult) {\n        return [];\n      }\n\n      const { tokenBalances, stakedBalances } = balanceResult;\n      const chainResults: ProcessedBalance[] = [];\n\n      if (includeNative && includeNativeAndStaked) {\n        // Add native token entries for all addresses being processed\n        const allAddressesForNative = new Set<string>();\n        accountTokenGroups.forEach((group) => {\n          allAddressesForNative.add(group.accountAddress);\n        });\n\n        // Ensure native token entries exist for all addresses\n        allAddressesForNative.forEach((address) => {\n          const nativeBalance = tokenBalances[ZERO_ADDRESS]?.[address] || null;\n          chainResults.push({\n            success: true,\n            value: nativeBalance || new BN('0'),\n            account: address as ChecksumAddress,\n            token: ZERO_ADDRESS,\n            chainId,\n          });\n        });\n      }\n\n      // Add other token balances\n      Object.entries(tokenBalances).forEach(([tokenAddr, balances]) => {\n        // Skip native token since we handled it explicitly above\n        if (tokenAddr === ZERO_ADDRESS) {\n          return;\n        }\n        Object.entries(balances).forEach(([acct, bn]) => {\n          chainResults.push({\n            success: bn !== null,\n            value: bn,\n            account: acct as ChecksumAddress,\n            token: checksum(tokenAddr),\n            chainId,\n          });\n        });\n      });\n\n      // Add staked balances for all addresses being processed\n      const stakingContractAddress = this.#getStakingContractAddress(chainId);\n      if (includeNativeAndStaked && stakingContractAddress) {\n        // Get all unique addresses being processed for this chain\n        const allAddresses = new Set<string>();\n        accountTokenGroups.forEach((group) => {\n          allAddresses.add(group.accountAddress);\n        });\n\n        // Add staked balance entry for each address\n        const checksummedStakingAddress = checksum(stakingContractAddress);\n        allAddresses.forEach((address) => {\n          const stakedBalance = stakedBalances?.[address] ?? null;\n          chainResults.push({\n            success: true,\n            value: stakedBalance ?? new BN('0'),\n            account: address as ChecksumAddress,\n            token: checksummedStakingAddress,\n            chainId,\n          });\n        });\n      }\n\n      return chainResults;\n    });\n\n    // Wait for all chains to complete (or fail) and collect results\n    const chainResultsArray = await Promise.allSettled(chainProcessingPromises);\n    const results: ProcessedBalance[] = [];\n\n    chainResultsArray.forEach((chainResult) => {\n      if (chainResult.status === 'fulfilled') {\n        results.push(...chainResult.value);\n      }\n    });\n\n    return { balances: results };\n  }\n\n  /**\n   * Ensures that the block tracker has the latest block data before performing multicall operations.\n   * This is a temporary fix to ensure that the block number is up to date.\n   *\n   * @param chainId - The chain id to update block data for.\n   */\n  async #ensureFreshBlockData(chainId: Hex): Promise<void> {\n    // Force fresh block data before multicall\n    // TODO: This is a temporary fix to ensure that the block number is up to date.\n    // We should remove this once we have a better solution for this on the block tracker controller.\n    const networkClient = this.#getNetworkClient(chainId);\n    await networkClient.blockTracker?.checkForLatestBlock?.();\n  }\n}\n\ntype AccountTokenGroup = {\n  accountAddress: ChecksumAddress;\n  tokenAddresses: ChecksumAddress[];\n};\n\nfunction buildAccountTokenGroups(\n  queryAllAccounts: boolean,\n  selectedAccount: ChecksumAddress,\n  accountTokenMap: { [account: string]: string[] },\n): AccountTokenGroup[] {\n  const pairs: {\n    accountAddress: ChecksumAddress;\n    tokenAddress: ChecksumAddress;\n  }[] = [];\n\n  const add = ([account, tokens]: [string, string[]]): void => {\n    const checksumAccount = checksum(account);\n    const shouldInclude =\n      queryAllAccounts || checksumAccount === checksum(selectedAccount);\n    if (!shouldInclude) {\n      return;\n    }\n    tokens.forEach((token: string) =>\n      pairs.push({\n        accountAddress: account as ChecksumAddress,\n        tokenAddress: checksum(token),\n      }),\n    );\n  };\n\n  Object.entries(accountTokenMap).forEach(add);\n\n  // group by account\n  const map = new Map<ChecksumAddress, ChecksumAddress[]>();\n  pairs.forEach(({ accountAddress, tokenAddress }) => {\n    if (!map.has(accountAddress)) {\n      map.set(accountAddress, []);\n    }\n    const tokens = map.get(accountAddress);\n    if (tokens) {\n      tokens.push(tokenAddress);\n    }\n  });\n\n  return Array.from(map.entries()).map(([accountAddress, tokenAddresses]) => ({\n    accountAddress,\n    tokenAddresses,\n  }));\n}\n\n/**\n * Merges imported & detected tokens for the requested chain and returns a list\n * of `{ accountAddress, tokenAddresses[] }` suitable for getTokenBalancesForMultipleAddresses.\n *\n * @param chainId - The chain ID to build account token groups for\n * @param queryAllAccounts - Whether to query all accounts or just the selected one\n * @param selectedAccount - The currently selected account\n * @param allAccounts - All available accounts\n * @param allTokens - All tokens from TokensController\n * @param allDetectedTokens - All detected tokens from TokensController\n * @returns Array of account/token groups for multicall\n */\nfunction buildAccountTokenGroupsStatic(\n  chainId: ChainIdHex,\n  queryAllAccounts: boolean,\n  selectedAccount: ChecksumAddress,\n  allAccounts: InternalAccount[],\n  allTokens: TokensControllerState['allTokens'],\n  allDetectedTokens: TokensControllerState['allDetectedTokens'],\n): {\n  accountTokenGroups: AccountTokenGroup[];\n  includeNativeAndStaked: true;\n} {\n  const accountTokenMap: { [account: string]: string[] } = {};\n\n  // Add all tokens\n  Object.entries(allTokens[chainId] ?? {}).forEach(([account, tokens]) => {\n    accountTokenMap[account] = tokens.map((token) => token.address);\n  });\n\n  // Add all detected tokens\n  Object.entries(allDetectedTokens[chainId] ?? {}).forEach(\n    ([account, tokens]) => {\n      if (!accountTokenMap[account]) {\n        accountTokenMap[account] = [];\n      }\n      accountTokenMap[account] = Array.from(\n        new Set([\n          ...accountTokenMap[account],\n          ...tokens.map((token) => token.address),\n        ]),\n      );\n    },\n  );\n\n  // Add native tokens\n  if (queryAllAccounts) {\n    allAccounts.forEach((a) => {\n      accountTokenMap[a.address] ??= [];\n      accountTokenMap[a.address].push(ZERO_ADDRESS);\n    });\n  } else {\n    accountTokenMap[selectedAccount] ??= [];\n    accountTokenMap[selectedAccount].push(ZERO_ADDRESS);\n  }\n\n  return {\n    accountTokenGroups: buildAccountTokenGroups(\n      queryAllAccounts,\n      selectedAccount,\n      accountTokenMap,\n    ),\n    includeNativeAndStaked: true,\n  };\n}\n\nfunction buildUnprocessedAccountTokenGroupsStatic(\n  chainId: ChainIdHex,\n  queryAllAccounts: boolean,\n  selectedAccount: ChecksumAddress,\n  unprocessedTokens: UnprocessedTokens,\n): {\n  accountTokenGroups: AccountTokenGroup[];\n  includeNativeAndStaked: false;\n} {\n  const accountTokenMap: { [account: string]: string[] } = {};\n  Object.entries(unprocessedTokens).forEach(([account, tokens]) => {\n    const lowercaseAccount = account.toLowerCase();\n    if (\n      queryAllAccounts ||\n      lowercaseAccount === selectedAccount.toLowerCase()\n    ) {\n      const tokenAddresses =\n        tokens?.[chainId]?.map((tokenAddress) => tokenAddress.toLowerCase()) ??\n        [];\n      accountTokenMap[lowercaseAccount] = tokenAddresses;\n    }\n  });\n\n  return {\n    accountTokenGroups: buildAccountTokenGroups(\n      queryAllAccounts,\n      selectedAccount,\n      accountTokenMap,\n    ),\n    includeNativeAndStaked: false,\n  };\n}\n"]}