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.
Use this page when you want the smallest possible end-to-end Cloak integration.
This snippet covers:
- shielded SOL deposit
- full withdrawal (send)
- partial withdrawal (keep change private)
import {
CLOAK_PROGRAM_ID,
NATIVE_SOL_MINT,
createUtxo,
createZeroUtxo,
fullWithdraw,
generateUtxoKeypair,
partialWithdraw,
transact,
} from "@cloak.dev/sdk";
import { Connection, Keypair } from "@solana/web3.js";
const connection = new Connection(
"https://api.mainnet-beta.solana.com",
"confirmed",
);
const signer = Keypair.fromSecretKey(/* Uint8Array secret key */);
const amount = 1_000_000_000n; // 1 SOL
const owner = await generateUtxoKeypair();
const depositOutput = await createUtxo(amount, owner, NATIVE_SOL_MINT);
const deposited = await transact(
{
inputUtxos: [await createZeroUtxo(NATIVE_SOL_MINT)],
outputUtxos: [depositOutput],
externalAmount: amount,
depositor: signer.publicKey,
},
{
connection,
programId: CLOAK_PROGRAM_ID,
depositorKeypair: signer,
walletPublicKey: signer.publicKey,
},
);
const recipient = Keypair.generate().publicKey;
// Full withdrawal: sends all spendable value externally.
await fullWithdraw(deposited.outputUtxos, recipient, {
connection,
programId: CLOAK_PROGRAM_ID,
depositorKeypair: signer,
walletPublicKey: signer.publicKey,
cachedMerkleTree: deposited.merkleTree,
});
// OR partial withdrawal: withdraw a portion, keep private change in shielded state.
// await partialWithdraw(deposited.outputUtxos, recipient, 200_000_000n, {
// connection,
// programId: CLOAK_PROGRAM_ID,
// depositorKeypair: signer,
// walletPublicKey: signer.publicKey,
// cachedMerkleTree: deposited.merkleTree,
// });
One-file CLI contract (recommended for one-shot scripts)
For minimal script generation, keep this exact interface:
- file:
send-sol-private.ts
- command:
npx tsx send-sol-private.ts <recipientPubkey> <lamports>
- env vars:
SOLANA_RPC_URL, KEYPAIR_PATH
Guardrails:
- keep tx amounts as
bigint
- use
KEYPAIR_PATH (file-based keypair), not raw private key env vars
- do not parse SOL decimals with float math (
parseFloat, AMOUNT_SOL)
- read recipient/amount from
process.argv (<recipientPubkey> <lamports>), not RECIPIENT_ADDRESS/SEND_LAMPORTS env vars
- keep
programId fixed to CLOAK_PROGRAM_ID internally
- rely on SDK stale-root retries for standard flows
- call
process.exit(0) on success and process.exit(1) on failure
Mainnet smoke test (keypair bytes)
Use this when you want a first real send with explicit runtime values.
export SOLANA_RPC_URL="https://api.mainnet-beta.solana.com"
# Must be an absolute path. Do not rely on `~` expansion inside env files.
export KEYPAIR_PATH="/Users/you/.config/solana/id.json"
Sanity-check before running:
solana-keygen pubkey "$KEYPAIR_PATH"
solana balance "$(solana-keygen pubkey "$KEYPAIR_PATH")" --url "$SOLANA_RPC_URL"
Run your script entrypoint that wraps the snippet above:
npx tsx send.ts "<recipient-wallet-pubkey>" "50000000"
Funding and net amount
For SOL withdraw/send paths:
gross = abs(externalAmount)
fee = 5_000_000 + floor(gross * 3 / 1000)
net = gross - fee
Worked example (gross = 50_000_000, i.e. 0.05 SOL):
- fee =
5_150_000 lamports (0.00515 SOL)
- net =
44_850_000 lamports (0.04485 SOL)
Recommended first-run sender balance for this smoke test: 0.07 SOL or higher.
Next: Code Examples