Registry and aliases
Five functions
import { chains, create, getChain, has, register } from "@agntn/chains";
chains(); // ["ethereum", "base", "arbitrum", ...] in registration order
has("solana"); // true
create("solana"); // a fresh Solana instance
getChain("sol"); // the same class, by alias
register(MyChain); // a class this package does not ship
create takes a canonical key and throws UnknownChainError for anything else. getChain is the forgiving one, and the one the CLI and the tools use.
What getChain accepts
Three spellings, tried in this order:
- The canonical key.
ethereum,bsc,zksync,octra. Lowercase, names the chain rather than its ticker. A short name is still a name, sobscandarbitrumstay keys. - An alias. The ticker spellings and nicknames a caller may already be holding:
eth,matic,pol,btc,arb,arb1,op,bnb,binance,avax,ftm,xdai,bera,ltc,pep,xec,ada,sol,xlm,xrp,ripple,apt,trx,oct,ar,xmr,dcr, and a few more likecoinbasefor Base andmainnetfor Ethereum. Each chain's page lists its own. - The display name.
Arbitrum One,BNB Chain,zkSync Era, read straight off the registered classes. That round trip matters for agents, which get a name out of one call and put it into the next.
Input is lowercased and trimmed first, so Matic and BTC work. Symbols aren't in the automatic index on purpose: six chains report ETH, and matching on it would make the answer depend on registration order. eth resolves only because it's in the alias table, pointing at Ethereum.
getChain("matic").key; // "polygon"
getChain("BNB Chain").key; // "bsc"
getChain("Arbitrum One").key; // "arbitrum"
getChain(); // Ethereum
getChain(""); // throws UnsupportedChainError
getChain() with no argument still means Ethereum. getChain("") or a blank string doesn't, that's a caller mistake, and it throws rather than quietly answering about the wrong chain. The error message quotes the input, so blank and control character input stays visible in a log.
Where the classes come from
Chain (abstract)
├── EVM (abstract)
│ ├── Ethereum
│ ├── Base
│ ├── Arbitrum
│ └── ...
├── Move (abstract)
│ ├── Aptos
│ └── Sui
├── Bitcoin
├── Solana
├── Stellar
├── Xrpl
├── Ton
├── Tron
├── Octra
├── Arweave
├── Monero
└── Decred
Each chain is its own class holding its own metadata. EVM and Move own the family type and the address format, and everything else is declared per class, down to the coin type all thirteen EVM chains repeat.
One list puts them in the registry: builtins in src/chains/index.ts. No module registers itself as it loads, so the CLI is the only entry that runs anything on import, and sideEffects in package.json says so, which lets a bundler drop whatever your project never touches. The cost is that a chain file counts for nothing until its class joins that list, and a test compares the two so it can't drift quietly.
Registering your own
register(ChainClass) puts a class under its static key. From then on create, getChain, has, chains() and identify see it like any built-in. Registering the same key again replaces the class. The whole recipe, including the two casts the closed key and type unions ask for, is in Custom chains.
Getting Started
Install the package. Resolve one chain. Check one address. Every chain is a class and every answer comes from the class that knows the format.
Address validation
assertAddress on every chain. What each family decodes and which checksums are verified and which are left alone. A format check and nothing more.