From 7471bcba090afb20ea0b5ed0ecedcc35415b0a7b Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Mon, 8 Jun 2026 00:00:32 +0800 Subject: [PATCH] Release v4.0.19 --- Cargo.toml | 2 +- README.md | 27 ++++++++++- README_CN.md | 27 ++++++++++- docs/TRADING_PARAMETERS.md | 29 ++++++++++- docs/TRADING_PARAMETERS_CN.md | 29 ++++++++++- examples/simple_trading/src/main.rs | 49 ++++++++++++++++--- src/client/mod.rs | 74 +++++++++++++++++++++++++++++ src/lib.rs | 1 + 8 files changed, 222 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63da783..c1f66d9 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "4.0.18" +version = "4.0.19" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index 978dc69..784d9b1 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,8 @@ Optional builder methods: | `.wait_for_all_submits(true)` | In fast-submit mode, wait for all SWQoS lane responses and return all signatures. | | `.simulate(true)` | Build and simulate the transaction instead of sending it. | | `.grpc_recv_us(ts)` | Attach upstream receive timestamp for latency tracing. | -| `SimpleBuyParams::with_durable_nonce(...)` / `SimpleSellParams::with_durable_nonce(...)` | Use durable nonce instead of `recent_blockhash`. | +| `.durable_nonce(nonce_info)` | Use durable nonce and clear `recent_blockhash`. Recommended when you start from `SimpleBuyParams::new(...)` / `SimpleSellParams::new(...)`. | +| `SimpleBuyParams::with_durable_nonce(...)` / `SimpleSellParams::with_durable_nonce(...)` | Construct params directly with durable nonce instead of `recent_blockhash`. | | `SimpleSellParams::with_tip(false)` | Disable relay tips for sells. Buys use the gas fee strategy/tip settings. | `TradeBuyParams` and `TradeSellParams` remain available as advanced low-level APIs. See the dedicated [Trading Parameters Reference](docs/TRADING_PARAMETERS.md). @@ -323,7 +324,29 @@ let temporal_config = SwqosConfig::Temporal( - If no custom URL is provided (`None`), the system will use the default endpoint for the specified `SwqosRegion` - This allows for maximum flexibility while maintaining backward compatibility -When using multiple MEV services, you need to use `Durable Nonce`. You need to use the `fetch_nonce_info` function to get the latest `nonce` value, and use it as the `durable_nonce` when trading. +When using multiple MEV services, you need to use `Durable Nonce`. Fetch the latest nonce value and attach it to the high-level buy/sell params: + +```rust +use sol_trade_sdk::{fetch_nonce_info, AccountPolicy, BuyAmount, SimpleBuyParams}; + +let nonce_info = fetch_nonce_info(&client.infrastructure.rpc, nonce_account) + .await + .expect("nonce account must be initialized"); + +let buy_params = SimpleBuyParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + mint_pubkey, + BuyAmount::WithMaxInput { quote_amount: buy_sol_amount }, + DexParamEnum::PumpFun(pumpfun_params), + recent_blockhash, // will be cleared by `.durable_nonce(...)` + gas_fee_strategy.clone(), +) +.durable_nonce(nonce_info) +.account_policy(AccountPolicy::HotPathMinimal); + +client.buy_simple(buy_params).await?; +``` #### Astralane (Binary / Plain HTTP / QUIC) diff --git a/README_CN.md b/README_CN.md index c94d430..7de525a 100755 --- a/README_CN.md +++ b/README_CN.md @@ -258,7 +258,8 @@ client.buy_simple(buy_params).await?; | `.wait_for_all_submits(true)` | fast-submit 模式下等待所有 SWQoS 通道返回,并拿到全部签名。 | | `.simulate(true)` | 只构建并模拟交易,不真正发送。 | | `.grpc_recv_us(ts)` | 传入上游收到事件的微秒时间戳,用于延迟追踪。 | -| `SimpleBuyParams::with_durable_nonce(...)` / `SimpleSellParams::with_durable_nonce(...)` | 使用 durable nonce,不使用 `recent_blockhash`。 | +| `.durable_nonce(nonce_info)` | 使用 durable nonce,并清空 `recent_blockhash`。如果你从 `SimpleBuyParams::new(...)` / `SimpleSellParams::new(...)` 开始构造,推荐用这个。 | +| `SimpleBuyParams::with_durable_nonce(...)` / `SimpleSellParams::with_durable_nonce(...)` | 直接用 durable nonce 构造参数,不使用 `recent_blockhash`。 | | `SimpleSellParams::with_tip(false)` | 关闭卖出交易 relay tip。买入的 tip 使用 gas fee strategy 控制。 | `TradeBuyParams` 和 `TradeSellParams` 仍保留为高级低层接口。详细说明见 [交易参数参考手册](docs/TRADING_PARAMETERS_CN.md)。 @@ -322,7 +323,29 @@ let temporal_config = SwqosConfig::Temporal( - 如果没有提供自定义 URL(`None`),系统将使用指定 `SwqosRegion` 的默认端点 - 这提供了最大的灵活性,同时保持向后兼容性 -当使用多个MEV服务时,需要使用`Durable Nonce`。你需要使用`fetch_nonce_info`函数获取最新的`nonce`值,并在交易的时候将`durable_nonce`填入交易参数。 +当使用多个 MEV 服务时,需要使用 `Durable Nonce`。先获取最新 nonce,再挂到新的 buy/sell 参数上: + +```rust +use sol_trade_sdk::{fetch_nonce_info, AccountPolicy, BuyAmount, SimpleBuyParams}; + +let nonce_info = fetch_nonce_info(&client.infrastructure.rpc, nonce_account) + .await + .expect("nonce account must be initialized"); + +let buy_params = SimpleBuyParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + mint_pubkey, + BuyAmount::WithMaxInput { quote_amount: buy_sol_amount }, + DexParamEnum::PumpFun(pumpfun_params), + recent_blockhash, // 会被 `.durable_nonce(...)` 清空 + gas_fee_strategy.clone(), +) +.durable_nonce(nonce_info) +.account_policy(AccountPolicy::HotPathMinimal); + +client.buy_simple(buy_params).await?; +``` #### Astralane(Binary / Plain / QUIC) diff --git a/docs/TRADING_PARAMETERS.md b/docs/TRADING_PARAMETERS.md index a74bc7f..da4956b 100644 --- a/docs/TRADING_PARAMETERS.md +++ b/docs/TRADING_PARAMETERS.md @@ -30,7 +30,7 @@ Use `SimpleBuyParams` and `SimpleSellParams` for new integrations. They keep the | `address_lookup_table_account` | `Option` | ❌ | Optional ALT to reduce transaction size. | | `wait_tx_confirmed` | `bool` | ❌ | Whether to wait for chain confirmation before returning. Default is `false`. | | `wait_for_all_submits` | `bool` | ❌ | Fast-submit mode only: wait for every SWQoS lane response and return all signatures. | -| `durable_nonce` | `Option` | ❌ | Durable nonce info. Use `SimpleBuyParams::with_durable_nonce(...)`; do not combine with `recent_blockhash`. | +| `durable_nonce` | `Option` | ❌ | Durable nonce info. Use `.durable_nonce(nonce_info)` or `SimpleBuyParams::with_durable_nonce(...)`; do not combine with `recent_blockhash`. | | `simulate` | `bool` | ❌ | Build and simulate instead of submitting. Default is `false`. | | `grpc_recv_us` | `Option` | ❌ | Upstream receive timestamp in microseconds for latency tracing. | @@ -50,7 +50,7 @@ Use `SimpleBuyParams` and `SimpleSellParams` for new integrations. They keep the | `address_lookup_table_account` | `Option` | ❌ | Optional ALT to reduce transaction size. | | `wait_tx_confirmed` | `bool` | ❌ | Whether to wait for chain confirmation before returning. Default is `false`. | | `wait_for_all_submits` | `bool` | ❌ | Fast-submit mode only: wait for every SWQoS lane response and return all signatures. | -| `durable_nonce` | `Option` | ❌ | Durable nonce info. Use `SimpleSellParams::with_durable_nonce(...)`; do not combine with `recent_blockhash`. | +| `durable_nonce` | `Option` | ❌ | Durable nonce info. Use `.durable_nonce(nonce_info)` or `SimpleSellParams::with_durable_nonce(...)`; do not combine with `recent_blockhash`. | | `simulate` | `bool` | ❌ | Build and simulate instead of submitting. Default is `false`. | | `with_tip` | `bool` | ❌ | Whether sells include relay tips. Default is `true`; set with `.with_tip(false)`. | | `grpc_recv_us` | `Option` | ❌ | Upstream receive timestamp in microseconds for latency tracing. | @@ -74,6 +74,31 @@ Use `SimpleBuyParams` and `SimpleSellParams` for new integrations. They keep the | `CreateMissing` | Include ATA creation where possible. | Convenience matters more than smallest transaction size. | | `AssumePrepared` | Do not create or close token accounts; caller prepared everything. | Advanced deterministic flows. | +### Durable Nonce With Simple Params + +`fetch_nonce_info` and `DurableNonceInfo` are re-exported from the crate root: + +```rust +use sol_trade_sdk::{fetch_nonce_info, SimpleBuyParams}; + +let nonce_info = fetch_nonce_info(&client.infrastructure.rpc, nonce_account) + .await + .expect("nonce account must be initialized"); + +let buy_params = SimpleBuyParams::new( + dex_type, + pay_with, + mint, + amount, + extension_params, + recent_blockhash, + gas_fee_strategy, +) +.durable_nonce(nonce_info); +``` + +Calling `.durable_nonce(...)` clears `recent_blockhash`; nonce transactions use the nonce value as the transaction blockhash. + ## TradeBuyParams `TradeBuyParams` is the advanced low-level buy API. New integrations should prefer `SimpleBuyParams` unless they need direct control over individual ATA flags. diff --git a/docs/TRADING_PARAMETERS_CN.md b/docs/TRADING_PARAMETERS_CN.md index 2c0fd57..57c0e0a 100644 --- a/docs/TRADING_PARAMETERS_CN.md +++ b/docs/TRADING_PARAMETERS_CN.md @@ -30,7 +30,7 @@ | `address_lookup_table_account` | `Option` | ❌ | 可选 ALT,用于减少交易体积。 | | `wait_tx_confirmed` | `bool` | ❌ | 是否等链上确认后再返回。默认 `false`。 | | `wait_for_all_submits` | `bool` | ❌ | fast-submit 模式下,是否等待所有 SWQoS 通道返回并拿到全部签名。 | -| `durable_nonce` | `Option` | ❌ | durable nonce 信息。使用 `SimpleBuyParams::with_durable_nonce(...)` 设置,不要和 `recent_blockhash` 混用。 | +| `durable_nonce` | `Option` | ❌ | durable nonce 信息。使用 `.durable_nonce(nonce_info)` 或 `SimpleBuyParams::with_durable_nonce(...)` 设置,不要和 `recent_blockhash` 混用。 | | `simulate` | `bool` | ❌ | 只构建并模拟交易,不提交。默认 `false`。 | | `grpc_recv_us` | `Option` | ❌ | 上游收到事件的微秒时间戳,用于延迟追踪。 | @@ -50,7 +50,7 @@ | `address_lookup_table_account` | `Option` | ❌ | 可选 ALT,用于减少交易体积。 | | `wait_tx_confirmed` | `bool` | ❌ | 是否等链上确认后再返回。默认 `false`。 | | `wait_for_all_submits` | `bool` | ❌ | fast-submit 模式下,是否等待所有 SWQoS 通道返回并拿到全部签名。 | -| `durable_nonce` | `Option` | ❌ | durable nonce 信息。使用 `SimpleSellParams::with_durable_nonce(...)` 设置,不要和 `recent_blockhash` 混用。 | +| `durable_nonce` | `Option` | ❌ | durable nonce 信息。使用 `.durable_nonce(nonce_info)` 或 `SimpleSellParams::with_durable_nonce(...)` 设置,不要和 `recent_blockhash` 混用。 | | `simulate` | `bool` | ❌ | 只构建并模拟交易,不提交。默认 `false`。 | | `with_tip` | `bool` | ❌ | 卖出交易是否带 relay tip。默认 `true`,可通过 `.with_tip(false)` 关闭。 | | `grpc_recv_us` | `Option` | ❌ | 上游收到事件的微秒时间戳,用于延迟追踪。 | @@ -74,6 +74,31 @@ | `CreateMissing` | 尽量在交易内创建缺失 ATA。 | 更重视方便,不追求最小交易体积。 | | `AssumePrepared` | 不创建也不关闭 token account,调用方保证都已准备好。 | 高级确定性流程。 | +### Simple 参数使用 Durable Nonce + +`fetch_nonce_info` 和 `DurableNonceInfo` 已从 crate root 重新导出: + +```rust +use sol_trade_sdk::{fetch_nonce_info, SimpleBuyParams}; + +let nonce_info = fetch_nonce_info(&client.infrastructure.rpc, nonce_account) + .await + .expect("nonce account must be initialized"); + +let buy_params = SimpleBuyParams::new( + dex_type, + pay_with, + mint, + amount, + extension_params, + recent_blockhash, + gas_fee_strategy, +) +.durable_nonce(nonce_info); +``` + +调用 `.durable_nonce(...)` 会清空 `recent_blockhash`;nonce 交易会使用 nonce value 作为 transaction blockhash。 + ## TradeBuyParams `TradeBuyParams` 是高级低层买入 API。新接入建议优先使用 `SimpleBuyParams`,只有需要直接控制单个 ATA flag 时再使用它。 diff --git a/examples/simple_trading/src/main.rs b/examples/simple_trading/src/main.rs index 44b26d9..c1ecfa7 100644 --- a/examples/simple_trading/src/main.rs +++ b/examples/simple_trading/src/main.rs @@ -14,11 +14,11 @@ use sol_trade_sdk::{ core::params::{DexParamEnum, PumpFunParams}, factory::DexType, }, - AccountPolicy, BuyAmount, SellAmount, SimpleBuyParams, SimpleSellParams, SolanaTrade, - TradeTokenType, + AccountPolicy, BuyAmount, DurableNonceInfo, SellAmount, SimpleBuyParams, SimpleSellParams, + SolanaTrade, TradeTokenType, }; use solana_commitment_config::CommitmentConfig; -use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer}; +use solana_sdk::{hash::Hash, pubkey::Pubkey, signature::Keypair, signer::Signer}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -83,7 +83,41 @@ async fn main() -> Result<(), Box> { // client.buy_simple(buy_params).await?; let _ = buy_params; + // Durable nonce is supported by the same high-level params. In production, + // fetch it with `sol_trade_sdk::fetch_nonce_info(...)` immediately before + // building the transaction. + let nonce_buy_params = SimpleBuyParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + mint, + BuyAmount::WithMaxInput { quote_amount: 100_000 }, + pumpfun_params.clone(), + recent_blockhash, + gas_fee_strategy.clone(), + ) + .durable_nonce(DurableNonceInfo { + nonce_account: Some(Pubkey::new_unique()), + current_nonce: Some(Hash::new_unique()), + }) + .account_policy(AccountPolicy::HotPathMinimal); + let _ = nonce_buy_params; + let sell_params = SimpleSellParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + mint, + SellAmount::ExactInput(1_000_000), + pumpfun_params.clone(), + client.infrastructure.rpc.get_latest_blockhash().await?, + gas_fee_strategy.clone(), + ) + .slippage_basis_points(300) + .account_policy(AccountPolicy::HotPathMinimal); + + // client.sell_simple(sell_params).await?; + let _ = sell_params; + + let nonce_sell_params = SimpleSellParams::new( DexType::PumpFun, TradeTokenType::SOL, mint, @@ -92,11 +126,12 @@ async fn main() -> Result<(), Box> { client.infrastructure.rpc.get_latest_blockhash().await?, gas_fee_strategy, ) - .slippage_basis_points(300) + .durable_nonce(DurableNonceInfo { + nonce_account: Some(Pubkey::new_unique()), + current_nonce: Some(Hash::new_unique()), + }) .account_policy(AccountPolicy::HotPathMinimal); - - // client.sell_simple(sell_params).await?; - let _ = sell_params; + let _ = nonce_sell_params; println!("Built simple buy/sell params for payer {}", client.payer.pubkey()); Ok(()) diff --git a/src/client/mod.rs b/src/client/mod.rs index d1c76e9..bcdc2f2 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -335,6 +335,16 @@ impl SimpleBuyParams { self } + /// Use a durable nonce instead of the recent blockhash passed to [`Self::new`]. + /// + /// This clears `recent_blockhash` because nonce transactions must use the + /// nonce value as their transaction blockhash. + pub fn durable_nonce(mut self, value: DurableNonceInfo) -> Self { + self.durable_nonce = Some(value); + self.recent_blockhash = None; + self + } + /// Wait for confirmation before returning. pub fn wait_tx_confirmed(mut self, value: bool) -> Self { self.wait_tx_confirmed = value; @@ -444,6 +454,16 @@ impl SimpleSellParams { self } + /// Use a durable nonce instead of the recent blockhash passed to [`Self::new`]. + /// + /// This clears `recent_blockhash` because nonce transactions must use the + /// nonce value as their transaction blockhash. + pub fn durable_nonce(mut self, value: DurableNonceInfo) -> Self { + self.durable_nonce = Some(value); + self.recent_blockhash = None; + self + } + /// Wait for confirmation before returning. pub fn wait_tx_confirmed(mut self, value: bool) -> Self { self.wait_tx_confirmed = value; @@ -1910,6 +1930,33 @@ mod tests { assert!(!low.wait_tx_confirmed); } + #[test] + fn simple_buy_builder_can_use_durable_nonce() { + let nonce_account = Pubkey::new_unique(); + let nonce_hash = Hash::new_unique(); + let durable_nonce = DurableNonceInfo { + nonce_account: Some(nonce_account), + current_nonce: Some(nonce_hash), + }; + + let simple = SimpleBuyParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + Pubkey::new_unique(), + BuyAmount::ExactInput(10_000), + dummy_pumpfun_params(), + Hash::new_unique(), + GasFeeStrategy::new(), + ) + .durable_nonce(durable_nonce.clone()); + + let low: TradeBuyParams = simple.into(); + + assert!(low.recent_blockhash.is_none()); + assert_eq!(low.durable_nonce.as_ref().and_then(|n| n.nonce_account), Some(nonce_account)); + assert_eq!(low.durable_nonce.as_ref().and_then(|n| n.current_nonce), Some(nonce_hash)); + } + #[test] fn simple_sell_auto_creates_non_sol_output_ata() { let simple = SimpleSellParams { @@ -1939,4 +1986,31 @@ mod tests { assert!(!low.close_output_token_ata); assert!(!low.close_mint_token_ata); } + + #[test] + fn simple_sell_builder_can_use_durable_nonce() { + let nonce_account = Pubkey::new_unique(); + let nonce_hash = Hash::new_unique(); + let durable_nonce = DurableNonceInfo { + nonce_account: Some(nonce_account), + current_nonce: Some(nonce_hash), + }; + + let simple = SimpleSellParams::new( + DexType::PumpFun, + TradeTokenType::SOL, + Pubkey::new_unique(), + SellAmount::ExactInput(50_000), + dummy_pumpfun_params(), + Hash::new_unique(), + GasFeeStrategy::new(), + ) + .durable_nonce(durable_nonce.clone()); + + let low: TradeSellParams = simple.into(); + + assert!(low.recent_blockhash.is_none()); + assert_eq!(low.durable_nonce.as_ref().and_then(|n| n.nonce_account), Some(nonce_account)); + assert_eq!(low.durable_nonce.as_ref().and_then(|n| n.current_nonce), Some(nonce_hash)); + } } diff --git a/src/lib.rs b/src/lib.rs index d9d5b02..3e2485b 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod swqos; pub mod trading; pub mod utils; +pub use crate::common::nonce_cache::{fetch_nonce_info, DurableNonceInfo}; // Re-export for SwqosConfig (Node1/BlockRazor transport; Astralane submission mode) pub use crate::swqos::{AstralaneTransport, SwqosTransport}; pub use client::{