@anywalletkit/networks
@anywalletkit/networks
Section titled “@anywalletkit/networks”One table of networks for every wallet build: the catalog mainnets with their public RPC failover lists, the default testnets for every chain family, the networks and RPCs the user adds, and a verifier that proves an endpoint serves the network it claims. The mobile app and the extension kit read the same table.
Install
Section titled “Install”npm install @anywalletkit/networks# or: pnpm add @anywalletkit/networks · yarn add @anywalletkit/networksOn npm: @anywalletkit/networks, published from the release workflow with provenance.
Pure TypeScript, no runtime dependencies beyond @anywalletkit/swap2-assets (for isCatalogChain). Runs under Hermes (no URL, no atob, no structuredClone).
import { NetworksTable, verifyNetwork, parseAddChainParams, checkRpcUrl } from '@anywalletkit/networks'
const table = new NetworksTable(myJsonStorage) // AsyncStorage / chrome.storage / memoryJsonStorage()await table.load() // once at cold start; before it rpcUrlsForSync / usesUserRpcSync throw (not-loaded)
await table.addRpc('eip155:8453', 'https://my.base.rpc')await table.rpcUrlsFor('eip155:8453') // ['https://my.base.rpc', 'https://base-rpc.publicnode.com', …]await table.setOnlyOwnRpc('eip155:8453', true) // → ['https://my.base.rpc'] (no public fallback)table.rpcUrlsForSync('eip155:8453') // same, from memory — for a sync client factorytable.usesUserRpcSync('eip155:8453') // true → label the review sheet "Custom RPC — simulation not trusted"
const r = await verifyNetwork(await table.get('eip155:8453')!, { rpc: 'https://my.base.rpc' })// { ok: true, latencyMs: 212, tip: 52896400, reported: '8453' } | { ok: false, error: 'chain-id-mismatch: Endpoint serves chain 1, not 8453.', reported: '1', … }await table.markRpc('eip155:8453', 'https://my.base.rpc', r) // a failure parks the URL (skipped by rpcUrlsFor) with the reasoninterface Network { caip2: string // eip155:11155111, solana:EtWT…, bip122:<genesis>, ton:-3, tron:nile, near:testnet, zcash:testnet family: 'evm' | 'solana' | 'ton' | 'tron' | 'btc' | 'near' | 'zcash' name: string; symbol: string; decimals: number kind: 'catalog' | 'testnet' | 'custom' rpcs: string[] // tried in order explorer?: string source: 'builtin' | 'user' | { origin: string; addedAt: number } // a site-added network carries its origin onlyOwnRpc?: boolean // drop the public fallback balancesViaOwnRpc?: boolean enabled: boolean // testnets ship off}Rules the table enforces:
- a builtin entry (catalog mainnet or default testnet) cannot be removed; the user can only prepend RPCs and flip toggles — the shipped list stays as the fallback unless
onlyOwnRpc; - a custom network has no fallback by definition and needs at least one RPC; a stored custom entry for a builtin chain id is ignored (a site must not replace Ethereum’s endpoints);
rpcUrlsFor(caip2)= healthy user RPCs, then the builtin list (unlessonlyOwnRpcand at least one user RPC is healthy);markRpc(caip2, url, result)parks a failed user RPC with its error until it passes again or is removed — an endpoint that changes chain id under us is treated as poisoning, not as an outage;- only the deltas are persisted (
NetworksState: overrides per builtin, custom networks, issues, the http opt-in), so a shipped list update reaches every install.
URL policy (owner decision 2)
Section titled “URL policy (owner decision 2)”checkRpcUrl(url, { allowHttpLocal }): https:// anywhere; http:// only for private addresses — localhost, *.local, *.internal, *.home.arpa, *.lan, 0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16, 198.18/15, IPv4 written any way a URL parser reads it (127.1, 2130706433, 0x7f.0.0.1), any host that is not printable ASCII (a URL parser maps 127.0.0.1 to 127.0.0.1), IPv6 loopback / ULA / link-local / multicast and the IPv4 inside a mapped, NAT64 or 6to4 address — and only with allowHttpLocal (the Advanced toggle, stored in the table). No credentials in the URL, and no backslash, tab, CR/LF or other control character anywhere in it (a WHATWG parser ends the host at \ and drops tab/CR/LF, so the host fetch connects to would not be the one checked). parseAddChainParams never grants the local exception to a site.
isPrivateHost(host) is that test on its own, and isPublicHttpsUrl(url) the one for URLs a wallet fetches on someone else’s word (ENS gateways, avatars): https, no credentials, a public host. Both are also in @anywalletkit/networks/url, which does not load the catalog. A public name that resolves to a private address (DNS rebinding) is not caught by a host test.
verifyNetwork(network, { rpc?, fetch?, allowHttpLocal?, timeoutMs? })
Section titled “verifyNetwork(network, { rpc?, fetch?, allowHttpLocal?, timeoutMs? })”| family | identity (must match the CAIP-2 id) | tip |
|---|---|---|
| evm | eth_chainId |
eth_blockNumber |
| solana | getGenesisHash starts with the reference (solana:mainnet / devnet / testnet aliases resolve) |
getSlot |
| ton | config param 19 (global id, parsed from the BOC) = -239 / -3 |
getMasterchainInfo seqno |
| tron | genesis block id (wallet/getblockbynum 0) ends with the chain reference (728126428 ≙ 0x2b6653dc, nile ≙ 0xcd8690dc, shasta ≙ 0x94a9059e) |
wallet/getnowblock |
| btc | Esplora /block-height/0, Blockbook /api/v2/block-index/0 |
/blocks/tip/height / bestHeight |
| zcash | same, plus Blockchair dashboards/block/0 |
/stats blocks |
| near | status.chain_id |
latest_block_height |
Returns { ok, latencyMs, tip?, error?, reported? } and never throws. Errors are prefixed with a code: chain-id-mismatch, url-policy, redirected, http-<status>, rpc-error, bad-json, timeout, unreachable, no-rpc, bad-network. Redirects are refused (redirect: 'error', and an answer whose url / redirected / 3xx status shows a redirect is redirected: Endpoint redirected to <host>; enter the final URL): the URL policy judged the address the user or site gave, not where it points. A custom fetch that cannot refuse redirects should report url or redirected, or the redirected request goes out before the check sees it. http-<status> and bad-json carry no body text. Answer a site (wallet_addEthereumChain) with a generic error, not with error. verifyRpcs(network) checks every endpoint. scripts/verify-live.mjs runs the real thing against every builtin endpoint.
Builtin table
Section titled “Builtin table”CATALOG_NETWORKS — the 30 EVM mainnets of the asset catalog (Ethereum, Base, BNB, Arbitrum, Optimism, Polygon, Avalanche, HyperEVM, Robinhood, zkSync, Celo, MegaETH, Unichain, Monad, Plasma, Linea, Gnosis, Sonic, World Chain, Sei, Ink, Plume, X Layer, Scroll, Berachain, Arc, Mantle, Katana, Morph, Flare) with the public failover lists, plus Solana, TON, Tron, Bitcoin, NEAR and Zcash with public endpoints (the mobile app prepends its keyed proxies through the table).
DEFAULT_TESTNETS (shipped enabled: false; endpoints and identities called on 2026-10-21):
| Network | caip2 | endpoints |
|---|---|---|
| Sepolia | eip155:11155111 |
ethereum-sepolia-rpc.publicnode.com, sepolia.drpc.org (rpc.sepolia.org no longer answers) |
| Base Sepolia | eip155:84532 |
sepolia.base.org, base-sepolia-rpc.publicnode.com |
| Arbitrum Sepolia | eip155:421614 |
sepolia-rollup.arbitrum.io/rpc, arbitrum-sepolia-rpc.publicnode.com |
| OP Sepolia | eip155:11155420 |
sepolia.optimism.io, optimism-sepolia-rpc.publicnode.com |
| Polygon Amoy | eip155:80002 |
polygon-amoy-bor-rpc.publicnode.com, polygon-amoy.drpc.org (rpc-amoy.polygon.technology no longer answers) |
| BSC testnet | eip155:97 |
bsc-testnet-rpc.publicnode.com, data-seed-prebsc-1-s1.bnbchain.org:8545 |
| Monad testnet | eip155:10143 |
testnet-rpc.monad.xyz |
| Solana devnet | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 (wallet-solana CAIP2_GENESIS.devnet) |
api.devnet.solana.com |
| Bitcoin testnet4 | bip122:00000000da84f2bafbbc53dee25a72ae (genesis from mempool.space/testnet4) |
mempool.space/testnet4/api — wallet-btc‘s testnet option (tb1…, coin type 1’) serves it; the package’s CAIP2_GENESIS.testnet is testnet3 |
| TON testnet | ton:-3 |
testnet.toncenter.com/api/v2/jsonRPC (global id -3 confirmed from config param 19) |
| Tron Nile | tron:nile (wallet-tron also takes nile / tron:0xcd8690dc) |
nile.trongrid.io |
| NEAR testnet | near:testnet |
rpc.testnet.near.org, test.rpc.fastnear.com |
| Zcash testnet | zcash:testnet |
none — unverified. Blockchair has no zcash/testnet route and no other public API answered; the user supplies a Blockbook |
EIP-3085
Section titled “EIP-3085”parseAddChainParams(params, { known?, origin? }) turns a wallet_addEthereumChain request into a Network (kind: 'custom', source: { origin, addedAt }) or the EIP-1193 error to answer with (-32602 bad params, 4902 chain not in the catalog). https RPCs only; nativeCurrency.decimals must be 18. Pass known to accept chains outside the asset catalog.
Classes
Section titled “Classes”Interfaces
Section titled “Interfaces”- AddChainError
- ChainDescriptor
- FetchResponseLike
- JsonStorage
- Network
- NetworkEntry
- NetworkOverride
- NetworksState
- ParseAddChainOptions
- RpcIssue
- UrlPolicyOptions
- VerifyOptions
- VerifyResult
Type Aliases
Section titled “Type Aliases”Variables
Section titled “Variables”- ADD_CHAIN_ERRORS
- BTC_CHAIN
- BTC_ESPLORA
- BTC_TESTNET4_CHAIN
- BUILTIN_NETWORKS
- CATALOG_NETWORKS
- DEFAULT_TESTNETS
- EVM_MAINNETS
- NEAR_CHAIN
- NEAR_PROVIDERS
- NEAR_TESTNET_CHAIN
- NON_EVM_CHAINS
- NON_EVM_MAINNETS
- SOLANA_CHAIN
- SOLANA_DEVNET_CHAIN
- SOLANA_RPCS
- TON_CHAIN
- TON_ENDPOINTS
- TON_TESTNET_CHAIN
- TRON_CHAIN
- TRON_NILE_CHAIN
- TRON_PROVIDERS
- ZCASH_CHAIN
- ZCASH_PROVIDERS
- ZCASH_TESTNET_CHAIN