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:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user