feat: upgrade to v0.5.2

This commit is contained in:
ysq
2025-08-27 14:27:26 +08:00
parent c10406915c
commit 9438172029
16 changed files with 189 additions and 159 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "sol-trade-sdk"
version = "0.5.1"
version = "0.5.2"
edition = "2021"
authors = [
"William <byteblock6@gmail.com>",
+2 -2
View File
@@ -33,14 +33,14 @@ Add the dependency to your `Cargo.toml`:
```toml
# Add to your Cargo.toml
sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.1" }
sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.2" }
```
### Use crates.io
```toml
# Add to your Cargo.toml
sol-trade-sdk = "0.5.1"
sol-trade-sdk = "0.5.2"
```
## Usage Examples
+2 -2
View File
@@ -33,14 +33,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
```toml
# 添加到您的 Cargo.toml
sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.1" }
sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.5.2" }
```
### 使用 crates.io
```toml
# 添加到您的 Cargo.toml
sol-trade-sdk = "0.5.1"
sol-trade-sdk = "0.5.2"
```
## 使用示例
+11 -9
View File
@@ -60,7 +60,7 @@ impl BloxrouteClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let body = serde_json::json!({
"transaction": {
@@ -80,32 +80,34 @@ impl BloxrouteClient {
.text()
.await?;
// 5. `serde_json::from_str()` 解析 JSON,减少 `.json().await?` 额外等待
// 5. Use `serde_json::from_str()` to parse JSON, reducing extra wait from `.json().await?`
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" bloxroute{}提交: {:?}", trade_type, start_time.elapsed());
println!(" bloxroute {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" bloxroute{}提交失败: {:?}", trade_type, _error);
eprintln!(" bloxroute {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" bloxroute {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" bloxroute{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" bloxroute {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" bloxroute{}确认: {:?}", trade_type, start_time.elapsed());
println!(" bloxroute {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<()> {
let start_time = Instant::now();
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let body = serde_json::json!({
"entries": transactions
@@ -132,9 +134,9 @@ impl BloxrouteClient {
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" bloxroute{}提交: {:?}", trade_type, start_time.elapsed());
println!(" bloxroute {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" bloxroute{}提交失败: {:?}", trade_type, _error);
eprintln!(" bloxroute {} submission failed: {:?}", trade_type, _error);
}
}
+5 -5
View File
@@ -59,7 +59,7 @@ pub async fn poll_transaction_confirmation(rpc: &SolanaRpcClient, txt_sig: Signa
pub async fn send_nb_transaction(client: Client, endpoint: &str, auth_token: &str, transaction: &Transaction) -> Result<Signature, anyhow::Error> {
// 序列化交易
let serialized = bincode::serialize(transaction)
.map_err(|e| anyhow::anyhow!("序列化交易失败: {}", e))?;
.map_err(|e| anyhow::anyhow!("Transaction serialization failed: {}", e))?;
// Base64编码
let encoded = STANDARD.encode(serialized);
@@ -79,20 +79,20 @@ pub async fn send_nb_transaction(client: Client, endpoint: &str, auth_token: &st
.json(&request_data)
.send()
.await
.map_err(|e| anyhow::anyhow!("请求失败: {}", e))?;
.map_err(|e| anyhow::anyhow!("Request failed: {}", e))?;
let resp = response.json::<serde_json::Value>().await
.map_err(|e| anyhow::anyhow!("解析响应失败: {}", e))?;
.map_err(|e| anyhow::anyhow!("Response parsing failed: {}", e))?;
if let Some(reason) = resp["reason"].as_str() {
return Err(anyhow::anyhow!(reason.to_string()));
}
let signature = resp["signature"].as_str()
.ok_or_else(|| anyhow::anyhow!("响应中缺少signature字段"))?;
.ok_or_else(|| anyhow::anyhow!("Missing signature field in response"))?;
let signature = Signature::from_str(signature)
.map_err(|e| anyhow::anyhow!("无效的签名: {}", e))?;
.map_err(|e| anyhow::anyhow!("Invalid signature: {}", e))?;
Ok(signature)
}
+10 -8
View File
@@ -61,16 +61,16 @@ impl FlashBlockClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
// FlashBlock API格式
// FlashBlock API format
let request_body = serde_json::to_string(&json!({
"transactions": [content]
}))?;
let url = format!("{}/api/v2/submit-batch", self.endpoint);
// 发送请求到FlashBlock
// Send request to FlashBlock
let response_text = self.http_client.post(&url)
.body(request_body)
.header("Authorization", &self.auth_token)
@@ -80,25 +80,27 @@ impl FlashBlockClient {
.text()
.await?;
// 解析响应
// Parse response
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("success").is_some() || response_json.get("result").is_some() {
println!(" FlashBlock{}提交: {:?}", trade_type, start_time.elapsed());
println!(" FlashBlock {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" FlashBlock{}提交失败: {:?}", trade_type, _error);
eprintln!(" FlashBlock {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" FlashBlock {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" FlashBlock{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" FlashBlock {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" FlashBlock{}确认: {:?}", trade_type, start_time.elapsed());
println!(" FlashBlock {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
+9 -7
View File
@@ -64,7 +64,7 @@ impl JitoClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let request_body = serde_json::to_string(&json!({
"id": 1,
@@ -99,22 +99,24 @@ impl JitoClient {
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" jito{}提交: {:?}", trade_type, start_time.elapsed());
println!(" jito {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" jito{}提交失败: {:?}", trade_type, _error);
eprintln!(" jito {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" jito {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" jito{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" jito {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" jito{}确认: {:?}", trade_type, start_time.elapsed());
println!(" jito {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
@@ -153,9 +155,9 @@ impl JitoClient {
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" jito{}提交: {:?}", trade_type, start_time.elapsed());
println!(" jito {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" jito{}提交失败: {:?}", trade_type, _error);
eprintln!(" jito {} submission failed: {:?}", trade_type, _error);
}
}
+4 -4
View File
@@ -53,10 +53,10 @@ pub enum TradeType {
impl std::fmt::Display for TradeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
TradeType::Create => "创建",
TradeType::CreateAndBuy => "创建并买入",
TradeType::Buy => "买入",
TradeType::Sell => "卖出",
TradeType::Create => "Create",
TradeType::CreateAndBuy => "Create and Buy",
TradeType::Buy => "Buy",
TradeType::Sell => "Sell",
};
write!(f, "{}", s)
}
+7 -5
View File
@@ -60,7 +60,7 @@ impl NextBlockClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let request_body = serde_json::to_string(&json!({
"transaction": {
@@ -80,22 +80,24 @@ impl NextBlockClient {
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" nextblock{}提交: {:?}", trade_type, start_time.elapsed());
println!(" nextblock {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" nextblock{}提交失败: {:?}", trade_type, _error);
eprintln!(" nextblock {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" nextblock {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" nextblock{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" nextblock {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed());
println!(" nextblock {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
+33 -31
View File
@@ -51,15 +51,15 @@ impl Node1Client {
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
let rpc_client = SolanaRpcClient::new(rpc_url);
let http_client = Client::builder()
// 由于有 ping 机制,可以延长连接池空闲超时
.pool_idle_timeout(Duration::from_secs(300)) // 5分钟,比 ping 间隔更长
.pool_max_idle_per_host(32) // 减少连接数,因为连接会更稳定
// TCP keepalive 可以设置得更长,因为 ping 会主动保持连接
.tcp_keepalive(Some(Duration::from_secs(300))) // 5分钟
// HTTP/2 keepalive 间隔可以更长
.http2_keep_alive_interval(Duration::from_secs(30)) // 30
// 请求超时可以适当延长,因为连接更稳定
.timeout(Duration::from_secs(15)) // 15
// Due to ping mechanism, can extend connection pool idle timeout
.pool_idle_timeout(Duration::from_secs(300)) // 5 minutes, longer than ping interval
.pool_max_idle_per_host(32) // Reduce connections as they will be more stable
// TCP keepalive can be set longer as ping will actively maintain connections
.tcp_keepalive(Some(Duration::from_secs(300))) // 5 minutes
// HTTP/2 keepalive interval can be longer
.http2_keep_alive_interval(Duration::from_secs(30)) // 30 seconds
// Request timeout can be appropriately extended as connections are more stable
.timeout(Duration::from_secs(15)) // 15 seconds
.connect_timeout(Duration::from_secs(5))
.build()
.unwrap();
@@ -73,7 +73,7 @@ impl Node1Client {
stop_ping: Arc::new(AtomicBool::new(false)),
};
// 启动 ping 任务
// Start ping task
let client_clone = client.clone();
tokio::spawn(async move {
client_clone.start_ping_task().await;
@@ -82,7 +82,7 @@ impl Node1Client {
client
}
/// 启动定期 ping 任务以保持连接活跃
/// Start periodic ping task to keep connections active
async fn start_ping_task(&self) {
let endpoint = self.endpoint.clone();
let auth_token = self.auth_token.clone();
@@ -90,7 +90,7 @@ impl Node1Client {
let stop_ping = self.stop_ping.clone();
let handle = tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(60)); // 每60秒ping一次
let mut interval = tokio::time::interval(Duration::from_secs(60)); // Ping every 60 seconds
loop {
interval.tick().await;
@@ -99,14 +99,14 @@ impl Node1Client {
break;
}
// 发送 ping 请求
// Send ping request
if let Err(e) = Self::send_ping_request(&http_client, &endpoint, &auth_token).await {
eprintln!("Node1 ping 请求失败: {}", e);
eprintln!("Node1 ping request failed: {}", e);
}
}
});
// 更新 ping_handle - 使用 Mutex 来安全地更新
// Update ping_handle - use Mutex to safely update
{
let mut ping_guard = self.ping_handle.lock().await;
if let Some(old_handle) = ping_guard.as_ref() {
@@ -116,25 +116,25 @@ impl Node1Client {
}
}
/// 发送 ping 请求到 /ping 端点
/// Send ping request to /ping endpoint
async fn send_ping_request(http_client: &Client, endpoint: &str, _auth_token: &str) -> Result<()> {
// 构建 ping URL
// Build ping URL
let ping_url = if endpoint.ends_with('/') {
format!("{}ping", endpoint)
} else {
format!("{}/ping", endpoint)
};
// 发送 GET 请求到 /ping 端点(不需要 api-key
// Send GET request to /ping endpoint (no api-key required)
let response = http_client.get(&ping_url)
.send()
.await?;
if response.status().is_success() {
// ping 成功,连接保持活跃
// 可以选择性地记录日志,但为了减少噪音,这里不打印
// ping successful, connection remains active
// Can optionally log, but to reduce noise, not printing here
} else {
eprintln!("Node1 ping 请求返回非成功状态: {}", response.status());
eprintln!("Node1 ping request returned non-success status: {}", response.status());
}
Ok(())
@@ -143,7 +143,7 @@ impl Node1Client {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let request_body = serde_json::to_string(&json!({
"jsonrpc": "2.0",
@@ -155,7 +155,7 @@ impl Node1Client {
]
}))?;
// Node1使用api-key header而不是URL参数
// Node1 uses api-key header instead of URL parameter
let response_text = self.http_client.post(&self.endpoint)
.body(request_body)
.header("Content-Type", "application/json")
@@ -165,25 +165,27 @@ impl Node1Client {
.text()
.await?;
// 解析JSON响应
// Parse JSON response
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" node1{}提交: {:?}", trade_type, start_time.elapsed());
println!(" node1 {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" node1{}提交失败: {:?}", trade_type, _error);
eprintln!(" node1 {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" node1 {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" node1{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" node1 {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" node1{}确认: {:?}", trade_type, start_time.elapsed());
println!(" node1 {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
@@ -198,11 +200,11 @@ impl Node1Client {
impl Drop for Node1Client {
fn drop(&mut self) {
// 确保在客户端被销毁时停止 ping 任务
// Ensure ping task stops when client is destroyed
self.stop_ping.store(true, Ordering::Relaxed);
// 尝试立即停止 ping 任务
// 使用 tokio::spawn 来避免阻塞 Drop
// Try to stop ping task immediately
// Use tokio::spawn to avoid blocking Drop
let ping_handle = self.ping_handle.clone();
tokio::spawn(async move {
let mut ping_guard = ping_handle.lock().await;
+32 -18
View File
@@ -1,14 +1,14 @@
use std::{sync::Arc, time::Instant};
use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_sdk::{
commitment_config::CommitmentLevel,
transaction::VersionedTransaction,
};
use solana_sdk::{commitment_config::CommitmentLevel, transaction::VersionedTransaction};
use solana_transaction_status::UiTransactionEncoding;
use crate::{common::SolanaRpcClient, swqos::{common::poll_transaction_confirmation, SwqosType, TradeType}};
use crate::swqos::SwqosClientTrait;
use crate::{
common::SolanaRpcClient,
swqos::{common::poll_transaction_confirmation, SwqosType, TradeType},
};
use anyhow::Result;
#[derive(Clone)]
@@ -18,30 +18,44 @@ pub struct SolRpcClient {
#[async_trait::async_trait]
impl SwqosClientTrait for SolRpcClient {
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let signature = self.rpc_client.send_transaction_with_config(transaction, RpcSendTransactionConfig{
skip_preflight: true,
preflight_commitment: Some(CommitmentLevel::Processed),
encoding: Some(UiTransactionEncoding::Base64),
max_retries: Some(3),
min_context_slot: Some(0),
}).await?;
async fn send_transaction(
&self,
trade_type: TradeType,
transaction: &VersionedTransaction,
) -> Result<()> {
let signature = self
.rpc_client
.send_transaction_with_config(
transaction,
RpcSendTransactionConfig {
skip_preflight: true,
preflight_commitment: Some(CommitmentLevel::Processed),
encoding: Some(UiTransactionEncoding::Base64),
max_retries: Some(3),
min_context_slot: Some(0),
},
)
.await?;
let start_time = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" rpc{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" rpc{} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
}
println!(" signature: {:?}", signature);
println!(" rpc{}确认: {:?}", trade_type, start_time.elapsed());
println!(" rpc{} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<()> {
async fn send_transactions(
&self,
trade_type: TradeType,
transactions: &Vec<VersionedTransaction>,
) -> Result<()> {
for transaction in transactions {
self.send_transaction(trade_type, transaction).await?;
}
@@ -61,4 +75,4 @@ impl SolRpcClient {
pub fn new(rpc_client: Arc<SolanaRpcClient>) -> Self {
Self { rpc_client }
}
}
}
+8 -6
View File
@@ -61,9 +61,9 @@ impl TemporalClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
// 按照 Nozomi 文档要求构建请求体
// Build request body according to Nozomi documentation requirements
let request_body = serde_json::to_string(&json!({
"jsonrpc": "2.0",
"id": 1,
@@ -89,22 +89,24 @@ impl TemporalClient {
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" nozomi{}提交: {:?}", trade_type, start_time.elapsed());
println!(" nozomi {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
// eprintln!("nozomi交易提交失败: {:?}", _error);
// eprintln!("nozomi transaction submission failed: {:?}", _error);
}
} else {
eprintln!(" nozomi {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" nozomi{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" nozomi {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" nozomi{}确认: {:?}", trade_type, start_time.elapsed());
println!(" nozomi {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
+11 -9
View File
@@ -61,7 +61,7 @@ impl ZeroSlotClient {
pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> {
let start_time = Instant::now();
let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?;
println!(" 交易编码base64: {:?}", start_time.elapsed());
println!(" Transaction encoded to base64: {:?}", start_time.elapsed());
let request_body = serde_json::to_string(&json!({
"jsonrpc": "2.0",
@@ -78,34 +78,36 @@ impl ZeroSlotClient {
url.push_str("/?api-key=");
url.push_str(&self.auth_token);
// 4. 直接使用 `text().await?`,避免 `json().await?` 的异步 JSON 解析
// 4. Use `text().await?` directly, avoiding async JSON parsing from `json().await?`
let response_text = self.http_client.post(&url)
.body(request_body) // 直接传字符串,避免 `json()` 开销
.header("Content-Type", "application/json") // 显式指定 JSON 头
.body(request_body) // Pass string directly, avoiding `json()` overhead
.header("Content-Type", "application/json") // Explicitly specify JSON header
.send()
.await?
.text()
.await?;
// 5. `serde_json::from_str()` 解析 JSON,减少 `.json().await?` 额外等待
// 5. Use `serde_json::from_str()` to parse JSON, reducing extra wait from `.json().await?`
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
if response_json.get("result").is_some() {
println!(" 0slot{}提交: {:?}", trade_type, start_time.elapsed());
println!(" 0slot {} submitted: {:?}", trade_type, start_time.elapsed());
} else if let Some(_error) = response_json.get("error") {
eprintln!(" 0slot{}提交失败: {:?}", trade_type, _error);
eprintln!(" 0slot {} submission failed: {:?}", trade_type, _error);
}
} else {
eprintln!(" 0slot {} submission failed: {:?}", trade_type, response_text);
}
let start_time: Instant = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, signature).await {
Ok(_) => (),
Err(e) => {
println!(" 0slot{}确认失败: {:?}", trade_type, start_time.elapsed());
println!(" 0slot {} confirmation failed: {:?}", trade_type, start_time.elapsed());
return Err(e);
},
}
println!(" 0slot{}确认: {:?}", trade_type, start_time.elapsed());
println!(" 0slot {} confirmed: {:?}", trade_type, start_time.elapsed());
Ok(())
}
+22 -22
View File
@@ -17,7 +17,7 @@ use crate::{
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024;
/// 通用交易执行器实现
/// Generic trade executor implementation
pub struct GenericTradeExecutor {
instruction_builder: Arc<dyn InstructionBuilder>,
protocol_name: &'static str,
@@ -46,8 +46,8 @@ impl TradeExecutor for GenericTradeExecutor {
return Err(anyhow!("RPC is not set"));
}
let rpc = params.rpc.as_ref().unwrap().clone();
let mut timer = TradeTimer::new("构建买入交易指令");
// 构建指令
let mut timer = TradeTimer::new("Building buy transaction instructions");
// Build instructions
let instructions = self.instruction_builder.build_buy_instructions(&params).await?;
let final_instructions = match middleware_manager.clone() {
Some(middleware_manager) => middleware_manager
@@ -58,9 +58,9 @@ impl TradeExecutor for GenericTradeExecutor {
)?,
None => instructions,
};
timer.stage("构建rpc交易指令");
timer.stage("Building RPC transaction instructions");
// 构建交易
// Build transaction
let transaction = build_rpc_transaction(
params.payer.clone(),
&params.priority_fee,
@@ -73,13 +73,13 @@ impl TradeExecutor for GenericTradeExecutor {
true,
)
.await?;
timer.stage("rpc提交确认");
timer.stage("RPC submission confirmation");
// 发送交易
// Send transaction
if params.wait_transaction_confirmed {
rpc.send_and_confirm_transaction(&transaction).await?;
} else {
// 异步发送交易
// Send transaction asynchronously
rpc.send_transaction(&transaction).await?;
}
timer.finish();
@@ -95,9 +95,9 @@ impl TradeExecutor for GenericTradeExecutor {
if params.data_size_limit == 0 {
params.data_size_limit = MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
}
let timer = TradeTimer::new("构建买入交易指令");
let timer = TradeTimer::new("Building buy transaction instructions");
// 验证参数 - 转换为BuyParams进行验证
// Validate parameters - convert to BuyParams for validation
let buy_params = BuyParams {
rpc: params.rpc,
payer: params.payer.clone(),
@@ -112,7 +112,7 @@ impl TradeExecutor for GenericTradeExecutor {
protocol_params: params.protocol_params.clone(),
};
// 构建指令
// Build instructions
let instructions = self.instruction_builder.build_buy_instructions(&buy_params).await?;
let final_instructions = match middleware_manager.clone() {
Some(middleware_manager) => middleware_manager
@@ -126,7 +126,7 @@ impl TradeExecutor for GenericTradeExecutor {
timer.finish();
// 并行执行交易
// Execute transactions in parallel
parallel_execute_with_tips(
params.swqos_clients,
params.payer,
@@ -155,9 +155,9 @@ impl TradeExecutor for GenericTradeExecutor {
return Err(anyhow!("RPC is not set"));
}
let rpc = params.rpc.as_ref().unwrap().clone();
let mut timer = TradeTimer::new("构建卖出交易指令");
let mut timer = TradeTimer::new("Building sell transaction instructions");
// 构建指令
// Build instructions
let instructions = self.instruction_builder.build_sell_instructions(&params).await?;
let final_instructions = match middleware_manager.clone() {
Some(middleware_manager) => middleware_manager
@@ -168,9 +168,9 @@ impl TradeExecutor for GenericTradeExecutor {
)?,
None => instructions,
};
timer.stage("卖出交易指令");
timer.stage("Sell transaction instructions");
// 构建交易
// Build transaction
let transaction = build_sell_transaction(
params.payer.clone(),
&params.priority_fee,
@@ -182,9 +182,9 @@ impl TradeExecutor for GenericTradeExecutor {
false,
)
.await?;
timer.stage("卖出交易签名");
timer.stage("Sell transaction signing");
// 发送交易
// Send transaction
if params.wait_transaction_confirmed {
rpc.send_and_confirm_transaction(&transaction).await?;
} else {
@@ -200,9 +200,9 @@ impl TradeExecutor for GenericTradeExecutor {
params: SellWithTipParams,
middleware_manager: Option<Arc<MiddlewareManager>>,
) -> Result<()> {
let timer = TradeTimer::new("构建卖出交易指令");
let timer = TradeTimer::new("Building sell transaction instructions");
// 转换为SellParams进行指令构建
// Convert to SellParams for instruction building
let sell_params = SellParams {
rpc: params.rpc,
payer: params.payer.clone(),
@@ -216,7 +216,7 @@ impl TradeExecutor for GenericTradeExecutor {
protocol_params: params.protocol_params.clone(),
};
// 构建指令
// Build instructions
let instructions = self.instruction_builder.build_sell_instructions(&sell_params).await?;
let final_instructions = match middleware_manager.clone() {
Some(middleware_manager) => middleware_manager
@@ -230,7 +230,7 @@ impl TradeExecutor for GenericTradeExecutor {
timer.finish();
// 并行执行交易
// Execute transactions in parallel
parallel_execute_with_tips(
params.swqos_clients,
params.payer,
+17 -12
View File
@@ -18,7 +18,7 @@ use crate::{
},
};
/// 并行执行交易的通用函数
/// Generic function for parallel transaction execution
pub async fn parallel_execute_with_tips(
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
@@ -49,8 +49,10 @@ pub async fn parallel_execute_with_tips(
let handle = tokio::spawn(async move {
core_affinity::set_for_current(core_id);
let mut timer =
TradeTimer::new(format!("构建交易指令: {:?}", swqos_client.get_swqos_type()));
let mut timer = TradeTimer::new(format!(
"Building transaction instructions: {:?}",
swqos_client.get_swqos_type()
));
let transaction = if matches!(trade_type, TradeType::Sell)
&& swqos_client.get_swqos_type() == SwqosType::Default
@@ -99,7 +101,8 @@ pub async fn parallel_execute_with_tips(
} else {
let tip_account = swqos_client.get_tip_account()?;
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
priority_fee.buy_tip_fee = priority_fee.buy_tip_fees[i % priority_fee.buy_tip_fees.len()];
priority_fee.buy_tip_fee =
priority_fee.buy_tip_fees[i % priority_fee.buy_tip_fees.len()];
build_tip_transaction_with_priority_fee(
payer,
@@ -116,7 +119,10 @@ pub async fn parallel_execute_with_tips(
.await?
};
timer.stage(format!("提交交易指令: {:?}", swqos_client.get_swqos_type()));
timer.stage(format!(
"Submitting transaction instructions: {:?}",
swqos_client.get_swqos_type()
));
swqos_client.send_transaction(trade_type, &transaction).await?;
@@ -126,11 +132,10 @@ pub async fn parallel_execute_with_tips(
handles.push(handle);
}
// 任意一个成功即返回
// Return as soon as any one succeeds
let (tx, mut rx) = mpsc::channel(swqos_clients.len());
// 启动监听任务
// Start monitoring tasks
for handle in handles {
let tx = tx.clone();
tokio::spawn(async move {
@@ -138,9 +143,9 @@ pub async fn parallel_execute_with_tips(
let _ = tx.send(result).await;
});
}
drop(tx); // 关闭发送端
drop(tx); // Close the sender
// 等待第一个成功的结果
// Wait for the first successful result
let mut errors = Vec::new();
if !wait_transaction_confirmed {
@@ -157,6 +162,6 @@ pub async fn parallel_execute_with_tips(
}
}
// 如果没有成功的,返回错误
return Err(anyhow!("所有交易都失败了: {:?}", errors));
// If no success, return error
return Err(anyhow!("All transactions failed: {:?}", errors));
}
+15 -18
View File
@@ -1,6 +1,6 @@
use std::time::Instant;
/// 交易时间测量器
/// Trade time measurement tool
#[derive(Clone)]
pub struct TradeTimer {
start_time: Instant,
@@ -8,31 +8,28 @@ pub struct TradeTimer {
}
impl TradeTimer {
/// 创建新的计时器
/// Create a new timer
pub fn new(stage: impl Into<String>) -> Self {
Self {
start_time: Instant::now(),
stage: stage.into(),
}
Self { start_time: Instant::now(), stage: stage.into() }
}
/// 记录当前阶段耗时并开始新阶段
/// Record current stage time and start a new stage
pub fn stage(&mut self, new_stage: impl Into<String>) {
let elapsed = self.start_time.elapsed();
println!(" {} 耗时: {:?}", self.stage, elapsed);
println!(" {} time cost: {:?}", self.stage, elapsed);
self.start_time = Instant::now();
self.stage = new_stage.into();
}
/// 完成计时并输出最终耗时
/// Complete timing and output final time cost
pub fn finish(mut self) {
let elapsed = self.start_time.elapsed();
println!(" {} 耗时: {:?}", self.stage, elapsed);
self.stage.clear(); // 清空stage,避免Drop时重复打印
println!(" {} time cost: {:?}", self.stage, elapsed);
self.stage.clear(); // Clear stage to avoid duplicate printing in Drop
}
/// 获取当前阶段的耗时(不重置计时器)
/// Get the elapsed time of current stage (without resetting the timer)
pub fn elapsed(&self) -> std::time::Duration {
self.start_time.elapsed()
}
@@ -42,7 +39,7 @@ impl Drop for TradeTimer {
fn drop(&mut self) {
if !self.stage.is_empty() {
let elapsed = self.start_time.elapsed();
println!(" {} 耗时: {:?}", self.stage, elapsed);
println!(" {} time cost: {:?}", self.stage, elapsed);
}
}
}
}