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.

The UTXO API is the current production path for SDK-based app integrations.

Core APIs

  • transact(params, options)
  • transfer(...)
  • partialWithdraw(...)
  • fullWithdraw(...)
  • swapUtxo(...)
  • swapWithChange(...)
All are exported from @cloak.dev/sdk.

Transaction semantics

import type { Utxo, TransactParams, TransactOptions } from "@cloak.dev/sdk";

interface TransactParams {
  inputUtxos: Utxo[];
  outputUtxos: Utxo[];
  recipient?: PublicKey;
  externalAmount?: bigint; // +deposit, -withdraw, 0=shield-to-shield
}
externalAmount rules:
  • > 0: public deposit into the pool
  • < 0: public withdrawal from the pool
  • 0: fully shielded transfer
Under the hood, SDK builds proof + 264-byte public inputs and submits the resulting on-chain transaction.

Fees

  • SOL withdraw/swap:
    • gross = abs(externalAmount)
    • fee = 5_000_000 + floor(gross * 3 / 1000)
    • net = gross - fee
  • Deposits have no protocol fee but still must satisfy min deposit (10_000_000 lamports)
SDK helpers you can call directly:
  • SOL: calculateSolFeeLamports, calculateSolNetAmountLamports

Create UTXOs

import {
  createUtxo,
  generateUtxoKeypair,
  NATIVE_SOL_MINT,
} from "@cloak.dev/sdk";

const keypair = await generateUtxoKeypair();
const utxo = await createUtxo(100_000_000n, keypair, NATIVE_SOL_MINT);

Deposit example (transact)

import {
  CLOAK_PROGRAM_ID,
  transact,
  createUtxo,
  generateUtxoKeypair,
} from "@cloak.dev/sdk";
import { Connection, Keypair } from "@solana/web3.js";

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

const owner = await generateUtxoKeypair();
const output = await createUtxo(20_000_000n, owner); // 0.02 SOL

const result = await transact(
{
inputUtxos: [],
outputUtxos: [output],
externalAmount: 20_000_000n,
},
{
connection,
programId,
depositorKeypair,
}
);

console.log(result.signature, result.commitmentIndices);

Withdraw and transfer helpers

import { transfer, partialWithdraw, fullWithdraw } from "@cloak.dev/sdk";

await transfer(inputUtxos, recipientUtxoPublicKey, 30_000_000n, options);
await partialWithdraw(inputUtxos, recipientWallet, 10_000_000n, options);
await fullWithdraw(inputUtxos, recipientWallet, options);

partialWithdraw/fullWithdraw use negative externalAmount semantics.

Swap example

import { swapWithChange } from "@cloak.dev/sdk";

await swapWithChange(
inputUtxos,
50_000_000n, // swap amount (lamports)
outputMint,
recipientAta,
1_000_000n, // min output
{
connection,
programId,
},
recipientWallet
);

swapWithChange sends a TransactSwap payload (proof + 264-byte public inputs + swap params).

Viewing-key registration requirements

  • Default behavior enforces viewing-key registration before protocol txs.
  • TransactOptions.enforceViewingKeyRegistration defaults to true.
  • Registration signs the fixed sign-in message and submits the registration record.
If viewing key is missing, history and decrypt flows cannot resolve your transaction data.

Risk-oracle deposits

When deposits require Range/Switchboard validation, include:
  • riskOracleQueue
  • riskQuoteUrl (or getRiskQuoteInstruction)
The SDK can fetch risk quotes for you; you can also provide your own quote backend or a direct instruction callback. If deposit account list is large, use v0 transactions with addressLookupTableAccounts.

Operational notes

  • Amounts use bigint in the UTXO API.
  • Proof retries are built in for stale roots (0x1001).
  • SDK-side Merkle reconstruction is preferred for older indices.
  • Optional cachedMerkleTree helps sequential txs avoid extra rebuild/fetch.
  • Optional useUniqueNullifiers helps repeated development runs avoid nullifier collisions.

Troubleshooting

  • Invalid public inputs size: the protocol expects 264 bytes, not 232.
  • RootNotFound / 0x1001: regenerate proof with fresh root (SDK retry usually handles this).
  • 0x1020 in local repeated runs: set CLOAK_UNIQUE_NULLIFIERS=1.
  • viewing key not found: re-run a transaction with registration enabled, then rescan history.

Next