sol-trade-sdk 4.0.3

Rust SDK to interact with the dex trade Solana program.
Documentation
//! 🚀 交易构建器对象池
//!
//! 预分配交易构建器,避免运行时分配:
//! - 对象池重用
//! - 零分配构建
//! - 零拷贝 I/O
//! - 内存预热

/// 预分配指令容量(单笔交易常见指令数)
const TX_BUILDER_INSTRUCTION_CAP: usize = 32;
/// 预分配地址查找表数量
const TX_BUILDER_LOOKUP_TABLE_CAP: usize = 8;
/// 对象池最大容量
const TX_BUILDER_POOL_CAP: usize = 1000;
/// 多路提交并发数(与 async_executor SWQOS_DEDICATED_DEFAULT_THREADS 一致,保证不串行)
const PARALLEL_SENDER_COUNT: usize = 18;
/// 启动时预填充数量,必须 >= PARALLEL_SENDER_COUNT,否则 18 路并发 build 会触发分配或争抢
const TX_BUILDER_POOL_PREFILL: usize = 64;

use crossbeam_queue::ArrayQueue;
use once_cell::sync::Lazy;
use solana_sdk::{
    hash::Hash,
    instruction::Instruction,
    message::{v0, Message, VersionedMessage},
    pubkey::Pubkey,
};
use solana_message::AddressLookupTableAccount;
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(TX_BUILDER_INSTRUCTION_CAP),
            lookup_tables: Vec::with_capacity(TX_BUILDER_LOOKUP_TABLE_CAP),
        }
    }

    /// 重置构建器 (清空但保留容量)
    #[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 {
        self.reset();
        self.instructions.extend_from_slice(instructions);

        if let Some(alt) = address_lookup_table_account {
            let message = v0::Message::try_compile(
                payer,
                &self.instructions,
                std::slice::from_ref(alt),
                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(TX_BUILDER_POOL_CAP);
    let prefill = TX_BUILDER_POOL_PREFILL.max(PARALLEL_SENDER_COUNT);
    for _ in 0..prefill {
        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);
        }
    }
}