Revert "feat: Fastlane SWQoS, PumpSwap pool lookup and init robustness"

This reverts commit 1142829394.
This commit is contained in:
Wood
2026-03-06 11:03:49 +08:00
parent 1142829394
commit 07487c06cb
9 changed files with 28 additions and 384 deletions
-9
View File
@@ -25,15 +25,6 @@ pub async fn update_rents(client: &SolanaRpcClient) -> Result<(), anyhow::Error>
Ok(())
}
/// 165 字节 Token 账户的典型租金(lamports),RPC 超时时用作回退
const DEFAULT_TOKEN_ACCOUNT_RENT: u64 = 2_039_280;
/// 当 RPC 超时或不可用时设置默认租金,避免客户端创建卡死
pub fn set_default_rents() {
SPL_TOKEN_RENT.store(DEFAULT_TOKEN_ACCOUNT_RENT, Ordering::Release);
SPL_TOKEN_2022_RENT.store(DEFAULT_TOKEN_ACCOUNT_RENT, Ordering::Release);
}
pub fn start_rent_updater(client: Arc<SolanaRpcClient>) {
tokio::spawn(async move {
loop {
-19
View File
@@ -331,22 +331,3 @@ 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;
/// Fastlane SWQoS: v2 API POST /v2/sendTransaction, body = binary bincode. ny / fra endpoints.
pub const SWQOS_ENDPOINTS_FASTLANE: [&str; 8] = [
"http://64.130.37.195:8080", // NewYork (ny)
"http://70.40.184.37:8080", // Frankfurt (fra)
"http://70.40.184.37:8080", // Amsterdam -> fra
"http://64.130.37.195:8080", // SLC -> ny
"http://64.130.37.195:8080", // Tokyo -> ny
"http://70.40.184.37:8080", // London -> fra
"http://64.130.37.195:8080", // LosAngeles -> ny
"http://64.130.37.195:8080", // Default -> ny
];
/// Fastlane: server parses tip from tx; this is optional default tip wallet for SDK.
pub const FASTLANE_TIP_ACCOUNTS: &[Pubkey] = &[
pubkey!("DYQ3ATDe9fBNeMYXDfrUtAQXQ41xzaH1hJ1CXktnL3fG"),
];
pub const SWQOS_MIN_TIP_FASTLANE: f64 = 0.00001;
+12 -88
View File
@@ -2,17 +2,13 @@ use crate::{
common::{
spl_associated_token_account::get_associated_token_address_with_program_id, SolanaRpcClient,
},
constants::{TOKEN_PROGRAM, WSOL_TOKEN_ACCOUNT},
constants::TOKEN_PROGRAM,
instruction::utils::pumpswap_types::{pool_decode, Pool},
};
use anyhow::anyhow;
use solana_account_decoder::UiAccountEncoding;
use solana_sdk::pubkey::Pubkey;
/// PumpSwap 池账户总长度(见 pump-public-docs Breaking Change):8 字节 discriminator + 244 字节 Pool。
/// 官方文档:pool structure needs to be 244 bytes (was 243),含 is_mayhem_mode。DataSize 必须与此一致,否则 getProgramAccounts 会返回 0。
const POOL_ACCOUNT_DATA_LEN: u64 = 8 + 244;
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
pub mod seeds {
/// Seed for the global state PDA
@@ -33,10 +29,6 @@ pub mod seeds {
/// Seed for pool v2 PDA (required by program upgrade, readonly at end of account list)
pub const POOL_V2_SEED: &[u8] = b"pool-v2";
/// Legacy pool PDA seed (used with index, creator, base_mint, quote_mint)
pub const POOL_SEED: &[u8] = b"pool";
/// Pump program: pool-authority PDA seed (creator for canonical pool)
pub const POOL_AUTHORITY_SEED: &[u8] = b"pool-authority";
}
/// Constants related to program accounts and authorities
@@ -61,8 +53,6 @@ pub mod accounts {
pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
pub const AMM_PROGRAM: Pubkey = pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");
/// Pump Bonding Curve programcanonical pool 的 creator 来自此程序的 pool-authority PDA
pub const PUMP_PROGRAM_ID: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
pub const LP_FEE_BASIS_POINTS: u64 = 25;
pub const PROTOCOL_FEE_BASIS_POINTS: u64 = 5;
@@ -162,35 +152,6 @@ pub fn get_pool_v2_pda(base_mint: &Pubkey) -> Option<Pubkey> {
Some(pda)
}
/// Pump 程序上的 pool-authority PDAcanonical pool 的 creator),与 @pump-fun/pump-swap-sdk 一致。
#[inline]
pub fn get_pump_pool_authority_pda(mint: &Pubkey) -> Pubkey {
Pubkey::find_program_address(
&[seeds::POOL_AUTHORITY_SEED, mint.as_ref()],
&accounts::PUMP_PROGRAM_ID,
)
.0
}
/// Canonical Pump 池 PDAindex=0creator=pumpPoolAuthorityPda(mint)base_mint=mintquote_mint=WSOL。
/// 与 @pump-fun/pump-swap-sdk 的 canonicalPumpPoolPda(mint) 一致,用于从 bonding curve 迁移后的标准池查找。
#[inline]
pub fn get_canonical_pool_pda(mint: &Pubkey) -> Pubkey {
const CANONICAL_POOL_INDEX: u16 = 0;
let authority = get_pump_pool_authority_pda(mint);
let (pda, _) = Pubkey::find_program_address(
&[
seeds::POOL_SEED,
&CANONICAL_POOL_INDEX.to_le_bytes(),
authority.as_ref(),
mint.as_ref(),
WSOL_TOKEN_ACCOUNT.as_ref(),
],
&accounts::AMM_PROGRAM,
);
pda
}
// Find a pool for a specific mint
pub async fn find_pool(rpc: &SolanaRpcClient, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
let (pool_address, _) = find_by_mint(rpc, mint).await?;
@@ -270,12 +231,11 @@ pub async fn find_by_base_mint(
rpc: &SolanaRpcClient,
base_mint: &Pubkey,
) -> Result<(Pubkey, Pool), anyhow::Error> {
// Use getProgramAccounts to find pools for the given mint.
// base_mint 在账户布局中的偏移:8(discriminator) + 1(bump) + 2(index) + 32(creator) = 43
// Use getProgramAccounts to find pools for the given mint
let filters = vec![
solana_rpc_client_api::filter::RpcFilterType::DataSize(POOL_ACCOUNT_DATA_LEN),
// solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool account size
solana_rpc_client_api::filter::RpcFilterType::Memcmp(
solana_client::rpc_filter::Memcmp::new_base58_encoded(43, base_mint.as_ref()),
solana_client::rpc_filter::Memcmp::new_base58_encoded(43, &base_mint.to_bytes()),
),
];
let config = solana_rpc_client_api::config::RpcProgramAccountsConfig {
@@ -322,12 +282,11 @@ pub async fn find_by_quote_mint(
rpc: &SolanaRpcClient,
quote_mint: &Pubkey,
) -> Result<(Pubkey, Pool), anyhow::Error> {
// Use getProgramAccounts to find pools for the given mint.
// quote_mint 在账户布局中的偏移:8 + 1 + 2 + 32 + 32 = 75
// Use getProgramAccounts to find pools for the given mint
let filters = vec![
solana_rpc_client_api::filter::RpcFilterType::DataSize(POOL_ACCOUNT_DATA_LEN),
// solana_rpc_client_api::filter::RpcFilterType::DataSize(211), // Pool account size
solana_rpc_client_api::filter::RpcFilterType::Memcmp(
solana_client::rpc_filter::Memcmp::new_base58_encoded(75, quote_mint.as_ref()),
solana_client::rpc_filter::Memcmp::new_base58_encoded(75, &quote_mint.to_bytes()),
),
];
let config = solana_rpc_client_api::config::RpcProgramAccountsConfig {
@@ -370,52 +329,17 @@ pub async fn find_by_quote_mint(
Ok((address, pool))
}
/// 按 mint 查找 PumpSwap 池(本函数仅用于 PumpSwap,其他 DEX 勿用)。
///
/// 查找顺序(与 @pump-fun/pump-swap-sdk 一致):
/// 1. Pool v2 PDA ["pool-v2", base_mint] — 一次 getAccount
/// 2. Canonical pool PDA ["pool", 0, pumpPoolAuthority(mint), mint, WSOL] — 迁移后的标准池
/// 3. getProgramAccounts 按 base_mint / quote_mint 过滤
pub async fn find_by_mint(
rpc: &SolanaRpcClient,
mint: &Pubkey,
) -> Result<(Pubkey, Pool), anyhow::Error> {
let mut diag = Vec::<String>::new();
// 1. PumpSwap v2 PDAseeds: ["pool-v2", base_mint]
if let Some(pool_address) = get_pool_v2_pda(mint) {
diag.push(format!("PDA(v2)={}", pool_address));
match fetch_pool(rpc, &pool_address).await {
Ok(pool) if pool.base_mint == *mint => return Ok((pool_address, pool)),
Ok(_) => diag.push("PDA(v2) 账户存在但 base_mint 不匹配".into()),
Err(e) => diag.push(format!("PDA(v2) get_account/decode 失败: {}", e)),
}
if let Ok((address, pool)) = find_by_base_mint(rpc, mint).await {
return Ok((address, pool));
}
// 2. Canonical pool PDA(与 pump-swap-sdk canonicalPumpPoolPda(mint) 一致)
let canonical_address = get_canonical_pool_pda(mint);
diag.push(format!("canonical={}", canonical_address));
match fetch_pool(rpc, &canonical_address).await {
Ok(pool) if pool.base_mint == *mint => return Ok((canonical_address, pool)),
Ok(_) => diag.push("canonical 账户存在但 base_mint 不匹配".into()),
Err(e) => diag.push(format!("canonical get_account/decode 失败: {}", e)),
if let Ok((address, pool)) = find_by_quote_mint(rpc, mint).await {
return Ok((address, pool));
}
// 3. 回退:getProgramAccounts 按 base_mint / quote_mint
match find_by_base_mint(rpc, mint).await {
Ok((address, pool)) => return Ok((address, pool)),
Err(e) => diag.push(format!("getProgramAccounts(base_mint): {}", e)),
}
match find_by_quote_mint(rpc, mint).await {
Ok((address, pool)) => return Ok((address, pool)),
Err(e) => diag.push(format!("getProgramAccounts(quote_mint): {}", e)),
}
Err(anyhow!(
"No pool found for mint {}. 诊断: {}。若使用自建 RPC 请确认已开启 getProgramAccounts 或换用公共 RPC 重试;若代币未在 PumpSwap 建池请先在 pump.fun/DEX 上确认",
mint,
diag.join("; ")
))
Err(anyhow!("No pool found for mint {}", mint))
}
pub async fn get_token_balances(
-1
View File
@@ -19,7 +19,6 @@ pub struct Pool {
pub is_cashback_coin: bool,
}
/// Borsh 解码用的 Pool 字段长度。链上池账户数据为 244 字节(pump-public-docs),末尾可有 reserved 字节,解码只取前 POOL_SIZE。
pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1 + 1;
pub fn pool_decode(data: &[u8]) -> Option<Pool> {
+14 -86
View File
@@ -54,25 +54,6 @@ fn validate_protocol_params(dex_type: DexType, params: &DexParamEnum) -> bool {
}
}
/// 按 mint 查找池地址(通用入口,根据 DEX 类型分发,仅 PumpSwap 等已实现的类型会走优化路径)。
///
/// * `dex_type`PumpSwap 时先走 PDA 再回退 getProgramAccounts,其他类型返回未实现错误。
pub async fn find_pool_by_mint(
rpc: &SolanaRpcClient,
mint: &Pubkey,
dex_type: DexType,
) -> Result<Pubkey, anyhow::Error> {
match dex_type {
DexType::PumpSwap => {
crate::instruction::utils::pumpswap::find_pool(rpc, mint).await
}
_ => Err(anyhow::anyhow!(
"find_pool_by_mint not implemented for {:?}",
dex_type
)),
}
}
/// Type of the token to buy
#[derive(Clone, PartialEq)]
pub enum TradeTokenType {
@@ -116,47 +97,27 @@ impl TradingInfrastructure {
config.commitment.clone(),
));
// Initialize rent cache (with timeout so slow RPC doesn't block forever)
const RENT_UPDATE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(15);
match tokio::time::timeout(RENT_UPDATE_TIMEOUT, common::seed::update_rents(&rpc)).await {
Ok(Ok(())) => {}
Ok(Err(e)) => {
if sdk_log::sdk_log_enabled() {
warn!(target: "sol_trade_sdk", "rent update failed: {}, using defaults", e);
}
common::seed::set_default_rents();
}
Err(_) => {
if sdk_log::sdk_log_enabled() {
warn!(target: "sol_trade_sdk", "rent update timed out ({}s), using defaults; check RPC", RENT_UPDATE_TIMEOUT.as_secs());
}
common::seed::set_default_rents();
}
}
// Initialize rent cache and start background updater
common::seed::update_rents(&rpc).await.unwrap();
common::seed::start_rent_updater(rpc.clone());
// Create SWQOS clients with blacklist checking(单节点超时 5s,避免某一家卡死整段初始化)
const SWQOS_CLIENT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
// Create SWQOS clients with blacklist checking
let mut swqos_clients: Vec<Arc<SwqosClient>> = vec![];
for swqos in &config.swqos_configs {
// Check blacklist, skip disabled providers
if swqos.is_blacklisted() {
if sdk_log::sdk_log_enabled() {
warn!(target: "sol_trade_sdk", "⚠️ SWQOS {:?} is blacklisted, skipping", swqos.swqos_type());
}
continue;
}
match tokio::time::timeout(
SWQOS_CLIENT_TIMEOUT,
SwqosConfig::get_swqos_client(
config.rpc_url.clone(),
config.commitment.clone(),
swqos.clone(),
),
)
.await
{
Ok(Ok(swqos_client)) => swqos_clients.push(swqos_client),
Ok(Err(err)) => {
match SwqosConfig::get_swqos_client(
config.rpc_url.clone(),
config.commitment.clone(),
swqos.clone(),
).await {
Ok(swqos_client) => swqos_clients.push(swqos_client),
Err(err) => {
if sdk_log::sdk_log_enabled() {
warn!(
target: "sol_trade_sdk",
@@ -165,16 +126,6 @@ impl TradingInfrastructure {
);
}
}
Err(_) => {
if sdk_log::sdk_log_enabled() {
warn!(
target: "sol_trade_sdk",
"swqos {:?} init timed out ({}s), skipping",
swqos.swqos_type(),
SWQOS_CLIENT_TIMEOUT.as_secs()
);
}
}
}
}
@@ -397,7 +348,7 @@ impl TradingClient {
}
}
/// 确保钱包存在 WSOL ATA;不存在则发交易创建(会花费租金 + 手续费,初始化阶段唯一会扣钱的逻辑)
/// Helper to ensure WSOL ATA exists for a wallet
async fn ensure_wsol_ata(payer: &Arc<Keypair>, rpc: &Arc<SolanaRpcClient>) {
let wsol_ata =
crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
@@ -545,32 +496,9 @@ impl TradingClient {
// Initialize wallet-specific caches
crate::common::fast_fn::fast_init(&payer.pubkey());
// ═══════════════════════════════════════════════════════════════════════════════
// 初始化阶段会花费租金/手续费的唯一路径:创建 WSOL ATAensure_wsol_ata
// - 触发条件:create_wsol_ata_on_startup == true 且钱包 SOL >= MIN_SOL_FOR_WSOL_ATA_LAMPORTS
// - 花费:ATA 租金(约 0.00203928 SOL)+ 交易手续费;钱包不足时已跳过
// - 其它初始化(TradingInfrastructure::new、update_rents、get_swqos_client)仅 RPC/HTTP,不发送交易
// ═══════════════════════════════════════════════════════════════════════════════
// Handle WSOL ATA creation if configured
if trade_config.create_wsol_ata_on_startup {
const MIN_SOL_FOR_WSOL_ATA_LAMPORTS: u64 = 500_000; // 约 0.0005 SOL,用于 ATA 租金 + 手续费
const BALANCE_CHECK_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
let balance = tokio::time::timeout(
BALANCE_CHECK_TIMEOUT,
infrastructure.rpc.get_balance(&payer.pubkey()),
)
.await
.unwrap_or(Ok(0))
.unwrap_or(0);
if balance >= MIN_SOL_FOR_WSOL_ATA_LAMPORTS {
Self::ensure_wsol_ata(&payer, &infrastructure.rpc).await;
} else if sdk_log::sdk_log_enabled() {
info!(
target: "sol_trade_sdk",
"⏭️ 跳过创建 WSOL ATA:钱包 SOL 不足(当前 {} lamports,需要至少 {}",
balance,
MIN_SOL_FOR_WSOL_ATA_LAMPORTS
);
}
Self::ensure_wsol_ata(&payer, &infrastructure.rpc).await;
}
let instance = Self {
-118
View File
@@ -1,118 +0,0 @@
//! Fastlane SWQoS client: v2 API only (POST /v2/sendTransaction, body = binary bincode).
//! Endpoints: ny http://64.130.37.195:8080, fra http://70.40.184.37:8080.
use crate::swqos::common::{default_http_client_builder, poll_transaction_confirmation};
use anyhow::Result;
use bincode::serialize as bincode_serialize;
use rand::seq::IndexedRandom;
use reqwest::Client;
use solana_client::rpc_client::SerializableTransaction;
use solana_sdk::transaction::VersionedTransaction;
use std::sync::Arc;
use std::time::Instant;
use crate::swqos::{SwqosClientTrait, SwqosType, TradeType};
use crate::{common::SolanaRpcClient, constants::swqos::FASTLANE_TIP_ACCOUNTS};
/// Fastlane v2 submit path (binary bincode, no Base64).
const FASTLANE_V2_PATH: &str = "/v2/sendTransaction";
#[derive(Clone)]
pub struct FastlaneClient {
/// Base URL including port, e.g. http://64.130.37.195:8080
pub base_url: String,
/// Optional API key for request header api-key (empty = no auth).
pub api_key: String,
pub rpc_client: Arc<SolanaRpcClient>,
pub http_client: Client,
}
#[async_trait::async_trait]
impl SwqosClientTrait for FastlaneClient {
async fn send_transaction(&self, trade_type: TradeType, transaction: &VersionedTransaction, wait_confirmation: bool) -> Result<()> {
self.send_transaction_impl(trade_type, transaction, wait_confirmation).await
}
async fn send_transactions(&self, trade_type: TradeType, transactions: &Vec<VersionedTransaction>, wait_confirmation: bool) -> Result<()> {
for tx in transactions {
self.send_transaction_impl(trade_type, tx, wait_confirmation).await?;
}
Ok(())
}
fn get_tip_account(&self) -> Result<String> {
let tip_account = *FASTLANE_TIP_ACCOUNTS
.choose(&mut rand::rng())
.or_else(|| FASTLANE_TIP_ACCOUNTS.first())
.unwrap();
Ok(tip_account.to_string())
}
fn get_swqos_type(&self) -> SwqosType {
SwqosType::Fastlane
}
}
impl FastlaneClient {
pub fn new(rpc_url: String, base_url: String, api_key: String) -> Self {
let rpc_client = SolanaRpcClient::new(rpc_url);
let http_client = default_http_client_builder().build().unwrap();
Self {
base_url,
api_key,
rpc_client: Arc::new(rpc_client),
http_client,
}
}
fn v2_url(&self) -> String {
let base = self.base_url.trim_end_matches('/');
format!("{}{}", base, FASTLANE_V2_PATH)
}
/// Send transaction via Fastlane v2 API: POST /v2/sendTransaction, body = bincode.
pub async fn send_transaction_impl(
&self,
trade_type: TradeType,
transaction: &VersionedTransaction,
wait_confirmation: bool,
) -> Result<()> {
let start_time = Instant::now();
let signature = transaction.get_signature();
let body_bytes = bincode_serialize(transaction)
.map_err(|e| anyhow::anyhow!("Fastlane bincode serialize failed: {}", e))?;
let url = self.v2_url();
let mut req = self.http_client.post(&url).header("Content-Type", "application/octet-stream").body(body_bytes);
if !self.api_key.is_empty() {
req = req.header("api-key", self.api_key.as_str());
}
let response = req.send().await?;
let status = response.status();
let _ = response.bytes().await;
if status.is_success() {
println!(" [fastlane] {} submitted: {:?}", trade_type, start_time.elapsed());
} else {
eprintln!(" [fastlane] {} submission failed: status {}", trade_type, status);
return Err(anyhow::anyhow!("Fastlane sendTransaction failed: {}", status));
}
let start_confirm = Instant::now();
match poll_transaction_confirmation(&self.rpc_client, *signature, wait_confirmation).await {
Ok(_) => (),
Err(e) => {
println!(" signature: {:?}", signature);
println!(" [fastlane] {} confirmation failed: {:?}", trade_type, start_confirm.elapsed());
return Err(e);
}
}
if wait_confirmation {
println!(" signature: {:?}", signature);
println!(" [fastlane] {} confirmed: {:?}", trade_type, start_confirm.elapsed());
}
Ok(())
}
}
-20
View File
@@ -10,7 +10,6 @@ pub mod node1;
pub mod flashblock;
pub mod blockrazor;
pub mod astralane;
pub mod fastlane;
pub mod stellium;
pub mod lightspeed;
pub mod soyas;
@@ -37,7 +36,6 @@ use crate::{
SWQOS_ENDPOINTS_FLASHBLOCK,
SWQOS_ENDPOINTS_BLOCKRAZOR,
SWQOS_ENDPOINTS_ASTRALANE,
SWQOS_ENDPOINTS_FASTLANE,
SWQOS_ENDPOINTS_STELLIUM,
SWQOS_ENDPOINTS_SOYAS,
SWQOS_ENDPOINTS_SPEEDLANDING,
@@ -52,7 +50,6 @@ use crate::{
SWQOS_MIN_TIP_FLASHBLOCK,
SWQOS_MIN_TIP_BLOCKRAZOR,
SWQOS_MIN_TIP_ASTRALANE,
SWQOS_MIN_TIP_FASTLANE,
SWQOS_MIN_TIP_STELLIUM,
SWQOS_MIN_TIP_LIGHTSPEED,
SWQOS_MIN_TIP_SOYAS,
@@ -70,7 +67,6 @@ use crate::{
flashblock::FlashBlockClient,
blockrazor::BlockRazorClient,
astralane::AstralaneClient,
fastlane::FastlaneClient,
stellium::StelliumClient,
lightspeed::LightspeedClient,
soyas::SoyasClient,
@@ -121,7 +117,6 @@ pub enum SwqosType {
FlashBlock,
BlockRazor,
Astralane,
Fastlane,
Stellium,
Lightspeed,
Soyas,
@@ -142,7 +137,6 @@ impl SwqosType {
Self::FlashBlock,
Self::BlockRazor,
Self::Astralane,
Self::Fastlane,
Self::Stellium,
Self::Lightspeed,
Self::Soyas,
@@ -174,7 +168,6 @@ pub trait SwqosClientTrait {
SwqosType::FlashBlock => SWQOS_MIN_TIP_FLASHBLOCK,
SwqosType::BlockRazor => SWQOS_MIN_TIP_BLOCKRAZOR,
SwqosType::Astralane => SWQOS_MIN_TIP_ASTRALANE,
SwqosType::Fastlane => SWQOS_MIN_TIP_FASTLANE,
SwqosType::Stellium => SWQOS_MIN_TIP_STELLIUM,
SwqosType::Lightspeed => SWQOS_MIN_TIP_LIGHTSPEED,
SwqosType::Soyas => SWQOS_MIN_TIP_SOYAS,
@@ -218,8 +211,6 @@ pub enum SwqosConfig {
BlockRazor(String, SwqosRegion, Option<String>),
/// Astralane(api_token, region, custom_url)
Astralane(String, SwqosRegion, Option<String>),
/// Fastlane(api_key_optional, region, custom_url). v2 API only: POST /v2/sendTransaction, body = bincode.
Fastlane(String, SwqosRegion, Option<String>),
/// Stellium(api_token, region, custom_url)
Stellium(String, SwqosRegion, Option<String>),
/// Lightspeed(api_key, region, custom_url) - Solana Vibe Station
@@ -249,7 +240,6 @@ impl SwqosConfig {
SwqosConfig::FlashBlock(_, _, _) => SwqosType::FlashBlock,
SwqosConfig::BlockRazor(_, _, _) => SwqosType::BlockRazor,
SwqosConfig::Astralane(_, _, _) => SwqosType::Astralane,
SwqosConfig::Fastlane(_, _, _) => SwqosType::Fastlane,
SwqosConfig::Stellium(_, _, _) => SwqosType::Stellium,
SwqosConfig::Lightspeed(_, _, _) => SwqosType::Lightspeed,
SwqosConfig::Soyas(_, _, _) => SwqosType::Soyas,
@@ -278,7 +268,6 @@ impl SwqosConfig {
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::Fastlane => SWQOS_ENDPOINTS_FASTLANE[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(),
@@ -371,15 +360,6 @@ impl SwqosConfig {
);
Ok(Arc::new(astralane_client))
},
SwqosConfig::Fastlane(api_key, region, url) => {
let base_url = SwqosConfig::get_endpoint(SwqosType::Fastlane, region, url);
let fastlane_client = FastlaneClient::new(
rpc_url.clone(),
base_url,
api_key,
);
Ok(Arc::new(fastlane_client))
},
SwqosConfig::Stellium(auth_token, region, url) => {
let endpoint = SwqosConfig::get_endpoint(SwqosType::Stellium, region, url);
let stellium_client = StelliumClient::new(
+2 -26
View File
@@ -2,11 +2,7 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction
use solana_system_interface::instruction::transfer;
use crate::common::{
fast_fn::{
get_associated_token_address_with_program_id_fast,
get_associated_token_address_with_program_id_fast_use_seed,
},
spl_token::close_account,
fast_fn::get_associated_token_address_with_program_id_fast, spl_token::close_account,
SolanaRpcClient,
};
use anyhow::anyhow;
@@ -40,30 +36,10 @@ pub async fn get_token_balance(
payer: &Pubkey,
mint: &Pubkey,
) -> Result<u64, anyhow::Error> {
get_token_balance_with_options(
rpc,
let ata = crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
payer,
mint,
&crate::constants::TOKEN_PROGRAM,
false,
)
.await
}
/// 使用与交易指令一致的 ATA 推导(可选 seed)查询余额;卖出/余额查询应与买入使用同一 ATA 地址。
#[inline]
pub async fn get_token_balance_with_options(
rpc: &SolanaRpcClient,
payer: &Pubkey,
mint: &Pubkey,
token_program: &Pubkey,
use_seed: bool,
) -> Result<u64, anyhow::Error> {
let ata = get_associated_token_address_with_program_id_fast_use_seed(
payer,
mint,
token_program,
use_seed,
);
let balance = rpc.get_token_account_balance(&ata).await?;
let balance_u64 =
-17
View File
@@ -31,23 +31,6 @@ impl TradingClient {
trading::common::utils::get_token_balance(&self.infrastructure.rpc, &self.payer.pubkey(), mint).await
}
/// 使用与交易一致的 ATA 推导(含 seed 优化)查询 payer 某 mint 的余额;卖出前查余额应使用此接口并传入池的 base_token_program,否则若使用 seed ATA 会查错账户。
#[inline]
pub async fn get_payer_token_balance_with_program(
&self,
mint: &Pubkey,
token_program: &Pubkey,
) -> Result<u64, anyhow::Error> {
trading::common::utils::get_token_balance_with_options(
&self.infrastructure.rpc,
&self.payer.pubkey(),
mint,
token_program,
self.use_seed_optimize,
)
.await
}
#[inline]
pub fn get_payer_pubkey(&self) -> Pubkey {
self.payer.pubkey()