Merge pull request #52 from HelvetiCrypt/main
feat(swqos): add Lightspeed (Solana Vibe Station) support
This commit is contained in:
@@ -117,6 +117,12 @@ pub const STELLIUM_TIP_ACCOUNTS: &[Pubkey] = &[
|
||||
pubkey!("ste11TMV68LMi1BguM4RQujtbNCZvf1sjsASpqgAvSX"),
|
||||
];
|
||||
|
||||
// Lightspeed (Solana Vibe Station) tip accounts
|
||||
pub const LIGHTSPEED_TIP_ACCOUNTS: &[Pubkey] = &[
|
||||
pubkey!("53PhM3UTdMQWu5t81wcd35AHGc5xpmHoRjem7GQPvXjA"),
|
||||
pubkey!("9tYF5yPDC1NP8s6diiB3kAX6ZZnva9DM3iDwJkBRarBB"),
|
||||
];
|
||||
|
||||
// NewYork,
|
||||
// Frankfurt,
|
||||
// Amsterdam,
|
||||
@@ -247,3 +253,4 @@ pub const SWQOS_MIN_TIP_FLASHBLOCK: f64 = 0.001;
|
||||
pub const SWQOS_MIN_TIP_BLOCKRAZOR: f64 = 0.001;
|
||||
pub const SWQOS_MIN_TIP_ASTRALANE: f64 = SWQOS_MIN_TIP_DEFAULT;
|
||||
pub const SWQOS_MIN_TIP_STELLIUM: f64 = 0.001; // Stellium requires minimum 0.001 SOL tip
|
||||
pub const SWQOS_MIN_TIP_LIGHTSPEED: f64 = 0.001; // Lightspeed requires minimum 0.001 SOL tip
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
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::LIGHTSPEED_TIP_ACCOUNTS};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LightspeedClient {
|
||||
pub endpoint: String,
|
||||
pub auth_token: String,
|
||||
pub rpc_client: Arc<SolanaRpcClient>,
|
||||
pub http_client: Client,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl SwqosClientTrait for LightspeedClient {
|
||||
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<VersionedTransaction>) -> Result<()> {
|
||||
self.send_transactions(trade_type, transactions).await
|
||||
}
|
||||
|
||||
fn get_tip_account(&self) -> Result<String> {
|
||||
let tip_account = *LIGHTSPEED_TIP_ACCOUNTS.choose(&mut rand::rng()).or_else(|| LIGHTSPEED_TIP_ACCOUNTS.first()).unwrap();
|
||||
Ok(tip_account.to_string())
|
||||
}
|
||||
|
||||
fn get_swqos_type(&self) -> SwqosType {
|
||||
SwqosType::Lightspeed
|
||||
}
|
||||
}
|
||||
|
||||
impl LightspeedClient {
|
||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
||||
// Lightspeed endpoint should already include /lightspeed path
|
||||
// Format: https://<tier>.rpc.solanavibestation.com/lightspeed?api_key=<key>
|
||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||
let http_client = Client::builder()
|
||||
// Optimized connection pool settings for high performance
|
||||
.pool_idle_timeout(Duration::from_secs(120))
|
||||
.pool_max_idle_per_host(256)
|
||||
.tcp_keepalive(Some(Duration::from_secs(60)))
|
||||
.tcp_nodelay(true) // Disable Nagle's algorithm for lower latency
|
||||
.http2_keep_alive_interval(Duration::from_secs(10))
|
||||
.http2_keep_alive_timeout(Duration::from_secs(5))
|
||||
.http2_adaptive_window(true) // Enable adaptive flow control
|
||||
.timeout(Duration::from_millis(3000))
|
||||
.connect_timeout(Duration::from_millis(2000))
|
||||
.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?;
|
||||
|
||||
// Lightspeed uses standard Solana JSON-RPC format for sendTransaction
|
||||
let request_body = serde_json::to_string(&json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "sendTransaction",
|
||||
"params": [
|
||||
content,
|
||||
{
|
||||
"encoding": "base64",
|
||||
"skipPreflight": true,
|
||||
"preflightCommitment": "processed",
|
||||
"maxRetries": 0
|
||||
}
|
||||
]
|
||||
}))?;
|
||||
|
||||
let response_text = self.http_client.post(&self.endpoint)
|
||||
.body(request_body)
|
||||
.header("Content-Type", "application/json")
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
if let Ok(response_json) = serde_json::from_str::<serde_json::Value>(&response_text) {
|
||||
if response_json.get("result").is_some() {
|
||||
println!(" [lightspeed] {} submitted: {:?}", trade_type, start_time.elapsed());
|
||||
} else if let Some(_error) = response_json.get("error") {
|
||||
eprintln!(" [lightspeed] {} submission failed: {:?}", trade_type, _error);
|
||||
}
|
||||
} else {
|
||||
eprintln!(" [lightspeed] {} 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!(" signature: {:?}", signature);
|
||||
println!(" [lightspeed] {} confirmation failed: {:?}", trade_type, start_time.elapsed());
|
||||
return Err(e);
|
||||
},
|
||||
}
|
||||
println!(" signature: {:?}", signature);
|
||||
println!(" [lightspeed] {} confirmed: {:?}", trade_type, start_time.elapsed());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>) -> Result<()> {
|
||||
for transaction in transactions {
|
||||
self.send_transaction(trade_type, transaction).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -11,6 +11,7 @@ pub mod flashblock;
|
||||
pub mod blockrazor;
|
||||
pub mod astralane;
|
||||
pub mod stellium;
|
||||
pub mod lightspeed;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -45,7 +46,8 @@ use crate::{
|
||||
flashblock::FlashBlockClient,
|
||||
blockrazor::BlockRazorClient,
|
||||
astralane::AstralaneClient,
|
||||
stellium::StelliumClient
|
||||
stellium::StelliumClient,
|
||||
lightspeed::LightspeedClient
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,6 +87,7 @@ pub enum SwqosType {
|
||||
BlockRazor,
|
||||
Astralane,
|
||||
Stellium,
|
||||
Lightspeed,
|
||||
Default,
|
||||
}
|
||||
|
||||
@@ -101,6 +104,7 @@ impl SwqosType {
|
||||
Self::BlockRazor,
|
||||
Self::Astralane,
|
||||
Self::Stellium,
|
||||
Self::Lightspeed,
|
||||
Self::Default,
|
||||
]
|
||||
}
|
||||
@@ -151,6 +155,10 @@ pub enum SwqosConfig {
|
||||
Astralane(String, SwqosRegion, Option<String>),
|
||||
/// Stellium(api_token, region, custom_url)
|
||||
Stellium(String, SwqosRegion, Option<String>),
|
||||
/// Lightspeed(api_key, region, custom_url) - Solana Vibe Station
|
||||
/// Endpoint format: https://<tier>.rpc.solanavibestation.com/lightspeed?api_key=<key>
|
||||
/// Minimum tip: 0.001 SOL
|
||||
Lightspeed(String, SwqosRegion, Option<String>),
|
||||
}
|
||||
|
||||
impl SwqosConfig {
|
||||
@@ -170,6 +178,7 @@ impl SwqosConfig {
|
||||
SwqosType::BlockRazor => SWQOS_ENDPOINTS_BLOCKRAZOR[region as usize].to_string(),
|
||||
SwqosType::Astralane => SWQOS_ENDPOINTS_ASTRALANE[region as usize].to_string(),
|
||||
SwqosType::Stellium => SWQOS_ENDPOINTS_STELLIUM[region as usize].to_string(),
|
||||
SwqosType::Lightspeed => "".to_string(), // Lightspeed requires custom URL with api_key
|
||||
SwqosType::Default => "".to_string(),
|
||||
}
|
||||
}
|
||||
@@ -266,6 +275,15 @@ impl SwqosConfig {
|
||||
);
|
||||
Arc::new(stellium_client)
|
||||
},
|
||||
SwqosConfig::Lightspeed(auth_token, region, url) => {
|
||||
let endpoint = SwqosConfig::get_endpoint(SwqosType::Lightspeed, region, url);
|
||||
let lightspeed_client = LightspeedClient::new(
|
||||
rpc_url.clone(),
|
||||
endpoint.to_string(),
|
||||
auth_token
|
||||
);
|
||||
Arc::new(lightspeed_client)
|
||||
},
|
||||
SwqosConfig::Default(endpoint) => {
|
||||
let rpc = SolanaRpcClient::new_with_commitment(
|
||||
endpoint,
|
||||
|
||||
@@ -27,6 +27,7 @@ use crate::{
|
||||
SWQOS_MIN_TIP_BLOCKRAZOR,
|
||||
SWQOS_MIN_TIP_ASTRALANE,
|
||||
SWQOS_MIN_TIP_STELLIUM,
|
||||
SWQOS_MIN_TIP_LIGHTSPEED,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -169,6 +170,7 @@ pub async fn execute_parallel(
|
||||
SwqosType::BlockRazor => SWQOS_MIN_TIP_BLOCKRAZOR,
|
||||
SwqosType::Astralane => SWQOS_MIN_TIP_ASTRALANE,
|
||||
SwqosType::Stellium => SWQOS_MIN_TIP_STELLIUM,
|
||||
SwqosType::Lightspeed => SWQOS_MIN_TIP_LIGHTSPEED,
|
||||
SwqosType::Default => SWQOS_MIN_TIP_DEFAULT,
|
||||
};
|
||||
if config.2.tip < min_tip {
|
||||
|
||||
Reference in New Issue
Block a user