Getting Started

Quick Start

Build your first stealth payment in 5 minutes. This guide covers the complete flow: generate, send, scan, and spend.

Step 1: Generate a stealth identity

The receiver generates a StealthMetaAddress containing spending and viewing keypairs. The public portion is shared with senders.

receiver.rs
rust
use cloak_sdk::StealthMetaAddress;

// Generate a new stealth meta-address
let meta = StealthMetaAddress::generate();

// Save keys securely (private keys never leave this file)
meta.save_to_file("~/.cloak/keys.json")?;

// Share the public portion with senders
let public_address = meta.to_public_string();
println!("Share this: {}", public_address);
// → "stealth1:sol:<spending_pubkey>:<viewing_pubkey>"

Step 2: Create a stealth payment

The sender uses the receiver's public meta-address to derive a unique one-time stealth address and an ephemeral keypair via ECDH.

sender.rs
rust
use cloak_sdk::{PublicMetaAddress, StealthPayment};

// Parse the receiver's public meta-address
let recipient = PublicMetaAddress::from_string("stealth1:sol:...")?;

// Create a stealth payment (1 SOL = 1_000_000_000 lamports)
let payment = StealthPayment::create(&recipient, 1_000_000_000)?;

// payment.stealth_address  → where to send the SOL
// payment.ephemeral_pubkey → publish on-chain for detection
// payment.amount           → the amount in lamports

Step 2b: Send with hidden amount (optional)

Use zero-knowledge proofs to hide the payment amount on-chain:

sender_private.rs
rust
use cloak_sdk::zk::{self, AmountCommitment};

// Generate proving/verifying keys (one-time setup)
let (pk, _pvk) = zk::setup()?;

// Create a Pedersen commitment to the amount
let commitment = AmountCommitment::commit(1_000_000_000);

// Generate a Groth16 proof
let proof = zk::prove(&pk, 1_000_000_000, &commitment)?;

// commitment + proof go on-chain via send_stealth_private
// The amount stays completely private

Step 3: Scan for incoming payments

The receiver scans on-chain announcements to detect payments addressed to them using their viewing key.

receiver_scan.rs
rust
use cloak_sdk::{Scanner, Announcement};

let scanner = Scanner::new(&meta);

// Scan a list of announcements from the chain
let announcements: Vec<Announcement> = fetch_announcements(rpc_url).await?;
let detected = scanner.scan_announcements_list(&announcements)?;

println!("Found {} payments!", detected.len());
for payment in &detected {
    println!("  Address: {}", payment.stealth_address);
    println!("  Amount: {:?}", payment.amount);
}

Step 4: Spend from a stealth address

For each detected payment, the receiver derives the spending keypair and signs a transaction to claim the funds.

receiver_spend.rs
rust
use cloak_sdk::StealthKeypair;
use solana_sdk::transaction::Transaction;
use solana_sdk::system_instruction;

for payment in detected {
    // Derive the spending keypair
    let keypair = StealthKeypair::derive(&meta, &payment.ephemeral_pubkey)?;
    let solana_kp = keypair.to_solana_keypair()?;

    // Verify the address matches
    assert_eq!(solana_kp.pubkey(), payment.stealth_address);

    // Create and sign a transfer transaction
    let ix = system_instruction::transfer(
        &payment.stealth_address,
        &destination_wallet,
        payment.amount.unwrap_or(0),
    );
    let tx = Transaction::new_signed_with_payer(
        &[ix],
        Some(&payment.stealth_address),
        &[&solana_kp],
        recent_blockhash,
    );
    client.send_and_confirm_transaction(&tx)?;
}

Using the CLI

All of the above can be done from the terminal:

Terminal
bash
# Generate stealth identity
cloak init

# Send a payment
cloak --program-id <PROGRAM_ID> send <meta-address> 0.5

# Send with hidden amount (zk-SNARK)
cloak --program-id <PROGRAM_ID> send <meta-address> 0.5 --private

# Send through relayer (hidden sender)
cloak --program-id <PROGRAM_ID> send <meta-address> 0.5 --relayer http://localhost:3000

# Scan for incoming payments
cloak --program-id <PROGRAM_ID> scan

# Spend from a stealth address
cloak --program-id <PROGRAM_ID> spend <stealth-address> <destination> all --ephemeral <hex>

# View payment history
cloak history --unspent

Complete end-to-end example

e2e.rs
rust
use cloak_sdk::{
    StealthMetaAddress, PublicMetaAddress,
    StealthPayment, StealthKeypair,
    Scanner, Announcement,
};

fn main() -> anyhow::Result<()> {
    // === RECEIVER SETUP ===
    let meta = StealthMetaAddress::generate();
    let public_addr = meta.to_public_string();
    println!("Public address: {}", public_addr);

    // === SENDER CREATES PAYMENT ===
    let recipient = PublicMetaAddress::from_string(&public_addr)?;
    let payment = StealthPayment::create(&recipient, 500_000_000)?;
    println!("Send SOL to: {}", payment.stealth_address);

    // === RECEIVER DETECTS PAYMENT ===
    let announcement = Announcement {
        stealth_address: payment.stealth_address,
        ephemeral_pubkey: payment.ephemeral_pubkey,
        amount: payment.amount,
        timestamp: 0,
    };
    let scanner = Scanner::new(&meta);
    let detected = scanner.scan_announcements_list(&[announcement])?;
    assert_eq!(detected.len(), 1);
    println!("Payment detected!");

    // === RECEIVER DERIVES SPENDING KEY ===
    let keypair = StealthKeypair::derive(&meta, &detected[0].ephemeral_pubkey)?;
    assert_eq!(keypair.address(), payment.stealth_address);
    println!("Spending key derived. Ready to claim funds.");

    Ok(())
}