SolanaMevBot
  • 🤖SolanaMevBot - Solana Arbitrage Bot
    • 📘Blog
      • The Current Landscape of MEV on Solana
      • How to Frontrun on Solana: A Step-by-Step Guide
    • đŸ’ĩFees
  • Onchain Bot
    • â„šī¸How this bot work
    • đŸ› ī¸Getting started
    • â›“ī¸Onchain program
    • 🔍Address lookup table
    • 📝Bot configurations
  • 🚀Releases
  • â€ŧī¸Trouble shooting
  • 💹Performance tuning
  • Onchain Bot Dashboard
  • Jupiter Bot
    • â„šī¸How this bot work
    • đŸ› ī¸Getting started
    • 📝Bot configurations
      • 📚Config examples
    • 🔡Common workflow
    • 💹Performance tuning
    • 🧩Bot Strategy
    • â€ŧī¸Trouble shooting
    • 🚀Releases
    • ❓FAQ
    • Dashboard
  • Community
    • Discord
Powered by GitBook
On this page
  1. Onchain Bot

Onchain program

PreviousGetting startedNextAddress lookup table

Last updated 17 hours ago

The onchain program takes in mints and pools, and will calculate the most optimal arbitrage trade between them and execute the trades.

If you want maximum flexibility, you can write your own bot and just call the onchain program from there. This allow you to do whatever customization you want but still utilize the onchain program to get maximum arb opportunity.

Demo bot:

Current supported DEX:

  • Raydium

  • Raydium CPMM

  • Raydium CLMM

  • Pumpfun Swap

  • Meteora DLMM

  • Meteora Dynamic Pool

  • Orca whirlpool

NOTE: It's recommended to include this address lookup table as it has all the fixed keys needed: 4sKLJ1Qoudh8PJyqBeuKocYdsZvxTcRShUt9aKqwhgvC

NOTE: max_bin_to_process is how many bins the program can look through when calculating optimal trade size. Each bin lookup will take ~10k compute units. So you should setup your compute unit limit accordingly. For example, I usually use 650_000 compute unit limit with max_bin_to_process = 30. Bot uses the following code to calculate this number:

let bins = ((config.bot.compute_unit_limit - 300_000 - if enable_kamino { 80_000 } else { 0 })
        / 10_000) as u64;

Here's a simplified Rust example for how to interact with the program. You can include multiple X mints with their according pools here.

You can also check to see how to extract all the keys for each pool type.

pub fn generate_onchain_swap_multiple_mints_instruction(
    wallet: &Pubkey,
    base_mint: &Pubkey, // Support SOL/USDC
    wallet_base_account: &Pubkey,
    token_program: &Pubkey,
    system_program: &Pubkey,
    associated_token_program: &Pubkey,
    token_pools: &[(
        Pubkey, // X mint
        Pubkey, // Wallet X account
        Vec<(
            Pubkey, // Raydium program ID
            Pubkey, // Raydium authority
            // Raydium pools
            Pubkey, // Raydium AMM account
            Pubkey, // Raydium token X vault
            Pubkey, // Raydium SOL vault
        )>,
        Vec<(
            Pubkey, // Raydium CP program ID
            Pubkey, // Raydium CP authority
            // Raydium CP pools
            Pubkey, // Raydium CP pool
            Pubkey, // Raydium CP amm config
            Pubkey, // Raydium CP token X vault
            Pubkey, // Raydium CP SOL vault
            Pubkey, // Raydium CP observation
        )>,
        Vec<(
            Pubkey, // Pump program ID
            Pubkey, // Pump global config
            Pubkey, // Pump authority
            Pubkey, // Pump fee wallet
            // Pump pools
            Pubkey, // Pump pool
            Pubkey, // Pump token X account
            Pubkey, // Pump SOL account
            Pubkey, // Pump fee token wallet
        )>,
        Vec<(
            Pubkey, // DLMM program ID
            Pubkey, // DLMM event authority
            // DLMM pairs
            Pubkey,           // DLMM pair account
            Pubkey,           // DLMM token X vault
            Pubkey,           // DLMM SOL vault
            Pubkey,           // DLMM oracle
            Vec<AccountMeta>, // Bin array accounts
        )>,
        Vec<(
            Pubkey, // Whirlpool program ID
            // Whirlpool pools
            Pubkey,           // Whirlpool pool
            Pubkey,           // Whirlpool oracle
            Pubkey,           // Whirlpool token X vault
            Pubkey,           // Whirlpool token SOL vault
            Vec<AccountMeta>, // Whirlpool tick array accounts
        )>,
        Vec<(
            Pubkey, // Raydium CLMM program ID
            // Raydium CLMM pools
            Pubkey,           // Raydium CLMM pool
            Pubkey,           // Raydium CLMM amm config
            Pubkey,           // Raydium CLMM observation state
            Pubkey,           // Raydium CLMM bitmap extension
            Pubkey,           // Raydium CLMM token X vault
            Pubkey,           // Raydium CLMM token SOL vault
            Vec<AccountMeta>, // Raydium CLMM tick array accounts
        )>,
        Vec<(
            Pubkey, // Meteora program ID
            Pubkey, // Meteora vault program ID
            // Meteora pools
            Pubkey, // Meteora pool
            Pubkey, // Meteora pool token X vault
            Pubkey, // Meteora pool token SOL vault
            Pubkey, // Meteora pool token X token vault
            Pubkey, // Meteora pool token SOL token vault
            Pubkey, // Meteora pool token X lp mint
            Pubkey, // Meteora pool token SOL lp mint
            Pubkey, // Meteora pool token X pool lp
            Pubkey, // Meteora pool token SOL pool lp
            Pubkey, // Meteora pool admin token fee X
            Pubkey, // Meteora pool admin token fee SOL
        )>,
    )],
    program_id: &Pubkey,
    minimum_profit: u64,
    max_bin_to_process: u64,
) -> Instruction {
    // Create the list of accounts required for the instruction
    let fee_collector = get_associated_token_address(
        &Pubkey::from_str("FeexKmRMrBUTBvwUxgUtPhkRxfRepcaoefJJiqnPeVno").unwrap(),
        base_mint,
    );
    let mut accounts = vec![
        // Fixed accounts (0-6)
        AccountMeta::new_readonly(*wallet, true), // 0. Wallet (signer)
        AccountMeta::new_readonly(*base_mint, false), // 1. SOL mint
        AccountMeta::new(fee_collector, false),   // 2. Fee collector
        AccountMeta::new(*wallet_base_account, false), // 3. Wallet SOL account
        AccountMeta::new_readonly(*token_program, false), // 4. Token program
        AccountMeta::new_readonly(*system_program, false), // 5. System program
        AccountMeta::new_readonly(*associated_token_program, false), // 6. Associated Token program
    ];

    // Add accounts for each token X and its pools
    for (
        x_mint,
        wallet_x_account,
        raydium_pools,
        raydium_cp_pools,
        pump_pools,
        dlmm_pairs,
        whirlpool_pools,
        raydium_clmm_pools,
        meteora_pools,
    ) in token_pools
    {
        // Add token X mint and wallet account
        accounts.push(AccountMeta::new_readonly(*x_mint, false));
        accounts.push(AccountMeta::new(*wallet_x_account, false));

        // Add Raydium pools
        for (program_id, authority, amm, x_vault, sol_vault) in raydium_pools.iter() {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new_readonly(*authority, false));
            accounts.push(AccountMeta::new(*amm, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*sol_vault, false));
        }

        // Add Raydium CP pools
        for (program_id, authority, pool, amm_config, x_vault, sol_vault, observation) in
            raydium_cp_pools.iter()
        {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new_readonly(*authority, false));
            accounts.push(AccountMeta::new(*pool, false));
            accounts.push(AccountMeta::new_readonly(*amm_config, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*sol_vault, false));
            accounts.push(AccountMeta::new(*observation, false));
        }

        // Add Pump pools
        for (
            program_id,
            global_config,
            authority,
            fee_wallet,
            pool,
            x_account,
            sol_account,
            fee_token_wallet,
        ) in pump_pools.iter()
        {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new_readonly(*global_config, false));
            accounts.push(AccountMeta::new_readonly(*authority, false));
            accounts.push(AccountMeta::new_readonly(*fee_wallet, false));
            accounts.push(AccountMeta::new_readonly(*pool, false));
            accounts.push(AccountMeta::new(*x_account, false));
            accounts.push(AccountMeta::new(*sol_account, false));
            accounts.push(AccountMeta::new(*fee_token_wallet, false));
        }

        // Add DLMM pairs
        for (program_id, event_authority, pair, x_vault, sol_vault, oracle, bin_arrays) in
            dlmm_pairs.iter()
        {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new(*event_authority, false));
            accounts.push(AccountMeta::new(*pair, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*sol_vault, false));
            accounts.push(AccountMeta::new(*oracle, false));
            accounts.extend(bin_arrays.clone());
        }

        // Add Whirlpool pools
        for (program_id, pool, oracle, x_vault, y_vault, tick_arrays) in whirlpool_pools.iter() {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new(*pool, false));
            accounts.push(AccountMeta::new(*oracle, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*y_vault, false));
            accounts.extend(tick_arrays.clone());
        }
        // Add Raydium CLMM pools
        for (
            program_id,
            pool,
            amm_config,
            observation_state,
            bitmap_extension,
            x_vault,
            y_vault,
            tick_arrays,
        ) in raydium_clmm_pools.iter()
        {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new(*pool, false));
            accounts.push(AccountMeta::new_readonly(*amm_config, false));
            accounts.push(AccountMeta::new(*observation_state, false));
            accounts.push(AccountMeta::new(*bitmap_extension, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*y_vault, false));
            accounts.extend(tick_arrays.clone());
        }
        // Add Meteora pools
        for (
            program_id,
            vault_program_id,
            pool,
            x_vault,
            sol_vault,
            x_token_vault,
            sol_token_vault,
            x_lp_mint,
            sol_lp_mint,
            x_pool_lp,
            sol_pool_lp,
            x_admin_fee,
            sol_admin_fee,
        ) in meteora_pools.iter()
        {
            accounts.push(AccountMeta::new_readonly(*program_id, false));
            accounts.push(AccountMeta::new_readonly(*vault_program_id, false));
            accounts.push(AccountMeta::new(*pool, false));
            accounts.push(AccountMeta::new(*x_vault, false));
            accounts.push(AccountMeta::new(*sol_vault, false));
            accounts.push(AccountMeta::new(*x_token_vault, false));
            accounts.push(AccountMeta::new(*sol_token_vault, false));
            accounts.push(AccountMeta::new(*x_lp_mint, false));
            accounts.push(AccountMeta::new(*sol_lp_mint, false));
            accounts.push(AccountMeta::new(*x_pool_lp, false));
            accounts.push(AccountMeta::new(*sol_pool_lp, false));
            accounts.push(AccountMeta::new(*x_admin_fee, false));
            accounts.push(AccountMeta::new(*sol_admin_fee, false));
        }
    }

    // Create instruction data
    let mut data = vec![15u8];
    data.extend_from_slice(&minimum_profit.to_le_bytes());
    data.extend_from_slice(&max_bin_to_process.to_le_bytes());

    Instruction {
        program_id: *program_id,
        accounts,
        data,
    }
}

â›“ī¸
https://github.com/Cetipoo/solana-onchain-arbitrage-bot
https://github.com/Cetipoo/solana-onchain-arbitrage-bot