// Friendly names and address checks for the chains 1Click lists. export const CHAIN_NAMES = { eth: 'Ethereum', base: 'Base', arb: 'Arbitrum', op: 'Optimism', pol: 'Polygon', bsc: 'BNB Chain', avax: 'Avalanche', sol: 'Solana', near: 'NEAR', tron: 'Tron', ton: 'TON', btc: 'Bitcoin', xrp: 'XRP Ledger', doge: 'Dogecoin', ltc: 'Litecoin', sui: 'Sui', aptos: 'Aptos', stellar: 'Stellar', gnosis: 'Gnosis', bera: 'Berachain', monad: 'Monad', zec: 'Zcash', bch: 'Bitcoin Cash', cardano: 'Cardano', starknet: 'Starknet', scroll: 'Scroll', xlayer: 'X Layer', plasma: 'Plasma', }; export const EVM_CHAIN_IDS = { eth: 1, base: 8453, arb: 42161, op: 10, pol: 137, bsc: 56, avax: 43114, gnosis: 100, scroll: 534352, bera: 80094, monad: 143, xlayer: 196, }; const POPULAR_SYMBOLS = ['USDC', 'USDT', 'ETH', 'SOL', 'BTC', 'NEAR']; const HOME_CHAIN = { BTC: 'btc', SOL: 'sol', XRP: 'xrp', DOGE: 'doge', LTC: 'ltc', TRX: 'tron', TON: 'ton' }; const POPULAR_CHAINS = ['base', 'arb', 'sol', 'eth', 'pol', 'bsc', 'op', 'tron', 'near', 'btc']; export function chainName(c) { return CHAIN_NAMES[c] || c; } export function addressOk(chain, a) { const s = String(a).trim(); if (chain in EVM_CHAIN_IDS) return /^0x[0-9a-fA-F]{40}$/.test(s); if (chain === 'sol') return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(s); if (chain === 'tron') return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(s); if (chain === 'btc') return /^(bc1[a-z0-9]{20,70}|[13][1-9A-HJ-NP-Za-km-z]{25,39})$/.test(s); if (chain === 'near') return /^[a-z0-9._-]{2,64}$/.test(s); return /^[A-Za-z0-9:._-]{20,100}$/.test(s); } /** Symbols in a sensible order, then the chains each can be paid from. */ export function groupTokens(tokens) { const bySymbol = new Map(); for (const t of tokens) { if (!t.assetId || !t.symbol || !t.blockchain) continue; if (!bySymbol.has(t.symbol)) bySymbol.set(t.symbol, []); bySymbol.get(t.symbol).push(t); } const rank = (s) => { const i = POPULAR_SYMBOLS.indexOf(s); return i === -1 ? 99 : i; }; const chainRank = (c) => { const i = POPULAR_CHAINS.indexOf(c); return i === -1 ? 99 : i; }; return [...bySymbol.entries()] .sort((a, b) => rank(a[0]) - rank(b[0]) || a[0].localeCompare(b[0])) .map(([symbol, list]) => ({ symbol, // A coin's home chain comes first: BTC on Bitcoin, not a wrapped copy elsewhere. options: list.sort( (a, b) => (b.blockchain === HOME_CHAIN[symbol]) - (a.blockchain === HOME_CHAIN[symbol]) || chainRank(a.blockchain) - chainRank(b.blockchain) || a.blockchain.localeCompare(b.blockchain), ), })); } /** "base:USDC:0xabc" as the contract stores it, or '' if it would not pass. */ export function refundTag(chain, symbol, address) { const tag = `${chain}:${symbol}:${address}`; return /^[A-Za-z0-9:._-]{1,80}$/.test(tag) ? tag : ''; } export function parseRefundTag(tag) { const [chain, symbol, ...rest] = String(tag).split(':'); const address = rest.join(':'); return chain && symbol && address ? { chain, symbol, address } : null; }