SPB Git

Repository Public

networks.ts 17.6 KB 275 lines typescript

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
Files main
Menu

Theme