SDK & Integration
SDK: Shielded UTXO
Working with shielded notes: keypairs, Utxo objects, the 2-in/2-out transaction builder, signed external amounts, and the ExtData hash that binds recipient, relayer, fee, and encrypted outputs.
Profile A spends and creates notes. A Keypair derives the spend key, a Utxo is a note, and buildTransactionInput assembles a 2-in/2-out join-split witness.
Keypairs
import { Keypair } from "@shh/sdk";
const kp = await Keypair.generate(); // random privkey
// kp.pubkey === H(privkey)
const sig = await kp.sign(commitment, merklePath);pubkey = H(privkey); sign(commitment, path) = H(privkey, commitment, path). Keypair.generate(privkey?) lets you reconstruct a known key. init() computes the pubkey (await it before use).
Notes
import { Utxo } from "@shh/sdk";
const note = new Utxo({ amount: 1_000_000n, keypair: kp });
const commitment = await note.getCommitment(); // H(amount, pubkey, blinding)
const nullifier = await note.getNullifier(); // H(commitment, index, signature)amount- Note value (bigint). Constrained to 248 bits in the circuit.
blinding- Per-note randomness; defaults to
randomField(). keypair- The owning
Keypair. index- Leaf index once inserted, or
nullfor a not-yet-committed output.
Building a transaction
buildTransactionInput pads inputs and outputs to two each (with distinct dummy keypairs so nullifiers stay unique), computes publicAmount = (extAmount − fee) mod p, and binds the extDataHash:
import { buildTransactionInput } from "@shh/sdk";
const { input, root, publicAmount, extDataHash,
inputNullifiers, outputCommitments, extData } =
await buildTransactionInput({
inputs: [noteA], // spent notes (≤ 2)
outputs: [noteB, noteC], // created notes (≤ 2)
tree, // current shielded MerkleTree
extAmount: 0n, // 0 = private transfer, >0 deposit, <0 withdraw
fee: 0n,
recipient, relayer,
});ExtData & amounts
hashExtData(extData)keccak256(abi.encode(extData)) mod p— matchesShieldedPool._extDataHash. Binds recipient, signedextAmount, relayer, fee, and the two encrypted-output ciphertexts.toFieldAmount(value)((value % p) + p) % p— maps a signed amount into the field, matching_calcPublicAmount.
Proving (Node)
import { generateTransaction } from "@shh/sdk/node";
const result = await generateTransaction({
inputs, outputs, tree, extAmount, fee, recipient, relayer,
wasmPath: "build/transaction2x2.wasm",
zkeyPath: "build/transaction2x2.zkey",
});
// result.proof, result.publicSignals, result.extData …