Skip to Content
DocumentQuickstart
Get started

From npm install to a settled private trade — end to end.

This walkthrough wires the SDK into a fresh Next.js app and exercises every layer: wallet → deposit → order → settle → claim. Skim the Architecture page first if any of those words look unfamiliar.

1. Install

npm install @zkscatter/sdk ethers react@^19

See Installation for Next.js wiring and Web Worker asset setup.

2. Configure the network

import { parseTokenList, withNativeEthAlias, type NetworkConfig, } from "@zkscatter/sdk"; const wethAddress = "0x..."; export const network: NetworkConfig = { chainId: 11155111, rpcUrl: process.env.NEXT_PUBLIC_RPC_URL!, contracts: { privateSettlement: "0x...", commitmentPool: "0x...", relayerRegistry: "0x...", identityGate: "0x...", feeVault: "0x...", weth: wethAddress, }, tokens: withNativeEthAlias( parseTokenList(process.env.NEXT_PUBLIC_TOKEN_LIST), wethAddress, ), };

parseTokenList expects the compact addr:symbol:decimals,… format — useful for shipping the same config through env vars across web and mobile.

3. Mount the wallet provider

"use client"; import { WalletProvider } from "@zkscatter/sdk/react"; import { network } from "@/lib/network"; export function Providers({ children }: { children: React.ReactNode }) { return <WalletProvider network={network}>{children}</WalletProvider>; }
import { Providers } from "./providers"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body><Providers>{children}</Providers></body> </html> ); }

4. Deposit into the pool

"use client"; import { useWallet } from "@zkscatter/sdk/react"; import { generateNote, generateDepositProof, deriveEdDSAKey, } from "@zkscatter/sdk/zk"; import { ensureAllowance, callDeposit } from "@zkscatter/sdk/contracts"; import { network } from "@/lib/network"; export function DepositButton({ token, amount }: { token: string; amount: bigint }) { const { signer } = useWallet(); async function deposit() { if (!signer) throw new Error("Connect wallet first"); const eddsa = await deriveEdDSAKey(signer); const note = generateNote(token, amount, eddsa.publicKey); const result = await generateDepositProof(note, { wasm: "/zk/deposit.wasm", zkey: "/zk/deposit_final.zkey", }); // ensureAllowance returns pending approval txs; wait for each to // confirm before depositing or the deposit can race ahead. const approvals = await ensureAllowance( signer, token, network.contracts.commitmentPool, amount, ); await Promise.all(approvals.map((tx) => tx.wait())); const tx = await callDeposit( signer, network.contracts.commitmentPool, result, token, amount, ); await tx.wait(); // Persist `note` + `result.commitment` — see /guides/persist-notes. } return <button onClick={deposit}>Deposit</button>; }

5. Discover relayers and place an order

import { loadRelayersWithApiInfo, RelayerClient, type OrderData, } from "@zkscatter/sdk/relayer"; import { generateAuthorizeProof, randomFieldElement, } from "@zkscatter/sdk/zk"; const relayers = await loadRelayersWithApiInfo( network.contracts.relayerRegistry, readProvider, ); const best = relayers.find((r) => r.online && r.api); if (!best?.api) throw new Error("No relayer online"); const expiry = BigInt(Math.floor(Date.now() / 1000) + 600); // AuthorizeProofInput uses `note.token` for the sell side — no // separate `sellToken` field. `claims` here must match the order's // claim distribution (see /guides/place-order for the full shape). const authResult = await generateAuthorizeProof( { note, leafIndex, merkleProof, sellAmount: amountIn, buyToken: tokenB, buyAmount: amountOut, maxFee, expiry, nonce, relayer: best.address, eddsaPrivateKey: eddsa.privateKey, claims: [/* stealth-addressed payouts */], newSalt: randomFieldElement(), }, { wasm: "/zk/authorize.wasm", zkey: "/zk/authorize_final.zkey" }, ); const order: OrderData = { maker: account, sellToken: tokenA, buyToken: tokenB, sellAmount: amountIn.toString(), buyAmount: amountOut.toString(), maxFee: Number(maxFee), expiry: Number(expiry), nonce: Number(nonce), claims, // wire-format claim entries }; // `signature` is a relayer-protocol string — see the relayer's // /api/info for the format it accepts. const client = new RelayerClient(best.url); await client.submitOrder(order, signature, "cover_taker");

See Place an order for the complete flow, including how to build the merkle proof from indexed pool events.

6. Claim a payout

When a counterparty’s order matches yours, your stealth payout lands as a new commitment in the pool. Claim it whenever you like:

import { generateClaimProof } from "@zkscatter/sdk/zk"; import { callClaimWithProof } from "@zkscatter/sdk/contracts"; const claimResult = await generateClaimProof(claimInput, { wasm: "/zk/claim.wasm", zkey: "/zk/claim_final.zkey", }); const tx = await callClaimWithProof( signer, network.contracts.privateSettlement, claimResult.proof, { recipient, token, amount, releaseTime }, ); await tx.wait();

Claims can also be batched — see Claim a payout.

What’s next

Last updated on