refactor: Major SDK architecture refactoring and API consolidation

- Consolidate separate buy/sell modules into unified trading interface
- Remove protocol-specific buy/sell files (bonk, pumpfun, pumpswap)
- Add new trading constants and utility functions
- Simplify API with unified buy/sell methods supporting multiple protocols
- Enhance documentation with comprehensive examples and usage guides
- Add balance checking and token account management utilities
- Improve code organization and maintainability
This commit is contained in:
ysq
2025-07-10 18:14:21 +08:00
parent 57c2848a57
commit b891b2bc27
41 changed files with 1297 additions and 2744 deletions
+324 -598
View File
@@ -2,34 +2,28 @@ pub mod common;
pub mod constants;
pub mod instruction;
pub mod protos;
pub mod swqos;
pub mod streaming;
pub mod swqos;
pub mod trading;
pub mod utils;
use crate::swqos::SwqosConfig;
use crate::trading::core::params::BonkParams;
use crate::trading::core::params::PumpFunParams;
use crate::trading::core::params::PumpSwapParams;
use crate::trading::core::traits::ProtocolParams;
use crate::trading::factory::TradingProtocol;
use crate::trading::BuyParams;
use crate::trading::SellParams;
use crate::trading::TradeFactory;
use common::{PriorityFee, SolanaRpcClient, TradeConfig};
use rustls::crypto::{ring::default_provider, CryptoProvider};
use solana_sdk::hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
use std::sync::Mutex;
use rustls::crypto::{ring::default_provider, CryptoProvider};
use solana_sdk::{
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use swqos::SwqosClient;
use common::{PriorityFee, SolanaRpcClient, TradeConfig};
use constants::trade_type::COPY_BUY;
use crate::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
use crate::swqos::SwqosConfig;
use crate::trading::core::params::PumpFunParams;
use crate::trading::core::params::PumpFunSellParams;
use crate::trading::core::params::PumpSwapParams;
use crate::trading::core::params::BonkParams;
use crate::trading::BuyWithTipParams;
use crate::trading::SellParams;
use crate::trading::SellWithTipParams;
pub struct SolanaTrade {
pub payer: Arc<Keypair>,
pub rpc: Arc<SolanaRpcClient>,
@@ -107,11 +101,95 @@ impl SolanaTrade {
.clone()
}
pub async fn buy_use_buy_params(
/// Execute a buy order for a specified token
///
/// # Arguments
///
/// * `mint` - The public key of the token mint to buy
/// * `creator` - Optional creator public key for the token (defaults to Pubkey::default() if None)
/// * `amount_sol` - Amount of SOL to spend on the purchase (in lamports)
/// * `slippage_basis_points` - Optional slippage tolerance in basis points (e.g., 100 = 1%)
/// * `recent_blockhash` - Recent blockhash for transaction validity
/// * `custom_buy_tip_fee` - Optional custom tip fee for priority processing (in SOL)
/// * `with_tip` - Whether to include tip for MEV protection and priority processing
/// * `protocol` - Trading protocol to use (PumpFun, PumpSwap, or Bonk)
/// * `protocol_params` - Optional protocol-specific parameters (uses defaults if None)
///
/// # Returns
///
/// Returns `Ok(())` if the buy order is successfully executed, or an error if the transaction fails.
///
/// # Errors
///
/// This function will return an error if:
/// - Invalid protocol parameters are provided
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient SOL balance for the purchase
///
/// # Example
///
/// ```rust
/// use solana_sdk::pubkey::Pubkey;
/// use solana_sdk::hash::Hash;
/// use crate::trading::factory::TradingProtocol;
///
/// let mint = Pubkey::new_unique();
/// let amount_sol = 1_000_000_000; // 1 SOL in lamports
/// let slippage = Some(500); // 5% slippage
/// let recent_blockhash = Hash::default();
///
/// solana_trade.buy(
/// mint,
/// None,
/// amount_sol,
/// slippage,
/// recent_blockhash,
/// None,
/// true,
/// TradingProtocol::PumpFun,
/// None,
/// ).await?;
/// ```
pub async fn buy(
&self,
buy_params: BuyWithTipParams,
mint: Pubkey,
creator: Option<Pubkey>,
amount_sol: u64,
slippage_basis_points: Option<u64>,
recent_blockhash: Hash,
custom_buy_tip_fee: Option<f64>,
with_tip: bool,
protocol: TradingProtocol,
protocol_params: Option<Box<dyn ProtocolParams>>,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(protocol.clone());
let protocol_params = if let Some(params) = protocol_params {
params
} else {
match protocol {
TradingProtocol::PumpFun => {
Box::new(PumpFunParams::default()) as Box<dyn ProtocolParams>
}
TradingProtocol::PumpSwap => {
Box::new(PumpSwapParams::default()) as Box<dyn ProtocolParams>
}
TradingProtocol::Bonk => Box::new(BonkParams::default()) as Box<dyn ProtocolParams>,
}
};
let buy_params = BuyParams {
rpc: Some(self.rpc.clone()),
payer: self.payer.clone(),
mint: mint,
creator: creator.unwrap_or(Pubkey::default()),
amount_sol: amount_sol,
slippage_basis_points: slippage_basis_points,
priority_fee: self.trade_config.priority_fee.clone(),
lookup_table_key: self.trade_config.lookup_table_key,
recent_blockhash,
data_size_limit: 0,
protocol_params: protocol_params.clone(),
};
let mut priority_fee = buy_params.priority_fee.clone();
if custom_buy_tip_fee.is_some() {
priority_fee.buy_tip_fee = custom_buy_tip_fee.unwrap();
@@ -122,85 +200,126 @@ impl SolanaTrade {
custom_buy_tip_fee.unwrap(),
];
}
let mint = buy_params.mint;
let creator = buy_params.creator;
let buy_sol_cost = buy_params.amount_sol;
let slippage_basis_points = buy_params.slippage_basis_points;
let recent_blockhash = buy_params.recent_blockhash;
if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunParams>()
{
trading::pumpfun::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.bonding_curve.clone(),
COPY_BUY.to_string(),
)
.await
} else if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
protocol_params.auto_handle_wsol,
)
.await
} else if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
buy_sol_cost,
slippage_basis_points,
priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.auto_handle_wsol,
)
.await
} else {
let buy_with_tip_params = buy_params.clone().with_tip(self.swqos_clients.clone());
// Validate protocol params
let is_valid_params = match protocol {
TradingProtocol::PumpFun => protocol_params
.as_any()
.downcast_ref::<PumpFunParams>()
.is_some(),
TradingProtocol::PumpSwap => protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
.is_some(),
TradingProtocol::Bonk => protocol_params
.as_any()
.downcast_ref::<BonkParams>()
.is_some(),
};
if !is_valid_params {
return Err(anyhow::anyhow!("Invalid protocol params for Trade"));
}
// Execute buy based on tip preference
if with_tip {
executor.buy_with_tip(buy_with_tip_params).await
} else {
executor.buy(buy_params).await
}
}
pub async fn buy_with_tip_use_buy_params(
/// Execute a sell order for a specified token
///
/// # Arguments
///
/// * `mint` - The public key of the token mint to sell
/// * `creator` - Optional creator public key for the token (defaults to Pubkey::default() if None)
/// * `amount_token` - Amount of tokens to sell (in smallest token units)
/// * `slippage_basis_points` - Optional slippage tolerance in basis points (e.g., 100 = 1%)
/// * `recent_blockhash` - Recent blockhash for transaction validity
/// * `custom_buy_tip_fee` - Optional custom tip fee for priority processing (in SOL)
/// * `with_tip` - Whether to include tip for MEV protection and priority processing
/// * `protocol` - Trading protocol to use (PumpFun, PumpSwap, or Bonk)
/// * `protocol_params` - Optional protocol-specific parameters (uses defaults if None)
///
/// # Returns
///
/// Returns `Ok(())` if the sell order is successfully executed, or an error if the transaction fails.
///
/// # Errors
///
/// This function will return an error if:
/// - Invalid protocol parameters are provided
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient token balance for the sale
/// - Token account doesn't exist or is not properly initialized
///
/// # Example
///
/// ```rust
/// use solana_sdk::pubkey::Pubkey;
/// use solana_sdk::hash::Hash;
/// use crate::trading::factory::TradingProtocol;
///
/// let mint = Pubkey::new_unique();
/// let amount_token = 1_000_000; // Amount of tokens to sell
/// let slippage = Some(500); // 5% slippage
/// let recent_blockhash = Hash::default();
///
/// solana_trade.sell(
/// mint,
/// None,
/// amount_token,
/// slippage,
/// recent_blockhash,
/// None,
/// true,
/// TradingProtocol::PumpFun,
/// None,
/// ).await?;
/// ```
pub async fn sell(
&self,
buy_params: BuyWithTipParams,
mint: Pubkey,
creator: Option<Pubkey>,
amount_token: u64,
slippage_basis_points: Option<u64>,
recent_blockhash: Hash,
custom_buy_tip_fee: Option<f64>,
with_tip: bool,
protocol: TradingProtocol,
protocol_params: Option<Box<dyn ProtocolParams>>,
) -> Result<(), anyhow::Error> {
let mut priority_fee = buy_params.priority_fee.clone();
let executor = TradeFactory::create_executor(protocol.clone());
let protocol_params = if let Some(params) = protocol_params {
params
} else {
match protocol {
TradingProtocol::PumpFun => {
Box::new(PumpFunParams::default()) as Box<dyn ProtocolParams>
}
TradingProtocol::PumpSwap => {
Box::new(PumpSwapParams::default()) as Box<dyn ProtocolParams>
}
TradingProtocol::Bonk => Box::new(BonkParams::default()) as Box<dyn ProtocolParams>,
}
};
let sell_params = SellParams {
rpc: Some(self.rpc.clone()),
payer: self.payer.clone(),
mint: mint,
creator: creator.unwrap_or(Pubkey::default()),
amount_token: Some(amount_token),
slippage_basis_points: slippage_basis_points,
priority_fee: self.trade_config.priority_fee.clone(),
lookup_table_key: self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params: protocol_params.clone(),
};
let mut priority_fee = sell_params.priority_fee.clone();
if custom_buy_tip_fee.is_some() {
priority_fee.buy_tip_fee = custom_buy_tip_fee.unwrap();
priority_fee.buy_tip_fees = vec![
@@ -210,516 +329,123 @@ impl SolanaTrade {
custom_buy_tip_fee.unwrap(),
];
}
let mint = buy_params.mint;
let creator = buy_params.creator;
let buy_sol_cost = buy_params.amount_sol;
let slippage_basis_points = buy_params.slippage_basis_points;
let recent_blockhash = buy_params.recent_blockhash;
if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunParams>()
{
trading::pumpfun::buy::buy_with_tip(
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.bonding_curve.clone(),
COPY_BUY.to_string(),
)
.await
} else if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::buy::buy_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
protocol_params.auto_handle_wsol,
)
.await
} else if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
buy_sol_cost,
slippage_basis_points,
priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.auto_handle_wsol,
)
.await
} else {
let sell_with_tip_params = sell_params.clone().with_tip(self.swqos_clients.clone());
// Validate protocol params
let is_valid_params = match protocol {
TradingProtocol::PumpFun => protocol_params
.as_any()
.downcast_ref::<PumpFunParams>()
.is_some(),
TradingProtocol::PumpSwap => protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
.is_some(),
TradingProtocol::Bonk => protocol_params
.as_any()
.downcast_ref::<BonkParams>()
.is_some(),
};
if !is_valid_params {
return Err(anyhow::anyhow!("Invalid protocol params for Trade"));
}
// Execute sell based on tip preference
if with_tip {
executor.sell_with_tip(sell_with_tip_params).await
} else {
executor.sell(sell_params).await
}
}
/// Sell tokens by percentage
pub async fn sell_by_percent_use_sell_params(
/// Execute a sell order for a percentage of the specified token amount
///
/// This is a convenience function that calculates the exact amount to sell based on
/// a percentage of the total token amount and then calls the `sell` function.
///
/// # Arguments
///
/// * `mint` - The public key of the token mint to sell
/// * `creator` - Optional creator public key for the token (defaults to Pubkey::default() if None)
/// * `amount_token` - Total amount of tokens available (in smallest token units)
/// * `percent` - Percentage of tokens to sell (1-100, where 100 = 100%)
/// * `slippage_basis_points` - Optional slippage tolerance in basis points (e.g., 100 = 1%)
/// * `recent_blockhash` - Recent blockhash for transaction validity
/// * `custom_buy_tip_fee` - Optional custom tip fee for priority processing (in SOL)
/// * `with_tip` - Whether to include tip for MEV protection and priority processing
/// * `protocol` - Trading protocol to use (PumpFun, PumpSwap, or Bonk)
/// * `protocol_params` - Optional protocol-specific parameters (uses defaults if None)
///
/// # Returns
///
/// Returns `Ok(())` if the sell order is successfully executed, or an error if the transaction fails.
///
/// # Errors
///
/// This function will return an error if:
/// - `percent` is 0 or greater than 100
/// - Invalid protocol parameters are provided
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient token balance for the calculated sale amount
/// - Token account doesn't exist or is not properly initialized
///
/// # Example
///
/// ```rust
/// use solana_sdk::pubkey::Pubkey;
/// use solana_sdk::hash::Hash;
/// use crate::trading::factory::TradingProtocol;
///
/// let mint = Pubkey::new_unique();
/// let total_tokens = 10_000_000; // Total tokens available
/// let percent = 50; // Sell 50% of tokens
/// let slippage = Some(500); // 5% slippage
/// let recent_blockhash = Hash::default();
///
/// // This will sell 5_000_000 tokens (50% of 10_000_000)
/// solana_trade.sell_by_percent(
/// mint,
/// None,
/// total_tokens,
/// percent,
/// slippage,
/// recent_blockhash,
/// None,
/// true,
/// TradingProtocol::PumpFun,
/// None,
/// ).await?;
/// ```
pub async fn sell_by_percent(
&self,
sell_params: SellParams,
mint: Pubkey,
creator: Option<Pubkey>,
amount_token: u64,
percent: u64,
slippage_basis_points: Option<u64>,
recent_blockhash: Hash,
custom_buy_tip_fee: Option<f64>,
with_tip: bool,
protocol: TradingProtocol,
protocol_params: Option<Box<dyn ProtocolParams>>,
) -> Result<(), anyhow::Error> {
let mint = sell_params.mint;
let creator = sell_params.creator;
let amount_token = sell_params.amount_token;
let recent_blockhash = sell_params.recent_blockhash;
if let Some(_) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunSellParams>()
{
trading::pumpfun::sell::sell_by_percent(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
creator,
percent,
amount_token.unwrap_or(0),
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::sell::sell_by_percent(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
creator,
percent,
None,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::sell::sell_by_percent(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
percent,
None,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else {
return Err(anyhow::anyhow!("Invalid protocol params for Trade"));
if percent == 0 || percent > 100 {
return Err(anyhow::anyhow!("Percentage must be between 1 and 100"));
}
}
/// Sell tokens by amount
pub async fn sell_by_amount_use_sell_params(
&self,
sell_params: SellParams,
) -> Result<(), anyhow::Error> {
let mint = sell_params.mint;
let creator = sell_params.creator;
let amount = sell_params.amount_token;
let recent_blockhash = sell_params.recent_blockhash;
if let Some(_) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunSellParams>()
{
trading::pumpfun::sell::sell_by_amount(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
creator,
amount.unwrap_or(0),
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::sell::sell_by_amount(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
creator,
amount.unwrap_or(0),
None,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::sell::sell_by_amount(
self.rpc.clone(),
self.payer.clone(),
mint.clone(),
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
amount.unwrap_or(0),
None,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else {
Err(anyhow::anyhow!("Invalid protocol params for Trade"))
}
}
pub async fn sell_by_percent_with_tip_use_sell_params(
&self,
sell_params: SellWithTipParams,
percent: u64,
) -> Result<(), anyhow::Error> {
let mint = sell_params.mint;
let creator = sell_params.creator;
let amount_token = sell_params.amount_token;
let recent_blockhash = sell_params.recent_blockhash;
if let Some(_) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunSellParams>()
{
trading::pumpfun::sell::sell_by_percent_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
percent,
amount_token.unwrap_or(0),
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::sell::sell_by_percent_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
percent,
sell_params.slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::sell::sell_by_percent_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
percent,
sell_params.slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else {
Err(anyhow::anyhow!("Invalid protocol params for Trade"))
}
}
pub async fn sell_by_amount_with_tip_use_sell_params(
&self,
sell_params: SellWithTipParams,
) -> Result<(), anyhow::Error> {
let mint = sell_params.mint;
let creator = sell_params.creator;
let amount = sell_params.amount_token;
let recent_blockhash = sell_params.recent_blockhash;
if let Some(_) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunSellParams>()
{
trading::pumpfun::sell::sell_by_amount_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
amount.unwrap_or(0),
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>()
{
trading::pumpswap::sell::sell_by_amount_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
creator,
amount.unwrap_or(0),
sell_params.slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
)
.await
} else if let Some(protocol_params) = sell_params
.protocol_params
.as_any()
.downcast_ref::<BonkParams>()
{
trading::bonk::sell::sell_by_amount_with_tip(
self.rpc.clone(),
self.swqos_clients.clone(),
self.payer.clone(),
mint,
protocol_params.virtual_base.unwrap_or(0),
protocol_params.virtual_quote.unwrap_or(0),
protocol_params.real_base_before.unwrap_or(0),
protocol_params.real_quote_before.unwrap_or(0),
amount.unwrap_or(0),
sell_params.slippage_basis_points,
self.priority_fee.clone(),
self.trade_config.lookup_table_key,
recent_blockhash,
)
.await
} else {
Err(anyhow::anyhow!("Invalid protocol params for Trade"))
}
}
#[inline]
pub async fn get_sol_balance(&self, payer: &Pubkey) -> Result<u64, anyhow::Error> {
trading::pumpfun::common::get_sol_balance(&self.rpc, payer).await
}
#[inline]
pub async fn get_payer_sol_balance(&self) -> Result<u64, anyhow::Error> {
trading::pumpfun::common::get_sol_balance(&self.rpc, &self.payer.pubkey()).await
}
#[inline]
pub async fn get_token_balance(
&self,
payer: &Pubkey,
mint: &Pubkey,
) -> Result<u64, anyhow::Error> {
println!(
"get_token_balance payer: {}, mint: {}, rpc_url: {}",
payer, mint, self.trade_config.rpc_url
);
trading::pumpfun::common::get_token_balance(&self.rpc, payer, mint).await
}
#[inline]
pub async fn get_payer_token_balance(&self, mint: &Pubkey) -> Result<u64, anyhow::Error> {
trading::pumpfun::common::get_token_balance(&self.rpc, &self.payer.pubkey(), mint).await
}
#[inline]
pub fn get_payer_pubkey(&self) -> Pubkey {
self.payer.pubkey()
}
#[inline]
pub fn get_payer(&self) -> &Keypair {
self.payer.as_ref()
}
#[inline]
pub fn get_token_price(&self, virtual_sol_reserves: u64, virtual_token_reserves: u64) -> f64 {
trading::pumpfun::common::get_token_price(virtual_sol_reserves, virtual_token_reserves)
}
#[inline]
pub fn get_buy_price(&self, amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
trading::pumpfun::common::get_buy_price(amount, trade_info)
}
#[inline]
pub async fn transfer_sol(
&self,
payer: &Keypair,
receive_wallet: &Pubkey,
amount: u64,
) -> Result<(), anyhow::Error> {
trading::pumpfun::common::transfer_sol(&self.rpc, payer, receive_wallet, amount).await
}
#[inline]
pub async fn close_token_account(&self, mint: &Pubkey) -> Result<(), anyhow::Error> {
trading::pumpfun::common::close_token_account(&self.rpc, self.payer.as_ref(), mint).await
}
#[inline]
pub async fn get_current_price(&self, mint: &Pubkey) -> Result<f64, anyhow::Error> {
let (bonding_curve, _) =
trading::pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?;
let virtual_sol_reserves = bonding_curve.virtual_sol_reserves;
let virtual_token_reserves = bonding_curve.virtual_token_reserves;
Ok(trading::pumpfun::common::get_token_price(
virtual_sol_reserves,
virtual_token_reserves,
))
}
#[inline]
pub async fn get_real_sol_reserves(&self, mint: &Pubkey) -> Result<u64, anyhow::Error> {
let (bonding_curve, _) =
trading::pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?;
let actual_sol_reserves = bonding_curve.real_sol_reserves;
Ok(actual_sol_reserves)
}
#[inline]
pub async fn get_creator(&self, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
let (bonding_curve, _) =
trading::pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?;
let creator = bonding_curve.creator;
Ok(creator)
}
#[inline]
pub async fn get_current_price_with_pumpswap(
&self,
pool_address: &Pubkey,
) -> Result<f64, anyhow::Error> {
let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?;
let (base_amount, quote_amount) = pool.get_token_balances(&self.rpc).await?;
// Calculate price using constant product formula (x * y = k)
// Price = quote_amount / base_amount
if base_amount == 0 {
return Err(anyhow::anyhow!(
"Base amount is zero, cannot calculate price"
));
}
let price = quote_amount as f64 / base_amount as f64;
Ok(price)
}
#[inline]
pub async fn get_real_sol_reserves_with_pumpswap(
&self,
pool_address: &Pubkey,
) -> Result<u64, anyhow::Error> {
let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?;
let (_, quote_amount) = pool.get_token_balances(&self.rpc).await?;
Ok(quote_amount)
}
#[inline]
pub async fn get_payer_token_balance_with_pumpswap(
&self,
pool_address: &Pubkey,
) -> Result<u64, anyhow::Error> {
let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?;
let (base_amount, _) = pool.get_token_balances(&self.rpc).await?;
Ok(base_amount)
let amount = amount_token * percent / 100;
self.sell(
mint,
creator,
amount,
slippage_basis_points,
recent_blockhash,
custom_buy_tip_fee,
with_tip,
protocol,
protocol_params,
)
.await
}
}