Notes & commitments
A noteis the pool's unit of value — a private UTXO. It exists on-chain only as a Poseidon commitmentin an append-only Merkle tree of depth 26 (capacity 2²⁶ ≈ 67 million notes).
Structure
note = (asset_id, amount, owner_pk, blinding)
note_commitment = Poseidon(asset_id, amount, secret)
// the commitment binds every field; the blinding/secret makes
// identical (asset, amount) pairs produce unlinkable commitments- asset_id — which asset the note carries (ZUL or wSOL). It is public at shield/unshield (the vault movement reveals it anyway) but stays private inside transfers.
- amount— arbitrary value in the asset's base units.
- owner — a spending key; ownership is proven in-circuit from the secret key, never revealed.
- blinding — randomness that makes commitments unlinkable.
The commitment tree
Commitments append left-to-right; the tree never deletes. Spent notes stay in the tree forever — "spent" exists only in the nullifier set. The current root is part of chain state, and proofs reference a recent root, so a transfer built a moment ago stays valid as the tree grows.
Poseidon is used here (instead of the blake3 used for account state) because the tree must be walked inside the circuit: Poseidon costs a few constraints per hash where SHA-class functions cost tens of thousands.
Note discovery
The chain cannot tell recipients "you got paid" — that would be a link. Instead, each output note is encrypted to its recipient (X25519 ECDH + XChaCha20-Poly1305) and shipped inside the transaction. Wallets scan blocks and trial-decrypt every ciphertext; the ones that open are yours. The SDK's shielded client automates scanning and keeps an encrypted local note store.
Depth 26 ≈ 67M notes is a deliberate prototype bound — big enough for years of testnet activity, small enough to keep proofs fast. The live fill rate is visible on /explorer/shielded.