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
This guide covers withdrawing tokens from your private balance to any public address.
Basic Withdrawal
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
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
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 |
signing | Signing transaction data |
proving | Generating zero-knowledge proof |
unshielding | Executing withdrawal transaction |
enum UnshieldStep {
preparing = 'preparing',
signing = 'signing',
proving = 'proving',
unshielding = 'unshielding',
}
Withdrawal Result
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:
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:
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:
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:
// 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
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
// The withdrawal fee is included in the amount
// No separate gas estimation needed as the prover pays gas
UI Example
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
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
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:
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