> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.umbraprivacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdraw

> getETAIntoATAWithdrawerFunction: move tokens from an EncryptedTokenAccount to an AssociatedTokenAccount. Returns WithdrawResult with Arcium MPC callback confirmation.

## Overview

The `withdraw` operation is the reverse of a deposit. It moves tokens from a user's `EncryptedTokenAccount (ETA)` back to their `AssociatedTokenAccount (ATA)`.

```
ETA (on-chain encrypted balance)
          │
          ▼
  Pool custody (on-chain SPL)  ──withdraw──▶  Your ATA
```

Arcium MPC verifies the withdrawal authorization. The operation follows the [dual-instruction pattern](/concepts/how-umbra-works#the-dual-instruction-pattern) — the SDK waits for both the handler and the callback transactions to confirm before returning.

## Usage

```typescript theme={null}
import { getETAIntoATAWithdrawerFunction } from "@umbra-privacy/sdk/withdrawal";

const withdraw = getETAIntoATAWithdrawerFunction({ client });

const result = await withdraw(
  destinationAddress, // where tokens go (typically client.signer.address)
  mint,               // SPL or Token-2022 mint address
  amount,             // amount in native token units (U64)
  options?,           // optional
);
```

## Parameters

<ParamField path="destinationAddress" type="Address" required>
  The Solana address whose ATA will receive the withdrawn tokens. This is typically `client.signer.address`.
</ParamField>

<ParamField path="mint" type="Address" required>
  The SPL or Token-2022 mint address. Must match a mint for which the user has an existing ETA.
</ParamField>

<ParamField path="amount" type="bigint" required>
  The amount to withdraw, in the token's native units. Must not exceed the current ETA balance.
</ParamField>

<ParamField path="options.optionalData" type="OptionalData32">
  32 bytes of caller metadata stored with the withdrawal. Defaults to 32 zero bytes. **Must be a pre-hashed or pre-encrypted 32-byte value** — never store plaintext identifiers.
</ParamField>

<ParamField path="options.accountInfoCommitment" type="Commitment" default="&#x22;confirmed&#x22;">
  Commitment level used for RPC account reads during withdrawal preparation.
</ParamField>

<ParamField path="options.computationOffset" type="U64">
  Optional Arcium computation-account PDA offset. Defaults to a fresh random `U64` per call (CSPRNG).
</ParamField>

<ParamField path="options.mpcCallbackDataOffset" type="U128">
  Optional MpcCallbackData PDA offset (paired with the signer in seeds). Defaults to a fresh random `U128` per call.
</ParamField>

<ParamField path="options.feeVaultOffset" type="U128" default="0n">
  Fee-vault PDA offset. The fee-vault path is currently unused for public-ATA withdrawals — leave at the default `0n` (sentinel for "no fee-vault interaction").
</ParamField>

<ParamField path="options.destinationProgram" type="Address">
  Optional observer-CPI destination program invoked after the MPC callback. Defaults to the System Program (sentinel = "no observer CPI").
</ParamField>

<ParamField path="options.hooks" type="WithdrawalHooks">
  Per-phase lifecycle hooks (`onValidationStart`, `onAccountFetchComplete`, `queueComputation`, `onComplete`, `onError`, etc.).
</ParamField>

<Note>
  There are no `priorityFees`, `awaitCallback`, `skipPreflight`, `maxRetries`, or `epochInfoCommitment` options on the withdrawer. Priority fees are configured at factory time. Callback waiting is always-on — the result's `callback` field is populated on success.
</Note>

## Return Value

Returns a `Promise<WithdrawResult>`. The shape mirrors `DepositResult`:

```typescript theme={null}
interface WithdrawResult {
  readonly signatures: readonly TransactionSignature[];
  readonly queueSignature: TransactionSignature;
  readonly callback?: CallbackOutcome;
  readonly rentClaim?: RentClaimOutcome;
}

type CallbackOutcome =
  | { readonly status: "finalized"; readonly signature?: TransactionSignature; readonly elapsedMs: number }
  | { readonly status: "pruned";    readonly elapsedMs: number }
  | { readonly status: "timed-out"; readonly elapsedMs: number };

type RentClaimOutcome =
  | { readonly claimed: true;  readonly signature: TransactionSignature }
  | { readonly claimed: false; readonly reason: string };
```

* `signatures` — every transaction signature submitted by this call, in submission order.
* `queueSignature` — the handler (queue computation) transaction signature.
* `callback` — present once the callback round-trip completes (always awaited). `"finalized"` means the MPC callback landed (and exposes `signature`); `"pruned"` / `"timed-out"` indicate the callback did not land — recover via the staged recoverer (below).
* `rentClaim` — best-effort rent reclamation outcome. The withdrawal itself still succeeded even when `claimed: false`.

## Example

```typescript theme={null}
import { getETAIntoATAWithdrawerFunction } from "@umbra-privacy/sdk/withdrawal";

const withdraw = getETAIntoATAWithdrawerFunction({ client });

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

const result = await withdraw(client.signer.address, USDC, 50_000_000n); // 50 USDC
console.log("Queue signature:", result.queueSignature);
if (result.callback?.status === "finalized") {
  console.log("Callback signature:", result.callback.signature);
}
```

## Withdrawal destination

The tokens are sent to the **ATA** associated with the `destinationAddress` for the given mint. If that ATA does not exist, the transaction will fail with an account-not-found error. Create the ATA first using standard token tooling if needed.

## Callback failure recovery

If the handler succeeds but the Arcium callback never lands (network partition, compute budget, Arcium outage), the tokens stay **staged** in the pool ATA. Reclaim them with the recoverer factories from `@umbra-privacy/sdk/account`:

```typescript theme={null}
import { getStagedSplRecovererFunction, getStagedSolRecovererFunction } from "@umbra-privacy/sdk/account";

const recover = getStagedSplRecovererFunction({ client });
await recover({ mint });
```

No MPC, no ZK proof — a synchronous recovery transaction. Use `getStagedSolRecovererFunction` for wSOL.

## Error Handling

Withdrawal errors are typed with a `stage` field identifying exactly where in the pipeline the failure occurred:

```typescript theme={null}
import { isEncryptedWithdrawalError } from "@umbra-privacy/sdk/errors";

try {
  const result = await withdraw(client.signer.address, mint, amount);
} catch (err) {
  if (isEncryptedWithdrawalError(err)) {
    switch (err.stage) {
      case "validation":
        // Zero amount, or no ETA exists for this mint.
        console.error("Withdrawal validation failed:", err.message);
        break;

      case "mint-fetch":
        // Could not fetch the mint account — check RPC connectivity and mint address.
        break;

      case "pda-derivation":
        // Could not derive required program addresses — unexpected on-chain state.
        break;

      case "instruction-build":
        // Could not construct the instruction — protocol state mismatch.
        break;

      case "transaction-sign":
        // User rejected the transaction in their wallet.
        showNotification("Withdrawal cancelled.");
        break;

      case "transaction-send":
        // Transaction submitted but confirmation timed out.
        // The transaction may have landed — check on-chain before retrying.
        console.warn("Confirmation timeout. Check signature on-chain:", err.cause);
        break;

      default:
        console.error("Withdrawal failed at stage:", err.stage, err);
    }
  } else {
    throw err;
  }
}
```

See [Error Handling](/reference/errors) for retry guidance and a full stage reference.

## Timing

Withdrawals follow the [dual-instruction pattern](/concepts/how-umbra-works#the-dual-instruction-pattern). The SDK submits the handler transaction, waits for Arcium to complete the MPC computation, then waits for the callback transaction to confirm. Total wall-clock time is typically a few seconds on a well-connected network.
