The contract stores protocol state required to verify private spends and construct future witnesses.

State model

StateStorage key or getterPurpose
Commitment leavesTreeDataKey::Leaf(index), get_commitmentsPublic commitment hashes
Leaf countTreeDataKey::LeafCount, get_commitment_countNumber of stored leaves
Leaf ephemeral keysTreeDataKey::LeafEphemeral(index), get_leaf_ephemeralRecipient-side shared-secret recovery input
Merkle rootTREE_ROOT_KEY, get_merkle_rootCurrent pool root
Merkle frontierTreeDataKey::PairwiseFrontier, get_pairwise_frontierPairwise LeanIMT insertion state
Root historyRootHistoryKey::Root(i), is_known_rootRecent roots accepted for proof verification
Nullifier stateNulifierDataKey::Nulifier(hash), is_nulifier_hash_consumedDouble-spend prevention
Token balanceToken contract balance, get_token_balancePublic token balance held by the pool contract
Public slot configget_public_slot_configPublic input/output slot counts

Commitment insertion

transact stores either zero output commitments or exactly two output commitments. Exactly one non-zero output commitment is rejected by the current contract shape. This keeps output handling aligned with the circuit’s two-output transaction model.

Nullifier checks

When a transaction spends private inputs:
  1. The circuit publishes nullifier hashes in public signals.
  2. The contract checks each hash against stored nullifier state.
  3. Any reused nullifier hash causes the transaction to fail.
  4. After proof verification succeeds, the contract marks the nullifier hashes as consumed.
The raw nullifier remains private in the witness.

Root history

The contract accepts proofs against recent roots rather than only the latest root. This allows transactions to be built from a recent view of pool state while new leaves are appended by other transactions. Current implementation detail:
ROOT_HISTORY_SIZE = 90
is_known_root checks whether the submitted stateRoot is still present in the ring buffer.

Token legs

Public token movement is handled inside transact:
Token legDirection
Public depositfrom -> pool contract
Public withdrawalPool contract -> public Stellar account encoded in public signals
Private transfers between pool commitments do not expose recipient or sender account movement on-chain beyond public signals, nullifier hashes, commitments, and the encrypted audit payload.