refactor: Refactor SDK core architecture and trading parameter system

- Refactor SolanaTrade main class structure with improved modular design
- Refactor trading parameter system: BuyParams/SellParams -> InternalBuyParams/InternalSellParams
- Remove deprecated PriorityFee and related utility functions
- Add SwqosSettings to replace legacy SwqosConfig
- Add comprehensive technical documentation:
  • Address Lookup Table usage guide (EN/CN)
  • Nonce Cache mechanism documentation (EN/CN)
  • Trading Parameters documentation (EN/CN)
- Refactor all example code to adapt to new API interfaces
- Optimize public API design and code comments
- Clean up redundant utility functions and type definitions

Impact: 43 files changed, 2013 insertions(+), 1735 deletions(-)
This commit is contained in:
ysq
2025-09-17 00:10:33 +08:00
parent 943c2627f3
commit c5ff7c1cbb
43 changed files with 2010 additions and 1732 deletions
+8 -10
View File
@@ -1,4 +1,3 @@
use crate::common::PriorityFee;
use dashmap::DashMap;
use once_cell::sync::Lazy;
use smallvec::SmallVec;
@@ -20,19 +19,18 @@ static COMPUTE_BUDGET_CACHE: Lazy<DashMap<ComputeBudgetCacheKey, SmallVec<[Instr
#[inline(always)]
pub fn compute_budget_instructions(
priority_fee: &PriorityFee,
unit_price: u64,
unit_limit: u32,
data_size_limit: u32,
is_rpc: bool,
is_buy: bool,
) -> SmallVec<[Instruction; 3]> {
let (unit_price, unit_limit) = if is_rpc {
(priority_fee.rpc_unit_price, priority_fee.rpc_unit_limit)
} else {
(priority_fee.tip_unit_price, priority_fee.tip_unit_limit)
};
// Create cache key
let cache_key = ComputeBudgetCacheKey { data_size_limit, unit_price, unit_limit, is_buy };
let cache_key = ComputeBudgetCacheKey {
data_size_limit,
unit_price: unit_price,
unit_limit: unit_limit,
is_buy,
};
// Try to get from cache first
if let Some(cached_insts) = COMPUTE_BUDGET_CACHE.get(&cache_key) {
+7 -8
View File
@@ -16,12 +16,13 @@ use super::{
compute_budget_manager::compute_budget_instructions,
nonce_manager::{add_nonce_instruction, get_transaction_blockhash},
};
use crate::{common::PriorityFee, trading::MiddlewareManager};
use crate::trading::MiddlewareManager;
/// Build standard RPC transaction
pub async fn build_transaction(
payer: Arc<Keypair>,
priority_fee: &PriorityFee,
unit_limit: u32,
unit_price: u64,
business_instructions: Vec<Instruction>,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
@@ -36,10 +37,8 @@ pub async fn build_transaction(
let mut instructions = Vec::with_capacity(business_instructions.len() + 5);
// Add nonce instruction
if is_buy {
if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) {
return Err(e);
}
if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) {
return Err(e);
}
// Add tip transfer instruction
@@ -53,9 +52,9 @@ pub async fn build_transaction(
// Add compute budget instructions
instructions.extend(compute_budget_instructions(
priority_fee,
unit_price,
unit_limit,
data_size_limit,
!with_tip,
is_buy,
));
+3 -3
View File
@@ -5,7 +5,7 @@ use std::{sync::Arc, time::Instant};
use crate::trading::core::parallel::{buy_parallel_execute, sell_parallel_execute};
use super::{
params::{BuyParams, SellParams},
params::{InternalBuyParams, InternalSellParams},
traits::{InstructionBuilder, TradeExecutor},
};
@@ -26,7 +26,7 @@ impl GenericTradeExecutor {
#[async_trait::async_trait]
impl TradeExecutor for GenericTradeExecutor {
async fn buy_with_tip(&self, params: BuyParams) -> Result<Signature> {
async fn buy_with_tip(&self, params: InternalBuyParams) -> Result<Signature> {
let start = Instant::now();
// Build instructions directly from params to avoid unnecessary cloning
@@ -47,7 +47,7 @@ impl TradeExecutor for GenericTradeExecutor {
buy_parallel_execute(params, final_instructions, self.protocol_name).await
}
async fn sell_with_tip(&self, params: SellParams) -> Result<Signature> {
async fn sell_with_tip(&self, params: InternalSellParams) -> Result<Signature> {
let start = Instant::now();
// Build instructions directly from params to avoid unnecessary cloning
+92 -98
View File
@@ -8,21 +8,21 @@ use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use crate::{
common::PriorityFee,
swqos::{SwqosClient, SwqosType, TradeType},
trading::{common::build_transaction, BuyParams, MiddlewareManager, SellParams},
swqos::{settings::SwqosSettings, SwqosType, TradeType},
trading::{
common::build_transaction, InternalBuyParams, InternalSellParams, MiddlewareManager,
},
};
pub async fn buy_parallel_execute(
params: BuyParams,
params: InternalBuyParams,
instructions: Vec<Instruction>,
protocol_name: &'static str,
) -> Result<Signature> {
parallel_execute(
params.swqos_clients,
params.swqos_settings,
params.payer,
instructions,
params.priority_fee,
params.lookup_table_key,
params.recent_blockhash,
params.data_size_limit,
@@ -36,15 +36,14 @@ pub async fn buy_parallel_execute(
}
pub async fn sell_parallel_execute(
params: SellParams,
params: InternalSellParams,
instructions: Vec<Instruction>,
protocol_name: &'static str,
) -> Result<Signature> {
parallel_execute(
params.swqos_clients,
params.swqos_settings,
params.payer,
instructions,
params.priority_fee,
params.lookup_table_key,
params.recent_blockhash,
0,
@@ -59,10 +58,9 @@ pub async fn sell_parallel_execute(
/// Generic function for parallel transaction execution
async fn parallel_execute(
swqos_clients: Vec<Arc<SwqosClient>>,
swqos_settings: Vec<Arc<SwqosSettings>>,
payer: Arc<Keypair>,
instructions: Vec<Instruction>,
priority_fee: Arc<PriorityFee>,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
data_size_limit: u32,
@@ -72,113 +70,109 @@ async fn parallel_execute(
wait_transaction_confirmed: bool,
with_tip: bool,
) -> Result<Signature> {
if swqos_settings.is_empty() {
return Err(anyhow!("swqos_settings is empty"));
}
if !with_tip
&& swqos_settings
.iter()
.find(|swqos| {
matches!(swqos.swqos_client.as_ref().unwrap().get_swqos_type(), SwqosType::Default)
})
.is_none()
{
return Err(anyhow!("No Rpc Default Swqos configured"));
}
let cores = core_affinity::get_core_ids().unwrap();
let mut handles: Vec<JoinHandle<Result<Signature>>> = Vec::with_capacity(swqos_clients.len());
if is_buy && with_tip && priority_fee.buy_tip_fees.is_empty() {
return Err(anyhow!("buy_tip_fees is empty"));
}
if !is_buy && with_tip && priority_fee.sell_tip_fees.is_empty() {
return Err(anyhow!("sell_tip_fees is empty"));
}
let mut handles: Vec<JoinHandle<Result<Signature>>> = Vec::with_capacity(swqos_settings.len());
let instructions = Arc::new(instructions);
for i in 0..swqos_clients.len() {
let swqos_client = swqos_clients[i].clone();
if !with_tip && !matches!(swqos_client.get_swqos_type(), SwqosType::Default) {
continue;
}
let payer = payer.clone();
let instructions = instructions.clone();
let priority_fee = priority_fee.clone();
let core_id = cores[i % cores.len()];
for i in 0..swqos_settings.len() {
if let Some(swqos_client) = swqos_settings[i].swqos_client.as_ref() {
if !with_tip && !matches!(swqos_client.get_swqos_type(), SwqosType::Default) {
continue;
}
let payer = payer.clone();
let instructions = instructions.clone();
let core_id = cores[i % cores.len()];
let middleware_manager = middleware_manager.clone();
let middleware_manager = middleware_manager.clone();
let swqos_client = swqos_client.clone();
let buy_tip_fee = swqos_settings[i].buy_tip_fee;
let sell_tip_fee = swqos_settings[i].sell_tip_fee;
let unit_limit = swqos_settings[i].unit_limit;
let unit_price = swqos_settings[i].unit_price;
let handle = tokio::spawn(async move {
core_affinity::set_for_current(core_id);
let handle = tokio::spawn(async move {
core_affinity::set_for_current(core_id);
let swqos_type = swqos_client.get_swqos_type();
let mut start = Instant::now();
let swqos_type = swqos_client.get_swqos_type();
let mut start = Instant::now();
let tip_account_str = swqos_client.get_tip_account()?;
let tip_account = Arc::new(Pubkey::from_str(&tip_account_str).unwrap_or_default());
let tip_account_str = swqos_client.get_tip_account()?;
let tip_account = Arc::new(Pubkey::from_str(&tip_account_str).unwrap_or_default());
let tip_amount = if with_tip {
if is_buy {
if priority_fee.buy_tip_fees.len() > i {
priority_fee.buy_tip_fees[i]
let tip_amount = if with_tip {
if is_buy {
buy_tip_fee
} else {
println!(
"❗️❗️❗️[{:?}] - Using buy_tip_fees[0]: {:?}",
swqos_type, priority_fee.buy_tip_fees[0]
);
priority_fee.buy_tip_fees[0]
sell_tip_fee
}
} else {
if priority_fee.sell_tip_fees.len() > i {
priority_fee.sell_tip_fees[i]
} else {
println!(
"❗️❗️❗️[{:?}] - Using sell_tip_fees[0]: {:?}",
swqos_type, priority_fee.sell_tip_fees[0]
);
priority_fee.sell_tip_fees[0]
}
}
} else {
0.0
};
0.0
};
let transaction = build_transaction(
payer,
&priority_fee,
instructions.as_ref().clone(),
lookup_table_key,
recent_blockhash,
data_size_limit,
middleware_manager,
protocol_name,
is_buy,
swqos_type != SwqosType::Default,
&tip_account,
tip_amount,
)
.await?;
println!(
"[{:?}] - Building transaction instructions: {:?}",
swqos_type,
start.elapsed()
);
start = Instant::now();
swqos_client
.send_transaction(
if is_buy { TradeType::Buy } else { TradeType::Sell },
&transaction,
let transaction = build_transaction(
payer,
unit_limit,
unit_price,
instructions.as_ref().clone(),
lookup_table_key,
recent_blockhash,
data_size_limit,
middleware_manager,
protocol_name,
is_buy,
swqos_type != SwqosType::Default,
&tip_account,
tip_amount,
)
.await?;
println!(
"[{:?}] - Submitting transaction instructions: {:?}",
swqos_type,
start.elapsed()
);
println!(
"[{:?}] - Building transaction instructions: {:?}",
swqos_type,
start.elapsed()
);
transaction
.signatures
.first()
.ok_or_else(|| anyhow!("Transaction has no signatures"))
.cloned()
});
start = Instant::now();
handles.push(handle);
swqos_client
.send_transaction(
if is_buy { TradeType::Buy } else { TradeType::Sell },
&transaction,
)
.await?;
println!(
"[{:?}] - Submitting transaction instructions: {:?}",
swqos_type,
start.elapsed()
);
transaction
.signatures
.first()
.ok_or_else(|| anyhow!("Transaction has no signatures"))
.cloned()
});
handles.push(handle);
}
}
// Return as soon as any one succeeds
let (tx, mut rx) = mpsc::channel(swqos_clients.len());
let (tx, mut rx) = mpsc::channel(handles.len());
// Start monitoring tasks
for handle in handles {
+12 -12
View File
@@ -1,9 +1,9 @@
use super::traits::ProtocolParams;
use crate::common::bonding_curve::BondingCurveAccount;
use crate::common::{PriorityFee, SolanaRpcClient};
use crate::common::SolanaRpcClient;
use crate::solana_streamer_sdk::streaming::event_parser::common::EventType;
use crate::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
use crate::swqos::SwqosClient;
use crate::swqos::settings::SwqosSettings;
use crate::trading::common::get_multi_token_balances;
use crate::trading::MiddlewareManager;
use solana_hash::Hash;
@@ -18,56 +18,56 @@ use spl_associated_token_account::get_associated_token_address;
use std::sync::Arc;
/// Buy parameters
#[derive(Clone)]
pub struct BuyParams {
pub struct InternalBuyParams {
pub rpc: Option<Arc<SolanaRpcClient>>,
pub payer: Arc<Keypair>,
pub mint: Pubkey,
pub sol_amount: u64,
pub slippage_basis_points: Option<u64>,
pub priority_fee: Arc<PriorityFee>,
pub lookup_table_key: Option<Pubkey>,
pub recent_blockhash: Hash,
pub data_size_limit: u32,
pub wait_transaction_confirmed: bool,
pub protocol_params: Box<dyn ProtocolParams>,
pub open_seed_optimize: bool,
pub swqos_clients: Vec<Arc<SwqosClient>>,
pub swqos_settings: Vec<Arc<SwqosSettings>>,
pub middleware_manager: Option<Arc<MiddlewareManager>>,
pub create_wsol_ata: bool,
pub close_wsol_ata: bool,
pub create_mint_ata: bool,
pub custom_cu_limit: Option<u32>,
}
/// Sell parameters
#[derive(Clone)]
pub struct SellParams {
pub struct InternalSellParams {
pub rpc: Option<Arc<SolanaRpcClient>>,
pub payer: Arc<Keypair>,
pub mint: Pubkey,
pub token_amount: Option<u64>,
pub slippage_basis_points: Option<u64>,
pub priority_fee: Arc<PriorityFee>,
pub lookup_table_key: Option<Pubkey>,
pub recent_blockhash: Hash,
pub wait_transaction_confirmed: bool,
pub with_tip: bool,
pub protocol_params: Box<dyn ProtocolParams>,
pub open_seed_optimize: bool,
pub swqos_clients: Vec<Arc<SwqosClient>>,
pub swqos_settings: Vec<Arc<SwqosSettings>>,
pub middleware_manager: Option<Arc<MiddlewareManager>>,
pub create_wsol_ata: bool,
pub close_wsol_ata: bool,
pub custom_cu_limit: Option<u32>,
}
impl std::fmt::Debug for BuyParams {
impl std::fmt::Debug for InternalBuyParams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BuyParams: {:?}", self)
write!(f, "InternalBuyParams: {:?}", self)
}
}
impl std::fmt::Debug for SellParams {
impl std::fmt::Debug for InternalSellParams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SellParams: {:?}", self)
write!(f, "InternalSellParams: {:?}", self)
}
}
+5 -5
View File
@@ -1,4 +1,4 @@
use super::params::{BuyParams, SellParams};
use super::params::{InternalBuyParams, InternalSellParams};
use anyhow::Result;
use solana_sdk::{instruction::Instruction, signature::Signature};
@@ -6,9 +6,9 @@ use solana_sdk::{instruction::Instruction, signature::Signature};
#[async_trait::async_trait]
pub trait TradeExecutor: Send + Sync {
/// 使用MEV服务执行买入交易
async fn buy_with_tip(&self, params: BuyParams) -> Result<Signature>;
async fn buy_with_tip(&self, params: InternalBuyParams) -> Result<Signature>;
/// 使用MEV服务执行卖出交易
async fn sell_with_tip(&self, params: SellParams) -> Result<Signature>;
async fn sell_with_tip(&self, params: InternalSellParams) -> Result<Signature>;
/// 获取协议名称
fn protocol_name(&self) -> &'static str;
}
@@ -17,10 +17,10 @@ pub trait TradeExecutor: Send + Sync {
#[async_trait::async_trait]
pub trait InstructionBuilder: Send + Sync {
/// 构建买入指令
async fn build_buy_instructions(&self, params: &BuyParams) -> Result<Vec<Instruction>>;
async fn build_buy_instructions(&self, params: &InternalBuyParams) -> Result<Vec<Instruction>>;
/// 构建卖出指令
async fn build_sell_instructions(&self, params: &SellParams) -> Result<Vec<Instruction>>;
async fn build_sell_instructions(&self, params: &InternalSellParams) -> Result<Vec<Instruction>>;
}
/// 协议特定参数trait - 允许每个协议定义自己的参数
+1 -1
View File
@@ -3,7 +3,7 @@ pub mod core;
pub mod factory;
pub mod middleware;
pub use core::params::{BuyParams, SellParams};
pub use core::params::{InternalBuyParams, InternalSellParams};
pub use core::traits::{InstructionBuilder, TradeExecutor};
pub use factory::TradeFactory;
pub use middleware::{InstructionMiddleware, MiddlewareManager};