diff --git a/README.md b/README.md index b6e8000..5e1eab7 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ This release refreshes PumpFun native-SOL quote handling so SOL/WSOL sentinels p 4. **Raydium CPMM Trading**: Support for Raydium CPMM (Concentrated Pool Market Maker) trading operations 5. **Raydium AMM V4 Trading**: Support for Raydium AMM V4 (Automated Market Maker) trading operations 6. **Meteora DAMM V2 Trading**: Support for Meteora DAMM V2 (Dynamic AMM) trading operations -7. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, FlashBlock, BlockRazor, Node1, Astralane and other services +7. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, FlashBlock, BlockRazor, Node1, Astralane, LunarLander and other services 8. **Concurrent Trading**: Submit through every configured SWQoS provider plus the default RPC lane; the first accepted result can return early while slower routes continue submitting 9. **Unified Trading Interface**: Use unified trading protocol enums for trading operations 10. **Middleware System**: Support for custom instruction middleware to modify, add, or remove instructions before transaction execution @@ -161,6 +161,7 @@ let swqos_configs: Vec = vec![ // Astralane: 4th param = AstralaneTransport — Binary (default), Plain (/iris), or Quic SwqosConfig::Astralane("your_astralane_api_key".to_string(), SwqosRegion::Frankfurt, None, None), // Binary HTTP /irisb SwqosConfig::SpeedLanding("your api_token".to_string(), SwqosRegion::Frankfurt, None), + SwqosConfig::LunarLander("your_hellomoon_api_key".to_string(), SwqosRegion::Frankfurt, None), ]; // Create TradeConfig instance let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment) @@ -508,6 +509,8 @@ You can apply for a key through the official website: [Community Website](https: - **BlockRazor**: High-speed transaction execution with API key authentication - **Astralane**: Blockchain network acceleration (Binary/Plain HTTP and QUIC) - **SpeedLanding**: High-speed transaction execution with API key authentication +- **Node1**: High-speed transaction execution with API key authentication +- **LunarLander**: HelloMoon transaction landing service (minimum tip: 0.001 SOL) ## 📁 Project Structure diff --git a/README_CN.md b/README_CN.md index eb5d3b0..c8e2627 100755 --- a/README_CN.md +++ b/README_CN.md @@ -104,7 +104,7 @@ 4. **Raydium CPMM 交易**: 支持 Raydium CPMM (Concentrated Pool Market Maker) 的交易操作 5. **Raydium AMM V4 交易**: 支持 Raydium AMM V4 (Automated Market Maker) 的交易操作 6. **Meteora DAMM V2 交易**: 支持 Meteora DAMM V2 (Dynamic AMM) 的交易操作 -7. **多种 MEV 保护**: 支持 Jito、Temporal、FlashBlock、BlockRazor、Astralane、SpeedLanding 等服务 +7. **多种 MEV 保护**: 支持 Jito、Nextblock、ZeroSlot、Temporal、Bloxroute、FlashBlock、BlockRazor、Node1、Astralane、SpeedLanding、LunarLander 等服务 8. **并发交易**: 所有已配置的 SWQoS 通道和默认 RPC 通道都会发出提交;首个成功只影响返回,较慢通道会继续提交 9. **统一交易接口**: 使用统一的交易协议枚举进行交易操作 10. **中间件系统**: 支持自定义指令中间件,可在交易执行前对指令进行修改、添加或移除 @@ -161,6 +161,7 @@ let swqos_configs: Vec = vec![ // Astralane:第4个参数为 AstralaneTransport — Binary(默认)、Plain(/iris)或 Quic SwqosConfig::Astralane("your_astralane_api_key".to_string(), SwqosRegion::Frankfurt, None, None), // Binary /irisb SwqosConfig::SpeedLanding("your api_token".to_string(), SwqosRegion::Frankfurt, None), + SwqosConfig::LunarLander("your_hellomoon_api_key".to_string(), SwqosRegion::Frankfurt, None), ]; // 创建 TradeConfig 实例 let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment) @@ -505,6 +506,8 @@ legacy SOL 事件里如果 `quote_mint` 是默认值或 Solscan SOL,并且 quo - **BlockRazor**: 高速交易执行,支持 API 密钥认证 - **Astralane**: 区块链网络加速(Binary/Plain HTTP 与 QUIC) - **SpeedLanding**: 高速交易执行,支持 API 密钥认证 +- **Node1**: 高速交易执行,支持 API 密钥认证 +- **LunarLander**: HelloMoon 交易着陆服务(最低小费:0.001 SOL) ## 📁 项目结构 diff --git a/src/constants/swqos.rs b/src/constants/swqos.rs index e7eba5d..c192072 100755 --- a/src/constants/swqos.rs +++ b/src/constants/swqos.rs @@ -184,6 +184,20 @@ pub const SOLAMI_TIP_ACCOUNTS: &[Pubkey] = &[ pubkey!("uiuaQsxA47JybQAVN4FTfYuoEDkMiXV1r591Aewbeam"), ]; +// LunarLander (HelloMoon) tip accounts +pub const LUNARLANDER_TIP_ACCOUNTS: &[Pubkey] = &[ + pubkey!("moon17L6BgxXRX5uHKudAmqVF96xia9h8ygcmG2sL3F"), + pubkey!("moon26Sek222Md7ZydcAGxoKG832DK36CkLrS3PQY4c"), + pubkey!("moon7fwyajcVstMoBnVy7UBcTx87SBtNoGGAaH2Cb8V"), + pubkey!("moonBtH9HvLHjLqi9ivyrMVKgFUsSfrz9BwQ9khhn1u"), + pubkey!("moonCJg8476LNFLptX1qrK8PdRsA1HD1R6XWyu9MB93"), + pubkey!("moonF2sz7qwAtdETnrgxNbjonnhGGjd6r4W4UC9284s"), + pubkey!("moonKfftMiGSak3cezvhEqvkPSzwrmQxQHXuspC96yj"), + pubkey!("moonQBUKBpkifLcTd78bfxxt4PYLwmJ5admLW6cBBs8"), + pubkey!("moonXwpKwoVkMegt5Bc776cSW793X1irL5hHV1vJ3JA"), + pubkey!("moonZ6u9E2fgk6eWd82621eLPHt9zuJuYECXAYjMY1C"), +]; + // `SwqosRegion` 与下列各 `SWQOS_ENDPOINTS_*` 下标严格对应(共 10 项): // 0 NewYork, 1 Frankfurt, 2 Amsterdam, 3 Dublin, 4 SLC, 5 Tokyo, 6 Singapore, 7 London, 8 LosAngeles, 9 Default。 // @@ -433,6 +447,21 @@ pub const SWQOS_ENDPOINTS_SPEEDLANDING: [&str; 10] = [ "fra.speedlanding.trade:17778", // Default: 非地理区域;EU 枢纽 ]; +/// HelloMoon Lunar Lander: JSON-RPC sendTransaction, auth via ?api-key= query param. +/// `SwqosRegion` order; regions without a dedicated published PoP use the nearest available host. +pub const SWQOS_ENDPOINTS_LUNARLANDER: [&str; 10] = [ + "http://nyc.lunar-lander.hellomoon.io/send", // NewYork + "http://fra.lunar-lander.hellomoon.io/send", // Frankfurt + "http://ams.lunar-lander.hellomoon.io/send", // Amsterdam + "http://ams.lunar-lander.hellomoon.io/send", // Dublin -> Amsterdam + "http://ash.lunar-lander.hellomoon.io/send", // SLC -> Ashburn + "http://tyo.lunar-lander.hellomoon.io/send", // Tokyo + "http://tyo.lunar-lander.hellomoon.io/send", // Singapore -> Tokyo + "http://fra.lunar-lander.hellomoon.io/send", // London -> Frankfurt + "http://nyc.lunar-lander.hellomoon.io/send", // LosAngeles -> NYC + "http://lunar-lander.hellomoon.io/send", // Default +]; + /// Helius Sender: POST /fast, dual routing to validators and Jito. API key optional (custom TPS only). pub const SWQOS_ENDPOINTS_HELIUS: [&str; 10] = [ "http://ewr-sender.helius-rpc.com/fast", @@ -477,6 +506,7 @@ pub const SWQOS_MIN_TIP_SPEEDLANDING: f64 = 0.001; // Speedlanding requires mini /// Helius Sender: 0.0002 SOL when not swqos_only; use SWQOS_MIN_TIP_HELIUS_SWQOS_ONLY when swqos_only=true. pub const SWQOS_MIN_TIP_HELIUS: f64 = 0.0002; pub const SWQOS_MIN_TIP_SOLAMI: f64 = 0.0001; +pub const SWQOS_MIN_TIP_LUNARLANDER: f64 = 0.001; // LunarLander 最低小费 0.001 SOL /// Helius Sender with swqos_only: minimum 0.000005 SOL (much lower tip allowed). pub const SWQOS_MIN_TIP_HELIUS_SWQOS_ONLY: f64 = 0.000005; @@ -503,6 +533,7 @@ mod tests { &SWQOS_ENDPOINTS_SOYAS, &SWQOS_ENDPOINTS_SPEEDLANDING, &SWQOS_ENDPOINTS_HELIUS, + &SWQOS_ENDPOINTS_LUNARLANDER, &SWQOS_ENDPOINTS_SOLAMI, ]; diff --git a/src/swqos/lunarlander.rs b/src/swqos/lunarlander.rs new file mode 100644 index 0000000..cb13cdc --- /dev/null +++ b/src/swqos/lunarlander.rs @@ -0,0 +1,298 @@ +//! HelloMoon Lunar Lander SWQOS client. +//! +//! High-performance transaction landing service with keep-alive ping. +//! Auth via `?api-key=` query parameter. Minimum tip: 0.001 SOL. + +use crate::swqos::common::{ + default_http_client_builder, poll_transaction_confirmation, serialize_transaction_and_encode, +}; +use rand::seq::IndexedRandom; +use reqwest::Client; +use serde_json::json; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::{sync::Arc, time::Instant}; + +use solana_transaction_status::UiTransactionEncoding; +use std::time::Duration; + +use crate::swqos::SwqosClientTrait; +use crate::swqos::{SwqosType, TradeType}; +use anyhow::Result; +use solana_sdk::transaction::VersionedTransaction; + +use crate::{common::SolanaRpcClient, constants::swqos::LUNARLANDER_TIP_ACCOUNTS}; + +#[derive(Clone)] +pub struct LunarLanderClient { + pub endpoint: String, + pub api_key: String, + pub rpc_client: Arc, + pub http_client: Client, + stop_ping: Arc, +} + +#[async_trait::async_trait] +impl SwqosClientTrait for LunarLanderClient { + async fn send_transaction( + &self, + trade_type: TradeType, + transaction: &VersionedTransaction, + wait_confirmation: bool, + ) -> Result<()> { + self.send_transaction(trade_type, transaction, wait_confirmation).await + } + + async fn send_transactions( + &self, + trade_type: TradeType, + transactions: &Vec, + wait_confirmation: bool, + ) -> Result<()> { + self.send_transactions(trade_type, transactions, wait_confirmation).await + } + + fn get_tip_account(&self) -> Result { + let tip_account = *LUNARLANDER_TIP_ACCOUNTS + .choose(&mut rand::rng()) + .or_else(|| LUNARLANDER_TIP_ACCOUNTS.first()) + .unwrap(); + Ok(tip_account.to_string()) + } + + fn get_swqos_type(&self) -> SwqosType { + SwqosType::LunarLander + } +} + +impl LunarLanderClient { + /// Derive the ping URL from the send endpoint by replacing the last path segment. + fn ping_url(endpoint: &str, api_key: &str) -> String { + // Find the last '/' that is part of the path (after "://") + let scheme_end = endpoint.find("://").map(|i| i + 3).unwrap_or(0); + let base = endpoint[scheme_end..] + .rfind('/') + .map(|i| &endpoint[..scheme_end + i]) + .unwrap_or(endpoint); + format!("{}/ping?api-key={}", base, api_key) + } + + /// Derive the send URL with the API key query parameter. + fn send_url(endpoint: &str, api_key: &str) -> String { + let separator = if endpoint.contains('?') { '&' } else { '?' }; + format!("{}{}api-key={}", endpoint, separator, api_key) + } + + pub fn new(rpc_url: String, endpoint: String, api_key: String) -> Self { + let rpc_client = SolanaRpcClient::new(rpc_url); + let http_client = default_http_client_builder().build().unwrap(); + + let stop_ping = Arc::new(AtomicBool::new(false)); + + let client = Self { + rpc_client: Arc::new(rpc_client), + endpoint: endpoint.clone(), + api_key: api_key.clone(), + http_client: http_client.clone(), + stop_ping: stop_ping.clone(), + }; + + // Start ping task + let client_clone = client.clone(); + tokio::spawn(async move { + client_clone.start_ping_task().await; + }); + + client + } + + /// Start periodic ping task to keep connections active. + /// GET /ping every 30s on the same TCP connection (recommended 30-45s by HelloMoon). + async fn start_ping_task(&self) { + let ping_url = Self::ping_url(&self.endpoint, &self.api_key); + let http_client = self.http_client.clone(); + let stop_ping = self.stop_ping.clone(); + + tokio::spawn(async move { + // Immediate first ping to warm connection and reduce first-submit cold start latency + if let Ok(resp) = + http_client.get(&ping_url).timeout(Duration::from_millis(1500)).send().await + { + let status = resp.status(); + let _ = resp.bytes().await; + if !status.is_success() && crate::common::sdk_log::sdk_log_enabled() { + eprintln!(" [lunarlander] ping failed with status: {}", status); + } + } + let mut interval = tokio::time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + if stop_ping.load(Ordering::Relaxed) { + break; + } + match http_client.get(&ping_url).timeout(Duration::from_millis(1500)).send().await { + Ok(response) => { + let status = response.status(); + let _ = response.bytes().await; + if !status.is_success() && crate::common::sdk_log::sdk_log_enabled() { + eprintln!(" [lunarlander] ping failed with status: {}", status); + } + } + Err(e) => { + if crate::common::sdk_log::sdk_log_enabled() { + eprintln!(" [lunarlander] ping request error: {:?}", e); + } + } + } + } + }); + } + + pub async fn send_transaction( + &self, + trade_type: TradeType, + transaction: &VersionedTransaction, + wait_confirmation: bool, + ) -> Result<()> { + let start_time = Instant::now(); + let (content, signature) = + serialize_transaction_and_encode(transaction, UiTransactionEncoding::Base64)?; + + let request_body = serde_json::to_string(&json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "sendTransaction", + "params": [ + content, + { "encoding": "base64" } + ] + }))?; + + let url = Self::send_url(&self.endpoint, &self.api_key); + + let response_text = self + .http_client + .post(&url) + .body(request_body) + .header("Content-Type", "application/json") + .header("Connection", "keep-alive") + .header("Keep-Alive", "timeout=30, max=1000") + .send() + .await? + .text() + .await?; + + // Parse response + if let Ok(response_json) = serde_json::from_str::(&response_text) { + if crate::common::sdk_log::sdk_log_enabled() { + if response_json.get("result").is_some() { + println!(" [lunarlander] {} submitted: {:?}", trade_type, start_time.elapsed()); + } else if let Some(error) = response_json.get("error") { + eprintln!(" [lunarlander] {} submission failed: {:?}", trade_type, error); + } + } + } else if crate::common::sdk_log::sdk_log_enabled() { + eprintln!(" [lunarlander] {} submission failed: {:?}", trade_type, response_text); + } + + let start_time: Instant = Instant::now(); + match poll_transaction_confirmation(&self.rpc_client, signature, wait_confirmation).await { + Ok(_) => (), + Err(e) => { + if crate::common::sdk_log::sdk_log_enabled() { + println!(" signature: {:?}", signature); + println!( + " [lunarlander] {} confirmation failed: {:?}", + trade_type, + start_time.elapsed() + ); + } + return Err(e); + } + } + if wait_confirmation && crate::common::sdk_log::sdk_log_enabled() { + println!(" signature: {:?}", signature); + println!(" [lunarlander] {} confirmed: {:?}", trade_type, start_time.elapsed()); + } + + Ok(()) + } + + pub async fn send_transactions( + &self, + trade_type: TradeType, + transactions: &Vec, + wait_confirmation: bool, + ) -> Result<()> { + for transaction in transactions { + self.send_transaction(trade_type, transaction, wait_confirmation).await?; + } + Ok(()) + } + + /// Stop the ping task + pub fn stop_ping_task(&self) { + self.stop_ping.store(true, Ordering::Relaxed); + } +} + +impl Drop for LunarLanderClient { + fn drop(&mut self) { + // Stop ping task when client is dropped + self.stop_ping.store(true, Ordering::Relaxed); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ping_url_default_endpoint() { + let url = + LunarLanderClient::ping_url("http://nyc.lunar-lander.hellomoon.io/send", "test-key"); + assert_eq!(url, "http://nyc.lunar-lander.hellomoon.io/ping?api-key=test-key"); + } + + #[test] + fn test_ping_url_geolocated_endpoint() { + let url = LunarLanderClient::ping_url("http://lunar-lander.hellomoon.io/send", "test-key"); + assert_eq!(url, "http://lunar-lander.hellomoon.io/ping?api-key=test-key"); + } + + #[test] + fn test_ping_url_custom_with_nested_path() { + let url = LunarLanderClient::ping_url("http://proxy.example.com/v2/lunar/send", "key123"); + assert_eq!(url, "http://proxy.example.com/v2/lunar/ping?api-key=key123"); + } + + #[test] + fn test_ping_url_custom_pathless() { + // Custom URL with no path — should append /ping to the host + let url = LunarLanderClient::ping_url("http://proxy.example.com", "key123"); + assert_eq!(url, "http://proxy.example.com/ping?api-key=key123"); + } + + #[test] + fn test_send_url_simple() { + let url = + LunarLanderClient::send_url("http://nyc.lunar-lander.hellomoon.io/send", "test-key"); + assert_eq!(url, "http://nyc.lunar-lander.hellomoon.io/send?api-key=test-key"); + } + + #[test] + fn test_send_url_existing_query_params() { + let url = LunarLanderClient::send_url("http://proxy.example.com/send?foo=bar", "test-key"); + assert_eq!(url, "http://proxy.example.com/send?foo=bar&api-key=test-key"); + } + + #[test] + fn test_tip_account_is_valid() { + // Verify the tip accounts array is non-empty and accounts parse as pubkeys + assert!(!LUNARLANDER_TIP_ACCOUNTS.is_empty()); + for account in LUNARLANDER_TIP_ACCOUNTS { + // If these were invalid, the pubkey! macro would fail at compile time, + // but verify they are 32 bytes + assert_eq!(account.to_bytes().len(), 32); + } + } +} diff --git a/src/swqos/mod.rs b/src/swqos/mod.rs index a6e3ad4..646f0fb 100755 --- a/src/swqos/mod.rs +++ b/src/swqos/mod.rs @@ -7,6 +7,7 @@ pub mod flashblock; pub mod helius; pub mod jito; pub mod lightspeed; +pub mod lunarlander; pub mod nextblock; pub mod node1; pub mod node1_quic; @@ -33,22 +34,23 @@ use crate::{ SWQOS_ENDPOINTS_ASTRALANE_QUIC, SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV, SWQOS_ENDPOINTS_BLOCKRAZOR, SWQOS_ENDPOINTS_BLOCKRAZOR_GRPC, SWQOS_ENDPOINTS_BLOX, SWQOS_ENDPOINTS_FLASHBLOCK, SWQOS_ENDPOINTS_HELIUS, SWQOS_ENDPOINTS_JITO, - SWQOS_ENDPOINTS_NEXTBLOCK, SWQOS_ENDPOINTS_NODE1, SWQOS_ENDPOINTS_NODE1_QUIC, - SWQOS_ENDPOINTS_SOLAMI, SWQOS_ENDPOINTS_SOYAS, SWQOS_ENDPOINTS_SPEEDLANDING, - SWQOS_ENDPOINTS_STELLIUM, SWQOS_ENDPOINTS_TEMPORAL, SWQOS_ENDPOINTS_ZERO_SLOT, - SWQOS_MIN_TIP_ASTRALANE, SWQOS_MIN_TIP_BLOCKRAZOR, SWQOS_MIN_TIP_BLOXROUTE, - SWQOS_MIN_TIP_DEFAULT, SWQOS_MIN_TIP_FLASHBLOCK, SWQOS_MIN_TIP_HELIUS, SWQOS_MIN_TIP_JITO, - SWQOS_MIN_TIP_LIGHTSPEED, SWQOS_MIN_TIP_NEXTBLOCK, SWQOS_MIN_TIP_NODE1, + SWQOS_ENDPOINTS_LUNARLANDER, SWQOS_ENDPOINTS_NEXTBLOCK, SWQOS_ENDPOINTS_NODE1, + SWQOS_ENDPOINTS_NODE1_QUIC, SWQOS_ENDPOINTS_SOLAMI, SWQOS_ENDPOINTS_SOYAS, + SWQOS_ENDPOINTS_SPEEDLANDING, SWQOS_ENDPOINTS_STELLIUM, SWQOS_ENDPOINTS_TEMPORAL, + SWQOS_ENDPOINTS_ZERO_SLOT, SWQOS_MIN_TIP_ASTRALANE, SWQOS_MIN_TIP_BLOCKRAZOR, + SWQOS_MIN_TIP_BLOXROUTE, SWQOS_MIN_TIP_DEFAULT, SWQOS_MIN_TIP_FLASHBLOCK, + SWQOS_MIN_TIP_HELIUS, SWQOS_MIN_TIP_JITO, SWQOS_MIN_TIP_LIGHTSPEED, + SWQOS_MIN_TIP_LUNARLANDER, SWQOS_MIN_TIP_NEXTBLOCK, SWQOS_MIN_TIP_NODE1, SWQOS_MIN_TIP_SOLAMI, SWQOS_MIN_TIP_SOYAS, SWQOS_MIN_TIP_SPEEDLANDING, SWQOS_MIN_TIP_STELLIUM, SWQOS_MIN_TIP_TEMPORAL, SWQOS_MIN_TIP_ZERO_SLOT, }, swqos::{ astralane::AstralaneClient, blockrazor::BlockRazorClient, bloxroute::BloxrouteClient, flashblock::FlashBlockClient, helius::HeliusClient, jito::JitoClient, - lightspeed::LightspeedClient, nextblock::NextBlockClient, node1::Node1Client, - node1_quic::Node1QuicClient, solami::SolamiClient, solana_rpc::SolRpcClient, - soyas::SoyasClient, speedlanding::SpeedlandingClient, stellium::StelliumClient, - temporal::TemporalClient, zeroslot::ZeroSlotClient, + lightspeed::LightspeedClient, lunarlander::LunarLanderClient, nextblock::NextBlockClient, + node1::Node1Client, node1_quic::Node1QuicClient, solami::SolamiClient, + solana_rpc::SolRpcClient, soyas::SoyasClient, speedlanding::SpeedlandingClient, + stellium::StelliumClient, temporal::TemporalClient, zeroslot::ZeroSlotClient, }, }; @@ -123,6 +125,7 @@ pub enum SwqosType { Speedlanding, Helius, Solami, + LunarLander, Default, } @@ -146,6 +149,7 @@ impl SwqosType { Self::Speedlanding => "Speedlanding", Self::Helius => "Helius", Self::Solami => "Solami", + Self::LunarLander => "LunarLander", Self::Default => "Default", } } @@ -167,6 +171,7 @@ impl SwqosType { Self::Speedlanding, Self::Helius, Self::Solami, + Self::LunarLander, Self::Default, ] } @@ -209,6 +214,7 @@ pub trait SwqosClientTrait { SwqosType::Speedlanding => SWQOS_MIN_TIP_SPEEDLANDING, SwqosType::Helius => SWQOS_MIN_TIP_HELIUS, SwqosType::Solami => SWQOS_MIN_TIP_SOLAMI, + SwqosType::LunarLander => SWQOS_MIN_TIP_LUNARLANDER, SwqosType::Default => SWQOS_MIN_TIP_DEFAULT, } } @@ -271,6 +277,9 @@ pub enum SwqosConfig { Helius(String, SwqosRegion, Option, Option), /// Solami(api_key, region, custom_url) Solami(String, SwqosRegion, Option), + /// LunarLander(api_key, region, custom_url) - HelloMoon Lunar Lander + /// Minimum tip: 0.001 SOL + LunarLander(String, SwqosRegion, Option), } impl SwqosConfig { @@ -292,6 +301,7 @@ impl SwqosConfig { SwqosConfig::Speedlanding(_, _, _) => SwqosType::Speedlanding, SwqosConfig::Helius(_, _, _, _) => SwqosType::Helius, SwqosConfig::Solami(_, _, _) => SwqosType::Solami, + SwqosConfig::LunarLander(_, _, _) => SwqosType::LunarLander, } } @@ -321,6 +331,7 @@ impl SwqosConfig { SwqosType::Speedlanding => SWQOS_ENDPOINTS_SPEEDLANDING[region as usize].to_string(), SwqosType::Helius => SWQOS_ENDPOINTS_HELIUS[region as usize].to_string(), SwqosType::Solami => SWQOS_ENDPOINTS_SOLAMI[region as usize].to_string(), + SwqosType::LunarLander => SWQOS_ENDPOINTS_LUNARLANDER[region as usize].to_string(), SwqosType::Default => "".to_string(), } } @@ -527,6 +538,12 @@ impl SwqosConfig { SolamiClient::new(rpc_url.clone(), endpoint.to_string(), auth_token).await?; Ok(Arc::new(solami_client)) } + SwqosConfig::LunarLander(api_key, region, url) => { + let endpoint = SwqosConfig::get_endpoint(SwqosType::LunarLander, region, url); + let lunarlander_client = + LunarLanderClient::new(rpc_url.clone(), endpoint.to_string(), api_key); + Ok(Arc::new(lunarlander_client)) + } SwqosConfig::Default(endpoint) => { let rpc = SolanaRpcClient::new_with_commitment(endpoint, commitment); let rpc_client = SolRpcClient::new(Arc::new(rpc));