One key. Every chain.

Typed key generation, address derivation, and message signing for ten blockchains. One interface in TypeScript, the same eighteen tools over MCP, and nothing ever leaves the process.

11
chains
2
curves
0
network calls
18
MCP tools

Derivation

Keys in, addresses out

A private key is an integer. Multiply it by the generator, hash the result, encode the hash, and you have an address. Every chain is the same three steps with different rules. The panel walks the keyspace live.

  • Both curves from @noble, nothing else underneath
  • Legacy, SegWit, Taproot, EIP-55, base58check, bech32
  • Same 32 bytes, ten chains, derived in this tab
Open the explorer

private key 1

Bitcoin · secp256k1

  1. 132-byte secretinteger in 1 … n − 1
    0000000000000000000000000000000000000000000000000000000000000001
  2. 2public keyk · G, compressed, 33 bytes
    0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
  3. 3addresshash, then encode
    • legacyP2PKH · base58check1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
    • segwitP2WPKH · bech32bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
    • taprootP2TR · bech32mbc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9
all chains in the explorer

One interface

Same calls, every chain

Every blockchain is a class with the same shape. Swap the import and the rest of the code stays. A chain that can't do what you asked throws, Decred on a segwit address, Cardano on an HD walk, instead of handing you something that only looks right.

  • generateWallet, getAddress, signMessage on every driver
  • Drivers load lazily, only the chains you use ship
  • Custom chains extend the same abstract class
Working with keys
TSbitcoin.ts
import { useBlockchain, blockchains } from "@agntn/keys";

const chain = useBlockchain(await blockchains.bitcoin()());
const wallet = chain.generateWallet();
const signature = chain.signMessage("hello", wallet.private);

// address of private key 1 on this chain
// bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

HD wallets

Mnemonic to wallet in one call

Walk a BIP39 mnemonic down a BIP44 path and get the wallet at the end of it. Paths are parsed and validated, so a hardened segment in the wrong place fails before derivation. Puzzle phrases with a bad checksum go through on request, words untouched.

  • BIP32 for secp256k1, SLIP-10 for ed25519
  • Bitcoin and Litecoin infer the address type from the purpose
  • Broken checksum? Derive anyway and get a warning with the wallet
Generating wallets

derivation path

BIP44 · Ethereum · test vector

mmaster/44'purpose/60'coin/0'account/0change/0index
const wallet = chain.deriveHDWallet(mnemonic, "m/44'/60'/0'/0/0");
// wallet.address
// 0x9858EfFD232B4033E47d90003D41EC34EcaEda94

Blockchains

Ten drivers, one shape

Each chain is an adapter over the shared primitives. Adding one means implementing the hashing and encoding rules, not the cryptography.

  • Mainnet and testnet address formats
  • Address validation for every chain
  • Sui on both curves, Cardano enterprise and stake
All blockchains

Agents

Eighteen tools over MCP

Everything the library does is exposed as an MCP tool with the same parameters. Point an agent at it and it derives, validates, and signs without touching the network. The Pi extension runs the same executors from a checkout.

  • Keys, WIF, mnemonics, BIP44 paths, addresses, signing
  • stdio server started with one command
  • A generated mnemonic comes back marked as already in the transcript
MCP server setup

toolkeys_get_address

stdio · keys mcp

request

{
  "blockchain": "bitcoin",
  "publicKey": "0279be667ef9dcbbac…f81798",
  "addressType": "segwit"
}

result

{
  "chain": "bitcoin",
  "network": "mainnet",
  "address": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
}

Start with one command

Experimental until 1.0. Pin exact versions and keep real funds on a hardware wallet.

@agntn/keys·MIT license· Keys never leave the browser.