Withdraw with Swap
Withdraw tokens and swap them via Jupiter in one transaction
Withdraw with Swap
Section titled “Withdraw with Swap”You can withdraw tokens and swap them to a different token in a single transaction using Jupiter integration. This adds another layer of privacy since the output token differs from the input.
Overview
Section titled “Overview”Step-by-Step Guide
Section titled “Step-by-Step Guide”-
Get Jupiter Quote
First, get a swap quote from Jupiter:
interface JupiterQuote {inputMint: string;outputMint: string;inAmount: string;outAmount: string;priceImpactPct: number;slippageBps: number;}async function getJupiterQuote(inputMint: string,outputMint: string,amount: number,slippageBps: number = 50 // 0.5%): Promise<JupiterQuote> {// Option 1: Use Turbine.cash Jupiter proxyconst response = await fetch(`${API_BASE}/jupiter`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({inputMint,outputMint,amount,slippageBps,dexes: [], // Empty for all DEXes}),});return response.json();}Or use Jupiter API directly:
async function getJupiterQuoteDirect(inputMint: string,outputMint: string,amount: number,slippageBps: number = 50): Promise<any> {const params = new URLSearchParams({inputMint,outputMint,amount: amount.toString(),slippageBps: slippageBps.toString(),});const response = await fetch(`https://quote-api.jup.ag/v6/quote?${params}`);return response.json();} -
Generate Withdrawal Proof
Same as regular withdrawal:
const { proof, nullifierHash } = await generateWithdrawProof(deposit.nullifier,deposit.secret,merkleProof,merkleRoot,recipient,deposit.leafIndex); -
Call relay_swap Endpoint
interface RelaySwapParams {nullifierHash: number[];proof: number[];root: number[];recipient: string;depth: number;depositSize: number;input_mint: string; // Token being withdrawnoutput_mint: string; // Token to receive after swapnetwork?: string;}async function relaySwap(params: RelaySwapParams): Promise<RelayResponse> {const response = await fetch(`${API_BASE}/relay_swap`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(params),});return response.json();}
Complete Example
Section titled “Complete Example”async function withdrawWithSwap( deposit: DepositSecrets, recipient: string, outputMint: string, authToken: string, slippageBps: number = 50, network: string = 'mainnet'): Promise<WithdrawSwapResult> { // 1. Get quote to preview swap const quote = await getJupiterQuote( deposit.mint, outputMint, deposit.amount, slippageBps );
console.log(`Swapping ${deposit.amount} for ~${quote.outAmount}`); console.log(`Price impact: ${quote.priceImpactPct}%`);
// 2. Get Merkle proof const proofPath = await getMerkleProof(deposit.mint, deposit.leafIndex, network); const merkleRoot = await getMerkleRoot(deposit.mint, network);
// 3. Generate ZK proof const { proof, nullifierHash } = await generateWithdrawProof( deposit.nullifier, deposit.secret, proofPath, merkleRoot, recipient, deposit.leafIndex );
// 4. Check OFAC await getOfacSignature(authToken, network);
// 5. Call relay_swap const result = await relaySwap({ nullifierHash: Array.from(nullifierHash), proof: Array.from(proof), root: Array.from(merkleRoot), recipient, depth: 32, depositSize: deposit.amount, input_mint: deposit.mint, output_mint: outputMint, network, });
if (result.status !== 200 || !result.signature) { throw new Error(result.error || 'Swap withdrawal failed'); }
markDepositSpent(deposit);
return { signature: result.signature, inputAmount: deposit.amount, expectedOutput: quote.outAmount, };}Supported Tokens
Section titled “Supported Tokens”Jupiter supports thousands of tokens. Common pairs:
| Input | Output | Use Case |
|---|---|---|
| USDC | SOL | Convert stables to native |
| USDC | USDT | Stablecoin swap |
| SOL | USDC | De-risk to stables |
Slippage Configuration
Section titled “Slippage Configuration”Slippage tolerance is set in basis points (bps):
| Slippage | bps | Risk |
|---|---|---|
| 0.1% | 10 | May fail on volatile pairs |
| 0.5% | 50 | Balanced (recommended) |
| 1.0% | 100 | Higher tolerance for volatile tokens |
| 3.0% | 300 | Use for very illiquid tokens |
// Conservative slippage for stablecoinsconst stablecoinSlippage = 10; // 0.1%
// Standard slippageconst defaultSlippage = 50; // 0.5%
// High slippage for volatile pairsconst volatileSlippage = 100; // 1%Withdraw+swap includes multiple fees:
| Fee Type | Description |
|---|---|
| Relay Fee | [PLACEHOLDER: FEE_WITHDRAW_PCT] |
| Swap Fee | [PLACEHOLDER: FEE_SWAP_PCT] |
| Jupiter Fees | DEX fees (varies by route) |
Total cost = Relay Fee + Swap Fee + Jupiter routing fees
Error Handling
Section titled “Error Handling”| Error | Cause | Solution |
|---|---|---|
SlippageExceeded | Price moved too much | Increase slippage or retry |
InsufficientLiquidity | Not enough liquidity | Try different route/DEX |
InvalidOutputMint | Token not supported | Check token address |
SwapFailed | Jupiter swap failed | Check Jupiter status |
Pre-flight Checks
Section titled “Pre-flight Checks”Before submitting, validate the swap:
async function validateSwap( inputMint: string, outputMint: string, amount: number): Promise<{ valid: boolean; reason?: string }> { try { const quote = await getJupiterQuote(inputMint, outputMint, amount);
// Check price impact if (quote.priceImpactPct > 3) { return { valid: false, reason: `High price impact: ${quote.priceImpactPct}%`, }; }
// Check minimum output const minOutput = amount * 0.95; // Example: expect at least 95% if (parseInt(quote.outAmount) < minOutput) { return { valid: false, reason: 'Output amount too low', }; }
return { valid: true }; } catch (e) { return { valid: false, reason: 'Quote failed' }; }}Next Steps
Section titled “Next Steps”- Revert - Handle failed deposits
- API Reference - Full relay_swap documentation