Typed key generation, address derivation, and message signing across eight blockchains and two curves.
Warning
@agntn/keys is experimental. The package name, public API, provider model, and tool surfaces may change before the first stable release. Pin exact versions if you build on it now.
- π Key generation - cryptographically secure private keys via Web Crypto API
- π« Address generation - all major formats per chain (legacy, segwit, taproot, base58, hex)
- β Address validation - verify validity and checksums for every supported format
- πΌ Wallet construction - generate a new wallet, or derive one from a private key or from a BIP39 mnemonic and derivation path
- βοΈ Message signing - sign and verify with secp256k1 or ed25519
- π€οΈ BIP44 paths - derivation path utilities for all supported chains
- π§© BIP39 puzzles - validate phrases, narrow one missing word, and map words or indices across all 10 official lists
- π Lazy loading - blockchain implementations load on demand for smaller bundles
- π€ MCP server - the same 13 key, mnemonic, address, and signing tools over stdio
- π Fully typed - TypeScript definitions for every interface
pnpm add @agntn/keysConcrete blockchain classes are lazy-loaded. The double-call pattern blockchains.chain(options)() first passes config, then imports and constructs the class.
import { useBlockchain, blockchains } from "@agntn/keys";
const ethereum = await blockchains.ethereum()();
const chain = useBlockchain(ethereum);
const wallet = chain.generateWallet();
console.log(wallet.keys.private); // hex private key
console.log(wallet.keys.public); // hex public key
console.log(wallet.address); // 0x... checksum addressimport { useBlockchain, blockchains } from "@agntn/keys";
const btc = useBlockchain(await blockchains.bitcoin()());
const privateKey = btc.generateKeyPrivate();
const publicKey = btc.getKeyPublic(privateKey);
btc.getAddress(publicKey); // legacy (1...)
btc.getAddress(publicKey, "segwit"); // native segwit (bc1q...)
btc.getAddress(publicKey, "taproot"); // taproot (bc1p...)
btc.getAddress(publicKey, "p2sh"); // pay-to-script-hash (3...)
btc.getAddress(publicKey, "p2wsh"); // witness script hash
// testnet
const testnet = useBlockchain(await blockchains.bitcoin({ network: "testnet" })());
testnet.getAddress(publicKey, "segwit"); // tb1q...import { useBlockchain, blockchains } from "@agntn/keys";
const chain = useBlockchain(await blockchains.solana()());
const { keys } = chain.generateKeys();
const signature = chain.signMessage("hello", keys.private);
const valid = chain.verifyMessage("hello", signature, keys.public); // trueimport { useBlockchain, blockchains } from "@agntn/keys";
const eth = useBlockchain(await blockchains.ethereum()());
const base = useBlockchain(await blockchains.base()());
const privateKey = eth.generateKeyPrivate();
const pubKey = eth.getKeyPublic(privateKey);
eth.getAddress(pubKey) === base.getAddress(pubKey); // trueimport { mnemonicToSeed } from "@agntn/keys/bip39";
import { getMasterKeyFromSeed, deriveHDKey } from "@agntn/keys/bip32";
const seed = mnemonicToSeed(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
);
const master = getMasterKeyFromSeed(seed);
const account = deriveHDKey(master, "m/84'/0'/0'/0/0");Use @agntn/keys/slip10 instead of @agntn/keys/bip32 for ed25519 derivation.
import { useBlockchain, blockchains } from "@agntn/keys";
const mnemonic =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const btc = useBlockchain(await blockchains.bitcoin()());
btc.deriveHDWallet(mnemonic, "m/84'/0'/0'/0/0").address; // bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
const sol = useBlockchain(await blockchains.solana()());
sol.deriveHDWallet(mnemonic, "m/44'/501'/0'/0'", { passphrase: "TREZOR" }).address;secp256k1 chains walk BIP32 and ed25519 chains walk SLIP-10, which accepts hardened segments only. Bitcoin reads the address type off the purpose level (44, 49, 84, 86) unless one is passed. Cardano throws, because CIP-1852 starts from the entropy rather than the BIP39 seed.
import { getMnemonicWordCandidates } from "@agntn/keys/bip39";
const candidates = getMnemonicWordCandidates(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon ?",
);The result only satisfies the BIP39 checksum. It does not prove that a candidate belongs to the wallet or puzzle target.
import { lookupBIP39Indices, lookupBIP39Words } from "@agntn/keys/bip39";
const wordMatches = await lookupBIP39Words(["orologio", "civetta"], "italian");
const indexMatches = await lookupBIP39Indices([1, 1179, 2048], "italian", 1);Language keys cover the 10 official BIP39 lists. Word lookup is case-insensitive and normalizes Unicode to NFKD. Index lookup uses base 0 by default and accepts base 1 explicitly.
The package includes a stdio MCP server with the same 13 operations used by the Pi extension. After installing the package, configure an MCP client to run keys mcp. A checkout can run the built entry directly:
{
"mcpServers": {
"keys": {
"command": "node",
"args": ["/absolute/path/to/keys/dist/cli.mjs", "mcp"]
}
}
}Hosts that own their transport can import createMcpServer from @agntn/keys/mcp.
The server handles private keys, mnemonics, entropy, messages, and signatures as plaintext MCP arguments or results. They enter client transcripts. Use only public puzzle material or disposable test keys, never a wallet that controls real funds.
| Chain | Curve | Address Formats | Testnet |
|---|---|---|---|
| Bitcoin | secp256k1 | legacy, p2sh, segwit, p2wsh, taproot | β |
| Ethereum | secp256k1 | EIP-55 checksum | - |
| Base | secp256k1 | EVM-compatible | - |
| Solana | ed25519 | base58 | - |
| Aptos | ed25519 | 0x-prefixed hex | - |
| Cardano | ed25519 | payment, stake, enterprise | β |
| SUI | ed25519, secp256k1 | 0x-prefixed hex (blake2b) | - |
| TRON | secp256k1 | base58check | β |
All chains support key generation, address derivation, address validation, and message signing.
Built on audited cryptographic packages from @paulmillr:
- @noble/curves - elliptic curve implementations (secp256k1, ed25519)
- @noble/hashes - SHA-256, Keccak, BLAKE2b, SHA3
- @scure/base - base58, bech32, hex encoding
- @scure/bip32 - HD wallet key derivation
- micro-key-producer - SLIP-0010 for ed25519
Caution
Never use this with real funds or with any wallet that has ever been used. Generated and signed material is handled as plaintext; treat every key it touches as burned the moment it is produced. Generate fresh throwaway keys for testing only and assume anything passing through @agntn/keys is compromised. Keys that control real funds belong on a hardware wallet, never in a process, log, or agent transcript.