SDK & Integration

SDK: Merkle tree & proofs

The MerkleTree class, inclusion proofs, the constants that must match the contracts, and formatProof — the one transform that reorders a snarkjs proof into the (a, b, c) shape the Solidity verifier expects.

The SDK's MerkleTree mirrors the on-chain MerkleTreeWithHistory exactly: same depth, same ZERO_VALUE, same Poseidon node ordering. You rebuild it client-side from indexed leaves to generate inclusion proofs.

MerkleTree

typescript
import { MerkleTree, LEVELS, ZERO_VALUE } from "@shh/sdk";

const tree = new MerkleTree(LEVELS, leaves);  // leaves: bigint[]
const i    = tree.indexOf(commitment);        // -1 if absent
const root = tree.root();
const path = tree.proof(i);                   // { pathElements, pathIndices }
indexOf(leaf)
Insertion index of a leaf, or -1.
root()
Current Poseidon root (bigint).
proof(index)
A MerklePath: pathElements[LEVELS] and pathIndices for the inclusion proof.
zeros / levels
Precomputed empty-subtree values and the configured depth — used to pad dummy inputs.

Formatting proofs for Solidity

snarkjs emits a proof as pi_a, pi_b, pi_c. The Solidity verifier wants (a, b, c) with the b coordinates swapped. formatProof does exactly that:

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

const sol = formatProof(snarkjsProof);
// sol.a: [bigint, bigint]
// sol.b: [[bigint, bigint], [bigint, bigint]]  (G2 coords reordered)
// sol.c: [bigint, bigint]

Reconstructing trees from the backend

The wallet backend indexes leaves so clients don't have to scan the chain. Fetch them, build the tree, and you're ready to prove:

typescript
const leaves = await fetch("/api/pool/leaves").then((r) => r.json());
const tree = new MerkleTree(LEVELS, leaves.map(BigInt));

The endpoints are documented in the Wallet backend API.