Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cloak.ag/llms.txt

Use this file to discover all available pages before exploring further.

Install

npm install @cloak.dev/sdk @solana/web3.js @solana/wallet-adapter-react
If your app standardizes on @solana/kit, add it as well and use the bridge pattern in Solana Kit Integration.
npm install @solana/kit
Add wallet UI packages as needed:
npm install @solana/wallet-adapter-react-ui @solana/wallet-adapter-wallets

Note-based integration (CloakSDK)

import { useMemo } from "react";
import { CloakSDK } from "@cloak.dev/sdk";
import { useWallet } from "@solana/wallet-adapter-react";
import { PublicKey } from "@solana/web3.js";

export function useCloak() {
  const { publicKey, signTransaction, sendTransaction } = useWallet();

  return useMemo(() => {
    if (!publicKey || !sendTransaction) return null;

    return new CloakSDK({
      wallet: { publicKey, signTransaction, sendTransaction },
      network: "mainnet",
      programId: new PublicKey("zh1eLd6rSphLejbFfJEneUwzHRfMKxgzrgkfwA6qRkW"),
    });
  }, [publicKey, signTransaction, sendTransaction]);
}

UTXO integration with wallet signing

For UTXO transact deposits in browser apps, pass wallet signing fields in TransactOptions:
const result = await transact(params, {
  connection,
  programId,
  relayUrl,
  signTransaction, // wallet adapter signer
  depositorPublicKey, // wallet public key
  riskOracleQueue, // if required by deployment
  riskQuoteUrl, // risk quote backend (if needed)
  addressLookupTableAccounts,
});
Make sure your wallet supports signMessage so viewing-key registration can complete before transactions.

Full wallet-adapter SOL send flow (transact -> fullWithdraw)

import {
  CLOAK_PROGRAM_ID,
  NATIVE_SOL_MINT,
  createUtxo,
  createZeroUtxo,
  fullWithdraw,
  generateUtxoKeypair,
  isRootNotFoundError,
  transact,
} from "@cloak.dev/sdk";
import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection(process.env.SOLANA_RPC_URL!, "confirmed");
const programId = CLOAK_PROGRAM_ID;

async function sendWithWalletAdapter({
  walletPublicKey,
  signTransaction,
  signMessage,
  recipientAddress,
  amountLamports,
}: {
  walletPublicKey: PublicKey;
  signTransaction: (tx: any) => Promise<any>;
  signMessage: (msg: Uint8Array) => Promise<Uint8Array>;
  recipientAddress: string;
  amountLamports: bigint;
}) {
  const owner = await generateUtxoKeypair();
  const output = await createUtxo(amountLamports, owner, NATIVE_SOL_MINT);

  const deposited = await transact(
    {
      inputUtxos: [await createZeroUtxo(NATIVE_SOL_MINT)],
      outputUtxos: [output],
      externalAmount: amountLamports,
      depositor: walletPublicKey,
    },
    {
      connection,
      programId,
      signTransaction,
      depositorPublicKey: walletPublicKey,
      walletPublicKey,
      onProgress: (status) => console.log("deposit", status),
      onProofProgress: (percent) => console.log("deposit proof", percent),
    },
  );

  const recipient = new PublicKey(recipientAddress);
  let withdrawResult: Awaited<ReturnType<typeof fullWithdraw>> | undefined;

  for (let attempt = 1; attempt <= 3; attempt += 1) {
    try {
      withdrawResult = await fullWithdraw(deposited.outputUtxos, recipient, {
        connection,
        programId,
        walletPublicKey,
        signMessage,
        cachedMerkleTree: deposited.merkleTree,
        onProgress: (status) => console.log("withdraw", status),
        onProofProgress: (percent) => console.log("withdraw proof", percent),
      });
      break;
    } catch (error) {
      if (!isRootNotFoundError(error) || attempt === 3) throw error;
      await new Promise((resolve) => setTimeout(resolve, 1_500));
    }
  }

  if (!withdrawResult) throw new Error("withdraw did not produce a result");

  return {
    depositSignature: deposited.signature,
    withdrawSignature: withdrawResult.signature,
  };
}
Notes:
  • Keep amount values as bigint end-to-end.
  • Use cachedMerkleTree from deposit for the immediate withdraw call.
  • Treat isRootNotFoundError as retryable with bounded backoff.

UX requirements

  • Show progress during proof generation (onProgress, onProofProgress).
  • Handle stale-root retries gracefully (isRootNotFoundError).
  • Keep circuit files accessible in browser deployments.
  • Ensure viewing-key registration succeeds before transact/swap actions.
  • For privacy history pages, expose explicit cache clear + rescan controls after swaps or failed scans.