Onchain program
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.
Here's a simplified Rust example for how to inteact with the program.
/// This supports arbitrage between a Raydium AMM and up to 3 DLMM pairs
pub fn generate_onchain_swap_multiple_instruction(
wallet: &Pubkey,
sol_mint: &Pubkey,
token_mint: &Pubkey,
wallet_sol_account: &Pubkey,
wallet_token_account: &Pubkey,
raydium_program_id: &Pubkey,
token_program: &Pubkey,
raydium_authority: &Pubkey,
raydium_amm: &Pubkey,
raydium_sol_vault: &Pubkey,
raydium_token_vault: &Pubkey,
dlmm_program_id: &Pubkey,
dlmm_event_authority: &Pubkey,
dlmm_pairs: &[(
&Pubkey, // DLMM pair account
&Pubkey, // DLMM token vault
&Pubkey, // DLMM SOL vault
&Pubkey, // DLMM oracle
Vec<AccountMeta>, // Bin array accounts needed for DLMM operations
)],
program_id: &Pubkey,
tip_amount: u64,
) -> Instruction {
// Create the list of accounts required for the onchain swap multiple instruction
let fee_collector = Pubkey::from_str("6AGB9kqgSp2mQXwYpdrV4QVV8urvCaDS35U1wsLssy6H").unwrap();
let mut accounts = vec![
// Required accounts in exact order
AccountMeta::new_readonly(*wallet, true), // Wallet (signer)
AccountMeta::new_readonly(*sol_mint, false), // SOL mint
AccountMeta::new_readonly(*token_mint, false), // Token mint
AccountMeta::new(fee_collector, false), // Fee collector
AccountMeta::new(*wallet_sol_account, false), // Wallet SOL token account
AccountMeta::new(*wallet_token_account, false), // Wallet token account
// Program IDs and authorities
AccountMeta::new_readonly(*dlmm_program_id, false), // DLMM program ID
AccountMeta::new_readonly(*raydium_program_id, false), // Raydium program ID
AccountMeta::new_readonly(*token_program, false), // Token program
AccountMeta::new_readonly(*raydium_authority, false), // Raydium authority
AccountMeta::new(*dlmm_event_authority, false), // DLMM event authority
// Raydium accounts
AccountMeta::new(*raydium_amm, false), // Raydium AMM account
AccountMeta::new(*raydium_sol_vault, false), // Raydium SOL vault
AccountMeta::new(*raydium_token_vault, false), // Raydium token vault
];
// Add accounts for each DLMM pair (up to 3)
for (dlmm_pair, dlmm_token_vault, dlmm_sol_vault, dlmm_oracle, bin_array_accounts) in dlmm_pairs
{
// Add the DLMM pair accounts
accounts.push(AccountMeta::new(**dlmm_pair, false)); // DLMM pair account
accounts.push(AccountMeta::new(**dlmm_token_vault, false)); // DLMM token vault
accounts.push(AccountMeta::new(**dlmm_sol_vault, false)); // DLMM SOL vault
accounts.push(AccountMeta::new(**dlmm_oracle, false)); // DLMM oracle
// Add bin array accounts (3 per DLMM pair)
accounts.extend(bin_array_accounts.clone());
}
// Create instruction data - the instruction ID followed by tip amount as u64 le bytes
let mut data = vec![6u8];
// If you use spam, set this to 0
// If you use jito, set this to the tip amount you pay.
// Program will fail if the profit is less than the number set here.
data.extend_from_slice(&tip_amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts,
data,
}
}
To get the bin array, here is an example code once you decode the LBPair for DLMM pool. You need the meteora dlmm sdk to make this work: https://github.com/MeteoraAg/dlmm-sdk/tree/main
You want to periodically update the bin array accounts as bin may move out of the list you have.
fn update_bin_array_accounts(dlmm_pool: &Pubkey, lb_pair: &LbPair) -> Result<Vec<AccountMeta>> {
// Calculate the bin array index from the active bin ID
let bin_array_index =
meteora_dlmm::state::bin::BinArray::bin_id_to_bin_array_index(lb_pair.active_id)
.map_err(|e| anyhow!("Failed to convert bin ID to bin array index: {:?}", e))?;
// Calculate a range of bin array indices centered on the active bin's array index
let mut bin_array_accounts = vec![];
let offsets = [-1, 0, 1];
for offset in offsets {
let array_idx = bin_array_index + offset;
let (array_pda, _) =
meteora_dlmm::utils::pda::derive_bin_array_pda(*dlmm_pool, array_idx as i64);
bin_array_accounts.push(AccountMeta::new(array_pda, false));
}
Ok(bin_array_accounts)
}
Last updated