Merge branch 'main' into feat/support-usdc-quote

# Conflicts:
#	examples/pumpswap_trading/src/main.rs
#	examples/raydium_cpmm_trading/src/main.rs
#	src/trading/core/executor.rs
This commit is contained in:
tommggo
2025-10-12 15:39:43 +08:00
73 changed files with 7589 additions and 768 deletions
@@ -1,38 +0,0 @@
use std::sync::Arc;
use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey};
use crate::common::{
address_lookup_cache::{get_address_lookup_table_account, AddressLookupTableCache},
SolanaRpcClient,
};
/// 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(
rpc: Option<Arc<SolanaRpcClient>>,
lookup_table_key: Option<Pubkey>,
) -> Vec<AddressLookupTableAccount> {
match lookup_table_key {
Some(key) => {
let account = get_address_lookup_table_account(&key).await;
if account.addresses.len() == 0 {
if rpc.is_some() {
let _ = AddressLookupTableCache::get_instance()
.set_address_lookup_table(rpc.unwrap(), &key)
.await;
let new_account = get_address_lookup_table_account(&key).await;
if new_account.addresses.len() == 0 {
return Vec::new();
} else {
return vec![new_account];
}
} else {
return Vec::new();
}
}
return vec![account];
}
None => Vec::new(),
}
}
-2
View File
@@ -1,7 +1,6 @@
pub mod nonce_manager;
pub mod transaction_builder;
pub mod compute_budget_manager;
pub mod address_lookup_manager;
pub mod utils;
pub mod wsol_manager;
@@ -9,6 +8,5 @@ pub mod wsol_manager;
pub use nonce_manager::*;
pub use transaction_builder::*;
pub use compute_budget_manager::*;
pub use address_lookup_manager::*;
pub use utils::*;
pub use wsol_manager::*;
+22 -21
View File
@@ -1,22 +1,18 @@
use solana_hash::Hash;
use solana_sdk::{
instruction::Instruction,
message::{v0, VersionedMessage},
native_token::sol_str_to_lamports,
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
transaction::VersionedTransaction,
instruction::Instruction, message::AddressLookupTableAccount, native_token::sol_str_to_lamports, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::VersionedTransaction
};
use solana_system_interface::instruction::transfer;
use std::sync::Arc;
use super::{
address_lookup_manager::get_address_lookup_table_accounts,
compute_budget_manager::compute_budget_instructions,
nonce_manager::{add_nonce_instruction, get_transaction_blockhash},
};
use crate::{common::{nonce_cache::DurableNonceInfo, SolanaRpcClient}, trading::MiddlewareManager};
use crate::{
common::{nonce_cache::DurableNonceInfo, SolanaRpcClient},
trading::{MiddlewareManager, core::transaction_pool::{acquire_builder, release_builder}},
};
/// Build standard RPC transaction
pub async fn build_transaction(
@@ -25,7 +21,7 @@ pub async fn build_transaction(
unit_limit: u32,
unit_price: u64,
business_instructions: Vec<Instruction>,
lookup_table_key: Option<Pubkey>,
address_lookup_table_account: Option<AddressLookupTableAccount>,
recent_blockhash: Option<Hash>,
data_size_limit: u32,
middleware_manager: Option<Arc<MiddlewareManager>>,
@@ -70,15 +66,11 @@ pub async fn build_transaction(
// Get blockhash for transaction
let blockhash = get_transaction_blockhash(recent_blockhash, durable_nonce.clone());
// Get address lookup table accounts
let address_lookup_table_accounts =
get_address_lookup_table_accounts(rpc, lookup_table_key).await;
// Build transaction
build_versioned_transaction(
payer,
instructions,
address_lookup_table_accounts,
address_lookup_table_account,
blockhash,
middleware_manager,
protocol_name,
@@ -91,7 +83,7 @@ pub async fn build_transaction(
async fn build_versioned_transaction(
payer: Arc<Keypair>,
instructions: Vec<Instruction>,
address_lookup_table_accounts: Vec<solana_sdk::message::AddressLookupTableAccount>,
address_lookup_table_account: Option<AddressLookupTableAccount>,
blockhash: Hash,
middleware_manager: Option<Arc<MiddlewareManager>>,
protocol_name: &str,
@@ -106,14 +98,23 @@ async fn build_versioned_transaction(
)?,
None => instructions,
};
let v0_message: v0::Message = v0::Message::try_compile(
// 使用预分配的交易构建器以降低延迟
let mut builder = acquire_builder();
let versioned_msg = builder.build_zero_alloc(
&payer.pubkey(),
&full_instructions,
&address_lookup_table_accounts,
address_lookup_table_account,
blockhash,
)?;
let versioned_msg = VersionedMessage::V0(v0_message);
);
let msg_bytes = versioned_msg.serialize();
let signature = payer.try_sign_message(&msg_bytes).expect("sign failed");
Ok(VersionedTransaction { signatures: vec![signature], message: versioned_msg })
let tx = VersionedTransaction { signatures: vec![signature], message: versioned_msg };
// 归还构建器到池
release_builder(builder);
Ok(tx)
}
+253
View File
@@ -0,0 +1,253 @@
//! 并行执行器
use anyhow::{anyhow, Result};
use crossbeam_queue::ArrayQueue;
use solana_hash::Hash;
use solana_sdk::message::AddressLookupTableAccount;
use solana_sdk::{
instruction::Instruction, pubkey::Pubkey, signature::Keypair, signature::Signature,
};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{str::FromStr, sync::Arc, time::Instant};
use crate::{
common::nonce_cache::DurableNonceInfo,
common::{GasFeeStrategy, SolanaRpcClient},
swqos::{SwqosClient, SwqosType, TradeType},
trading::{common::build_transaction, MiddlewareManager},
};
#[repr(align(64))]
struct TaskResult {
success: bool,
signature: Signature,
_error: Option<anyhow::Error>,
}
struct ResultCollector {
results: Arc<ArrayQueue<TaskResult>>,
success_flag: Arc<AtomicBool>,
completed_count: Arc<AtomicUsize>,
total_tasks: usize,
}
impl ResultCollector {
fn new(capacity: usize) -> Self {
Self {
results: Arc::new(ArrayQueue::new(capacity)),
success_flag: Arc::new(AtomicBool::new(false)),
completed_count: Arc::new(AtomicUsize::new(0)),
total_tasks: capacity,
}
}
fn submit(&self, result: TaskResult) {
// 🚀 优化:ArrayQueue 内部已保证同步,无需额外 fence
let is_success = result.success;
let _ = self.results.push(result);
if is_success {
self.success_flag.store(true, Ordering::Release); // Release 确保 push 可见
}
self.completed_count.fetch_add(1, Ordering::Release);
}
async fn wait_for_success(&self) -> Option<(bool, Signature)> {
let start = Instant::now();
let timeout = std::time::Duration::from_secs(30);
loop {
// 🚀 Acquire 确保看到 push 的内容
if self.success_flag.load(Ordering::Acquire) {
while let Some(result) = self.results.pop() {
if result.success {
return Some((true, result.signature));
}
}
}
let completed = self.completed_count.load(Ordering::Acquire);
if completed >= self.total_tasks {
while let Some(result) = self.results.pop() {
return Some((result.success, result.signature));
}
return None;
}
if start.elapsed() > timeout {
return None;
}
tokio::task::yield_now().await;
}
}
fn get_first(&self) -> Option<(bool, Signature)> {
if let Some(result) = self.results.pop() {
Some((result.success, result.signature))
} else {
None
}
}
}
pub async fn execute_parallel(
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
rpc: Option<Arc<SolanaRpcClient>>,
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,
wait_transaction_confirmed: bool,
with_tip: bool,
gas_fee_strategy: GasFeeStrategy,
) -> Result<(bool, Signature)> {
let _exec_start = Instant::now();
if swqos_clients.is_empty() {
return Err(anyhow!("swqos_clients is empty"));
}
if !with_tip
&& swqos_clients
.iter()
.find(|swqos| matches!(swqos.get_swqos_type(), SwqosType::Default))
.is_none()
{
return Err(anyhow!("No Rpc Default Swqos configured."));
}
let cores = core_affinity::get_core_ids().unwrap();
let instructions = Arc::new(instructions);
// 预先计算所有有效的组合
let task_configs: Vec<_> = swqos_clients
.iter()
.enumerate()
.filter(|(_, swqos_client)| {
with_tip || matches!(swqos_client.get_swqos_type(), SwqosType::Default)
})
.flat_map(|(i, swqos_client)| {
let gas_fee_strategy_configs = gas_fee_strategy.get_strategies(if is_buy {
TradeType::Buy
} else {
TradeType::Sell
});
gas_fee_strategy_configs
.into_iter()
.filter(|config| config.0.eq(&swqos_client.get_swqos_type()))
.map(move |config| (i, swqos_client.clone(), config))
})
.collect();
if task_configs.is_empty() {
return Err(anyhow!("No available gas fee strategy configs"));
}
// Task preparation completed
let collector = Arc::new(ResultCollector::new(task_configs.len()));
let _spawn_start = Instant::now();
for (i, swqos_client, gas_fee_strategy_config) in task_configs {
let core_id = cores[i % cores.len()];
let payer = payer.clone();
let instructions = instructions.clone();
let middleware_manager = middleware_manager.clone();
let swqos_type = swqos_client.get_swqos_type();
let tip_account_str = swqos_client.get_tip_account()?;
let tip_account = Arc::new(Pubkey::from_str(&tip_account_str).unwrap_or_default());
let collector = collector.clone();
let tip = gas_fee_strategy_config.2.tip;
let unit_limit = gas_fee_strategy_config.2.cu_limit;
let unit_price = gas_fee_strategy_config.2.cu_price;
let rpc = rpc.clone();
let durable_nonce = durable_nonce.clone();
let address_lookup_table_account = address_lookup_table_account.clone();
tokio::spawn(async move {
let _task_start = Instant::now();
core_affinity::set_for_current(core_id);
let tip_amount = if with_tip { tip } else { 0.0 };
let _build_start = Instant::now();
let transaction = match build_transaction(
payer,
rpc,
unit_limit,
unit_price,
instructions.as_ref().clone(),
address_lookup_table_account,
recent_blockhash,
data_size_limit,
middleware_manager,
protocol_name,
is_buy,
swqos_type != SwqosType::Default,
&tip_account,
tip_amount,
durable_nonce,
)
.await
{
Ok(tx) => tx,
Err(e) => {
// Build transaction failed
collector.submit(TaskResult {
success: false,
signature: Signature::default(),
_error: Some(e),
});
return;
}
};
// Transaction built
let _send_start = Instant::now();
let success = match swqos_client
.send_transaction(
if is_buy { TradeType::Buy } else { TradeType::Sell },
&transaction,
)
.await
{
Ok(()) => true,
Err(_e) => {
// Send transaction failed
false
}
};
// Transaction sent
if let Some(signature) = transaction.signatures.first() {
collector.submit(TaskResult { success, signature: *signature, _error: None });
}
});
}
// All tasks spawned
if !wait_transaction_confirmed {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
if let Some(result) = collector.get_first() {
return Ok(result);
}
return Err(anyhow!("No transaction signature available"));
}
if let Some(result) = collector.wait_for_success().await {
Ok(result)
} else {
Err(anyhow!("All transactions failed"))
}
}
+156
View File
@@ -0,0 +1,156 @@
//! 执行模块
use anyhow::Result;
use solana_sdk::{
instruction::Instruction,
pubkey::Pubkey,
signature::Keypair,
};
use crate::perf::{
hardware_optimizations::BranchOptimizer,
simd::SIMDMemory,
};
/// 预取工具
pub struct Prefetch;
impl Prefetch {
#[inline(always)]
pub fn instructions(instructions: &[Instruction]) {
if instructions.is_empty() {
return;
}
// 预取第一条指令
unsafe {
BranchOptimizer::prefetch_read_data(&instructions[0]);
}
// 预取中间指令
if instructions.len() > 2 {
unsafe {
BranchOptimizer::prefetch_read_data(&instructions[instructions.len() / 2]);
}
}
// 预取最后一条指令
if instructions.len() > 1 {
unsafe {
BranchOptimizer::prefetch_read_data(&instructions[instructions.len() - 1]);
}
}
}
#[inline(always)]
pub fn pubkey(pubkey: &Pubkey) {
unsafe {
BranchOptimizer::prefetch_read_data(pubkey);
}
}
#[inline(always)]
pub fn keypair(keypair: &Keypair) {
unsafe {
BranchOptimizer::prefetch_read_data(keypair);
}
}
}
/// 内存操作
pub struct MemoryOps;
impl MemoryOps {
#[inline(always)]
pub unsafe fn copy(dst: *mut u8, src: *const u8, len: usize) {
// 优先使用 AVX2 SIMD 加速
SIMDMemory::copy_avx2(dst, src, len);
}
#[inline(always)]
pub unsafe fn compare(a: *const u8, b: *const u8, len: usize) -> bool {
// 优先使用 AVX2 SIMD 比较
SIMDMemory::compare_avx2(a, b, len)
}
#[inline(always)]
pub unsafe fn zero(ptr: *mut u8, len: usize) {
// 优先使用 AVX2 SIMD 清零
SIMDMemory::zero_avx2(ptr, len);
}
}
/// 指令处理器
pub struct InstructionProcessor;
impl InstructionProcessor {
#[inline(always)]
pub fn preprocess(instructions: &[Instruction]) -> Result<()> {
// 分支预测: 大概率指令不为空
if BranchOptimizer::unlikely(instructions.is_empty()) {
return Err(anyhow::anyhow!("Instructions empty"));
}
// 预取所有指令到缓存
Prefetch::instructions(instructions);
// 分支预测: 大概率指令数量合理
if BranchOptimizer::unlikely(instructions.len() > 64) {
log::warn!("Large instruction count: {}", instructions.len());
}
Ok(())
}
#[inline(always)]
pub fn calculate_size(instructions: &[Instruction]) -> usize {
let mut total_size = 0;
for instr in instructions {
// 预取下一条指令
unsafe {
if let Some(next_instr) = instructions.get(total_size + 1) {
BranchOptimizer::prefetch_read_data(next_instr);
}
}
total_size += instr.data.len();
total_size += instr.accounts.len() * 32; // 每个账户 32 字节
}
total_size
}
}
/// 执行路径
pub struct ExecutionPath;
impl ExecutionPath {
#[inline(always)]
pub fn is_buy(input_mint: &Pubkey) -> bool {
// 分支预测: 大概率是买入
let is_buy = input_mint == &crate::constants::SOL_TOKEN_ACCOUNT
|| input_mint == &crate::constants::WSOL_TOKEN_ACCOUNT
|| input_mint == &crate::constants::USD1_TOKEN_ACCOUNT
|| input_mint == &crate::constants::USDC_TOKEN_ACCOUNT;
if BranchOptimizer::likely(is_buy) {
return true;
}
false
}
#[inline(always)]
pub fn select<T>(
condition: bool,
fast_path: impl FnOnce() -> T,
slow_path: impl FnOnce() -> T,
) -> T {
if BranchOptimizer::likely(condition) {
fast_path()
} else {
slow_path()
}
}
}
+85 -24
View File
@@ -2,13 +2,25 @@ use anyhow::Result;
use solana_sdk::signature::Signature;
use std::{sync::Arc, time::Instant};
use crate::trading::core::{
parallel::{buy_parallel_execute, sell_parallel_execute},
traits::TradeExecutor,
use crate::{
perf::syscall_bypass::SystemCallBypassManager,
trading::core::{
async_executor::execute_parallel,
execution::{Prefetch, InstructionProcessor, ExecutionPath},
traits::TradeExecutor,
},
};
use once_cell::sync::Lazy;
use super::{params::SwapParams, traits::InstructionBuilder};
/// 🚀 全局系统调用绕过管理器
static SYSCALL_BYPASS: Lazy<SystemCallBypassManager> = Lazy::new(|| {
use crate::perf::syscall_bypass::SyscallBypassConfig;
SystemCallBypassManager::new(SyscallBypassConfig::default())
.expect("Failed to create SystemCallBypassManager")
});
/// Generic trade executor implementation
pub struct GenericTradeExecutor {
instruction_builder: Arc<dyn InstructionBuilder>,
@@ -20,42 +32,91 @@ impl GenericTradeExecutor {
instruction_builder: Arc<dyn InstructionBuilder>,
protocol_name: &'static str,
) -> Self {
Self { instruction_builder, protocol_name }
Self {
instruction_builder,
protocol_name,
}
}
}
#[async_trait::async_trait]
impl TradeExecutor for GenericTradeExecutor {
async fn swap(&self, params: SwapParams) -> Result<(bool, Signature)> {
let start = Instant::now();
// 暂时支持这三种。后续重构扩展builder 支持所有的 swap
let is_buy = params.input_mint == crate::constants::SOL_TOKEN_ACCOUNT
|| params.input_mint == crate::constants::WSOL_TOKEN_ACCOUNT
|| params.input_mint == crate::constants::USDC_TOKEN_ACCOUNT
let total_start = Instant::now();
// 判断买卖方向
let is_buy = ExecutionPath::is_buy(&params.input_mint)
|| (params.input_mint == crate::constants::USD1_TOKEN_ACCOUNT
&& params.output_mint != crate::constants::WSOL_TOKEN_ACCOUNT);
// Build instructions directly from params to avoid unnecessary cloning
// CPU 预取
Prefetch::keypair(&params.payer);
// 构建指令
let build_start = Instant::now();
let instructions = if is_buy {
self.instruction_builder.build_buy_instructions(&params).await?
} else {
self.instruction_builder.build_sell_instructions(&params).await?
};
let build_elapsed = build_start.elapsed();
// 指令预处理
InstructionProcessor::preprocess(&instructions)?;
// 中间件处理
let final_instructions = match &params.middleware_manager {
Some(middleware_manager) => middleware_manager
.apply_middlewares_process_protocol_instructions(
instructions,
self.protocol_name.to_string(),
is_buy,
)?,
None => instructions,
Some(middleware_manager) => {
middleware_manager
.apply_middlewares_process_protocol_instructions(
instructions,
self.protocol_name.to_string(),
is_buy,
)?
}
None => instructions
};
println!("Building swap transaction instructions time cost: {:?}", start.elapsed());
// Execute transactions in parallel
if is_buy {
buy_parallel_execute(params, final_instructions, self.protocol_name).await
} else {
sell_parallel_execute(params, final_instructions, self.protocol_name).await
}
// 提交前耗时
let before_submit_elapsed = total_start.elapsed();
// 并行发送交易
let send_start = Instant::now();
let result = execute_parallel(
params.swqos_clients.clone(),
params.payer,
params.rpc,
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,
params.wait_transaction_confirmed,
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();
// 使用快速时间戳获取性能指标
let timestamp_ns = SYSCALL_BYPASS.fast_timestamp_nanos();
// 在完成后一次性打印所有耗时,避免阻塞关键路径
println!("[时间戳] {}ns", timestamp_ns);
println!("[构建指令] 耗时: {:.3}ms ({:.0}μs)",
build_elapsed.as_micros() as f64 / 1000.0, build_elapsed.as_micros());
println!("[提交前耗时] {:.3}ms ({:.0}μs)",
before_submit_elapsed.as_micros() as f64 / 1000.0, before_submit_elapsed.as_micros());
println!("[发送交易] 耗时: {:.3}ms ({:.0}μs)",
send_elapsed.as_micros() as f64 / 1000.0, send_elapsed.as_micros());
println!("[总耗时] {:.3}ms ({:.0}μs)",
total_elapsed.as_micros() as f64 / 1000.0, total_elapsed.as_micros());
result
}
fn protocol_name(&self) -> &'static str {
+3 -1
View File
@@ -1,4 +1,6 @@
pub mod params;
pub mod traits;
pub mod executor;
pub mod parallel;
pub mod async_executor;
pub mod transaction_pool;
pub mod execution;
+6 -3
View File
@@ -6,6 +6,7 @@ use solana_sdk::{
use std::{str::FromStr, sync::Arc, time::Instant};
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use log::{info, debug};
use crate::{
common::nonce_cache::DurableNonceInfo,
@@ -87,7 +88,9 @@ async fn parallel_execute(
{
return Err(anyhow!("No Rpc Default Swqos configured."));
}
// 🚀 获取 CPU 核心并优化亲和性分配
let cores = core_affinity::get_core_ids().unwrap();
let _num_cores = cores.len();
let mut handles: Vec<JoinHandle<Result<(bool, Signature, Option<anyhow::Error>)>>> =
Vec::with_capacity(swqos_clients.len());
@@ -161,7 +164,7 @@ async fn parallel_execute(
)
.await?;
println!(
debug!(
"[{:?}] - [{:?}] - Building transaction instructions: {:?}",
swqos_type,
gas_fee_strategy_config.1,
@@ -186,7 +189,7 @@ async fn parallel_execute(
}
};
println!(
debug!(
"[{:?}] - [{:?}] - Submitting transaction instructions: {:?}",
swqos_type,
gas_fee_strategy_config.1,
@@ -247,6 +250,6 @@ async fn parallel_execute(
}
}
println!("All transactions failed: {:?}", errors);
info!("All transactions failed: {:?}", errors);
return Ok((false, last_signature.unwrap()));
}
+4 -2
View File
@@ -2,12 +2,13 @@ use super::traits::ProtocolParams;
use crate::common::bonding_curve::BondingCurveAccount;
use crate::common::nonce_cache::DurableNonceInfo;
use crate::common::spl_associated_token_account::get_associated_token_address_with_program_id;
use crate::common::SolanaRpcClient;
use crate::common::{GasFeeStrategy, SolanaRpcClient};
use crate::constants::TOKEN_PROGRAM;
use crate::swqos::{SwqosClient, TradeType};
use crate::trading::common::get_multi_token_balances;
use crate::trading::MiddlewareManager;
use solana_hash::Hash;
use solana_sdk::message::AddressLookupTableAccount;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
@@ -23,7 +24,7 @@ pub struct SwapParams {
pub output_token_program: Option<Pubkey>,
pub input_amount: Option<u64>,
pub slippage_basis_points: Option<u64>,
pub lookup_table_key: Option<Pubkey>,
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
pub recent_blockhash: Option<Hash>,
pub data_size_limit: u32,
pub wait_transaction_confirmed: bool,
@@ -38,6 +39,7 @@ pub struct SwapParams {
pub create_output_mint_ata: bool,
pub close_output_mint_ata: bool,
pub fixed_output_amount: Option<u64>,
pub gas_fee_strategy: GasFeeStrategy,
}
impl std::fmt::Debug for SwapParams {
+171
View File
@@ -0,0 +1,171 @@
//! 🚀 交易构建器对象池
//!
//! 预分配交易构建器,避免运行时分配:
//! - 对象池重用
//! - 零分配构建
//! - 零拷贝 I/O
//! - 内存预热
use crossbeam_queue::ArrayQueue;
use once_cell::sync::Lazy;
use solana_sdk::{
hash::Hash, instruction::Instruction, message::{v0, AddressLookupTableAccount, Message, VersionedMessage}, pubkey::Pubkey
};
use std::sync::Arc;
/// 预分配的交易构建器
pub struct PreallocatedTxBuilder {
/// 预分配的指令容器
instructions: Vec<Instruction>,
/// 预分配的地址查找表
lookup_tables: Vec<v0::MessageAddressTableLookup>,
}
impl PreallocatedTxBuilder {
fn new() -> Self {
Self {
instructions: Vec::with_capacity(32), // 预分配32条指令空间
lookup_tables: Vec::with_capacity(8), // 预分配8个查找表空间
}
}
/// 重置构建器 (清空但保留容量)
#[inline(always)]
fn reset(&mut self) {
self.instructions.clear();
self.lookup_tables.clear();
}
/// 🚀 零分配构建交易
///
/// # 交易版本自动选择
///
/// - **有地址查找表** (`lookup_table = Some`): 使用 `VersionedMessage::V0`
/// - 支持地址查找表压缩
/// - 减少交易大小
/// - 需要 RPC 支持 V0
///
/// - **无地址查找表** (`lookup_table = None`): 使用 `VersionedMessage::Legacy`
/// - 兼容所有 RPC 节点
/// - 无需地址查找表支持
/// - 适用于简单交易
///
/// # 示例
///
/// ```rust,ignore
/// // 无查找表 -> Legacy 消息
/// let msg = builder.build_zero_alloc(&payer, &ixs, None, blockhash);
/// assert!(matches!(msg, VersionedMessage::Legacy(_)));
///
/// // 有查找表 -> V0 消息
/// let msg = builder.build_zero_alloc(&payer, &ixs, Some(table_key), blockhash);
/// assert!(matches!(msg, VersionedMessage::V0(_)));
/// ```
#[inline(always)]
pub fn build_zero_alloc(
&mut self,
payer: &Pubkey,
instructions: &[Instruction],
address_lookup_table_account: Option<AddressLookupTableAccount>,
recent_blockhash: Hash,
) -> VersionedMessage {
// 重用已分配的 vector
self.reset();
self.instructions.extend_from_slice(instructions);
// ✅ 如果有查找表,使用 V0 消息
if let Some(address_lookup_table_account) = address_lookup_table_account {
// self.lookup_tables.push(v0::MessageAddressTableLookup {
// account_key: table_key,
// writable_indexes: vec![],
// readonly_indexes: vec![],
// });
// // 使用 Message::new 创建 legacy 消息,然后提取编译后的指令
// let legacy_msg = Message::new(&self.instructions, Some(payer));
// // 构建 V0 消息
// let message = v0::Message {
// header: legacy_msg.header,
// account_keys: legacy_msg.account_keys,
// recent_blockhash,
// instructions: legacy_msg.instructions,
// address_table_lookups: self.lookup_tables.clone(),
// };
let message = v0::Message::try_compile(
payer,
&self.instructions,
&[address_lookup_table_account],
recent_blockhash,
).expect("v0 message compile failed");
VersionedMessage::V0(message)
} else {
// ✅ 没有查找表,使用 Legacy 消息(兼容所有 RPC
let message = Message::new_with_blockhash(
&self.instructions,
Some(payer),
&recent_blockhash,
);
VersionedMessage::Legacy(message)
}
}
}
/// 🚀 全局交易构建器对象池
static TX_BUILDER_POOL: Lazy<Arc<ArrayQueue<PreallocatedTxBuilder>>> = Lazy::new(|| {
let pool = ArrayQueue::new(1000); // 1000个预分配构建器
// 预填充池
for _ in 0..100 {
let _ = pool.push(PreallocatedTxBuilder::new());
}
Arc::new(pool)
});
/// 🚀 从池中获取构建器
#[inline(always)]
pub fn acquire_builder() -> PreallocatedTxBuilder {
TX_BUILDER_POOL
.pop()
.unwrap_or_else(|| PreallocatedTxBuilder::new())
}
/// 🚀 归还构建器到池
#[inline(always)]
pub fn release_builder(mut builder: PreallocatedTxBuilder) {
builder.reset();
let _ = TX_BUILDER_POOL.push(builder);
}
/// 获取池统计
pub fn get_pool_stats() -> (usize, usize) {
(TX_BUILDER_POOL.len(), TX_BUILDER_POOL.capacity())
}
/// 🚀 RAII 构建器包装器 (自动归还)
pub struct TxBuilderGuard {
builder: Option<PreallocatedTxBuilder>,
}
impl TxBuilderGuard {
pub fn new() -> Self {
Self {
builder: Some(acquire_builder()),
}
}
pub fn get_mut(&mut self) -> &mut PreallocatedTxBuilder {
self.builder.as_mut().unwrap()
}
}
impl Drop for TxBuilderGuard {
fn drop(&mut self) {
if let Some(builder) = self.builder.take() {
release_builder(builder);
}
}
}