Skip to main content
This quickstart uses the current UTXO API (transact + partialWithdraw).

Prerequisites

  • Node.js 18+
  • A funded Solana keypair
  • Relay URL
  • Solana RPC URL
  • Hosted circuit artifacts at https://cloak.ag/circuits

Install

mkdir cloak-demo && cd cloak-demo
npm init -y
npm install @cloak.ag/sdk @solana/web3.js tsx

Example script

Create demo.ts:
import {
  createUtxo,
  createZeroUtxo,
  generateUtxoKeypair,
  partialWithdraw,
  transact,
  setCircuitsPath,
} from "@cloak.ag/sdk";
import { Connection, Keypair, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { readFileSync } from "node:fs";
import { join } from "node:path";

const connection = new Connection(
  process.env.SOLANA_RPC_URL || "https://api.devnet.solana.com",
  "confirmed"
);

const keypairPath = process.env.KEYPAIR_PATH || join(process.env.HOME!, ".config/solana/id.json");
const keypair = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(readFileSync(keypairPath, "utf8")))
);

const programId = new PublicKey(
  process.env.CLOAK_PROGRAM_ID || "c1oak6tetxYnNfvXKFkpn1d98FxtK7B68vBQLYQpWKp"
);
const relayUrl = process.env.CLOAK_RELAY_URL || "http://127.0.0.1:5500";
setCircuitsPath(process.env.CIRCUITS_PATH || "https://cloak.ag/circuits");

const owner = await generateUtxoKeypair();
const depositAmount = BigInt(0.02 * LAMPORTS_PER_SOL); // 0.02 SOL

const depositUtxo = await createUtxo(depositAmount, owner);
const deposit = await transact(
  {
    inputUtxos: [await createZeroUtxo()],
    outputUtxos: [depositUtxo],
    externalAmount: depositAmount,
    depositor: keypair.publicKey,
  },
  {
    connection,
    programId,
    relayUrl,
    depositorKeypair: keypair,
  }
);

const recipient = Keypair.generate().publicKey;
const withdraw = await partialWithdraw(
  deposit.outputUtxos,
  recipient,
  BigInt(0.005 * LAMPORTS_PER_SOL),
  {
    connection,
    programId,
    relayUrl,
    depositorKeypair: keypair,
    cachedMerkleTree: deposit.merkleTree,
  }
);

console.log("Deposit tx:", deposit.signature);
console.log("Withdraw tx:", withdraw.signature);
Run it:
npx tsx demo.ts

Important runtime notes

  • Relay validates proof_bytes as 256 bytes and public_inputs as 264 bytes.
  • SDK retries root-staleness (0x1001) by rebuilding proofs.
  • Viewing-key registration is enforced by default in web/SDK transaction flows.
  • If your deployment uses a different program, pass explicit programId.

Verify scanner output

Add a scanner step in your app/script:
import { scanTransactions, toComplianceReport } from "@cloak.ag/sdk";

const scan = await scanTransactions({
  connection,
  programId,
  viewingKeyNk: /* your 32-byte nk */,
});

const report = toComplianceReport(scan);
console.log(report.summary);
This returns chain-native totals (deposits, withdrawals, fees, final balance).

Next steps