2025-09-07 17:22:58 +08:00
|
|
|
use anyhow::Result;
|
2025-10-30 22:11:55 +08:00
|
|
|
use solana_hash::Hash;
|
|
|
|
|
use solana_sdk::{
|
|
|
|
|
instruction::Instruction, message::AddressLookupTableAccount, pubkey::Pubkey,
|
|
|
|
|
signature::Keypair, signature::Signature,
|
|
|
|
|
};
|
2025-09-07 18:07:45 +08:00
|
|
|
use std::{sync::Arc, time::Instant};
|
2025-06-17 23:32:20 +08:00
|
|
|
|
2025-10-06 23:40:27 +08:00
|
|
|
use crate::{
|
2025-10-30 22:11:55 +08:00
|
|
|
common::{nonce_cache::DurableNonceInfo, GasFeeStrategy, SolanaRpcClient},
|
2025-10-06 23:40:27 +08:00
|
|
|
perf::syscall_bypass::SystemCallBypassManager,
|
|
|
|
|
trading::core::{
|
|
|
|
|
async_executor::execute_parallel,
|
2025-10-30 22:11:55 +08:00
|
|
|
execution::{ExecutionPath, InstructionProcessor, Prefetch},
|
2025-10-06 23:40:27 +08:00
|
|
|
traits::TradeExecutor,
|
|
|
|
|
},
|
2025-10-30 22:11:55 +08:00
|
|
|
trading::MiddlewareManager,
|
2025-06-17 23:32:20 +08:00
|
|
|
};
|
2025-10-06 23:40:27 +08:00
|
|
|
use once_cell::sync::Lazy;
|
2025-07-10 18:14:21 +08:00
|
|
|
|
2025-09-22 00:05:20 +08:00
|
|
|
use super::{params::SwapParams, traits::InstructionBuilder};
|
|
|
|
|
|
2025-10-06 23:40:27 +08:00
|
|
|
/// 🚀 全局系统调用绕过管理器
|
|
|
|
|
static SYSCALL_BYPASS: Lazy<SystemCallBypassManager> = Lazy::new(|| {
|
|
|
|
|
use crate::perf::syscall_bypass::SyscallBypassConfig;
|
|
|
|
|
SystemCallBypassManager::new(SyscallBypassConfig::default())
|
|
|
|
|
.expect("Failed to create SystemCallBypassManager")
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-27 14:27:26 +08:00
|
|
|
/// Generic trade executor implementation
|
2025-06-17 23:32:20 +08:00
|
|
|
pub struct GenericTradeExecutor {
|
|
|
|
|
instruction_builder: Arc<dyn InstructionBuilder>,
|
|
|
|
|
protocol_name: &'static str,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GenericTradeExecutor {
|
|
|
|
|
pub fn new(
|
|
|
|
|
instruction_builder: Arc<dyn InstructionBuilder>,
|
|
|
|
|
protocol_name: &'static str,
|
|
|
|
|
) -> Self {
|
2025-10-30 22:11:55 +08:00
|
|
|
Self { instruction_builder, protocol_name }
|
2025-06-17 23:32:20 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
|
impl TradeExecutor for GenericTradeExecutor {
|
2025-11-16 23:46:58 +08:00
|
|
|
async fn swap(&self, params: SwapParams) -> Result<(bool, Signature, Option<anyhow::Error>)> {
|
2025-10-06 23:40:27 +08:00
|
|
|
let total_start = Instant::now();
|
|
|
|
|
|
|
|
|
|
// 判断买卖方向
|
|
|
|
|
let is_buy = ExecutionPath::is_buy(¶ms.input_mint)
|
2025-09-22 00:05:20 +08:00
|
|
|
|| (params.input_mint == crate::constants::USD1_TOKEN_ACCOUNT
|
|
|
|
|
&& params.output_mint != crate::constants::WSOL_TOKEN_ACCOUNT);
|
2025-10-06 23:40:27 +08:00
|
|
|
|
|
|
|
|
// CPU 预取
|
|
|
|
|
Prefetch::keypair(¶ms.payer);
|
|
|
|
|
|
|
|
|
|
// 构建指令
|
|
|
|
|
let build_start = Instant::now();
|
2025-09-22 00:05:20 +08:00
|
|
|
let instructions = if is_buy {
|
|
|
|
|
self.instruction_builder.build_buy_instructions(¶ms).await?
|
|
|
|
|
} else {
|
|
|
|
|
self.instruction_builder.build_sell_instructions(¶ms).await?
|
2025-08-19 18:07:21 +08:00
|
|
|
};
|
2025-10-06 23:40:27 +08:00
|
|
|
let build_elapsed = build_start.elapsed();
|
|
|
|
|
|
|
|
|
|
// 指令预处理
|
|
|
|
|
InstructionProcessor::preprocess(&instructions)?;
|
|
|
|
|
|
|
|
|
|
// 中间件处理
|
2025-09-09 17:02:24 +08:00
|
|
|
let final_instructions = match ¶ms.middleware_manager {
|
2025-10-30 22:11:55 +08:00
|
|
|
Some(middleware_manager) => middleware_manager
|
|
|
|
|
.apply_middlewares_process_protocol_instructions(
|
|
|
|
|
instructions,
|
|
|
|
|
self.protocol_name.to_string(),
|
|
|
|
|
is_buy,
|
|
|
|
|
)?,
|
|
|
|
|
None => instructions,
|
2025-08-19 18:07:21 +08:00
|
|
|
};
|
2025-10-06 23:40:27 +08:00
|
|
|
|
|
|
|
|
// 提交前耗时
|
|
|
|
|
let before_submit_elapsed = total_start.elapsed();
|
|
|
|
|
|
2025-10-30 22:11:55 +08:00
|
|
|
// 如果是模拟模式,直接通过 RPC 模拟交易
|
|
|
|
|
if params.simulate {
|
|
|
|
|
let send_start = Instant::now();
|
|
|
|
|
let result = simulate_transaction(
|
|
|
|
|
params.rpc,
|
|
|
|
|
params.payer,
|
|
|
|
|
final_instructions,
|
|
|
|
|
params.address_lookup_table_account,
|
|
|
|
|
params.recent_blockhash,
|
|
|
|
|
params.durable_nonce,
|
|
|
|
|
if is_buy { params.data_size_limit } else { 0 },
|
|
|
|
|
params.middleware_manager,
|
|
|
|
|
self.protocol_name,
|
|
|
|
|
is_buy,
|
|
|
|
|
if is_buy { true } else { params.with_tip },
|
|
|
|
|
params.gas_fee_strategy,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
let send_elapsed = send_start.elapsed();
|
|
|
|
|
let total_elapsed = total_start.elapsed();
|
|
|
|
|
|
|
|
|
|
// Get performance metrics using fast timestamp
|
|
|
|
|
let timestamp_ns = SYSCALL_BYPASS.fast_timestamp_nanos();
|
|
|
|
|
|
|
|
|
|
// Print all timing metrics at once to avoid blocking critical path
|
|
|
|
|
println!("[Timestamp] {}ns", timestamp_ns);
|
|
|
|
|
println!(
|
|
|
|
|
"[Build Instructions] Time: {:.3}ms ({:.0}μs)",
|
|
|
|
|
build_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
build_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Before Submit] {:.3}ms ({:.0}μs)",
|
|
|
|
|
before_submit_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
before_submit_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Simulate Transaction] Time: {:.3}ms ({:.0}μs)",
|
|
|
|
|
send_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
send_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Total Time] {:.3}ms ({:.0}μs)",
|
|
|
|
|
total_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
total_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 23:40:27 +08:00
|
|
|
// 并行发送交易
|
|
|
|
|
let send_start = Instant::now();
|
|
|
|
|
let result = execute_parallel(
|
|
|
|
|
params.swqos_clients.clone(),
|
|
|
|
|
params.payer,
|
|
|
|
|
params.rpc,
|
|
|
|
|
final_instructions,
|
2025-10-07 20:56:02 +08:00
|
|
|
params.address_lookup_table_account,
|
2025-10-06 23:40:27 +08:00
|
|
|
params.recent_blockhash,
|
|
|
|
|
params.durable_nonce,
|
|
|
|
|
if is_buy { params.data_size_limit } else { 0 },
|
|
|
|
|
params.middleware_manager,
|
|
|
|
|
self.protocol_name,
|
|
|
|
|
is_buy,
|
|
|
|
|
params.wait_transaction_confirmed,
|
|
|
|
|
if is_buy { true } else { params.with_tip },
|
2025-10-07 23:12:37 +08:00
|
|
|
params.gas_fee_strategy,
|
2025-10-06 23:40:27 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
let send_elapsed = send_start.elapsed();
|
|
|
|
|
let total_elapsed = total_start.elapsed();
|
|
|
|
|
|
2025-10-30 22:11:55 +08:00
|
|
|
// Get performance metrics using fast timestamp
|
2025-10-06 23:40:27 +08:00
|
|
|
let timestamp_ns = SYSCALL_BYPASS.fast_timestamp_nanos();
|
|
|
|
|
|
2025-10-30 22:11:55 +08:00
|
|
|
// Print all timing metrics at once to avoid blocking critical path
|
|
|
|
|
println!("[Timestamp] {}ns", timestamp_ns);
|
|
|
|
|
println!(
|
|
|
|
|
"[Build Instructions] Time: {:.3}ms ({:.0}μs)",
|
|
|
|
|
build_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
build_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Before Submit] {:.3}ms ({:.0}μs)",
|
|
|
|
|
before_submit_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
before_submit_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Send Transaction] Time: {:.3}ms ({:.0}μs)",
|
|
|
|
|
send_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
send_elapsed.as_micros()
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"[Total Time] {:.3}ms ({:.0}μs)",
|
|
|
|
|
total_elapsed.as_micros() as f64 / 1000.0,
|
|
|
|
|
total_elapsed.as_micros()
|
|
|
|
|
);
|
2025-10-06 23:40:27 +08:00
|
|
|
|
|
|
|
|
result
|
2025-06-17 23:32:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn protocol_name(&self) -> &'static str {
|
|
|
|
|
self.protocol_name
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-30 22:11:55 +08:00
|
|
|
|
|
|
|
|
/// Simulate transaction using RPC client
|
|
|
|
|
async fn simulate_transaction(
|
|
|
|
|
rpc: Option<Arc<SolanaRpcClient>>,
|
|
|
|
|
payer: Arc<Keypair>,
|
|
|
|
|
instructions: Vec<Instruction>,
|
|
|
|
|
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
|
|
|
|
recent_blockhash: Option<Hash>,
|
|
|
|
|
durable_nonce: Option<DurableNonceInfo>,
|
|
|
|
|
data_size_limit: u32,
|
|
|
|
|
middleware_manager: Option<Arc<MiddlewareManager>>,
|
|
|
|
|
protocol_name: &'static str,
|
|
|
|
|
is_buy: bool,
|
|
|
|
|
with_tip: bool,
|
|
|
|
|
gas_fee_strategy: GasFeeStrategy,
|
2025-11-16 23:46:58 +08:00
|
|
|
) -> Result<(bool, Signature, Option<anyhow::Error>)> {
|
2025-10-30 22:11:55 +08:00
|
|
|
use crate::trading::common::build_transaction;
|
|
|
|
|
use solana_client::rpc_config::RpcSimulateTransactionConfig;
|
|
|
|
|
use solana_commitment_config::CommitmentLevel;
|
|
|
|
|
use solana_transaction_status::UiTransactionEncoding;
|
|
|
|
|
|
|
|
|
|
let rpc = rpc.ok_or_else(|| anyhow::anyhow!("RPC client is required for simulation"))?;
|
|
|
|
|
|
|
|
|
|
// Get gas fee strategy for simulation (use Default swqos type)
|
|
|
|
|
let trade_type =
|
|
|
|
|
if is_buy { crate::swqos::TradeType::Buy } else { crate::swqos::TradeType::Sell };
|
|
|
|
|
let gas_fee_configs = gas_fee_strategy.get_strategies(trade_type);
|
|
|
|
|
|
|
|
|
|
let default_config = gas_fee_configs
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|config| config.0 == crate::swqos::SwqosType::Default)
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("No default gas fee strategy found"))?;
|
|
|
|
|
|
|
|
|
|
let tip = if with_tip { default_config.2.tip } else { 0.0 };
|
|
|
|
|
let unit_limit = default_config.2.cu_limit;
|
|
|
|
|
let unit_price = default_config.2.cu_price;
|
|
|
|
|
|
|
|
|
|
// Build transaction for simulation
|
|
|
|
|
let transaction = build_transaction(
|
|
|
|
|
payer.clone(),
|
|
|
|
|
Some(rpc.clone()),
|
|
|
|
|
unit_limit,
|
|
|
|
|
unit_price,
|
|
|
|
|
instructions,
|
|
|
|
|
address_lookup_table_account,
|
|
|
|
|
recent_blockhash,
|
|
|
|
|
data_size_limit,
|
|
|
|
|
middleware_manager,
|
|
|
|
|
protocol_name,
|
|
|
|
|
is_buy,
|
|
|
|
|
false, // simulate doesn't need tip instruction
|
|
|
|
|
&Pubkey::default(),
|
|
|
|
|
tip,
|
|
|
|
|
durable_nonce,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
// Simulate the transaction
|
|
|
|
|
use solana_commitment_config::CommitmentConfig;
|
|
|
|
|
let simulate_result = rpc
|
|
|
|
|
.simulate_transaction_with_config(
|
|
|
|
|
&transaction,
|
|
|
|
|
RpcSimulateTransactionConfig {
|
|
|
|
|
sig_verify: false, // Don't verify signature during simulation for speed
|
|
|
|
|
replace_recent_blockhash: false, // Use actual blockhash from transaction
|
|
|
|
|
commitment: Some(CommitmentConfig {
|
|
|
|
|
commitment: CommitmentLevel::Processed, // Use Processed level to get latest state
|
|
|
|
|
}),
|
|
|
|
|
encoding: Some(UiTransactionEncoding::Base64), // Base64 encoding
|
|
|
|
|
accounts: None, // Don't return specific account states (can be specified if needed)
|
|
|
|
|
min_context_slot: None, // Don't specify minimum context slot
|
|
|
|
|
inner_instructions: true, // Enable inner instructions for debugging and detailed execution flow
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
let signature = transaction
|
|
|
|
|
.signatures
|
|
|
|
|
.first()
|
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("Transaction has no signatures"))?
|
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
|
|
if let Some(err) = simulate_result.value.err {
|
|
|
|
|
println!("\n========== [Simulation Failed] ==========");
|
|
|
|
|
println!("Error Type: {:?}", err);
|
|
|
|
|
println!("Signature: {:?}", signature);
|
|
|
|
|
|
|
|
|
|
// Print logs
|
|
|
|
|
if let Some(logs) = simulate_result.value.logs {
|
|
|
|
|
println!("\n========== Transaction Logs ==========");
|
|
|
|
|
for (i, log) in logs.iter().enumerate() {
|
|
|
|
|
println!("{:3}. {}", i + 1, log);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print account usage
|
|
|
|
|
if let Some(units_consumed) = simulate_result.value.units_consumed {
|
|
|
|
|
println!("\n========== Resource Consumption ==========");
|
|
|
|
|
println!("Compute Units Consumed: {}", units_consumed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("=========================================\n");
|
2025-11-16 23:46:58 +08:00
|
|
|
return Ok((false, signature, Some(anyhow::anyhow!("{:?}", err))));
|
2025-10-30 22:11:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Simulation succeeded
|
|
|
|
|
println!("\n========== [Simulation Succeeded] ==========");
|
|
|
|
|
println!("Signature: {:?}", signature);
|
|
|
|
|
|
|
|
|
|
if let Some(units_consumed) = simulate_result.value.units_consumed {
|
|
|
|
|
println!("Compute Units Consumed: {}", units_consumed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(logs) = simulate_result.value.logs {
|
|
|
|
|
println!("\n========== Transaction Logs ==========");
|
|
|
|
|
for (i, log) in logs.iter().enumerate() {
|
|
|
|
|
println!("{:3}. {}", i + 1, log);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("============================================\n");
|
|
|
|
|
|
2025-11-16 23:46:58 +08:00
|
|
|
Ok((true, signature, None))
|
2025-10-30 22:11:55 +08:00
|
|
|
}
|