ID@agntn/chainsv0.5.0

Name a chain. Check an address.

Blockchains as classes, one file each: chain ID, CAIP-2, coin type, explorer. The spellings people actually type resolve to the same class, and every chain checks its own address by decoding it, not by counting characters. Nothing here touches a network.

Chains
36
14 EVM · 12 UTXO
Families
11
3 with a shared base
Decoded
36 of 36
36 check a txid too
Install$ pnpm add @agntn/chains
Callidentify("0x1f9840a8…01F984")14 of 36 accept · 0 unchecked
Address / ethereum

0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984

The Ethereum sample, run through every validator in the registry. 14 chains take the format. That narrows it to EVM, it doesn't say where the address is used.

accepts rejects
Address

matic, btc, arb. Same class every time

getChain("matic") hands you the Polygon class with everything on it, and create("polygon") does the same from the canonical key. Symbols stay out of the automatic index, six chains report ETH and the answer would depend on registration order. This file walks through 36 chains, every value read off the class in your browser.

  • Keys name the chain, not the ticker: ethereum, bsc, octra. Tickers are aliases
  • Display names round trip, so the name one tool prints resolves in the next call
  • getChain('') throws. A blank string is a mistake, not a request for Ethereum
Read Registry and aliases
Fileethereum.tsevm · read off the class

Resolve

import { getChain } from "@agntn/chains";// "eth" resolves to "ethereum"const chain = getChain("eth");chain.key;      // "ethereum"chain.name;     // "Ethereum"chain.symbol;   // "ETH", 18 decimalschain.chainId;  // "0x1"chain.caip2;    // "eip155:1"chain.bip44;    // 60chain.explorer; // "https://etherscan.io"

Decode the bytes, not count the characters

A 34-character window takes a Bitcoin address, a TRON address and half the typos in between. So the validators decode. The panel checks the current sample against its own chain, against a chain with another format, and with one character changed. Where the format carries a checksum the last row fails on it, and each chain's page says which checks stay unverified.

  • Base58 addresses are decoded: Solana wants 32 bytes, TRON 25 under 0x41, XRP its own alphabet
  • Base58Check, Bech32, Bech32m, CashAddr and EIP-55 checksums are all verified, so one character off fails
  • Rejected is an answer. InvalidAddressError carries the chain key and the address
Read How each format is checked
CallassertAddress("0x1f9840…F984")3 checks
Verdict / ethereum

accepted once, rejected 2×

evm / ETH
  1. create("ethereum").assertAddress(address)returned unchanged

    its own chain, Ethereum

  2. create("bitcoin").assertAddress(address)InvalidAddressError

    a chain with another format, Bitcoin

  3. create("ethereum").assertAddress(typo)InvalidAddressError

    one character off: 0x1f9840a8…4201F985

One address, every validator at once

An address of unknown origin gets run through the whole registry. One family lights up for most chains, EVM for the 0x address that 14 chains share, Move for the short 0x1 that Aptos and Sui both write. A match narrows the family. It doesn't say the address is used there, and the tool text says that out loud.

  • identify(address) partitions the registry: chains that accept the format, chains with no validator
  • An EVM address matches all 14 EVM chains. That's the honest answer, not a bug
  • No validator means unchecked, never a silent no. Right now every built-in chain has one
Read Identify an address
Callchains_address_identify("0x1f9…F984")14 of 36

Families

  • EVM14 / 14
  • UTXO0 / 12
  • Solana0 / 1
  • Stellar0 / 1
  • XRP Ledger0 / 1
  • Move0 / 2
  • TON0 / 1
  • TRON0 / 1
  • Octra0 / 1
  • Arweave0 / 1
  • Monero0 / 1

14 chains read this address as theirs. A format match narrows the family, it doesn't prove the address is used on any of them.

MCP · Pi · OMPsame partition as identify()

36 chains, 11 families

Each chain is a class in its own file with its own metadata. The family classes hold only what their members share: EVM and Move own the address format, everything else is declared per chain, coin type included. 12 UTXO chains, and a UTXO heritage doesn't mean Bitcoin's encoding: CashAddr, CIP-19, two version bytes on Decred, F4Jumble on Zcash. The page for a chain says which bytes are checked and which checksum is left alone.

Importimport { chains, create } from "@agntn/chains"

chains()36 chains · registry order
Chain ID or CAIP-2
EthereumethereumETH · 18EVM0x1coin 60
BasebaseETH · 18EVM0x2105coin 60
Arbitrum OnearbitrumETH · 18EVM0xa4b1coin 60
OptimismoptimismETH · 18EVM0xacoin 60
Polygon PoSpolygonPOL · 18EVM0x89coin 60
BNB ChainbscBNB · 18EVM0x38coin 60
Avalanche C-ChainavalancheAVAX · 18EVM0xa86acoin 60
Fantom OperafantomFTM · 18EVM0xfacoin 60
Gnosis ChaingnosisxDAI · 18EVM0x64coin 60
LinealineaETH · 18EVM0xe708coin 60
zkSync ErazksyncETH · 18EVM0x144coin 60
ScrollscrollETH · 18EVM0x82750coin 60
BerachainberachainBERA · 18EVM0x138decoin 60
BitcoinbitcoinBTC · 8UTXObip122:000000000019d6689c085ae165831e93coin 0
LitecoinlitecoinLTC · 8UTXObip122:12a765e31ffd4059bada1e25190f6e98coin 2
PepecoinpepecoinPEP · 8UTXObip122:37981c0c48b8d48965376c8a42ece9a0coin 3434
eCashecashXEC · 2UTXOnone registeredcoin 899
CardanocardanoADA · 6UTXOcip34:1-764824073coin 1815
SolanasolanaSOL · 9Solanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpcoin 501
StellarstellarXLM · 7Stellarstellar:pubnetcoin 148
XRP LedgerxrplXRP · 6XRP Ledgerxrpl:0coin 144
AptosaptosAPT · 8Moveaptos:1coin 637
SuisuiSUI · 9Movesui:mainnetcoin 784
TON (The Open Network)tonTON · 9TONton:-239coin 607
TRONtronTRX · 6TRONtron:0x2b6653dccoin 195
OctraoctraOCT · 6Octranone registeredno coin type
ArweavearweaveAR · 12Arweavearweave:7wIUcoin 472
MoneromoneroXMR · 12Moneromonero:418015bb9ae982a1975da7d79277c270coin 128
DecreddecredDCR · 8UTXOnone registeredcoin 42
ArcarcUSDC · 18EVM0x13b2coin 60
DogecoindogecoinDOGE · 8UTXObip122:1a91e3dace36e2be3bf030a65679fe82coin 3
Bitcoin CashbitcoincashBCH · 8UTXObip122:000000000000000000651ef99cb9fcbecoin 145
Bitcoin SVbitcoinsvBSV · 8UTXOnone registeredcoin 236
Bitcoin GoldbitcoingoldBTG · 8UTXOnone registeredcoin 156
DashdashDASH · 8UTXObip122:00000ffd590b1485b3caadc19b22e637coin 5
ZcashzcashZEC · 8UTXObip122:00040fe8ec8471911baa1db1266ea15dcoin 133
read from the registry in your browser / no networkgetChain("matic") → polygon

5 tools, three hosts

chains mcp serves the tools over stdio, the Pi and OMP extensions render them in the terminal. All three call the same executors, so they answer identically and a fix lands once. Absent fields say so out loud, bip44: none, because a missing coin type reads as not shown and invites a model to supply one from memory.

  • chains_lookup, chains_address_validate, chains_txid_validate, chains_address_identify, chains_list
  • The text carries the whole answer, and a miss names the keys that exist, so the next call has somewhere to go
  • A rejected address or txid is an answer, not a tool error. Only an unknown chain, or one with no txid check, sets isError
Read MCP, Pi and OMP
Callchains_lookup("eth")
Tool / EVM

Ethereum

The spelling a model typed, answered with every field and a plain none for what the chain doesn't have.

key
"ethereum"
symbol
ETH · 18 decimals
caip2
eip155:1
bip44
60
explorer
etherscan.io
MCP · Pi · OMPchains mcp · stdio

Extend Chain, call register

Every built-in is a concrete class extending the exported abstract Chain, or EVM when the format is Ethereum's. Yours is the same shape, one file. Throw InvalidAddressError with your key and the address, and identify treats your chain like any other. No plugin manifest.

  • A static key, the metadata fields, and assertAddress when the format is known
  • register(MyChain) puts it behind create, getChain, identify and has
  • Nothing registers itself on import. The registry is one list, so a bundler can drop what you never touch
Read Custom chains
Filenano.tsextends Chain
import { Chain, InvalidAddressError, register } from "@agntn/chains";import type { ChainKey, ChainType } from "@agntn/chains";const ADDRESS = /^nano_[13][13456789abcdefghijkmnopqrstuwxyz]{59}$/;class Nano extends Chain {  static readonly key = "nano" as ChainKey;  readonly type = "nano" as ChainType;  readonly name = "Nano";  readonly symbol = "XNO";  readonly explorer = "https://nanexplorer.com/nano";  override assertAddress(address: string): string {    if (!ADDRESS.test(address)) throw new InvalidAddressError(this.key, address);    return address;  }}register(Nano);
Startpnpm add @agntn/chainsNode.js 26 or newer

Start with one command

One install gives you the library, the chains CLI and the MCP server. Hand it whatever spelling you have and get the class back, with the checks that go with it.

  • PinPre-1.0, so pin exact versions.
  • FormatA green check means the string fits the format. Not that it exists.
  • OfflineNothing here talks to a node, an explorer or anything else.

First lookup

$ pnpm add @agntn/chainsimport { getChain, identify } from "@agntn/chains";const chain = getChain("btc");chain.caip2;  // "bip122:000000000019d6689c085ae165831e93"chain.assertAddress("bc1qw508…v8f3t4");  // returned unchangedidentify("bc1qw508…v8f3t4").matches.length;  // 1