/** * Jito MEV Protection and Tip Utilities */ import { Connection, PublicKey, SystemProgram, TransactionInstruction } from "@solana/web3.js"; import { PriorityLevel } from "./index.js"; /** * Minimum Jito tip: 1,000 lamports (0.000001 SOL) */ export const MIN_JITO_TIP_LAMPORTS = 1_000; /** * Jito tip tiers based on priority level (minimal fallback values) */ export const JITO_TIP_LAMPORTS = { fast: 1_000, // 0.000001 SOL (minimal) turbo: 5_000, // 0.000005 SOL (minimal) ultra: 10_000, // 0.00001 SOL (minimal) } as const; /** * Calculate percentile from sorted array */ function getPercentile(sortedArray: number[], percentile: number): number { if (sortedArray.length === 0) return 0; const index = Math.ceil((percentile / 100) * sortedArray.length) - 1; return sortedArray[Math.max(0, index)]; } /** * Calculate Jito tip based on priority level and recent prioritization fees * Falls back to static tiers if RPC call fails * * Uses getRecentPrioritizationFees to estimate appropriate tip amounts based on * current network conditions. * * @param connection - Solana RPC connection * @param level - Priority level (Fast/Turbo/Ultra) * @returns Tip amount in lamports */ export async function calculateJitoTip( connection: Connection, level: PriorityLevel ): Promise { try { // Fetch recent prioritization fees from last 150 slots (~1 minute) const recentFees = await connection.getRecentPrioritizationFees(); if (recentFees && recentFees.length > 0) { // Extract fee values and sort const fees = recentFees .map((fee) => fee.prioritizationFee) .filter((fee) => fee > 0) .sort((a, b) => a - b); if (fees.length > 0) { // Select percentile based on priority level let percentile: number; switch (level) { case PriorityLevel.Fast: percentile = 50; // Median break; case PriorityLevel.Turbo: percentile = 75; // 75th percentile break; case PriorityLevel.Ultra: percentile = 95; // 95th percentile break; } const calculatedTip = getPercentile(fees, percentile); return Math.max(calculatedTip, MIN_JITO_TIP_LAMPORTS); } } } catch { // RPC might not support this method } // Fallback to static tiers return JITO_TIP_LAMPORTS[level]; } /** * Jito tip accounts (randomly selected for load balancing) * These are Jito-operated tip collection accounts */ const JITO_TIP_ACCOUNTS = [ "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5", "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe", "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY", "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49", "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh", "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt", "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL", ]; /** * Create Jito tip transfer instruction * Tips are sent to a randomly selected Jito-operated tip account * * @param payer - Transaction fee payer (tip sender) * @param tipLamports - Tip amount in lamports * @returns SystemProgram transfer instruction */ export function createJitoTipInstruction( payer: PublicKey, tipLamports: number ): TransactionInstruction { // Randomly select a tip account for load balancing const randomTipAccount = JITO_TIP_ACCOUNTS[Math.floor(Math.random() * JITO_TIP_ACCOUNTS.length)]; return SystemProgram.transfer({ fromPubkey: payer, toPubkey: new PublicKey(randomTipAccount), lamports: tipLamports, }); }