Claude-skill-registry jito-bundles-and-priority-fees
Master Jito bundles for MEV protection and priority fee optimization on Solana - bundle submission, tip strategies, and transaction landing. Use for trading bots, high-priority transactions, and MEV-aware applications.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/jito-bundles-and-priority-fees" ~/.claude/skills/majiayu000-claude-skill-registry-jito-bundles-and-priority-fees && rm -rf "$T"
manifest:
skills/data/jito-bundles-and-priority-fees/SKILL.mdsource content
Jito Bundles and Priority Fees
Role framing: You are a Solana transaction optimization specialist who understands Jito infrastructure, priority fees, and MEV dynamics. Your goal is to help applications land transactions reliably and protect against MEV extraction.
Initial Assessment
- What are you building: trading bot, DEX, NFT mint, or general dApp?
- Transaction volume: occasional high-priority or sustained high-frequency?
- MEV exposure: are your transactions susceptible to front-running or sandwich attacks?
- Budget: what's acceptable tip/fee spend per transaction?
- Latency requirements: how critical is landing in the next slot?
- Do you need atomic bundles (multiple txs that must execute together)?
Core Principles
- Priority fees are for congestion, tips are for Jito: Different mechanisms, different purposes.
- Bundles are atomic: All transactions in a bundle execute or none do.
- Tips go to validators, not network: Jito tips incentivize block builders to include your bundle.
- Higher tip ≠ guaranteed inclusion: You're competing with other bundles; tip relative to value.
- Bundles can fail silently: No on-chain record if bundle wasn't included.
- Not all validators run Jito: ~80-90% of stake, but some slots won't process bundles.
Workflow
1. Understanding the Landscape
Standard Solana Transaction Path: User → RPC → Leader Validator → Block Jito Bundle Path: User → Jito Block Engine → Jito Relayer → Leader Validator → Block Key difference: Bundles go through Jito's infrastructure, which sequences them to prevent MEV extraction.
Priority fees vs Jito tips:
| Aspect | Priority Fee | Jito Tip |
|---|---|---|
| Where paid | On-chain, part of tx | Separate tip tx in bundle |
| Who receives | Validator + burn | Validator (via Jito) |
| Guarantees | Higher priority in mempool | Bundle inclusion if won auction |
| Use case | General tx prioritization | MEV protection, atomic execution |
2. When to Use What
| Scenario | Recommendation |
|---|---|
| Normal dApp transaction | Priority fee only |
| Trading bot (no MEV concern) | Priority fee, maybe Jito for speed |
| DEX swap (MEV risk) | Jito bundle for protection |
| Arbitrage | Jito bundle (atomic multi-tx) |
| NFT mint (competitive) | Priority fee + Jito bundle |
| Liquidation | Jito bundle (need speed + protection) |
3. Priority Fee Implementation
import { ComputeBudgetProgram, Transaction, TransactionInstruction, } from '@solana/web3.js'; // Add priority fee to transaction function addPriorityFee( transaction: Transaction, microLamports: number, computeUnits: number = 200_000 ): Transaction { // Set compute unit limit const computeLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits, }); // Set compute unit price (priority fee) const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({ microLamports: microLamports, // micro-lamports per compute unit }); // Prepend to transaction transaction.instructions = [ computeLimitIx, computePriceIx, ...transaction.instructions, ]; return transaction; } // Calculate fee in SOL: // fee = (microLamports * computeUnits) / 1_000_000_000_000 // Example: 10,000 microLamports * 200,000 CU = 0.000002 SOL
Priority fee tiers (as of late 2024):
| Network State | Micro-lamports | ~Cost per 200k CU |
|---|---|---|
| Quiet | 1,000 | 0.0000002 SOL |
| Normal | 10,000 | 0.000002 SOL |
| Busy | 50,000 | 0.00001 SOL |
| Congested | 200,000 | 0.00004 SOL |
| Extreme | 1,000,000+ | 0.0002+ SOL |
4. Jito Bundle Implementation
import { Connection, Keypair, Transaction, VersionedTransaction } from '@solana/web3.js'; // Jito endpoints const JITO_ENDPOINTS = { mainnet: { blockEngine: 'https://mainnet.block-engine.jito.wtf', // Regional endpoints for lower latency: // ny: 'https://ny.mainnet.block-engine.jito.wtf' // amsterdam: 'https://amsterdam.mainnet.block-engine.jito.wtf' // frankfurt: 'https://frankfurt.mainnet.block-engine.jito.wtf' // tokyo: 'https://tokyo.mainnet.block-engine.jito.wtf' }, }; // Jito tip accounts (rotate through these) const JITO_TIP_ACCOUNTS = [ '96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5', 'HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe', 'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY', 'ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49', 'DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh', 'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt', 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', '3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT', ]; // Create tip instruction function createTipInstruction( fromPubkey: PublicKey, tipLamports: number ): TransactionInstruction { const tipAccount = new PublicKey( JITO_TIP_ACCOUNTS[Math.floor(Math.random() * JITO_TIP_ACCOUNTS.length)] ); return SystemProgram.transfer({ fromPubkey, toPubkey: tipAccount, lamports: tipLamports, }); } // Send bundle to Jito async function sendJitoBundle( transactions: (Transaction | VersionedTransaction)[], tipLamports: number, payer: Keypair ): Promise<string> { // Serialize transactions const serializedTxs = transactions.map((tx) => { if (tx instanceof VersionedTransaction) { return Buffer.from(tx.serialize()).toString('base64'); } return Buffer.from(tx.serialize()).toString('base64'); }); // Send to Jito block engine const response = await fetch( `${JITO_ENDPOINTS.mainnet.blockEngine}/api/v1/bundles`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'sendBundle', params: [serializedTxs], }), } ); const result = await response.json(); if (result.error) { throw new Error(`Bundle failed: ${result.error.message}`); } return result.result; // Bundle ID } // Check bundle status async function checkBundleStatus(bundleId: string): Promise<any> { const response = await fetch( `${JITO_ENDPOINTS.mainnet.blockEngine}/api/v1/bundles`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getBundleStatuses', params: [[bundleId]], }), } ); return response.json(); }
5. Tip Strategy
// Dynamic tip calculation based on expected value function calculateTip( expectedProfitLamports: number, urgency: 'low' | 'medium' | 'high' | 'critical' ): number { const urgencyMultiplier = { low: 0.01, // 1% of profit medium: 0.05, // 5% of profit high: 0.10, // 10% of profit critical: 0.20, // 20% of profit }; const baseTip = expectedProfitLamports * urgencyMultiplier[urgency]; // Floor and ceiling const minTip = 10_000; // 0.00001 SOL const maxTip = 100_000_000; // 0.1 SOL return Math.max(minTip, Math.min(maxTip, Math.floor(baseTip))); } // Typical tip ranges: // - Simple swap protection: 0.0001 - 0.001 SOL // - Competitive arb: 0.001 - 0.01 SOL // - High-value arb: 1-10% of profit // - NFT mint: 0.01 - 0.1 SOL during peak
6. Bundle Construction Best Practices
// Example: Protected swap with tip async function protectedSwap( swapIx: TransactionInstruction[], payer: Keypair, connection: Connection ): Promise<string> { // Create swap transaction const swapTx = new Transaction(); swapTx.add(...swapIx); // Add priority fee for backup addPriorityFee(swapTx, 10_000, 400_000); // Create tip transaction (separate tx in bundle) const tipTx = new Transaction(); tipTx.add(createTipInstruction(payer.publicKey, 100_000)); // 0.0001 SOL // Get recent blockhash const { blockhash } = await connection.getLatestBlockhash(); swapTx.recentBlockhash = blockhash; tipTx.recentBlockhash = blockhash; swapTx.feePayer = payer.publicKey; tipTx.feePayer = payer.publicKey; // Sign both swapTx.sign(payer); tipTx.sign(payer); // Bundle: [swap, tip] - tip goes last // If swap fails, tip doesn't execute (atomic) const bundleId = await sendJitoBundle([swapTx, tipTx], 100_000, payer); return bundleId; }
7. Monitoring and Retry Logic
async function sendBundleWithRetry( transactions: Transaction[], tipLamports: number, payer: Keypair, maxRetries: number = 5 ): Promise<{ success: boolean; signature?: string; attempts: number }> { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const bundleId = await sendJitoBundle(transactions, tipLamports, payer); // Wait for result await new Promise((r) => setTimeout(r, 500)); const status = await checkBundleStatus(bundleId); if (status.result?.value?.[0]?.confirmation_status === 'confirmed') { return { success: true, signature: status.result.value[0].transactions[0], attempts: attempt, }; } // Bundle not landed, retry with higher tip tipLamports = Math.floor(tipLamports * 1.5); } catch (error) { console.error(`Attempt ${attempt} failed:`, error); } // Small delay between retries await new Promise((r) => setTimeout(r, 100)); } return { success: false, attempts: maxRetries }; }
Templates / Playbooks
Fee/Tip Decision Matrix
interface FeeStrategy { scenario: string; priorityFee: number; // micro-lamports jitoTip: number; // lamports useBundle: boolean; } const strategies: FeeStrategy[] = [ { scenario: 'Normal dApp interaction', priorityFee: 10_000, jitoTip: 0, useBundle: false, }, { scenario: 'Time-sensitive but not MEV-exposed', priorityFee: 50_000, jitoTip: 0, useBundle: false, }, { scenario: 'DEX swap (MEV protection)', priorityFee: 10_000, jitoTip: 50_000, useBundle: true, }, { scenario: 'Arbitrage', priorityFee: 10_000, jitoTip: 'dynamic (% of profit)', useBundle: true, }, { scenario: 'NFT mint (competitive)', priorityFee: 200_000, jitoTip: 1_000_000, useBundle: true, }, { scenario: 'Liquidation', priorityFee: 100_000, jitoTip: 500_000, useBundle: true, }, ];
RPC + Jito Configuration
const infraConfig = { // Primary RPC for reads rpcRead: process.env.RPC_READ_URL, // RPC for non-Jito sends rpcSend: process.env.RPC_SEND_URL, // Jito for bundles jito: { blockEngine: process.env.JITO_BLOCK_ENGINE, // Use regional endpoint closest to you region: 'ny', // ny, amsterdam, frankfurt, tokyo }, // Fallback strategy fallback: { onJitoFailure: 'retry_with_rpc', // or 'fail_fast' maxJitoRetries: 3, priorityFeeEscalation: 1.5, // multiply on each retry }, };
Common Failure Modes + Debugging
"Bundle not landing"
- Cause: Tip too low, competing with higher-tipping bundles
- Detection: Bundle status shows "dropped" or no confirmation
- Fix: Increase tip; check current tip floor via Jito API; retry with escalating tips
"Bundle landed but transaction failed"
- Cause: On-chain error (slippage, insufficient funds, etc.)
- Detection: Transaction shows error on explorer
- Fix: This is application logic issue, not Jito issue. Debug the transaction itself
"Transaction landed via RPC before bundle"
- Cause: RPC was faster than Jito path
- Detection: Signature exists but bundle shows "dropped"
- Fix: Not a problem if tx succeeded. For MEV protection, ensure you're not sending to RPC simultaneously
"Bundle simulation failed"
- Cause: Transaction would fail on-chain
- Detection: Jito returns simulation error
- Fix: Fix the transaction before sending; ensure fresh blockhash
"Rate limited by Jito"
- Cause: Exceeded request limits
- Detection: 429 response
- Fix: Implement backoff; contact Jito for higher limits if needed
Quality Bar / Validation
Implementation is complete when:
- Priority fee calculation adapts to network conditions
- Jito bundles submit successfully with appropriate tips
- Retry logic handles failures gracefully
- Tip amounts scale with transaction value/urgency
- Monitoring tracks bundle landing rates
- Fallback to RPC exists for non-Jito validators
Output Format
Provide:
- Strategy recommendation: Use Jito bundles, priority fees, or both
- Configuration code: Priority fee and tip calculations
- Bundle construction: How to structure transactions
- Monitoring setup: How to track success rates
- Cost estimate: Expected fees per transaction type
Examples
Simple Example: Add Priority Fee to Swap
Input: "Make this swap transaction land faster"
Output:
import { ComputeBudgetProgram, Transaction } from '@solana/web3.js'; // Your existing swap instruction const swapIx = /* ... your Jupiter/Raydium swap instruction ... */; // Create transaction with priority fee const tx = new Transaction(); // 1. Set compute limit (estimate or use 400k for swaps) tx.add( ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) ); // 2. Set priority fee (50k microlamports = ~0.00002 SOL for 400k CU) tx.add( ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50_000 }) ); // 3. Add your swap instruction tx.add(swapIx); // Total additional cost: ~0.00002 SOL // Should land within 1-2 slots during normal conditions
Complex Example: MEV-Protected Arbitrage Bundle
Input: "Build an arb bot that uses Jito for MEV protection"
Output:
// See examples/jito-arb-bot.ts for full implementation // Key components: class JitoArbBot { private jitoClient: JitoClient; private minProfitLamports = 50_000; // 0.00005 SOL minimum async executeArb( buyIx: TransactionInstruction[], sellIx: TransactionInstruction[], expectedProfitLamports: number ): Promise<ArbResult> { // Don't execute if profit too low after tip const tip = this.calculateTip(expectedProfitLamports); const netProfit = expectedProfitLamports - tip; if (netProfit < this.minProfitLamports) { return { executed: false, reason: 'Profit below threshold after tip' }; } // Build atomic bundle: // Tx 1: Buy // Tx 2: Sell // Tx 3: Tip (only executes if buy+sell succeed) const bundle = await this.buildArbBundle(buyIx, sellIx, tip); // Send with retry const result = await this.sendBundleWithRetry(bundle, tip); return { executed: result.success, signature: result.signature, expectedProfit: expectedProfitLamports, actualTip: tip, netProfit: netProfit, }; } private calculateTip(profitLamports: number): number { // Dynamic tip: 5% of profit, min 10k lamports, max 1 SOL return Math.max( 10_000, Math.min(1_000_000_000, Math.floor(profitLamports * 0.05)) ); } }