From 38a8e2dae413b373c8898dbf7dcb1dd592cd7470 Mon Sep 17 00:00:00 2001 From: wood Date: Mon, 7 Jul 2025 01:01:38 +0800 Subject: [PATCH] update jito and nextblock --- src/lib.rs | 3 +- src/swqos/common.rs | 14 +++- src/swqos/define.rs | 20 ++--- src/swqos/jito.rs | 137 +++++++++++++++++++++++++-------- src/swqos/mod.rs | 5 +- src/swqos/nextblock.rs | 162 +++++++++++++--------------------------- src/swqos/solana_rpc.rs | 13 ++-- src/swqos/temporal.rs | 16 ++-- src/swqos/zeroslot.rs | 16 ++-- 9 files changed, 204 insertions(+), 182 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c939407..c518b21 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,8 @@ impl SolanaTrade { let jito_client = JitoClient::new( rpc_url.clone(), swqos.endpoint, - ).await.expect("Failed to create Jito client"); + swqos.auth_token + ); swqos_clients.push(Arc::new(jito_client)); } SwqosType::NextBlock => { diff --git a/src/swqos/common.rs b/src/swqos/common.rs index 5aa97b8..ec45623 100755 --- a/src/swqos/common.rs +++ b/src/swqos/common.rs @@ -10,8 +10,20 @@ use tokio::time::sleep; use crate::common::types::SolanaRpcClient; use anyhow::Result; use base64::Engine; -use base64::engine::general_purpose::STANDARD; +use base64::engine::general_purpose::{self, STANDARD}; use reqwest::Client; +use solana_sdk::transaction::VersionedTransaction; + +pub trait FormatBase64VersionedTransaction { + fn to_base64_string(&self) -> String; +} + +impl FormatBase64VersionedTransaction for VersionedTransaction { + fn to_base64_string(&self) -> String { + let tx_bytes = bincode::serialize(self).unwrap(); + general_purpose::STANDARD.encode(tx_bytes) + } +} pub async fn poll_transaction_confirmation(rpc: &SolanaRpcClient, txt_sig: Signature) -> Result { let timeout: Duration = Duration::from_secs(5); diff --git a/src/swqos/define.rs b/src/swqos/define.rs index 46e3134..12ed37c 100755 --- a/src/swqos/define.rs +++ b/src/swqos/define.rs @@ -1,23 +1,25 @@ pub const SWQOS_ENDPOINTS_JITO: [&str; 3] = [ "https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles", - "https://ams.block-engine.jito.wtf/api/v1/bundles", "https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles", + "https://ams.block-engine.jito.wtf/api/v1/bundles", ]; -pub const SWQOS_ENDPOINTS_NEXTBLOCK: [&str; 3] = [ - "https://fra.nextblock.io", - "https://ams.nextblock.io", - "https://ny.nextblock.io", +pub const SWQOS_ENDPOINTS_NEXTBLOCK: [&str; 5] = [ + "http://ny.nextblock.io", + "http://fra.nextblock.io", + "http://slc.nextblock.io", + "http://tokyo.nextblock.io", + "http://london.nextblock.io", ]; pub const SWQOS_ENDPOINTS_ZERO_SLOT: [&str; 3] = [ + "http://ny1.0slot.trade", "http://de1.0slot.trade", - "http://ams.0slot.trade", - "http://ny.0slot.trade", + "http://ams1.0slot.trade", ]; pub const SWQOS_ENDPOINTS_TEMPORAL: [&str; 3] = [ + "http://ewr1.nozomi.temporal.xyz", "http://fra2.nozomi.temporal.xyz", - "http://ams2.nozomi.temporal.xyz", - "http://ny2.nozomi.temporal.xyz", + "http://ams1.nozomi.temporal.xyz", ]; \ No newline at end of file diff --git a/src/swqos/jito.rs b/src/swqos/jito.rs index d66c341..af7e6e0 100755 --- a/src/swqos/jito.rs +++ b/src/swqos/jito.rs @@ -1,13 +1,15 @@ -use tonic::transport::Channel; -use tokio::sync::Mutex; +use crate::swqos::common::{poll_transaction_confirmation, serialize_transaction_and_encode, FormatBase64VersionedTransaction}; +use rand::seq::IndexedRandom; +use reqwest::Client; +use serde_json::json; +use std::{sync::Arc, time::Instant}; -use rand::{rng, seq::IteratorRandom}; -use anyhow::{anyhow, Result}; -use std::sync::Arc; -use solana_sdk::{transaction::VersionedTransaction, signature::Signature}; -use crate::protos::searcher::searcher_service_client::SearcherServiceClient; -use crate::protos::searcher_client::{self, get_searcher_client_no_auth, send_bundle_with_confirmation}; +use std::time::Duration; +use solana_transaction_status::UiTransactionEncoding; + +use anyhow::Result; +use solana_sdk::transaction::VersionedTransaction; use crate::swqos::{SwqosType, TradeType}; use crate::swqos::SwqosClientTrait; @@ -15,25 +17,27 @@ use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::JITO_TIP_ACCO pub struct JitoClient { + pub endpoint: String, + pub auth_token: String, pub rpc_client: Arc, - pub searcher_client: Arc>>, + pub http_client: Client, } #[async_trait::async_trait] impl SwqosClientTrait for JitoClient { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { - self.send_bundle_with_confirmation(trade_type, &vec![transaction.clone()]).await?.first().cloned().ok_or(anyhow!("Failed to send transaction")) + async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> { + self.send_transaction(trade_type, transaction).await } - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { - self.send_bundle_with_confirmation(trade_type, transactions).await + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { + self.send_transactions(trade_type, transactions).await } fn get_tip_account(&self) -> Result { - if let Some(acc) = JITO_TIP_ACCOUNTS.iter().choose(&mut rng()) { + if let Some(acc) = JITO_TIP_ACCOUNTS.choose(&mut rand::rng()) { Ok(acc.to_string()) } else { - Err(anyhow!("no valid tip accounts found")) + Err(anyhow::anyhow!("no valid tip accounts found")) } } @@ -43,24 +47,95 @@ impl SwqosClientTrait for JitoClient { } impl JitoClient { - pub async fn new(rpc_url: String, block_engine_url: String) -> Result { + pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self { let rpc_client = SolanaRpcClient::new(rpc_url); - let searcher_client = get_searcher_client_no_auth(block_engine_url.as_str()).await?; - Ok(Self { rpc_client: Arc::new(rpc_client), searcher_client: Arc::new(Mutex::new(searcher_client)) }) - } - - pub async fn send_bundle_with_confirmation( - &self, - trade_type: TradeType, - transactions: &Vec, - ) -> Result> { - send_bundle_with_confirmation(self.rpc_client.clone(), trade_type, &transactions, self.searcher_client.clone()).await + let http_client = Client::builder() + .pool_idle_timeout(Duration::from_secs(60)) + .pool_max_idle_per_host(64) + .tcp_keepalive(Some(Duration::from_secs(1200))) + .http2_keep_alive_interval(Duration::from_secs(15)) + .timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(5)) + .build() + .unwrap(); + Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client } } - pub async fn send_bundle_no_wait( - &self, - transactions: &Vec, - ) -> Result> { - searcher_client::send_bundle_no_wait(&transactions, self.searcher_client.clone()).await + 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()); + + let request_body = serde_json::to_string(&json!({ + "id": 1, + "jsonrpc": "2.0", + "method": "sendTransaction", + "params": [ + content, + { + "encoding": "base64" + } + ] + }))?; + + let endpoint = format!("{}/api/v1/transactions", self.endpoint); + let response_text = self.http_client.post(&endpoint) + .body(request_body) + .header("Content-Type", "application/json") + .send() + .await? + .text() + .await?; + + if let Ok(response_json) = serde_json::from_str::(&response_text) { + if response_json.get("result").is_some() { + println!(" jito{}提交: {:?}", trade_type, start_time.elapsed()); + } else if let Some(_error) = response_json.get("error") { + eprintln!(" jito{}提交失败: {:?}", trade_type, _error); + } + } + + let start_time: Instant = Instant::now(); + match poll_transaction_confirmation(&self.rpc_client, signature).await { + Ok(_) => (), + Err(_) => (), + } + + println!(" jito{}确认: {:?}", trade_type, start_time.elapsed()); + + Ok(()) + } + + pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { + let start_time = Instant::now(); + let txs_base64 = transactions.iter().map(|tx| tx.to_base64_string()).collect::>(); + let body = serde_json::json!({ + "jsonrpc": "2.0", + "method": "sendBundle", + "params": [ + txs_base64, + { "encoding": "base64" } + ], + "id": 1, + }); + + let endpoint = format!("{}/api/v1/bundles", self.endpoint); + let response_text = self.http_client.post(&endpoint) + .body(body.to_string()) + .header("Content-Type", "application/json") + .send() + .await? + .text() + .await?; + + if let Ok(response_json) = serde_json::from_str::(&response_text) { + if response_json.get("result").is_some() { + println!(" jito{}提交: {:?}", trade_type, start_time.elapsed()); + } else if let Some(_error) = response_json.get("error") { + eprintln!(" jito{}提交失败: {:?}", trade_type, _error); + } + } + + Ok(()) } } \ No newline at end of file diff --git a/src/swqos/mod.rs b/src/swqos/mod.rs index f1b79c2..98cfb3b 100755 --- a/src/swqos/mod.rs +++ b/src/swqos/mod.rs @@ -7,7 +7,6 @@ pub mod zeroslot; pub mod temporal; pub mod define; -use solana_sdk::signature::Signature; use solana_sdk::transaction::VersionedTransaction; use tokio::sync::RwLock; @@ -52,8 +51,8 @@ pub type SwqosClient = dyn SwqosClientTrait + Send + Sync + 'static; #[async_trait::async_trait] pub trait SwqosClientTrait { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result; - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result>; + async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()>; + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()>; fn get_tip_account(&self) -> Result; fn get_swqos_type(&self) -> SwqosType; } diff --git a/src/swqos/nextblock.rs b/src/swqos/nextblock.rs index ca4b830..b7dc1cf 100755 --- a/src/swqos/nextblock.rs +++ b/src/swqos/nextblock.rs @@ -1,20 +1,11 @@ -use crate::protos::nextblock_grpc; -use crate::protos::nextblock_grpc::api_client::ApiClient; -use crate::swqos::common::{poll_transaction_confirmation, serialize_smart_transaction_and_encode}; +use crate::swqos::common::{poll_transaction_confirmation, serialize_transaction_and_encode}; use rand::seq::IndexedRandom; -use rustls::crypto::ring::default_provider; -use rustls::crypto::CryptoProvider; -use tonic::transport::Channel; -use yellowstone_grpc_client::Interceptor; -use std::str::FromStr; +use reqwest::Client; +use serde_json::json; use std::{sync::Arc, time::Instant}; -use solana_sdk::{signature::Signature}; - -use tonic::{service::interceptor::InterceptedService, transport::Uri, Status}; use std::time::Duration; use solana_transaction_status::UiTransactionEncoding; -use tonic::transport::ClientTlsConfig; use anyhow::Result; use solana_sdk::transaction::VersionedTransaction; @@ -23,42 +14,21 @@ use crate::swqos::SwqosClientTrait; use crate::{common::SolanaRpcClient, constants::pumpfun::accounts::NEXTBLOCK_TIP_ACCOUNTS}; - -#[derive(Clone)] -pub struct MyInterceptor { - auth_token: String, -} - -impl MyInterceptor { - pub fn new(auth_token: String) -> Self { - Self { auth_token } - } -} - -impl Interceptor for MyInterceptor { - fn call(&mut self, mut request: tonic::Request<()>) -> Result, Status> { - request.metadata_mut().insert( - "authorization", - tonic::metadata::MetadataValue::from_str(&self.auth_token) - .map_err(|_| Status::invalid_argument("Invalid auth token"))? - ); - Ok(request) - } -} - #[derive(Clone)] pub struct NextBlockClient { + pub endpoint: String, + pub auth_token: String, pub rpc_client: Arc, - pub client: ApiClient>, + pub http_client: Client, } #[async_trait::async_trait] impl SwqosClientTrait for NextBlockClient { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> { self.send_transaction(trade_type, transaction).await } - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { self.send_transactions(trade_type, transactions).await } @@ -74,93 +44,63 @@ impl SwqosClientTrait for NextBlockClient { impl NextBlockClient { pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self { - if CryptoProvider::get_default().is_none() { - let _ = default_provider() - .install_default() - .map_err(|e| anyhow::anyhow!("Failed to install crypto provider: {:?}", e)); - } - - let endpoint = endpoint.parse::().unwrap(); - let tls = ClientTlsConfig::new().with_native_roots(); - let channel = Channel::builder(endpoint) - .tls_config(tls).expect("Failed to create TLS config") - .tcp_keepalive(Some(Duration::from_secs(60))) - .http2_keep_alive_interval(Duration::from_secs(30)) - .keep_alive_while_idle(true) - .timeout(Duration::from_secs(30)) - .connect_timeout(Duration::from_secs(10)) - .connect_lazy(); - - let client = ApiClient::with_interceptor(channel, MyInterceptor::new(auth_token)); let rpc_client = SolanaRpcClient::new(rpc_url); - Self { rpc_client: Arc::new(rpc_client), client } + let http_client = Client::builder() + .pool_idle_timeout(Duration::from_secs(60)) + .pool_max_idle_per_host(64) + .tcp_keepalive(Some(Duration::from_secs(1200))) + .http2_keep_alive_interval(Duration::from_secs(15)) + .timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(5)) + .build() + .unwrap(); + Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client } } - pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> { let start_time = Instant::now(); - let (content, signature) = serialize_smart_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?; - - self.client.clone().post_submit_v2(nextblock_grpc::PostSubmitRequest { - transaction: Some(nextblock_grpc::TransactionMessage { - content, - is_cleanup: false, - }), - skip_pre_flight: true, - front_running_protection: Some(true), - experimental_front_running_protection: Some(true), - snipe_transaction: Some(true), - }).await?; + let (content, signature) = serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64).await?; + println!(" 交易编码base64: {:?}", start_time.elapsed()); - println!(" nextblock{}提交: {:?}", trade_type, start_time.elapsed()); + let request_body = serde_json::to_string(&json!({ + "transaction": { + "content": content + }, + "frontRunningProtection": false + }))?; + + let response_text = self.http_client.post(&self.endpoint) + .body(request_body) + .header("Authorization", &self.auth_token) + .header("Content-Type", "application/json") + .send() + .await? + .text() + .await?; + + if let Ok(response_json) = serde_json::from_str::(&response_text) { + if response_json.get("result").is_some() { + println!(" nextblock{}提交: {:?}", trade_type, start_time.elapsed()); + } else if let Some(_error) = response_json.get("error") { + eprintln!(" nextblock{}提交失败: {:?}", trade_type, _error); + } + } let start_time: Instant = Instant::now(); - let timeout: Duration = Duration::from_secs(10); - while Instant::now().duration_since(start_time) < timeout { - match poll_transaction_confirmation(&self.rpc_client, signature).await { - Ok(_) => break, - Err(_) => continue, - } + match poll_transaction_confirmation(&self.rpc_client, signature).await { + Ok(_) => (), + Err(_) => (), } println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed()); - Ok(signature) + Ok(()) } - pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { - let mut entries = Vec::new(); - let encoding = UiTransactionEncoding::Base64; - - let mut signatures = Vec::new(); + pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { for transaction in transactions { - let (content, signature) = serialize_smart_transaction_and_encode(transaction, encoding).await?; - entries.push(nextblock_grpc::PostSubmitRequestEntry { - transaction: Some(nextblock_grpc::TransactionMessage { - content, - is_cleanup: false, - }), - skip_pre_flight: true, - }); - signatures.push(signature); + self.send_transaction(trade_type, transaction).await?; } - - self.client.clone().post_submit_batch_v2(nextblock_grpc::PostSubmitBatchRequest { - entries, - submit_strategy: nextblock_grpc::SubmitStrategy::PSubmitAll as i32, - use_bundle: Some(true), - front_running_protection: Some(true), - }).await?; - - let start_time: Instant = Instant::now(); - for signature in signatures.clone() { - match poll_transaction_confirmation(&self.rpc_client, signature).await { - Ok(_) => continue, - Err(_) => continue, - } - } - - println!(" nextblock{}确认: {:?}", trade_type, start_time.elapsed()); - - Ok(signatures) + Ok(()) } } \ No newline at end of file diff --git a/src/swqos/solana_rpc.rs b/src/swqos/solana_rpc.rs index ede0fd5..911c5b8 100755 --- a/src/swqos/solana_rpc.rs +++ b/src/swqos/solana_rpc.rs @@ -3,7 +3,6 @@ use std::{sync::Arc, time::Instant}; use solana_client::rpc_config::RpcSendTransactionConfig; use solana_sdk::{ commitment_config::CommitmentLevel, - signature::Signature, transaction::VersionedTransaction, }; use solana_transaction_status::UiTransactionEncoding; @@ -19,7 +18,7 @@ pub struct SolRpcClient { #[async_trait::async_trait] impl SwqosClientTrait for SolRpcClient { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + 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), @@ -36,16 +35,14 @@ impl SwqosClientTrait for SolRpcClient { println!(" signature: {:?}", signature); println!(" rpc{}确认: {:?}", trade_type, start_time.elapsed()); - Ok(signature) + Ok(()) } - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { - let mut signatures = Vec::new(); + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { for transaction in transactions { - let signature = self.send_transaction(trade_type, transaction).await?; - signatures.push(signature); + self.send_transaction(trade_type, transaction).await?; } - Ok(signatures) + Ok(()) } fn get_tip_account(&self) -> Result { diff --git a/src/swqos/temporal.rs b/src/swqos/temporal.rs index 088c7e2..2002e3c 100755 --- a/src/swqos/temporal.rs +++ b/src/swqos/temporal.rs @@ -28,11 +28,11 @@ pub struct TemporalClient { #[async_trait::async_trait] impl SwqosClientTrait for TemporalClient { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> { self.send_transaction(trade_type, transaction).await } - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { self.send_transactions(trade_type, transactions).await } @@ -61,7 +61,7 @@ impl TemporalClient { Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client } } - pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + 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()); @@ -106,15 +106,13 @@ impl TemporalClient { println!(" nozomi{}确认: {:?}", trade_type, start_time.elapsed()); - Ok(signature) + Ok(()) } - pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { - let mut signatures = Vec::new(); + pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { for transaction in transactions { - let signature = self.send_transaction(trade_type, transaction).await?; - signatures.push(signature); + self.send_transaction(trade_type, transaction).await?; } - Ok(signatures) + Ok(()) } } \ No newline at end of file diff --git a/src/swqos/zeroslot.rs b/src/swqos/zeroslot.rs index 6dbde85..d9946c3 100755 --- a/src/swqos/zeroslot.rs +++ b/src/swqos/zeroslot.rs @@ -26,11 +26,11 @@ pub struct ZeroSlotClient { #[async_trait::async_trait] impl SwqosClientTrait for ZeroSlotClient { - async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result<()> { self.send_transaction(trade_type, transaction).await } - async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { + async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { self.send_transactions(trade_type, transactions).await } @@ -59,7 +59,7 @@ impl ZeroSlotClient { Self { rpc_client: Arc::new(rpc_client), endpoint, auth_token, http_client } } - pub async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction) -> Result { + 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()); @@ -105,15 +105,13 @@ impl ZeroSlotClient { println!(" 0slot{}确认: {:?}", trade_type, start_time.elapsed()); - Ok(signature) + Ok(()) } - pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result> { - let mut signatures = Vec::new(); + pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { for transaction in transactions { - let signature = self.send_transaction(trade_type, transaction).await?; - signatures.push(signature); + self.send_transaction(trade_type, transaction).await?; } - Ok(signatures) + Ok(()) } } \ No newline at end of file