From 5449d515919dfc12c5bcf5bc676c516c4b96492d Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Mon, 25 May 2026 22:01:15 +0800 Subject: [PATCH] Release sol-trade-sdk 4.0.14 --- Cargo.toml | 2 +- README.md | 13 +++++---- README_CN.md | 14 ++++++---- src/instruction/pumpfun.rs | 37 ++++++++++++++++++++---- src/trading/core/async_executor.rs | 7 +++-- src/trading/core/params/pumpfun.rs | 45 +++++++++++++++--------------- 6 files changed, 74 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f945b35..e30e971 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "4.0.13" +version = "4.0.14" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index 528097d..501adc0 100644 --- a/README.md +++ b/README.md @@ -108,14 +108,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.13" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.14" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "4.0.13" +sol-trade-sdk = "4.0.14" ``` ## 🛠️ Usage Examples @@ -367,7 +367,7 @@ PumpFun has two instruction sets for bonding-curve trading: The SDK-side builder is version-neutral: callers use the normal buy/sell flow, and `quote_mint` selects the correct on-chain discriminator and account layout internally. There is no user-facing V2 switch required. -**Default: V1**. When `quote_mint` is `Pubkey::default()`, the SDK uses V1 instructions which produce smaller transactions that fit within the 1232-byte `PACKET_DATA_SIZE` limit without requiring an Address Lookup Table. Passing an explicit quote mint selects V2: WSOL/native SOL for SOL-paired V2, or USDC for USDC-paired pools. +**Default: V1**. When `quote_mint` is `Pubkey::default()` or the Solscan SOL sentinel (`So11111111111111111111111111111111111111111`), the SDK uses V1 instructions which produce smaller transactions that fit within the 1232-byte `PACKET_DATA_SIZE` limit without requiring an Address Lookup Table. Passing `WSOL_TOKEN_ACCOUNT` selects SOL V2; passing USDC selects USDC V2. **Key changes in v2 instructions:** - `quote_mint` parameter — pass wrapped SOL for SOL-paired, or USDC mint for USDC-paired @@ -378,12 +378,12 @@ The SDK-side builder is version-neutral: callers use the normal buy/sell flow, a **Pass `quote_mint` into `PumpFunParams::from_trade`**: -When using event/parser data, pass the event's `quote_mint` right after `mint`. `Pubkey::default()` means the legacy SOL layout; explicit native SOL or WSOL means the SOL V2 layout; USDC means the USDC V2 layout. +When using event/parser data, pass the event's `quote_mint` right after `mint`. `Pubkey::default()` and Solscan SOL (`So11111111111111111111111111111111111111111`) mean the legacy SOL layout; `WSOL_TOKEN_ACCOUNT` means SOL V2; USDC means USDC V2. ```rust // quote_mint is not a PDA. It is the quote SPL mint carried by parser/gRPC events: -// - Legacy SOL pool: Pubkey::default() from log events -// - SOL V2 pool: native SOL or WSOL from parser data +// - Legacy SOL pool: Pubkey::default() or Solscan SOL from parser data +// - SOL V2 pool: WSOL_TOKEN_ACCOUNT // - USDC V2 pool: USDC mint let quote_mint = e.quote_mint; let params = PumpFunParams::from_trade( @@ -407,6 +407,7 @@ let params = PumpFunParams::from_trade( For USDC-paired coins, pass `USDC_TOKEN_ACCOUNT` as the buy `input_mint` and sell `output_mint`; SOL/WSOL is only valid for SOL-paired PumpFun curves. When consuming parser events, map `quoteMint`, `virtualQuoteReserves`, and `realQuoteReserves` into `PumpFunParams::from_trade(...)`; USDC pools use `4_292_000_000` as the initial virtual quote reserve. +For legacy SOL events where `quote_mint` is `Pubkey::default()` or Solscan SOL, use `virtual_sol_reserves` / `real_sol_reserves` when the quote-reserve fields are absent or zero. > **Note**: V2 transactions with ATA creation + durable nonce may exceed `PACKET_DATA_SIZE`. Enable an Address Lookup Table (`address_lookup_table_account`) when using V2. diff --git a/README_CN.md b/README_CN.md index c68cfc3..4985d6d 100755 --- a/README_CN.md +++ b/README_CN.md @@ -108,14 +108,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.13" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "4.0.14" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "4.0.13" +sol-trade-sdk = "4.0.14" ``` ## 🛠️ 使用示例 @@ -374,10 +374,11 @@ SDK 侧调用入口保持统一:正常使用 `buy` / `sell` 流程即可,SDK **使用方式:** -把事件里的 `quote_mint` 传给 `PumpFunParams::from_trade`。`quote_mint` 不是 PDA,它就是 quote SPL mint;`Pubkey::default()` 表示旧版 SOL 布局,显式 native SOL/WSOL 表示 SOL V2,USDC 表示 USDC V2: +把事件里的 `quote_mint` 传给 `PumpFunParams::from_trade`。`quote_mint` 不是 PDA,它就是 quote SPL mint;`Pubkey::default()` 和 Solscan SOL(`So11111111111111111111111111111111111111111`)表示旧版 SOL 布局,`WSOL_TOKEN_ACCOUNT` 表示 SOL V2,USDC 表示 USDC V2: ```rust -// SOL 池:log 事件里通常是 Pubkey::default(),parser 数据里可能是 native SOL/WSOL +// legacy SOL 池:log 事件里可能是 Pubkey::default(),parser 数据里是 Solscan SOL sentinel +// SOL V2 池:WSOL_TOKEN_ACCOUNT // USDC 池:就是 USDC mint let quote_mint = e.quote_mint; @@ -406,11 +407,12 @@ client.sell(sell_params).await?; USDC 配对币必须用 USDC 买入、卖出也结算为 USDC;SOL/WSOL 只适用于 SOL 配对的 PumpFun 曲线。SDK 会在提交前拒绝 USDC quote 池的 SOL 输入,避免链上 6063 失败。 消费 parser 事件时,需要把 `quoteMint`、`virtualQuoteReserves`、`realQuoteReserves` 传进 `PumpFunParams::from_trade(...)`;USDC 池初始虚拟 quote reserve 是 `4_292_000_000`。 +legacy SOL 事件里如果 `quote_mint` 是默认值或 Solscan SOL,并且 quote reserve 字段缺失/为 0,应回退使用 `virtual_sol_reserves` / `real_sol_reserves`。 | quote_mint | 实际使用的指令 | 说明 | |-----------|---------|------| -| 未设置(默认) | 旧版 `buy`/`sell`/`buy_exact_sol_in` | 向后兼容,仅 SOL | -| `WSOL_TOKEN_ACCOUNT` / native SOL | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | SOL 配对,统一布局 | +| 未设置(默认)/ `SOL_TOKEN_ACCOUNT` (`So111...11111`) | 旧版 `buy`/`sell`/`buy_exact_sol_in` | 向后兼容,仅 SOL | +| `WSOL_TOKEN_ACCOUNT` (`So111...11112`) | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | SOL 配对,统一布局 | | `USDC_TOKEN_ACCOUNT` | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | USDC 配对(必须使用 v2) | ## 🛡️ MEV 保护服务 diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index 90a4611..0591ca4 100644 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -1,8 +1,8 @@ //! Pump.fun bonding-curve swap ix assembly ([`SwapParams`](crate::trading::core::params::SwapParams)). //! //! The SDK selects the legacy or V2 on-chain layout from `PumpFunParams.quote_mint`. -//! `Pubkey::default()` keeps the smaller legacy SOL layout; explicit WSOL/native SOL -//! or USDC uses the V2 27/26-account unified metas. +//! `Pubkey::default()` and the Solscan SOL sentinel keep the smaller legacy SOL layout; +//! explicit WSOL or USDC uses the V2 27/26-account unified metas. //! Default (`false`) keeps the smaller legacy SOL-paired instruction layout for latency. use crate::{ @@ -49,7 +49,8 @@ fn effective_pump_mint_token_program(protocol_params: &PumpFunParams) -> Pubkey } /// Resolve quote mint and its token program from PumpFunParams. -/// `Pubkey::default()` means legacy SOL-paired → use WSOL mint for V2 instructions. +/// `Pubkey::default()` / `SOL_TOKEN_ACCOUNT` means legacy SOL-paired; use WSOL mint when a +/// downstream V2 helper needs a concrete SPL quote mint. #[inline] fn effective_quote_mint_and_token_program(protocol_params: &PumpFunParams) -> (Pubkey, Pubkey) { let curve_quote_mint = protocol_params.bonding_curve.effective_quote_mint(); @@ -126,7 +127,9 @@ fn should_use_v2_layout(params: &SwapParams) -> Result { .downcast_ref::() .ok_or_else(|| anyhow!("Invalid protocol params for PumpFun"))?; let (quote_mint, _) = effective_quote_mint_and_token_program(protocol_params); - Ok(protocol_params.quote_mint != Pubkey::default() || !is_sol_quote_mint("e_mint)) + let explicit_v2_quote = protocol_params.quote_mint != Pubkey::default() + && protocol_params.quote_mint != crate::constants::SOL_TOKEN_ACCOUNT; + Ok(explicit_v2_quote || !is_sol_quote_mint("e_mint)) } #[inline] @@ -1111,7 +1114,7 @@ mod tests { } #[test] - fn pumpfun_native_sol_quote_mint_normalizes_to_wsol() { + fn pumpfun_solscan_sol_quote_mint_keeps_legacy_layout() { let mint = pump_mint(); let params = PumpFunParams::from_trade( Pubkey::default(), @@ -1131,10 +1134,32 @@ mod tests { Some(false), ); - assert_eq!(params.quote_mint, crate::constants::WSOL_TOKEN_ACCOUNT); + assert_eq!(params.quote_mint, Pubkey::default()); assert_eq!(params.bonding_curve.quote_mint, crate::constants::WSOL_TOKEN_ACCOUNT); } + #[test] + fn pumpfun_with_solscan_sol_quote_mint_selects_v1() { + let mut params = swap_params_for_buy(pump_mint(), TOKEN_PROGRAM); + params.create_output_mint_ata = false; + if let DexParamEnum::PumpFun(protocol_params) = &mut params.protocol_params { + *protocol_params = + protocol_params.clone().with_quote_mint(crate::constants::SOL_TOKEN_ACCOUNT); + assert_eq!(protocol_params.quote_mint, Pubkey::default()); + assert_eq!( + protocol_params.bonding_curve.quote_mint, + crate::constants::WSOL_TOKEN_ACCOUNT + ); + } + + let ix = build_buy(¶ms).unwrap().pop().unwrap(); + assert_eq!( + &ix.data[..8], + crate::instruction::utils::pumpfun::BUY_EXACT_SOL_IN_DISCRIMINATOR + ); + assert_eq!(ix.accounts.len(), 18); + } + #[test] fn pumpfun_v2_usdc_buy_rejects_sol_input() { let mut params = swap_params_for_buy(pump_mint(), TOKEN_PROGRAM); diff --git a/src/trading/core/async_executor.rs b/src/trading/core/async_executor.rs index 1b86325..af3f4bb 100644 --- a/src/trading/core/async_executor.rs +++ b/src/trading/core/async_executor.rs @@ -46,6 +46,7 @@ use crate::{ const SWQOS_POOL_WORKERS: usize = 18; const SWQOS_QUEUE_CAP: usize = 128; const SWQOS_DEDICATED_DEFAULT_THREADS: usize = 18; +const FAST_SUBMIT_RESULT_TIMEOUT: Duration = Duration::from_secs(5); /// Shared across all jobs in one batch; built once, cloned as single Arc per job (minimal hot-path clone). struct SwqosSharedContext { @@ -473,7 +474,7 @@ impl ResultCollector { /// Fast submit mode for callers that do not wait for on-chain confirmation. /// Return as soon as one route accepts, a landed failure consumes the nonce, all routes finish, - /// or a short submit window expires. Slow HTTP routes continue in worker tasks but no longer + /// or the submit result window expires. Slow HTTP routes continue in worker tasks but no longer /// block post-buy monitoring / sell scheduling. async fn wait_for_first_submitted( &self, @@ -734,10 +735,10 @@ pub async fn execute_parallel( // All jobs enqueued (no spawn on hot path) if !wait_transaction_confirmed { - let ret = collector.wait_for_first_submitted(Duration::from_millis(500)).await.unwrap_or(( + let ret = collector.wait_for_first_submitted(FAST_SUBMIT_RESULT_TIMEOUT).await.unwrap_or(( false, vec![], - Some(anyhow!("No SWQOS result within fast submit window")), + Some(anyhow!("No SWQOS result within submit result window")), vec![], )); let (success, signatures, last_error, submit_timings) = ret; diff --git a/src/trading/core/params/pumpfun.rs b/src/trading/core/params/pumpfun.rs index b9412da..0127741 100644 --- a/src/trading/core/params/pumpfun.rs +++ b/src/trading/core/params/pumpfun.rs @@ -15,8 +15,8 @@ use std::sync::Arc; /// ix 组装与链下询价见 [`Self::effective_creator_for_trade`]、[`crate::instruction::utils::pumpfun::resolve_creator_vault_for_ix_with_fee_sharing`]。 /// /// **V2 instructions**: The SDK selects the layout automatically from `quote_mint`. -/// Leave `quote_mint` default for legacy SOL layout, pass WSOL/native SOL for SOL V2, -/// or pass USDC for USDC-paired coins. +/// Leave `quote_mint` default, or pass the Solscan SOL sentinel (`SOL_TOKEN_ACCOUNT`), for +/// legacy SOL layout. Pass `WSOL_TOKEN_ACCOUNT` for SOL V2, or USDC for USDC-paired coins. #[derive(Clone)] pub struct PumpFunParams { pub bonding_curve: Arc, @@ -40,12 +40,22 @@ pub struct PumpFunParams { /// Fee recipient for buy/sell account #2. Set from sol-parser-sdk (`tradeEvent.feeRecipient` / 同笔 create_v2+buy 回填的 `observed_fee_recipient`);热路径不查 RPC。 /// `Pubkey::default()` 时只能使用 SDK 静态 fallback,可能落后于主网 Global;交易热路径应优先传入 gRPC / parser 观测值。 pub fee_recipient: Pubkey, - /// Quote mint for v2 instructions (default: `So11111111111111111111111111111111111111112` for SOL-paired). + /// Quote mint layout selector. Default and `SOL_TOKEN_ACCOUNT` use legacy SOL layout. + /// `WSOL_TOKEN_ACCOUNT` selects SOL v2; USDC selects USDC v2. /// For USDC-paired coins, set to `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`. pub quote_mint: Pubkey, } impl PumpFunParams { + #[inline] + fn quote_mint_for_layout(quote_mint: Pubkey) -> Pubkey { + if quote_mint == Pubkey::default() || quote_mint == crate::constants::SOL_TOKEN_ACCOUNT { + Pubkey::default() + } else { + BondingCurveAccount::normalize_quote_mint(quote_mint) + } + } + pub fn immediate_sell( creator_vault: Pubkey, token_program: Pubkey, @@ -151,17 +161,13 @@ impl PumpFunParams { close_token_account_when_sell: close_token_account_when_sell, token_program: token_program, fee_recipient, - quote_mint: if quote_mint == Pubkey::default() { - Pubkey::default() - } else { - effective_quote_mint - }, + quote_mint: Self::quote_mint_for_layout(quote_mint), } } /// Build PumpFun params from event/parser data. Pass `quote_mint` from the event: - /// `Pubkey::default()` for legacy SOL layout, `WSOL_TOKEN_ACCOUNT`/native SOL for SOL V2, - /// and `USDC_TOKEN_ACCOUNT` for USDC V2. + /// `Pubkey::default()` / `SOL_TOKEN_ACCOUNT` for legacy SOL layout, + /// `WSOL_TOKEN_ACCOUNT` for SOL V2, and `USDC_TOKEN_ACCOUNT` for USDC V2. /// Also pass `is_cashback_coin` from the event so sells include the correct remaining accounts. /// /// `mayhem_mode`: @@ -218,11 +224,7 @@ impl PumpFunParams { close_token_account_when_sell: close_token_account_when_sell, token_program: token_program, fee_recipient, - quote_mint: if quote_mint == Pubkey::default() { - Pubkey::default() - } else { - effective_quote_mint - }, + quote_mint: Self::quote_mint_for_layout(quote_mint), } } @@ -317,7 +319,7 @@ impl PumpFunParams { close_token_account_when_sell: None, token_program: mint_account.owner, fee_recipient: Pubkey::default(), - quote_mint, + quote_mint: Self::quote_mint_for_layout(quote_mint), }) } @@ -357,15 +359,14 @@ impl PumpFunParams { } /// Sets `quote_mint`. The instruction builder derives V1/V2 layout from this value. - /// For SOL-paired coins, pass `WSOL_TOKEN_ACCOUNT` or leave default. + /// For legacy SOL-paired coins, pass `SOL_TOKEN_ACCOUNT` or leave default. + /// Pass `WSOL_TOKEN_ACCOUNT` only when you intentionally want SOL v2 layout. #[inline] pub fn with_quote_mint(mut self, quote_mint: Pubkey) -> Self { let effective_quote_mint = BondingCurveAccount::normalize_quote_mint(quote_mint); - self.quote_mint = - if quote_mint == Pubkey::default() { Pubkey::default() } else { effective_quote_mint }; - if let Some(curve) = Arc::get_mut(&mut self.bonding_curve) { - *curve = curve.clone().with_quote_mint(effective_quote_mint); - } + self.quote_mint = Self::quote_mint_for_layout(quote_mint); + let curve = Arc::make_mut(&mut self.bonding_curve); + *curve = curve.clone().with_quote_mint(effective_quote_mint); self }