Protocol
Operations

Withdraw

How a withdrawal spends your notes and pays tokens out of a pool token account to a public Solana address, and what stays private when it does.

Umbra's clients, not the protocol

The platform wallet on this page is the wallet committed in slot 4 of your identity, which the protocol does use, for example in fee pricing. That Umbra's clients derive it from your account signature is their standard, not a protocol rule.

A withdrawal spends notes you own and pays tokens out of the pool token account to a public Solana address. The destination, the token and the amount are public, because tokens cross out of the pool there. Which notes paid for it, and whose they were, stay private.

Summary

A withdrawal is the shielded pool's process_3x3x2x2 transaction with a withdrawal lane active, which pays a public amount of one token from that token's pool token account to a destination named in the transaction. The transaction has two withdrawal lanes, and the SDK uses one. It spends up to three of your notes and usually creates one new note: your change.

Notes spent
input note
input note
dummy
Deposit lanes
dummy
dummy
One transaction
same shape every time
Notes created
output note
dummy
dummy
Withdrawal lanes
withdrawal
dummy
A withdrawal that spends two notes, pays out through one withdrawal lane and returns the change as a new note.

Each withdrawal lane has a public counterparty, which the proof binds. For an SPL Token or Token-2022 token the destination is a token account of that mint and the counterparty is its owner. For SOL the destination is the wallet itself, paid plain SOL from the pool's lamport account.

The screener screens the submitter and each lane's counterparty, and signs only a Clear verdict. A relayer submits and adds the nullifier freshness proof, as for a private transfer.

A withdrawal needs no allowlist entry: when governance revokes a token, new deposits of it stop, but transfers and withdrawals continue.

Before you start

  • You are registered and hold enough. The proof shows your identity commitment is in the identity tree, and your notes of the token must cover the amount, the protocol fee on the withdrawal and on any change note, and the relayer's tip.
  • The destination can receive it. A token destination must be a token account of the same mint; if it does not exist yet, the relayer can create the owner's associated token account for a published extra fee. A SOL payment to a wallet that does not exist yet must be at least the rent-exempt minimum.
  • The pool can fund it. One pool token account per token, shared by every pool index, must hold the amount. For SOL, the pool's lamport account must hold it above the minimum balance it always keeps.
  • A relayer will carry it, and every attester is available. As for a private transfer.

How a withdrawal runs

A withdrawal runs in the same three phases as a private transfer. Your device prepares the transaction, collects the six attester signatures while it proves, and hands the instruction to a relayer, which adds the freshness proof and submits.

flowchart LR
A["Your device prepares"] --> B["Attesters sign"]
A --> C["Your device proves"]
B --> D["Relayer submits"]
C --> D
D --> E["Pool verifies"]
E --> F["Pool pays out"]

A withdrawal adds a public destination, which the screener also screens, and a payout at the very end.

Phase 1: preparing on your device

Your device prepares the transaction itself, with one read each from the relayer and the indexer.

sequenceDiagram
participant U as Your device
participant R as Relayer
participant I as Indexer
participant Z as Zone authority
U->>R: read policy
R-->>U: submitter, tip accounts
U->>I: one read for paths
I-->>U: paths and fee rates
U->>U: build change note and lane
opt notes outside zone 0
  U->>Z: authorisation message
  Z-->>U: EdDSA signature
end

1. Your device reads the relayer's policy and selects notes

The SDK reads the relayer's policy first, because the relayer's tip account is part of what every attester signs. It checks that the relayer carries this token and accepts the tip, adds the creation fee if the destination does not exist yet, and chooses up to three of your notes of the token.

2. The indexer returns paths and fee rates

One request to the indexer returns the Merkle paths for your notes, your identity tree path and the fee rates. The paths must match roots the pool still accepts, or the proof fails.

3. Your device builds the change note and the lane

The SDK prices the protocol fees, then builds the withdrawal lane (token, amount and destination) and your change note, which it encrypts to you. It builds the sender record and splits the per-transaction viewing key into three shares, one per guardian node. The destination is part of the context digest every attester signs, and the proof binds the counterparty, so nobody downstream can redirect the payout.

4. The zone authority signs, outside zone 0

If the notes you spend, or your change note's zone, are outside zone 0, a signer from that zone's authority signs a message derived from the context digest. The proof checks these signatures, and they never appear on chain. See zones.

Phase 2: collecting signatures and proving

The screener checks the submitter and the public counterparty of each withdrawal lane, while the other attesters sign and your device proves.

sequenceDiagram
participant U as Your device
participant G as Guardian nodes
participant S as Screener
participant A as Other attesters
Note over A: fee enclave, data endorser
par attest
  U->>G: one encrypted share each
  G-->>U: three BLS signatures
  U->>S: payload and accounts
  S-->>U: BLS signature if Clear
  U->>A: openings, change note
  A-->>U: two BLS signatures
and prove
  U->>U: transaction proof
end

5. Each guardian node checks its share and signs

Each node decrypts its share of the per-transaction viewing key, checks it against its commitment, escrows it and signs the context digest. It does not check the proof or any amount. See what a guardian node checks.

6. The screener screens the submitter and destination party

The screener works out the submitter and the destination party: the owner of the destination token account, or the wallet itself for SOL. If any screening answer is missing the verdict is Indeterminate, even if another flagged a wallet; otherwise any flag makes it Deny. Only Clear produces a signature, so without it the withdrawal cannot land. See screening.

7. The fee enclave opens the fee commitments and signs

The fee enclave opens the three fee commitments, applies any discount from its schedule, and refuses a withdrawal lane whose token is not one of the transaction's three fee slots. It escrows the openings and signs.

8. The data endorser checks the change note and signs

The data endorser checks that the encrypted change note matches the memo digest in the payload, archives it and signs. Your device later finds the change note in that archive.

9. Your device proves and hands the instruction to the relayer

Your device makes the transaction proof and combines the six BLS signatures into one combined attester signature. It sends the relayer the finished instruction, leaving the nullifier root and freshness proof for the relayer to fill.

Phase 3: relaying, verifying and paying out

The relayer submits, and the pool pays out only once it has verified everything.

sequenceDiagram
participant R as Relayer
participant P as Shielded pool
participant T as Pool token account
participant X as Destination
R->>R: validate, prove freshness
R->>P: signed v1 transaction
P->>P: checks, both proofs
P->>T: pay the lane
T->>X: tokens
P->>P: credit relayer tip

10. The relayer validates, creates the destination if asked, and submits

The relayer checks the request against its policy and the chain, including that an existing token destination holds the lane's token. It creates a missing destination only if it is the named owner's associated token account and the tip covers its published creation fee; a relayer with no such fee creates none. It then proves nullifier freshness against the latest nullifier root and sends a Solana v1 transaction as submitter and fee payer, with any destination created ahead of the withdrawal.

11. The shielded pool checks and verifies

The program runs the ordered checks of any value transaction, listed on the deposit page. Before it verifies the combined attester signature or either proof, it refuses the transaction if the pool cannot fund the withdrawal lanes. The proof's public input includes each lane's token, amount and counterparty, which the program reads from the destination account itself, so a proof made for one destination cannot pay another.

12. The shielded pool records the spend and pays out

The program stages the three nullifiers and three fee commitments and appends three note commitments to the note tree. It then pays each lane: a token by a checked transfer signed by the pool token authority, a program-derived address only the program's own logic can make sign, and SOL in lamports from the pool's lamport account straight to the wallet, with no signature at all. Finally it credits the relayer's tip to its tip accrual for that token. See SOL.

What a chain observer sees

Withdraw
Everything a chain observer can read
Program
Shielded pool, process_3x3x2x2
Submitter and fee payer
The relayer's own address
Destination
The token account or wallet paid
Token
The mint paid out
Amount
The amount paid out
Relayer tip
An amount and a token, or nothing
Nullifiers
Three field elements
New note commitments
Three field elements
Fee commitments
Three field elements
Who withdrew
Private
Which notes paid for it
Private
Change left behind
PrivateInside a note commitment
Protocol fee
PrivateInside the fee commitments
Zone
Private
A withdrawal's public side is the lane. The notes behind it stay private.

What can go wrong

  • The screener does not clear the submitter or the destination. No signature is produced, nothing moves and your notes are untouched.
  • The destination is not usable. A token account of another mint, a missing account the relayer was not paid to create, or a SOL payment below the rent-exempt minimum to a wallet that holds no lamports is refused.
  • The pool cannot fund it. The program refuses before it verifies the signature or the proofs.
  • A root or the proof is too old, a nullifier is already staged, or an attester is down. These fail as they do for a private transfer, and the escape hatch is the way out when an attester will not sign.

Privacy notes

  • The lane is public. Anyone can see where the tokens went, which token and how much.
  • Amounts and timing correlate. A withdrawal of about a deposit's amount, soon after it, is easy to link; waiting and choosing amounts that do not echo a deposit weakens that. See how privacy is lost.
  • Destinations can identify you. Withdrawing to your main or platform wallet links the withdrawal to your public registration, and reusing a destination links your withdrawals to each other.
  • Your platform wallet counts as you for fees. A withdrawal to it is priced as same-owner and one to your main wallet is not, because only the platform wallet is committed inside your identity. See the fee model.
  • The tip's token is public. By default it is the token withdrawn, which is public here anyway.

Last updated

On this page