Onchain program
Last updated
Last updated
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,
}
}