Skip to main content
PrivacyPoolsContract is the on-chain execution boundary for the Arcane Compliance privacy pool on Stellar/Soroban. It receives a zero-knowledge proof and its associated public signals from your application, verifies the proof against the current pool state, applies any public token movements, updates the Merkle tree and nullifier registry, and emits an encrypted audit event. Every deposit, withdrawal, transfer, and mixed transaction your users perform flows through this single contract.

The transact entrypoint

The contract exposes one public transaction entrypoint. All operation types — deposits, withdrawals, transfers, and mixed transactions — are expressed through this single method.
pub fn transact(
    env: &Env,
    from: Address,
    proof_bytes: Bytes,
    pub_signals_bytes: Bytes,
    encoded: Bytes,
) -> Result<(), Error>
ArgumentSourceMeaning
fromStellar walletThe authenticated transaction signer. The contract calls from.require_auth() before doing anything else.
proof_bytesArcane SDKSerialized Groth16 proof generated client-side from the witness.
pub_signals_bytesArcane SDKSerialized public inputs and outputs for the circuit, including the state root, nullifier hashes, and output commitments.
encodedApplication / SDK flowEncrypted audit payload that the contract emits verbatim as AuditEncodedDigest.digest. Your application prepares this using the SDK.

Execution sequence

When you call transact, the contract performs the following steps in order. A failed check at any step causes the entire transaction to revert.
The contract stores either zero output commitments or exactly two output commitments. Submitting exactly one non-zero output commitment is rejected. This constraint keeps the contract aligned with the circuit’s two-output transaction model.

Transaction types

The user-facing operation names are protocol-level concepts. They are not separate Soroban methods — they are all represented as different shapes of transact inputs.
User-facing operationtransact representation
DepositPublic deposit token leg plus one or two private output commitments
WithdrawalPrivate input commitment spend plus a public withdrawal token leg
TransferPrivate input commitment spend plus private output commitments — no public token movement
Mixed transactionCombination of private input spends, private output commitments, public deposits, and public withdrawals

What the contract validates

transact enforces the following checks before accepting a transaction:
  • Authenticationfrom.require_auth() must pass. The signer must authorize the invocation.
  • Root history membership — the stateRoot embedded in pub_signals_bytes must be present in the contract’s root history ring buffer. Proofs built against a root that has aged out of history will be rejected.
  • Nullifier uniqueness — every nullifier hash extracted from pub_signals_bytes must not already be marked as consumed. Any reuse causes an immediate revert.
  • Groth16 proof validity — the proof must verify against the stored verification key using the provided public signals. An invalid or malformed proof is rejected.
If the stateRoot in your proof has aged out of the root history window, you must rebuild the witness against a more recent root and re-generate the proof before resubmitting. See On-Chain State for details on root history.

Public getters

The contract exposes read-only getter methods for all pool state your application needs to construct witnesses, monitor pool health, or display balances.
GetterPurpose
get_merkle_rootReturns the current Merkle root of the commitment tree
get_merkle_depthReturns the configured depth of the Merkle tree
get_commitment_countReturns the number of leaves currently stored in the tree
get_commitmentsReturns all commitment leaf hashes
get_leaf_ephemeral(index)Returns the ephemeral BabyJubJub public key stored alongside a given leaf, used for recipient-side shared-secret recovery
get_pairwise_frontierReturns the current pairwise frontier state used by the LeanIMT insertion algorithm
is_nulifier_hash_consumed(hash)Returns true if the given nullifier hash has already been spent
is_known_root(root)Returns true if the given root is still present in the root history ring buffer
get_token_balanceReturns the current token balance held by the pool contract
get_public_slot_configReturns the public input and output slot configuration for the circuit
get_adminReturns the contract admin address
Before building a withdrawal or transfer witness, call get_merkle_root, get_commitment_count, and get_commitments to get a consistent snapshot of the current tree state. Use the root returned by get_merkle_root as the stateRoot in your proof to maximize the chance that the root is still in history when your transaction lands.