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

## Basic Withdrawal

```typescript theme={null}
const result = await sdk.vault.unshield({
  tokenAddress: '0x...token-address',
  amount: 1000000000000000000n, // 1 token
  recipientAddress: '0x...recipient-address',
});

console.log('Transaction hash:', result.txHash);
```

## Withdrawal Parameters

```typescript theme={null}
interface UnshieldParams {
  tokenAddress: Hex;          // Token to unshield
  amount: bigint;             // Amount in wei
  recipientAddress: Hex;      // Destination address
  onProgress?: OnProgress;    // Progress callback
}
```

| Parameter          | Type       | Required | Description                |
| ------------------ | ---------- | -------- | -------------------------- |
| `tokenAddress`     | `Hex`      | Yes      | Token contract address     |
| `amount`           | `bigint`   | Yes      | Amount in smallest unit    |
| `recipientAddress` | `Hex`      | Yes      | Destination public address |
| `onProgress`       | `function` | No       | Progress callback          |

## Progress Tracking

```typescript theme={null}
await sdk.vault.unshield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  recipientAddress: '0x...',
  onProgress: ({ step, message, txHash }) => {
    console.log(`Step: ${step}`);
    console.log(`Message: ${message}`);
    if (txHash) {
      console.log(`Transaction: ${txHash}`);
    }
  },
});
```

### Withdrawal Steps

| Step          | Description                      |
| ------------- | -------------------------------- |
| `preparing`   | Preparing withdrawal data        |
| `proving`     | Generating zero-knowledge proof  |
| `unshielding` | Executing withdrawal transaction |

```typescript theme={null}
enum UnshieldStep {
  preparing = 'preparing',
  proving = 'proving',
  unshielding = 'unshielding',
}
```

## Withdrawal Result

```typescript theme={null}
interface UnshieldResult {
  txHash: Hex;          // Main withdrawal transaction
  amount: bigint;       // Amount withdrawn
}
```

## Unwrapping WETH to ETH

WETH unwrapping is a separate step after the withdrawal settles on-chain.
Call `unwrapWeth()` once `getUnshieldStatus()` confirms the withdrawal is complete:

```typescript theme={null}
const WETH_ADDRESS = '0x4200000000000000000000000000000000000006';

// Step 1: Unshield WETH from the privacy pool
const result = await sdk.vault.unshield({
  tokenAddress: WETH_ADDRESS,
  amount: 1000000000000000000n,
  recipientAddress: '0x...my-address',
});

// Step 2: Poll until the withdrawal settles on-chain
// (use getUnshieldStatus or the polling mechanism)

// Step 3: Unwrap WETH to ETH (connected wallet must match recipient)
const unwrapTxHash = await sdk.vault.unwrapWeth(
  1000000000000000000n,
  '0x...my-address',
);
console.log('Unwrap tx:', unwrapTxHash);
```

## Withdraw to Self

Withdraw to your connected wallet:

```typescript theme={null}
const myAddress = sdk.auth.getAddress();

await sdk.vault.unshield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  recipientAddress: myAddress,
});
```

## Withdraw to Any Address

You can withdraw to any valid Ethereum address:

```typescript theme={null}
import { isEvmAddress } from '@sunnyside-io/privacy-boost';

const recipientAddress = '0x...';

// Validate address
if (!isEvmAddress(recipientAddress)) {
  throw new Error('Invalid recipient address');
}

await sdk.vault.unshield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  recipientAddress,
});
```

## Partial Withdrawals

Withdraw any amount up to your shielded balance:

```typescript theme={null}
// Check available balance first
const balance = await sdk.vault.getBalance(tokenAddress);

if (amount > balance) {
  throw new Error('Insufficient shielded balance');
}

await sdk.vault.unshield({
  tokenAddress,
  amount, // Any amount <= balance
  recipientAddress,
});
```

## Error Handling

```typescript theme={null}
try {
  await sdk.vault.unshield(params);
} catch (error) {
  switch (error.code) {
    case 'INSUFFICIENT_BALANCE':
      console.log('Not enough shielded balance');
      break;
    case 'INVALID_RECIPIENT':
      console.log('Invalid recipient address');
      break;
    case 'PROOF_GENERATION_FAILED':
      console.log('Failed to generate proof');
      break;
    case 'WITHDRAWAL_FAILED':
      console.log('Withdrawal transaction failed');
      break;
    default:
      console.log('Withdrawal error:', error.message);
  }
}
```

## Estimating Fees

```typescript theme={null}
// The withdrawal fee is included in the amount
// No separate gas estimation needed as the prover pays gas
```

## UI Example

```tsx theme={null}
import { useState } from 'react';

function UnshieldForm() {
  const [amount, setAmount] = useState('');
  const [recipient, setRecipient] = useState('');
  const [step, setStep] = useState('');
  const [loading, setLoading] = useState(false);

  const handleUnshield = async () => {
    setLoading(true);
    try {
      const parsedAmount = parseAmount(amount, decimals);

      await sdk.vault.unshield({
        tokenAddress,
        amount: parsedAmount,
        recipientAddress: recipient as Hex,
        onProgress: ({ step, message }) => {
          setStep(message);
        },
      });

      alert('Withdrawal successful!');
    } catch (error) {
      alert(`Withdrawal failed: ${error.message}`);
    } finally {
      setLoading(false);
      setStep('');
    }
  };

  return (
    <div>
      <input
        type="text"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount"
        disabled={loading}
      />
      <input
        type="text"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
        placeholder="Recipient address"
        disabled={loading}
      />
      <button onClick={handleUnshield} disabled={loading}>
        {loading ? step || 'Processing...' : 'Withdraw'}
      </button>
    </div>
  );
}
```

## Best Practices

### 1. Validate Balance Before Withdrawal

```typescript theme={null}
async function safeUnshield(params: UnshieldParams) {
  const balance = await sdk.vault.getBalance(params.tokenAddress);

  if (params.amount > balance) {
    throw new Error(
      `Insufficient balance. Available: ${balance}, Requested: ${params.amount}`
    );
  }

  return sdk.vault.unshield(params);
}
```

### 2. Validate Recipient Address

```typescript theme={null}
import { isEvmAddress } from '@sunnyside-io/privacy-boost';

function validateRecipient(address: string) {
  if (!isEvmAddress(address)) {
    throw new Error('Invalid Ethereum address');
  }
  if (address === '0x0000000000000000000000000000000000000000') {
    throw new Error('Cannot withdraw to zero address');
  }
}
```

### 3. Handle Long Operations

Withdrawals can take time due to proof generation:

```typescript theme={null}
const EXPECTED_DURATION = 30000; // 30 seconds

// Show estimated time
setStatus('Generating proof... This may take up to 30 seconds.');

await sdk.vault.unshield({
  ...params,
  onProgress: ({ step }) => {
    if (step === 'proving') {
      setStatus('Generating zero-knowledge proof...');
    } else if (step === 'unshielding') {
      setStatus('Submitting withdrawal transaction...');
    }
  },
});
```

## Next Steps

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