> ## 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

```kotlin theme={null}
try {
    val result = withContext(Dispatchers.IO) {
        sdk.unshield(
            tokenAddress = "0x...token-address",
            amount = "1000000000000000000", // 1 token
            recipient = "0x...recipient-address"
        )
    }
    println("Transaction hash: ${result.txHash}")
} catch (e: SDKError) {
    println("Withdrawal failed: $e")
}
```

## 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

```kotlin theme={null}
data class UnshieldResult(
    val txHash: String,  // Main withdrawal transaction hash
    val fee: String      // Fee paid (in wei)
)
```

## Withdraw to Self

Withdraw to your connected wallet address:

```kotlin theme={null}
val myAddress = sdk.getWalletAddress()!!

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipient = myAddress
    )
}
```

## Withdraw to Any Address

You can withdraw to any valid Ethereum address:

```kotlin theme={null}
val recipientAddress = "0x..."

// Validate address first
require(sdk.isValidAddress(recipientAddress)) { "Invalid recipient address" }

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipient = recipientAddress
    )
}
```

## Partial Withdrawals

Withdraw any amount up to your shielded balance:

```kotlin theme={null}
// Check available balance first
val balance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = tokenAddress)
}

val shielded = balance.shieldedBalance.toBigInteger()
val unshieldAmount = amount.toBigInteger()

require(unshieldAmount <= shielded) { "Insufficient shielded balance" }

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = tokenAddress,
        amount = amount,
        recipient = recipientAddress
    )
}
```

## Error Handling

```kotlin theme={null}
try {
    val result = withContext(Dispatchers.IO) {
        sdk.unshield(
            tokenAddress = tokenAddress,
            amount = amount,
            recipient = recipient
        )
    }
} catch (e: SDKError) {
    when (e) {
        is SDKError.InsufficientBalance ->
            println("Not enough shielded balance")
        is SDKError.InvalidAddress ->
            println("Invalid recipient address")
        is SDKError.InvalidAmount ->
            println("Invalid amount format")
        is SDKError.WalletError ->
            println("Wallet error: ${e.message}")
        is SDKError.NetworkError ->
            println("Network error: ${e.message}")
        else ->
            println("Withdrawal error: $e")
    }
}
```

## Best Practices

### 1. Validate Balance Before Withdrawal

```kotlin theme={null}
suspend fun validateWithdrawal(tokenAddress: String, amount: String) {
    val balance = withContext(Dispatchers.IO) {
        sdk.getBalance(tokenAddress = tokenAddress)
    }

    val shielded = balance.shieldedBalance.toBigInteger()
    val requested = amount.toBigInteger()

    require(requested <= shielded) {
        "Insufficient balance. Available: ${balance.shieldedBalance}, Requested: $amount"
    }
}
```

### 2. Validate Recipient Address

```kotlin theme={null}
fun validateRecipient(address: String) {
    require(sdk.isValidAddress(address)) { "Invalid Ethereum address" }
    require(address != "0x0000000000000000000000000000000000000000") {
        "Cannot withdraw to zero address"
    }
}
```

### 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.

```kotlin theme={null}
// In a ViewModel
viewModelScope.launch {
    _state.value = WithdrawState.Loading("Generating proof... This may take up to 30 seconds.")

    try {
        val result = withContext(Dispatchers.IO) {
            sdk.unshield(
                tokenAddress = tokenAddress,
                amount = amount,
                recipient = recipient
            )
        }
        _state.value = WithdrawState.Success(result.txHash)
    } catch (e: SDKError) {
        _state.value = WithdrawState.Error(e)
    }
}
```

## Next Steps

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