diff --git a/Cargo.toml b/Cargo.toml index 0d877f7..1e743fe 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "0.6.0" +version = "0.6.1" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index 2dc3c20..895e8a8 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.0" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.1" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "0.6.0" +sol-trade-sdk = "0.6.1" ``` ## Usage Examples diff --git a/README_CN.md b/README_CN.md index bed7e05..2062088 100755 --- a/README_CN.md +++ b/README_CN.md @@ -33,14 +33,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.0" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.6.1" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "0.6.0" +sol-trade-sdk = "0.6.1" ``` ## 使用示例 diff --git a/src/common/fast_fn.rs b/src/common/fast_fn.rs index eafddf5..819bc52 100644 --- a/src/common/fast_fn.rs +++ b/src/common/fast_fn.rs @@ -16,10 +16,10 @@ const MAX_INSTRUCTION_CACHE_SIZE: usize = 10000; // --------------------- Instruction Cache --------------------- -/// 指令缓存键,用于唯一标识指令类型和参数 +/// Instruction cache key for uniquely identifying instruction types and parameters #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum InstructionCacheKey { - /// Associated Token Account 创建指令 + /// Associated Token Account creation instruction CreateAssociatedTokenAccount { payer: Pubkey, owner: Pubkey, @@ -30,18 +30,18 @@ pub enum InstructionCacheKey { CloseWsolAccount { payer: Pubkey, wsol_token_account: Pubkey }, } -/// 全局指令缓存,用于存储常用指令 +/// Global instruction cache for storing common instructions static INSTRUCTION_CACHE: Lazy>> = Lazy::new(|| { RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_INSTRUCTION_CACHE_SIZE).unwrap())) }); -/// 获取缓存的指令,如果不存在则计算并缓存 +/// Get cached instruction, compute and cache if not exists pub fn get_cached_instruction(cache_key: InstructionCacheKey, compute_fn: F) -> Instruction where F: FnOnce() -> Instruction, { - // 尝试从缓存中获取(使用读锁) + // Try to get from cache (using read lock) { let cache = INSTRUCTION_CACHE.read(); if let Some(cached_instruction) = cache.peek(&cache_key) { @@ -49,10 +49,10 @@ where } } - // 缓存未命中,计算新的指令 + // Cache miss, compute new instruction let instruction = compute_fn(); - // 将计算结果存入缓存(使用写锁) + // Store computation result in cache (using write lock) { let mut cache = INSTRUCTION_CACHE.write(); cache.put(cache_key, instruction.clone()); @@ -69,7 +69,7 @@ pub fn create_associated_token_account_idempotent_fast( mint: &Pubkey, token_program: &Pubkey, ) -> Instruction { - // 创建缓存键 + // Create cache key let cache_key = InstructionCacheKey::CreateAssociatedTokenAccount { payer: *payer, owner: *owner, @@ -77,23 +77,23 @@ pub fn create_associated_token_account_idempotent_fast( token_program: *token_program, }; - // 使用缓存获取指令 + // Use cache to get instruction get_cached_instruction(cache_key, || { - // 使用缓存的方式获取 Associated Token Address + // Get Associated Token Address using cache let associated_token_address = get_associated_token_address_with_program_id_fast(owner, mint, token_program); - // 创建 Associated Token Account 指令 - // 参考 spl_associated_token_account::instruction::create_associated_token_account 的实现 + // Create Associated Token Account instruction + // Reference implementation of spl_associated_token_account::instruction::create_associated_token_account Instruction { program_id: ASSOCIATED_TOKEN_PROGRAM_ID, accounts: vec![ - AccountMeta::new(*payer, true), // 支付者(签名者,可写) - AccountMeta::new(associated_token_address, false), // ATA地址(可写,非签名者) - AccountMeta::new_readonly(*owner, false), // Token账户拥有者(只读,非签名者) - AccountMeta::new_readonly(*mint, false), // Token mint地址(只读,非签名者) + AccountMeta::new(*payer, true), // Payer (signer, writable) + AccountMeta::new(associated_token_address, false), // ATA address (writable, non-signer) + AccountMeta::new_readonly(*owner, false), // Token account owner (readonly, non-signer) + AccountMeta::new_readonly(*mint, false), // Token mint address (readonly, non-signer) crate::constants::SYSTEM_PROGRAM_META, - AccountMeta::new_readonly(*token_program, false), // Token程序(只读,非签名者) + AccountMeta::new_readonly(*token_program, false), // Token program (readonly, non-signer) ], data: vec![1], } @@ -102,7 +102,7 @@ pub fn create_associated_token_account_idempotent_fast( // --------------------- PDA --------------------- -/// PDA 缓存键,用于唯一标识 PDA 计算的输入参数 +/// PDA cache key for uniquely identifying PDA computation input parameters #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PdaCacheKey { PumpFunUserVolume(Pubkey), @@ -113,16 +113,16 @@ pub enum PdaCacheKey { PumpSwapUserVolume(Pubkey), } -/// 全局 PDA 缓存,用于存储计算结果 +/// Global PDA cache for storing computation results static PDA_CACHE: Lazy>> = Lazy::new(|| RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_PDA_CACHE_SIZE).unwrap()))); -/// 获取缓存的 PDA,如果不存在则计算并缓存 +/// Get cached PDA, compute and cache if not exists pub fn get_cached_pda(cache_key: PdaCacheKey, compute_fn: F) -> Option where F: FnOnce() -> Option, { - // 尝试从缓存中获取(使用读锁) + // Try to get from cache (using read lock) { let cache = PDA_CACHE.read(); if let Some(cached_pda) = cache.peek(&cache_key) { @@ -130,10 +130,10 @@ where } } - // 缓存未命中,计算新的 PDA + // Cache miss, compute new PDA let pda_result = compute_fn(); - // 如果计算成功,将结果存入缓存(使用写锁) + // If computation succeeds, store result in cache (using write lock) if let Some(pda) = pda_result { let mut cache = PDA_CACHE.write(); cache.put(cache_key, pda); @@ -144,7 +144,7 @@ where // --------------------- ATA --------------------- -/// ATA 缓存键,用于 Associated Token Address 缓存 +/// ATA cache key for Associated Token Address caching #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct AtaCacheKey { wallet_address: Pubkey, @@ -152,11 +152,11 @@ struct AtaCacheKey { token_program_id: Pubkey, } -/// 全局 ATA 缓存,用于存储 Associated Token Address 计算结果 +/// Global ATA cache for storing Associated Token Address computation results static ATA_CACHE: Lazy>> = Lazy::new(|| RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_ATA_CACHE_SIZE).unwrap()))); -/// 获取缓存的 Associated Token Address,如果不存在则计算并缓存 +/// Get cached Associated Token Address, compute and cache if not exists pub fn get_associated_token_address_with_program_id_fast( wallet_address: &Pubkey, token_mint_address: &Pubkey, @@ -168,7 +168,7 @@ pub fn get_associated_token_address_with_program_id_fast( token_program_id: *token_program_id, }; - // 尝试从缓存中获取(使用读锁) + // Try to get from cache (using read lock) { let cache = ATA_CACHE.read(); if let Some(cached_ata) = cache.peek(&cache_key) { @@ -176,14 +176,14 @@ pub fn get_associated_token_address_with_program_id_fast( } } - // 缓存未命中,计算新的 ATA + // Cache miss, compute new ATA let ata = get_associated_token_address_with_program_id( wallet_address, token_mint_address, token_program_id, ); - // 将计算结果存入缓存(使用写锁) + // Store computation result in cache (using write lock) { let mut cache = ATA_CACHE.write(); cache.put(cache_key, ata); @@ -192,20 +192,20 @@ pub fn get_associated_token_address_with_program_id_fast( ata } -// --------------------- 初始化账号 --------------------- +// --------------------- Initialize Accounts --------------------- pub fn fast_init(payer: &Pubkey) { - // 获取 PumpFun 用户量累加器 PDA + // Get PumpFun user volume accumulator PDA crate::instruction::utils::pumpfun::get_user_volume_accumulator_pda(payer); - // 获取 PumpSwap 用户量累加器 PDA + // Get PumpSwap user volume accumulator PDA crate::instruction::utils::pumpswap::get_user_volume_accumulator_pda(payer); - // 获取 wSOL ATA 地址 + // Get wSOL ATA address let wsol_token_account = get_associated_token_address_with_program_id_fast( payer, &crate::constants::WSOL_TOKEN_ACCOUNT, &crate::constants::TOKEN_PROGRAM, ); - // 获取 Close wSOL Account 指令 + // Get Close wSOL Account instruction get_cached_instruction( crate::common::fast_fn::InstructionCacheKey::CloseWsolAccount { payer: *payer, diff --git a/src/common/types.rs b/src/common/types.rs index f8a2dce..9cefc3e 100755 --- a/src/common/types.rs +++ b/src/common/types.rs @@ -38,9 +38,9 @@ pub struct PriorityFee { pub tip_unit_price: u64, pub rpc_unit_limit: u32, pub rpc_unit_price: u64, - // 与 swqos 顺序一致 (Matches the order of swqos) + // Matches the order of swqos pub buy_tip_fees: Vec, - // 与 swqos 顺序一致 (Matches the order of swqos) + // Matches the order of swqos pub sell_tip_fees: Vec, } @@ -51,9 +51,9 @@ impl Default for PriorityFee { tip_unit_price: DEFAULT_TIP_UNIT_PRICE, rpc_unit_limit: DEFAULT_RPC_UNIT_LIMIT, rpc_unit_price: DEFAULT_RPC_UNIT_PRICE, - // 与 swqos 顺序一致 (Matches the order of swqos) + // Matches the order of swqos buy_tip_fees: vec![DEFAULT_BUY_TIP_FEE], - // 与 swqos 顺序一致 (Matches the order of swqos) + // Matches the order of swqos sell_tip_fees: vec![DEFAULT_SELL_TIP_FEE], } } diff --git a/src/instruction/bonk.rs b/src/instruction/bonk.rs index b3d26aa..1038e9e 100755 --- a/src/instruction/bonk.rs +++ b/src/instruction/bonk.rs @@ -28,6 +28,9 @@ pub struct BonkInstructionBuilder; #[async_trait::async_trait] impl InstructionBuilder for BonkInstructionBuilder { async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } @@ -43,7 +46,20 @@ impl InstructionBuilder for BonkInstructionBuilder { protocol_params.pool_state }; - // Create user token accounts + // ======================================== + // Trade calculation and account address preparation + // ======================================== + let amount_in: u64 = params.sol_amount; + let share_fee_rate: u64 = 0; + let minimum_amount_out: u64 = get_buy_token_amount_from_sol_amount( + amount_in, + protocol_params.virtual_base, + protocol_params.virtual_quote, + protocol_params.real_base, + protocol_params.real_quote, + params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128, + ); + let user_base_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -57,7 +73,6 @@ impl InstructionBuilder for BonkInstructionBuilder { &crate::constants::TOKEN_PROGRAM, ); - // Get pool token accounts let base_vault_account = if protocol_params.base_vault == Pubkey::default() { get_vault_pda(&pool_state, ¶ms.mint).unwrap() } else { @@ -69,22 +84,9 @@ impl InstructionBuilder for BonkInstructionBuilder { protocol_params.quote_vault }; - let virtual_base = protocol_params.virtual_base; - let virtual_quote = protocol_params.virtual_quote; - let real_base = protocol_params.real_base; - let real_quote = protocol_params.real_quote; - - let amount_in: u64 = params.sol_amount; - let share_fee_rate: u64 = 0; - let minimum_amount_out: u64 = get_buy_token_amount_from_sol_amount( - amount_in, - virtual_base, - virtual_quote, - real_base, - real_quote, - params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128, - ); - + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(6); if protocol_params.auto_handle_wsol { @@ -92,7 +94,6 @@ impl InstructionBuilder for BonkInstructionBuilder { .extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), amount_in)); } - // Create user's base token account instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), @@ -100,7 +101,12 @@ impl InstructionBuilder for BonkInstructionBuilder { &protocol_params.mint_token_program, )); - // Create buy instruction + let mut data = [0u8; 32]; + data[..8].copy_from_slice(&BUY_EXECT_IN_DISCRIMINATOR); + data[8..16].copy_from_slice(&amount_in.to_le_bytes()); + data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes()); + data[24..32].copy_from_slice(&share_fee_rate.to_le_bytes()); + let accounts: [AccountMeta; 18] = [ AccountMeta::new(params.payer.pubkey(), true), // Payer (signer) accounts::AUTHORITY_META, // Authority (readonly) @@ -121,17 +127,10 @@ impl InstructionBuilder for BonkInstructionBuilder { AccountMeta::new(protocol_params.platform_associated_account, false), // Platform Associated Account AccountMeta::new(protocol_params.creator_associated_account, false), // Creator Associated Account ]; - // Create instruction data - let mut data = [0u8; 32]; - data[..8].copy_from_slice(&BUY_EXECT_IN_DISCRIMINATOR); - data[8..16].copy_from_slice(&amount_in.to_le_bytes()); - data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes()); - data[24..32].copy_from_slice(&share_fee_rate.to_le_bytes()); instructions.push(Instruction::new_with_bytes(accounts::BONK, &data, accounts.to_vec())); if protocol_params.auto_handle_wsol { - // Close wSOL ATA account, reclaim rent instructions.push(crate::trading::common::close_wsol(¶ms.payer.pubkey())); } @@ -139,6 +138,9 @@ impl InstructionBuilder for BonkInstructionBuilder { } async fn build_sell_instructions(&self, params: &SellParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== if params.rpc.is_none() { return Err(anyhow!("RPC is not set")); } @@ -151,7 +153,6 @@ impl InstructionBuilder for BonkInstructionBuilder { let rpc = params.rpc.as_ref().unwrap().clone(); - // Get token balance let mut amount = params.token_amount; if params.token_amount.is_none() || params.token_amount.unwrap_or(0) == 0 { let balance_u64 = @@ -170,22 +171,19 @@ impl InstructionBuilder for BonkInstructionBuilder { protocol_params.pool_state }; - let virtual_base = protocol_params.virtual_base; - let virtual_quote = protocol_params.virtual_quote; - let real_base = protocol_params.real_base; - let real_quote = protocol_params.real_quote; - - // Calculate expected SOL amount + // ======================================== + // Trade calculation and account address preparation + // ======================================== + let share_fee_rate: u64 = 0; let minimum_amount_out: u64 = get_sell_sol_amount_from_token_amount( amount, - virtual_base, - virtual_quote, - real_base, - real_quote, + protocol_params.virtual_base, + protocol_params.virtual_quote, + protocol_params.real_base, + protocol_params.real_quote, params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128, ); - // Create user token accounts let user_base_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -199,7 +197,6 @@ impl InstructionBuilder for BonkInstructionBuilder { &crate::constants::TOKEN_PROGRAM, ); - // Get pool token accounts let base_vault_account = if protocol_params.base_vault == Pubkey::default() { get_vault_pda(&pool_state, ¶ms.mint).unwrap() } else { @@ -211,17 +208,19 @@ impl InstructionBuilder for BonkInstructionBuilder { protocol_params.quote_vault }; - let share_fee_rate: u64 = 0; - + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(3); - // Handle wSOL - instructions.push( - // Create wSOL ATA account if it doesn't exist - crate::trading::common::create_wsol_ata(¶ms.payer.pubkey()), - ); + instructions.push(crate::trading::common::create_wsol_ata(¶ms.payer.pubkey())); + + let mut data = [0u8; 32]; + data[..8].copy_from_slice(&SELL_EXECT_IN_DISCRIMINATOR); + data[8..16].copy_from_slice(&amount.to_le_bytes()); + data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes()); + data[24..32].copy_from_slice(&share_fee_rate.to_le_bytes()); - // Create sell instruction let accounts: [AccountMeta; 18] = [ AccountMeta::new(params.payer.pubkey(), true), // Payer (signer) accounts::AUTHORITY_META, // Authority (readonly) @@ -243,13 +242,6 @@ impl InstructionBuilder for BonkInstructionBuilder { AccountMeta::new(protocol_params.creator_associated_account, false), // Creator Associated Account ]; - // Create instruction data - let mut data = [0u8; 32]; - data[..8].copy_from_slice(&SELL_EXECT_IN_DISCRIMINATOR); - data[8..16].copy_from_slice(&amount.to_le_bytes()); - data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes()); - data[24..32].copy_from_slice(&share_fee_rate.to_le_bytes()); - instructions.push(Instruction::new_with_bytes(accounts::BONK, &data, accounts.to_vec())); if protocol_params.auto_handle_wsol { diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index f1bc842..95d3554 100755 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -26,7 +26,9 @@ pub struct PumpFunInstructionBuilder; #[async_trait::async_trait] impl InstructionBuilder for PumpFunInstructionBuilder { async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { - // Get PumpFun specific parameters + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() @@ -38,14 +40,12 @@ impl InstructionBuilder for PumpFunInstructionBuilder { } let bonding_curve = &protocol_params.bonding_curve; - - let max_sol_cost = calculate_with_slippage_buy( - params.sol_amount, - params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), - ); let creator_vault_pda = protocol_params.creator_vault; let creator = get_creator(&creator_vault_pda); + // ======================================== + // Trade calculation and account address preparation + // ======================================== let buy_token_amount = get_buy_token_amount_from_sol_amount( bonding_curve.virtual_token_reserves as u128, bonding_curve.virtual_sol_reserves as u128, @@ -54,6 +54,39 @@ impl InstructionBuilder for PumpFunInstructionBuilder { params.sol_amount, ); + let max_sol_cost = calculate_with_slippage_buy( + params.sol_amount, + params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), + ); + + let bonding_curve_addr = if bonding_curve.account == Pubkey::default() { + get_bonding_curve_pda(¶ms.mint).unwrap() + } else { + bonding_curve.account + }; + + let associated_bonding_curve = + if protocol_params.associated_bonding_curve == Pubkey::default() { + crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + &bonding_curve_addr, + ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, + ) + } else { + protocol_params.associated_bonding_curve + }; + + let user_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + ¶ms.payer.pubkey(), + ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, + ); + + let user_volume_accumulator = get_user_volume_accumulator_pda(¶ms.payer.pubkey()).unwrap(); + + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(2); // Create associated token account @@ -64,42 +97,18 @@ impl InstructionBuilder for PumpFunInstructionBuilder { &crate::constants::TOKEN_PROGRAM, )); - // Create buy instruction data let mut buy_data = [0u8; 24]; - buy_data[..8].copy_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); + buy_data[..8].copy_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // Method ID buy_data[8..16].copy_from_slice(&buy_token_amount.to_le_bytes()); buy_data[16..24].copy_from_slice(&max_sol_cost.to_le_bytes()); - let bonding_curve = if bonding_curve.account == Pubkey::default() { - get_bonding_curve_pda(¶ms.mint).unwrap() - } else { - bonding_curve.account - }; - let associated_bonding_curve = - if protocol_params.associated_bonding_curve == Pubkey::default() { - crate::common::fast_fn::get_associated_token_address_with_program_id_fast( - &bonding_curve, - ¶ms.mint, - &crate::constants::TOKEN_PROGRAM, - ) - } else { - protocol_params.associated_bonding_curve - }; - let accounts: [AccountMeta; 16] = [ global_constants::GLOBAL_ACCOUNT_META, global_constants::FEE_RECIPIENT_META, AccountMeta::new_readonly(params.mint, false), - AccountMeta::new(bonding_curve, false), + AccountMeta::new(bonding_curve_addr, false), AccountMeta::new(associated_bonding_curve, false), - AccountMeta::new( - crate::common::fast_fn::get_associated_token_address_with_program_id_fast( - ¶ms.payer.pubkey(), - ¶ms.mint, - &crate::constants::TOKEN_PROGRAM, - ), - false, - ), + AccountMeta::new(user_token_account, false), AccountMeta::new(params.payer.pubkey(), true), crate::constants::SYSTEM_PROGRAM_META, crate::constants::TOKEN_PROGRAM_META, @@ -107,15 +116,11 @@ impl InstructionBuilder for PumpFunInstructionBuilder { accounts::EVENT_AUTHORITY_META, accounts::PUMPFUN_META, accounts::GLOBAL_VOLUME_ACCUMULATOR_META, - AccountMeta::new( - get_user_volume_accumulator_pda(¶ms.payer.pubkey()).unwrap(), - false, - ), + AccountMeta::new(user_volume_accumulator, false), accounts::FEE_CONFIG_META, accounts::FEE_PROGRAM_META, ]; - // Create buy instruction instructions.push(Instruction::new_with_bytes( accounts::PUMPFUN, &buy_data, @@ -126,15 +131,15 @@ impl InstructionBuilder for PumpFunInstructionBuilder { } async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - // Get PumpFun specific parameters + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() .downcast_ref::() .ok_or_else(|| anyhow!("Invalid protocol params for PumpFun"))?; - let bonding_curve = &protocol_params.bonding_curve; - let token_amount = if let Some(amount) = params.token_amount { if amount == 0 { return Err(anyhow!("Amount cannot be zero")); @@ -143,40 +148,36 @@ impl InstructionBuilder for PumpFunInstructionBuilder { } else { return Err(anyhow!("Amount token is required")); }; - let ata = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( - ¶ms.payer.pubkey(), - ¶ms.mint, - &crate::constants::TOKEN_PROGRAM, - ); + + let bonding_curve = &protocol_params.bonding_curve; let creator_vault_pda = protocol_params.creator_vault; let creator = get_creator(&creator_vault_pda); + // ======================================== + // Trade calculation and account address preparation + // ======================================== let sol_amount = get_sell_sol_amount_from_token_amount( bonding_curve.virtual_token_reserves as u128, bonding_curve.virtual_sol_reserves as u128, creator, token_amount, ); + let min_sol_output = calculate_with_slippage_sell( sol_amount, params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), ); - // Create sell instruction data - let mut sell_data = [0u8; 24]; - sell_data[..8].copy_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); - sell_data[8..16].copy_from_slice(&token_amount.to_le_bytes()); - sell_data[16..24].copy_from_slice(&min_sol_output.to_le_bytes()); - - let bonding_curve = if bonding_curve.account == Pubkey::default() { + let bonding_curve_addr = if bonding_curve.account == Pubkey::default() { get_bonding_curve_pda(¶ms.mint).unwrap() } else { bonding_curve.account }; + let associated_bonding_curve = if protocol_params.associated_bonding_curve == Pubkey::default() { crate::common::fast_fn::get_associated_token_address_with_program_id_fast( - &bonding_curve, + &bonding_curve_addr, ¶ms.mint, &crate::constants::TOKEN_PROGRAM, ) @@ -184,20 +185,29 @@ impl InstructionBuilder for PumpFunInstructionBuilder { protocol_params.associated_bonding_curve }; + let user_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + ¶ms.payer.pubkey(), + ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, + ); + + // ======================================== + // Build instructions + // ======================================== + let mut instructions = Vec::with_capacity(2); + + let mut sell_data = [0u8; 24]; + sell_data[..8].copy_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // Method ID + sell_data[8..16].copy_from_slice(&token_amount.to_le_bytes()); + sell_data[16..24].copy_from_slice(&min_sol_output.to_le_bytes()); + let accounts: [AccountMeta; 14] = [ global_constants::GLOBAL_ACCOUNT_META, global_constants::FEE_RECIPIENT_META, AccountMeta::new_readonly(params.mint, false), - AccountMeta::new(bonding_curve, false), + AccountMeta::new(bonding_curve_addr, false), AccountMeta::new(associated_bonding_curve, false), - AccountMeta::new( - crate::common::fast_fn::get_associated_token_address_with_program_id_fast( - ¶ms.payer.pubkey(), - ¶ms.mint, - &crate::constants::TOKEN_PROGRAM, - ), - false, - ), + AccountMeta::new(user_token_account, false), AccountMeta::new(params.payer.pubkey(), true), crate::constants::SYSTEM_PROGRAM_META, AccountMeta::new(creator_vault_pda, false), @@ -208,15 +218,17 @@ impl InstructionBuilder for PumpFunInstructionBuilder { accounts::FEE_PROGRAM_META, ]; - // Create sell instruction - let mut instructions = - vec![Instruction::new_with_bytes(accounts::PUMPFUN, &sell_data, accounts.to_vec())]; + instructions.push(Instruction::new_with_bytes( + accounts::PUMPFUN, + &sell_data, + accounts.to_vec(), + )); - // If selling all tokens, close the account + // Optional: Close token account if protocol_params.close_token_account_when_sell.unwrap_or(false) { instructions.push(close_account( &crate::constants::TOKEN_PROGRAM, - &ata, + &user_token_account, ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), &[¶ms.payer.pubkey()], diff --git a/src/instruction/pumpswap.rs b/src/instruction/pumpswap.rs index 1fc1014..380a5cc 100755 --- a/src/instruction/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -23,7 +23,9 @@ pub struct PumpSwapInstructionBuilder; #[async_trait::async_trait] impl InstructionBuilder for PumpSwapInstructionBuilder { async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { - // Get PumpSwap specific parameters + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() @@ -34,7 +36,6 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { return Err(anyhow!("Amount cannot be zero")); } - // Build instructions based on account information let pool = protocol_params.pool; let base_mint = protocol_params.base_mint; let quote_mint = protocol_params.quote_mint; @@ -54,15 +55,17 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { return Err(anyhow!("Invalid base mint and quote mint")); } + // ======================================== + // Trade calculation and account address preparation + // ======================================== let quote_mint_is_wsol = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT; - - let mut token_amount = 0; - let mut sol_amount = 0; - let mut creator = Pubkey::default(); if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY { creator = params_coin_creator_vault_authority; } + + let mut token_amount = 0; + let mut sol_amount = 0; if quote_mint_is_wsol { let result = buy_quote_input_internal( params.sol_amount, @@ -91,7 +94,6 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { sol_amount = params.sol_amount; } - // Create user token accounts let user_base_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -104,7 +106,11 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { "e_mint, "e_token_program, ); + let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(6); if auto_handle_wsol { @@ -112,7 +118,6 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { .extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), sol_amount)); } - // Create user's base token account instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), @@ -120,8 +125,6 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { if quote_mint_is_wsol { &base_token_program } else { "e_token_program }, )); - let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); - // Create buy instruction let mut accounts = Vec::with_capacity(23); accounts.extend([ @@ -184,14 +187,15 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { } async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - // Get PumpSwap specific parameters + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() .downcast_ref::() .ok_or_else(|| anyhow!("Invalid protocol params for PumpSwap"))?; - // Build instructions based on account information let pool = protocol_params.pool; let base_mint = protocol_params.base_mint; let quote_mint = protocol_params.quote_mint; @@ -214,16 +218,17 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { return Err(anyhow!("Token amount is not set")); } + // ======================================== + // Trade calculation and account address preparation + // ======================================== let quote_mint_is_wsol = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT; - - let mut token_amount = 0; - let mut sol_amount = 0; - let mut creator = Pubkey::default(); if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY { creator = params_coin_creator_vault_authority; } + let mut token_amount = 0; + let mut sol_amount = 0; if quote_mint_is_wsol { let result = sell_base_input_internal( params.token_amount.unwrap(), @@ -253,7 +258,6 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { } let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); - let user_base_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -267,18 +271,17 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { "e_token_program, ); + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(3); - // Insert wSOL - instructions.push( - // Create wSOL ATA account if it doesn't exist - crate::common::fast_fn::create_associated_token_account_idempotent_fast( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - &crate::constants::TOKEN_PROGRAM, - ), - ); + instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )); // Create sell instruction let mut accounts = Vec::with_capacity(23); diff --git a/src/instruction/raydium_amm_v4.rs b/src/instruction/raydium_amm_v4.rs index 1defe73..c09b20e 100755 --- a/src/instruction/raydium_amm_v4.rs +++ b/src/instruction/raydium_amm_v4.rs @@ -19,6 +19,9 @@ pub struct RaydiumAmmV4InstructionBuilder; #[async_trait::async_trait] impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } @@ -28,8 +31,10 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { .downcast_ref::() .ok_or_else(|| anyhow!("Invalid protocol params for RaydiumCpmm"))?; + // ======================================== + // Trade calculation and account address preparation + // ======================================== let is_base_in = protocol_params.coin_mint == crate::constants::WSOL_TOKEN_ACCOUNT; - let amount_in: u64 = params.sol_amount; let swap_result = compute_swap_amount( protocol_params.coin_reserve, @@ -40,21 +45,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { ); let minimum_amount_out = swap_result.min_amount_out; - let mut instructions = Vec::with_capacity(6); - - if protocol_params.auto_handle_wsol { - // Handle wSOL - instructions - .extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), amount_in)); - } - - instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - ¶ms.mint, - &crate::constants::TOKEN_PROGRAM, - )); - let user_source_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -68,6 +58,23 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { &crate::constants::TOKEN_PROGRAM, ); + // ======================================== + // Build instructions + // ======================================== + let mut instructions = Vec::with_capacity(6); + + if protocol_params.auto_handle_wsol { + instructions + .extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), amount_in)); + } + + instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, + )); + // Create buy instruction let accounts: [AccountMeta; 17] = [ crate::constants::TOKEN_PROGRAM_META, // Token Program (readonly) @@ -109,6 +116,9 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { } async fn build_sell_instructions(&self, params: &SellParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() @@ -119,6 +129,9 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { return Err(anyhow!("Token amount is not set")); } + // ======================================== + // Trade calculation and account address preparation + // ======================================== let is_base_in = protocol_params.pc_mint == crate::constants::WSOL_TOKEN_ACCOUNT; let swap_result = compute_swap_amount( protocol_params.coin_reserve, @@ -129,19 +142,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { ); let minimum_amount_out = swap_result.min_amount_out; - let mut instructions = Vec::with_capacity(3); - - // Handle wSOL - instructions.push( - // Create wSOL ATA account if it doesn't exist - crate::common::fast_fn::create_associated_token_account_idempotent_fast( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - &crate::constants::TOKEN_PROGRAM, - ), - ); - let user_source_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), @@ -155,6 +155,18 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { &crate::constants::TOKEN_PROGRAM, ); + // ======================================== + // Build instructions + // ======================================== + let mut instructions = Vec::with_capacity(3); + + instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )); + // Create buy instruction let accounts: [AccountMeta; 17] = [ crate::constants::TOKEN_PROGRAM_META, // Token Program (readonly) @@ -181,11 +193,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { data[1..9].copy_from_slice(¶ms.token_amount.unwrap_or(0).to_le_bytes()); data[9..17].copy_from_slice(&minimum_amount_out.to_le_bytes()); - // let mut data = vec![]; - // data.extend_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); - // data.extend_from_slice(&amount_in.to_le_bytes()); - // data.extend_from_slice(&minimum_amount_out.to_le_bytes()); - instructions.push(Instruction::new_with_bytes( accounts::RAYDIUM_AMM_V4, &data, diff --git a/src/instruction/raydium_cpmm.rs b/src/instruction/raydium_cpmm.rs index 2583e11..067b7e2 100755 --- a/src/instruction/raydium_cpmm.rs +++ b/src/instruction/raydium_cpmm.rs @@ -24,6 +24,9 @@ pub struct RaydiumCpmmInstructionBuilder; #[async_trait::async_trait] impl InstructionBuilder for RaydiumCpmmInstructionBuilder { async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } @@ -44,6 +47,9 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { protocol_params.pool_state }; + // ======================================== + // Trade calculation and account address preparation + // ======================================== let is_base_in = protocol_params.base_mint == crate::constants::WSOL_TOKEN_ACCOUNT; let mint_token_program = if is_base_in { protocol_params.quote_token_program @@ -51,6 +57,16 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { protocol_params.base_token_program }; + let amount_in: u64 = params.sol_amount; + let result = compute_swap_amount( + protocol_params.base_reserve, + protocol_params.quote_reserve, + is_base_in, + amount_in, + params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), + ); + let minimum_amount_out = result.min_amount_out; + let wsol_token_account = get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), &crate::constants::WSOL_TOKEN_ACCOUNT, @@ -62,7 +78,6 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { &mint_token_program, ); - // Get pool token accounts let wsol_vault_account = get_vault_account( &pool_state, &crate::constants::WSOL_TOKEN_ACCOUNT, @@ -78,16 +93,9 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { protocol_params.observation_state }; - let amount_in: u64 = params.sol_amount; - let result = compute_swap_amount( - protocol_params.base_reserve, - protocol_params.quote_reserve, - is_base_in, - amount_in, - params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), - ); - let minimum_amount_out = result.min_amount_out; - + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(6); if protocol_params.auto_handle_wsol { @@ -139,6 +147,9 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { } async fn build_sell_instructions(&self, params: &SellParams) -> Result> { + // ======================================== + // Parameter validation and basic data preparation + // ======================================== let protocol_params = params .protocol_params .as_any() @@ -149,6 +160,20 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { return Err(anyhow!("Token amount is not set")); } + let pool_state = if protocol_params.pool_state == Pubkey::default() { + get_pool_pda( + &accounts::AMM_CONFIG, + &protocol_params.base_mint, + &protocol_params.quote_mint, + ) + .unwrap() + } else { + protocol_params.pool_state + }; + + // ======================================== + // Trade calculation and account address preparation + // ======================================== let is_base_in = protocol_params.base_mint == params.mint; let mint_token_program = if is_base_in { protocol_params.base_token_program @@ -165,17 +190,6 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { ) .min_amount_out; - let pool_state = if protocol_params.pool_state == Pubkey::default() { - get_pool_pda( - &accounts::AMM_CONFIG, - &protocol_params.base_mint, - &protocol_params.quote_mint, - ) - .unwrap() - } else { - protocol_params.pool_state - }; - let wsol_token_account = get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), &crate::constants::WSOL_TOKEN_ACCOUNT, @@ -187,7 +201,6 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { &mint_token_program, ); - // Get pool token accounts let wsol_vault_account = get_vault_account( &pool_state, &crate::constants::WSOL_TOKEN_ACCOUNT, @@ -203,18 +216,17 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { protocol_params.observation_state }; + // ======================================== + // Build instructions + // ======================================== let mut instructions = Vec::with_capacity(3); - // Handle wSOL - instructions.push( - // Create wSOL ATA account if it doesn't exist - crate::common::fast_fn::create_associated_token_account_idempotent_fast( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - &crate::constants::TOKEN_PROGRAM, - ), - ); + instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )); // Create sell instruction let accounts: [AccountMeta; 13] = [ diff --git a/src/instruction/utils/bonk.rs b/src/instruction/utils/bonk.rs index b72c482..45b2ed1 100644 --- a/src/instruction/utils/bonk.rs +++ b/src/instruction/utils/bonk.rs @@ -95,18 +95,18 @@ pub fn get_amount_in( ) -> 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(); - // 根据 AMM 公式反推: amount_in_net = (amount_out * input_reserve) / (output_reserve - amount_out) + // 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); diff --git a/src/instruction/utils/pumpswap.rs b/src/instruction/utils/pumpswap.rs index 54aacee..8e00e3f 100644 --- a/src/instruction/utils/pumpswap.rs +++ b/src/instruction/utils/pumpswap.rs @@ -40,7 +40,7 @@ pub mod accounts { pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - // PumpSwap 协议费用接收者 + // PumpSwap protocol fee recipient pub const PROTOCOL_FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"); @@ -192,9 +192,9 @@ pub async fn find_by_base_mint( rpc: &SolanaRpcClient, base_mint: &Pubkey, ) -> Result<(Pubkey, Pool), anyhow::Error> { - // 使用getProgramAccounts查找给定mint的池子 + // Use getProgramAccounts to find pools for the given mint let filters = vec![ - // solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool账户的大小 + // solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool account size solana_rpc_client_api::filter::RpcFilterType::Memcmp( solana_client::rpc_filter::Memcmp::new_base58_encoded(43, &base_mint.to_bytes()), ), @@ -228,9 +228,9 @@ pub async fn find_by_quote_mint( rpc: &SolanaRpcClient, quote_mint: &Pubkey, ) -> Result<(Pubkey, Pool), anyhow::Error> { - // 使用getProgramAccounts查找给定mint的池子 + // Use getProgramAccounts to find pools for the given mint let filters = vec![ - // solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool账户的大小 + // solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool account size solana_rpc_client_api::filter::RpcFilterType::Memcmp( solana_client::rpc_filter::Memcmp::new_base58_encoded(75, "e_mint.to_bytes()), ), diff --git a/src/instruction/utils/raydium_cpmm.rs b/src/instruction/utils/raydium_cpmm.rs index 5dbc8b7..0e2e658 100644 --- a/src/instruction/utils/raydium_cpmm.rs +++ b/src/instruction/utils/raydium_cpmm.rs @@ -77,10 +77,10 @@ pub fn get_observation_state_pda(pool_state: &Pubkey) -> Option { pda.map(|pubkey| pubkey.0) } -/// 获取池子中两个代币的余额 +/// Get the balances of two tokens in the pool /// -/// # 返回值 -/// 返回 token0_balance, token1_balance +/// # Returns +/// Returns token0_balance, token1_balance pub async fn get_pool_token_balances( rpc: &SolanaRpcClient, pool_state: &Pubkey, @@ -92,20 +92,20 @@ pub async fn get_pool_token_balances( let token1_vault = get_vault_pda(pool_state, token1_mint).unwrap(); let token1_balance = rpc.get_token_account_balance(&token1_vault).await?; - // 解析余额字符串为 u64 + // Parse balance string to u64 let token0_amount = - token0_balance.amount.parse::().map_err(|e| anyhow!("解析 token0 余额失败: {}", e))?; + token0_balance.amount.parse::().map_err(|e| anyhow!("Failed to parse token0 balance: {}", e))?; let token1_amount = - token1_balance.amount.parse::().map_err(|e| anyhow!("解析 token1 余额失败: {}", e))?; + token1_balance.amount.parse::().map_err(|e| anyhow!("Failed to parse token1 balance: {}", e))?; Ok((token0_amount, token1_amount)) } -/// 计算代币价格 (token1/token0) +/// Calculate token price (token1/token0) /// -/// # 返回值 -/// 返回 token1 相对于 token0 的价格 +/// # Returns +/// Returns the price of token1 relative to token0 pub async fn calculate_price( token0_amount: u64, token1_amount: u64, @@ -113,25 +113,25 @@ pub async fn calculate_price( mint1_decimals: u8, ) -> Result { if token0_amount == 0 { - return Err(anyhow!("Token0 余额为零,无法计算价格")); + return Err(anyhow!("Token0 balance is zero, cannot calculate price")); } - // 考虑小数位精度 + // Consider decimal precision let token0_adjusted = token0_amount as f64 / 10_f64.powi(mint0_decimals as i32); let token1_adjusted = token1_amount as f64 / 10_f64.powi(mint1_decimals as i32); let price = token1_adjusted / token0_adjusted; Ok(price) } -/// 获取 vault 账户地址的辅助函数 +/// Helper function to get vault account address /// -/// # 参数 -/// - `pool_state`: 池子状态账户地址 -/// - `token_mint`: 代币 mint 地址 -/// - `protocol_params`: 协议参数 -/// - `is_wsol`: 是否为 wSOL 代币 +/// # Parameters +/// - `pool_state`: Pool state account address +/// - `token_mint`: Token mint address +/// - `protocol_params`: Protocol parameters +/// - `is_wsol`: Whether it's a wSOL token /// -/// # 返回值 -/// 返回对应的 vault 账户地址 +/// # Returns +/// Returns the corresponding vault account address pub fn get_vault_account( pool_state: &Pubkey, token_mint: &Pubkey, @@ -139,7 +139,7 @@ pub fn get_vault_account( is_wsol: bool, ) -> Pubkey { if is_wsol { - // 如果是 wSOL,检查是否为 base mint + // If it's wSOL, check if it's the base mint if protocol_params.base_mint == crate::constants::WSOL_TOKEN_ACCOUNT && protocol_params.base_vault != Pubkey::default() { @@ -152,7 +152,7 @@ pub fn get_vault_account( get_vault_pda(pool_state, &crate::constants::WSOL_TOKEN_ACCOUNT).unwrap() } } else { - // 对于其他代币,检查是否为 base 或 quote mint + // For other tokens, check if it's the base or quote mint if *token_mint == protocol_params.base_mint && protocol_params.base_vault != Pubkey::default() { diff --git a/src/trading/common/address_lookup_manager.rs b/src/trading/common/address_lookup_manager.rs index 9d393f1..9f8d379 100755 --- a/src/trading/common/address_lookup_manager.rs +++ b/src/trading/common/address_lookup_manager.rs @@ -2,8 +2,8 @@ use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey}; use crate::common::address_lookup_cache::get_address_lookup_table_account; -/// 获取地址查找表账户列表 -/// 如果提供了lookup_table_key,则获取对应的账户,否则返回空列表 +/// Get address lookup table account list +/// If lookup_table_key is provided, get the corresponding account, otherwise return empty list pub async fn get_address_lookup_table_accounts( lookup_table_key: Option, ) -> Vec { diff --git a/src/trading/common/compute_budget_manager.rs b/src/trading/common/compute_budget_manager.rs index e9f9a6d..952055d 100755 --- a/src/trading/common/compute_budget_manager.rs +++ b/src/trading/common/compute_budget_manager.rs @@ -4,7 +4,7 @@ use once_cell::sync::Lazy; use smallvec::SmallVec; use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruction}; -/// 缓存键,包含计算预算指令的所有参数 +/// Cache key containing all parameters for compute budget instructions #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct ComputeBudgetCacheKey { data_size_limit: u32, @@ -13,8 +13,8 @@ struct ComputeBudgetCacheKey { is_buy: bool, } -/// 全局缓存,存储计算预算指令 -/// 使用 DashMap 提供高性能的无锁并发访问 +/// Global cache storing compute budget instructions +/// Uses DashMap for high-performance lock-free concurrent access static COMPUTE_BUDGET_CACHE: Lazy>> = Lazy::new(|| DashMap::new()); @@ -31,15 +31,15 @@ pub fn compute_budget_instructions( (priority_fee.tip_unit_price, priority_fee.tip_unit_limit) }; - // 创建缓存键 + // Create cache key let cache_key = ComputeBudgetCacheKey { data_size_limit, unit_price, unit_limit, is_buy }; - // 先尝试从缓存中获取 + // Try to get from cache first if let Some(cached_insts) = COMPUTE_BUDGET_CACHE.get(&cache_key) { return cached_insts.clone(); } - // 缓存未命中,生成新的指令 + // Cache miss, generate new instructions let mut insts = SmallVec::<[Instruction; 3]>::new(); if is_buy { @@ -51,7 +51,7 @@ pub fn compute_budget_instructions( ComputeBudgetInstruction::set_compute_unit_limit(unit_limit), ]); - // 将结果存入缓存 + // Store result in cache let insts_clone = insts.clone(); COMPUTE_BUDGET_CACHE.insert(cache_key, insts_clone); diff --git a/src/trading/common/transaction_builder.rs b/src/trading/common/transaction_builder.rs index ee5fc6d..dfbaa5d 100755 --- a/src/trading/common/transaction_builder.rs +++ b/src/trading/common/transaction_builder.rs @@ -18,7 +18,7 @@ use super::{ }; use crate::{common::PriorityFee, trading::MiddlewareManager}; -/// 构建标准的RPC交易 +/// Build standard RPC transaction pub async fn build_transaction( payer: Arc, priority_fee: &PriorityFee, @@ -35,14 +35,14 @@ pub async fn build_transaction( ) -> Result { let mut instructions = Vec::with_capacity(business_instructions.len() + 5); - // 添加nonce指令 + // Add nonce instruction if is_buy { if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) { return Err(e); } } - // 添加计算预算指令 + // Add compute budget instructions instructions.extend(compute_budget_instructions( priority_fee, data_size_limit, @@ -50,10 +50,10 @@ pub async fn build_transaction( is_buy, )); - // 添加业务指令 + // Add business instructions instructions.extend(business_instructions); - // 添加小费转账指令 + // Add tip transfer instruction if with_tip { instructions.push(transfer( &payer.pubkey(), @@ -62,14 +62,14 @@ pub async fn build_transaction( )); } - // 获取交易使用的blockhash + // Get blockhash for transaction let blockhash = if is_buy { get_transaction_blockhash(recent_blockhash) } else { recent_blockhash }; - // 获取地址查找表账户 + // Get address lookup table accounts let address_lookup_table_accounts = get_address_lookup_table_accounts(lookup_table_key).await; - // 构建交易 + // Build transaction build_versioned_transaction( payer, instructions, @@ -82,7 +82,7 @@ pub async fn build_transaction( .await } -/// 构建版本化交易的底层函数 +/// Low-level function for building versioned transactions async fn build_versioned_transaction( payer: Arc, instructions: Vec,