V9 Instruction
Previous V9 instruction of the SolanaMevBot onchain program, still supported for existing integrations
This page documents the V9 instruction (opcode 28). It remains supported and unchanged. For the current V10 instruction with bridge pools and bridge routes, see Onchain Program.
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: https://github.com/Cetipoo/solana-onchain-arbitrage-bot
NOTE: It's recommended to include this address lookup table as it has all the fixed keys needed: 4sKLJ1Qoudh8PJyqBeuKocYdsZvxTcRShUt9aKqwhgvC
NOTE: It's recommended to use at least 250_000 compute unit limit when only amm pools/dlmm, and at least 450_000 compute unit limit with other pools. Ideally you may want 400_000 - 600_000 to be able to hit the big arbs and not being limited by compute unit. Using merge mint will need a bit more CU than just one mint.
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 https://github.com/Cetipoo/solana-onchain-arbitrage-bot to see how to extract all the keys for each pool type.
Current supported DEX
- Raydium
- Raydium CPMM
- Raydium CLMM
- Pumpfun Swap
- Meteora DLMM
- Meteora Dynamic Pool
- Meteora DAMM V2
- Orca whirlpool
- Humidifi
- Vertigo
- Heaven Dex
- Pancake Swap
- Byreal
- Futarchy
Example code to generate instruction
fn derive_pump_pool_v2(base_mint: &Pubkey, pump_program: &Pubkey) -> Pubkey {
Pubkey::find_program_address(&[b"pool-v2", base_mint.as_ref()], pump_program).0
}
pub fn generate_onchain_swap_multiple_mints_instruction(
wallet: &Pubkey,
base_mint: &Pubkey,
wallet_base_account: &Pubkey,
token_program: &Pubkey,
system_program: &Pubkey,
associated_token_program: &Pubkey,
token_pools: &[(
Pubkey, // X mint
Pubkey, // Token program ID
Pubkey, // Wallet X account
Vec<(
Pubkey, // Raydium program ID
Pubkey, // V9: Base mint
Pubkey, // Raydium authority
// Raydium pools
Pubkey, // Raydium AMM account
Pubkey, // Raydium token X vault
Pubkey, // Raydium base vault
)>,
Vec<(
Pubkey, // Raydium CP program ID
Pubkey, // V9: Base mint
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 base vault
Pubkey, // Raydium CP observation
)>,
Vec<(
Pubkey, // Pump program ID
Pubkey, // V9: Base mint
Pubkey, // Pump global config
Pubkey, // Pump authority
Pubkey, // Pump fee wallet (writable; use the Mayhem fee wallet for mayhem-mode pools)
// Pump pools
Pubkey, // Pump pool
Pubkey, // Pump token X account
Pubkey, // Pump base account
Pubkey, // Pump fee token wallet
Pubkey, // Pump coin creator vault
Pubkey, // Pump coin creator vault authority
bool, // Pump is cashback coin
)>,
Vec<(
Pubkey, // DLMM program ID
Pubkey, // V9: Base mint
Pubkey, // DLMM event authority
Option<Pubkey>, // DLMM memo program v2 (Only needed for Token 2022)
// DLMM pairs
Pubkey, // DLMM pair account
Pubkey, // DLMM token X vault
Pubkey, // DLMM base vault
Pubkey, // DLMM oracle
Vec<AccountMeta>, // Bin array accounts
)>,
Vec<(
Pubkey, // Whirlpool program ID
Pubkey, // V9: Base mint
Pubkey, // Whirlpool memo program v2
// Whirlpool pools
Pubkey, // Whirlpool pool
Pubkey, // Whirlpool oracle
Pubkey, // Whirlpool token X vault
Pubkey, // Whirlpool token base vault
Vec<AccountMeta>, // Whirlpool tick array accounts
)>,
Vec<(
Pubkey, // Raydium CLMM program ID
Pubkey, // V9: Base mint
Option<Pubkey>, // Raydium CLMM memo program v2 (Only needed for Token 2022)
// 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 base vault
Vec<AccountMeta>, // Raydium CLMM tick array accounts
)>,
Vec<(
Pubkey, // Meteora program ID
Pubkey, // V9: Base mint
Pubkey, // Meteora vault program ID
// Meteora pools
Pubkey, // Meteora pool
Pubkey, // Meteora pool token X vault
Pubkey, // Meteora pool token base vault
Pubkey, // Meteora pool token X token vault
Pubkey, // Meteora pool token base token vault
Pubkey, // Meteora pool token X lp mint
Pubkey, // Meteora pool token base lp mint
Pubkey, // Meteora pool token X pool lp
Pubkey, // Meteora pool token base pool lp
Pubkey, // Meteora pool admin token fee X
Pubkey, // Meteora pool admin token fee base
)>,
Vec<(
Pubkey, // Solfi program ID
Pubkey, // V9: Base mint
Pubkey, // Sysvar instructions
// Solfi pools
Pubkey, // Solfi pool
Pubkey, // Solfi token X vault
Pubkey, // Solfi token base vault
)>,
Vec<(
Pubkey, // DAMM V2 program ID
Pubkey, // V9: Base mint
Pubkey, // Event authority
Pubkey, // Pool authority
// DAMM V2 pools
Pubkey, // DAMM V2 pool
Pubkey, // DAMM V2 token X vault
Pubkey, // DAMM V2 token base vault
)>,
Vec<(
Pubkey, // Vertigo program ID
Pubkey, // V9: Base mint
// Vertigo pools
Pubkey, // Vertigo pool
Pubkey, // Vertigo pool owner
Pubkey, // Vertigo token X vault
Pubkey, // Vertigo token base vault
)>,
Vec<(
Pubkey, // Heaven program ID
Pubkey, // V9: Base mint
// Heaven pools
Pubkey, // Heaven pool
Pubkey, // Heaven protocol config
Pubkey, // Heaven token X vault
Pubkey, // Heaven token base vault
)>,
)],
program_id: &Pubkey,
minimum_profit: u64,
compute_unit_limit: u32,
no_failure_mode: bool,
use_flashloan: bool,
) -> Instruction {
let fee_collector = if use_flashloan {
Pubkey::from_str("6AGB9kqgSp2mQXwYpdrV4QVV8urvCaDS35U1wsLssy6H").unwrap()
} else {
let fee_accounts = [
Pubkey::from_str("GPpkDpzCDmYJY5qNhYmM14c7rct1zmkjWc2CjR5g7RZ1").unwrap(),
Pubkey::from_str("J6c7noBHvWju4mMA3wXt3igbBSp2m9ATbA6cjMtAUged").unwrap(),
Pubkey::from_str("BjsfwxDu7GX7RRW6oSRTpMkASdXAgCcHnXEcatqSfuuY").unwrap(),
];
fee_accounts[rand::random::<usize>() % fee_accounts.len()]
};
let mut accounts = vec![
AccountMeta::new(*wallet, true),
AccountMeta::new_readonly(*base_mint, false),
AccountMeta::new(fee_collector, false),
AccountMeta::new(*wallet_base_account, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*system_program, false),
AccountMeta::new_readonly(*associated_token_program, false),
];
// Add flashloan accounts if enabled
if use_flashloan {
accounts.push(AccountMeta::new_readonly(
Pubkey::from_str("5LFpzqgsxrSfhKwbaFiAEJ2kbc9QyimjKueswsyU4T3o").unwrap(),
false,
));
let token_pda = derive_vault_token_account(
&Pubkey::from_str("MEViEnscUm6tsQRoGd9h6nLQaQspKj7DB2M5FwM3Xvz").unwrap(),
base_mint,
);
accounts.push(AccountMeta::new(token_pda.0, false));
}
// Detect mixed mode by checking if any pool has a USDC base mint
let usdc_mint = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap();
let mut has_usdc_base = false;
// Check all pools to see if any have USDC as base mint
for (
_,
_,
_,
raydium_pools,
raydium_cp_pools,
pump_pools,
dlmm_pairs,
whirlpool_pools,
raydium_clmm_pools,
meteora_pools,
solfi_pools,
dammv2_pools,
vertigo_pools,
) in token_pools.iter()
{
// Check Raydium pools
for (_, pool_base_mint, _, _, _, _) in raydium_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Raydium CP pools
for (_, pool_base_mint, _, _, _, _, _, _) in raydium_cp_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Pump pools
for (_, pool_base_mint, _, _, _, _, _, _, _, _, _, _) in pump_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check DLMM pairs
for (_, pool_base_mint, _, _, _, _, _, _, _) in dlmm_pairs.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Whirlpool pools
for (_, pool_base_mint, _, _, _, _, _, _) in whirlpool_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Raydium CLMM pools
for (_, pool_base_mint, _, _, _, _, _, _, _, _) in raydium_clmm_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Meteora pools
for (_, pool_base_mint, _, _, _, _, _, _, _, _, _, _, _, _) in meteora_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Solfi pools
for (_, pool_base_mint, _, _, _, _) in solfi_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check DAMM V2 pools
for (_, pool_base_mint, _, _, _, _, _) in dammv2_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Vertigo pools
for (_, pool_base_mint, _, _, _, _) in vertigo_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
// Check Heaven pools
for (_, pool_base_mint, _, _, _, _) in heaven_pools.iter() {
if *pool_base_mint == usdc_mint {
has_usdc_base = true;
break;
}
}
if has_usdc_base {
break;
}
}
// If mixed mode is detected, add the required accounts
if has_usdc_base {
// Add mixed mode accounts
let wallet_usdc_account = get_associated_token_address(wallet, &usdc_mint);
let raydium_program_id =
Pubkey::from_str("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").unwrap();
let raydium_authority =
Pubkey::from_str("5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1").unwrap();
let sysvar_instructions = solana_program::sysvar::instructions::id();
// Raydium SOL/USDC pool accounts (mainnet)
let raydium_sol_usdc_pool =
Pubkey::from_str("58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2").unwrap();
let raydium_usdc_vault =
Pubkey::from_str("HLmqeL62xR1QoZ1HKKbXRrdN1p3phKpxRMb2VVopvBBz").unwrap();
let raydium_sol_vault =
Pubkey::from_str("DQyrAcCrDXQ7NeoqGgDCZwBvWDcYmFCjSb9JtteuvPpz").unwrap();
accounts.push(AccountMeta::new_readonly(usdc_mint, false));
accounts.push(AccountMeta::new(wallet_usdc_account, false));
accounts.push(AccountMeta::new_readonly(raydium_program_id, false));
accounts.push(AccountMeta::new_readonly(raydium_authority, false));
accounts.push(AccountMeta::new_readonly(sysvar_instructions, false));
accounts.push(AccountMeta::new(raydium_sol_usdc_pool, false));
accounts.push(AccountMeta::new(raydium_usdc_vault, false));
accounts.push(AccountMeta::new(raydium_sol_vault, false));
}
// Add token mint accounts and their pools
for (
x_mint,
token_program_id,
wallet_x_account,
raydium_pools,
raydium_cp_pools,
pump_pools,
dlmm_pairs,
whirlpool_pools,
raydium_clmm_pools,
meteora_pools,
solfi_pools,
dammv2_pools,
vertigo_pools,
) in token_pools.iter()
{
// Add mint info
accounts.push(AccountMeta::new_readonly(*x_mint, false));
accounts.push(AccountMeta::new_readonly(*token_program_id, false));
accounts.push(AccountMeta::new(*wallet_x_account, false));
// Add Raydium pools
for (program_id, pool_base_mint, authority, pool, x_vault, base_vault) in
raydium_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(*authority, false));
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
}
// Add Raydium CP pools
for (
program_id,
pool_base_mint,
authority,
pool,
amm_config,
x_vault,
base_vault,
observation,
) in raydium_cp_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
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(*base_vault, false));
accounts.push(AccountMeta::new(*observation, false));
}
// Add Pump pools
for (
program_id,
pool_base_mint,
global_config,
authority,
fee_wallet,
pool,
x_account,
base_account,
fee_token_wallet,
coin_creator_vault,
coin_creator_vault_authority,
is_cashback_coin,
) in pump_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(*global_config, false));
accounts.push(AccountMeta::new_readonly(*authority, false));
accounts.push(AccountMeta::new(*fee_wallet, false));
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*x_account, false));
accounts.push(AccountMeta::new(*base_account, false));
accounts.push(AccountMeta::new(*fee_token_wallet, false));
accounts.push(AccountMeta::new(*coin_creator_vault, false));
accounts.push(AccountMeta::new_readonly(
*coin_creator_vault_authority,
false,
));
let pump_program_id = Pubkey::from_str("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").unwrap();
let (global_volume_accumulator, _) = Pubkey::find_program_address(
&[b"global_volume_accumulator"],
&pump_program_id,
);
let (user_volume_accumulator, _) = Pubkey::find_program_address(
&[b"user_volume_accumulator", wallet.as_ref()],
&pump_program_id,
);
accounts.push(AccountMeta::new_readonly(global_volume_accumulator, false));
accounts.push(AccountMeta::new(user_volume_accumulator, false));
// Add fee_config and fee_program accounts for PumpSwap
let fee_config =
Pubkey::from_str("5PHirr8joyTMp9JMm6nW7hNDVyEYdkzDqazxPD7RaTjx").unwrap();
let pump_fee_program =
Pubkey::from_str("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ").unwrap();
accounts.push(AccountMeta::new_readonly(fee_config, false));
accounts.push(AccountMeta::new_readonly(pump_fee_program, false));
// Cashback-tail accounts for Pump AMM v2:
// Cashback pools add the user_volume_accumulator WSOL ATA.
// The user_volume_accumulator account itself was already added above.
if *is_cashback_coin {
let user_volume_accumulator_wsol_ata = get_associated_token_address(
&user_volume_accumulator,
&Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(),
);
accounts.push(AccountMeta::new(user_volume_accumulator_wsol_ata, false));
}
// Pump AMM v2 PDA is derived from the pool's base mint
// (the X mint in our token_pools entry).
let pool_v2 = derive_pump_pool_v2(x_mint, program_id);
accounts.push(AccountMeta::new_readonly(pool_v2, false));
}
// Add DLMM pairs
for (
program_id,
pool_base_mint,
event_authority,
memo_program,
pair,
x_vault,
base_vault,
oracle,
bin_arrays,
) in dlmm_pairs.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(*event_authority, false));
if memo_program.is_some() {
accounts.push(AccountMeta::new_readonly(memo_program.unwrap(), false));
}
accounts.push(AccountMeta::new(*pair, false));
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
accounts.push(AccountMeta::new(*oracle, false));
accounts.extend(bin_arrays.clone());
}
// Add Whirlpool pools
for (
program_id,
pool_base_mint,
memo_program,
pool,
oracle,
x_vault,
base_vault,
tick_arrays,
) in whirlpool_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(MEMO_PROGRAM.clone(), false));
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*oracle, false)); // Oracle NEEDS to be writable for Whirlpool
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
accounts.extend(tick_arrays.clone());
}
// Add Raydium CLMM pools
for (
program_id,
pool_base_mint,
memo_program,
pool,
amm_config,
observation_state,
bitmap_extension,
x_vault,
base_vault,
tick_arrays,
) in raydium_clmm_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
if memo_program.is_some() {
accounts.push(AccountMeta::new_readonly(memo_program.unwrap(), 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(*base_vault, false));
accounts.extend(tick_arrays.clone());
}
// Add Meteora pools
for (
program_id,
pool_base_mint,
vault_program_id,
pool,
x_vault,
base_vault,
x_token_vault,
base_token_vault,
x_lp_mint,
base_lp_mint,
x_pool_lp,
base_pool_lp,
x_admin_fee,
base_admin_fee,
) in meteora_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
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(*base_vault, false));
accounts.push(AccountMeta::new(*x_token_vault, false));
accounts.push(AccountMeta::new(*base_token_vault, false));
accounts.push(AccountMeta::new(*x_lp_mint, false));
accounts.push(AccountMeta::new(*base_lp_mint, false));
accounts.push(AccountMeta::new(*x_pool_lp, false));
accounts.push(AccountMeta::new(*base_pool_lp, false));
accounts.push(AccountMeta::new(*x_admin_fee, false));
accounts.push(AccountMeta::new(*base_admin_fee, false));
}
// Add Solfi pools
for (program_id, pool_base_mint, sysvar_instructions, pool, x_vault, base_vault) in
solfi_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(*sysvar_instructions, false));
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
}
// Add DAMM V2 pools
for (
program_id,
pool_base_mint,
event_authority,
pool_authority,
pool,
x_vault,
base_vault,
) in dammv2_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new_readonly(*event_authority, false));
accounts.push(AccountMeta::new_readonly(*pool_authority, false));
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
accounts.push(AccountMeta::new_readonly(
Pubkey::from_str("Sysvar1nstructions1111111111111111111111111").unwrap(),
false,
));
}
// Add Vertigo pools
for (program_id, pool_base_mint, pool, pool_owner, x_vault, base_vault) in
vertigo_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new_readonly(*pool_owner, false));
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
}
// Add Heaven pools
for (program_id, pool_base_mint, pool, protocol_config, x_vault, base_vault) in
heaven_pools.iter()
{
accounts.push(AccountMeta::new_readonly(*program_id, false));
accounts.push(AccountMeta::new_readonly(*pool_base_mint, false)); // V9: Add base mint
accounts.push(AccountMeta::new(*pool, false));
accounts.push(AccountMeta::new(*protocol_config, false)); // Protocol config is writable for Heaven
// Add fixed Heaven accounts
accounts.push(AccountMeta::new_readonly(solana_program::sysvar::instructions::ID, false)); // Instructions sysvar
accounts.push(AccountMeta::new_readonly(
Pubkey::from_str("HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny").unwrap(),
false,
)); // Heaven protocol account 1
accounts.push(AccountMeta::new_readonly(
Pubkey::from_str("CH31Xns5z3M1cTAbKW34jcxPPciazARpijcHj9rxtemt").unwrap(),
false,
)); // Heaven protocol account 2
accounts.push(AccountMeta::new(*x_vault, false));
accounts.push(AccountMeta::new(*base_vault, false));
}
}
// Create instruction data
let mut data = vec![28u8];
data.extend_from_slice(&minimum_profit.to_le_bytes());
data.extend_from_slice(&compute_unit_limit.to_le_bytes());
data.extend_from_slice(if no_failure_mode { &[1] } else { &[0] });
data.extend_from_slice(&0u16.to_le_bytes()); // reserved
data.extend_from_slice(if use_flashloan { &[1] } else { &[0] });
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn derive_vault_token_account(program_id: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[b"vault_token_account", mint.as_ref()], program_id)
}Additional fee account
The onchain program supports charging additional fees on top of the bot fee itself. This allows you to build customized bot and charge your users yourself.
To do so you need to change two things when constructing the instruction.
- Add your additional fee account. Note this need to be the same token mint as base mint. aka if base mint is WSOL your additional fee account also need to be WSOL account.
- Change the additional_fee_bp arg. The number is in base point. For example if you set 500, it means you will charge 5% of the profit yourself, onchain program will take 10%, and user will get 85% of the profit.
Without an additional fee account the split is 10% to the onchain program and 90% to the user. All three shares are calculated on the profit above minimum_profit, not on the whole trade.
let additional_fee_collector = Pubkey::from_str("XXX").unwrap();
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(additional_fee_collector, false), // 2.5 Additional fee account
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
];
...
...
// Create instruction data
let mut data = vec![28u8];
data.extend_from_slice(&minimum_profit.to_le_bytes());
data.extend_from_slice(&compute_unit_limit.to_le_bytes());
data.extend_from_slice(if no_failure_mode { &[1] } else { &[0] });
// Example of 5% additional fee
let additional_fee_bp = 500u16;
data.extend_from_slice(&additional_fee_bp.to_le_bytes());
data.extend_from_slice(if use_flashloan { &[1] } else { &[0] });Using fixed trade size
This is advanced feature and only available when you call the program yourself. SMB bot does not set this value.
// Create instruction data
let mut data = vec![28u8];
data.extend_from_slice(&minimum_profit.to_le_bytes());
data.extend_from_slice(&compute_unit_limit.to_le_bytes());
data.extend_from_slice(if no_failure_mode { &[1] } else { &[0] });
data.extend_from_slice(&0u16.to_le_bytes()); // reserved
data.extend_from_slice(if use_flashloan { &[1] } else { &[0] });
let trade_size: u64 = fixed_trade_size; // 0 => disable override, >0 => fixed size
data.extend_from_slice(&trade_size.to_le_bytes());Worked examples
Two real mainnet transactions sent with this instruction. Wallet-owned accounts are shown as placeholders; every pool, mint and program address is the real one. Index ranges refer to the instruction's account list.
SOL → X → SOL
Transaction 5ZVh…CY6Q. X is URA URARfsinxCRw4JpvQhuT4CxavdZXZEMjv9ZwWmWpwag (Token-2022), with six Raydium CLMM pools: four URA/SOL and two URA/USDC. Because USDC pools are present, the mixed mode accounts are included. The executed route was SOL → URA → SOL, buying URA on pool AgZb…YouB and selling it on pool Hgjk…Pgu4.
Instruction data (17 bytes):
1c opcode 28
00 00 00 00 00 00 00 00 minimum_profit = 0
63 b1 07 00 compute_unit_limit = 504_163
00 no_failure = 0
00 00 additional_fee_bp = 0
01 use_flashloan = 1Accounts (82):
[0] wallet (signer)
[1] WSOL mint So11111111111111111111111111111111111111112
[2] fee collector
[3] wallet WSOL account
[4] Token program
[5] System program
[6] Associated Token program
[7] flashloan authority
5LFpzqgsxrSfhKwbaFiAEJ2kbc9QyimjKueswsyU4T3o
[8] flashloan vault
CHy11HNwixLjdhWDbQLsJ5C5otKxke1e37zVeqium9Uo
[9-16] mixed mode: USDC mint, wallet USDC account, Raydium
program, Raydium authority, instructions sysvar,
Raydium SOL/USDC pool 58oQ…YQo2, USDC vault, SOL vault
[17-19] X mint URAR…pwag, Token-2022 program, wallet URA
account
[20-30] Raydium CLMM block, URA/SOL pool Hgjk…Pgu4: program,
WSOL mint, memo, pool, amm config, observation, bitmap
extension, URA vault, SOL vault, 2 tick arrays
[31-40] Raydium CLMM block, URA/USDC pool 7XSg…Rcsc: program,
USDC mint, memo, pool, amm config, observation, bitmap
extension, URA vault, USDC vault, 1 tick array
[41-50] Raydium CLMM block, URA/SOL pool 7uYx…rUd5 (1 tick
array)
[51-61] Raydium CLMM block, URA/SOL pool 3ihM…QZc6 (2 tick
arrays)
[62-71] Raydium CLMM block, URA/SOL pool AgZb…YouB (1 tick
array)
[72-81] Raydium CLMM block, URA/USDC pool Fd1j…yqYK (1 tick
array)SOL → USDC → X → SOL
Transaction 2ZLU…xhCy. X is SKR SKRbvo6Gf7GondiT3BbTfuRDPqLWei4j2Qy2NPGZhW3 (SPL Token), with five Meteora DLMM pools: four SKR/SOL and one SKR/USDC. The executed route was SOL → USDC on the Raydium mixed mode pool, USDC → SKR on DLMM pool 3EFv…g6wB, then SKR → SOL on DLMM pool BCv5…TjQ9.
Instruction data (17 bytes):
1c opcode 28
00 00 00 00 00 00 00 00 minimum_profit = 0
09 aa 07 00 compute_unit_limit = 502_281
01 no_failure = 1
00 00 additional_fee_bp = 0
01 use_flashloan = 1Accounts (75):
[0-6] wallet, WSOL mint, fee collector, wallet WSOL account,
Token program, System program, Associated Token
program
[7-8] flashloan authority, flashloan vault
[9-16] mixed mode: USDC mint, wallet USDC account, Raydium
program, Raydium authority, instructions sysvar,
Raydium SOL/USDC pool 58oQ…YQo2, USDC vault, SOL vault
[17-19] X mint SKRb…ZhW3, Token program, wallet SKR account
[20-30] DLMM block, SKR/SOL pair BCv5…TjQ9: program, WSOL
mint, event authority, pair, SKR vault, SOL vault,
oracle, bin array bitmap extension, 3 bin arrays
[31-41] DLMM block, SKR/USDC pair 3EFv…g6wB: program, USDC
mint, event authority, pair, SKR vault, USDC vault,
oracle, bin array bitmap extension, 3 bin arrays
[42-52] DLMM block, SKR/SOL pair CFC6…8TgH
[53-63] DLMM block, SKR/SOL pair 9Jiv…7Vcm
[64-74] DLMM block, SKR/SOL pair 65tP…KJFDThe same two arbitrages expressed with the V10 instruction are shown in the Onchain Program worked examples.