SPB Git

Repository Public

address.ts 2.2 KB 46 lines typescript

1import { base58, bech32, bech32m } from '@scure/base';2import { getAddress, isAddress } from 'viem';3import { Address as BtcAddress } from '@scure/btc-signer';4import { pluginFor } from './families.ts';5import { btcParams } from './build-btc.ts';6import { getNetwork } from './networks.ts';78/** Returns the normalized address or throws. EVM → EIP-55 checksum form. */9export function normalizeAddress(network: string, addr: string): string {10  const n = getNetwork(network);11  const a = addr.trim();12  if (n.family === 'evm') {13    if (!/^0x[0-9a-fA-F]{40}$/.test(a)) throw new Error('invalid EVM address');14    // Mixed-case input must carry a valid checksum; all-lower / all-upper is accepted and checksummed.15    const body = a.slice(2);16    const mixed = body !== body.toLowerCase() && body !== body.toUpperCase();17    if (mixed && !isAddress(a, { strict: true })) throw new Error('bad EVM checksum');18    const out = getAddress(a.toLowerCase());19    if (out === '0x0000000000000000000000000000000000000000') throw new Error('zero address');20    return out;21  }22  if (n.family === 'btc') {23    // this network only; decode validates checksum, prefix/version and program length24    const d = BtcAddress(btcParams(n)).decode(a);25    if (!['wpkh', 'wsh', 'tr', 'pkh', 'sh'].includes(d.type)) throw new Error(`unsupported ${n.name} address type`);26    if (n.utxo?.kind === 'pkh' && !['pkh', 'sh'].includes(d.type)) throw new Error(`unsupported ${n.name} address type`);27    const hrp = n.utxo?.bech32;28    if (hrp && a.toLowerCase().startsWith(hrp + '1')) {29      if (a !== a.toLowerCase() && a !== a.toUpperCase()) throw new Error('mixed-case bech32');30      const lower = a.toLowerCase();31      (d.type === 'tr' ? bech32m : bech32).decode(lower as `${string}1${string}`);32      return lower;33    }34    return a;35  }36  if (n.family !== 'sol') return pluginFor(n).normalizeAddress(n, a);37  const raw = base58.decode(a);38  if (raw.length !== 32) throw new Error('invalid Solana address');39  if (base58.encode(raw) !== a) throw new Error('non-canonical Solana address');40  return a;41}4243export function isValidAddress(network: string, addr: string): boolean {44  try { normalizeAddress(network, addr); return true; } catch { return false; }45}46
Files main
Menu

Theme