diff --git a/src/common/gas_fee_strategy.rs b/src/common/gas_fee_strategy.rs index 396157c..6f2457a 100644 --- a/src/common/gas_fee_strategy.rs +++ b/src/common/gas_fee_strategy.rs @@ -317,6 +317,34 @@ impl GasFeeStrategy { self.strategies.store(Arc::new(HashMap::new())); } + /// 动态更新买入小费(保持其他参数不变) + /// Dynamically update buy tip (keep other parameters unchanged) + pub fn update_buy_tip(&self, buy_tip: f64) { + self.strategies.rcu(|current_map| { + let mut new_map = (**current_map).clone(); + for ((swqos_type, trade_type, strategy_type), value) in new_map.iter_mut() { + if *trade_type == TradeType::Buy { + value.tip = buy_tip; + } + } + Arc::new(new_map) + }); + } + + /// 动态更新卖出小费(保持其他参数不变) + /// Dynamically update sell tip (keep other parameters unchanged) + pub fn update_sell_tip(&self, sell_tip: f64) { + self.strategies.rcu(|current_map| { + let mut new_map = (**current_map).clone(); + for ((swqos_type, trade_type, strategy_type), value) in new_map.iter_mut() { + if *trade_type == TradeType::Sell { + value.tip = sell_tip; + } + } + Arc::new(new_map) + }); + } + /// 打印所有策略。 /// Print all strategies pub fn print_all_strategies(&self) { diff --git a/src/constants/swqos.rs b/src/constants/swqos.rs index c5b245b..baeab1c 100755 --- a/src/constants/swqos.rs +++ b/src/constants/swqos.rs @@ -217,3 +217,13 @@ pub const SWQOS_ENDPOINTS_ASTRALANE: [&str; 8] = [ "http://lim.gateway.astralane.io/iris", ]; +pub const SWQOS_MIN_TIP_DEFAULT: f64 = 0.00001; // 其它SWQOS默认最低小费 +pub const SWQOS_MIN_TIP_JITO: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_NEXTBLOCK: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_ZERO_SLOT: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_TEMPORAL: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_BLOXROUTE: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_NODE1: f64 = 0.002; // 如需更高阈值可调整 +pub const SWQOS_MIN_TIP_FLASHBLOCK: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_BLOCKRAZOR: f64 = SWQOS_MIN_TIP_DEFAULT; +pub const SWQOS_MIN_TIP_ASTRALANE: f64 = SWQOS_MIN_TIP_DEFAULT; diff --git a/src/lib.rs b/src/lib.rs index 7a2b9b1..b6064c9 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -688,12 +688,28 @@ impl SolanaTrade { /// - 交易执行或确认失败 /// - 网络或 RPC 错误 pub async fn wrap_wsol_to_sol(&self, amount: u64) -> Result { - use crate::trading::common::wsol_manager::wrap_wsol_to_sol as wrap_wsol_to_sol_internal; + use crate::trading::common::wsol_manager::{wrap_wsol_to_sol as wrap_wsol_to_sol_internal, wrap_wsol_to_sol_without_create}; + use crate::common::seed::get_associated_token_address_with_program_id_use_seed; use solana_sdk::transaction::Transaction; - let recent_blockhash = self.rpc.get_latest_blockhash().await?; - let instructions = wrap_wsol_to_sol_internal(&self.payer.pubkey(), amount)?; + // 检查临时seed账户是否已存在 + let seed_ata_address = get_associated_token_address_with_program_id_use_seed( + &self.payer.pubkey(), + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )?; + let account_exists = self.rpc.get_account(&seed_ata_address).await.is_ok(); + + let instructions = if account_exists { + // 如果账户已存在,使用不创建账户的版本 + wrap_wsol_to_sol_without_create(&self.payer.pubkey(), amount)? + } else { + // 如果账户不存在,使用创建账户的版本 + wrap_wsol_to_sol_internal(&self.payer.pubkey(), amount)? + }; + + let recent_blockhash = self.rpc.get_latest_blockhash().await?; let mut transaction = Transaction::new_with_payer(&instructions, Some(&self.payer.pubkey())); transaction.sign(&[&*self.payer], recent_blockhash); let signature = self.rpc.send_and_confirm_transaction(&transaction).await?; diff --git a/src/trading/common/transaction_builder.rs b/src/trading/common/transaction_builder.rs index a384af5..7304fbb 100755 --- a/src/trading/common/transaction_builder.rs +++ b/src/trading/common/transaction_builder.rs @@ -46,24 +46,10 @@ pub async fn build_transaction( // Add tip transfer instruction if with_tip && tip_amount > 0.0 { - // 🔧 Node1 最小小费金额限制:0.002 SOL(仅限 Node1) - const MIN_TIP_AMOUNT: f64 = 0.002; - - // 检查是否是 Node1 的 tip_account - let is_node1 = NODE1_TIP_ACCOUNTS.iter().any(|&account| account == *tip_account); - - let actual_tip_amount = if is_node1 && tip_amount < MIN_TIP_AMOUNT { - // Node1 要求最小 0.002 SOL - MIN_TIP_AMOUNT - } else { - // 其他 swqos 使用原始金额 - tip_amount - }; - instructions.push(transfer( &payer.pubkey(), tip_account, - sol_str_to_lamports(actual_tip_amount.to_string().as_str()).unwrap_or(0), + sol_str_to_lamports(tip_amount.to_string().as_str()).unwrap_or(0), )); } diff --git a/src/trading/common/wsol_manager.rs b/src/trading/common/wsol_manager.rs index ffde170..6ea6dfa 100644 --- a/src/trading/common/wsol_manager.rs +++ b/src/trading/common/wsol_manager.rs @@ -96,17 +96,22 @@ pub fn wrap_sol_only(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 2 } /// 将 WSOL 转换为 SOL,使用 seed 账户 -/// 1. 使用 super::seed::create_associated_token_account_use_seed 创建 WSOL seed 账号 -/// 2. 使用 get_associated_token_address_with_program_id_use_seed 获取该账号的 ATA 地址 -/// 3. 添加从用户 WSOL ATA 转账到该 seed ATA 账号的指令 -/// 4. 添加关闭 WSOL seed 账号的指令 +/// 1. 检查 seed 账户是否已存在 +/// 2. 如果不存在,使用 super::seed::create_associated_token_account_use_seed 创建 WSOL seed 账号 +/// 3. 使用 get_associated_token_address_with_program_id_use_seed 获取该账号的 ATA 地址 +/// 4. 添加从用户 WSOL ATA 转账到该 seed ATA 账号的指令 +/// 5. 添加关闭 WSOL seed 账号的指令 +/// +/// 注意:此函数只生成指令,不检查账户是否存在(需要调用方在发送交易前检查) +/// 如果临时账户已存在,可以安全地跳过创建步骤,直接转账并关闭 pub fn wrap_wsol_to_sol( payer: &Pubkey, amount: u64, ) -> Result, anyhow::Error> { let mut instructions = Vec::new(); - // 1. 创建 WSOL seed 账户 + // 1. 创建 WSOL seed 账户(注意:如果账户已存在会失败) + // 调用方应该先检查账户是否存在,如果存在则跳过此步骤 let seed_account_instructions = create_associated_token_account_use_seed( payer, payer, @@ -152,3 +157,49 @@ pub fn wrap_wsol_to_sol( Ok(instructions) } + +/// 将 WSOL 转换为 SOL(仅转账和关闭,不创建账户) +/// 用于当临时seed账户已存在的情况 +pub fn wrap_wsol_to_sol_without_create( + payer: &Pubkey, + amount: u64, +) -> Result, anyhow::Error> { + let mut instructions = Vec::new(); + + // 1. 获取 seed 账户的 ATA 地址 + let seed_ata_address = get_associated_token_address_with_program_id_use_seed( + payer, + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )?; + + // 2. 获取用户的 WSOL ATA 地址 + let user_wsol_ata = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + payer, + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + ); + + // 3. 添加从用户 WSOL ATA 转账到 seed ATA 的指令 + let transfer_instruction = crate::common::spl_token::transfer( + &crate::constants::TOKEN_PROGRAM, + &user_wsol_ata, + &seed_ata_address, + payer, + amount, + &[], + )?; + instructions.push(transfer_instruction); + + // 4. 添加关闭 WSOL seed 账户的指令 + let close_instruction = close_account( + &crate::constants::TOKEN_PROGRAM, + &seed_ata_address, + payer, + payer, + &[], + )?; + instructions.push(close_instruction); + + Ok(instructions) +} diff --git a/src/trading/core/async_executor.rs b/src/trading/core/async_executor.rs index 115b225..e5e6e0f 100644 --- a/src/trading/core/async_executor.rs +++ b/src/trading/core/async_executor.rs @@ -15,6 +15,18 @@ use crate::{ common::{GasFeeStrategy, SolanaRpcClient}, swqos::{SwqosClient, SwqosType, TradeType}, trading::{common::build_transaction, MiddlewareManager}, + constants::swqos::{ + SWQOS_MIN_TIP_DEFAULT, + SWQOS_MIN_TIP_JITO, + SWQOS_MIN_TIP_NEXTBLOCK, + SWQOS_MIN_TIP_ZERO_SLOT, + SWQOS_MIN_TIP_TEMPORAL, + SWQOS_MIN_TIP_BLOXROUTE, + SWQOS_MIN_TIP_NODE1, + SWQOS_MIN_TIP_FLASHBLOCK, + SWQOS_MIN_TIP_BLOCKRAZOR, + SWQOS_MIN_TIP_ASTRALANE, + }, }; #[repr(align(64))] @@ -142,6 +154,26 @@ pub async fn execute_parallel( gas_fee_strategy_configs .into_iter() .filter(|config| config.0.eq(&swqos_client.get_swqos_type())) + .filter(|config| { + // 当需要 tip 且不是 Default 时,按 provider 最低小费进行筛选 + if with_tip && !matches!(config.0, SwqosType::Default) { + let min_tip = match config.0 { + SwqosType::Jito => SWQOS_MIN_TIP_JITO, + SwqosType::NextBlock => SWQOS_MIN_TIP_NEXTBLOCK, + SwqosType::ZeroSlot => SWQOS_MIN_TIP_ZERO_SLOT, + SwqosType::Temporal => SWQOS_MIN_TIP_TEMPORAL, + SwqosType::Bloxroute => SWQOS_MIN_TIP_BLOXROUTE, + SwqosType::Node1 => SWQOS_MIN_TIP_NODE1, + SwqosType::FlashBlock => SWQOS_MIN_TIP_FLASHBLOCK, + SwqosType::BlockRazor => SWQOS_MIN_TIP_BLOCKRAZOR, + SwqosType::Astralane => SWQOS_MIN_TIP_ASTRALANE, + SwqosType::Default => SWQOS_MIN_TIP_DEFAULT, + }; + config.2.tip >= min_tip + } else { + true + } + }) .map(move |config| (i, swqos_client.clone(), config)) }) .collect();