Skip to content

Quickstart

This takes you from nothing to a funded post-quantum account that has sent a transaction on the live network.

  1. Generate a keypair. A 32-byte seed, which is your secret key.

  2. Derive your address offline. No transaction, no gas, no deployment. The address exists as a mathematical fact before it exists on chain.

  3. Fund it from the faucet. Funds arrive at an address with no code. This is normal.

  4. Send. Your first transaction deploys your account and makes the payment, paid for out of the balance already sitting there.

Terminal window
npm install viem
# plus viem-8141, from source — see /build/sdks/viem-8141/

The seed is the secret key. Thirty-two bytes, generated from a secure random source, is the whole thing — the ML-DSA keypair is derived from it deterministically.

import { randomBytes } from 'node:crypto';
import { bytesToHex } from 'viem';
import { createMldsaSigner, counterfactualAddress } from 'viem-8141';
const active = createMldsaSigner({ seed: bytesToHex(randomBytes(32)) });
const backup = createMldsaSigner({ seed: bytesToHex(randomBytes(32)) });
const account = counterfactualAddress({
activePublicKey: active.publicKey,
backupPublicKeyHash: backup.publicKeyKeccak,
});
console.log('Your address:', account);

Open faucet.frostfi.net, paste your address, and request funds. There is no captcha — your browser proves work by grinding ML-DSA-44 keypairs until one hashes with enough leading zero bits, which is a pleasingly on-theme way to rate-limit a post-quantum chain.

The faucet is also scriptable:

Terminal window
# 1. Get a challenge
curl -s "https://faucet.frostfi.net/api/challenge?address=0xYOUR_ADDRESS"
# → { "token": "…", "difficulty": 15, "algorithm": "ML-DSA-44",
# "rule": "SHA3-256(pk || token) must have 15 leading zero bits" }
# 2. Grind an ML-DSA-44 keypair matching the rule, sign
# "frost-faucet-v1" || token || address with it, then:
curl -s -X POST https://faucet.frostfi.net/api/claim \
-H 'content-type: application/json' \
--data '{"address":"0x…","token":"…","pk":"<hex>","sig":"<hex>"}'
# → { "tx_hash": "0x…", "amount_wei": "…" }

Confirm it arrived — with any Ethereum tooling, since reading is ordinary:

Terminal window
curl -s https://rpc.frostfi.net -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance",
"params":["0xYOUR_ADDRESS","latest"]}'

Your account still has no code at this point. eth_getCode returns 0x. That is expected — the address is counterfactual.

The first send is special: it carries a DEPLOY frame that materialises the account, and pays for that deployment out of the balance you just received.

import { createPublicClient, defineChain, http, parseEther } from 'viem';
import {
buildFirstSendTransaction,
frameActions,
frost,
serializeFrameTransaction,
signFrameTransactionWitness,
} from 'viem-8141';
const client = createPublicClient({
chain: frost,
transport: http('https://rpc.frostfi.net'),
}).extend(frameActions());
const tx = buildFirstSendTransaction({
activePublicKey: active.publicKey,
backupPublicKeyHash: backup.publicKeyKeccak,
recipient: '0xRECIPIENT',
value: parseEther('0.001'),
});
// Build → hash → sign → attach. The sig-hash excludes the witness bytes,
// so attaching the signature does not change it.
const signed = await signFrameTransactionWitness(tx, active);
const hash = await client.sendFrameTransaction(serializeFrameTransaction(signed));
const receipt = await client.waitForFrameReceipt(hash);
console.log(receipt.successful, receipt.frameReceipts);

Look it up on the explorer. Your account now has code, and its nonce is 3 — CREATE2 set it to 1, the key-pointer deployment bumped it to 2, and payment approval bumped it to 3. Always read eth_getTransactionCount; never assume.

Three things that have no equivalent on Ethereum:

  • Your address existed before your account did. It is a pure function of your public keys, derived through the CREATE2 factory. See Accounts and keys.
  • Your account deployed itself and paid its own way. No sponsor, no bundler, no separate deployment step.
  • A lattice signature authorised it. The account’s VERIFY frame called the 0x14 precompile with your signature and its own embedded public key, and only approved when it returned 1.