# Build Transaction Build an unsigned swap transaction ready for signing and submission. The API returns a base64-encoded transaction containing all necessary instructions—token transfers, DEX calls, and fee handling—in a single atomic transaction. ## How It Works The build transaction endpoint: 1. **Finds the best route** across supported DEXs 2. **Calculates fee** from swap amount (not separate wallet balance) 3. **Creates all required accounts** (ATAs if needed) 4. **Builds DEX swap instructions** for the optimal pool 5. **Adds fee transfer instruction** (direct token transfer, no temp accounts) 6. **Returns a base64 transaction** ready for signing You receive a complete, optimized transaction that just needs a signature. **Note:** Your application signs the transaction with the user's wallet and submits it to the Solana network. **Vybe never touches private keys**—we only build the transaction; signing and submission happen entirely on your side *** ### Smart Fee Handling When you set a `fee`, Vybe deducts it **from the swap tokens directly**—not from a separate wallet balance. This means: * No "Insufficient SOL for fee" errors when swapping SOL * No temporary fee accounts (saves at least ~0.004 SOL in rent) * Fee is extracted atomically within the same transaction See [Slippage & Fees](./slippage-fees.md) for full details on fee priority logic. *** ## Endpoint ``` POST /trading/swap ``` *** ## Request Parameters | Parameter | Type | Required | Default | Description | | ------------ | ------- | -------- | -------- | ----------------------------------------------- | | `wallet` | string | ✅ | | User's wallet public key | | `amount` | number | ✅ | | Amount to swap (UI units, e.g., 0.1 SOL) | | `inputMint` | string | ✅ | | Input token mint address | | `outputMint` | string | ✅ | | Output token mint address | | `router` | string | ❌ | `"vybe"` | Router mode: `"vybe"`, `"titan"`, `"jupiter"` | | `slippage` | number | ❌ | 5 | Slippage tolerance (percentage, e.g., 2 for 2%) | | `fee` | number | ❌ | 0 | Service fee percentage (e.g., 1 for 1%) | | `simulate` | boolean | ❌ | false | Simulate only, don't return transaction | | `gasless` | boolean | ❌ | false | Use Vybe as fee payer | | `protocol` | string | ❌ | | Force specific protocol (e.g., `"PUMPFUN"`) | | `pool` | string | ❌ | | Force specific pool address | | `vybeOnly` | boolean | ❌ | false | No fallback to aggregators | *** ## Example Request ```bash curl -X POST "https://api.vybenetwork.com/trading/swap" \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "wallet": "7Tar8QZTrRPwoGY5Ke9Vfwf6CmpBfekrNofERxgReza", "amount": 0.1, "inputMint": "So11111111111111111111111111111111111111112", "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "router": "vybe", "slippage": 1, "fee": 0.5 }' ``` *** ## Example Response ```json { "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHDg...", "inputAmount": 100000000, "inputToken": "So11111111111111111111111111111111111111112", "outputToken": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "inputDecimals": 9, "outputDecimals": 6, "swapFee": 500000, "protocol": "RAYDIUM_CLMM", "poolAddress": "8sLbNZoA1cfnvMJLPfp98ZLAnFSYCFApfJKMbiXNLwxj", "quote": { "inAmount": "100000000", "outAmount": "18523456", "priceImpactPct": 0.01 } } ``` *** ## Response Fields | Field | Description | | ---------------- | ------------------------------------- | | `transaction` | Base64-encoded unsigned transaction | | `inputAmount` | Input amount in base units | | `inputToken` | Input token mint address | | `outputToken` | Output token mint address | | `inputDecimals` | Input token decimal places | | `outputDecimals` | Output token decimal places | | `swapFee` | Fee amount in base units | | `protocol` | DEX protocol used | | `poolAddress` | Pool address used for swap | | `quote` | Quote details (amounts, price impact) | *** ## Complete Flow: Sign and Submit Here's how to execute a swap end-to-end: ### Using @solana/web3.js ```javascript import { Connection, Transaction, Keypair } from '@solana/web3.js'; async function executeSwap(wallet, inputMint, outputMint, amount) { // Step 1: Build the transaction const response = await fetch('https://api.vybenetwork.com/trading/swap', { method: 'POST', headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ wallet: wallet.publicKey.toString(), amount, inputMint, outputMint, router: 'vybe', slippage: 2 }) }); const { transaction: txBase64, quote } = await response.json(); // Step 2: Deserialize the transaction const transaction = Transaction.from(Buffer.from(txBase64, 'base64')); // Step 3: Sign with wallet transaction.sign(wallet); // Step 4: Submit to network const connection = new Connection('https://api.mainnet-beta.solana.com'); const signature = await connection.sendRawTransaction( transaction.serialize(), { skipPreflight: false } ); // Step 5: Confirm await connection.confirmTransaction(signature, 'confirmed'); return { signature, expectedOutput: quote.outAmount }; } ``` ### Using Wallet Adapter (React) ```javascript import { useWallet, useConnection } from '@solana/wallet-adapter-react'; import { Transaction } from '@solana/web3.js'; function SwapButton({ inputMint, outputMint, amount }) { const { publicKey, signTransaction } = useWallet(); const { connection } = useConnection(); const [loading, setLoading] = useState(false); async function handleSwap() { if (!publicKey || !signTransaction) return; setLoading(true); try { // Build transaction const response = await fetch('https://api.vybenetwork.com/trading/swap', { method: 'POST', headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ wallet: publicKey.toString(), amount, inputMint, outputMint, router: 'vybe', slippage: 2 }) }); const { transaction: txBase64 } = await response.json(); // Deserialize const transaction = Transaction.from(Buffer.from(txBase64, 'base64')); // Sign with wallet adapter const signed = await signTransaction(transaction); // Submit const signature = await connection.sendRawTransaction(signed.serialize()); await connection.confirmTransaction(signature, 'confirmed'); console.log('Swap successful:', signature); } catch (error) { console.error('Swap failed:', error); } setLoading(false); } return ( ); } ``` *** ## Router Options | Router | Behavior | Use When | | ----------- | ------------------------------------------------------- | ------------------- | | `"vybe"` | Direct DEX calls, falls back to Titan/Jupiter if needed | Maximum efficiency | | `"titan"` | Uses Titan aggregator | Wide token coverage | | `"jupiter"` | Uses Jupiter aggregator | Maximum coverage | ### Force Direct Integration Only ```json { "router": "vybe", "vybeOnly": true } ``` This prevents any fallback to aggregators. If Vybe Router doesn't support the token pair, the request will fail. *** ## Simulation Mode Test swaps without creating a transaction: ```json { "wallet": "...", "amount": 1, "inputMint": "...", "outputMint": "...", "simulate": true } ``` Returns quote and route information without the transaction, useful for testing and price discovery. *** ## Error Handling | Status | Error | Cause | Solution | | ------ | --------------------------- | --------------------------------- | ----------------------------------- | | 400 | Missing required parameters | Required fields not provided | Include all required fields | | 400 | Invalid wallet format | Wallet is not a valid public key | Check wallet address | | 422 | No swap route found | No liquidity for this pair | Try different router or check token | | 422 | Insufficient balance | Wallet doesn't have enough tokens | Check balance before swap | | 422 | Slippage exceeded | Price moved too much | Increase slippage tolerance | ### Robust Error Handling ```javascript async function buildSwapWithRetry(params, maxRetries = 2) { for (let i = 0; i <= maxRetries; i++) { try { const response = await fetch('https://api.vybenetwork.com/trading/swap', { method: 'POST', headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); if (!response.ok) { const error = await response.json(); // Retry with higher slippage on slippage errors if (error.message?.includes('Slippage') && i < maxRetries) { params.slippage = (params.slippage || 2) * 2; continue; } throw new Error(error.message || 'Swap failed'); } return response.json(); } catch (error) { if (i === maxRetries) throw error; } } } ``` *** ## Next Steps * [Supported Protocols](./supported-protocols.md) - Force specific DEXs * [Slippage & Fees](./slippage-fees.md) - Configure slippage and understand fees * [Gasless Mode](./gasless-mode.md) - Execute without SOL for gas