Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0897b53d3 | ||
|
|
e67a21df58 | ||
|
|
147c6068d1 | ||
|
|
6ce8ee582e | ||
|
|
bcc6860bc3 | ||
|
|
1b7e3781c7 |
+1
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "sol-trade-sdk"
|
name = "sol-trade-sdk"
|
||||||
version = "3.5.0"
|
version = "3.5.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = [
|
authors = [
|
||||||
"William <byteblock6@gmail.com>",
|
"William <byteblock6@gmail.com>",
|
||||||
@@ -19,8 +19,6 @@ members = [
|
|||||||
"examples/trading_client",
|
"examples/trading_client",
|
||||||
"examples/shared_infrastructure",
|
"examples/shared_infrastructure",
|
||||||
"examples/middleware_system",
|
"examples/middleware_system",
|
||||||
"examples/pumpfun_copy_trading",
|
|
||||||
"examples/pumpfun_sniper_trading",
|
|
||||||
"examples/pumpswap_trading",
|
"examples/pumpswap_trading",
|
||||||
"examples/bonk_sniper_trading",
|
"examples/bonk_sniper_trading",
|
||||||
"examples/bonk_copy_trading",
|
"examples/bonk_copy_trading",
|
||||||
|
|||||||
@@ -60,6 +60,16 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🆕 What's new in 3.5.2
|
||||||
|
|
||||||
|
- **SWQoS submit latency**: Sync serialize + buffer-pool hot path; `format!` body for single/batch submit (Bloxroute); avoid status clone in confirmation polling.
|
||||||
|
- **First-submit & 5-min idle**: Immediate first ping + 30s keepalive; `pool_max_idle_per_host=4` and `pool_idle_timeout=300s` (BlockRazor, Temporal, Node1, Astralane, Stellium) so submit reuses the same connection; ping consumes response body for connection reuse.
|
||||||
|
- **Docs**: All SWQoS comments translated to English.
|
||||||
|
|
||||||
|
## 🆕 What's new in 3.5.1
|
||||||
|
|
||||||
|
- **SWQoS / executor**: Updates to common SWQoS logic and trading executor.
|
||||||
|
|
||||||
## 🆕 What's new in 3.5.0
|
## 🆕 What's new in 3.5.0
|
||||||
|
|
||||||
- **Performance**: Hot-path timing only when logging; reduced clones (`execute_parallel` takes `&[Arc<SwqosClient>]`); shared HTTP client constants for SWQoS.
|
- **Performance**: Hot-path timing only when logging; reduced clones (`execute_parallel` takes `&[Arc<SwqosClient>]`); shared HTTP client constants for SWQoS.
|
||||||
@@ -97,14 +107,14 @@ Add the dependency to your `Cargo.toml`:
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Add to your Cargo.toml
|
# Add to your Cargo.toml
|
||||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.5.0" }
|
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.5.2" }
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use crates.io
|
### Use crates.io
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# Add to your Cargo.toml
|
# Add to your Cargo.toml
|
||||||
sol-trade-sdk = "3.5.0"
|
sol-trade-sdk = "3.5.2"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🛠️ Usage Examples
|
## 🛠️ Usage Examples
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
# 添加到您的 Cargo.toml
|
# 添加到您的 Cargo.toml
|
||||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.5.0" }
|
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.5.2" }
|
||||||
```
|
```
|
||||||
|
|
||||||
### 使用 crates.io
|
### 使用 crates.io
|
||||||
|
|||||||
+39
-16
@@ -89,41 +89,64 @@ pub async fn poll_transaction_confirmation(
|
|||||||
txt_sig: Signature,
|
txt_sig: Signature,
|
||||||
wait_confirmation: bool,
|
wait_confirmation: bool,
|
||||||
) -> Result<Signature> {
|
) -> Result<Signature> {
|
||||||
// If no confirmation needed, return signature immediately
|
poll_any_transaction_confirmation(rpc, &[txt_sig], wait_confirmation).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll multiple signatures in parallel (one RPC call per poll) and return the first one that confirms.
|
||||||
|
/// When transactions are submitted to multiple SWQOS channels, each channel produces a different
|
||||||
|
/// signature. Only one will land on-chain, so we must check all of them.
|
||||||
|
pub async fn poll_any_transaction_confirmation(
|
||||||
|
rpc: &SolanaRpcClient,
|
||||||
|
signatures: &[Signature],
|
||||||
|
wait_confirmation: bool,
|
||||||
|
) -> Result<Signature> {
|
||||||
|
if signatures.is_empty() {
|
||||||
|
return Err(anyhow::anyhow!("No signatures to confirm"));
|
||||||
|
}
|
||||||
|
// If no confirmation needed, return first signature immediately
|
||||||
if !wait_confirmation {
|
if !wait_confirmation {
|
||||||
return Ok(txt_sig);
|
return Ok(signatures[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeout: Duration = Duration::from_secs(15); // 15s to avoid timeout under network congestion
|
let timeout: Duration = Duration::from_secs(15);
|
||||||
let interval: Duration = Duration::from_millis(1000);
|
let interval: Duration = Duration::from_millis(1000);
|
||||||
let start: Instant = Instant::now();
|
let start: Instant = Instant::now();
|
||||||
let mut poll_count = 0u32;
|
let mut poll_count = 0u32;
|
||||||
|
// Track which signature landed (confirmed or failed on-chain)
|
||||||
|
let mut landed_sig: Option<Signature> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if start.elapsed() >= timeout {
|
if start.elapsed() >= timeout {
|
||||||
return Err(anyhow::anyhow!("Transaction {}'s confirmation timed out", txt_sig));
|
return Err(anyhow::anyhow!("Transaction confirmation timed out after {}s ({} signatures polled)", timeout.as_secs(), signatures.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
poll_count += 1;
|
poll_count += 1;
|
||||||
|
|
||||||
let status = rpc.get_signature_statuses(&[txt_sig]).await?;
|
let status = rpc.get_signature_statuses(signatures).await?;
|
||||||
let first = status.value.get(0).and_then(|o| o.as_ref());
|
// Check all signatures for any that confirmed successfully
|
||||||
match first {
|
for (i, maybe_status) in status.value.iter().enumerate() {
|
||||||
Some(s) => {
|
if let Some(s) = maybe_status {
|
||||||
if s.err.is_none()
|
if s.err.is_none()
|
||||||
&& (s.confirmation_status == Some(TransactionConfirmationStatus::Confirmed)
|
&& (s.confirmation_status == Some(TransactionConfirmationStatus::Confirmed)
|
||||||
|| s.confirmation_status == Some(TransactionConfirmationStatus::Finalized))
|
|| s.confirmation_status == Some(TransactionConfirmationStatus::Finalized))
|
||||||
{
|
{
|
||||||
return Ok(txt_sig);
|
return Ok(signatures[i]);
|
||||||
|
}
|
||||||
|
// Track the first signature that landed on-chain (even if errored)
|
||||||
|
if landed_sig.is_none() {
|
||||||
|
landed_sig = Some(signatures[i]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
|
||||||
sleep(interval).await;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let should_get_transaction = first.map(|s| s.err.is_some()).unwrap_or(false) || poll_count >= 10;
|
// If no signature has any status yet, keep waiting
|
||||||
|
if landed_sig.is_none() {
|
||||||
|
sleep(interval).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let landed = landed_sig.unwrap();
|
||||||
|
let should_get_transaction = poll_count >= 10;
|
||||||
|
|
||||||
if !should_get_transaction {
|
if !should_get_transaction {
|
||||||
sleep(interval).await;
|
sleep(interval).await;
|
||||||
@@ -132,7 +155,7 @@ pub async fn poll_transaction_confirmation(
|
|||||||
|
|
||||||
let tx_details = match rpc
|
let tx_details = match rpc
|
||||||
.get_transaction_with_config(
|
.get_transaction_with_config(
|
||||||
&txt_sig,
|
&landed,
|
||||||
RpcTransactionConfig {
|
RpcTransactionConfig {
|
||||||
encoding: Some(UiTransactionEncoding::JsonParsed),
|
encoding: Some(UiTransactionEncoding::JsonParsed),
|
||||||
max_supported_transaction_version: Some(0),
|
max_supported_transaction_version: Some(0),
|
||||||
@@ -155,7 +178,7 @@ pub async fn poll_transaction_confirmation(
|
|||||||
} else {
|
} else {
|
||||||
let meta = meta.unwrap();
|
let meta = meta.unwrap();
|
||||||
if meta.err.is_none() {
|
if meta.err.is_none() {
|
||||||
return Ok(txt_sig);
|
return Ok(landed);
|
||||||
} else {
|
} else {
|
||||||
// Extract error message from log_messages
|
// Extract error message from log_messages
|
||||||
let mut error_msg = String::new();
|
let mut error_msg = String::new();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use tracing::{info, trace, warn};
|
|||||||
use crate::{
|
use crate::{
|
||||||
common::{nonce_cache::DurableNonceInfo, GasFeeStrategy, SolanaRpcClient},
|
common::{nonce_cache::DurableNonceInfo, GasFeeStrategy, SolanaRpcClient},
|
||||||
perf::syscall_bypass::SystemCallBypassManager,
|
perf::syscall_bypass::SystemCallBypassManager,
|
||||||
swqos::common::poll_transaction_confirmation,
|
swqos::common::poll_any_transaction_confirmation,
|
||||||
trading::core::{
|
trading::core::{
|
||||||
async_executor::execute_parallel,
|
async_executor::execute_parallel,
|
||||||
execution::{InstructionProcessor, Prefetch},
|
execution::{InstructionProcessor, Prefetch},
|
||||||
@@ -156,10 +156,12 @@ impl TradeExecutor for GenericTradeExecutor {
|
|||||||
),
|
),
|
||||||
Err(e) => (false, vec![], Some(anyhow::anyhow!("{}", e))),
|
Err(e) => (false, vec![], Some(anyhow::anyhow!("{}", e))),
|
||||||
};
|
};
|
||||||
let first_sig = sigs.first().copied();
|
let confirm_result = if let Some(rpc) = params.rpc.as_ref() {
|
||||||
let confirm_result = if let (Some(rpc), Some(sig)) = (params.rpc.as_ref(), first_sig) {
|
if sigs.is_empty() {
|
||||||
|
(ok, sigs, err)
|
||||||
|
} else {
|
||||||
let confirm_start = (params.log_enabled && crate::common::sdk_log::sdk_log_enabled()).then(Instant::now);
|
let confirm_start = (params.log_enabled && crate::common::sdk_log::sdk_log_enabled()).then(Instant::now);
|
||||||
let poll_res = poll_transaction_confirmation(rpc, sig, true).await;
|
let poll_res = poll_any_transaction_confirmation(rpc, &sigs, true).await;
|
||||||
let confirm_elapsed = confirm_start.map(|s| s.elapsed()).unwrap_or(Duration::ZERO);
|
let confirm_elapsed = confirm_start.map(|s| s.elapsed()).unwrap_or(Duration::ZERO);
|
||||||
if params.log_enabled && crate::common::sdk_log::sdk_log_enabled() {
|
if params.log_enabled && crate::common::sdk_log::sdk_log_enabled() {
|
||||||
let dir = if is_buy { "Buy" } else { "Sell" };
|
let dir = if is_buy { "Buy" } else { "Sell" };
|
||||||
@@ -171,6 +173,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
|||||||
Ok(_) => (true, sigs, None),
|
Ok(_) => (true, sigs, None),
|
||||||
Err(e) => (false, sigs, Some(e)),
|
Err(e) => (false, sigs, Some(e)),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(ok, sigs, err)
|
(ok, sigs, err)
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user