UNPKG

@shogun-sdk/money-legos

Version:

Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.

731 lines 31.3 kB
import { GraphQL } from '@codex-data/sdk'; import { AccountLayout, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token'; import { PublicKey } from '@solana/web3.js'; import { formatUnits } from 'viem'; import { arbitrum, base, berachain, avalanche, bsc, mainnet, sonic, polygon } from 'viem/chains'; import { CHAIN_CONFIGS, CHAIN_MAP, EVM_CHAIN_IDS, isEVMChain, isNativeAddress, SOLANA_CHAIN_ID, } from '../config/chains.js'; import { compareAddresses, compareChains } from '../utils/address.js'; import { NATIVE_TOKEN } from '../config/addresses.js'; import { getEvmJsonRpcProvider, getSolanaProvider } from '../utils/rpc.js'; import { erc20Abi } from '../config/index.js'; import { getNormalizedSolanaToken } from './solana.js'; import { HyperEVM } from '../utils/viemChains.custom.js'; const TokenRankingAttribute = GraphQL.TokenRankingAttribute; const RankingDirection = GraphQL.RankingDirection; export const getEVMWalletBalance = async (sdk, address) => { if (!address) return null; const CHAIN_NAMES = { 1: 'ethereum', 42161: 'arbitrum', 8453: 'base', 80094: 'berachain', 56: 'bsc', [sonic.id]: 'sonic', 43114: 'avalanche', 137: 'polygon', 999: 'hyperevm' // Add new chains here easily }; // Generate variables dynamically const variables = EVM_CHAIN_IDS.reduce((acc, chainId) => ({ ...acc, [`${CHAIN_NAMES[chainId]}Address`]: `${address}:${chainId}`, }), {}); return await sdk.send(`query Balances( $ethereumAddress: String! $arbitrumAddress: String! $baseAddress: String! $berachainAddress: String! $bscAddress: String! $sonicAddress: String! $avalancheAddress: String! $polygonAddress: String! $hyperevmAddress: String! ) { ethereum: balances(input: { walletId: $ethereumAddress }) { items { walletId tokenId balance shiftedBalance } } arbitrum: balances(input: { walletId: $arbitrumAddress }) { items { walletId tokenId balance shiftedBalance } } base: balances(input: { walletId: $baseAddress }) { items { walletId tokenId balance shiftedBalance } } berachain: balances(input: { walletId: $berachainAddress }) { items { walletId tokenId balance shiftedBalance } } bsc: balances(input: { walletId: $bscAddress }) { items { walletId tokenId balance shiftedBalance } } sonic: balances(input: { walletId: $sonicAddress }) { items { walletId tokenId balance shiftedBalance } } avalanche: balances(input: { walletId: $avalancheAddress }) { items { walletId tokenId balance shiftedBalance } } polygon: balances(input: { walletId: $polygonAddress }) { items { walletId tokenId balance shiftedBalance } } hyperevm: balances(input: { walletId: $hyperevmAddress }) { items { walletId tokenId balance shiftedBalance } } }`, variables); }; const getBalance = async ({ walletAddress, chainId }) => { const publicClient = getEvmJsonRpcProvider(chainId); // Fetch token data using multicall const balance = await publicClient.getBalance({ address: walletAddress, }); return { balance: balance || BigInt(0), }; }; export const getPortfolioTokens = async (sdk, data, walletAddress, fallbackTokens) => { const wrappedNativeAddresses = CHAIN_CONFIGS.filter((c) => c.isEVM).map((c) => c.wrapped); const chainIds = CHAIN_CONFIGS.filter((c) => c.isEVM).map((c) => c.id); const walletTokensFiltered = data.filter((token) => token.balance !== '0'); const walletTokensAddresses = [ ...new Set(data .map((token) => token.tokenId.split(':')[0]) .filter((address) => address !== 'native' && address !== 'nta')), ]; const safeFilterTokens = async () => { try { const tokens = [...walletTokensAddresses, ...wrappedNativeAddresses]; const chunks = []; for (let i = 0; i < tokens.length; i += 200) chunks.push(tokens.slice(i, i + 200)); const results = []; for (const chunk of chunks) { try { const res = await sdk.queries.filterTokens({ tokens: chunk, filters: { network: [...chainIds] }, limit: 200, }); if (res?.filterTokens?.results?.length) results.push(...res.filterTokens.results); } catch (err) { console.error("[filterTokens] Chunk fetch failed:", err); } } return { filterTokens: { results } }; } catch (err) { console.error("[filterTokens] Failed to fetch tokens:", err); return { filterTokens: { results: [] } }; } }; const [codexInfo, ethNativeBalance, baseNativeBalance, arbNativeBalance, berachainNativeBalance, bscinNativeBalance, sonicNativeBalance, avalancheNativeBalance, polygonNativeBalance, hyperevmNativeBalance,] = await Promise.all([ safeFilterTokens(), getBalance({ walletAddress, chainId: mainnet.id, }), getBalance({ walletAddress, chainId: base.id, }), getBalance({ walletAddress, chainId: arbitrum.id, }), getBalance({ walletAddress, chainId: berachain.id, }), getBalance({ walletAddress, chainId: bsc.id, }), getBalance({ walletAddress, chainId: sonic.id, }), getBalance({ walletAddress, chainId: CHAIN_MAP[avalanche.id]?.id ?? avalanche.id, }), getBalance({ walletAddress, chainId: polygon.id, }), getBalance({ walletAddress, chainId: HyperEVM.id, }), ]); if (!codexInfo?.filterTokens?.results || codexInfo.filterTokens.results.length === 0) { console.warn("[getBalances] No Codex token data found."); return []; } const tokenResults = codexInfo.filterTokens.results.filter((result) => result?.token !== null && result?.token !== undefined && typeof result.priceUSD === 'string' && typeof result.token.address === 'string' && typeof result.token.symbol === 'string' && typeof result.token.decimals === 'number'); let mappedTokenBalances = mapEvmTokenBalancesToInfo(walletTokensFiltered, tokenResults).filter(Boolean); // Add fallback tokens if they exist and not already in results if (fallbackTokens) { const fallbackTokensToAdd = []; for (const token of [fallbackTokens.tokenIn, fallbackTokens.tokenOut].filter((t) => !!t && isEVMChain(t.chainId) && !compareAddresses(t.address, NATIVE_TOKEN.ETH))) { if (!token?.address || !token.chainId) continue; // Check if token already exists in results const exists = mappedTokenBalances .filter((t) => !!t) .some((t) => compareAddresses(t.tokenAddress, token.address) && t.network && compareChains(t.network, token.chainId)); if (!exists) { try { const publicClient = getEvmJsonRpcProvider(token.chainId); // Fetch token data using multicall const [balance, decimals, symbol, name] = await publicClient.multicall({ // @ts-ignore contracts: [ { address: token.address, abi: erc20Abi, functionName: 'balanceOf', args: [walletAddress], }, { address: token.address, abi: erc20Abi, functionName: 'decimals', }, { address: token.address, abi: erc20Abi, functionName: 'symbol', }, { address: token.address, abi: erc20Abi, functionName: 'name', }, ], multicallAddress: token.chainId === berachain.id ? '0x8adce287716648df409faccbbecf76fe25c8fe22' : undefined, }); const tokenDecimals = decimals.result || token.decimals || 18; const balanceFormatted = formatUnits(balance.result || BigInt(0), tokenDecimals); fallbackTokensToAdd.push({ tokenAddress: token.address, symbol: symbol.result || token.symbol, name: name.result || token.name || symbol.result || token.symbol, decimals: tokenDecimals, balance: (balance.result || BigInt(0)).toString(), balanceFormatted, totalSupply: '', mcap: '', logo: token.image || `/tokens/${token.address.toLowerCase()}.svg`, usdPrice: 0, usdValue: 0, nativeToken: false, network: token.chainId.toString(), walletAddress, isFallbackToken: true, // Mark as fallback token }); } catch (err) { console.error(`Error fetching fallback token data for ${token.address}:`, err); } } } // Add fallback tokens to results mappedTokenBalances = [...mappedTokenBalances, ...fallbackTokensToAdd]; } const mappedNativeBalances = mapEvmNativeBalancesToInfo(ethNativeBalance.balance, arbNativeBalance.balance, baseNativeBalance.balance, berachainNativeBalance.balance, bscinNativeBalance.balance, sonicNativeBalance.balance, avalancheNativeBalance.balance, polygonNativeBalance.balance, hyperevmNativeBalance.balance, walletAddress, tokenResults); // Group tokens by network const tokensByNetwork = mappedTokenBalances.reduce((acc, token) => { if (!token?.network || !token.tokenAddress) return acc; if (!acc[token.network]) { acc[token.network] = []; } acc[token.network]?.push(token); return acc; }, {}); console.log('useRefetchSwapBalancesUntilDataChanges tokensByNetwork', tokensByNetwork); // Get updated balances for each network using multicall const updatedBalances = await Promise.all(Object.entries(tokensByNetwork).map(async ([network, tokens]) => { try { const publicClient = getEvmJsonRpcProvider(Number(network)); const balanceResults = await publicClient.multicall({ contracts: tokens .filter((t) => !!t) .map((token) => ({ address: token.tokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [walletAddress], })), multicallAddress: Number(network) === berachain.id ? '0x8adce287716648df409faccbbecf76fe25c8fe22' : undefined, }); return tokens .filter((t) => !!t) .map((token, i) => { try { if (balanceResults[i]?.error) { return token; } const balance = balanceResults[i]?.result; const balanceFormatted = formatUnits(BigInt(balance ?? 0), token.decimals || 18); return { ...token, balance: balance?.toString() ?? '0', balanceFormatted, usdValue: Number(balanceFormatted) * (token.usdPrice || 0), }; } catch (err) { return token; } }); } catch (err) { return tokens; } })); // Flatten the updated balances array mappedTokenBalances = updatedBalances.flat(); const tokens = [...mappedTokenBalances, ...mappedNativeBalances].map((token) => ({ ...token, symbol: token?.symbol ?? '', name: token?.name ?? '', decimals: token?.decimals ?? 0, })); const filteredTokens = filterTokensByValue(tokens); return filteredTokens; }; export const mapEvmTokenBalancesToInfo = (balances, tokenInfos) => { return balances .map((item) => { const [tokenAddress, network] = item.tokenId.split(':'); const walletAddress = item.walletId.split(':')[0]; const tokenInfo = tokenInfos.find((info) => compareAddresses(info?.token?.address, tokenAddress) && info?.token?.networkId === Number(network)); if (!tokenInfo) { return null; } const balanceFormatted = item.shiftedBalance; const usdPrice = Number(tokenInfo.priceUSD); const usdValue = balanceFormatted * usdPrice; return { tokenAddress: tokenAddress, symbol: tokenInfo?.token?.symbol, name: tokenInfo?.token?.name, decimals: tokenInfo?.token?.decimals, balance: item.balance, balanceFormatted: balanceFormatted.toString(), totalSupply: tokenInfo?.token?.info?.totalSupply ?? '', mcap: tokenInfo.marketCap ?? '', logo: tokenInfo?.token?.info?.imageSmallUrl ?? `/tokens/${tokenAddress?.toLowerCase()}.svg`, usdPrice, usdPrice1hrPercentChange: tokenInfo.change1 ? Number(tokenInfo.change1) * 100 : 0, usdPrice4hrPercentChange: tokenInfo.change4 ? Number(tokenInfo.change4) * 100 : 0, usdPrice12hrPercentChange: tokenInfo.change12 ? Number(tokenInfo.change12) * 100 : 0, usdPrice24hrPercentChange: tokenInfo.change24 ? Number(tokenInfo.change24) * 100 : 0, usdValue, nativeToken: false, network: network ?? tokenInfo.token?.networkId, walletAddress, }; }) .filter((result) => result?.balance !== null); }; export const mapEvmNativeBalancesToInfo = (ethBalance, arbBalance, baseBalance, berachainBalance, bscBalance, sonicBalance, avalancheBalance, polygonBalance, hyperevmBalance, walletAddress, tokenInfos) => { const balances = [ { balance: ethBalance, network: 1 }, { balance: arbBalance, network: 42161 }, { balance: baseBalance, network: 8453 }, { balance: berachainBalance, network: 80094 }, { balance: bscBalance, network: 56 }, { balance: sonicBalance, network: sonic.id }, { balance: avalancheBalance, network: avalanche.id }, { balance: polygonBalance, network: polygon.id }, { balance: hyperevmBalance, network: HyperEVM.id }, ]; return balances .map(({ balance, network }) => { const tokenInfo = tokenInfos.find((info) => compareAddresses(info?.token?.address, CHAIN_MAP[network]?.wrapped)); if (!tokenInfo) { return null; } const balanceFormatted = formatUnits(balance, tokenInfo?.token?.decimals || 18); const usdPrice = parseFloat(tokenInfo?.priceUSD || '0'); const usdValue = Number(balanceFormatted) * usdPrice; const currencyName = CHAIN_MAP[network]?.nativeCurrency?.name; return { tokenAddress: NATIVE_TOKEN.ETH, symbol: CHAIN_MAP[network]?.nativeCurrency?.symbol, name: currencyName, decimals: tokenInfo?.token?.decimals, balance: balance.toString(), balanceFormatted: balanceFormatted.toString(), totalSupply: tokenInfo?.token?.info?.totalSupply || '', mcap: tokenInfo.marketCap ?? '', logo: tokenInfo?.token?.info?.imageSmallUrl ?? `/tokens/${NATIVE_TOKEN.ETH.toLowerCase()}.svg`, usdPrice, usdPrice1hrPercentChange: tokenInfo.change1 ? Number(tokenInfo.change1) * 100 : 0, usdPrice4hrPercentChange: tokenInfo.change4 ? Number(tokenInfo.change4) * 100 : 0, usdPrice12hrPercentChange: tokenInfo.change12 ? Number(tokenInfo.change12) * 100 : 0, usdPrice24hrPercentChange: tokenInfo.change24 ? Number(tokenInfo.change24) * 100 : 0, usdValue, nativeToken: true, network: tokenInfo.token?.networkId ?? network, walletAddress, }; }) .filter((result) => result !== null); }; export const getSolanaTokenBalances = async (sdk, walletAddress) => { // --- 1️⃣ Input validation if (!walletAddress) { console.error("[getSolanaTokenBalances] Missing wallet address"); return []; } let solanaProvider; try { solanaProvider = await getSolanaProvider(); } catch (err) { console.error("[getSolanaTokenBalances] Failed to get Solana provider:", err); return []; } let rpcAccounts_1, rpcAccounts_2, nativeBalance; try { [rpcAccounts_1, rpcAccounts_2, nativeBalance] = await Promise.all([ solanaProvider.getTokenAccountsByOwner(new PublicKey(walletAddress), { programId: TOKEN_PROGRAM_ID, }), solanaProvider.getTokenAccountsByOwner(new PublicKey(walletAddress), { programId: TOKEN_2022_PROGRAM_ID, }), solanaProvider.getBalance(new PublicKey(walletAddress)), ]); } catch (err) { console.error("[getSolanaTokenBalances] Error fetching token accounts or balance:", err); return [createNativeSolToken(walletAddress, 0)]; } const rpcAccounts = [...(rpcAccounts_1?.value || []), ...(rpcAccounts_2?.value || [])]; if (!rpcAccounts.length) { console.warn("[getSolanaTokenBalances] No token accounts found for wallet:", walletAddress); return [createNativeSolToken(walletAddress, nativeBalance)]; } // --- 2️⃣ Decode and filter valid tokens const filteredRpcAccounts = []; rpcAccounts.forEach((account) => { try { const buffer = account.account.data; const accountInfo = AccountLayout.decode(buffer); const token = { mint: accountInfo.mint.toString(), amount: accountInfo.amount.toString(), }; if (token.amount !== "0") filteredRpcAccounts.push(token); } catch (err) { console.warn("[getSolanaTokenBalances] Failed to decode token account:", err); } }); const tokensAddresses = [ ...filteredRpcAccounts.map((token) => (!isPumpfun(token.mint) ? token.mint : "")).filter(Boolean), "So11111111111111111111111111111111111111112", ]; const pumpAddresses = filteredRpcAccounts .map((token) => (isPumpfun(token.mint) ? token.mint : "")) .filter(Boolean); const allAddresses = [...tokensAddresses, ...pumpAddresses]; if (!allAddresses.length) { console.warn("[getSolanaTokenBalances] No valid token addresses found."); return [createNativeSolToken(walletAddress, nativeBalance)]; } // --- 3️⃣ Split into chunks (max 200 tokens per query) const chunkSize = 200; const addressChunks = []; for (let i = 0; i < allAddresses.length; i += chunkSize) { addressChunks.push(allAddresses.slice(i, i + chunkSize)); } // --- 4️⃣ Fetch token metadata in chunks with try/catch const allResults = []; for (const chunk of addressChunks) { try { const response = await sdk.queries.filterTokens({ tokens: chunk, filters: { network: [1399811149] }, }); const results = response?.filterTokens?.results || []; if (results.length) allResults.push(...results); } catch (err) { console.error("[getSolanaTokenBalances] Error fetching filterTokens for chunk:", err?.message || err); // Continue with next chunk instead of failing everything continue; } } // --- 5️⃣ Handle empty result if (!allResults.length) { console.warn("[getSolanaTokenBalances] No Codex token data found."); return [ ...filteredRpcAccounts.map((token) => mapSolanaToken(token, walletAddress)), createNativeSolToken(walletAddress, nativeBalance), ]; } // --- 6️⃣ Normalize Codex tokens const codexTokens = allResults.map((t) => { if (t?.token?.info?.address === "So11111111111111111111111111111111111111112" && t?.token?.info?.symbol === "SOL") { t.token.info.symbol = "wSOL"; t.token.symbol = "wSOL"; } return t; }); // --- 7️⃣ Merge wallet tokens with Codex data const tokens = filteredRpcAccounts .map((token) => { const codexToken = codexTokens.find((ct) => compareAddresses(ct.token.address, token.mint)); return codexToken ? mapSolanaToken(token, walletAddress, false, codexToken) : null; }) .filter((t) => t !== null); // type-safe filter const codexNativeToken = codexTokens.find((ct) => compareAddresses(ct.token.address, "So11111111111111111111111111111111111111112")); if (codexNativeToken?.token) { codexNativeToken.token.address = "So11111111111111111111111111111111111111111"; } // --- 8️⃣ Add native SOL token tokens.push(createNativeSolToken(walletAddress, nativeBalance, codexNativeToken)); const filteredTokens = tokens.filter((t) => t !== null); // --- ✅ 9️⃣ Final safe return return filterTokensByValue(filteredTokens); }; export const createNativeSolToken = (walletAddress, nativeBalance, codexData) => { return mapSolanaToken({ mint: 'So11111111111111111111111111111111111111111', amount: nativeBalance?.toString() || '0', }, walletAddress, true, codexData); }; export const isValidSolanaOrEvMaddreess = (address) => { try { // EVM address validation (0x followed by 40 hexadecimal characters) const evmAddressRegex = /^0x[a-fA-F0-9]{40}$/; // Solana address validation (base58 string of length 32-44) const solanaAddressRegex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/; return evmAddressRegex.test(address) || solanaAddressRegex.test(address); } catch (error) { return false; } }; export const mapSolanaToken = (token, walletAddress, isNative = false, codexToken) => { const balanceFormatted = token.amount && codexToken?.token?.decimals ? formatUnits(BigInt(token.amount), codexToken.token.decimals) : '0'; return { network: SOLANA_CHAIN_ID, walletAddress, tokenAddress: codexToken?.token?.address || 'So11111111111111111111111111111111111111111', symbol: codexToken?.token?.address === 'So11111111111111111111111111111111111111111' ? 'SOL' : codexToken?.token?.symbol || 'SOL', name: codexToken?.token?.address === 'So11111111111111111111111111111111111111111' ? 'SOL' : codexToken?.token?.name || 'SOL', decimals: codexToken?.token?.decimals ? Number(codexToken.token.decimals) : 9, balance: token.amount || '0', balanceFormatted, usdPrice: codexToken?.priceUSD ? Number(codexToken.priceUSD) : 0, usdValue: balanceFormatted && codexToken?.priceUSD ? Number(balanceFormatted) * Number(codexToken.priceUSD) : 0, nativeToken: isNative, usdPrice1hrPercentChange: codexToken?.change1 ? Number(codexToken.change1) * 100 : 0, usdPrice4hrPercentChange: codexToken?.change4 ? Number(codexToken.change4) * 100 : 0, usdPrice12hrPercentChange: codexToken?.change12 ? Number(codexToken.change12) * 100 : 0, usdPrice24hrPercentChange: codexToken?.change24 ? Number(codexToken.change24) * 100 : 0, totalSupply: codexToken?.token?.info?.totalSupply || '', mcap: codexToken?.marketCap ?? '', logo: codexToken?.token?.info?.imageSmallUrl ?? `/tokens/${(codexToken?.token?.address || 'So11111111111111111111111111111111111111111')?.toLowerCase()}.svg`, }; }; export function isPumpfun(tokenAddress) { return tokenAddress.endsWith('pump'); } export const normalizeCodexSolanaChainId = (chainId) => { if (chainId === 1399811149) { return SOLANA_CHAIN_ID; } else { return chainId; } }; // Update filterTokensByValue to skip filtering for fallback tokens export const filterTokensByValue = (tokens) => { const [fallbackTokens, regularTokens] = tokens.reduce(([fall, reg], token) => { if (token.isFallbackToken) { fall.push(token); } else { reg.push(token); } return [fall, reg]; }, [[], []]); const filteredRegularTokens = regularTokens .filter((token) => Number(token.usdValue ?? 0) > Number(0.01)) .sort((a, b) => Number(b.usdValue || 0) - Number(a.usdValue || 0)); return [...fallbackTokens, ...filteredRegularTokens]; }; const priceCache = new Map(); const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes cache duration export const SOLANA_NETWORK_ID = 1399811149; export const getTokenUSDPrice = async (sdk, tokenAddress, chainId) => { try { tokenAddress = getNormalizedSolanaToken(tokenAddress); if (isNativeAddress(tokenAddress) && CHAIN_MAP[chainId]?.wrapped) { tokenAddress = CHAIN_MAP[chainId]?.wrapped; } // Create cache key combining address and chain const cacheKey = `${tokenAddress.toLowerCase()}-${chainId}`; const now = Date.now(); // Check cache const cached = priceCache.get(cacheKey); if (cached && now - cached.timestamp < CACHE_DURATION) { return cached.price; } // Convert Solana chain ID if needed const networkId = chainId === SOLANA_CHAIN_ID ? SOLANA_NETWORK_ID : chainId; // Fetch price const response = await sdk.queries.getTokenPrices({ inputs: [ { address: tokenAddress, networkId, }, ], }); // Find matching token const token = response.getTokenPrices?.find((t) => t?.address.toLowerCase() === tokenAddress.toLowerCase()) || null; // Update cache priceCache.set(cacheKey, { price: token, timestamp: now, }); return token; } catch (error) { console.error('Error fetching token price:', error); return null; } }; // Cache for storing top tokens data const topTokensCache = { data: null, timestamp: 0, }; const TOKEN_LIMIT = 100; const SUPPORTED_NETWORKS = [...EVM_CHAIN_IDS, SOLANA_NETWORK_ID]; export const getTopTokens = async (sdk) => { try { const now = Date.now(); // Return cached data if valid if (topTokensCache.data && now - topTokensCache.timestamp < CACHE_DURATION) { return topTokensCache.data; } // Fetch data for all networks in parallel const networkPromises = SUPPORTED_NETWORKS.map((networkId) => sdk.queries.listTopTokens({ limit: TOKEN_LIMIT, networkFilter: [networkId], })); const responses = await Promise.all(networkPromises); const filteredResponse = responses.map((response) => response.listTopTokens?.filter((token) => Number(token.liquidity) >= 50_000)); // Combine and map tokens from all networks const mappedTokens = filteredResponse.flatMap((response) => response?.map((token) => ({ symbol: token.symbol, address: token.address, decimals: token.decimals, chainId: normalizeCodexSolanaChainId(token.networkId), image: token.imageSmallUrl, name: token.name, mcap: token.marketCap, })) || []); // Remove duplicates based on address and chainId const uniqueTokens = Array.from(new Map(mappedTokens.map((token) => [`${token.address}-${token.chainId}`, token])).values()); // Update cache topTokensCache.data = uniqueTokens; topTokensCache.timestamp = now; return uniqueTokens; } catch (error) { console.error('Error fetching top tokens:', error); // Return cached data if available, even if expired if (topTokensCache.data) { return topTokensCache.data; } return []; } }; export const searchToken = async (sdk, symbolOrAddress) => { const isAddress = isValidSolanaOrEvMaddreess(symbolOrAddress); const searchParams = isAddress ? { tokens: [symbolOrAddress] } : { phrase: symbolOrAddress }; const additionalParams = !isAddress ? { liquidity: { gte: 50_000, }, volume24: { gte: 100, }, } : {}; const token = await sdk.queries.filterTokens({ ...searchParams, filters: { network: [...EVM_CHAIN_IDS, 1399811149], // Including both EVM chains and Solana ...additionalParams, }, rankings: { attribute: TokenRankingAttribute.MarketCap, direction: RankingDirection.Desc }, }); return (token.filterTokens?.results?.map((result) => ({ symbol: result?.token?.symbol, address: result?.token?.address, decimals: result?.token?.decimals, chainId: normalizeCodexSolanaChainId(result?.token?.networkId), image: result?.token?.info?.imageSmallUrl, name: result?.token?.name, mcap: result?.marketCap, })) || []); }; export const searchTokenByAddress = async (sdk, options) => { const { network, addresses } = options; const networkIds = network ?? [...EVM_CHAIN_IDS, SOLANA_NETWORK_ID]; const tokens = await sdk.queries.filterTokens({ tokens: addresses, limit: 20, filters: { network: networkIds, }, }); return tokens.filterTokens?.results; }; export const getTokenInfo = async (sdk, tokenAddress, chainId) => { const data = await sdk.queries.filterTokens({ tokens: [tokenAddress], filters: { network: [chainId], }, limit: 1, }); return data?.filterTokens?.results?.[0]; }; //# sourceMappingURL=codex.js.map