@beebozy/dillithium-sdk
v0.1.0
Published
Browser-safe TypeScript SDK for the CKB Dilithium lock script: ML-DSA-65 key generation, lock args derivation, transaction hash signing, and witness serialization.
Maintainers
Readme
@ckb/dillithium-sdk
Experimental TypeScript SDK and CLI wallet for the CKB Dilithium lock script.
This package now provides both:
- low-level Dilithium helpers for key generation, lock args derivation, hashing, signing, and witness encoding
- a Node-first wallet layer for CKB address derivation, live-cell lookup, balance polling, transaction construction, signing, submission, and confirmation waiting
Install
npm install @ckb/dillithium-sdkWhat this package does
The on-chain lock script expects:
args:blake2b-256(publicKey)using CKB personalizationwitness.lock: serialized as[u32 pubkey_len LE | pubkey | u32 sig_len LE | sig]- ML-DSA-65 public keys and signatures
- signatures over the witness-aware CKB signing message (raw transaction hash + serialized witnesses)
This SDK mirrors those exact formats and adds wallet helpers around them.
Runtime
This package is currently Node.js-first, with browser-oriented packaging work in progress.
It bundles a compiled WebAssembly module and loads it from the installed package at runtime. It is suitable for:
- Node.js apps
- CLI tools
- server-side wallet tooling
- test scripts
Browser usage is still considered experimental until more real-world frontend transfer validation is complete.
Quick start: low-level SDK
import {
buildLockScript,
buildWitnessLock,
computeLockArgs,
generateKeypair,
signTxHash,
verifySignature,
} from "@ckb/dillithium-sdk";
const { publicKey, secretKey } = await generateKeypair();
const lockArgs = await computeLockArgs(publicKey);
const lock = await buildLockScript({
codeHash: "0x<deployed_code_hash>",
hashType: "data2",
publicKey,
});
const txHash = new Uint8Array(32);
const signature = await signTxHash(secretKey, txHash);
const ok = await verifySignature(publicKey, txHash, signature);
const witnessLock = buildWitnessLock(publicKey, signature);
console.log({
lockArgs: Buffer.from(lockArgs).toString("hex"),
lock,
witnessBytes: witnessLock.length,
ok,
});Quick start: wallet helpers
import {
CkbRpcClient,
addressFromPublicKey,
buildLockScript,
buildSignedTransfer,
generateKeypair,
getBalanceSummary,
waitForBalance,
waitForTransactionConfirmation,
} from "@ckb/dillithium-sdk";
const scriptConfig = {
codeHash: "0x<dillithium_code_hash>",
hashType: "data2",
network: "testnet",
cellDep: {
outPoint: {
txHash: "0x<deployment_tx_hash>",
index: "0x0",
},
depType: "code",
},
};
const { publicKey, secretKey } = await generateKeypair();
const lock = await buildLockScript({
codeHash: scriptConfig.codeHash,
hashType: scriptConfig.hashType,
publicKey,
});
const address = await addressFromPublicKey(publicKey, scriptConfig);
const client = new CkbRpcClient({
rpcUrl: "https://testnet.ckb.dev/rpc",
indexerUrl: "https://testnet.ckb.dev/indexer",
});
await waitForBalance(client, publicKey, scriptConfig, {
minCapacity: 80_00000000n,
});
const balance = await getBalanceSummary(client, lock);
console.log(address, balance.totalCapacity.toString());
const liveCells = await client.getCells(lock);
const signed = await buildSignedTransfer({
cells: liveCells,
fromLock: lock,
toLock: lock,
amount: 80_00000000n,
scriptConfig,
publicKey,
secretKey,
});
const txHash = await client.sendTransaction(signed.transaction);
await waitForTransactionConfirmation(client, { txHash });
console.log(txHash);CLI wallet
The package exposes a CLI named dillithium-wallet.
Generate a key file
dillithium-wallet keygen --out ./wallet.json --network testnetPrint the Dilithium CKB address
dillithium-wallet address \
--key-file ./wallet.json \
--network testnetWait for funding to appear
dillithium-wallet wait \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--indexer-url https://testnet.ckb.dev/indexer \
--min-ckb 80Check balance or wait for it
dillithium-wallet balance \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--indexer-url https://testnet.ckb.dev/indexerdillithium-wallet balance \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--indexer-url https://testnet.ckb.dev/indexer \
--wait \
--min-ckb 80Confirm a known outpoint over RPC
dillithium-wallet check-outpoint \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--tx-hash 0x132b03333e9e8272ed59ed57beefbe00a6955021a03e349987d150decbe6e6a7 \
--index 0Use this when explorer or raw RPC shows a funding transaction but balance still reports zero. The command checks the exact outpoint with node RPC and tells you whether it is still live and whether its lock matches the wallet derived from the selected key file.
Dry-run a transfer
dillithium-wallet transfer \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--indexer-url https://testnet.ckb.dev/indexer \
--to ckt1... \
--amount-ckb 80 \
--dry-runBroadcast a transfer and wait for follow-up visibility
dillithium-wallet transfer \
--key-file ./wallet.json \
--network testnet \
--rpc-url https://testnet.ckb.dev/rpc \
--indexer-url https://testnet.ckb.dev/indexer \
--to ckt1... \
--amount-ckb 80 \
--waitAPI additions
Address helpers
encodeCkbAddress(script, network)decodeCkbAddress(address)addressFromPublicKey(publicKey, scriptConfig)parseAddressToLock(address)
Wallet helpers
CkbRpcClientWalletErrorgetBalanceSummary(client, lock)getWalletState(client, publicKey, scriptConfig)waitForBalance(client, publicKey, scriptConfig, options?)waitForTransactionConfirmation(client, options)validateTransferRequest(options, recipientNetwork?)buildTransferTransaction(options)sealTransaction(options)buildSignedTransfer(options)computeTransactionHash(rawTransaction)minimumCellCapacity(lock, dataHex?, type?)
Constants
WITNESS_LOCK_BYTESDEFAULT_FEE_RATESHANNONS_PER_CKB
Notes
- This wallet path currently supports plain CKB transfers only.
- It uses the repo’s current signing model: sign the witness-aware CKB signing message, not the raw transaction hash alone.
- It does not yet support UDTs, DAO, multisig, or browser packaging.
- The CLI expects deployment metadata in
deployment/scripts.jsonin the current working directory to include thedillithium-lockentry for the chosen network. - The balance/transfer flow requires an indexer-backed endpoint for
get_cells. check-outpointuses raw RPCget_live_cell, which is useful for confirming a known funding cell but does not replace indexer-backed balance discovery.- For test suites, build the contract artifact first so
ckb-testtoolcan loadbuild/release/dillithium-lock.
Practical wallet lifecycle
- generate wallet
- derive address
- fund from faucet or another wallet
- wait for the funding tx to be indexed
- confirm funded balance
- if explorer shows funds but
balancestays zero, runcheck-outpointon the known funding outpoint to confirm the cell is live and belongs to this wallet - dry-run the transfer
- broadcast the transfer
- wait for tx visibility
- verify the balance decreased
Can the faucet be used directly from the CLI?
Not in this package today.
The current supported CLI flow starts before and after the faucet step, but does not claim faucet funds programmatically. That means:
- use CLI to generate wallet and print address
- fund that address via faucet UI or another funded sender
- return to CLI for waiting, balance checks, dry-run, signing, and sending
Development
From the repository root:
make build CONTRACT=dillithium-lock
npm install
npm run build:wasm
npm run build:sdk
npm test
cargo test --package testsPublishing checklist
From the repository root:
make build CONTRACT=dillithium-lock
npm install
npm run build:wasm
npm run build:sdk
npm test
cargo test --package testsFrom sdk/:
npm pack --dry-run
npm publish --access publicRepository
Source: https://github.com/beebozy/ckb-pq-dillithium-
