# Withdrawing

Withdrawing burns your deposit note and pays out `denomination - fee` to any recipient address you specify. The recipient does not have to be your wallet, does not have to exist yet, and is not linked on-chain to your depositor.

There are **two ways** to withdraw:

| Method                                          | Privacy | Cost                   | When to use                |
| ----------------------------------------------- | ------- | ---------------------- | -------------------------- |
| [Through a relayer](#withdrawing-via-a-relayer) | Strong  | Small fee (e.g., 0.5%) | The normal case. Use this. |
| [Self-relay](#self-relay-defeats-privacy)       | None    | Gas only               | Only for testing.          |

## What you'll need

* The deposit note you saved when you deposited.
* A recipient TON address. **It should not be your depositor wallet**, and ideally should not be linked on-chain to your depositor wallet.
* If using a relayer: the relayer's URL (e.g., `http://localhost:8090` for local dev, or the public relayer URL once mainnet launches).
* If self-relaying: another wallet you control, with mnemonic, that has enough TON to pay gas.

## Withdrawing via a relayer

This is the normal path. The relayer is a public service that submits the withdrawal transaction for you, meaning the on-chain "withdrawing wallet" is the relayer, not you, so there's no link to your depositor wallet.

```bash
tonado withdraw relay \
  "tonado-ton-1-testnet-0xa8b2...c4ef" \
  "EQRecipientAddress..." \
  --relayer http://localhost:8090
```

### What happens behind the scenes

> **The note never leaves your machine.** Everything cryptographic happens locally. The relayer only sees the final, broadcast-ready proof.

1. The CLI queries the relayer's `/status` endpoint to learn its address and fee policy.
2. The CLI queries the relayer's `/note/check` endpoint with the *derived nullifier hash* (never the note itself) to confirm the deposit is on-chain and not yet spent.
3. The CLI downloads the full commitment list from the relayer's `/tree/commitments` endpoint and rebuilds the Merkle tree locally. (Downloading the entire commitment list is what prevents the relayer from learning which specific deposit you're withdrawing.)
4. The CLI generates a Groth16 zero-knowledge proof locally using snarkjs. This takes 5–15 seconds for a circuit of \~30,000 constraints. The proof says: *"I know `(nullifier, secret)` such that `Poseidon(nullifier, secret)` is somewhere in the pool's Merkle tree."*
5. The proof additionally binds the recipient, the relayer's own address, the fee, and the refund as public inputs. The relayer can't tamper with any of these; if it tries, the on-chain verifier rejects.
6. The CLI sends `{proof, publicSignals, recipient, fee, refund}` to the relayer's `/withdraw` endpoint.
7. The relayer signs and submits the withdrawal transaction. The pool's on-chain verifier checks the proof, marks the nullifier spent, sends `denomination - fee` to the recipient, and pays the relayer its fee.

About **5 seconds later**, the recipient sees the funds arrive.

### Why this is safe

Even if the relayer is hostile, it cannot:

* **See your note.** It never leaves your machine.
* **Substitute the recipient.** The recipient is a public input bound to the proof. Tampering breaks the proof.
* **Substitute itself as a different relayer.** Same: bound to the proof.
* **Inflate the fee.** Same: bound to the proof.
* **Steal funds.** It has no nullifier, no secret, and no proof for any deposit it didn't see the broadcast for.

It *can* refuse to broadcast (censor you). If that happens, switch to a different relayer, or self-relay.

See [Threat model](/protocol-reference/threat-model.md) for the full attacker analysis.

## Self-relay (defeats privacy)

You can withdraw from a wallet you control instead of using a relayer. The CLI will print a warning. **Do not use this in production.**

```bash
tonado withdraw self \
  "tonado-ton-1-testnet-0xa8b2...c4ef" \
  "EQRecipientAddress..." \
  --mnemonic "<wallet you control>" \
  --network testnet
```

The cryptographic flow is identical. The difference is that the **wallet paying gas for the withdrawal** is on-chain and visible. Anyone watching can see "this wallet paid for a TONado Cash withdrawal," and if that wallet is at all linked to your depositor, the privacy is gone.

Self-relay is only useful for:

* Testing on testnet without a local relayer running.
* Recovery if no relayer will accept your withdrawal and you don't care about privacy.

## Choosing a recipient address

A few rules of thumb:

* **Never withdraw to your depositor wallet.** Defeats the entire point.
* **Never withdraw to an address tied to your depositor on-chain.** "Tied" means: ever sent funds to your depositor, received funds from your depositor, or shared a counterparty with it. Block explorers and clustering algorithms find these links.
* **Prefer a brand-new wallet** with no prior on-chain history.
* **Don't immediately move the funds out** to a known address either. Time and intermediate hops help.

## What if the withdrawal doesn't land

If you submit a withdrawal and nothing happens after a minute:

* Check that the relayer is online: `tonado relayer status --url <url>`.
* Re-run `tonado note check` to confirm the note isn't already spent (which would happen if a previous attempt actually succeeded but you missed the confirmation).
* If the relayer logs an error like "fee below floor," your fee was too small. Most relayers reject withdrawals where `fee < denomination * RELAYER_FEE_BPS / 10000` or `fee < RELAYER_FEE_MIN_TON`.

See [Troubleshooting](/using-tonado-cash/troubleshooting.md) for more.

## Once the withdrawal lands

Your note is **burned**. The pool tracks `nullifierHash = Poseidon(nullifier)` in its `nullifiers` dict, and any attempt to reuse the same note hits `ERR_ALREADY_SPENT`. There is no way to withdraw twice.

You can delete the note now, or keep it as a record. It's no longer sensitive.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tonadocash.com/using-tonado-cash/withdraw-walkthrough.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
