A private withdrawal spends up to six notes and pays a public recipient. Change and fee change stay in the pool as new notes.

Public and private

Circuit statement

The prover knows a signed EIP-1559 transaction paying value of coinId to recipientAddress, signed for VIRTUAL_CHAIN_ID. It also knows input notes under root, owned by the signer, with correct nullifiers, such that inputs = value + change + fee. The circuit binds the transaction’s gas fields (maxPriorityFeePerGas, maxFeePerGas, gasLimit). That is why Round withdrawals are prepared with fixed gas parameters before you sign.

Payout

The ShieldedWithdrawal event reports the full value that left the note. The withdrawal fee is charged whoever submits, relayer or not. The relayer fee is separate gas reimbursement.

Contract execution

  1. _checkSpend
  2. Verify the proof with 38 public inputs
  3. Record nullifiers
  4. Insert change and fee change in one batch
  5. Pay the relayer fee
  6. Pay the recipient value − protocolFee. A recipient equal to the pool is refused.
  7. Emit ShieldedWithdrawal
shieldedWithdrawal is guarded by whenWithdrawalsNotPaused only. The general paused flag does not stop exits.

Rounds

The circuit and contract have no notion of a round. Instant and Round withdrawals produce the same on-chain call. Round timing is enforced by the withdrawal scheduler.