feat: upgrade to v0.5.2
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sol-trade-sdk"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"William <byteblock6@gmail.com>",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(¶ms).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(),
|
||||
¶ms.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(¶ms).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(),
|
||||
¶ms.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,
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user