refactor: unify transaction building and execution architecture
- Remove redundant transaction builder functions and merge into single build_transaction() - Simplify compute budget manager with unified add_compute_budget_instructions() - Consolidate trade executor interface by removing separate buy/sell methods - Unify BuyParams/SellParams usage, remove *WithTipParams structs - Streamline parallel execution logic and remove TradeType parameter - Delete obsolete files: address_lookup.rs, tip_cache.rs - Clean up nonce manager by removing unused is_using_nonce() function This refactoring reduces code duplication and provides a cleaner, more maintainable API for transaction building and execution across all trading protocols.
This commit is contained in:
@@ -6,6 +6,7 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::match_event;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
@@ -13,9 +14,6 @@ use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::{
|
||||
common::address_lookup::get_address_lookup_table, solana_streamer_sdk::match_event,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
common::address_lookup_cache::AddressLookupTableCache,
|
||||
solana_streamer_sdk::streaming::event_parser::common::EventType,
|
||||
@@ -109,13 +107,10 @@ async fn setup_lookup_table_cache(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
lookup_table_address: Pubkey,
|
||||
) -> AnyResult<()> {
|
||||
let lookup_table = get_address_lookup_table(client, &lookup_table_address)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to get address lookup table: {}", e))?;
|
||||
|
||||
AddressLookupTableCache::get_instance()
|
||||
.add_or_update_table(lookup_table_address, Some(lookup_table));
|
||||
|
||||
.set_address_lookup_table(client, &lookup_table_address)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to set address lookup table: {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,364 +0,0 @@
|
||||
use solana_program::{
|
||||
address_lookup_table::{
|
||||
instruction::{
|
||||
create_lookup_table as create_lookup_table_instruction,
|
||||
extend_lookup_table as extend_lookup_table_instruction,
|
||||
freeze_lookup_table as freeze_lookup_table_instruction,
|
||||
},
|
||||
state::AddressLookupTable,
|
||||
},
|
||||
instruction::Instruction,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use solana_sdk::{
|
||||
message::{v0::Message as MessageV0, AddressLookupTableAccount, VersionedMessage},
|
||||
signature::{Keypair, Signer},
|
||||
transaction::{Transaction, VersionedTransaction},
|
||||
};
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use crate::{common::SolanaRpcClient, constants};
|
||||
|
||||
/// Create address lookup table (if it doesn't exist)
|
||||
pub async fn create_lookup_table_if_not_exists(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
authority: &Keypair,
|
||||
payer: &Keypair,
|
||||
) -> Result<Pubkey, Box<dyn std::error::Error>> {
|
||||
// 1. Calculate the expected lookup table address
|
||||
let recent_slot = client.get_slot().await?;
|
||||
let (create_ix, lookup_table_address) =
|
||||
create_lookup_table_instruction(authority.pubkey(), payer.pubkey(), recent_slot);
|
||||
|
||||
// 2. Create new table
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[create_ix],
|
||||
Some(&payer.pubkey()),
|
||||
&[payer, authority],
|
||||
blockhash,
|
||||
);
|
||||
|
||||
client.send_and_confirm_transaction(&transaction).await?;
|
||||
|
||||
Ok(lookup_table_address)
|
||||
}
|
||||
|
||||
/// Add addresses to lookup table
|
||||
pub async fn extend_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
payer: &Keypair,
|
||||
authority: &Keypair,
|
||||
lookup_table_address: &Pubkey,
|
||||
addresses: Vec<Pubkey>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let extend_ix = extend_lookup_table_instruction(
|
||||
*lookup_table_address,
|
||||
authority.pubkey(),
|
||||
Some(payer.pubkey()),
|
||||
addresses.clone(),
|
||||
);
|
||||
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[extend_ix],
|
||||
Some(&payer.pubkey()),
|
||||
&[payer, authority],
|
||||
blockhash,
|
||||
);
|
||||
|
||||
client.send_and_confirm_transaction(&transaction).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Freeze lookup table to prevent further modifications
|
||||
pub async fn freeze_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
payer: &Keypair,
|
||||
authority: &Keypair,
|
||||
lookup_table_address: &Pubkey,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let freeze_ix = freeze_lookup_table_instruction(*lookup_table_address, authority.pubkey());
|
||||
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[freeze_ix],
|
||||
Some(&payer.pubkey()),
|
||||
&[payer, authority],
|
||||
blockhash,
|
||||
);
|
||||
|
||||
client.send_and_confirm_transaction(&transaction).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get lookup table information
|
||||
pub async fn get_address_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
lookup_table_address: &Pubkey,
|
||||
) -> Result<AddressLookupTableAccount, Box<dyn Error>> {
|
||||
let account = client.get_account(lookup_table_address).await?;
|
||||
let lookup_table = AddressLookupTable::deserialize(&account.data)?;
|
||||
|
||||
let address_lookup_table_account = AddressLookupTableAccount {
|
||||
key: *lookup_table_address,
|
||||
addresses: lookup_table.addresses.to_vec(),
|
||||
};
|
||||
|
||||
Ok(address_lookup_table_account)
|
||||
}
|
||||
|
||||
/// Send transaction using lookup table
|
||||
pub async fn send_transaction_with_lut(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
instructions: Vec<Instruction>,
|
||||
payer: &Keypair,
|
||||
signers: Vec<&Keypair>,
|
||||
address_lookup_tables: Vec<AddressLookupTableAccount>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
|
||||
let message = VersionedMessage::V0(MessageV0::try_compile(
|
||||
&payer.pubkey(),
|
||||
&instructions,
|
||||
&address_lookup_tables,
|
||||
blockhash,
|
||||
)?);
|
||||
|
||||
let tx = VersionedTransaction::try_new(message, &signers)?;
|
||||
|
||||
let signature = client.send_and_confirm_transaction(&tx).await?;
|
||||
|
||||
println!("Transaction confirmed: {}", signature);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send transaction using a specific subset of addresses from lookup table
|
||||
pub async fn send_transaction_with_filtered_lut(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
instructions: Vec<Instruction>,
|
||||
payer: &Keypair,
|
||||
signers: Vec<&Keypair>,
|
||||
lookup_table: AddressLookupTableAccount,
|
||||
address_indices_to_use: &[usize], // List of address indices to use
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Create a new lookup table account containing only selected addresses
|
||||
let filtered_addresses: Vec<Pubkey> = address_indices_to_use
|
||||
.iter()
|
||||
.filter_map(|&index| lookup_table.addresses.get(index).copied())
|
||||
.collect();
|
||||
|
||||
println!("Selected {} addresses from lookup table for transaction", filtered_addresses.len());
|
||||
for (i, addr) in filtered_addresses.iter().enumerate() {
|
||||
println!("Using address {}: {}", i, addr);
|
||||
}
|
||||
|
||||
let filtered_lookup_table =
|
||||
AddressLookupTableAccount { key: lookup_table.key, addresses: filtered_addresses };
|
||||
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
|
||||
let message = VersionedMessage::V0(MessageV0::try_compile(
|
||||
&payer.pubkey(),
|
||||
&instructions,
|
||||
&[filtered_lookup_table],
|
||||
blockhash,
|
||||
)?);
|
||||
|
||||
let tx = VersionedTransaction::try_new(message, &signers)?;
|
||||
|
||||
let signature = client.send_and_confirm_transaction(&tx).await?;
|
||||
|
||||
println!("Transaction confirmed: {}", signature);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get recent block slot for creating lookup table
|
||||
pub async fn get_recent_slot(client: Arc<SolanaRpcClient>) -> Result<u64, Box<dyn Error>> {
|
||||
let slot = client.get_slot().await?;
|
||||
Ok(slot)
|
||||
}
|
||||
|
||||
/// Send transaction using specified address list
|
||||
///
|
||||
/// This method accepts a set of target addresses, automatically finds their indices in the lookup table,
|
||||
/// then uses these addresses to create a filtered lookup table for sending transactions
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `instructions` - Transaction instructions
|
||||
/// * `payer` - Account that pays transaction fees
|
||||
/// * `signers` - Transaction signers
|
||||
/// * `lookup_table` - Address lookup table
|
||||
/// * `addresses_to_use` - List of addresses to use
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns transaction signature on success, error on failure
|
||||
pub async fn send_transaction_with_addresses(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
instructions: Vec<Instruction>,
|
||||
payer: &Keypair,
|
||||
signers: Vec<&Keypair>,
|
||||
lookup_table: AddressLookupTableAccount,
|
||||
addresses_to_use: &[Pubkey],
|
||||
) -> Result<String, Box<dyn Error>> {
|
||||
// Build address to index mapping
|
||||
let mut address_to_index = std::collections::HashMap::new();
|
||||
for (i, addr) in lookup_table.addresses.iter().enumerate() {
|
||||
address_to_index.insert(*addr, i);
|
||||
}
|
||||
|
||||
// Find indices of all existing addresses
|
||||
let mut indices_to_use = Vec::new();
|
||||
let mut found_addresses = Vec::new();
|
||||
let mut missing_addresses = Vec::new();
|
||||
|
||||
for addr in addresses_to_use {
|
||||
if let Some(&index) = address_to_index.get(addr) {
|
||||
indices_to_use.push(index);
|
||||
found_addresses.push(*addr);
|
||||
} else {
|
||||
missing_addresses.push(*addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any addresses were not found
|
||||
if !missing_addresses.is_empty() {
|
||||
println!("Warning: {} addresses not found in lookup table", missing_addresses.len());
|
||||
for (i, addr) in missing_addresses.iter().enumerate() {
|
||||
println!("Address not found {}: {}", i, addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if no addresses were found
|
||||
if indices_to_use.is_empty() {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"No specified addresses found in lookup table",
|
||||
)));
|
||||
}
|
||||
|
||||
// Create a new lookup table account containing only selected addresses
|
||||
let filtered_addresses: Vec<Pubkey> = indices_to_use
|
||||
.iter()
|
||||
.filter_map(|&index| lookup_table.addresses.get(index).copied())
|
||||
.collect();
|
||||
|
||||
println!("Selected {} addresses from lookup table for transaction", filtered_addresses.len());
|
||||
for (i, addr) in filtered_addresses.iter().enumerate() {
|
||||
println!("Using address {}: {}", i, addr);
|
||||
}
|
||||
|
||||
let filtered_lookup_table =
|
||||
AddressLookupTableAccount { key: lookup_table.key, addresses: filtered_addresses };
|
||||
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
|
||||
let message = VersionedMessage::V0(MessageV0::try_compile(
|
||||
&payer.pubkey(),
|
||||
&instructions,
|
||||
&[filtered_lookup_table],
|
||||
blockhash,
|
||||
)?);
|
||||
|
||||
let tx = VersionedTransaction::try_new(message, &signers)?;
|
||||
|
||||
let signature = client.send_and_confirm_transaction(&tx).await?;
|
||||
|
||||
println!("Transaction confirmed: {}", signature);
|
||||
Ok(signature.to_string())
|
||||
}
|
||||
|
||||
pub async fn create_pumpfun_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
payer: &Keypair,
|
||||
authority: &Keypair,
|
||||
) -> Result<Pubkey, Box<dyn Error>> {
|
||||
let recent_slot = client.get_slot().await?;
|
||||
let (create_ix, lookup_table_address) =
|
||||
create_lookup_table_instruction(authority.pubkey(), payer.pubkey(), recent_slot);
|
||||
|
||||
let blockhash = client.get_latest_blockhash().await?;
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[create_ix],
|
||||
Some(&payer.pubkey()),
|
||||
&[payer, authority],
|
||||
blockhash,
|
||||
);
|
||||
|
||||
client.send_and_confirm_transaction(&transaction).await?;
|
||||
|
||||
Ok(lookup_table_address)
|
||||
}
|
||||
|
||||
pub async fn add_pumpfun_address_to_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
payer: &Keypair,
|
||||
authority: &Keypair,
|
||||
lookup_table_address: &Pubkey,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let addresses = get_pumpfun_addresses(payer.pubkey(), vec![]);
|
||||
extend_lookup_table(client, payer, authority, lookup_table_address, addresses).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn extend_pumpfun_address_to_lookup_table(
|
||||
client: Arc<SolanaRpcClient>,
|
||||
payer: &Keypair,
|
||||
authority: &Keypair,
|
||||
lookup_table_address: &Pubkey,
|
||||
addresses: Vec<Pubkey>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
extend_lookup_table(client, payer, authority, lookup_table_address, addresses).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_pumpfun_addresses(payer: Pubkey, include_addresses: Vec<Pubkey>) -> Vec<Pubkey> {
|
||||
let mut addresses = vec![
|
||||
payer,
|
||||
constants::pumpfun::accounts::PUMPFUN,
|
||||
constants::pumpfun::accounts::SYSTEM_PROGRAM,
|
||||
constants::pumpfun::accounts::TOKEN_PROGRAM,
|
||||
constants::pumpfun::accounts::RENT,
|
||||
constants::pumpfun::accounts::EVENT_AUTHORITY,
|
||||
constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM,
|
||||
constants::pumpfun::global_constants::GLOBAL_ACCOUNT,
|
||||
constants::pumpfun::global_constants::FEE_RECIPIENT,
|
||||
];
|
||||
|
||||
addresses.extend(include_addresses);
|
||||
|
||||
addresses
|
||||
}
|
||||
|
||||
pub fn get_pumpfun_filtered_addresses(
|
||||
payer: Pubkey,
|
||||
include_addresses: Vec<Pubkey>,
|
||||
) -> Vec<Pubkey> {
|
||||
let mut addresses = vec![
|
||||
payer,
|
||||
constants::pumpfun::accounts::PUMPFUN,
|
||||
constants::pumpfun::accounts::SYSTEM_PROGRAM,
|
||||
constants::pumpfun::accounts::TOKEN_PROGRAM,
|
||||
constants::pumpfun::accounts::RENT,
|
||||
constants::pumpfun::accounts::EVENT_AUTHORITY,
|
||||
constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM,
|
||||
constants::pumpfun::global_constants::GLOBAL_ACCOUNT,
|
||||
constants::pumpfun::global_constants::FEE_RECIPIENT,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_1,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_2,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_3,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_4,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_5,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_6,
|
||||
constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_7,
|
||||
// constants::pumpfun::global_constants::PUMPFUN_AMM_FEE_8,
|
||||
];
|
||||
|
||||
addresses.extend(include_addresses);
|
||||
|
||||
addresses
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
use dashmap::DashMap;
|
||||
use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use solana_sdk::{
|
||||
address_lookup_table::state::AddressLookupTable, message::AddressLookupTableAccount,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
use crate::common::SolanaRpcClient;
|
||||
|
||||
/// AddressLookupTableInfo struct, stores address lookup table related information
|
||||
#[derive(Clone)]
|
||||
@@ -28,8 +36,24 @@ impl AddressLookupTableCache {
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Get lookup table information
|
||||
pub async fn set_address_lookup_table(
|
||||
&self,
|
||||
client: Arc<SolanaRpcClient>,
|
||||
lookup_table_address: &Pubkey,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let account = client.get_account(lookup_table_address).await?;
|
||||
let lookup_table = AddressLookupTable::deserialize(&account.data)?;
|
||||
let address_lookup_table_account = AddressLookupTableAccount {
|
||||
key: *lookup_table_address,
|
||||
addresses: lookup_table.addresses.to_vec(),
|
||||
};
|
||||
self.add_or_update_table(lookup_table_address.clone(), Some(address_lookup_table_account));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add or update address lookup table information - lock-free implementation
|
||||
pub fn add_or_update_table(
|
||||
fn add_or_update_table(
|
||||
&self,
|
||||
lookup_table_address: Pubkey,
|
||||
address_lookup_table: Option<AddressLookupTableAccount>,
|
||||
@@ -51,42 +75,8 @@ impl AddressLookupTableCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove address lookup table - lock-free implementation
|
||||
pub fn remove_table(&self, lookup_table_address: &Pubkey) -> bool {
|
||||
self.tables.remove(lookup_table_address).is_some()
|
||||
}
|
||||
|
||||
/// Get address lookup table information - lock-free implementation
|
||||
pub fn get_table(&self, lookup_table_address: &Pubkey) -> Option<AddressLookupTableInfo> {
|
||||
self.tables.get(lookup_table_address).map(|entry| entry.value().clone())
|
||||
}
|
||||
|
||||
/// Get all table addresses - lock-free implementation
|
||||
pub fn get_all_table_addresses(&self) -> Vec<Pubkey> {
|
||||
self.tables.iter().map(|entry| *entry.key()).collect()
|
||||
}
|
||||
|
||||
/// Check if table exists - lock-free implementation
|
||||
pub fn table_exists(&self, lookup_table_address: &Pubkey) -> bool {
|
||||
self.tables.contains_key(lookup_table_address)
|
||||
}
|
||||
|
||||
/// Update address lookup table content - lock-free implementation
|
||||
pub fn update_table_content(
|
||||
&self,
|
||||
lookup_table_address: &Pubkey,
|
||||
address_lookup_table: AddressLookupTableAccount,
|
||||
) -> bool {
|
||||
if let Some(mut entry) = self.tables.get_mut(lookup_table_address) {
|
||||
entry.address_lookup_table = Some(address_lookup_table);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Get table content - high-performance lock-free implementation
|
||||
pub fn get_table_content(&self, lookup_table_address: &Pubkey) -> AddressLookupTableAccount {
|
||||
fn get_table_content(&self, lookup_table_address: &Pubkey) -> AddressLookupTableAccount {
|
||||
let result = self
|
||||
.tables
|
||||
.get(lookup_table_address)
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
pub mod address_lookup;
|
||||
// pub mod address_lookup;
|
||||
pub mod nonce_cache;
|
||||
pub mod tip_cache;
|
||||
pub mod types;
|
||||
pub mod address_lookup_cache;
|
||||
pub mod subscription_handle;
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
/// TipCache 单例,用于存储和管理 tip 金额
|
||||
pub struct TipCache {
|
||||
/// tip 金额
|
||||
tip_amount: Mutex<f64>,
|
||||
}
|
||||
|
||||
static TIP_CACHE: OnceLock<Arc<TipCache>> = OnceLock::new();
|
||||
|
||||
impl TipCache {
|
||||
/// 获取 TipCache 单例实例
|
||||
pub fn get_instance() -> Arc<TipCache> {
|
||||
TIP_CACHE
|
||||
.get_or_init(|| {
|
||||
Arc::new(TipCache {
|
||||
tip_amount: Mutex::new(0.001),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// 初始化 tip 金额
|
||||
pub fn init(&self, tip_amount: Option<f64>) {
|
||||
let amount = tip_amount.unwrap_or(0.001);
|
||||
self.update_tip(amount);
|
||||
}
|
||||
|
||||
/// 获取 tip 金额
|
||||
pub fn get_tip(&self) -> f64 {
|
||||
*self.tip_amount.lock().unwrap()
|
||||
}
|
||||
|
||||
/// 更新 tip 金额
|
||||
pub fn update_tip(&self, amount: f64) {
|
||||
*self.tip_amount.lock().unwrap() = amount;
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -38,9 +38,10 @@ pub struct PriorityFee {
|
||||
pub tip_unit_price: u64,
|
||||
pub rpc_unit_limit: u32,
|
||||
pub rpc_unit_price: u64,
|
||||
pub buy_tip_fee: f64,
|
||||
// 与 swqos 顺序一致 (Matches the order of swqos)
|
||||
pub buy_tip_fees: Vec<f64>,
|
||||
pub sell_tip_fee: f64,
|
||||
// 与 swqos 顺序一致 (Matches the order of swqos)
|
||||
pub sell_tip_fees: Vec<f64>,
|
||||
}
|
||||
|
||||
impl Default for PriorityFee {
|
||||
@@ -50,9 +51,10 @@ impl Default for PriorityFee {
|
||||
tip_unit_price: DEFAULT_TIP_UNIT_PRICE,
|
||||
rpc_unit_limit: DEFAULT_RPC_UNIT_LIMIT,
|
||||
rpc_unit_price: DEFAULT_RPC_UNIT_PRICE,
|
||||
buy_tip_fee: DEFAULT_BUY_TIP_FEE,
|
||||
buy_tip_fees: vec![],
|
||||
sell_tip_fee: DEFAULT_SELL_TIP_FEE,
|
||||
// 与 swqos 顺序一致 (Matches the order of swqos)
|
||||
buy_tip_fees: vec![DEFAULT_BUY_TIP_FEE],
|
||||
// 与 swqos 顺序一致 (Matches the order of swqos)
|
||||
sell_tip_fees: vec![DEFAULT_SELL_TIP_FEE],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-55
@@ -7,6 +7,7 @@ pub mod trading;
|
||||
pub mod utils;
|
||||
pub use solana_streamer_sdk;
|
||||
|
||||
use crate::constants::trade::trade::DEFAULT_SLIPPAGE;
|
||||
use crate::swqos::SwqosConfig;
|
||||
use crate::trading::core::params::BonkParams;
|
||||
use crate::trading::core::params::PumpFunParams;
|
||||
@@ -53,7 +54,7 @@ impl Clone for SolanaTrade {
|
||||
|
||||
impl SolanaTrade {
|
||||
#[inline]
|
||||
pub async fn new(payer: Arc<Keypair>, mut trade_config: TradeConfig) -> Self {
|
||||
pub async fn new(payer: Arc<Keypair>, trade_config: TradeConfig) -> Self {
|
||||
if CryptoProvider::get_default().is_none() {
|
||||
let _ = default_provider()
|
||||
.install_default()
|
||||
@@ -62,23 +63,8 @@ impl SolanaTrade {
|
||||
|
||||
let rpc_url = trade_config.rpc_url.clone();
|
||||
let swqos_configs = trade_config.swqos_configs.clone();
|
||||
let mut priority_fee = trade_config.priority_fee.clone();
|
||||
let priority_fee = trade_config.priority_fee.clone();
|
||||
let commitment = trade_config.commitment.clone();
|
||||
if priority_fee.buy_tip_fees.len() < swqos_configs.len() {
|
||||
// Fill the array, only fill the missing elements
|
||||
let mut buy_tip_fees = priority_fee.buy_tip_fees.clone();
|
||||
let default_fee = priority_fee.buy_tip_fee;
|
||||
// Calculate the number of elements that need to be added
|
||||
let missing_count = swqos_configs.len() - buy_tip_fees.len();
|
||||
// Add missing elements using default values
|
||||
for _ in 0..missing_count {
|
||||
buy_tip_fees.push(default_fee);
|
||||
}
|
||||
// Update buy_tip_fees in priority_fee
|
||||
priority_fee.buy_tip_fees = buy_tip_fees;
|
||||
trade_config.priority_fee = priority_fee.clone();
|
||||
}
|
||||
|
||||
let mut swqos_clients: Vec<Arc<SwqosClient>> = vec![];
|
||||
|
||||
for swqos in swqos_configs {
|
||||
@@ -160,6 +146,12 @@ impl SolanaTrade {
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
wait_transaction_confirmed: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
if slippage_basis_points.is_none() {
|
||||
println!(
|
||||
"slippage_basis_points is none, use default slippage basis points: {}",
|
||||
DEFAULT_SLIPPAGE
|
||||
);
|
||||
}
|
||||
let executor = TradeFactory::create_executor(dex_type.clone());
|
||||
let protocol_params = extension_params;
|
||||
|
||||
@@ -179,23 +171,8 @@ impl SolanaTrade {
|
||||
protocol_params: protocol_params.clone(),
|
||||
};
|
||||
if custom_priority_fee.is_some() {
|
||||
let mut custom_priority_fee = custom_priority_fee.unwrap();
|
||||
// Fill the array, only fill the missing elements
|
||||
if custom_priority_fee.buy_tip_fees.len() < self.swqos_clients.len() {
|
||||
let mut buy_tip_fees = custom_priority_fee.buy_tip_fees.clone();
|
||||
let default_fee = custom_priority_fee.buy_tip_fee;
|
||||
// Calculate the number of elements that need to be added
|
||||
let missing_count = self.swqos_clients.len() - buy_tip_fees.len();
|
||||
// Add missing elements using default values
|
||||
for _ in 0..missing_count {
|
||||
buy_tip_fees.push(default_fee);
|
||||
}
|
||||
// Update buy_tip_fees in custom_priority_fee
|
||||
custom_priority_fee.buy_tip_fees = buy_tip_fees;
|
||||
}
|
||||
buy_params.priority_fee = custom_priority_fee;
|
||||
buy_params.priority_fee = custom_priority_fee.unwrap();
|
||||
}
|
||||
let buy_with_tip_params = buy_params.clone().with_tip(self.swqos_clients.clone());
|
||||
|
||||
// Validate protocol params
|
||||
let is_valid_params = match dex_type {
|
||||
@@ -216,7 +193,9 @@ impl SolanaTrade {
|
||||
return Err(anyhow::anyhow!("Invalid protocol params for Trade"));
|
||||
}
|
||||
|
||||
executor.buy_with_tip(buy_with_tip_params, self.middleware_manager.clone()).await
|
||||
executor
|
||||
.buy_with_tip(buy_params, self.swqos_clients.clone(), self.middleware_manager.clone())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Execute a sell order for a specified token
|
||||
@@ -259,6 +238,12 @@ impl SolanaTrade {
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
wait_transaction_confirmed: bool,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
if slippage_basis_points.is_none() {
|
||||
println!(
|
||||
"slippage_basis_points is none, use default slippage basis points: {}",
|
||||
DEFAULT_SLIPPAGE
|
||||
);
|
||||
}
|
||||
let executor = TradeFactory::create_executor(dex_type.clone());
|
||||
let protocol_params = extension_params;
|
||||
|
||||
@@ -275,25 +260,11 @@ impl SolanaTrade {
|
||||
recent_blockhash,
|
||||
wait_transaction_confirmed: wait_transaction_confirmed,
|
||||
protocol_params: protocol_params.clone(),
|
||||
with_tip: with_tip,
|
||||
};
|
||||
if custom_priority_fee.is_some() {
|
||||
let mut custom_priority_fee = custom_priority_fee.unwrap();
|
||||
// Fill the array, only fill the missing elements
|
||||
if custom_priority_fee.buy_tip_fees.len() < self.swqos_clients.len() {
|
||||
let mut buy_tip_fees = custom_priority_fee.buy_tip_fees.clone();
|
||||
let default_fee = custom_priority_fee.buy_tip_fee;
|
||||
// Calculate the number of elements that need to be added
|
||||
let missing_count = self.swqos_clients.len() - buy_tip_fees.len();
|
||||
// Add missing elements using default values
|
||||
for _ in 0..missing_count {
|
||||
buy_tip_fees.push(default_fee);
|
||||
}
|
||||
// Update buy_tip_fees in custom_priority_fee
|
||||
custom_priority_fee.buy_tip_fees = buy_tip_fees;
|
||||
}
|
||||
sell_params.priority_fee = custom_priority_fee;
|
||||
sell_params.priority_fee = custom_priority_fee.unwrap();
|
||||
}
|
||||
let sell_with_tip_params = sell_params.clone().with_tip(self.swqos_clients.clone());
|
||||
|
||||
// Validate protocol params
|
||||
let is_valid_params = match dex_type {
|
||||
@@ -315,11 +286,9 @@ impl SolanaTrade {
|
||||
}
|
||||
|
||||
// Execute sell based on tip preference
|
||||
if with_tip {
|
||||
executor.sell_with_tip(sell_with_tip_params, self.middleware_manager.clone()).await
|
||||
} else {
|
||||
executor.sell(sell_params, self.middleware_manager.clone()).await
|
||||
}
|
||||
executor
|
||||
.sell_with_tip(sell_params, self.swqos_clients.clone(), self.middleware_manager.clone())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Execute a sell order for a percentage of the specified token amount
|
||||
|
||||
@@ -2,72 +2,27 @@ use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruct
|
||||
|
||||
use crate::common::PriorityFee;
|
||||
|
||||
/// 为RPC交易添加计算预算指令
|
||||
pub fn add_rpc_compute_budget_instructions(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
priority_fee: &PriorityFee,
|
||||
data_size_limit: u32,
|
||||
) {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_price(
|
||||
priority_fee.rpc_unit_price,
|
||||
));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
priority_fee.rpc_unit_limit,
|
||||
));
|
||||
}
|
||||
|
||||
/// 为带小费的交易添加计算预算指令
|
||||
pub fn add_tip_compute_budget_instructions(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
priority_fee: &PriorityFee,
|
||||
data_size_limit: u32,
|
||||
) {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_price(
|
||||
priority_fee.tip_unit_price,
|
||||
));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
priority_fee.tip_unit_limit,
|
||||
));
|
||||
}
|
||||
|
||||
/// 通用的计算预算指令添加函数
|
||||
/// 为交易添加计算预算指令
|
||||
pub fn add_compute_budget_instructions(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
unit_price: u64,
|
||||
unit_limit: u32,
|
||||
priority_fee: &PriorityFee,
|
||||
data_size_limit: u32,
|
||||
is_rpc: bool,
|
||||
is_buy: bool,
|
||||
) {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_price(unit_price));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(unit_limit));
|
||||
}
|
||||
|
||||
pub fn add_sell_compute_budget_instructions(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
priority_fee: &PriorityFee,
|
||||
) {
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_price(
|
||||
priority_fee.rpc_unit_price,
|
||||
));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
priority_fee.rpc_unit_limit,
|
||||
));
|
||||
}
|
||||
|
||||
/// 为带小费的交易添加计算预算指令
|
||||
pub fn add_sell_tip_compute_budget_instructions(
|
||||
instructions: &mut Vec<Instruction>,
|
||||
priority_fee: &PriorityFee,
|
||||
) {
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_price(
|
||||
priority_fee.tip_unit_price,
|
||||
));
|
||||
instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
priority_fee.tip_unit_limit,
|
||||
));
|
||||
if is_buy {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
||||
}
|
||||
if is_rpc {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_compute_unit_price(priority_fee.rpc_unit_price));
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_compute_unit_limit(priority_fee.rpc_unit_limit));
|
||||
} else {
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_compute_unit_price(priority_fee.tip_unit_price));
|
||||
instructions
|
||||
.push(ComputeBudgetInstruction::set_compute_unit_limit(priority_fee.tip_unit_limit));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,3 @@ pub fn get_transaction_blockhash(recent_blockhash: Hash) -> Hash {
|
||||
recent_blockhash
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if using nonce account
|
||||
pub fn is_using_nonce() -> bool {
|
||||
let nonce_cache = NonceCache::get_instance();
|
||||
let nonce_info = nonce_cache.get_nonce_info();
|
||||
nonce_info.nonce_account.is_some()
|
||||
}
|
||||
|
||||
@@ -13,21 +13,13 @@ use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
address_lookup_manager::get_address_lookup_table_accounts,
|
||||
compute_budget_manager::{
|
||||
add_rpc_compute_budget_instructions, add_tip_compute_budget_instructions,
|
||||
},
|
||||
compute_budget_manager::add_compute_budget_instructions,
|
||||
nonce_manager::{add_nonce_instruction, get_transaction_blockhash},
|
||||
};
|
||||
use crate::{
|
||||
common::PriorityFee,
|
||||
trading::{
|
||||
common::{add_sell_compute_budget_instructions, add_sell_tip_compute_budget_instructions},
|
||||
MiddlewareManager,
|
||||
},
|
||||
};
|
||||
use crate::{common::PriorityFee, trading::MiddlewareManager};
|
||||
|
||||
/// 构建标准的RPC交易
|
||||
pub async fn build_rpc_transaction(
|
||||
pub async fn build_transaction(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
@@ -37,75 +29,37 @@ pub async fn build_rpc_transaction(
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
// 添加nonce指令
|
||||
if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// 添加计算预算指令
|
||||
add_rpc_compute_budget_instructions(&mut instructions, priority_fee, data_size_limit);
|
||||
|
||||
// 添加业务指令
|
||||
instructions.extend(business_instructions);
|
||||
|
||||
// 获取交易使用的blockhash
|
||||
let blockhash = get_transaction_blockhash(recent_blockhash);
|
||||
|
||||
// 获取地址查找表账户
|
||||
let address_lookup_table_accounts = get_address_lookup_table_accounts(lookup_table_key).await;
|
||||
|
||||
// 构建交易
|
||||
build_versioned_transaction(
|
||||
payer,
|
||||
instructions,
|
||||
address_lookup_table_accounts,
|
||||
blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 构建带小费的交易
|
||||
pub async fn build_tip_transaction(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
with_tip: bool,
|
||||
tip_account: &Pubkey,
|
||||
tip_amount: f64,
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
data_size_limit: u32,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
// 添加nonce指令
|
||||
if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) {
|
||||
return Err(e);
|
||||
if is_buy {
|
||||
if let Err(e) = add_nonce_instruction(&mut instructions, payer.as_ref()) {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加计算预算指令
|
||||
add_tip_compute_budget_instructions(&mut instructions, priority_fee, data_size_limit);
|
||||
add_compute_budget_instructions(&mut instructions, priority_fee, data_size_limit, true, is_buy);
|
||||
|
||||
// 添加业务指令
|
||||
instructions.extend(business_instructions);
|
||||
|
||||
// 添加小费转账指令
|
||||
instructions.push(transfer(
|
||||
&payer.pubkey(),
|
||||
tip_account,
|
||||
sol_str_to_lamports(tip_amount.to_string().as_str()).unwrap_or(0),
|
||||
));
|
||||
if with_tip {
|
||||
instructions.push(transfer(
|
||||
&payer.pubkey(),
|
||||
tip_account,
|
||||
sol_str_to_lamports(tip_amount.to_string().as_str()).unwrap_or(0),
|
||||
));
|
||||
}
|
||||
|
||||
// 获取交易使用的blockhash
|
||||
let blockhash = get_transaction_blockhash(recent_blockhash);
|
||||
let blockhash =
|
||||
if is_buy { get_transaction_blockhash(recent_blockhash) } else { recent_blockhash };
|
||||
|
||||
// 获取地址查找表账户
|
||||
let address_lookup_table_accounts = get_address_lookup_table_accounts(lookup_table_key).await;
|
||||
@@ -150,136 +104,3 @@ async fn build_versioned_transaction(
|
||||
|
||||
Ok(transaction)
|
||||
}
|
||||
|
||||
/// 构建带小费的交易(使用PriorityFee中的tip_fee)
|
||||
pub async fn build_tip_transaction_with_priority_fee(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
tip_account: &Pubkey,
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
data_size_limit: u32,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
build_tip_transaction(
|
||||
payer,
|
||||
priority_fee,
|
||||
business_instructions,
|
||||
tip_account,
|
||||
priority_fee.buy_tip_fee,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 构建标准的RPC交易
|
||||
pub async fn build_sell_transaction(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
// 添加计算预算指令
|
||||
add_sell_compute_budget_instructions(&mut instructions, priority_fee);
|
||||
|
||||
// 添加业务指令
|
||||
instructions.extend(business_instructions);
|
||||
|
||||
// 获取地址查找表账户
|
||||
let address_lookup_table_accounts = get_address_lookup_table_accounts(lookup_table_key).await;
|
||||
|
||||
// 构建交易
|
||||
build_versioned_transaction(
|
||||
payer,
|
||||
instructions,
|
||||
address_lookup_table_accounts,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn build_sell_tip_transaction(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
tip_account: &Pubkey,
|
||||
tip_amount: f64,
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
let mut instructions = vec![];
|
||||
|
||||
// 添加计算预算指令
|
||||
add_sell_tip_compute_budget_instructions(&mut instructions, priority_fee);
|
||||
|
||||
// 添加业务指令
|
||||
instructions.extend(business_instructions);
|
||||
|
||||
// 添加小费转账指令
|
||||
instructions.push(transfer(
|
||||
&payer.pubkey(),
|
||||
tip_account,
|
||||
sol_str_to_lamports(tip_amount.to_string().as_str()).unwrap_or(0),
|
||||
));
|
||||
|
||||
// 获取地址查找表账户
|
||||
let address_lookup_table_accounts = get_address_lookup_table_accounts(lookup_table_key).await;
|
||||
|
||||
// 构建交易
|
||||
build_versioned_transaction(
|
||||
payer,
|
||||
instructions,
|
||||
address_lookup_table_accounts,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn build_sell_tip_transaction_with_priority_fee(
|
||||
payer: Arc<Keypair>,
|
||||
priority_fee: &PriorityFee,
|
||||
business_instructions: Vec<Instruction>,
|
||||
tip_account: &Pubkey,
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
) -> Result<VersionedTransaction, anyhow::Error> {
|
||||
build_sell_tip_transaction(
|
||||
payer,
|
||||
priority_fee,
|
||||
business_instructions,
|
||||
tip_account,
|
||||
priority_fee.sell_tip_fee,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
+17
-121
@@ -1,19 +1,13 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::Result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
parallel::parallel_execute_with_tips,
|
||||
params::{BuyParams, BuyWithTipParams, SellParams, SellWithTipParams},
|
||||
params::{BuyParams, SellParams},
|
||||
timer::TradeTimer,
|
||||
traits::{InstructionBuilder, TradeExecutor},
|
||||
};
|
||||
use crate::{
|
||||
swqos::TradeType,
|
||||
trading::{
|
||||
common::{build_rpc_transaction, build_sell_transaction},
|
||||
middleware::MiddlewareManager,
|
||||
},
|
||||
};
|
||||
use crate::{swqos::SwqosClient, trading::middleware::MiddlewareManager};
|
||||
|
||||
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024;
|
||||
|
||||
@@ -34,66 +28,15 @@ impl GenericTradeExecutor {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TradeExecutor for GenericTradeExecutor {
|
||||
async fn buy(
|
||||
&self,
|
||||
mut params: BuyParams,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()> {
|
||||
if params.data_size_limit == 0 {
|
||||
params.data_size_limit = MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
|
||||
}
|
||||
if params.rpc.is_none() {
|
||||
return Err(anyhow!("RPC is not set"));
|
||||
}
|
||||
let rpc = params.rpc.as_ref().unwrap().clone();
|
||||
let mut timer = TradeTimer::new("Building buy transaction instructions");
|
||||
// Build instructions
|
||||
let instructions = self.instruction_builder.build_buy_instructions(¶ms).await?;
|
||||
let final_instructions = match middleware_manager.clone() {
|
||||
Some(middleware_manager) => middleware_manager
|
||||
.apply_middlewares_process_protocol_instructions(
|
||||
instructions,
|
||||
self.protocol_name.to_string(),
|
||||
true,
|
||||
)?,
|
||||
None => instructions,
|
||||
};
|
||||
timer.stage("Building RPC transaction instructions");
|
||||
|
||||
// Build transaction
|
||||
let transaction = build_rpc_transaction(
|
||||
params.payer.clone(),
|
||||
¶ms.priority_fee,
|
||||
final_instructions,
|
||||
params.lookup_table_key,
|
||||
params.recent_blockhash,
|
||||
params.data_size_limit,
|
||||
middleware_manager,
|
||||
self.protocol_name.to_string(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
timer.stage("RPC submission confirmation");
|
||||
|
||||
// Send transaction
|
||||
if params.wait_transaction_confirmed {
|
||||
rpc.send_and_confirm_transaction(&transaction).await?;
|
||||
} else {
|
||||
// Send transaction asynchronously
|
||||
rpc.send_transaction(&transaction).await?;
|
||||
}
|
||||
timer.finish();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn buy_with_tip(
|
||||
&self,
|
||||
mut params: BuyWithTipParams,
|
||||
params: BuyParams,
|
||||
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()> {
|
||||
if params.data_size_limit == 0 {
|
||||
params.data_size_limit = MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
|
||||
let mut data_size_limit = params.data_size_limit;
|
||||
if data_size_limit == 0 {
|
||||
data_size_limit = MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
|
||||
}
|
||||
let timer = TradeTimer::new("Building buy transaction instructions");
|
||||
|
||||
@@ -107,7 +50,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
priority_fee: params.priority_fee.clone(),
|
||||
lookup_table_key: params.lookup_table_key,
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
data_size_limit: params.data_size_limit,
|
||||
data_size_limit: data_size_limit,
|
||||
wait_transaction_confirmed: params.wait_transaction_confirmed,
|
||||
protocol_params: params.protocol_params.clone(),
|
||||
};
|
||||
@@ -128,76 +71,28 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
|
||||
// Execute transactions in parallel
|
||||
parallel_execute_with_tips(
|
||||
params.swqos_clients,
|
||||
swqos_clients,
|
||||
params.payer,
|
||||
final_instructions,
|
||||
params.priority_fee,
|
||||
params.lookup_table_key,
|
||||
params.recent_blockhash,
|
||||
params.data_size_limit,
|
||||
TradeType::Buy,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
self.protocol_name.to_string(),
|
||||
true,
|
||||
params.wait_transaction_confirmed,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sell(
|
||||
&self,
|
||||
params: SellParams,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()> {
|
||||
if params.rpc.is_none() {
|
||||
return Err(anyhow!("RPC is not set"));
|
||||
}
|
||||
let rpc = params.rpc.as_ref().unwrap().clone();
|
||||
let mut timer = TradeTimer::new("Building sell transaction instructions");
|
||||
|
||||
// Build instructions
|
||||
let instructions = self.instruction_builder.build_sell_instructions(¶ms).await?;
|
||||
let final_instructions = match middleware_manager.clone() {
|
||||
Some(middleware_manager) => middleware_manager
|
||||
.apply_middlewares_process_protocol_instructions(
|
||||
instructions,
|
||||
self.protocol_name.to_string(),
|
||||
false,
|
||||
)?,
|
||||
None => instructions,
|
||||
};
|
||||
timer.stage("Sell transaction instructions");
|
||||
|
||||
// Build transaction
|
||||
let transaction = build_sell_transaction(
|
||||
params.payer.clone(),
|
||||
¶ms.priority_fee,
|
||||
final_instructions,
|
||||
params.lookup_table_key,
|
||||
params.recent_blockhash,
|
||||
middleware_manager,
|
||||
self.protocol_name.to_string(),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
timer.stage("Sell transaction signing");
|
||||
|
||||
// Send transaction
|
||||
if params.wait_transaction_confirmed {
|
||||
rpc.send_and_confirm_transaction(&transaction).await?;
|
||||
} else {
|
||||
rpc.send_transaction(&transaction).await?;
|
||||
}
|
||||
timer.finish();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sell_with_tip(
|
||||
&self,
|
||||
params: SellWithTipParams,
|
||||
params: SellParams,
|
||||
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()> {
|
||||
let timer = TradeTimer::new("Building sell transaction instructions");
|
||||
@@ -214,6 +109,7 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
recent_blockhash: params.recent_blockhash,
|
||||
wait_transaction_confirmed: params.wait_transaction_confirmed,
|
||||
protocol_params: params.protocol_params.clone(),
|
||||
with_tip: params.with_tip,
|
||||
};
|
||||
|
||||
// Build instructions
|
||||
@@ -232,18 +128,18 @@ impl TradeExecutor for GenericTradeExecutor {
|
||||
|
||||
// Execute transactions in parallel
|
||||
parallel_execute_with_tips(
|
||||
params.swqos_clients,
|
||||
swqos_clients,
|
||||
params.payer,
|
||||
final_instructions,
|
||||
params.priority_fee,
|
||||
params.lookup_table_key,
|
||||
params.recent_blockhash,
|
||||
0,
|
||||
TradeType::Sell,
|
||||
middleware_manager,
|
||||
self.protocol_name.to_string(),
|
||||
false,
|
||||
params.wait_transaction_confirmed,
|
||||
params.with_tip,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -8,14 +8,7 @@ use tokio::task::JoinHandle;
|
||||
use crate::{
|
||||
common::PriorityFee,
|
||||
swqos::{SwqosClient, SwqosType, TradeType},
|
||||
trading::{
|
||||
common::{
|
||||
build_rpc_transaction, build_sell_tip_transaction_with_priority_fee,
|
||||
build_sell_transaction, build_tip_transaction_with_priority_fee,
|
||||
},
|
||||
core::timer::TradeTimer,
|
||||
MiddlewareManager,
|
||||
},
|
||||
trading::{common::build_transaction, core::timer::TradeTimer, MiddlewareManager},
|
||||
};
|
||||
|
||||
/// Generic function for parallel transaction execution
|
||||
@@ -27,20 +20,30 @@ pub async fn parallel_execute_with_tips(
|
||||
lookup_table_key: Option<Pubkey>,
|
||||
recent_blockhash: Hash,
|
||||
data_size_limit: u32,
|
||||
trade_type: TradeType,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: String,
|
||||
is_buy: bool,
|
||||
wait_transaction_confirmed: bool,
|
||||
with_tip: bool,
|
||||
) -> Result<()> {
|
||||
let cores = core_affinity::get_core_ids().unwrap();
|
||||
let mut handles: Vec<JoinHandle<Result<()>>> = vec![];
|
||||
|
||||
if is_buy && swqos_clients.len() > priority_fee.buy_tip_fees.len() {
|
||||
return Err(anyhow!("Number of tip clients exceeds the configured buy tip fees"));
|
||||
}
|
||||
if !is_buy && swqos_clients.len() > priority_fee.sell_tip_fees.len() {
|
||||
return Err(anyhow!("Number of tip clients exceeds the configured sell tip fees"));
|
||||
}
|
||||
|
||||
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 mut priority_fee = priority_fee.clone();
|
||||
let priority_fee = priority_fee.clone();
|
||||
let core_id = cores[i % cores.len()];
|
||||
|
||||
let middleware_manager = middleware_manager.clone();
|
||||
@@ -54,77 +57,40 @@ pub async fn parallel_execute_with_tips(
|
||||
swqos_client.get_swqos_type()
|
||||
));
|
||||
|
||||
let transaction = if matches!(trade_type, TradeType::Sell)
|
||||
&& swqos_client.get_swqos_type() == SwqosType::Default
|
||||
{
|
||||
build_sell_transaction(
|
||||
payer,
|
||||
&priority_fee,
|
||||
instructions,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await?
|
||||
} else if matches!(trade_type, TradeType::Sell)
|
||||
&& swqos_client.get_swqos_type() != SwqosType::Default
|
||||
{
|
||||
let tip_account = swqos_client.get_tip_account()?;
|
||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
||||
build_sell_tip_transaction_with_priority_fee(
|
||||
payer,
|
||||
&priority_fee,
|
||||
instructions,
|
||||
&tip_account,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await?
|
||||
} else if swqos_client.get_swqos_type() == SwqosType::Default {
|
||||
build_rpc_transaction(
|
||||
payer,
|
||||
&priority_fee,
|
||||
instructions,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
let tip_account = swqos_client.get_tip_account()?;
|
||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
||||
priority_fee.buy_tip_fee =
|
||||
priority_fee.buy_tip_fees[i % priority_fee.buy_tip_fees.len()];
|
||||
let tip_account = swqos_client.get_tip_account()?;
|
||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account).map_err(|e| anyhow!(e))?);
|
||||
if priority_fee.buy_tip_fees.len() == 0 {
|
||||
return Err(anyhow!("buy_tip_fees is empty"));
|
||||
}
|
||||
let tip_amount = priority_fee.buy_tip_fees[i];
|
||||
|
||||
build_tip_transaction_with_priority_fee(
|
||||
payer,
|
||||
&priority_fee,
|
||||
instructions,
|
||||
&tip_account,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
let transaction = build_transaction(
|
||||
payer,
|
||||
&priority_fee,
|
||||
instructions,
|
||||
lookup_table_key,
|
||||
recent_blockhash,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
swqos_client.get_swqos_type() != SwqosType::Default,
|
||||
&tip_account,
|
||||
tip_amount,
|
||||
)
|
||||
.await?;
|
||||
|
||||
timer.stage(format!(
|
||||
"Submitting transaction instructions: {:?}",
|
||||
swqos_client.get_swqos_type()
|
||||
));
|
||||
|
||||
swqos_client.send_transaction(trade_type, &transaction).await?;
|
||||
swqos_client
|
||||
.send_transaction(
|
||||
if is_buy { TradeType::Buy } else { TradeType::Sell },
|
||||
&transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
timer.finish();
|
||||
Ok::<(), anyhow::Error>(())
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::constants::bonk::accounts::{
|
||||
};
|
||||
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::trading::bonk::common::{
|
||||
get_amount_in, get_amount_in_net, get_amount_out, get_creator_associated_account,
|
||||
get_platform_associated_account,
|
||||
@@ -25,9 +24,7 @@ use crate::trading::pumpswap::common::{
|
||||
coin_creator_vault_ata, coin_creator_vault_authority, get_token_balances,
|
||||
};
|
||||
use crate::trading::raydium_cpmm::common::get_pool_token_balances;
|
||||
|
||||
/// Common buy parameters
|
||||
/// Contains all necessary information for executing buy transactions
|
||||
/// Buy parameters
|
||||
#[derive(Clone)]
|
||||
pub struct BuyParams {
|
||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||
@@ -43,26 +40,7 @@ pub struct BuyParams {
|
||||
pub protocol_params: Box<dyn ProtocolParams>,
|
||||
}
|
||||
|
||||
/// Buy parameters with MEV service support
|
||||
/// Extends BuyParams with MEV client configurations for transaction acceleration
|
||||
#[derive(Clone)]
|
||||
pub struct BuyWithTipParams {
|
||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||
pub swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
pub payer: Arc<Keypair>,
|
||||
pub mint: Pubkey,
|
||||
pub sol_amount: u64,
|
||||
pub slippage_basis_points: Option<u64>,
|
||||
pub priority_fee: 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>,
|
||||
}
|
||||
|
||||
/// Common sell parameters
|
||||
/// Contains all necessary information for executing sell transactions
|
||||
/// Sell parameters
|
||||
#[derive(Clone)]
|
||||
pub struct SellParams {
|
||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||
@@ -74,23 +52,7 @@ pub struct SellParams {
|
||||
pub lookup_table_key: Option<Pubkey>,
|
||||
pub recent_blockhash: Hash,
|
||||
pub wait_transaction_confirmed: bool,
|
||||
pub protocol_params: Box<dyn ProtocolParams>,
|
||||
}
|
||||
|
||||
/// Sell parameters with MEV service support
|
||||
/// Extends SellParams with MEV client configurations for transaction acceleration
|
||||
#[derive(Clone)]
|
||||
pub struct SellWithTipParams {
|
||||
pub rpc: Option<Arc<SolanaRpcClient>>,
|
||||
pub swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
pub payer: Arc<Keypair>,
|
||||
pub mint: Pubkey,
|
||||
pub token_amount: Option<u64>,
|
||||
pub slippage_basis_points: Option<u64>,
|
||||
pub priority_fee: 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>,
|
||||
}
|
||||
|
||||
@@ -532,44 +494,3 @@ impl ProtocolParams for RaydiumAmmV4Params {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl BuyParams {
|
||||
/// Convert to BuyWithTipParams
|
||||
/// Transforms basic buy parameters into MEV-enabled parameters
|
||||
pub fn with_tip(self, swqos_clients: Vec<Arc<SwqosClient>>) -> BuyWithTipParams {
|
||||
BuyWithTipParams {
|
||||
rpc: self.rpc,
|
||||
swqos_clients,
|
||||
payer: self.payer,
|
||||
mint: self.mint,
|
||||
sol_amount: self.sol_amount,
|
||||
slippage_basis_points: self.slippage_basis_points,
|
||||
priority_fee: self.priority_fee,
|
||||
lookup_table_key: self.lookup_table_key,
|
||||
recent_blockhash: self.recent_blockhash,
|
||||
data_size_limit: self.data_size_limit,
|
||||
wait_transaction_confirmed: self.wait_transaction_confirmed,
|
||||
protocol_params: self.protocol_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SellParams {
|
||||
/// Convert to SellWithTipParams
|
||||
/// Transforms basic sell parameters into MEV-enabled parameters
|
||||
pub fn with_tip(self, swqos_clients: Vec<Arc<SwqosClient>>) -> SellWithTipParams {
|
||||
SellWithTipParams {
|
||||
rpc: self.rpc,
|
||||
swqos_clients,
|
||||
payer: self.payer,
|
||||
mint: self.mint,
|
||||
token_amount: self.token_amount,
|
||||
slippage_basis_points: self.slippage_basis_points,
|
||||
priority_fee: self.priority_fee,
|
||||
lookup_table_key: self.lookup_table_key,
|
||||
recent_blockhash: self.recent_blockhash,
|
||||
wait_transaction_confirmed: self.wait_transaction_confirmed,
|
||||
protocol_params: self.protocol_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-12
@@ -1,26 +1,28 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{swqos::SwqosClient, trading::MiddlewareManager};
|
||||
use anyhow::Result;
|
||||
use solana_sdk::instruction::Instruction;
|
||||
use crate::trading::MiddlewareManager;
|
||||
|
||||
use super::params::{BuyParams, BuyWithTipParams, SellParams, SellWithTipParams};
|
||||
use super::params::{BuyParams, SellParams};
|
||||
|
||||
/// 交易执行器trait - 定义了所有交易协议都需要实现的核心方法
|
||||
#[async_trait::async_trait]
|
||||
pub trait TradeExecutor: Send + Sync {
|
||||
/// 执行买入交易
|
||||
async fn buy(&self, params: BuyParams, middleware_manager: Option<Arc<MiddlewareManager>>) -> Result<()>;
|
||||
|
||||
/// 使用MEV服务执行买入交易
|
||||
async fn buy_with_tip(&self, params: BuyWithTipParams, middleware_manager: Option<Arc<MiddlewareManager>>) -> Result<()>;
|
||||
|
||||
/// 执行卖出交易
|
||||
async fn sell(&self, params: SellParams, middleware_manager: Option<Arc<MiddlewareManager>>) -> Result<()>;
|
||||
|
||||
async fn buy_with_tip(
|
||||
&self,
|
||||
params: BuyParams,
|
||||
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()>;
|
||||
/// 使用MEV服务执行卖出交易
|
||||
async fn sell_with_tip(&self, params: SellWithTipParams, middleware_manager: Option<Arc<MiddlewareManager>>) -> Result<()>;
|
||||
|
||||
async fn sell_with_tip(
|
||||
&self,
|
||||
params: SellParams,
|
||||
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
) -> Result<()>;
|
||||
/// 获取协议名称
|
||||
fn protocol_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ pub mod pumpswap;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_cpmm;
|
||||
|
||||
pub use core::params::{BuyParams, BuyWithTipParams, SellParams, SellWithTipParams};
|
||||
pub use core::params::{BuyParams, SellParams};
|
||||
pub use core::traits::{InstructionBuilder, TradeExecutor};
|
||||
pub use factory::TradeFactory;
|
||||
pub use middleware::{InstructionMiddleware, MiddlewareManager};
|
||||
|
||||
Reference in New Issue
Block a user