# Withdraw

> Exit the pool to any public address, with no on-chain link back to where the value came from.

A withdrawal spends notes and lands their value at a public address, which is how funds leave the pool. The destination and the amount are public, but there is no on-chain link to the original depositor or any addresses to which that note was transferred within the pool.

## How a withdrawal works

```mermaid
sequenceDiagram
    participant You
    participant Relayer
    participant Relay as PrivacyPoolRelay
    participant PoolVault
    participant Recipient as Recipient address

    Note over You,Relayer: fee quote agreed (see Relaying)
    Note over You: builds the transact proof, bound to thedestination and to PrivacyPoolRelay as processor
    You->>Relayer: proof + signed quote
    Relayer->>Relay: relay
    Relay->>PoolVault: transact
    PoolVault-->>Relay: withdrawn value
    Relay-->>Recipient: value minus feeAmount
```

A withdrawal moves through three stages:

1.  **Prove.** A withdrawal is a transact with a non-zero public output. You build the proof, binding it to the destination address (so the funds can't be redirected after proving) and to the [PrivacyPoolRelay](/protocol/contracts/privacy-pool-relay) contract as its processor. The `amount` is the net the recipient receives, so the input notes must also cover the relayer's fee, and any remainder returns to you as a change note.
2.  **Submit.** You hand the proof and signed quote to the relayer, which submits it through PrivacyPoolRelay to the [PoolVault](/protocol/contracts/pool-vault).
3.  **Release.** The PoolVault releases the withdrawn value to the relay contract, which forwards it to the destination minus the relayer's fee.

A fresh destination with no on-chain history keeps the withdrawal hard to link, while withdrawing to an address with established activity re-attaches your identity to the funds.

## How to withdraw

```ts
const withdrawAmount = "0x2386f26fc10000";   // 0.01 ETH in wei
const prepared = await session.prepareWithdraw({
    inputCommitments: [noteToSpend.commitment],
    amount: withdrawAmount,
    tokenId: NATIVE_ETH,
    recipientAddress: publicDestination,
});

const result = await session.relayWithdraw(prepared.relayerOptions[0]);
console.log("withdrawal tx:", result.txHash);
```

The input notes you pass as `inputCommitments` must be `ACTIVE`, the only spendable state in the note lifecycle (`INACTIVE` -> `PENDING` -> `ACTIVE` -> `SPENT` | `EXITED`). A `PENDING` note (deposited but not yet attested) cannot be spent.

`relayWithdraw(...)` returns a `TxReceipt` directly, so the hash is `result.txHash`.

## Constraints

-   **Fees and change.** The relayer's fee is taken from the withdrawn asset, while on-chain gas is paid in ETH by the relayer. A partial withdrawal is not a separate operation, just a withdrawal that leaves a change note.
-   Same quote-expiry and partial-failure considerations as a private [transfer](/operations/transfer) (relayer path only); see [Relaying](relaying).

## Behind the scenes

SDK call `session.prepareWithdraw(...)`, then `session.relayWithdraw(...)`
Contract method `PrivacyPoolRelay.relay(...)`, which calls `PoolVault.transact(...)`. The transact carries a public output leg
Circuit [`transact_NxM`](/protocol/circuits/transact) (the `Transact` template, e.g. `transact_1x2`). Withdrawal is a transact proof whose public `amountOut` / `tokenIdOut` signals are non-zero (they are `0` for a pure transfer)

## Variants

-   [Batch withdraw](batch-withdraw): more than five notes, one recipient, one transaction.
-   [Stealth withdraw](stealth-withdraw): pay a one-time address the recipient discovers by scanning.
-   [Withdraw and swap](withdraw-and-swap): leave as a different token, on this chain or another.
-   [Yield withdraw](yield-withdraw): unwrap a yield-share note to its underlying on the way out.

## What's next

-   The withdrawn value is public; nothing further happens in-protocol.
-   Any un-withdrawn remainder lands as a [change note](manage-notes), ready to spend.
