import { multicall } from '@wagmi/core' import { type Address, zeroAddress } from 'viem' import multicallABI from '../constants/abis/multicall.json' import erc20ABI from '../constants/abis/erc20.json' import { Token } from '@/utils/types' import { formatBigIntToString } from './bigint/format' import { TokenAndBalance } from './actions/fetchPortfolioBalances' import { CHAINS_BY_ID } from '@/constants/chains' import { wagmiConfig } from '@/wagmiConfig' export const sortByVisibilityRank = (tokens: Token[]) => { if (tokens === undefined) { return [] } return Object.values(tokens).sort( (a, b) => b.visibilityRank - a.visibilityRank ) } const sortArrayByBalance = (array) => { return array.sort((a, b) => { const balanceA = BigInt(a.balance || '') const balanceB = BigInt(b.balance || '') if (balanceA < balanceB) { return 1 } else if (balanceA > balanceB) { return -1 } else { return 0 } }) } export const sortByTokenBalance = async ( tokens: Token[], chainId: number, address: any ): Promise => { const tokensWithBalances: any[] = [] const multicallInputs = [] if (chainId === undefined || !address) { tokens.forEach((token) => { tokensWithBalances.push({ token, balance: 0n, }) }) } else { tokens.forEach((token) => { // deterministic multicall3 address on all eth chains const multicallAddress: Address = `0xcA11bde05977b3631167028862bE2a173976CA11` const tokenAddress = token?.addresses[chainId as keyof Token['addresses']] if (tokenAddress === zeroAddress || tokenAddress === undefined) { multicallInputs.push({ address: multicallAddress, abi: multicallABI, functionName: 'getEthBalance', chainId, args: [address], }) } else { multicallInputs.push({ address: tokenAddress, abi: erc20ABI, functionName: 'balanceOf', chainId, args: [address], }) } }) } let multicallData: any[] | any if (multicallInputs.length > 0) { // @ts-ignore multicallData = await multicall(wagmiConfig, { contracts: multicallInputs, chainId, }) return sortArrayByBalance( sortByVisibilityRank( multicallData.map( ( tokenBalance: { result: bigint; status: string } | undefined, index: number ) => ({ queriedChain: CHAINS_BY_ID[chainId], token: tokens[index], tokenAddress: tokens[index].addresses[chainId], balance: tokenBalance.result, parsedBalance: formatBigIntToString( tokenBalance.result, tokens[index]?.decimals[chainId], 4 ), }) ) ) ) } return tokensWithBalances } // Function to sort the tokens by priorityRank and alphabetically export const sortTokensByPriorityRankAndAlpha = (arr: Token[]): Token[] => { // Create a copy of the array to prevent modifying the original one const sortedArr = arr && [...arr] return sortedArr?.sort((a, b) => { // Sort by priorityRank first if (a.priorityRank !== b.priorityRank) { return a.priorityRank - b.priorityRank } // If priorityRank is the same, sort by symbol return a.symbol.localeCompare(b.symbol) }) } export const separateAndSortTokensWithBalances = ( tokensAndBalances: TokenAndBalance[] ): Token[] => { const hasTokensAndBalances = tokensAndBalances.length > 0 if (hasTokensAndBalances) { const tokensWithBalances = tokensAndBalances .filter((t) => !(t.balance === 0n)) .map((t) => t.token) const a = sortTokensByPriorityRankAndAlpha(tokensWithBalances) const tokensWithNoBalances = tokensAndBalances .filter((t) => t.balance === 0n) .map((t) => t.token) const b = sortTokensByPriorityRankAndAlpha(tokensWithNoBalances) return [...a, ...b] } else { return [] } }