duly / web/lib/chains.js · raw
// 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;}