SDK & Integration

SDK: Privacy Pool notes

Working with fixed-denomination notes in code: create a PoolNote, derive its commitment and nullifier hash, build a withdrawal witness across the state and association trees, and prove it.

A PoolNote is the Profile B note: a (nullifier, secret) pair whose commitment = H(nullifier, secret). Both values are random F_p by default and are the only things you need to keep to withdraw.

Creating a note & depositing

typescript
import { PoolNote, toFixedHex } from "@shh/sdk";
import { ethers } from "ethers";

const note = new PoolNote();              // fresh nullifier + secret
const commitment = await note.commitment();

// deposit exactly the pool denomination
await pool.deposit(toFixedHex(commitment), { value: denomination });

// persist these — they are the funds:
save({ nullifier: note.nullifier, secret: note.secret });

The PoolNote API

new PoolNote(nullifier?, secret?)
Both default to randomField(). Pass saved values to reconstruct a note for withdrawal.
note.commitment()
Promise<bigint>H(nullifier, secret), the deposited leaf.
note.nullifierHash()
Promise<bigint>H(nullifier), published on withdraw to prevent double spend.

Building a withdrawal witness

buildPoolWithdrawInput is browser-safe — it produces the circuit input without proving. It needs both trees, the note, and the bound withdrawal parameters:

typescript
import { buildPoolWithdrawInput } from "@shh/sdk";

const { input, stateRoot, associationRoot, nullifierHash } =
  await buildPoolWithdrawInput({
    note,
    stateTree,        // MerkleTree of all deposited commitments
    associationTree,  // MerkleTree of ASP-approved commitments
    recipient,        // address as uint (bigint)
    relayer,          // bind the relayer (or 0n to self-submit)
    fee,
    refund,
  });

It looks up the commitment's index in both trees (throwing if it isn't approved), generates both Merkle proofs, and assembles every public and private signal the circuit expects.

Proving (Node)

typescript
import { generatePoolWithdraw } from "@shh/sdk/node";

const { proof, stateRoot, associationRoot, nullifierHash } =
  await generatePoolWithdraw({
    note, stateTree, associationTree,
    recipient, relayer, fee, refund,
    wasmPath: "build/poolWithdraw.wasm",
    zkeyPath: "build/poolWithdraw.zkey",
  });

// proof is already in Solidity (a, b, c) shape — see formatProof

To submit without paying gas yourself, hand the proof to the relayer.