From 9874cd5358bf3c61bdd8e760a984c8626d22b3c9 Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:57:49 +0800 Subject: [PATCH] Release sol-trade-sdk v4.0.15 --- Cargo.toml | 2 +- README.md | 13 ++++++--- README_CN.md | 13 ++++++--- src/client/mod.rs | 43 +++++++++++++++++++++++++++++- src/common/fast_timing.rs | 13 ++++++--- src/trading/core/async_executor.rs | 16 ++++++++++- 6 files changed, 87 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e30e971..c96bafc 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "4.0.14" +version = "4.0.15" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index 501adc0..092dc30 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,12 @@ This SDK is available in multiple languages: | **Python** | [sol-trade-sdk-python](https://github.com/0xfnzero/sol-trade-sdk-python) | Async/await native support | | **Go** | [sol-trade-sdk-golang](https://github.com/0xfnzero/sol-trade-sdk-golang) | Concurrent-safe with goroutine support | +## 🔖 Current Release + +**Rust crate:** `sol-trade-sdk = "4.0.15"` + +This release refreshes PumpFun V2 and USDC quote-pool handling, keeps the default RPC submit lane active alongside SWQoS lanes, restores the fast-submit result window to 5 seconds, and aligns Raydium CPMM fixed-output swaps with the on-chain `swap_base_out` instruction. Trade execution requires a caller-supplied `recent_blockhash` or durable nonce; hot-path execution does not query RPC for blockhash, account, or balance data. + ## ✨ Features 1. **PumpFun Trading**: Unified SDK-side `buy`, `sell`, and `buy_exact_quote_in` flow, selecting legacy or V2 on-chain instructions as needed (SOL + USDC) @@ -88,10 +94,11 @@ This SDK is available in multiple languages: 5. **Raydium AMM V4 Trading**: Support for Raydium AMM V4 (Automated Market Maker) trading operations 6. **Meteora DAMM V2 Trading**: Support for Meteora DAMM V2 (Dynamic AMM) trading operations 7. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, FlashBlock, BlockRazor, Node1, Astralane and other services -8. **Concurrent Trading**: Send transactions using multiple MEV services simultaneously; the fastest succeeds while others fail +8. **Concurrent Trading**: Submit through every configured SWQoS provider plus the default RPC lane; the first accepted result can return early while slower routes continue submitting 9. **Unified Trading Interface**: Use unified trading protocol enums for trading operations 10. **Middleware System**: Support for custom instruction middleware to modify, add, or remove instructions before transaction execution 11. **Shared Infrastructure**: Share expensive RPC and SWQoS clients across multiple wallets for reduced resource usage +12. **Hot-Path RPC Boundary**: Trade execution uses caller-supplied blockhash or durable nonce and never queries RPC for blockhash, account, or balance data ## 📦 Installation @@ -108,14 +115,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.14" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.15" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "4.0.14" +sol-trade-sdk = "4.0.15" ``` ## 🛠️ Usage Examples diff --git a/README_CN.md b/README_CN.md index 4985d6d..e1152cd 100755 --- a/README_CN.md +++ b/README_CN.md @@ -79,6 +79,12 @@ | **Python** | [sol-trade-sdk-python](https://github.com/0xfnzero/sol-trade-sdk-python) | 原生 async/await 支持 | | **Go** | [sol-trade-sdk-golang](https://github.com/0xfnzero/sol-trade-sdk-golang) | 并发安全,goroutine 支持 | +## 🔖 当前版本 + +**Rust crate:** `sol-trade-sdk = "4.0.15"` + +本版本刷新 PumpFun V2 与 USDC quote 池处理逻辑,确保默认 RPC 提交通道会和 SWQoS 通道一起发出,快速提交结果等待窗口恢复为 5 秒,并将 Raydium CPMM fixed-output 交易对齐到链上 `swap_base_out` 指令。交易执行必须由调用方传入 `recent_blockhash` 或 durable nonce;热路径不会查询 RPC 获取 blockhash、账户或余额数据。 + ## ✨ 项目特性 1. **PumpFun 交易**: SDK 侧统一为 `buy`、`sell`、`buy_exact_quote_in` 流程,内部按需选择旧版或 V2 链上指令(SOL + USDC) @@ -88,10 +94,11 @@ 5. **Raydium AMM V4 交易**: 支持 Raydium AMM V4 (Automated Market Maker) 的交易操作 6. **Meteora DAMM V2 交易**: 支持 Meteora DAMM V2 (Dynamic AMM) 的交易操作 7. **多种 MEV 保护**: 支持 Jito、Temporal、FlashBlock、BlockRazor、Astralane、SpeedLanding 等服务 -8. **并发交易**: 同时使用多个 MEV 服务发送交易,最快的成功,其他失败 +8. **并发交易**: 所有已配置的 SWQoS 通道和默认 RPC 通道都会发出提交;首个成功只影响返回,较慢通道会继续提交 9. **统一交易接口**: 使用统一的交易协议枚举进行交易操作 10. **中间件系统**: 支持自定义指令中间件,可在交易执行前对指令进行修改、添加或移除 11. **共享基础设施**: 多钱包可共享同一套 RPC 与 SWQoS 客户端,降低资源占用 +12. **热路径 RPC 边界**: 交易执行使用调用方传入的 blockhash 或 durable nonce,不在热路径查询 blockhash、账户或余额 ## 📦 安装 @@ -108,14 +115,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.14" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.15" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "4.0.14" +sol-trade-sdk = "4.0.15" ``` ## 🛠️ 使用示例 diff --git a/src/client/mod.rs b/src/client/mod.rs index 40462ff..eff4330 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -50,6 +50,15 @@ fn validate_protocol_params(dex_type: DexType, params: &DexParamEnum) -> bool { } } +#[inline] +fn normalize_swqos_configs(rpc_url: &str, configs: &[SwqosConfig]) -> Vec { + let mut out = configs.to_vec(); + if !out.iter().any(|c| matches!(c.swqos_type(), SwqosType::Default)) { + out.push(SwqosConfig::Default(rpc_url.to_string())); + } + out +} + /// 按 mint 查找池地址(通用入口,根据 DEX 类型分发,仅 PumpSwap 等已实现的类型会走优化路径)。 /// /// * `dex_type`:PumpSwap 时先走 PDA 再回退 getProgramAccounts,其他类型返回未实现错误。 @@ -134,8 +143,9 @@ impl TradingInfrastructure { // Create SWQOS clients with blacklist checking(QUIC 握手可能较慢,单节点超时 15s) const SWQOS_CLIENT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(15); + let swqos_configs = normalize_swqos_configs(&config.rpc_url, &config.swqos_configs); let mut swqos_clients: Vec> = vec![]; - for swqos in &config.swqos_configs { + for swqos in &swqos_configs { if swqos.is_blacklisted() { if sdk_log::sdk_log_enabled() { warn!(target: "sol_trade_sdk", "⚠️ SWQOS {:?} is blacklisted, skipping", swqos.swqos_type()); @@ -752,6 +762,12 @@ impl TradingClient { Some(Arc::new(if cap < v.len() { v[..cap].to_vec() } else { v })); } } + if self.use_dedicated_sender_threads { + crate::trading::core::async_executor::warm_dedicated_sender_pool( + self.sender_thread_cores.as_ref().map(|v| v.as_slice()), + self.max_sender_concurrency, + ); + } self } @@ -1263,3 +1279,28 @@ impl TradingClient { Ok(signature.to_string()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::swqos::SwqosRegion; + + #[test] + fn normalize_swqos_configs_adds_default_rpc_route() { + let configs = vec![SwqosConfig::Jito("uuid".to_string(), SwqosRegion::Frankfurt, None)]; + let normalized = normalize_swqos_configs("https://rpc.example", &configs); + + assert_eq!(normalized.len(), 2); + assert!(normalized.iter().any(|c| matches!(c.swqos_type(), SwqosType::Jito))); + assert!(normalized.iter().any(|c| matches!(c.swqos_type(), SwqosType::Default))); + } + + #[test] + fn normalize_swqos_configs_does_not_duplicate_default_rpc_route() { + let configs = vec![SwqosConfig::Default("https://rpc.example".to_string())]; + let normalized = normalize_swqos_configs("https://rpc.example", &configs); + + assert_eq!(normalized.len(), 1); + assert!(matches!(normalized[0].swqos_type(), SwqosType::Default)); + } +} diff --git a/src/common/fast_timing.rs b/src/common/fast_timing.rs index 160fa01..cf5b6d8 100644 --- a/src/common/fast_timing.rs +++ b/src/common/fast_timing.rs @@ -156,8 +156,13 @@ mod tests { #[test] fn test_fast_now_overhead() { - // 测试调用开销 - let iterations = 10_000; + // This is a coarse regression guard, not a benchmark. On non-Linux targets + // the fast timer uses `Instant::elapsed()`, and CI/desktop scheduler jitter can + // move a single run above 100ns even when the code path is still sub-microsecond. + let iterations = 100_000; + for _ in 0..1_000 { + let _ = fast_now_nanos(); + } let start = Instant::now(); for _ in 0..iterations { @@ -171,8 +176,8 @@ mod tests { println!("Average fast_now_nanos() call: {}ns", avg_per_call); } - // 快速时间戳应该非常快(< 100ns per call) - assert!(avg_per_call < 100); + // Keep the hot-path timer safely sub-microsecond without making the test flaky. + assert!(avg_per_call < 500); } #[test] diff --git a/src/trading/core/async_executor.rs b/src/trading/core/async_executor.rs index 3a966ce..7d67a7b 100644 --- a/src/trading/core/async_executor.rs +++ b/src/trading/core/async_executor.rs @@ -147,10 +147,15 @@ async fn run_one_swqos_job(job: SwqosJob) { async fn swqos_worker_loop(queue: Arc>, notify: Arc) { loop { + let notified = notify.notified(); + tokio::pin!(notified); + notified.as_mut().enable(); + if let Some(job) = queue.pop() { + drop(notified); run_one_swqos_job(job).await; } else { - notify.notified().await; + notified.await; } } } @@ -232,6 +237,15 @@ fn ensure_dedicated_pool( (queue, notify) } +/// Pre-spawn dedicated sender threads during SDK initialization, avoiding first-submit thread +/// creation cost on the trading hot path. +pub fn warm_dedicated_sender_pool( + sender_thread_cores: Option<&[usize]>, + max_sender_concurrency: usize, +) { + let _ = ensure_dedicated_pool(sender_thread_cores, max_sender_concurrency); +} + fn ensure_dedicated_worker_count( queue: Arc>, notify: Arc,