Guide

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.
Pre-1.0. The API and the tool list can still move. Pin exact versions if you build on it now.
A passing check is a format check. It says the string fits the chain's rules. It doesn't say the address exists, holds anything, or belongs to who you think. Where a format has a checksum, the chain's page says whether it's verified.

Why this exists

Every web3 library I write needs the same handful of facts: Polygon's chain ID, Bitcoin's coin type, which explorer to link, whether an address even looks right. Re-declared in every one of them, and slightly different each time. So they live here once, as classes, and everything else imports them.

The second reason is the address check. A regex that counts characters takes a Bitcoin address, a TRON address and half the typos in between. Here the validators decode: Solana wants 32 bytes behind the base58, TRON wants 25 under version 0x41, a bc1 address has to pass its Bech32 checksum. Rejected is an answer with the chain key on it.

Install

pnpm add @agntn/chains

First call

ethereum.ts
import { Ethereum, EVM, create, getChain } from "@agntn/chains";

const ethereum = create("ethereum");

ethereum instanceof Ethereum; // true
ethereum instanceof EVM; // true
ethereum.name; // "Ethereum"
ethereum.symbol; // "ETH"
ethereum.chainId; // "0x1"
ethereum.caip2; // "eip155:1"

// Aliases resolve to the same concrete classes.
const polygon = getChain("matic");
polygon.key; // "polygon"

// Validation lives on the class that knows the format.
ethereum.assertAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984");

create(key) wants the canonical key and builds a fresh instance. getChain(input) takes whatever spelling you're holding, a key, a display name or an alias like matic, and gives you the same class. With no argument it means Ethereum. With an empty string it throws, because a blank input is a mistake, not a request for the default.

What is on a chain

FieldTypeDescription
keyChainKeyCanonical class key, ethereum not eth
namestringDisplay name
symbolstringNative token symbol
decimalsnumber?Native currency decimal places
typeChainTypeFamily: evm, utxo, move, solana and so on
bip44number?BIP-44 / SLIP-0044 coin type
chainIdstring?EVM chain ID in hexadecimal
caip2string?CAIP-2 identifier
explorerstringBlock explorer base URL
rpcDefaultstring?Default public RPC endpoint

Optional fields stay empty when the chain has no registered value. Octra has no BIP-44 coin type and no CAIP-2 namespace, so both are undefined rather than invented. The full story of each field, decimals included, is in Metadata.

Errors

import { ChainsError, InvalidAddressError, UnsupportedChainError } from "@agntn/chains";

try {
  getChain("btc").assertAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5");
} catch (error) {
  if (error instanceof InvalidAddressError) {
    error.chain; // "bitcoin", the canonical key
    error.address; // the string that failed
  }
}

Everything thrown here descends from ChainsError, so you catch one type and read fields instead of parsing messages. UnknownChainError is create() with a key nobody registered, UnsupportedChainError is getChain() with a spelling that matches nothing, InvalidAddressError is a failed format check, and AddressValidationUnsupportedError is a chain that carries no validator at all. .chain holds the canonical key on the last two, the same value create() takes.

Next

@agntn/chains·MIT license· A format check, not proof an address exists. Nothing on this site talks to a chain.