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:
+324
-598
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user