Solana DEX Trading Bot Development (Jupiter, Raydium)
Solana theoretically handles 65,000 TPS at 400ms confirmation time. Practically for trading bot this means: competitive advantage exists, but only if you properly handle priority fees and understand Solana transaction scheduling mechanics. Most bots lose not because of bad algorithm but because transactions land at end of queue due to incorrectly calculated priority fee.
Solana transaction model: differs from EVM
In Ethereum gas price determines priority. In Solana—combination of compute units and priority fee:
- Compute units (CU)—like gas, limit on computational resources for transaction. Maximum 1.4M CU per transaction.
- Priority fee—additional payment in lamports per compute unit beyond base fee.
Essential instructions for competitive bot:
import { ComputeBudgetProgram } from "@solana/web3.js";
// Set CU limit (important: no more than needed)
const setComputeLimit = ComputeBudgetProgram.setComputeUnitLimit({
units: 200_000, // usually 100k-200k enough for swap
});
// Set priority fee
const setPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 100_000, // 0.1 lamport per CU = 0.02 SOL on 200k CU
});
transaction.add(setComputeLimit, setPriorityFee, ...swapInstructions);
Incorrectly calculated CU limit too low → transaction fails with exceeded compute budget. Too high → overpay and lower priority (validators optimize throughput by fee/CU ratio).
Dynamic priority fee calculation: use getRecentPrioritizationFees RPC method, returns recent fee levels for accounts involved in transaction. Take 75-90 percentile for high priority.
Jupiter: route aggregation
Jupiter—de-facto standard liquidity aggregator on Solana. Aggregates Raydium, Orca, Meteora, Lifinity and 20+ more DEX/AMM. API v6—most current version.
Quote API
const quote = await fetch(`https://quote-api.jup.ag/v6/quote?` + new URLSearchParams({
inputMint: "So11111111111111111111111111111111111111112", // SOL
outputMint: USDC_MINT,
amount: "1000000000", // 1 SOL in lamports
slippageBps: "50", // 0.5%
onlyDirectRoutes: "false",
maxAccounts: "64", // limit accounts in transaction
}));
const quoteData = await quote.json();
// quoteData.outAmount — expected USDC output
// quoteData.priceImpactPct — price impact percent
maxAccounts: 64—critical parameter. Solana transaction limited to 64 unique accounts. Complex route through 4-5 protocols can exceed limit → TooManyAccounts error. Limiting reduces route optimality but guarantees executability.
Swap execution
After getting quote—execute via Swap API:
const swapResponse = await fetch("https://quote-api.jup.ag/v6/swap", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
quoteResponse: quoteData,
userPublicKey: wallet.publicKey.toString(),
wrapAndUnwrapSol: true,
dynamicComputeUnitLimit: true, // automatic CU calculation
prioritizationFeeLamports: "auto", // automatic priority fee
}),
});
const { swapTransaction } = await swapResponse.json();
// Deserialize, sign, send
const tx = VersionedTransaction.deserialize(Buffer.from(swapTransaction, "base64"));
tx.sign([wallet]);
const txid = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: false,
maxRetries: 3,
});
dynamicComputeUnitLimit: true—Jupiter automatically simulates transaction and sets correct CU limit. Recommended for production.
Raydium: direct integration
Jupiter operates on top of Raydium, but for latency-sensitive operations direct Raydium CLMM (Concentrated Liquidity Market Maker) integration is faster.
Raydium SDK v2:
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import { PublicKey } from "@solana/web3.js";
const raydium = await Raydium.load({
owner: wallet,
connection,
disableFeatureCheck: true,
});
// Get pool info
const poolInfo = await raydium.clmm.getPoolInfoFromRpc(POOL_ID);
// Build swap transaction
const { transaction } = await raydium.clmm.swap({
poolInfo,
ownerInfo: { useSOLBalance: true },
inputMint: new PublicKey(INPUT_MINT),
amountIn: new BN(amount),
amountOutMin: new BN(minAmountOut),
observationId: poolInfo.observationId,
txVersion: TxVersion.V0, // Versioned transactions
});
Versioned Transactions (V0) with Address Lookup Tables—mandatory for complex multi-hop swaps. Allow including more accounts via ALT compression.
Latency optimization
For competitive bot latency measured not in seconds—in milliseconds:
Jito bundling. Jito—MEV infrastructure on Solana. Bundle of multiple transactions sent directly to Jito block engine, bypassing standard gossip. Advantage: atomic execution of multiple transactions in one block, first slot in block.
import { searcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
const client = searcherClient(JITO_BLOCK_ENGINE_URL, keypair);
const bundle = new Bundle([tx1, tx2], 5); // max 5 transactions
await client.sendBundle(bundle);
Jito charges tip—minimum 1000 lamports per bundle, realistically 10,000-100,000+ lamports under competitive conditions.
Geyser plugin / Yellowstone. For real-time on-chain data monitoring (pool changes, new transactions)—Solana Geyser plugin via Yellowstone gRPC. Latency 5-20ms vs 200-500ms via standard RPC polling.
Geographic location. Solana cluster nodes concentrated in specific datacenters. Placing bot close to validator nodes—Amsterdam, Frankfurt for Europe, Ashburn for US—reduces network latency 10-50ms.
Monitoring and risk management
Transaction confirmation tracking: confirmTransaction via WebSocket subscription much faster than polling. Statuses: processed → confirmed → finalized.
Failed transaction handling: On BlockhashNotFound error (stale blockhash)—automatically fetch new blockhash and retry. On SlippageToleranceExceeded—recalculate quote and retry with current data.
Capital management: Separate keypair for each trading strategy. Never hold entire balance in hot wallet. Hardware wallet or KMS for long-term storage.
Comparison: Jupiter API vs direct integration
| Parameter | Jupiter API | Direct integration (Raydium SDK) |
|---|---|---|
| Route optimality | High (20+ DEX) | Lower (one protocol) |
| Quote latency | ~100-200ms | ~20-50ms (on-chain) |
| Maintenance | Minimal | SDK updates |
| Complexity | Low | High |
| Customization | Limited | Full |
For most bots Jupiter API—right choice: best prices, less code. Direct Raydium integration—only if need sub-50ms latency or specific pool interaction (LP management, concentrated liquidity range orders).
Development process
Analytics (2-3 days). Determine strategy, choose Jupiter API vs direct integration, define latency and volume requirements.
Core bot development (1-2 weeks). WebSocket monitoring, quote engine, execution with priority fees, Jito bundling (if needed).
Risk management and monitoring (3-5 days). Slippage protection, failed tx handling, Telegram alerts, metrics.
Optimization (3-5 days). Profile latency, optimize CU usage, tune priority fees under real traffic.
Timeline estimates
Basic bot with Jupiter API and automatic priority fee—1 week. Competitive bot with Jito bundling, Geyser monitoring and custom strategies—2-3 weeks. Cost calculated individually.







