ParaloomPARALOOM

Developer Guide

How to integrate with paraloom today, while paraloom-sdk is still being built.

Developer guide

Heads up. A paraloom-sdk is on the roadmap but does not yet exist as a publishable crate or NPM package. If you're integrating today, you have two real options: the paraloom CLI, or the paraloom-core Rust workspace as a git dependency. This guide focuses on those.

What you can build today

Use caseStatusSurface
Private SOL payments (deposit / transfer / withdraw)working on devnetparaloom wallet … CLI; paraloom::bridge + paraloom::privacy crates
Run a verifying validatorworking on devnetparaloom validator … — see Validator guide
Submit and run private WASM computealpha on devnetparaloom compute …; output-notes pending
Custom Groth16 circuits with shared inframanualparaloom::privacy::circuits + ark-groth16 directly
Generic dApp integration via SDKnot yettracked as future work

The CLI is the most stable contract. It wraps the same crate APIs with versioned UX. Full surface in API reference.

Deposit → withdraw walkthrough

# 1. Wallet
paraloom wallet new --out ./alice.note

# 2. Public deposit (1 SOL → privacy pool)
paraloom wallet deposit --amount 1 --note ./alice.note --network devnet

# 3. Private transfer to bob
paraloom wallet transfer --amount 0.4 --recipient <bob-shielded-pk> \
    --note ./alice.note --network devnet

# 4. Withdraw remaining 0.6 SOL to a public address
paraloom wallet withdraw --amount 0.6 --recipient <sol-pubkey> \
    --note ./alice.note --network devnet

Each step:

  • emits the right circuit-shaped Groth16 proof under the hood,
  • handles expiration_slot replay-protection windows,
  • uses on-chain validator registry for peer discovery — no manual seeds.

Compute walkthrough (alpha)

# Build a WASM target
cargo build --release --target wasm32-unknown-unknown -p my-compute-program

# Submit private compute job
paraloom compute submit \
    --wasm target/wasm32-unknown-unknown/release/my_compute.wasm \
    --input ./input.json \
    --private --replication 3 --network devnet
# → job_id: 7f3c8...

paraloom compute status 7f3c8...
paraloom compute result 7f3c8... --decrypt > output.bin

See Compute layer for the privacy story (Pedersen + ownership proof + AES delivery), known limitations, and roadmap.

Path 2 — paraloom-core as a Rust dependency

Pin to a release tag while crate-internal APIs are still moving:

[dependencies]
paraloom = { git = "https://github.com/paraloom-labs/paraloom-core.git", tag = "v0.5.0-rc2" }
tokio    = { version = "1", features = ["full"] }

Withdraw from Rust

use paraloom::bridge::BridgeClient;
use paraloom::privacy::circuits::WithdrawalCircuit;
use paraloom::privacy::proof::generate_withdraw_proof;

let bridge = BridgeClient::new(rpc_url, program_id, payer)?;
let current_slot = bridge.current_slot().await?;
let expiration_slot = current_slot + 150;        // ~1 minute window

let proof_bytes = generate_withdraw_proof(WithdrawalCircuit {
    // Public inputs:
    nullifier, amount, recipient, merkle_root: bridge.merkle_root().await?, expiration_slot,
    // Private witnesses:
    value, randomness, secret, merkle_path,
}, &proving_key)?;

let sig = bridge
    .withdraw(proof_bytes, nullifier, amount, recipient, expiration_slot)
    .await?;

Why expiration_slot? See Solana bridge — it's enforced on-chain to bound replay windows, and it's part of the proof's public inputs so a leaked proof becomes useless after the window passes.

Custom Groth16 circuits

Paraloom's privacy circuits are built on ark-groth16 over BLS12-381. You can build your own circuit on the same primitives:

use ark_groth16::Groth16;
use ark_bls12_381::Bls12_381;
use paraloom::privacy::poseidon::poseidon_hash_pair;

struct MyCircuit { /* your fields */ }

impl ConstraintSynthesizer<Fr> for MyCircuit {
    fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> Result<(), SynthesisError> {
        // your constraints, optionally reusing Poseidon/Pedersen gadgets from `paraloom::privacy::gadgets`
        Ok(())
    }
}

For circuits intended to settle on Solana via the paraloom bridge, the verifying key must be present in the on-chain Anchor program — that's not a casual change; it requires a program upgrade. For circuits used purely off-chain (e.g. private compute correctness), no on-chain change is needed.

Patterns & footguns

Always include expiration_slot in withdraw proofs

The on-chain program rejects any withdrawal with current_slot > expiration_slot. Generate the proof with a window matched to expected confirmation latency (~150 slots ≈ 1 minute is a reasonable default on devnet). Closed under #61.

u64 range proofs are in-circuit

Don't try to enforce value bounds outside the circuit. The withdraw circuit already constrains value to fit in 64 bits via bit-decomposition; without that constraint, malicious provers could exploit field arithmetic to forge value. Closed under #60.

Cache proving keys

Proving keys are ~1.5 MB and load takes hundreds of ms. Cache them in your process; only re-load on rotation (which is rare — keys come from the MPC ceremony for mainnet).

Don't share notes by accident

Shielded notes are bearer instruments — anyone with the secret can spend the commitment. The CLI writes notes with chmod 600; if you build your own client, do the same.

Devnet vs mainnet

Devnet uses devnet-only proving keys generated locally. Mainnet keys come from the BGM17 phase-2 MPC ceremony — see Ceremony. A circuit verified against devnet keys won't verify against mainnet keys (and vice versa). Don't ship devnet artefacts to mainnet.

What paraloom-sdk will add (future)

The planned SDK is meant to make all of the above into a small surface area:

  • TypeScript bindings (browser + Node) wrapping the WASM proof system
  • A typed BridgeClient with expiration_slot defaults baked in
  • A wallet abstraction that manages note storage, randomness, and rotation
  • A WebSocket subscription layer for DepositEvent / WithdrawEvent

Until then: build against the CLI for stability, or pin a paraloom-core tag for the Rust path.

References

On this page