Withdraw SOL
Withdraw SOL from your private balance to any recipient address.
const result = await client . withdraw ({
lamports: number ,
recipientAddress? : string ,
referrer? : string
})
Parameters
Parameter Type Required Description lamportsnumberYes Amount in lamports to withdraw recipientAddressstringNo Recipient wallet address. Defaults to your own wallet referrerstringNo Optional referrer wallet address
Returns
{
tx : string , // Transaction signature
recipient : string , // Recipient address
amount_in_lamports : number , // Amount received (after fees)
fee_in_lamports : number , // Fee paid
isPartial : boolean // True if balance was insufficient for full amount
}
Example
import { PrivacyCash } from 'privacycash'
const client = new PrivacyCash ({
RPC_url: process . env . SOLANA_RPC_URL ! ,
owner: process . env . PRIVATE_KEY !
})
// Withdraw 0.1 SOL to a clean wallet
const result = await client . withdraw ({
lamports: 0.1 * 1_000_000_000 ,
recipientAddress: 'CLEAN_WALLET_ADDRESS'
})
console . log ( 'Transaction:' , result . tx )
console . log ( 'Amount received:' , result . amount_in_lamports / 1_000_000_000 , 'SOL' )
console . log ( 'Fee paid:' , result . fee_in_lamports / 1_000_000_000 , 'SOL' )
Withdrawal Fees
Component Amount Base fee 0.006 SOL per recipient Protocol fee 0.35% of withdrawal amount
Fee Calculation Example
// Withdrawing 1 SOL
const withdrawAmount = 1_000_000_000 // 1 SOL in lamports
// Fee calculation:
// Base: 0.006 SOL = 6,000,000 lamports
// Protocol: 1 SOL × 0.35% = 0.0035 SOL = 3,500,000 lamports
// Total fee: ~9,500,000 lamports (~0.0095 SOL)
const result = await client . withdraw ({
lamports: withdrawAmount ,
recipientAddress: 'RECIPIENT'
})
console . log ( 'Requested:' , withdrawAmount / 1e9 , 'SOL' )
console . log ( 'Received:' , result . amount_in_lamports / 1e9 , 'SOL' )
console . log ( 'Fee:' , result . fee_in_lamports / 1e9 , 'SOL' )
How Withdrawals Work
UTXO Selection
The SDK selects your largest UTXOs to cover the withdrawal amount
ZK Proof Generation
A zero-knowledge proof is generated proving you own the funds
Relayer Submission
The proof is sent to the relayer, which signs and submits the transaction
Funds Received
The recipient receives funds with no on-chain link to your wallet
Privacy Guarantee
The withdrawal transaction on-chain (visible on SolScan or other explorers) contains no information about the original depositor. The zero-knowledge proof ensures:
The relayer cannot modify the recipient address
The relayer cannot modify the amount
Any tampering causes the transaction to fail
Partial Withdrawals
If your private balance is less than the requested amount, the SDK performs a partial withdrawal:
// You have 0.05 SOL private balance
// Request to withdraw 0.1 SOL
const result = await client . withdraw ({
lamports: 100_000_000 , // 0.1 SOL
recipientAddress: 'RECIPIENT'
})
if ( result . isPartial ) {
console . log ( 'Partial withdrawal - balance was insufficient' )
console . log ( 'Actually withdrew:' , result . amount_in_lamports / 1e9 , 'SOL' )
}
Withdraw to Self
If you omit recipientAddress, funds are withdrawn to your own wallet:
// Withdraw to your own wallet (unshield)
const result = await client . withdraw ({
lamports: 50_000_000 // 0.05 SOL
})
console . log ( 'Withdrawn to:' , result . recipient ) // Your wallet address
Withdrawing to your own wallet reduces privacy since it links your deposit and withdrawal addresses.
Best Practices
Use Clean Wallets Always withdraw to a fresh, never-used wallet address
Wait Before Withdrawing Wait at least a day between deposit and withdrawal
Split Withdrawals Split large amounts into multiple smaller withdrawals over time
Vary Amounts Don’t withdraw the exact same amount you deposited
Example: Privacy-Optimized Withdrawal
// Deposited 1 SOL yesterday
// Good: Withdraw different amounts over multiple days
await client . withdraw ({
lamports: 400_000_000 , // 0.4 SOL
recipientAddress: 'CLEAN_WALLET_1'
})
// Wait a day...
await client . withdraw ({
lamports: 350_000_000 , // 0.35 SOL
recipientAddress: 'CLEAN_WALLET_2'
})
// Wait another day...
await client . withdraw ({
lamports: 200_000_000 , // 0.2 SOL
recipientAddress: 'CLEAN_WALLET_3'
})
Error Handling
try {
const result = await client . withdraw ({
lamports: 100_000_000 ,
recipientAddress: 'RECIPIENT'
})
console . log ( 'Withdrawal successful:' , result . tx )
} catch ( error ) {
if ( error . message . includes ( 'no balance' )) {
console . error ( 'No private balance available' )
} else if ( error . message . includes ( 'Need at least 1 unspent UTXO' )) {
console . error ( 'No UTXOs available for withdrawal' )
} else {
console . error ( 'Withdrawal failed:' , error . message )
}
}
Common Errors
Error Solution no balanceDeposit funds first Need at least 1 unspent UTXOWait for pending deposits to confirm withdraw amount too lowIncrease withdrawal amount to cover fees