feat(swqos): align endpoint tables with SwqosRegion and document fallbacks
- Extend SWQOS regional constants to 10 entries (Dublin, Singapore, Default) so indices match SwqosRegion 0..9 everywhere - Document geographic nearest-fill among published PoPs; call out Default and single-PoP provider constraints in comments - Fix Node1 QUIC Default endpoint to match HTTP; tune ZeroSlot/Blox/Speedlanding regional mappings where a closer published PoP exists - Add unit tests for table length and Astralane/Node1 cross-table consistency - Update SwqosRegion rustdoc, README, and trading_client example accordingly Made-with: Cursor
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
- [⚡ Trading Parameters](#-trading-parameters)
|
||||
- [📊 Usage Examples Summary Table](#-usage-examples-summary-table)
|
||||
- [⚙️ SWQoS Service Configuration](#️-swqos-service-configuration)
|
||||
- [Astralane QUIC (Low-Latency)](#astralane-quic-low-latency)
|
||||
- [Astralane (Binary / Plain / QUIC)](#astralane-binary--plain--quic)
|
||||
- [🔧 Middleware System](#-middleware-system)
|
||||
- [🔍 Address Lookup Tables](#-address-lookup-tables)
|
||||
- [🔍 Nonce Cache](#-nonce-cache)
|
||||
@@ -131,13 +131,13 @@ let swqos_configs: Vec<SwqosConfig> = vec![
|
||||
SwqosConfig::Default(rpc_url.clone()),
|
||||
SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt, None),
|
||||
SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt, None),
|
||||
// Astralane: HTTP (4th param None) or QUIC (Some(SwqosTransport::Quic)); same API key
|
||||
SwqosConfig::Astralane("your_astralane_api_key".to_string(), SwqosRegion::Frankfurt, None, None), // HTTP
|
||||
// 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::Astralane(
|
||||
"your_astralane_api_key".to_string(),
|
||||
SwqosRegion::Frankfurt,
|
||||
None,
|
||||
Some(SwqosTransport::Quic),
|
||||
Some(AstralaneTransport::Quic),
|
||||
), // QUIC
|
||||
];
|
||||
// Create TradeConfig instance
|
||||
@@ -147,7 +147,7 @@ let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
|
||||
// .log_enabled(true) // default: true - SDK timing / SWQOS logs
|
||||
// .check_min_tip(false) // default: false - filter SWQOS below min tip
|
||||
// .swqos_cores_from_end(false) // default: false - bind SWQOS to last N CPU cores
|
||||
// .mev_protection(false) // default: false - MEV protection (Astralane port 9000 / BlockRazor sandwichMitigation)
|
||||
// .mev_protection(false) // default: false - MEV (Astralane QUIC :9000 or HTTP mev-protect / BlockRazor)
|
||||
.build();
|
||||
|
||||
// Create TradingClient
|
||||
@@ -280,28 +280,28 @@ let bloxroute_config = SwqosConfig::Bloxroute(
|
||||
|
||||
When using multiple MEV services, you need to use `Durable Nonce`. You need to use the `fetch_nonce_info` function to get the latest `nonce` value, and use it as the `durable_nonce` when trading.
|
||||
|
||||
#### Astralane QUIC (Low-Latency)
|
||||
#### Astralane (Binary / Plain HTTP / QUIC)
|
||||
|
||||
Astralane supports both HTTP and **QUIC** transport. QUIC reduces connection overhead and can lower submission latency. To use the QUIC channel, pass `Some(SwqosTransport::Quic)` as the fourth parameter of `SwqosConfig::Astralane`. Astralane’s QUIC service uses a **single endpoint** (no per-region endpoints); the SDK ignores the `region` (and optional custom URL) when QUIC is selected. You can pass the same region as your other SWQoS configs for consistency.
|
||||
Astralane supports **Binary** HTTP (`/irisb`), **Plain** HTTP (`/iris`), and **QUIC** (`host:7000`, or `:9000` when global `mev_protection` is true). Pass `Some(AstralaneTransport::Plain)`, `Some(AstralaneTransport::Quic)`, or use `None` / omit for **Binary** (default). Global `mev_protection` adds `mev-protect=true` on HTTP or selects QUIC port 9000.
|
||||
|
||||
```rust
|
||||
use sol_trade_sdk::{SwqosConfig, SwqosRegion, SwqosTransport};
|
||||
use sol_trade_sdk::{SwqosConfig, SwqosRegion, AstralaneTransport};
|
||||
|
||||
// Astralane over QUIC (low-latency); region is ignored (single QUIC endpoint)
|
||||
let swqos_configs: Vec<SwqosConfig> = vec![
|
||||
SwqosConfig::Default(rpc_url.clone()),
|
||||
SwqosConfig::Astralane(
|
||||
"your_astralane_api_key".to_string(),
|
||||
SwqosRegion::Frankfurt, // same as other services; ignored for QUIC
|
||||
SwqosRegion::Frankfurt,
|
||||
None,
|
||||
Some(SwqosTransport::Quic),
|
||||
Some(AstralaneTransport::Quic),
|
||||
),
|
||||
];
|
||||
// Then create TradeConfig / TradingClient as usual with swqos_configs
|
||||
```
|
||||
|
||||
- **HTTP** (default): use `None` or `Some(SwqosTransport::Http)`; region and optional custom URL apply.
|
||||
- **QUIC**: use `Some(SwqosTransport::Quic)`; the SDK uses a single QUIC endpoint and ignores region. Same API key as HTTP.
|
||||
- **Binary** (default): `None` or `Some(AstralaneTransport::Binary)` — `/irisb`, bincode body.
|
||||
- **Plain**: `Some(AstralaneTransport::Plain)` — `/iris`.
|
||||
- **QUIC**: `Some(AstralaneTransport::Quic)` — regional `host:7000` / `:9000` (MEV); same API key.
|
||||
|
||||
---
|
||||
|
||||
@@ -364,7 +364,7 @@ You can apply for a key through the official website: [Community Website](https:
|
||||
- **FlashBlock**: High-speed transaction execution with API key authentication
|
||||
- **BlockRazor**: High-speed transaction execution with API key authentication
|
||||
- **Node1**: High-speed transaction execution with API key authentication
|
||||
- **Astralane**: Blockchain network acceleration (supports HTTP and QUIC; see [Astralane QUIC](#astralane-quic-low-latency) above)
|
||||
- **Astralane**: Blockchain network acceleration (Binary/Plain HTTP and QUIC; see [Astralane](#astralane-binary--plain--quic) above)
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
|
||||
+14
-14
@@ -48,7 +48,7 @@
|
||||
- [⚡ 交易参数](#-交易参数)
|
||||
- [📊 使用示例汇总表格](#-使用示例汇总表格)
|
||||
- [⚙️ SWQoS 服务配置说明](#️-swqos-服务配置说明)
|
||||
- [Astralane QUIC(低延迟)](#astralane-quic低延迟)
|
||||
- [Astralane(Binary / Plain / QUIC)](#astralanebinary--plain--quic)
|
||||
- [🔧 中间件系统说明](#-中间件系统说明)
|
||||
- [🔍 地址查找表](#-地址查找表)
|
||||
- [🔍 Nonce 缓存](#-nonce-缓存)
|
||||
@@ -131,13 +131,13 @@ let swqos_configs: Vec<SwqosConfig> = vec![
|
||||
SwqosConfig::Default(rpc_url.clone()),
|
||||
SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt, None),
|
||||
SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt, None),
|
||||
// Astralane:第4个参数 None 为 HTTP,Some(SwqosTransport::Quic) 为 QUIC;同一 API key
|
||||
SwqosConfig::Astralane("your_astralane_api_key".to_string(), SwqosRegion::Frankfurt, None, None), // HTTP
|
||||
// Astralane:第4个参数为 AstralaneTransport — Binary(默认)、Plain(/iris)或 Quic
|
||||
SwqosConfig::Astralane("your_astralane_api_key".to_string(), SwqosRegion::Frankfurt, None, None), // Binary /irisb
|
||||
SwqosConfig::Astralane(
|
||||
"your_astralane_api_key".to_string(),
|
||||
SwqosRegion::Frankfurt,
|
||||
None,
|
||||
Some(SwqosTransport::Quic),
|
||||
Some(AstralaneTransport::Quic),
|
||||
), // QUIC
|
||||
];
|
||||
// 创建 TradeConfig 实例
|
||||
@@ -147,7 +147,7 @@ let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
|
||||
// .log_enabled(true) // 默认: true - SDK 计时 / SWQOS 日志
|
||||
// .check_min_tip(false) // 默认: false - 过滤低于最低小费的 SWQOS
|
||||
// .swqos_cores_from_end(false) // 默认: false - 将 SWQOS 绑定到末尾 N 个 CPU 核心
|
||||
// .mev_protection(false) // 默认: false - MEV 保护(Astralane 端口 9000 / BlockRazor sandwichMitigation)
|
||||
// .mev_protection(false) // 默认: false - MEV(Astralane QUIC :9000 或 HTTP mev-protect / BlockRazor)
|
||||
.build();
|
||||
|
||||
// 创建 TradingClient
|
||||
@@ -279,28 +279,28 @@ let bloxroute_config = SwqosConfig::Bloxroute(
|
||||
|
||||
当使用多个MEV服务时,需要使用`Durable Nonce`。你需要使用`fetch_nonce_info`函数获取最新的`nonce`值,并在交易的时候将`durable_nonce`填入交易参数。
|
||||
|
||||
#### Astralane QUIC(低延迟)
|
||||
#### Astralane(Binary / Plain / QUIC)
|
||||
|
||||
Astralane 支持 **HTTP** 与 **QUIC** 两种传输方式。QUIC 可减少连接开销,降低提交延迟。使用 QUIC 时,将 `SwqosConfig::Astralane` 的第四个参数设为 `Some(SwqosTransport::Quic)`。Astralane 的 QUIC 服务使用**单一端点**(无分区域端点),选 QUIC 时 SDK 会忽略 `region` 与可选自定义 URL;为与其他 SWQoS 配置一致,可传入相同 region。
|
||||
Astralane 支持 **Binary** HTTP(`/irisb`)、**Plain** HTTP(`/iris`)与 **QUIC**(`host:7000`,全局 `mev_protection` 为 true 时用 `:9000`)。第四个参数:`Some(AstralaneTransport::Plain)`、`Some(AstralaneTransport::Quic)`,或 `None` 表示 **Binary**(默认)。全局 `mev_protection` 会在 HTTP 上附加 `mev-protect=true`,或为 QUIC 选择 9000 端口。
|
||||
|
||||
```rust
|
||||
use sol_trade_sdk::{SwqosConfig, SwqosRegion, SwqosTransport};
|
||||
use sol_trade_sdk::{SwqosConfig, SwqosRegion, AstralaneTransport};
|
||||
|
||||
// Astralane 使用 QUIC(低延迟);region 会被忽略(QUIC 单一端点)
|
||||
let swqos_configs: Vec<SwqosConfig> = vec![
|
||||
SwqosConfig::Default(rpc_url.clone()),
|
||||
SwqosConfig::Astralane(
|
||||
"your_astralane_api_key".to_string(),
|
||||
SwqosRegion::Frankfurt, // 与其他服务一致即可;QUIC 时会被忽略
|
||||
SwqosRegion::Frankfurt,
|
||||
None,
|
||||
Some(SwqosTransport::Quic),
|
||||
Some(AstralaneTransport::Quic),
|
||||
),
|
||||
];
|
||||
// 然后照常使用 swqos_configs 创建 TradeConfig / TradingClient
|
||||
```
|
||||
|
||||
- **HTTP**(默认):第四个参数为 `None` 或 `Some(SwqosTransport::Http)`;region 与可选自定义 URL 生效。
|
||||
- **QUIC**:第四个参数为 `Some(SwqosTransport::Quic)`;SDK 使用单一 QUIC 端点并忽略 region。与 HTTP 使用同一 API key。
|
||||
- **Binary**(默认):`None` 或 `Some(AstralaneTransport::Binary)` — `/irisb`,bincode 正文。
|
||||
- **Plain**:`Some(AstralaneTransport::Plain)` — `/iris`。
|
||||
- **QUIC**:`Some(AstralaneTransport::Quic)` — 按区域的 `host:7000` / `:9000`(MEV);同一 API key。
|
||||
|
||||
---
|
||||
|
||||
@@ -363,7 +363,7 @@ SDK 不会在每次卖出时通过 RPC 拉取 creator_vault(以避免延迟)
|
||||
- **FlashBlock**: 高速交易执行,支持 API 密钥认证
|
||||
- **BlockRazor**: 高速交易执行,支持 API 密钥认证
|
||||
- **Node1**: 高速交易执行,支持 API 密钥认证
|
||||
- **Astralane**: 区块链网络加速(支持 HTTP 与 QUIC,见上方 [Astralane QUIC](#astralane-quic低延迟))
|
||||
- **Astralane**: 区块链网络加速(Binary/Plain HTTP 与 QUIC,见 [Astralane](#astralanebinary--plain--quic))
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
use sol_trade_sdk::{
|
||||
common::{AnyResult, InfrastructureConfig, TradeConfig},
|
||||
swqos::{SwqosConfig, SwqosRegion},
|
||||
SwqosTransport, TradingClient, TradingInfrastructure,
|
||||
AstralaneTransport, TradingClient, TradingInfrastructure,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
@@ -52,8 +52,8 @@ async fn create_trading_client_simple() -> AnyResult<TradingClient> {
|
||||
"your_api_token".to_string(),
|
||||
SwqosRegion::Frankfurt,
|
||||
None,
|
||||
Some(SwqosTransport::Quic),
|
||||
), // QUIC; use None for HTTP
|
||||
Some(AstralaneTransport::Quic),
|
||||
), // QUIC;None / Some(Binary) / Some(Plain) 为 HTTP
|
||||
// Helius Sender: 4th param swqos_only Some(true) => min tip 0.000005 SOL; None => 0.0002 SOL
|
||||
SwqosConfig::Helius("".to_string(), SwqosRegion::Default, None, Some(true)),
|
||||
];
|
||||
|
||||
+4
-4
@@ -12,7 +12,7 @@ pub struct InfrastructureConfig {
|
||||
/// When true, SWQOS sender threads use the *last* N cores instead of the first N. Reduces contention with main thread / default tokio workers that often use low-numbered cores. Default false.
|
||||
pub swqos_cores_from_end: bool,
|
||||
/// Global MEV protection flag. When true, SWQOS providers that support MEV protection
|
||||
/// (Astralane QUIC port 9000, BlockRazor revert_protection) will use their MEV-protected
|
||||
/// (Astralane QUIC `:9000` or HTTP `mev-protect=true`, BlockRazor) use MEV-protected
|
||||
/// endpoints/modes. Default false.
|
||||
pub mev_protection: bool,
|
||||
}
|
||||
@@ -92,8 +92,8 @@ pub struct TradeConfig {
|
||||
/// When true, SWQOS uses the *last* N cores (instead of the first N). Use when main thread / tokio use low-numbered cores to reduce CPU contention. Default false.
|
||||
pub swqos_cores_from_end: bool,
|
||||
/// Global MEV protection flag. When true, SWQOS providers that support MEV protection
|
||||
/// (Astralane QUIC port 9000, BlockRazor sandwichMitigation mode) will use their
|
||||
/// MEV-protected endpoints/modes. Default false (no MEV protection, lower latency).
|
||||
/// (Astralane QUIC `:9000` or Plain/Binary HTTP `mev-protect=true`, BlockRazor sandwichMitigation)
|
||||
/// use their MEV-protected endpoints/modes. Default false (no MEV protection, lower latency).
|
||||
pub mev_protection: bool,
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ impl TradeConfigBuilder {
|
||||
}
|
||||
|
||||
/// Enable global MEV protection. When `true`:
|
||||
/// - **Astralane QUIC** uses port `9000` (MEV-protected endpoint)
|
||||
/// - **Astralane QUIC** uses port `9000`; **Astralane HTTP** adds `mev-protect=true`
|
||||
/// - **BlockRazor** uses `mode=sandwichMitigation` (skips blacklisted Leader slots)
|
||||
///
|
||||
/// May reduce landing speed. Default: `false`.
|
||||
|
||||
+214
-92
@@ -171,215 +171,267 @@ pub const SPEEDLANDING_TIP_ACCOUNTS: &[Pubkey] = &[
|
||||
pubkey!("speede8xCcUq2Tiv1efXeTuE3k9TDNq8TnGKaKSc6J4"),
|
||||
];
|
||||
|
||||
// NewYork,
|
||||
// Frankfurt,
|
||||
// Amsterdam,
|
||||
// SLC,
|
||||
// Tokyo,
|
||||
// London,
|
||||
// LosAngeles,
|
||||
// Default,
|
||||
// `SwqosRegion` 与下列各 `SWQOS_ENDPOINTS_*` 下标严格对应(共 10 项):
|
||||
// 0 NewYork, 1 Frankfurt, 2 Amsterdam, 3 Dublin, 4 SLC, 5 Tokyo, 6 Singapore, 7 London, 8 LosAngeles, 9 Default。
|
||||
//
|
||||
// **地理就近(用户语义)**:当某枚举区域没有该服务商**独立公布**的 PoP 时,在**该服务商已出现的端点集合内**,按真实地理位置选**大圆距离最近**的一项作为填充;行尾注释说明依据。
|
||||
// **例外**:`SwqosRegion::Default`(下标 9)不表示地球上的点,表中为全局 URL 或文档默认枢纽,**不适用**地理就近,仅表示「未指定区域时的回退」。
|
||||
// 若某区域仅有一种「大区」级入口(例如全美只有一个美东 PoP),则地理上非最优但只能复用,注释会标明「受服务商可用区限制」。
|
||||
|
||||
pub const SWQOS_ENDPOINTS_JITO: [&str; 8] = [
|
||||
/// Jito mainnet block engines (`https://<region>.mainnet.block-engine.jito.wtf`).
|
||||
/// There is no Los Angeles engine → use Salt Lake City for `LosAngeles`; `SwqosRegion::Default` uses the global mainnet URL.
|
||||
pub const SWQOS_ENDPOINTS_JITO: [&str; 10] = [
|
||||
"https://ny.mainnet.block-engine.jito.wtf",
|
||||
"https://frankfurt.mainnet.block-engine.jito.wtf",
|
||||
"https://amsterdam.mainnet.block-engine.jito.wtf",
|
||||
"https://dublin.mainnet.block-engine.jito.wtf",
|
||||
"https://slc.mainnet.block-engine.jito.wtf",
|
||||
"https://tokyo.mainnet.block-engine.jito.wtf",
|
||||
"https://singapore.mainnet.block-engine.jito.wtf",
|
||||
"https://london.mainnet.block-engine.jito.wtf",
|
||||
"https://ny.mainnet.block-engine.jito.wtf",
|
||||
"https://slc.mainnet.block-engine.jito.wtf", // LosAngeles: no LA PoP; nearest US-West is SLC
|
||||
"https://mainnet.block-engine.jito.wtf",
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_NEXTBLOCK: [&str; 8] = [
|
||||
/// NextBlock regional HTTP hosts (see provider docs). `SwqosRegion` order; no dedicated LA PoP → SLC as US-West fallback.
|
||||
pub const SWQOS_ENDPOINTS_NEXTBLOCK: [&str; 10] = [
|
||||
"http://ny.nextblock.io",
|
||||
"http://frankfurt.nextblock.io",
|
||||
"http://amsterdam.nextblock.io",
|
||||
"http://fra.nextblock.io",
|
||||
"http://ams.nextblock.io",
|
||||
"http://dublin.nextblock.io",
|
||||
"http://slc.nextblock.io",
|
||||
"http://tokyo.nextblock.io",
|
||||
"http://sgp.nextblock.io",
|
||||
"http://london.nextblock.io",
|
||||
"http://singapore.nextblock.io",
|
||||
"http://frankfurt.nextblock.io",
|
||||
"http://slc.nextblock.io",
|
||||
"http://fra.nextblock.io", // Default: 非地理区域;服务商无「全局」主机名时用 EU 枢纽作未选区回退
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_ZERO_SLOT: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_ZERO_SLOT: [&str; 10] = [
|
||||
"http://ny.0slot.trade",
|
||||
"http://de2.0slot.trade", // Use de2 for TSW, and de1 for OVH
|
||||
"http://ams.0slot.trade",
|
||||
"http://ny.0slot.trade",
|
||||
"http://ams.0slot.trade", // Dublin: 无 IE 专用;在已公布 EU 点中选距爱尔兰最近的 ams(相对 de2 等)
|
||||
"http://la.0slot.trade", // SLC: no UT PoP; nearest US-West published host
|
||||
"http://jp.0slot.trade",
|
||||
"http://ams.0slot.trade",
|
||||
"http://jp.0slot.trade", // SG: 无本地 PoP;已公布 APAC 仅 jp,为表中离新加坡最近的大圆距离
|
||||
"http://ams.0slot.trade", // London: 无 UK 专用;已公布 EU 点中 ams 距伦敦最近之一
|
||||
"http://la.0slot.trade",
|
||||
"http://de2.0slot.trade", // Use de2 for TSW, and de1 for OVH
|
||||
"http://de2.0slot.trade", // Default: 非地理区域;EU 枢纽 de2 作未选区回退
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_TEMPORAL: [&str; 8] = [
|
||||
"http://ewr1.nozomi.temporal.xyz",
|
||||
/// Nozomi Direct regions: ewr1, fra2, ams1, lon1, lax1, tyo1, sgp1, …
|
||||
pub const SWQOS_ENDPOINTS_TEMPORAL: [&str; 10] = [
|
||||
"http://ewr1.nozomi.temporal.xyz", // NewYork → Newark
|
||||
"http://fra2.nozomi.temporal.xyz",
|
||||
"http://ams1.nozomi.temporal.xyz",
|
||||
"http://ewr1.nozomi.temporal.xyz",
|
||||
"http://lon1.nozomi.temporal.xyz", // Dublin: no IE host; UK nearest Direct PoP
|
||||
"http://lax1.nozomi.temporal.xyz", // SLC: US-West
|
||||
"http://tyo1.nozomi.temporal.xyz",
|
||||
"http://sgp1.nozomi.temporal.xyz",
|
||||
"http://pit1.nozomi.temporal.xyz",
|
||||
"http://fra2.nozomi.temporal.xyz",
|
||||
"http://lon1.nozomi.temporal.xyz",
|
||||
"http://lax1.nozomi.temporal.xyz",
|
||||
"http://fra2.nozomi.temporal.xyz", // Default: 非地理区域;EU Direct 枢纽
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_BLOX: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_BLOX: [&str; 10] = [
|
||||
"https://ny.solana.dex.blxrbdn.com",
|
||||
"https://germany.solana.dex.blxrbdn.com",
|
||||
"https://amsterdam.solana.dex.blxrbdn.com",
|
||||
"https://ny.solana.dex.blxrbdn.com",
|
||||
"https://uk.solana.dex.blxrbdn.com", // Dublin: IE/UK edge
|
||||
"https://la.solana.dex.blxrbdn.com", // SLC: no Mountain PoP; US-West LA
|
||||
"https://tokyo.solana.dex.blxrbdn.com",
|
||||
"https://tokyo.solana.dex.blxrbdn.com", // SG: 文档无 SGP 区域;已公布 APAC 仅 Tokyo,为距 SG 最近选项
|
||||
"https://uk.solana.dex.blxrbdn.com",
|
||||
"https://la.solana.dex.blxrbdn.com",
|
||||
"https://global.solana.dex.blxrbdn.com",
|
||||
"https://global.solana.dex.blxrbdn.com", // Default: 非地理区域;全球任播
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_NODE1: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_NODE1: [&str; 10] = [
|
||||
"http://ny.node1.me",
|
||||
"http://fra.node1.me",
|
||||
"http://ams.node1.me",
|
||||
"http://ny.node1.me",
|
||||
"http://lon.node1.me", // Dublin: 已公布中英爱区域用 lon(地理上近爱尔兰)
|
||||
"http://ny.node1.me", // SLC: 已公布美国仅 ny;美西无 PoP,受可用区限制复用美东
|
||||
"http://tk.node1.me",
|
||||
"http://tk.node1.me", // SG: 已公布 APAC 仅 tk;地理上为表中离 SG 最近
|
||||
"http://lon.node1.me",
|
||||
"http://ny.node1.me",
|
||||
"http://fra.node1.me",
|
||||
"http://ny.node1.me", // LosAngeles: 同上,美国仅 ny 入口
|
||||
"http://fra.node1.me", // Default: 非地理区域;与 QUIC 对齐为 EU 枢纽
|
||||
];
|
||||
|
||||
/// Node1 QUIC: port 16666. Region order: NewYork, Frankfurt, Amsterdam, SLC→ny, Tokyo, London, LosAngeles→ny, Default→ny.
|
||||
/// Node1 QUIC: port 16666. Region order matches [`SwqosRegion`].
|
||||
/// server_name = host part (e.g. ny.node1.me). Auth: first bi stream = 16-byte UUID; each tx = new bi stream, bincode body.
|
||||
pub const SWQOS_ENDPOINTS_NODE1_QUIC: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_NODE1_QUIC: [&str; 10] = [
|
||||
"ny.node1.me:16666",
|
||||
"fra.node1.me:16666",
|
||||
"ams.node1.me:16666",
|
||||
"ny.node1.me:16666", // SLC → ny
|
||||
"lon.node1.me:16666",
|
||||
"ny.node1.me:16666",
|
||||
"tk.node1.me:16666",
|
||||
"tk.node1.me:16666",
|
||||
"lon.node1.me:16666",
|
||||
"ny.node1.me:16666", // LA → ny
|
||||
"ny.node1.me:16666", // Default → ny
|
||||
"ny.node1.me:16666",
|
||||
"fra.node1.me:16666", // Default: 非地理区域;与 HTTP 对齐为 EU 枢纽
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_FLASHBLOCK: [&str; 8] = [
|
||||
/// Published: ny, slc, ams, fra, singapore, london, tokyo (no IE/UK split → london for Dublin).
|
||||
pub const SWQOS_ENDPOINTS_FLASHBLOCK: [&str; 10] = [
|
||||
"http://ny.flashblock.trade",
|
||||
"http://fra.flashblock.trade",
|
||||
"http://ams.flashblock.trade",
|
||||
"http://london.flashblock.trade", // Dublin: no IE host; UK nearest
|
||||
"http://slc.flashblock.trade",
|
||||
"http://tokyo.flashblock.trade",
|
||||
"http://singapore.flashblock.trade",
|
||||
"http://london.flashblock.trade",
|
||||
"http://ny.flashblock.trade",
|
||||
"http://ny.flashblock.trade",
|
||||
"http://slc.flashblock.trade",
|
||||
"http://fra.flashblock.trade", // Default: 非地理区域;EU 枢纽
|
||||
];
|
||||
|
||||
/// BlockRazor Send Transaction v2: plain-text Base64 body, auth in URI, Content-Type: text/plain. Keep-alive: POST /v2/health.
|
||||
/// 若 HTTP 返回 500,可尝试 HTTPS:https://<region>.solana.blockrazor.io/v2/sendTransaction(Frankfurt/NewYork/Tokyo),通过 custom_url 覆盖。
|
||||
pub const SWQOS_ENDPOINTS_BLOCKRAZOR: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_BLOCKRAZOR: [&str; 10] = [
|
||||
"http://newyork.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://frankfurt.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://amsterdam.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://newyork.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://london.solana.blockrazor.xyz:443/v2/sendTransaction", // Dublin: UK nearest published
|
||||
"http://newyork.solana.blockrazor.xyz:443/v2/sendTransaction", // SLC: 文档无美西;美国仅 NY,受可用区限制
|
||||
"http://tokyo.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://tokyo.solana.blockrazor.xyz:443/v2/sendTransaction", // SG: 已公布 APAC 仅 Tokyo,为距 SG 最近
|
||||
"http://london.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://newyork.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://frankfurt.solana.blockrazor.xyz:443/v2/sendTransaction",
|
||||
"http://newyork.solana.blockrazor.xyz:443/v2/sendTransaction", // LosAngeles: 无美西入口;美国仅 NY
|
||||
"http://frankfurt.solana.blockrazor.xyz:443/v2/sendTransaction", // Default: 非地理区域;EU 枢纽
|
||||
];
|
||||
|
||||
/// BlockRazor gRPC endpoints. Region order: NewYork, Frankfurt, Amsterdam, SLC, Tokyo, London, LosAngeles, Default.
|
||||
/// BlockRazor gRPC endpoints. Region order matches [`SwqosRegion`].
|
||||
/// Port 80 for gRPC protocol. Auth: apikey metadata in gRPC headers.
|
||||
pub const SWQOS_ENDPOINTS_BLOCKRAZOR_GRPC: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_BLOCKRAZOR_GRPC: [&str; 10] = [
|
||||
"http://newyork.solana-grpc.blockrazor.xyz:80",
|
||||
"http://frankfurt.solana-grpc.blockrazor.xyz:80",
|
||||
"http://amsterdam.solana-grpc.blockrazor.xyz:80",
|
||||
"http://newyork.solana-grpc.blockrazor.xyz:80",
|
||||
"http://london.solana-grpc.blockrazor.xyz:80",
|
||||
"http://newyork.solana-grpc.blockrazor.xyz:80", // SLC: 与 HTTP 一致;美国仅 NY
|
||||
"http://tokyo.solana-grpc.blockrazor.xyz:80",
|
||||
"http://tokyo.solana-grpc.blockrazor.xyz:80",
|
||||
"http://london.solana-grpc.blockrazor.xyz:80",
|
||||
"http://newyork.solana-grpc.blockrazor.xyz:80",
|
||||
"http://frankfurt.solana-grpc.blockrazor.xyz:80",
|
||||
"http://newyork.solana-grpc.blockrazor.xyz:80", // LosAngeles: 与 HTTP 一致
|
||||
"http://frankfurt.solana-grpc.blockrazor.xyz:80", // Default: 非地理区域
|
||||
];
|
||||
|
||||
/// Astralane binary API path (no Base64; use with ?api-key=...&method=sendTransaction|getHealth).
|
||||
/// Plain HTTP API path (`/iris?api-key=…&method=…`).
|
||||
pub const ASTRALANE_PATH_IRIS: &str = "iris";
|
||||
/// Binary HTTP API path (`/irisb?api-key=…&method=sendTransaction`, raw bincode body).
|
||||
pub const ASTRALANE_PATH_IRISB: &str = "irisb";
|
||||
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE: [&str; 8] = [
|
||||
/// Astralane **Plain** HTTP gateways (`/iris`). Pair with [`ASTRALANE_PATH_IRIS`].
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_PLAIN: [&str; 10] = [
|
||||
"http://ny.gateway.astralane.io/iris",
|
||||
"http://fr.gateway.astralane.io/iris",
|
||||
"http://ams.gateway.astralane.io/iris",
|
||||
"http://ams.gateway.astralane.io/iris", // Dublin: 无 IE 专用;在已公布 EU 点中选距爱尔兰最近的 ams
|
||||
"http://la.gateway.astralane.io/iris",
|
||||
"http://jp.gateway.astralane.io/iris",
|
||||
"http://sg.gateway.astralane.io/iris",
|
||||
"http://ams.gateway.astralane.io/iris", // London: 无 UK 专用;在已公布 EU 点中选距英国最近的 ams
|
||||
"http://la.gateway.astralane.io/iris",
|
||||
"https://edge.astralane.io/iris", // Default: 非地理区域;全局任播边缘
|
||||
];
|
||||
|
||||
/// Astralane **Binary** HTTP gateways (`/irisb`). Pair with [`ASTRALANE_PATH_IRISB`].
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_BINARY: [&str; 10] = [
|
||||
"http://ny.gateway.astralane.io/irisb",
|
||||
"http://fr.gateway.astralane.io/irisb",
|
||||
"http://ams.gateway.astralane.io/irisb",
|
||||
"http://ny.gateway.astralane.io/irisb",
|
||||
"http://ams.gateway.astralane.io/irisb", // Dublin: 同 Plain
|
||||
"http://la.gateway.astralane.io/irisb",
|
||||
"http://jp.gateway.astralane.io/irisb",
|
||||
"http://ny.gateway.astralane.io/irisb",
|
||||
"http://lax.gateway.astralane.io/irisb",
|
||||
"http://lim.gateway.astralane.io/irisb",
|
||||
"http://sg.gateway.astralane.io/irisb",
|
||||
"http://ams.gateway.astralane.io/irisb", // London: 同 Plain
|
||||
"http://la.gateway.astralane.io/irisb",
|
||||
"https://edge.astralane.io/irisb", // Default: 同 Plain
|
||||
];
|
||||
|
||||
/// Astralane QUIC endpoints (port 7000). Region order: NewYork, Frankfurt, Amsterdam, SLC, Tokyo, London, LosAngeles, Default.
|
||||
/// See: https://github.com/Astralane/astralane-quic-client. We use fr, ams, la, ny, lim, sg only (avoid ams2/fr2 for lower latency).
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_QUIC: [&str; 8] = [
|
||||
"ny.gateway.astralane.io:7000", // NewYork
|
||||
"fr.gateway.astralane.io:7000", // Frankfurt
|
||||
"ams.gateway.astralane.io:7000", // Amsterdam
|
||||
"lim.gateway.astralane.io:7000", // SLC (no slc, use lim)
|
||||
"sg.gateway.astralane.io:7000", // Tokyo (Asia)
|
||||
"ams.gateway.astralane.io:7000", // London (Europe, avoid ams2)
|
||||
"la.gateway.astralane.io:7000", // LosAngeles
|
||||
"lim.gateway.astralane.io:7000", // Default
|
||||
/// Astralane QUIC endpoints (port 7000). Region order matches [`SwqosRegion`].
|
||||
/// See: https://github.com/Astralane/astralane-quic-client.
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_QUIC: [&str; 10] = [
|
||||
"ny.gateway.astralane.io:7000",
|
||||
"fr.gateway.astralane.io:7000",
|
||||
"ams.gateway.astralane.io:7000",
|
||||
"ams.gateway.astralane.io:7000", // Dublin: 同 HTTP
|
||||
"la.gateway.astralane.io:7000", // SLC: 美西 la 为最近已公布美区入口
|
||||
"jp.gateway.astralane.io:7000",
|
||||
"sg.gateway.astralane.io:7000",
|
||||
"ams.gateway.astralane.io:7000", // London: 同 HTTP
|
||||
"la.gateway.astralane.io:7000",
|
||||
"lim.gateway.astralane.io:7000", // Default: 非地理区域;全局边缘
|
||||
];
|
||||
|
||||
/// Astralane QUIC MEV-protected endpoints (port 9000). Same region order as SWQOS_ENDPOINTS_ASTRALANE_QUIC.
|
||||
/// Use these when mev_protection=true to route through Astralane's MEV-protected path.
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV: [&str; 8] = [
|
||||
"ny.gateway.astralane.io:9000", // NewYork
|
||||
"fr.gateway.astralane.io:9000", // Frankfurt
|
||||
"ams.gateway.astralane.io:9000", // Amsterdam
|
||||
"lim.gateway.astralane.io:9000", // SLC (no slc, use lim)
|
||||
"sg.gateway.astralane.io:9000", // Tokyo (Asia)
|
||||
"ams.gateway.astralane.io:9000", // London (Europe)
|
||||
"la.gateway.astralane.io:9000", // LosAngeles
|
||||
"lim.gateway.astralane.io:9000", // Default
|
||||
pub const SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV: [&str; 10] = [
|
||||
"ny.gateway.astralane.io:9000",
|
||||
"fr.gateway.astralane.io:9000",
|
||||
"ams.gateway.astralane.io:9000",
|
||||
"ams.gateway.astralane.io:9000",
|
||||
"la.gateway.astralane.io:9000",
|
||||
"jp.gateway.astralane.io:9000",
|
||||
"sg.gateway.astralane.io:9000",
|
||||
"ams.gateway.astralane.io:9000",
|
||||
"la.gateway.astralane.io:9000",
|
||||
"lim.gateway.astralane.io:9000",
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_STELLIUM: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_STELLIUM: [&str; 10] = [
|
||||
"http://ewr1.flashrpc.com",
|
||||
"http://fra1.flashrpc.com",
|
||||
"http://ams1.flashrpc.com",
|
||||
"http://ewr1.flashrpc.com",
|
||||
"http://lhr1.flashrpc.com", // Dublin: 已公布 UK 用 lhr;地理上近爱尔兰
|
||||
"http://ewr1.flashrpc.com", // SLC: 已公布美国仅 ewr;无美西 PoP,受可用区限制
|
||||
"http://tyo1.flashrpc.com",
|
||||
"http://tyo1.flashrpc.com", // SG: 表中无 SGP;APAC 仅 tyo,为距 SG 最近
|
||||
"http://lhr1.flashrpc.com",
|
||||
"http://ewr1.flashrpc.com",
|
||||
"http://fra1.flashrpc.com",
|
||||
"http://ewr1.flashrpc.com", // LosAngeles: 同上,美国仅 ewr
|
||||
"http://fra1.flashrpc.com", // Default: 非地理区域;EU 枢纽
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_SOYAS: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_SOYAS: [&str; 10] = [
|
||||
"nyc.landing.soyas.xyz:9000",
|
||||
"fra.landing.soyas.xyz:9000",
|
||||
"ams.landing.soyas.xyz:9000",
|
||||
"nyc.landing.soyas.xyz:9000",
|
||||
"lon.landing.soyas.xyz:9000", // Dublin: 已公布用 lon;地理近爱尔兰
|
||||
"nyc.landing.soyas.xyz:9000", // SLC: 已公布美国仅 nyc;无美西
|
||||
"tyo.landing.soyas.xyz:9000",
|
||||
"tyo.landing.soyas.xyz:9000", // SG: 表中 APAC 仅 tyo
|
||||
"lon.landing.soyas.xyz:9000",
|
||||
"nyc.landing.soyas.xyz:9000",
|
||||
"fra.landing.soyas.xyz:9000",
|
||||
"nyc.landing.soyas.xyz:9000", // LosAngeles: 同上
|
||||
"fra.landing.soyas.xyz:9000", // Default: 非地理区域;EU 枢纽
|
||||
];
|
||||
|
||||
pub const SWQOS_ENDPOINTS_SPEEDLANDING: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_SPEEDLANDING: [&str; 10] = [
|
||||
"nyc.speedlanding.trade:17778",
|
||||
"fra.speedlanding.trade:17778",
|
||||
"ams.speedlanding.trade:17778",
|
||||
"nyc.speedlanding.trade:17778",
|
||||
"ams.speedlanding.trade:17778", // Dublin: 已公布 EU 点中 ams 地理近爱尔兰
|
||||
"nyc.speedlanding.trade:17778", // SLC: 表中美国仅 nyc;无美西 PoP,受可用区限制
|
||||
"tyo.speedlanding.trade:17778",
|
||||
"fra.speedlanding.trade:17778",
|
||||
"nyc.speedlanding.trade:17778",
|
||||
"fra.speedlanding.trade:17778",
|
||||
"sgp.speedlanding.trade:17778",
|
||||
"ams.speedlanding.trade:17778", // London: 已公布 EU 中 ams 距英国最近之一
|
||||
"nyc.speedlanding.trade:17778", // LosAngeles: 同上,美国仅 nyc
|
||||
"fra.speedlanding.trade:17778", // Default: 非地理区域;EU 枢纽
|
||||
];
|
||||
|
||||
/// Helius Sender: POST /fast, dual routing to validators and Jito. API key optional (custom TPS only).
|
||||
/// Region order: NewYork(EWR), Frankfurt, Amsterdam, SLC, Tokyo, London, LosAngeles(SG), Default(Global).
|
||||
pub const SWQOS_ENDPOINTS_HELIUS: [&str; 8] = [
|
||||
pub const SWQOS_ENDPOINTS_HELIUS: [&str; 10] = [
|
||||
"http://ewr-sender.helius-rpc.com/fast",
|
||||
"http://fra-sender.helius-rpc.com/fast",
|
||||
"http://ams-sender.helius-rpc.com/fast",
|
||||
"http://lon-sender.helius-rpc.com/fast", // Dublin: IE → UK/EU routing
|
||||
"http://slc-sender.helius-rpc.com/fast",
|
||||
"http://tyo-sender.helius-rpc.com/fast",
|
||||
"http://lon-sender.helius-rpc.com/fast",
|
||||
"http://sg-sender.helius-rpc.com/fast",
|
||||
"https://sender.helius-rpc.com/fast",
|
||||
"http://lon-sender.helius-rpc.com/fast",
|
||||
"http://slc-sender.helius-rpc.com/fast",
|
||||
"https://sender.helius-rpc.com/fast", // Default: 非地理区域;全局 Sender
|
||||
];
|
||||
|
||||
pub const SWQOS_MIN_TIP_DEFAULT: f64 = 0.00001; // 其它SWQOS默认最低小费
|
||||
@@ -400,3 +452,73 @@ pub const SWQOS_MIN_TIP_SPEEDLANDING: f64 = 0.001; // Speedlanding requires mini
|
||||
pub const SWQOS_MIN_TIP_HELIUS: f64 = 0.0002;
|
||||
/// Helius Sender with swqos_only: minimum 0.000005 SOL (much lower tip allowed).
|
||||
pub const SWQOS_MIN_TIP_HELIUS_SWQOS_ONLY: f64 = 0.000005;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SWQOS_REGION_ENDPOINT_TABLES: &[&[&str]] = &[
|
||||
&SWQOS_ENDPOINTS_JITO,
|
||||
&SWQOS_ENDPOINTS_NEXTBLOCK,
|
||||
&SWQOS_ENDPOINTS_ZERO_SLOT,
|
||||
&SWQOS_ENDPOINTS_TEMPORAL,
|
||||
&SWQOS_ENDPOINTS_BLOX,
|
||||
&SWQOS_ENDPOINTS_NODE1,
|
||||
&SWQOS_ENDPOINTS_NODE1_QUIC,
|
||||
&SWQOS_ENDPOINTS_FLASHBLOCK,
|
||||
&SWQOS_ENDPOINTS_BLOCKRAZOR,
|
||||
&SWQOS_ENDPOINTS_BLOCKRAZOR_GRPC,
|
||||
&SWQOS_ENDPOINTS_ASTRALANE_PLAIN,
|
||||
&SWQOS_ENDPOINTS_ASTRALANE_BINARY,
|
||||
&SWQOS_ENDPOINTS_ASTRALANE_QUIC,
|
||||
&SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV,
|
||||
&SWQOS_ENDPOINTS_STELLIUM,
|
||||
&SWQOS_ENDPOINTS_SOYAS,
|
||||
&SWQOS_ENDPOINTS_SPEEDLANDING,
|
||||
&SWQOS_ENDPOINTS_HELIUS,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn all_swqos_endpoint_tables_align_with_swqos_region() {
|
||||
const EXPECT: usize = 10;
|
||||
for (idx, table) in SWQOS_REGION_ENDPOINT_TABLES.iter().enumerate() {
|
||||
assert_eq!(
|
||||
table.len(),
|
||||
EXPECT,
|
||||
"SWQOS endpoint table index {} length must match SwqosRegion (10 variants)",
|
||||
idx
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn astralane_quic_hosts_match_mev_row_by_row() {
|
||||
for i in 0..10 {
|
||||
let base = SWQOS_ENDPOINTS_ASTRALANE_QUIC[i].trim_end_matches(":7000");
|
||||
let mev = SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV[i].trim_end_matches(":9000");
|
||||
assert_eq!(base, mev, "Astralane QUIC vs MEV host mismatch at index {}", i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn node1_http_host_matches_quic_without_port() {
|
||||
for i in 0..10 {
|
||||
let http_host = SWQOS_ENDPOINTS_NODE1[i]
|
||||
.strip_prefix("http://")
|
||||
.expect("NODE1 HTTP URL");
|
||||
let quic_host = SWQOS_ENDPOINTS_NODE1_QUIC[i]
|
||||
.strip_suffix(":16666")
|
||||
.expect("NODE1 QUIC endpoint");
|
||||
assert_eq!(http_host, quic_host, "Node1 HTTP vs QUIC host mismatch at index {}", i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn astralane_plain_and_binary_same_origin_per_region() {
|
||||
for i in 0..10 {
|
||||
let plain = SWQOS_ENDPOINTS_ASTRALANE_PLAIN[i].trim_end_matches("/iris");
|
||||
let binary = SWQOS_ENDPOINTS_ASTRALANE_BINARY[i].trim_end_matches("/irisb");
|
||||
assert_eq!(plain, binary, "Astralane Plain vs Binary base URL mismatch at index {}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ use crate::swqos::common::TradeError;
|
||||
use crate::swqos::SwqosClient;
|
||||
use crate::swqos::SwqosConfig;
|
||||
use crate::swqos::TradeType;
|
||||
// Re-export for SWQOS HTTP/QUIC choice in SwqosConfig (e.g. Astralane)
|
||||
pub use crate::swqos::SwqosTransport;
|
||||
// Re-export for SwqosConfig (Node1/BlockRazor transport; Astralane submission mode)
|
||||
pub use crate::swqos::{AstralaneTransport, SwqosTransport};
|
||||
use crate::trading::core::params::BonkParams;
|
||||
use crate::trading::core::params::DexParamEnum;
|
||||
use crate::trading::core::params::MeteoraDammV2Params;
|
||||
|
||||
+13
-5
@@ -27,6 +27,8 @@ pub enum AstralaneBackend {
|
||||
Http {
|
||||
endpoint: String,
|
||||
auth_token: String,
|
||||
/// Mirrors global `mev_protection`: adds `mev-protect=true` on HTTP sends (QUIC uses :9000 instead).
|
||||
mev_http: bool,
|
||||
http_client: Client,
|
||||
ping_handle: Arc<tokio::sync::Mutex<Option<JoinHandle<()>>>>,
|
||||
stop_ping: Arc<AtomicBool>,
|
||||
@@ -77,8 +79,8 @@ impl SwqosClientTrait for AstralaneClient {
|
||||
}
|
||||
|
||||
impl AstralaneClient {
|
||||
/// 使用 HTTP(irisb)提交。
|
||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String) -> Self {
|
||||
/// HTTP 提交:`/iris`(Plain)或 `/irisb`(Binary),由 `endpoint` URL 路径区分;`mev_http` 为 true 时附加 `mev-protect=true`。
|
||||
pub fn new(rpc_url: String, endpoint: String, auth_token: String, mev_http: bool) -> Self {
|
||||
let rpc_client = SolanaRpcClient::new(rpc_url);
|
||||
let http_client = default_http_client_builder().build().unwrap();
|
||||
let ping_handle = Arc::new(tokio::sync::Mutex::new(None));
|
||||
@@ -89,6 +91,7 @@ impl AstralaneClient {
|
||||
backend: AstralaneBackend::Http {
|
||||
endpoint,
|
||||
auth_token,
|
||||
mev_http,
|
||||
http_client,
|
||||
ping_handle,
|
||||
stop_ping,
|
||||
@@ -119,6 +122,7 @@ impl AstralaneClient {
|
||||
http_client,
|
||||
ping_handle,
|
||||
stop_ping,
|
||||
..
|
||||
} => {
|
||||
let endpoint = endpoint.clone();
|
||||
let auth_token = auth_token.clone();
|
||||
@@ -182,10 +186,14 @@ impl AstralaneClient {
|
||||
.map_err(|e| anyhow::anyhow!("Astralane binary serialize failed: {}", e))?;
|
||||
|
||||
match &self.backend {
|
||||
AstralaneBackend::Http { endpoint, auth_token, http_client, .. } => {
|
||||
let response = http_client
|
||||
AstralaneBackend::Http { endpoint, auth_token, mev_http, http_client, .. } => {
|
||||
let mut req = http_client
|
||||
.post(endpoint)
|
||||
.query(&[("api-key", auth_token.as_str()), ("method", "sendTransaction")])
|
||||
.query(&[("api-key", auth_token.as_str()), ("method", "sendTransaction")]);
|
||||
if *mev_http {
|
||||
req = req.query(&[("mev-protect", "true")]);
|
||||
}
|
||||
let response = req
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.body(body_bytes)
|
||||
.send()
|
||||
|
||||
+69
-40
@@ -28,8 +28,9 @@ use anyhow::Result;
|
||||
use crate::{
|
||||
common::SolanaRpcClient,
|
||||
constants::swqos::{
|
||||
SWQOS_ENDPOINTS_ASTRALANE, SWQOS_ENDPOINTS_ASTRALANE_QUIC,
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV, SWQOS_ENDPOINTS_BLOCKRAZOR,
|
||||
SWQOS_ENDPOINTS_ASTRALANE_BINARY, SWQOS_ENDPOINTS_ASTRALANE_PLAIN,
|
||||
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_SOYAS,
|
||||
@@ -63,7 +64,7 @@ pub const SWQOS_BLACKLIST: &[SwqosType] = &[
|
||||
|
||||
/// SWQOS 提交通道:HTTP、gRPC 或 QUIC(低延迟)。
|
||||
/// BlockRazor 支持 gRPC 和 HTTP。
|
||||
/// Astralane 和 Node1 支持 QUIC。
|
||||
/// Node1 支持 QUIC。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum SwqosTransport {
|
||||
#[default]
|
||||
@@ -72,6 +73,19 @@ pub enum SwqosTransport {
|
||||
Quic,
|
||||
}
|
||||
|
||||
/// Astralane 三种提交方式:QUIC TPU、Plain HTTP(`/iris`)、Binary HTTP(`/irisb` + bincode)。
|
||||
/// 与全局 [`crate::common::TradeConfig::mev_protection`] 配合:HTTP 加 `mev-protect=true`;QUIC 选 `:9000` / `:7000`。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum AstralaneTransport {
|
||||
/// Binary over HTTP:`…/irisb?api-key=…&method=sendTransaction`(与 `AstralaneClient` 当前序列化一致)。
|
||||
#[default]
|
||||
Binary,
|
||||
/// Plain HTTP:`…/iris?…`(非 irisb 路径)。
|
||||
Plain,
|
||||
/// QUIC(`host:7000`;MEV 时 `host:9000`)。
|
||||
Quic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum TradeType {
|
||||
Create,
|
||||
@@ -196,15 +210,23 @@ pub trait SwqosClientTrait {
|
||||
}
|
||||
}
|
||||
|
||||
/// 地理区域,用于默认 SWQOS 端点下标(见 `constants::swqos`)。
|
||||
///
|
||||
/// 各服务商常量表在**缺独立 PoP**时,于**已公布的端点集合内**按地理距离选最近项;[`SwqosRegion::Default`] 不表示地球上的位置,表中为全局/枢纽回退,不适用地理就近。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum SwqosRegion {
|
||||
NewYork,
|
||||
Frankfurt,
|
||||
Amsterdam,
|
||||
/// Ireland (EU); Jito publishes `dublin.mainnet.block-engine.jito.wtf`.
|
||||
Dublin,
|
||||
SLC,
|
||||
Tokyo,
|
||||
/// Southeast Asia (Singapore); not interchangeable with [`SwqosRegion::Tokyo`].
|
||||
Singapore,
|
||||
London,
|
||||
LosAngeles,
|
||||
/// 非地理区域:未指定区域时的回退,对应表中全局 URL 或枢纽,**不按地理距离选取**。
|
||||
Default,
|
||||
}
|
||||
|
||||
@@ -227,8 +249,8 @@ pub enum SwqosConfig {
|
||||
FlashBlock(String, SwqosRegion, Option<String>),
|
||||
/// BlockRazor(api_token, region, custom_url, transport). transport=None 或 Grpc => gRPC; Some(Http) => HTTP.
|
||||
BlockRazor(String, SwqosRegion, Option<String>, Option<SwqosTransport>),
|
||||
/// Astralane(api_token, region, custom_url, transport). transport=None 表示 Http。
|
||||
Astralane(String, SwqosRegion, Option<String>, Option<SwqosTransport>),
|
||||
/// Astralane(api_token, region, custom_url, mode). `None` => [`AstralaneTransport::Binary`](`/irisb`)。
|
||||
Astralane(String, SwqosRegion, Option<String>, Option<AstralaneTransport>),
|
||||
/// Stellium(api_token, region, custom_url)
|
||||
Stellium(String, SwqosRegion, Option<String>),
|
||||
/// Lightspeed(api_key, region, custom_url) - Solana Vibe Station
|
||||
@@ -285,7 +307,7 @@ impl SwqosConfig {
|
||||
SwqosType::Node1 => SWQOS_ENDPOINTS_NODE1[region as usize].to_string(),
|
||||
SwqosType::FlashBlock => SWQOS_ENDPOINTS_FLASHBLOCK[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_BINARY[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::Soyas => SWQOS_ENDPOINTS_SOYAS[region as usize].to_string(),
|
||||
@@ -300,7 +322,7 @@ impl SwqosConfig {
|
||||
region: SwqosRegion,
|
||||
url: Option<String>,
|
||||
transport: Option<SwqosTransport>,
|
||||
mev_protection: bool,
|
||||
_mev_protection: bool,
|
||||
) -> String {
|
||||
if let Some(custom_url) = url {
|
||||
return custom_url;
|
||||
@@ -324,19 +346,6 @@ impl SwqosConfig {
|
||||
SWQOS_ENDPOINTS_NODE1[region as usize].to_string()
|
||||
}
|
||||
}
|
||||
SwqosType::Astralane => {
|
||||
let use_quic = transport.map_or(false, |t| t == SwqosTransport::Quic);
|
||||
if use_quic {
|
||||
// MEV protection: port 9000; standard: port 7000
|
||||
if mev_protection {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV[region as usize].to_string()
|
||||
} else {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC[region as usize].to_string()
|
||||
}
|
||||
} else {
|
||||
SWQOS_ENDPOINTS_ASTRALANE[region as usize].to_string()
|
||||
}
|
||||
}
|
||||
_ => Self::get_endpoint(swqos_type, region, None),
|
||||
}
|
||||
}
|
||||
@@ -414,26 +423,46 @@ impl SwqosConfig {
|
||||
Ok(Arc::new(blockrazor_client))
|
||||
}
|
||||
}
|
||||
SwqosConfig::Astralane(auth_token, region, url, transport) => {
|
||||
let use_quic = transport.map_or(false, |t| t == SwqosTransport::Quic);
|
||||
if use_quic {
|
||||
let quic_endpoint = url.unwrap_or_else(|| {
|
||||
// MEV protection: port 9000; standard: port 7000
|
||||
if mev_protection {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV[region as usize].to_string()
|
||||
} else {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC[region as usize].to_string()
|
||||
}
|
||||
});
|
||||
let astralane_client =
|
||||
AstralaneClient::new_quic(rpc_url.clone(), &quic_endpoint, auth_token)
|
||||
.await?;
|
||||
Ok(Arc::new(astralane_client))
|
||||
} else {
|
||||
let endpoint = SwqosConfig::get_endpoint(SwqosType::Astralane, region, url);
|
||||
let astralane_client =
|
||||
AstralaneClient::new(rpc_url.clone(), endpoint.to_string(), auth_token);
|
||||
Ok(Arc::new(astralane_client))
|
||||
SwqosConfig::Astralane(auth_token, region, url, mode) => {
|
||||
let mode = mode.unwrap_or_default();
|
||||
match mode {
|
||||
AstralaneTransport::Quic => {
|
||||
let quic_endpoint = url.unwrap_or_else(|| {
|
||||
if mev_protection {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC_MEV[region as usize].to_string()
|
||||
} else {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_QUIC[region as usize].to_string()
|
||||
}
|
||||
});
|
||||
let astralane_client =
|
||||
AstralaneClient::new_quic(rpc_url.clone(), &quic_endpoint, auth_token)
|
||||
.await?;
|
||||
Ok(Arc::new(astralane_client))
|
||||
}
|
||||
AstralaneTransport::Plain => {
|
||||
let endpoint = url.unwrap_or_else(|| {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_PLAIN[region as usize].to_string()
|
||||
});
|
||||
let astralane_client = AstralaneClient::new(
|
||||
rpc_url.clone(),
|
||||
endpoint,
|
||||
auth_token,
|
||||
mev_protection,
|
||||
);
|
||||
Ok(Arc::new(astralane_client))
|
||||
}
|
||||
AstralaneTransport::Binary => {
|
||||
let endpoint = url.unwrap_or_else(|| {
|
||||
SWQOS_ENDPOINTS_ASTRALANE_BINARY[region as usize].to_string()
|
||||
});
|
||||
let astralane_client = AstralaneClient::new(
|
||||
rpc_url.clone(),
|
||||
endpoint,
|
||||
auth_token,
|
||||
mev_protection,
|
||||
);
|
||||
Ok(Arc::new(astralane_client))
|
||||
}
|
||||
}
|
||||
}
|
||||
SwqosConfig::Stellium(auth_token, region, url) => {
|
||||
|
||||
Reference in New Issue
Block a user