use crate::{ common::SolanaRpcClient, instruction::utils::bonk_types::{pool_state_decode, PoolState}, }; use anyhow::anyhow; use solana_sdk::pubkey::Pubkey; /// Constants used as seeds for deriving PDAs (Program Derived Addresses) pub mod seeds { pub const POOL_SEED: &[u8] = b"pool"; pub const POOL_VAULT_SEED: &[u8] = b"pool_vault"; } /// Constants related to program accounts and authorities pub mod accounts { use solana_sdk::{pubkey, pubkey::Pubkey}; pub const AUTHORITY: Pubkey = pubkey!("WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh"); pub const GLOBAL_CONFIG: Pubkey = pubkey!("6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX"); pub const USD1_GLOBAL_CONFIG: Pubkey = pubkey!("EPiZbnrThjyLnoQ6QQzkxeFqyL5uyg9RzNHHAudUPxBz"); pub const EVENT_AUTHORITY: Pubkey = pubkey!("2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr"); pub const BONK: Pubkey = pubkey!("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"); pub const PLATFORM_FEE_RATE: u128 = 100; // 1% pub const PROTOCOL_FEE_RATE: u128 = 25; // 0.25% pub const SHARE_FEE_RATE: u128 = 0; // 0% // META pub const AUTHORITY_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: AUTHORITY, is_signer: false, is_writable: false, }; pub const GLOBAL_CONFIG_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: GLOBAL_CONFIG, is_signer: false, is_writable: false, }; pub const USD1_GLOBAL_CONFIG_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: USD1_GLOBAL_CONFIG, is_signer: false, is_writable: false, }; pub const EVENT_AUTHORITY_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: EVENT_AUTHORITY, is_signer: false, is_writable: false, }; pub const BONK_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: BONK, is_signer: false, is_writable: false }; } pub const BUY_EXECT_IN_DISCRIMINATOR: [u8; 8] = [250, 234, 13, 123, 213, 156, 19, 236]; pub const BUY_EXECT_OUT_DISCRIMINATOR: [u8; 8] = [24, 211, 116, 40, 105, 3, 153, 56]; pub const SELL_EXECT_IN_DISCRIMINATOR: [u8; 8] = [149, 39, 222, 155, 211, 124, 152, 26]; pub const SELL_EXECT_OUT_DISCRIMINATOR: [u8; 8] = [95, 200, 71, 34, 8, 9, 11, 166]; pub async fn fetch_pool_state( rpc: &SolanaRpcClient, pool_address: &Pubkey, ) -> Result { let account = rpc.get_account(pool_address).await?; if account.owner != accounts::BONK { return Err(anyhow!("Account is not owned by Bonk program")); } let pool_state = pool_state_decode(&account.data[8..]) .ok_or_else(|| anyhow!("Failed to decode pool state"))?; Ok(pool_state) } pub fn get_amount_in_net( amount_in: u64, protocol_fee_rate: u128, platform_fee_rate: u128, share_fee_rate: u128, ) -> u64 { let amount_in_u128 = amount_in as u128; let protocol_fee = (amount_in_u128 * protocol_fee_rate / 10000) as u128; let platform_fee = (amount_in_u128 * platform_fee_rate / 10000) as u128; let share_fee = (amount_in_u128 * share_fee_rate / 10000) as u128; amount_in_u128 .checked_sub(protocol_fee) .unwrap() .checked_sub(platform_fee) .unwrap() .checked_sub(share_fee) .unwrap() as u64 } pub fn get_amount_in( amount_out: u64, protocol_fee_rate: u128, platform_fee_rate: u128, share_fee_rate: u128, virtual_base: u128, virtual_quote: u128, real_base: u128, real_quote: u128, slippage_basis_points: u128, ) -> u64 { let amount_out_u128 = amount_out as u128; // Consider slippage, actual required output amount is higher let amount_out_with_slippage = amount_out_u128 * 10000 / (10000 - slippage_basis_points); let input_reserve = virtual_quote.checked_add(real_quote).unwrap(); let output_reserve = virtual_base.checked_sub(real_base).unwrap(); // Reverse calculate using AMM formula: amount_in_net = (amount_out * input_reserve) / (output_reserve - amount_out) let numerator = amount_out_with_slippage.checked_mul(input_reserve).unwrap(); let denominator = output_reserve.checked_sub(amount_out_with_slippage).unwrap(); let amount_in_net = numerator.checked_div(denominator).unwrap(); // Calculate total fee rate let total_fee_rate = protocol_fee_rate + platform_fee_rate + share_fee_rate; let amount_in = amount_in_net * 10000 / (10000 - total_fee_rate); amount_in as u64 } pub fn get_amount_out( amount_in: u64, protocol_fee_rate: u128, platform_fee_rate: u128, share_fee_rate: u128, virtual_base: u128, virtual_quote: u128, real_base: u128, real_quote: u128, slippage_basis_points: u128, ) -> u64 { let amount_in_u128 = amount_in as u128; let protocol_fee = (amount_in_u128 * protocol_fee_rate / 10000) as u128; let platform_fee = (amount_in_u128 * platform_fee_rate / 10000) as u128; let share_fee = (amount_in_u128 * share_fee_rate / 10000) as u128; let amount_in_net = amount_in_u128 .checked_sub(protocol_fee) .unwrap() .checked_sub(platform_fee) .unwrap() .checked_sub(share_fee) .unwrap(); let input_reserve = virtual_quote.checked_add(real_quote).unwrap(); let output_reserve = virtual_base.checked_sub(real_base).unwrap(); let numerator = amount_in_net.checked_mul(output_reserve).unwrap(); let denominator = input_reserve.checked_add(amount_in_net).unwrap(); let mut amount_out = numerator.checked_div(denominator).unwrap(); amount_out = amount_out - (amount_out * slippage_basis_points) / 10000; amount_out as u64 } pub fn get_pool_pda(base_mint: &Pubkey, quote_mint: &Pubkey) -> Option { crate::common::fast_fn::get_cached_pda( crate::common::fast_fn::PdaCacheKey::BonkPool(*base_mint, *quote_mint), || { let seeds: &[&[u8]; 3] = &[seeds::POOL_SEED, base_mint.as_ref(), quote_mint.as_ref()]; let program_id: &Pubkey = &accounts::BONK; let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id); pda.map(|pubkey| pubkey.0) }, ) } pub fn get_vault_pda(pool_state: &Pubkey, mint: &Pubkey) -> Option { crate::common::fast_fn::get_cached_pda( crate::common::fast_fn::PdaCacheKey::BonkVault(*pool_state, *mint), || { let seeds: &[&[u8]; 3] = &[seeds::POOL_VAULT_SEED, pool_state.as_ref(), mint.as_ref()]; let program_id: &Pubkey = &accounts::BONK; let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id); pda.map(|pubkey| pubkey.0) }, ) } pub fn get_platform_associated_account(platform_config: &Pubkey) -> Option { let seeds: &[&[u8]; 2] = &[platform_config.as_ref(), crate::constants::WSOL_TOKEN_ACCOUNT.as_ref()]; let program_id: &Pubkey = &accounts::BONK; let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id); pda.map(|pubkey| pubkey.0) } pub fn get_creator_associated_account(creator: &Pubkey) -> Option { let seeds: &[&[u8]; 2] = &[creator.as_ref(), crate::constants::WSOL_TOKEN_ACCOUNT.as_ref()]; let program_id: &Pubkey = &accounts::BONK; let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id); pda.map(|pubkey| pubkey.0) }