From 84c2e501ce0b7e929f591cc07fa6d0864e9e38bc Mon Sep 17 00:00:00 2001 From: wei <1415121722@qq.com> Date: Wed, 20 Aug 2025 20:50:56 +0800 Subject: [PATCH] swqos support node1 --- Cargo.toml | 2 +- README.md | 9 ++-- README_CN.md | 9 ++-- src/constants/swqos.rs | 20 ++++++++ src/main.rs | 1 + src/swqos/mod.rs | 15 +++++- src/swqos/node1.rs | 112 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 src/swqos/node1.rs diff --git a/Cargo.toml b/Cargo.toml index 952d3da..baca06d 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "0.4.2" +version = "0.4.3" edition = "2021" authors = ["William ", "sgxiang ", "wei <1415121722@qq.com>"] repository = "https://github.com/0xfnzero/sol-trade-sdk" diff --git a/README.md b/README.md index d76997d..07ed927 100755 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A comprehensive Rust SDK for seamless interaction with Solana DEX trading progra 6. **Event Subscription**: Subscribe to PumpFun, PumpSwap, Bonk, Raydium CPMM, and Raydium AMM V4 program trading events 7. **Yellowstone gRPC**: Subscribe to program events using Yellowstone gRPC 8. **ShredStream Support**: Subscribe to program events using ShredStream -9. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, and other services +9. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, Node1, and other services 10. **Concurrent Trading**: Send transactions using multiple MEV services simultaneously; the fastest succeeds while others fail 11. **Unified Trading Interface**: Use unified trading protocol enums for trading operations 12. **Middleware System**: Support for custom instruction middleware to modify, add, or remove instructions before transaction execution @@ -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.4.2" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.4.3" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "0.4.2" +sol-trade-sdk = "0.4.3" ``` ## Usage Examples @@ -268,6 +268,7 @@ When configuring SWQOS services, note the different parameter requirements for e - **Bloxroute**: The first parameter is API Token - **ZeroSlot**: The first parameter is API Token - **Temporal**: The first parameter is API Token +- **Node1**: The first parameter is API Token ```rust use std::{str::FromStr, sync::Arc}; @@ -292,6 +293,7 @@ async fn test_create_solana_trade_client() -> AnyResult { SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Default(rpc_url.clone()), ]; @@ -908,6 +910,7 @@ let trade_config = TradeConfig { - **ZeroSlot**: Zero-latency transactions - **Temporal**: Time-sensitive transactions - **Bloxroute**: Blockchain network acceleration +- **Node1**: High-speed transaction execution with API key authentication ## New Architecture Features diff --git a/README_CN.md b/README_CN.md index 7d414ec..f59b7c5 100755 --- a/README_CN.md +++ b/README_CN.md @@ -13,7 +13,7 @@ 6. **事件订阅**: 订阅 PumpFun、PumpSwap、Bonk、Raydium CPMM 和 Raydium AMM V4 程序的交易事件 7. **Yellowstone gRPC**: 使用 Yellowstone gRPC 订阅程序事件 8. **ShredStream 支持**: 使用 ShredStream 订阅程序事件 -9. **多种 MEV 保护**: 支持 Jito、Nextblock、ZeroSlot、Temporal、Bloxroute 等服务 +9. **多种 MEV 保护**: 支持 Jito、Nextblock、ZeroSlot、Temporal、Bloxroute、Node1 等服务 10. **并发交易**: 同时使用多个 MEV 服务发送交易,最快的成功,其他失败 11. **统一交易接口**: 使用统一的交易协议枚举进行交易操作 12. **中间件系统**: 支持自定义指令中间件,可在交易执行前对指令进行修改、添加或移除 @@ -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.4.2" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "0.4.3" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "0.4.2" +sol-trade-sdk = "0.4.3" ``` ## 使用示例 @@ -268,6 +268,7 @@ async fn test_shreds() -> Result<(), Box> { - **Bloxroute**: 第一个参数是 API Token - **ZeroSlot**: 第一个参数是 API Token - **Temporal**: 第一个参数是 API Token +- **Node1**: 第一个参数是 API Token ```rust use std::{str::FromStr, sync::Arc}; @@ -292,6 +293,7 @@ async fn test_create_solana_trade_client() -> AnyResult { SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Default(rpc_url.clone()), ]; @@ -928,6 +930,7 @@ let trade_config = TradeConfig { - **ZeroSlot**: 零延迟交易 - **Temporal**: 时间敏感交易 - **Bloxroute**: 区块链网络加速 +- **Node1**: 高速交易执行,支持 API 密钥认证 ## 新架构特性 diff --git a/src/constants/swqos.rs b/src/constants/swqos.rs index 608ef26..effec96 100755 --- a/src/constants/swqos.rs +++ b/src/constants/swqos.rs @@ -59,6 +59,15 @@ pub const BLOX_TIP_ACCOUNTS: &[Pubkey] = &[ pubkey!("FogxVNs6Mm2w9rnGL1vkARSwJxvLE8mujTv3LK8RnUhF"), ]; +pub const NODE1_TIP_ACCOUNTS: &[Pubkey] = &[ + pubkey!("node1PqAa3BWWzUnTHVbw8NJHC874zn9ngAkXjgWEej"), + pubkey!("node1UzzTxAAeBTpfZkQPJXBAqixsbdth11ba1NXLBG"), + pubkey!("node1Qm1bV4fwYnCurP8otJ9s5yrkPq7SPZ5uhj3Tsv"), + pubkey!("node1PUber6SFmSQgvf2ECmXsHP5o3boRSGhvJyPMX1"), + pubkey!("node1AyMbeqiVN6eoQzEAwCA6Pk826hrdqdAHR7cdJ3"), + pubkey!("node1YtWCoTwwVYTFLfS19zquRQzYX332hs1HEuRBjC"), +]; + // NewYork, // Frankfurt, // Amsterdam, @@ -123,3 +132,14 @@ pub const SWQOS_ENDPOINTS_BLOX: [&str; 8] = [ "https://germany.solana.dex.blxrbdn.com", ]; +pub const SWQOS_ENDPOINTS_NODE1: [&str; 8] = [ + "https://ny.node1.me", + "http://ny.node1.me", + "https://ams.node1.me", + "http://ams.node1.me", + "https://fra.node1.me", + "http://fra.node1.me", + "https://fra.node1.me", + "http://fra.node1.me", +]; + diff --git a/src/main.rs b/src/main.rs index 0cd73bb..4416c3b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -80,6 +80,7 @@ fn create_swqos_configs(rpc_url: &str) -> Vec { SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt), + SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt), SwqosConfig::Default(rpc_url.to_string()), ] } diff --git a/src/swqos/mod.rs b/src/swqos/mod.rs index c715b60..224f4a3 100755 --- a/src/swqos/mod.rs +++ b/src/swqos/mod.rs @@ -5,6 +5,7 @@ pub mod nextblock; pub mod zeroslot; pub mod temporal; pub mod bloxroute; +pub mod node1; use std::sync::Arc; @@ -13,7 +14,7 @@ use tokio::sync::RwLock; use anyhow::Result; -use crate::{common::SolanaRpcClient, constants::swqos::{SWQOS_ENDPOINTS_BLOX, SWQOS_ENDPOINTS_JITO, SWQOS_ENDPOINTS_NEXTBLOCK, SWQOS_ENDPOINTS_TEMPORAL, SWQOS_ENDPOINTS_ZERO_SLOT}, swqos::{bloxroute::BloxrouteClient, jito::JitoClient, nextblock::NextBlockClient, solana_rpc::SolRpcClient, temporal::TemporalClient, zeroslot::ZeroSlotClient}}; +use crate::{common::SolanaRpcClient, constants::swqos::{SWQOS_ENDPOINTS_BLOX, SWQOS_ENDPOINTS_JITO, SWQOS_ENDPOINTS_NEXTBLOCK, SWQOS_ENDPOINTS_TEMPORAL, SWQOS_ENDPOINTS_ZERO_SLOT, SWQOS_ENDPOINTS_NODE1}, swqos::{bloxroute::BloxrouteClient, jito::JitoClient, nextblock::NextBlockClient, solana_rpc::SolRpcClient, temporal::TemporalClient, zeroslot::ZeroSlotClient, node1::Node1Client}}; lazy_static::lazy_static! { static ref TIP_ACCOUNT_CACHE: RwLock> = RwLock::new(Vec::new()); @@ -46,6 +47,7 @@ pub enum SwqosType { ZeroSlot, Temporal, Bloxroute, + Node1, Default, } @@ -79,6 +81,7 @@ pub enum SwqosConfig { Bloxroute(String, SwqosRegion), Temporal(String, SwqosRegion), ZeroSlot(String, SwqosRegion), + Node1(String, SwqosRegion), } impl SwqosConfig { @@ -89,6 +92,7 @@ impl SwqosConfig { SwqosType::ZeroSlot => SWQOS_ENDPOINTS_ZERO_SLOT[region as usize].to_string(), SwqosType::Temporal => SWQOS_ENDPOINTS_TEMPORAL[region as usize].to_string(), SwqosType::Bloxroute => SWQOS_ENDPOINTS_BLOX[region as usize].to_string(), + SwqosType::Node1 => SWQOS_ENDPOINTS_NODE1[region as usize].to_string(), SwqosType::Default => "".to_string(), } } @@ -140,6 +144,15 @@ impl SwqosConfig { ); Arc::new(bloxroute_client) }, + SwqosConfig::Node1(auth_token, region) => { + let endpoint = SwqosConfig::get_endpoint(SwqosType::Node1, region); + let node1_client = Node1Client::new( + rpc_url.clone(), + endpoint.to_string(), + auth_token + ); + Arc::new(node1_client) + }, SwqosConfig::Default(endpoint) => { let rpc = SolanaRpcClient::new_with_commitment( endpoint, diff --git a/src/swqos/node1.rs b/src/swqos/node1.rs new file mode 100644 index 0000000..ac56c9b --- /dev/null +++ b/src/swqos/node1.rs @@ -0,0 +1,112 @@ +use crate::swqos::common::{poll_transaction_confirmation, serialize_transaction_and_encode}; +use rand::seq::IndexedRandom; +use reqwest::Client; +use serde_json::json; +use std::{sync::Arc, time::Instant}; + +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; + +use crate::{common::SolanaRpcClient, constants::swqos::NODE1_TIP_ACCOUNTS}; + + +#[derive(Clone)] +pub struct Node1Client { + pub endpoint: String, + pub auth_token: String, + pub rpc_client: Arc, + pub http_client: Client, +} + +#[async_trait::async_trait] +impl SwqosClientTrait for Node1Client { + 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_transactions(trade_type, transactions).await + } + + fn get_tip_account(&self) -> Result { + let tip_account = *NODE1_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| NODE1_TIP_ACCOUNTS.first()).unwrap(); + Ok(tip_account.to_string()) + } + + fn get_swqos_type(&self) -> SwqosType { + SwqosType::Node1 + } +} + +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() + .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<()> { + 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!({ + "jsonrpc": "2.0", + "id": 1, + "method": "sendTransaction", + "params": [ + content, + { "encoding": "base64", "skipPreflight": true } + ] + }))?; + + // Node1使用api-key header而不是URL参数 + let response_text = self.http_client.post(&self.endpoint) + .body(request_body) + .header("Content-Type", "application/json") + .header("api-key", &self.auth_token) + .send() + .await? + .text() + .await?; + + // 解析JSON响应 + if let Ok(response_json) = serde_json::from_str::(&response_text) { + if response_json.get("result").is_some() { + println!(" node1{}提交: {:?}", trade_type, start_time.elapsed()); + } else if let Some(_error) = response_json.get("error") { + eprintln!(" node1{}提交失败: {:?}", trade_type, _error); + } + } + + let start_time: Instant = Instant::now(); + match poll_transaction_confirmation(&self.rpc_client, signature).await { + Ok(_) => (), + Err(_) => (), + } + + println!(" node1{}确认: {:?}", trade_type, start_time.elapsed()); + + Ok(()) + } + + pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec) -> Result<()> { + for transaction in transactions { + self.send_transaction(trade_type, transaction).await?; + } + Ok(()) + } +}