Flow classification
Every transact is one of three flows, discriminated by the sign ofexternal_amount:
FlowKind yourself. The orchestrator then routes:
- Deposits → always direct submission (user must sign the SOL/SPL transfer into the pool).
- Transfers / withdrawals → submitted via
relay_urlwhen set, otherwise direct. - Swaps → always submitted via
relay_url(DEX routing happens server-side).
UTXOs
keypair field is a field-element keypair (not an Ed25519 Solana keypair). It’s how you prove ownership of a note later — save it.
The fixed-arity circuit consumes exactly 2 inputs and 2 outputs. You supply the real ones; preflight pads with zero-UTXOs (random blinding) up to 2 each.
TransactOptions as the single knob surface
Every flow is one struct. Common fields:
Flow-specific extras:
Chaining:
The retry loop
transact() runs up to max_root_retries iterations of:
- fetch merkle proofs — pull on-chain state + rebuild local tree
- build circuit inputs — 90 signals, decimal-encoded
- prove — Groth16 via ark-circom (heavy; cached artifacts +
spawn_blocking) - encrypt chain notes — one compact note bound to output_commitments[0]
- submit — direct (with blockhash + transport retries) or
relay_urlPOST - reconcile indices — best-effort poll to fill
output_indices
IterationStatus:
Commitments and the cached tree
After each successful transact,TransactResult.cached_merkle_tree holds a local MerkleTree with the two new output commitments appended. Thread it into the next TransactOptions.cached_merkle_tree and you skip the /commitments fetch on the next call — the orchestrator seeds LoopState.merkle_tree from it directly.
sibling_commitments and pre_transaction_left_sibling on the result are populated from the post-tx tree so the next proof can use them without a fresh /commitments fetch.
Chain notes
Every non-zero-output transact emits exactly one compact v2 chain note:- Primary commitment:
output_commitments[0]— same value bound intochainNoteHash. - Plaintext: 8-byte LE unix timestamp.
- Encryption:
HKDF-SHA256(ikm=nk, salt=commitment)→ AES-256-GCM key + nonce. - Envelope:
[0x02 | count | (len, ciphertext)*]appended at instruction byte 521.
nk resolution falls back: opts.chain_note_nk → first non-zero output’s UTXO key → first non-zero input’s.
Related
- API reference — full type definitions.
- Error handling — the
Errorenum and when each variant fires. - Shared core concepts — protocol-level primitives that apply to both SDKs.