Repository Public
1// Network + asset registry. Every identifier here is pinned and cross-checked.2// USDC addresses: Circle official page https://developers.circle.com/stablecoins/usdc-contract-addresses3// (read 2026-11-10). Token addresses live in tokens.ts with their sources; all are re-verified on-chain at API start4// (symbol/decimals/chainId/genesis) and a token whose on-chain metadata differs is disabled.5/** 'evm', 'btc' (all Bitcoin-style UTXO chains) and 'sol' are built in; any other value names a plugin in families.ts */6export type Family = string;7export type NetworkId = string;89export interface AssetDef {10 /** 'native' or the token contract / mint address */11 id: string;12 symbol: string;13 name: string;14 decimals: number;15 /** Solana only: which token program owns the mint (classic SPL Token unless stated) */16 program?: 'token' | 'token2022';17 /** what the contract's own symbol() returns when it is not `symbol` (e.g. "USD₮0", or "USDC" for a bridged USDC.e) */18 onchainSymbol?: string;19}2021export interface NetworkDef {22 id: NetworkId;23 family: Family;24 name: string;25 /**26 * EVM: decimal chain id. BTC-style: genesis block hash. SOL: genesis hash. Stellar: network passphrase. XRPL: hash of ledger 32570. TRON: genesis block id. Aptos: chain id.27 * EVM: decimal chain id. BTC-style: genesis block hash. SOL: genesis hash. Stellar: network passphrase. XRPL: hash of ledger 32570. TRON: genesis block id.28 * Cosmos SDK: the chain id (it is part of every signed document, so a signature is valid on that chain only).29 * EVM: decimal chain id. BTC-style: genesis block hash. SOL: genesis hash. Stellar: network passphrase. XRPL: hash of ledger 32570. TRON: genesis block id. Sui: genesis checkpoint digest.30 * EVM: decimal chain id. BTC-style: genesis block hash. SOL: genesis hash. Stellar: network passphrase. XRPL: hash of ledger 32570. TRON: genesis block id. NEAR: genesis block hash.31 * EVM: decimal chain id. BTC-style: genesis block hash. SOL: genesis hash. Stellar: network passphrase. XRPL: hash of ledger 32570. TRON: genesis block id. TON: masterchain zero-state root hash.32 * Bitcoin Cash shares its genesis block with Bitcoin, Bitcoin SV and eCash, so its reference is the hash of block 661648,33 * the first block after the last of those splits (November 2020; a Bitcoin Cash Node checkpoint), which exists on no other chain.34 */35 chainRef: string;36 native: AssetDef;37 tokens: AssetDef[];38 rpc: string[];39 explorerTx: string;40 explorerAddr: string;41 /** BIP-44 coin type used for key derivation (plugin and UTXO families) */42 coinType?: number;43 /** Bitcoin-style chains: address encoding and script kind */44 utxo?: {45 bech32?: string; pubKeyHash: number; scriptHash: number; kind: 'wpkh' | 'pkh'; dust: number;46 /** CashAddr prefix (Bitcoin Cash) */47 cashaddr?: string;48 /** bounds for the fee rate in base units per byte, for chains whose node estimate is not a market rate */49 feeRate?: { min: number; max: number };50 /** height of the block whose hash is `chainRef` (default 0 = genesis) */51 refHeight?: number;52 };53 /** Cosmos SDK chains: bech32 account prefix and the denom of the native coin (used for the transfer and the fee) */54 cosmos?: { bech32: string; denom: string };55 /** destination memo / tag support: free text or an unsigned 32-bit number */56 memo?: 'text' | 'uint32';57 /** minimum the account must hold to exist on-chain (shown on the receive screen), in whole units of the native coin */58 activation?: string;59 /** confirmations displayed as "enough" for small amounts (finality is checked per chain) */60 minConf: number;61}6263import { TOKENS } from './tokens.ts';6465/** Allowlisted tokens of one network, from the verified table in tokens.ts. */66const tokensOf = (network: string): AssetDef[] => TOKENS.flatMap((t) => {67 const d = t.deployments[network];68 return d ? [{ id: d.address, symbol: t.symbol, name: d.name ?? t.name, decimals: d.decimals ?? t.decimals, ...(d.program ? { program: d.program } : {}), ...(d.onchainSymbol ? { onchainSymbol: d.onchainSymbol } : {}) }] : [];69});70const eth: AssetDef = { id: 'native', symbol: 'ETH', name: 'Ether', decimals: 18 };7172const DEFS = {73 ethereum: {74 id: 'ethereum', family: 'evm', name: 'Ethereum', chainRef: '1', native: eth,75 tokens: [],76 rpc: ['https://ethereum-rpc.publicnode.com', 'https://eth.drpc.org'],77 explorerTx: 'https://etherscan.io/tx/', explorerAddr: 'https://etherscan.io/address/', minConf: 12,78 },79 base: {80 id: 'base', family: 'evm', name: 'Base', chainRef: '8453', native: eth,81 tokens: [],82 rpc: ['https://mainnet.base.org', 'https://base-rpc.publicnode.com'],83 explorerTx: 'https://basescan.org/tx/', explorerAddr: 'https://basescan.org/address/', minConf: 12,84 },85 arbitrum: {86 id: 'arbitrum', family: 'evm', name: 'Arbitrum One', chainRef: '42161', native: eth,87 tokens: [],88 rpc: ['https://arb1.arbitrum.io/rpc', 'https://arbitrum-one-rpc.publicnode.com'],89 explorerTx: 'https://arbiscan.io/tx/', explorerAddr: 'https://arbiscan.io/address/', minConf: 12,90 },91 polygon: {92 id: 'polygon', family: 'evm', name: 'Polygon PoS', chainRef: '137',93 native: { id: 'native', symbol: 'POL', name: 'POL', decimals: 18 },94 tokens: [],95 rpc: ['https://polygon-bor-rpc.publicnode.com', 'https://polygon.drpc.org'],96 explorerTx: 'https://polygonscan.com/tx/', explorerAddr: 'https://polygonscan.com/address/', minConf: 64,97 },98 optimism: {99 id: 'optimism', family: 'evm', name: 'Optimism', chainRef: '10', native: eth, tokens: [],100 rpc: ['https://mainnet.optimism.io', 'https://optimism-rpc.publicnode.com'],101 explorerTx: 'https://optimistic.etherscan.io/tx/', explorerAddr: 'https://optimistic.etherscan.io/address/', minConf: 12,102 },103 bsc: {104 id: 'bsc', family: 'evm', name: 'BNB Smart Chain', chainRef: '56',105 native: { id: 'native', symbol: 'BNB', name: 'BNB', decimals: 18 }, tokens: [],106 rpc: ['https://bsc-dataseed.bnbchain.org', 'https://bsc-rpc.publicnode.com'],107 explorerTx: 'https://bscscan.com/tx/', explorerAddr: 'https://bscscan.com/address/', minConf: 15,108 },109 avalanche: {110 id: 'avalanche', family: 'evm', name: 'Avalanche C-Chain', chainRef: '43114',111 native: { id: 'native', symbol: 'AVAX', name: 'Avalanche', decimals: 18 }, tokens: [],112 rpc: ['https://api.avax.network/ext/bc/C/rpc', 'https://avalanche-c-chain-rpc.publicnode.com'],113 explorerTx: 'https://snowtrace.io/tx/', explorerAddr: 'https://snowtrace.io/address/', minConf: 2,114 },115 hyperevm: {116 id: 'hyperevm', family: 'evm', name: 'HyperEVM', chainRef: '999',117 native: { id: 'native', symbol: 'HYPE', name: 'Hyperliquid', decimals: 18 }, tokens: [],118 rpc: ['https://rpc.hyperliquid.xyz/evm', 'https://hyperliquid.drpc.org'],119 explorerTx: 'https://hyperevmscan.io/tx/', explorerAddr: 'https://hyperevmscan.io/address/', minConf: 2,120 },121 bitcoin: {122 id: 'bitcoin', family: 'btc', name: 'Bitcoin',123 chainRef: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',124 native: { id: 'native', symbol: 'BTC', name: 'Bitcoin', decimals: 8 }, tokens: [], coinType: 0,125 utxo: { bech32: 'bc', pubKeyHash: 0x00, scriptHash: 0x05, kind: 'wpkh', dust: 546 },126 rpc: ['https://mempool.space/api', 'https://blockstream.info/api'],127 explorerTx: 'https://mempool.space/tx/', explorerAddr: 'https://mempool.space/address/', minConf: 6,128 },129 litecoin: {130 id: 'litecoin', family: 'btc', name: 'Litecoin',131 chainRef: '12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2',132 native: { id: 'native', symbol: 'LTC', name: 'Litecoin', decimals: 8 }, tokens: [], coinType: 2,133 utxo: { bech32: 'ltc', pubKeyHash: 0x30, scriptHash: 0x32, kind: 'wpkh', dust: 5460 },134 rpc: ['https://litecoinspace.org/api', 'blockbook:https://litecoin.atomicwallet.io/api/v2'],135 explorerTx: 'https://litecoinspace.org/tx/', explorerAddr: 'https://litecoinspace.org/address/', minConf: 6,136 },137 // Dust: Dogecoin Core's soft dust limit, 0.01 DOGE (doc/fee-recommendation.md, read 2026-11-10; an output below it costs an138 // extra 0.01 DOGE of fee, below 0.001 DOGE it is rejected). Fee: 0.01 DOGE/kB recommended = 1000 koinu per byte.139 dogecoin: {140 id: 'dogecoin', family: 'btc', name: 'Dogecoin',141 chainRef: '1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691',142 native: { id: 'native', symbol: 'DOGE', name: 'Dogecoin', decimals: 8 }, tokens: [], coinType: 3,143 utxo: { pubKeyHash: 0x1e, scriptHash: 0x16, kind: 'pkh', dust: 1_000_000, feeRate: { min: 1000, max: 10_000 } },144 rpc: ['blockbook:https://blockbook.doge.zelcore.io/api/v2', 'bitcore:https://api.bitcore.io/api/DOGE/mainnet'],145 explorerTx: 'https://blockbook.doge.zelcore.io/tx/', explorerAddr: 'https://blockbook.doge.zelcore.io/address/', minConf: 20,146 },147 bitcoincash: {148 id: 'bitcoincash', family: 'bch', name: 'Bitcoin Cash',149 chainRef: '0000000000000000029e471c41818d24b8b74c911071c4ef0b4a0509f9b5a8ce',150 native: { id: 'native', symbol: 'BCH', name: 'Bitcoin Cash', decimals: 8 }, tokens: [], coinType: 145,151 utxo: { cashaddr: 'bitcoincash', pubKeyHash: 0x00, scriptHash: 0x05, kind: 'pkh', dust: 546, feeRate: { min: 1.1, max: 20 }, refHeight: 661648 },152 rpc: ['blockbook:https://blockbook.bch.zelcore.io/api/v2', 'bitcore:https://api.bitcore.io/api/BCH/mainnet'],153 explorerTx: 'https://blockbook.bch.zelcore.io/tx/', explorerAddr: 'https://blockbook.bch.zelcore.io/address/', minConf: 6,154 },155 solana: {156 id: 'solana', family: 'sol', name: 'Solana',157 chainRef: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d',158 native: { id: 'native', symbol: 'SOL', name: 'Solana', decimals: 9 },159 tokens: [],160 rpc: ['https://api.mainnet-beta.solana.com', 'https://solana-rpc.publicnode.com'],161 explorerTx: 'https://solscan.io/tx/', explorerAddr: 'https://solscan.io/account/', minConf: 1,162 },163 // Identity = the network passphrase (its SHA-256 is the network id mixed into every signature). Sources: SDF Horizon and164 // LOBSTR's public Horizon (independent operator), both keyless; verified live 2026-11-10.165 stellar: {166 id: 'stellar', family: 'stellar', name: 'Stellar',167 chainRef: 'Public Global Stellar Network ; September 2015',168 native: { id: 'native', symbol: 'XLM', name: 'Stellar Lumens', decimals: 7 }, tokens: [], coinType: 148,169 memo: 'text', activation: '1',170 rpc: ['https://horizon.stellar.org', 'https://horizon.stellar.lobstr.co'],171 explorerTx: 'https://stellar.expert/explorer/public/tx/', explorerAddr: 'https://stellar.expert/explorer/public/account/', minConf: 1,172 },173 xrpl: {174 // chainRef: hash of ledger 32570, the earliest ledger of the main network that still exists (ledgers 1–32569 were lost);175 // every full-history server returns it, and no other network (testnet, devnet, sidechains) has it.176 id: 'xrpl', family: 'xrpl', name: 'XRP Ledger',177 chainRef: '4109C6F2045FC7EFF4CDE8F9905D19C28820D86304080FF886B299F0206E42B5',178 native: { id: 'native', symbol: 'XRP', name: 'XRP', decimals: 6 }, tokens: [], coinType: 144,179 memo: 'uint32', activation: '1', // base reserve 1 XRP (server_state reserve_base = 1000000 drops, read 2026-11-10)180 // two independent operators and two implementations: XRPL Labs / XRPL Commons cluster (rippled) and Ripple (Clio), both on port 443181 rpc: ['https://xrplcluster.com', 'https://s1.ripple.com'],182 explorerTx: 'https://livenet.xrpl.org/transactions/', explorerAddr: 'https://livenet.xrpl.org/accounts/', minConf: 1,183 },184 tron: {185 // chainRef = genesis block id. No `activation`: nothing stays locked on TRON (the sender pays 1 TRX to create the recipient).186 // rpc[0] (TronGrid) also serves the /v1 history index used to discover deposits; both are java-tron HTTP APIs.187 id: 'tron', family: 'tron', name: 'TRON',188 chainRef: '00000000000000001ebf88508a03865c71d452e25f4d51194196a1d22b6653dc',189 native: { id: 'native', symbol: 'TRX', name: 'TRON', decimals: 6 }, tokens: [], coinType: 195,190 rpc: ['https://api.trongrid.io', 'https://tron-rpc.publicnode.com'],191 explorerTx: 'https://tronscan.org/#/transaction/', explorerAddr: 'https://tronscan.org/#/address/', minConf: 19,192 },193 aptos: {194 // chainRef = the chain id (1 = mainnet), which is part of every signed transaction. Public full nodes prune history, so the195 // genesis transaction cannot be read; the adapter verifies the chain id from the node and from the on-chain 0x1::chain_id196 // resource, a pinned ledger checkpoint, and that both sources report the same hash for the same recent block.197 // Sources: Aptos Labs (keyless tier) and PublicNode (independent operator); verified live 2026-11-10.198 // rpc[0] also serves the keyless indexer (/v1/graphql) used only to discover deposits, each then verified on a full node.199 // No `activation`: an Aptos account needs no minimum balance (the sender pays the storage fee of a new recipient).200 id: 'aptos', family: 'aptos', name: 'Aptos', chainRef: '1',201 native: { id: 'native', symbol: 'APT', name: 'Aptos', decimals: 8 }, tokens: [], coinType: 637,202 rpc: ['https://api.mainnet.aptoslabs.com/v1', 'https://aptos-rest.publicnode.com/v1'],203 explorerTx: 'https://explorer.aptoslabs.com/txn/', explorerAddr: 'https://explorer.aptoslabs.com/account/', minConf: 1,204 },205 cosmos: {206 // chainRef = chain id, signed into every transaction (SignDoc.chain_id). Public REST nodes prune early history, so no207 // early block hash can be pinned: identity = chain id reported by both sources + both agreeing on a recent block hash.208 // No `activation`: an account is created by its first incoming transfer and nothing stays locked.209 // Sources: Allnodes PublicNode and Polkachu, independent operators, keyless LCD/REST; verified live 2026-11-10.210 id: 'cosmos', family: 'cosmos', name: 'Cosmos Hub', chainRef: 'cosmoshub-4',211 native: { id: 'native', symbol: 'ATOM', name: 'Cosmos', decimals: 6 }, tokens: [], coinType: 118,212 cosmos: { bech32: 'cosmos', denom: 'uatom' }, memo: 'text',213 rpc: ['https://cosmos-rest.publicnode.com', 'https://cosmos-api.polkachu.com'],214 explorerTx: 'https://www.mintscan.io/cosmos/tx/', explorerAddr: 'https://www.mintscan.io/cosmos/address/', minConf: 1,215 },216 // chainRef = digest of the genesis checkpoint (base58); its first four bytes are the "chain identifier" 35834a8a. Every node217 // reports it as GetServiceInfo.chain_id. Sources: Mysten Labs full node and Triton One (rpcpool), independent operators, both218 // keyless, both speaking gRPC over HTTP/2 (sui.rpc.v2) — public Sui full nodes no longer serve JSON-RPC; verified live 2026-11-10.219 // No `activation`: a Sui address needs no minimum balance to exist.220 sui: {221 id: 'sui', family: 'sui', name: 'Sui',222 chainRef: '4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S',223 native: { id: 'native', symbol: 'SUI', name: 'Sui', decimals: 9 }, tokens: [], coinType: 784,224 rpc: ['https://fullnode.mainnet.sui.io', 'https://mainnet.sui.rpcpool.com'],225 explorerTx: 'https://suiscan.xyz/mainnet/tx/', explorerAddr: 'https://suiscan.xyz/mainnet/account/', minConf: 1,226 },227 near: {228 // chainRef = genesis block hash of mainnet (status.genesis_hash; the chain id string is "mainnet"). A NEAR transaction carries229 // no chain id: it is bound to the chain by the recent block hash it names, which the API reads from both sources.230 // The wallet's account is the implicit account of its key (64 hex characters); it exists on-chain once it has received NEAR.231 // No `activation`: an account this small needs no minimum balance (zero-balance accounts, NEP-448).232 // Sources: FastNEAR and dRPC (independent operators, different nodes; both keyless; verified live 2026-11-10).233 id: 'near', family: 'near', name: 'NEAR Protocol',234 chainRef: 'EPnLgE7iEq9s7yTkos96M3cWymH5avBAPm3qx3NXqR8H',235 native: { id: 'native', symbol: 'NEAR', name: 'NEAR', decimals: 24 }, tokens: [], coinType: 397,236 rpc: ['https://free.rpc.fastnear.com', 'https://near.drpc.org'],237 explorerTx: 'https://nearblocks.io/txns/', explorerAddr: 'https://nearblocks.io/address/', minConf: 3,238 },239 ton: {240 // chainRef = root hash of the masterchain zero state (hex), the block every main-network node starts from; testnet has another.241 // Sources: TON Center (liteserver-backed HTTP API of the TON Foundation) and TonAPI (Tonkeeper's indexer), both keyless and242 // rate-limited (about one request per second); verified live 2026-11-10. No `activation`: an address can hold any amount243 // before its wallet contract is deployed (the first outgoing transfer deploys it).244 id: 'ton', family: 'ton', name: 'TON',245 chainRef: '17a3a92992aabea785a7a090985a265cd31f323d849da51239737e321fb05569',246 native: { id: 'native', symbol: 'TON', name: 'Toncoin', decimals: 9 }, tokens: [], coinType: 607,247 memo: 'text',248 rpc: ['https://toncenter.com/api/v2', 'https://tonapi.io/v2'],249 explorerTx: 'https://tonviewer.com/transaction/', explorerAddr: 'https://tonviewer.com/', minConf: 1,250 },251} satisfies Record<string, NetworkDef>;252253/** Known ids are typed; any other string may be absent. */254export const NETWORKS = DEFS as { [K in keyof typeof DEFS]: NetworkDef } & Record<string, NetworkDef>;255256257for (const n of Object.values(NETWORKS) as NetworkDef[]) n.tokens = tokensOf(n.id);258259export const NETWORK_IDS = Object.keys(NETWORKS) as NetworkId[];260261export function getNetwork(id: string): NetworkDef {262 const n = (NETWORKS as Record<string, NetworkDef | undefined>)[id];263 if (!n) throw new Error(`unknown network: ${id}`);264 return n;265}266267/** Resolve an asset strictly by identifier (never by symbol/name). */268export function getAsset(network: string, assetId: string): AssetDef {269 const n = getNetwork(network);270 if (assetId === 'native') return n.native;271 const t = n.tokens.find((x) => (n.family === 'evm' ? x.id.toLowerCase() === assetId.toLowerCase() : x.id === assetId));272 if (!t) throw new Error(`asset not allowlisted on ${network}: ${assetId}`);273 return t;274}275