Withdrawals
This guide covers withdrawing tokens from the privacy pool back to a public wallet using the Privacy Boost React SDK.
Basic Withdrawal
import { useVault } from '@testinprod-io/privacy-boost-react';
function UnshieldButton() {
const { unshield } = useVault();
const handleUnshield = async () => {
const result = await unshield({
tokenAddress: '0x...token-address',
amount: '1.0',
});
console.log('Withdrawal request:', result.requestId);
};
return <button onClick={handleUnshield}>Withdraw</button>;
}
Withdrawal Parameters
| Parameter | Type | Required | Description |
|---|
tokenAddress | Hex | Yes | Token contract address |
amount | string | bigint | Yes | Amount (human-readable string or wei) |
recipientAddress | Hex | No | Destination wallet (defaults to connected wallet) |
onProgress | OnProgress | No | Progress callback |
Progress Tracking
function WithdrawWithProgress() {
const { unshield } = useVault();
const [step, setStep] = useState<string | null>(null);
const handleUnshield = async () => {
await unshield({
tokenAddress: '0x...token-address',
amount: '1.0',
onProgress: ({ message }) => setStep(message),
});
setStep(null);
};
return (
<div>
<button onClick={handleUnshield}>Withdraw</button>
{step && <p>{step}</p>}
</div>
);
}
Proof generation can take several seconds. Always show progress feedback to users during withdrawals.
Complete Example
import { useState } from 'react';
import { useVault, useBalances, useAuth } from '@testinprod-io/privacy-boost-react';
function UnshieldForm({ tokenAddress }: { tokenAddress: string }) {
const { unshield } = useVault();
const { refresh } = useBalances();
const { address } = useAuth();
const [amount, setAmount] = useState('');
const [recipient, setRecipient] = useState('');
const [loading, setLoading] = useState(false);
const [step, setStep] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const handleUnshield = async () => {
setLoading(true);
setError(null);
try {
await unshield({
tokenAddress,
amount,
recipientAddress: recipient || undefined,
onProgress: ({ message }) => setStep(message),
});
setAmount('');
await refresh();
} catch (err: any) {
if (err.code !== 'TRANSACTION_REJECTED') {
setError(err.message);
}
} finally {
setLoading(false);
setStep(null);
}
};
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 (default: ${address})`}
disabled={loading}
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
{step && <p>{step}</p>}
<button onClick={handleUnshield} disabled={loading || !amount}>
{loading ? 'Withdrawing...' : 'Withdraw'}
</button>
</div>
);
}
Error Handling
try {
await unshield({ tokenAddress, amount });
} catch (error: any) {
switch (error.code) {
case 'TRANSACTION_REJECTED':
return;
case 'INSUFFICIENT_BALANCE':
setError('Not enough shielded balance');
break;
case 'PROOF_GENERATION_FAILED':
setError('Failed to generate proof. Please try again.');
break;
default:
setError(error.message);
}
}
Best Practices
1. Show Proof Generation Time
Always show a progress indicator during proof generation.
2. Refresh Balances After Withdrawal
await unshield({ tokenAddress, amount });
await refresh();
Next Steps