Skip to main content

Overview

The transact_spl instruction enables private transactions for SPL tokens. It functions identically to the transact instruction but operates on SPL token accounts instead of native SOL. Each SPL token has its own dedicated merkle tree, initialized via initialize_tree_account_for_spl_token.

Function Signature

Parameters

Proof
required
Zero-knowledge proof containing:
  • proof_a: First proof component (64 bytes)
  • proof_b: Second proof component (128 bytes)
  • proof_c: Third proof component (64 bytes)
  • root: Merkle tree root (32 bytes)
  • public_amount: Public transaction amount (32 bytes)
  • ext_data_hash: Hash of external data (32 bytes)
  • input_nullifiers: Array of 2 nullifiers (32 bytes each)
  • output_commitments: Array of 2 commitments (32 bytes each)
ExtDataMinified
required
Minified external data:
  • ext_amount: Token amount to deposit (positive) or withdraw (negative)
  • fee: Transaction fee in token base units
Vec<u8>
required
Encrypted data for the first output UTXO.
Vec<u8>
required
Encrypted data for the second output UTXO.

Accounts

AccountLoader<MerkleTreeAccount>
required
Token-specific merkle tree. PDA: ["merkle_tree", mint.key()].
  • Mutable: Yes
Account<NullifierAccount>
required
First nullifier account. PDA: ["nullifier0", proof.input_nullifiers[0]].
  • Mutable: Yes
  • Initialized: Yes (created by this instruction)
Account<NullifierAccount>
required
Second nullifier account. PDA: ["nullifier1", proof.input_nullifiers[1]].
  • Mutable: Yes
  • Initialized: Yes (created by this instruction)
SystemAccount
required
Cross-check nullifier. PDA: ["nullifier0", proof.input_nullifiers[1]].
  • Must not exist
SystemAccount
required
Cross-check nullifier. PDA: ["nullifier1", proof.input_nullifiers[0]].
  • Must not exist
Account<GlobalConfig>
required
Global configuration. PDA: ["global_config"].
Signer
required
Transaction signer.
  • Mutable: Yes
Account<Mint>
required
SPL token mint account.
Account<TokenAccount>
required
Signer’s token account (source for deposits).
  • Mutable: Yes
  • Must be owned by signer
  • Must have correct mint
UncheckedAccount
required
Recipient wallet account (owner of recipient_token_account).
Account<TokenAccount>
required
Recipient’s token account (destination for withdrawals).
  • Mutable: Yes
  • Token mint must match
  • Token authority must be recipient
Account<TokenAccount>
required
Tree’s associated token account.
  • Mutable: Yes
  • Auto-created if needed (init_if_needed)
  • Authority: global_config PDA
UncheckedAccount
required
Fee recipient’s associated token account.
  • Mutable: Yes
  • Must already exist for supported tokens
Program<Token>
required
SPL Token program.
Program<AssociatedToken>
required
Associated Token program.
Program<System>
required
Solana system program.

Behavior

Deposits (ext_amount > 0)

  1. Validates token account ownership and mint
  2. Checks deposit amount is within limit
  3. Transfers tokens from signer_token_account to tree_ata
  4. Validates and transfers deposit fee to fee_recipient_ata
  5. Verifies zero-knowledge proof
  6. Appends commitments to token-specific merkle tree
  7. Emits SPL commitment events

Withdrawals (ext_amount < 0)

  1. Validates token accounts
  2. Verifies merkle root and proof
  3. Transfers tokens from tree_ata to recipient_token_account
  4. Transfers withdrawal fee to fee_recipient_ata
  5. Appends commitments to merkle tree
  6. Emits SPL commitment events

Code Example

Allowed Tokens

  • USDC: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  • USDT: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
  • ORE: oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp
  • ZEC: A7bdiYdS5GjqGFtxf17ppRHtDKPkkRqbKtR27dxvQXaS
  • stORE: sTorERYB6xAZ1SSbwpK3zoK2EEwbBrc7TZAzg1uCGiH
  • jlUSDC: 9BEcn9aPEmhSPbPQeFGjidRiEKki46fVQDyPpSQXPA2D
  • jlWSOL: 2uQsyo1fXXQkDtcpXnLofWy88PxcvnfH2L8FPSE62FVU

Events

This instruction emits two SplCommitmentData events:

Validations

signer_token_account.owner must equal signer.key()
signer_token_account.mint must equal mint.key()
Mint address must be in the allowed tokens list (unless localnet).
Same validations as the transact instruction apply.

Errors

Error
Token account is not owned by the signer.
Error
Token account mint doesn’t match the provided mint.
Error
Mint address is not in the allowed tokens list.
Error
Merkle root not found in tree history.
Error
External data hash mismatch.
Error
Zero-knowledge proof verification failed.
Error
Deposit amount exceeds the token’s maximum deposit limit.

See Also