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

# Withdrawals

# Withdrawals

This guide covers withdrawing tokens from your private balance to any public Ethereum address.

## Basic Withdrawal

```swift theme={null}
do {
    let result = try await sdk.unshield(
        tokenAddress: "0x...token-address",
        amount: "1000000000000000000", // 1 token
        recipient: "0x...recipient-address"
    )
    print("Transaction hash: \(result.txHash)")
} catch {
    print("Withdrawal failed: \(error)")
}
```

## Withdrawal Parameters

| Parameter      | Type     | Required | Description                   |
| -------------- | -------- | -------- | ----------------------------- |
| `tokenAddress` | `String` | Yes      | Token contract address        |
| `amount`       | `String` | Yes      | Amount in wei (smallest unit) |
| `recipient`    | `String` | Yes      | Destination Ethereum address  |

## Withdrawal Steps

Withdrawals involve zero-knowledge proof generation, which can take up to 30 seconds:

| Step        | Description                            |
| ----------- | -------------------------------------- |
| Preparing   | Preparing withdrawal data              |
| Signing     | Signing transaction data               |
| Proving     | Generating zero-knowledge proof        |
| Unshielding | Executing withdrawal transaction       |
| Unwrapping  | Unwrapping WETH to ETH (if applicable) |

## Withdrawal Result

```swift theme={null}
struct UnshieldResult {
    let txHash: String  // Main withdrawal transaction hash
    let fee: String     // Fee paid (in wei)
}
```

## Withdraw to Self

Withdraw to your connected wallet address:

```swift theme={null}
let myAddress = sdk.getWalletAddress()!

let result = try await sdk.unshield(
    tokenAddress: "0x...",
    amount: "1000000000000000000",
    recipient: myAddress
)
```

## Withdraw to Any Address

You can withdraw to any valid Ethereum address:

```swift theme={null}
let recipientAddress = "0x..."

// Validate address first
guard sdk.isValidAddress(recipientAddress) else {
    print("Invalid recipient address")
    return
}

let result = try await sdk.unshield(
    tokenAddress: "0x...",
    amount: "1000000000000000000",
    recipient: recipientAddress
)
```

## Partial Withdrawals

Withdraw any amount up to your shielded balance:

```swift theme={null}
// Check available balance first
let balance = try await sdk.getBalance(tokenAddress: tokenAddress)

guard let shielded = UInt64(balance.shieldedBalance),
      let unshieldAmount = UInt64(amount),
      unshieldAmount <= shielded else {
    print("Insufficient shielded balance")
    return
}

let result = try await sdk.unshield(
    tokenAddress: tokenAddress,
    amount: amount,
    recipient: recipientAddress
)
```

## Error Handling

```swift theme={null}
do {
    let result = try await sdk.unshield(
        tokenAddress: tokenAddress,
        amount: amount,
        recipient: recipient
    )
} catch SDKError.insufficientBalance {
    print("Not enough shielded balance")
} catch SDKError.invalidAddress {
    print("Invalid recipient address")
} catch SDKError.invalidAmount {
    print("Invalid amount format")
} catch SDKError.walletError(let message) {
    print("Wallet error: \(message)")
} catch SDKError.networkError(let message) {
    print("Network error: \(message)")
} catch {
    print("Withdrawal error: \(error)")
}
```

## Best Practices

### 1. Validate Balance Before Withdrawal

```swift theme={null}
func validateWithdrawal(tokenAddress: String, amount: String) async throws {
    let balance = try await sdk.getBalance(tokenAddress: tokenAddress)

    guard let shielded = UInt64(balance.shieldedBalance),
          let requested = UInt64(amount),
          requested <= shielded else {
        throw SDKError.insufficientBalance(message: "Insufficient shielded balance")
    }
}
```

### 2. Validate Recipient Address

```swift theme={null}
func validateRecipient(_ address: String) throws {
    guard sdk.isValidAddress(address) else {
        throw SDKError.invalidAddress
    }
    guard address != "0x0000000000000000000000000000000000000000" else {
        throw SDKError.invalidAddress
    }
}
```

### 3. Handle Long Operations

Withdrawals include zero-knowledge proof generation which can take up to 30 seconds. Show a loading indicator and inform users about the expected wait time.

```swift theme={null}
// Show estimated time in your UI
statusLabel.text = "Generating proof... This may take up to 30 seconds."

let result = try await sdk.unshield(
    tokenAddress: tokenAddress,
    amount: amount,
    recipient: recipient
)

statusLabel.text = "Withdrawal complete!"
```

## Next Steps

* [Deposits Guide](./deposits)
* [Transfers Guide](./transfers)
* [Balances Guide](./balances)
* [Error Handling](./error-handling)
