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"),
|
pubkey!("ste11TMV68LMi1BguM4RQujtbNCZvf1sjsASpqgAvSX"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Lightspeed (Solana Vibe Station) tip accounts
|
||||||
|
pub const LIGHTSPEED_TIP_ACCOUNTS: &[Pubkey] = &[
|
||||||
|
pubkey!("53PhM3UTdMQWu5t81wcd35AHGc5xpmHoRjem7GQPvXjA"),
|
||||||
|
pubkey!("9tYF5yPDC1NP8s6diiB3kAX6ZZnva9DM3iDwJkBRarBB"),
|
||||||
|
];
|
||||||
|
|
||||||
// NewYork,
|
// NewYork,
|
||||||
// Frankfurt,
|
// Frankfurt,
|
||||||
// Amsterdam,
|
// 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_BLOCKRAZOR: f64 = 0.001;
|
||||||
pub const SWQOS_MIN_TIP_ASTRALANE: f64 = SWQOS_MIN_TIP_DEFAULT;
|
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_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 blockrazor;
|
||||||
pub mod astralane;
|
pub mod astralane;
|
||||||
pub mod stellium;
|
pub mod stellium;
|
||||||
|
pub mod lightspeed;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -45,7 +46,8 @@ use crate::{
|
|||||||
flashblock::FlashBlockClient,
|
flashblock::FlashBlockClient,
|
||||||
blockrazor::BlockRazorClient,
|
blockrazor::BlockRazorClient,
|
||||||
astralane::AstralaneClient,
|
astralane::AstralaneClient,
|
||||||
stellium::StelliumClient
|
stellium::StelliumClient,
|
||||||
|
lightspeed::LightspeedClient
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,6 +87,7 @@ pub enum SwqosType {
|
|||||||
BlockRazor,
|
BlockRazor,
|
||||||
Astralane,
|
Astralane,
|
||||||
Stellium,
|
Stellium,
|
||||||
|
Lightspeed,
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +104,7 @@ impl SwqosType {
|
|||||||
Self::BlockRazor,
|
Self::BlockRazor,
|
||||||
Self::Astralane,
|
Self::Astralane,
|
||||||
Self::Stellium,
|
Self::Stellium,
|
||||||
|
Self::Lightspeed,
|
||||||
Self::Default,
|
Self::Default,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -151,6 +155,10 @@ pub enum SwqosConfig {
|
|||||||
Astralane(String, SwqosRegion, Option<String>),
|
Astralane(String, SwqosRegion, Option<String>),
|
||||||
/// Stellium(api_token, region, custom_url)
|
/// Stellium(api_token, region, custom_url)
|
||||||
Stellium(String, SwqosRegion, Option<String>),
|
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 {
|
impl SwqosConfig {
|
||||||
@@ -170,6 +178,7 @@ impl SwqosConfig {
|
|||||||
SwqosType::BlockRazor => SWQOS_ENDPOINTS_BLOCKRAZOR[region as usize].to_string(),
|
SwqosType::BlockRazor => SWQOS_ENDPOINTS_BLOCKRAZOR[region as usize].to_string(),
|
||||||
SwqosType::Astralane => SWQOS_ENDPOINTS_ASTRALANE[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::Stellium => SWQOS_ENDPOINTS_STELLIUM[region as usize].to_string(),
|
||||||
|
SwqosType::Lightspeed => "".to_string(), // Lightspeed requires custom URL with api_key
|
||||||
SwqosType::Default => "".to_string(),
|
SwqosType::Default => "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,6 +275,15 @@ impl SwqosConfig {
|
|||||||
);
|
);
|
||||||
Arc::new(stellium_client)
|
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) => {
|
SwqosConfig::Default(endpoint) => {
|
||||||
let rpc = SolanaRpcClient::new_with_commitment(
|
let rpc = SolanaRpcClient::new_with_commitment(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use crate::{
|
|||||||
SWQOS_MIN_TIP_BLOCKRAZOR,
|
SWQOS_MIN_TIP_BLOCKRAZOR,
|
||||||
SWQOS_MIN_TIP_ASTRALANE,
|
SWQOS_MIN_TIP_ASTRALANE,
|
||||||
SWQOS_MIN_TIP_STELLIUM,
|
SWQOS_MIN_TIP_STELLIUM,
|
||||||
|
SWQOS_MIN_TIP_LIGHTSPEED,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -169,6 +170,7 @@ pub async fn execute_parallel(
|
|||||||
SwqosType::BlockRazor => SWQOS_MIN_TIP_BLOCKRAZOR,
|
SwqosType::BlockRazor => SWQOS_MIN_TIP_BLOCKRAZOR,
|
||||||
SwqosType::Astralane => SWQOS_MIN_TIP_ASTRALANE,
|
SwqosType::Astralane => SWQOS_MIN_TIP_ASTRALANE,
|
||||||
SwqosType::Stellium => SWQOS_MIN_TIP_STELLIUM,
|
SwqosType::Stellium => SWQOS_MIN_TIP_STELLIUM,
|
||||||
|
SwqosType::Lightspeed => SWQOS_MIN_TIP_LIGHTSPEED,
|
||||||
SwqosType::Default => SWQOS_MIN_TIP_DEFAULT,
|
SwqosType::Default => SWQOS_MIN_TIP_DEFAULT,
|
||||||
};
|
};
|
||||||
if config.2.tip < min_tip {
|
if config.2.tip < min_tip {
|
||||||
|
|||||||
Reference in New Issue
Block a user