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
-107
View File
@@ -1,107 +0,0 @@
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
use crate::swqos::SwqosClient;
use crate::trading::{
core::params::{PumpSwapParams, BonkParams},
factory::Protocol,
BuyParams, TradeFactory,
};
use crate::{common::PriorityFee, SolanaRpcClient};
// Constants for compute budget
// Increased from 64KB to 256KB to handle larger transactions
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024;
// Buy tokens from a Pumpswap pool
pub async fn buy(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
auto_handle_wsol: bool,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::Bonk);
// 创建协议特定参数
let protocol_params = Box::new(BonkParams {
auto_handle_wsol: auto_handle_wsol,
virtual_base: Some(virtual_base),
virtual_quote: Some(virtual_quote),
real_base_before: Some(real_base_before),
real_quote_before: Some(real_quote_before),
});
// 创建买入参数
let buy_params = BuyParams {
rpc: Some(rpc.clone()),
payer: payer,
mint: mint,
creator: Pubkey::default(),
amount_sol: amount_sol,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash: recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
// 执行买入
executor.buy(buy_params).await?;
Ok(())
}
// Buy tokens using a MEV service
pub async fn buy_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
auto_handle_wsol: bool,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::Bonk);
// 创建协议特定参数
let protocol_params = Box::new(BonkParams {
auto_handle_wsol: auto_handle_wsol,
virtual_base: Some(virtual_base),
virtual_quote: Some(virtual_quote),
real_base_before: Some(real_base_before),
real_quote_before: Some(real_quote_before),
});
// 创建买入参数
let buy_params = BuyParams {
rpc: Some(rpc.clone()),
payer: payer,
mint: mint,
creator: Pubkey::default(),
amount_sol: amount_sol,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash: recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
let buy_with_tip_params = buy_params.with_tip(swqos_clients);
// 执行买入
executor.buy_with_tip(buy_with_tip_params).await?;
Ok(())
}
+2 -21
View File
@@ -1,8 +1,5 @@
use anyhow::anyhow;
use solana_sdk::pubkey::Pubkey;
use spl_associated_token_account::get_associated_token_address;
use crate::{common::SolanaRpcClient, constants};
use crate::constants;
pub fn get_amount_out(
amount_in: u64,
@@ -56,20 +53,4 @@ pub fn get_vault_pda(pool_state: &Pubkey, mint: &Pubkey) -> Option<Pubkey> {
let program_id: &Pubkey = &constants::bonk::accounts::BONK;
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
pda.map(|pubkey| pubkey.0)
}
pub async fn get_token_balance(
rpc: &SolanaRpcClient,
payer: &Pubkey,
mint: &Pubkey,
) -> Result<u64, anyhow::Error> {
println!("payer: {:?}", payer);
println!("mint: {:?}", mint);
let ata = get_associated_token_address(payer, mint);
let balance = rpc.get_token_account_balance(&ata).await?;
let balance_u64 = balance
.amount
.parse::<u64>()
.map_err(|_| anyhow!("Failed to parse token balance"))?;
Ok(balance_u64)
}
}
-2
View File
@@ -1,4 +1,2 @@
pub mod buy;
pub mod sell;
pub mod common;
pub mod pool;
-241
View File
@@ -1,241 +0,0 @@
use anyhow::anyhow;
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use solana_sdk::signature::Signer;
use std::sync::Arc;
use crate::common::{PriorityFee, SolanaRpcClient};
use crate::trading::bonk::common::get_token_balance;
use crate::swqos::SwqosClient;
use crate::trading::{
core::params::BonkParams, factory::Protocol, SellParams, TradeFactory,
};
// Sell tokens to a Pumpswap pool
pub async fn sell(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::Bonk);
// 创建PumpFun协议参数
let protocol_params = Box::new(BonkParams {
virtual_base: Some(virtual_base),
virtual_quote: Some(virtual_quote),
real_base_before: Some(real_base_before),
real_quote_before: Some(real_quote_before),
auto_handle_wsol: true,
});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator: Pubkey::default(),
amount_token: amount_token,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
// 执行卖出交易
executor.sell(sell_params).await?;
Ok(())
}
// Sell tokens by percentage
pub async fn sell_by_percent(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
percent: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let balance_u64 = get_token_balance(&rpc, &payer.pubkey(), &mint).await?;
let amount = balance_u64 * percent / 100;
sell(
rpc,
payer,
mint,
virtual_base,
virtual_quote,
real_base_before,
real_quote_before,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
/// Sell tokens by amount
pub async fn sell_by_amount(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
sell(
rpc,
payer,
mint,
virtual_base,
virtual_quote,
real_base_before,
real_quote_before,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
// Sell tokens using a MEV service
pub async fn sell_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::Bonk);
// 创建PumpFun协议参数
let protocol_params = Box::new(BonkParams {
virtual_base: Some(virtual_base),
virtual_quote: Some(virtual_quote),
real_base_before: Some(real_base_before),
real_quote_before: Some(real_quote_before),
auto_handle_wsol: true,
});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator: Pubkey::default(),
amount_token: amount_token,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
let sell_with_tip_params = sell_params.with_tip(swqos_clients);
// 执行卖出交易
executor.sell_with_tip(sell_with_tip_params).await?;
Ok(())
}
// Sell tokens by percentage using a MEV service
pub async fn sell_by_percent_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
percent: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let balance_u64 = get_token_balance(&rpc, &payer.pubkey(), &mint).await?;
let amount = balance_u64 * percent / 100;
sell_with_tip(
rpc,
swqos_clients,
payer,
mint,
virtual_base,
virtual_quote,
real_base_before,
real_quote_before,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
// Sell tokens by amount using a MEV service
pub async fn sell_by_amount_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
virtual_base: u128,
virtual_quote: u128,
real_base_before: u128,
real_quote_before: u128,
amount: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
sell_with_tip(
rpc,
swqos_clients,
payer,
mint,
virtual_base,
virtual_quote,
real_base_before,
real_quote_before,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
+3 -1
View File
@@ -2,9 +2,11 @@ pub mod nonce_manager;
pub mod transaction_builder;
pub mod compute_budget_manager;
pub mod address_lookup_manager;
pub mod utils;
// Re-export commonly used functions
pub use nonce_manager::*;
pub use transaction_builder::*;
pub use compute_budget_manager::*;
pub use address_lookup_manager::*;
pub use address_lookup_manager::*;
pub use utils::*;
@@ -1,4 +1,3 @@
use anyhow::anyhow;
use solana_hash::Hash;
use solana_sdk::{
instruction::Instruction,
+134
View File
@@ -0,0 +1,134 @@
use solana_sdk::{
pubkey::Pubkey, signature::Keypair, signer::Signer, system_instruction,
transaction::Transaction,
};
use spl_associated_token_account::get_associated_token_address;
use spl_token::instruction::close_account;
use crate::common::SolanaRpcClient;
use anyhow::anyhow;
#[inline]
pub async fn get_token_balance(
rpc: &SolanaRpcClient,
payer: &Pubkey,
mint: &Pubkey,
) -> Result<u64, anyhow::Error> {
println!("payer: {:?}", payer);
println!("mint: {:?}", mint);
let ata = get_associated_token_address(payer, mint);
let balance = rpc.get_token_account_balance(&ata).await?;
let balance_u64 = balance
.amount
.parse::<u64>()
.map_err(|_| anyhow!("Failed to parse token balance"))?;
Ok(balance_u64)
}
#[inline]
pub async fn get_sol_balance(
rpc: &SolanaRpcClient,
account: &Pubkey,
) -> Result<u64, anyhow::Error> {
let balance = rpc.get_balance(account).await?;
Ok(balance)
}
// Calculate slippage for buy operations
#[inline]
pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 {
amount + (amount * basis_points / 10000)
}
// Calculate slippage for sell operations
#[inline]
pub fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 {
if amount <= basis_points / 10000 {
1
} else {
amount - (amount * basis_points / 10000)
}
}
pub async fn transfer_sol(
rpc: &SolanaRpcClient,
payer: &Keypair,
receive_wallet: &Pubkey,
amount: u64,
) -> Result<(), anyhow::Error> {
if amount == 0 {
return Err(anyhow!("transfer_sol: Amount cannot be zero"));
}
let balance = get_sol_balance(rpc, &payer.pubkey()).await?;
if balance < amount {
return Err(anyhow!("Insufficient balance"));
}
let transfer_instruction =
system_instruction::transfer(&payer.pubkey(), receive_wallet, amount);
let recent_blockhash = rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[transfer_instruction],
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
rpc.send_and_confirm_transaction(&transaction).await?;
Ok(())
}
/// 关闭代币账户
///
/// 此函数用于关闭指定代币的关联代币账户,将账户中的代币余额转移给账户所有者。
///
/// # 参数
///
/// * `rpc` - Solana RPC客户端
/// * `payer` - 支付交易费用的账户
/// * `mint` - 代币的Mint地址
///
/// # 返回值
///
/// 返回一个Result,成功时返回(),失败时返回错误
pub async fn close_token_account(
rpc: &SolanaRpcClient,
payer: &Keypair,
mint: &Pubkey,
) -> Result<(), anyhow::Error> {
// 获取关联代币账户地址
let ata = get_associated_token_address(&payer.pubkey(), mint);
// 检查账户是否存在
let account_exists = rpc.get_account(&ata).await.is_ok();
if !account_exists {
return Ok(()); // 如果账户不存在,直接返回成功
}
// 构建关闭账户指令
let close_account_ix = close_account(
&spl_token::ID,
&ata,
&payer.pubkey(),
&payer.pubkey(),
&[&payer.pubkey()],
)?;
// 构建交易
let recent_blockhash = rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[close_account_ix],
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
// 发送交易
rpc.send_and_confirm_transaction(&transaction).await?;
Ok(())
}
+10 -18
View File
@@ -1,5 +1,4 @@
use anyhow::{anyhow, Result};
use solana_sdk::signer::Signer;
use std::sync::Arc;
use super::{
@@ -13,6 +12,8 @@ use crate::{
trading::common::{build_rpc_transaction, build_sell_transaction},
};
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024;
/// 通用交易执行器实现
pub struct GenericTradeExecutor {
instruction_builder: Arc<dyn InstructionBuilder>,
@@ -29,26 +30,14 @@ impl GenericTradeExecutor {
protocol_name,
}
}
/// 获取代币余额
async fn get_token_balance(
&self,
rpc: Arc<crate::common::SolanaRpcClient>,
payer: &solana_sdk::signature::Keypair,
mint: &solana_sdk::pubkey::Pubkey,
) -> Result<u64> {
let ata = spl_associated_token_account::get_associated_token_address(&payer.pubkey(), mint);
let balance = rpc.get_token_account_balance(&ata).await?;
balance
.amount
.parse::<u64>()
.map_err(|_| anyhow!("Failed to parse token balance"))
}
}
#[async_trait::async_trait]
impl TradeExecutor for GenericTradeExecutor {
async fn buy(&self, params: BuyParams) -> Result<()> {
async fn buy(&self, mut params: BuyParams) -> 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"));
}
@@ -80,7 +69,10 @@ impl TradeExecutor for GenericTradeExecutor {
Ok(())
}
async fn buy_with_tip(&self, params: BuyWithTipParams) -> Result<()> {
async fn buy_with_tip(&self, mut params: BuyWithTipParams) -> Result<()> {
if params.data_size_limit == 0 {
params.data_size_limit = MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT;
}
let mut timer = TradeTimer::new("构建买入交易指令");
// 验证参数 - 转换为BuyParams进行验证
+28 -17
View File
@@ -3,9 +3,9 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
use super::traits::ProtocolParams;
use crate::common::bonding_curve::BondingCurveAccount;
use crate::common::{PriorityFee, SolanaRpcClient};
use crate::swqos::SwqosClient;
use crate::common::bonding_curve::BondingCurveAccount;
/// 通用买入参数
#[derive(Clone)]
@@ -74,24 +74,18 @@ pub struct SellWithTipParams {
/// PumpFun协议特定参数
#[derive(Clone)]
pub struct PumpFunParams {
pub trade_type: String,
pub bonding_curve: Option<Arc<BondingCurveAccount>>,
}
impl ProtocolParams for PumpFunParams {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_box(&self) -> Box<dyn ProtocolParams> {
Box::new(self.clone())
impl PumpFunParams {
pub fn default() -> Self {
Self {
bonding_curve: None,
}
}
}
#[derive(Clone)]
pub struct PumpFunSellParams {}
impl ProtocolParams for PumpFunSellParams {
impl ProtocolParams for PumpFunParams {
fn as_any(&self) -> &dyn std::any::Any {
self
}
@@ -105,13 +99,18 @@ impl ProtocolParams for PumpFunSellParams {
#[derive(Clone)]
pub struct PumpSwapParams {
pub pool: Option<Pubkey>,
pub pool_base_token_account: Option<Pubkey>,
pub pool_quote_token_account: Option<Pubkey>,
pub user_base_token_account: Option<Pubkey>,
pub user_quote_token_account: Option<Pubkey>,
pub auto_handle_wsol: bool,
}
impl PumpSwapParams {
pub fn default() -> Self {
Self {
pool: None,
auto_handle_wsol: true,
}
}
}
impl ProtocolParams for PumpSwapParams {
fn as_any(&self) -> &dyn std::any::Any {
self
@@ -132,6 +131,18 @@ pub struct BonkParams {
pub auto_handle_wsol: bool,
}
impl BonkParams {
pub fn default() -> Self {
Self {
virtual_base: None,
virtual_quote: None,
real_base_before: None,
real_quote_before: None,
auto_handle_wsol: true,
}
}
}
impl ProtocolParams for BonkParams {
fn as_any(&self) -> &dyn std::any::Any {
self
-2
View File
@@ -1,7 +1,5 @@
use anyhow::Result;
use solana_sdk::instruction::Instruction;
use std::sync::Arc;
use super::params::{BuyParams, BuyWithTipParams, SellParams, SellWithTipParams};
/// 交易执行器trait - 定义了所有交易协议都需要实现的核心方法
+18 -18
View File
@@ -9,30 +9,30 @@ use super::{
/// 支持的交易协议
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Protocol {
pub enum TradingProtocol {
PumpFun,
PumpSwap,
Bonk,
}
impl std::fmt::Display for Protocol {
impl std::fmt::Display for TradingProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Protocol::PumpFun => write!(f, "PumpFun"),
Protocol::PumpSwap => write!(f, "PumpSwap"),
Protocol::Bonk => write!(f, "Bonk"),
TradingProtocol::PumpFun => write!(f, "PumpFun"),
TradingProtocol::PumpSwap => write!(f, "PumpSwap"),
TradingProtocol::Bonk => write!(f, "Bonk"),
}
}
}
impl std::str::FromStr for Protocol {
impl std::str::FromStr for TradingProtocol {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"pumpfun" => Ok(Protocol::PumpFun),
"pumpswap" => Ok(Protocol::PumpSwap),
"bonk" => Ok(Protocol::Bonk),
"pumpfun" => Ok(TradingProtocol::PumpFun),
"pumpswap" => Ok(TradingProtocol::PumpSwap),
"bonk" => Ok(TradingProtocol::Bonk),
_ => Err(anyhow!("Unsupported protocol: {}", s)),
}
}
@@ -43,17 +43,17 @@ pub struct TradeFactory;
impl TradeFactory {
/// 创建指定协议的交易执行器
pub fn create_executor(protocol: Protocol) -> Arc<dyn TradeExecutor> {
pub fn create_executor(protocol: TradingProtocol) -> Arc<dyn TradeExecutor> {
match protocol {
Protocol::PumpFun => {
TradingProtocol::PumpFun => {
let instruction_builder = Arc::new(PumpFunInstructionBuilder);
Arc::new(GenericTradeExecutor::new(instruction_builder, "PumpFun"))
}
Protocol::PumpSwap => {
TradingProtocol::PumpSwap => {
let instruction_builder = Arc::new(PumpSwapInstructionBuilder);
Arc::new(GenericTradeExecutor::new(instruction_builder, "PumpSwap"))
}
Protocol::Bonk => {
TradingProtocol::Bonk => {
let instruction_builder = Arc::new(BonkInstructionBuilder);
Arc::new(GenericTradeExecutor::new(
instruction_builder,
@@ -64,16 +64,16 @@ impl TradeFactory {
}
/// 获取所有支持的协议
pub fn supported_protocols() -> Vec<Protocol> {
pub fn supported_protocols() -> Vec<TradingProtocol> {
vec![
Protocol::PumpFun,
Protocol::PumpSwap,
Protocol::Bonk,
TradingProtocol::PumpFun,
TradingProtocol::PumpSwap,
TradingProtocol::Bonk,
]
}
/// 检查协议是否支持
pub fn is_supported(protocol: &Protocol) -> bool {
pub fn is_supported(protocol: &TradingProtocol) -> bool {
Self::supported_protocols().contains(protocol)
}
}
-88
View File
@@ -1,88 +0,0 @@
use crate::{
common::{bonding_curve::BondingCurveAccount, PriorityFee, SolanaRpcClient},
swqos::SwqosClient,
trading::{core::params::PumpFunParams, factory::Protocol, BuyParams, TradeFactory},
};
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 250000;
pub async fn buy(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
buy_sol_cost: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
bonding_curve: Option<Arc<BondingCurveAccount>>,
trade_type: String,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::PumpFun);
// 创建协议特定参数
let protocol_params = Box::new(PumpFunParams {
trade_type: trade_type,
bonding_curve: bonding_curve,
});
// 创建买入参数
let buy_params = BuyParams {
rpc: Some(rpc),
payer,
mint,
creator,
amount_sol: buy_sol_cost,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
// 执行买入
executor.buy(buy_params).await?;
Ok(())
}
pub async fn buy_with_tip(
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
buy_sol_cost: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
bonding_curve: Option<Arc<BondingCurveAccount>>,
trade_type: String,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::PumpFun);
// 创建协议特定参数
let protocol_params = Box::new(PumpFunParams {
trade_type: trade_type,
bonding_curve: bonding_curve,
});
// 创建买入参数
let buy_params = BuyParams {
rpc: None,
payer,
mint,
creator,
amount_sol: buy_sol_cost,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
let buy_with_tip_params = buy_params.with_tip(swqos_clients);
// 执行买入
executor.buy_with_tip(buy_with_tip_params).await?;
Ok(())
}
+3 -142
View File
@@ -1,104 +1,24 @@
use anyhow::anyhow;
use borsh::BorshDeserialize;
use spl_token::instruction::close_account;
use tokio::sync::RwLock;
use std::{collections::HashMap, sync::Arc};
use solana_sdk::{
compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey, signature::Keypair, signer::Signer, system_instruction, transaction::Transaction
compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey
};
use spl_associated_token_account::get_associated_token_address;
use pumpfun_program::accounts::BondingCurveAccount as PumpfunBondingCurveAccount;
use crate::{
common::{
bonding_curve::BondingCurveAccount, global::GlobalAccount, PriorityFee, SolanaRpcClient
},
constants::{
self, pumpfun::{global_constants::{CREATOR_FEE, FEE_BASIS_POINTS}, trade::DEFAULT_SLIPPAGE}
self, pumpfun::global_constants::{CREATOR_FEE, FEE_BASIS_POINTS}, trade::trade::DEFAULT_SLIPPAGE
},
streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent
streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent, trading::common::calculate_with_slippage_buy
};
lazy_static::lazy_static! {
static ref ACCOUNT_CACHE: RwLock<HashMap<Pubkey, Arc<GlobalAccount>>> = RwLock::new(HashMap::new());
}
pub async fn transfer_sol(rpc: &SolanaRpcClient, payer: &Keypair, receive_wallet: &Pubkey, amount: u64) -> Result<(), anyhow::Error> {
if amount == 0 {
return Err(anyhow!("transfer_sol: Amount cannot be zero"));
}
let balance = get_sol_balance(rpc, &payer.pubkey()).await?;
if balance < amount {
return Err(anyhow!("Insufficient balance"));
}
let transfer_instruction = system_instruction::transfer(
&payer.pubkey(),
receive_wallet,
amount,
);
let recent_blockhash = rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[transfer_instruction],
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
rpc.send_and_confirm_transaction(&transaction).await?;
Ok(())
}
/// 关闭代币账户
///
/// 此函数用于关闭指定代币的关联代币账户,将账户中的代币余额转移给账户所有者。
///
/// # 参数
///
/// * `rpc` - Solana RPC客户端
/// * `payer` - 支付交易费用的账户
/// * `mint` - 代币的Mint地址
///
/// # 返回值
///
/// 返回一个Result,成功时返回(),失败时返回错误
pub async fn close_token_account(rpc: &SolanaRpcClient, payer: &Keypair, mint: &Pubkey) -> Result<(), anyhow::Error> {
// 获取关联代币账户地址
let ata = get_associated_token_address(&payer.pubkey(), mint);
// 检查账户是否存在
let account_exists = rpc.get_account(&ata).await.is_ok();
if !account_exists {
return Ok(()); // 如果账户不存在,直接返回成功
}
// 构建关闭账户指令
let close_account_ix = close_account(
&spl_token::ID,
&ata,
&payer.pubkey(),
&payer.pubkey(),
&[&payer.pubkey()],
)?;
// 构建交易
let recent_blockhash = rpc.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[close_account_ix],
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
// 发送交易
rpc.send_and_confirm_transaction(&transaction).await?;
Ok(())
}
#[inline]
pub fn create_priority_fee_instructions(priority_fee: PriorityFee) -> Vec<Instruction> {
let mut instructions = Vec::with_capacity(2);
@@ -108,45 +28,6 @@ pub fn create_priority_fee_instructions(priority_fee: PriorityFee) -> Vec<Instru
instructions
}
// #[inline]
pub async fn get_token_balance(rpc: &SolanaRpcClient, payer: &Pubkey, mint: &Pubkey) -> Result<u64, anyhow::Error> {
let ata = get_associated_token_address(payer, mint);
// let account_data = rpc.get_account_data(&ata).await?;
// let token_account = Account::unpack(&account_data.as_slice())?;
// Ok(token_account.amount)
// println!("get_token_balance ata: {}", ata);
let balance = rpc.get_token_account_balance(&ata).await?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| anyhow!("Failed to parse token balance"))?;
Ok(balance_u64)
}
#[inline]
pub async fn get_token_balance_and_ata(rpc: &SolanaRpcClient, payer: &Keypair, mint: &Pubkey) -> Result<(u64, Pubkey), anyhow::Error> {
let ata = get_associated_token_address(&payer.pubkey(), mint);
// let account_data = rpc.get_account_data(&ata).await?;
// let token_account = Account::unpack(&account_data)?;
// Ok((token_account.amount, ata))
let balance = rpc.get_token_account_balance(&ata).await?;
let balance_u64 = balance.amount.parse::<u64>()
.map_err(|_| anyhow!("Failed to parse token balance"))?;
if balance_u64 == 0 {
return Err(anyhow!("Balance is 0"));
}
Ok((balance_u64, ata))
}
#[inline]
pub async fn get_sol_balance(rpc: &SolanaRpcClient, account: &Pubkey) -> Result<u64, anyhow::Error> {
let balance = rpc.get_balance(account).await?;
Ok(balance)
}
#[inline]
pub fn get_global_pda() -> Pubkey {
@@ -194,18 +75,8 @@ pub fn get_metadata_pda(mint: &Pubkey) -> Pubkey {
#[inline]
pub async fn get_global_account(/*rpc: &SolanaRpcClient*/) -> Result<Arc<GlobalAccount>, anyhow::Error> {
// let global = constants::global_constants::GLOBAL_ACCOUNT;
// if let Some(account) = ACCOUNT_CACHE.read().await.get(&global) {
// return Ok(account.clone());
// }
let global_account = GlobalAccount::new();
// let account = rpc.get_account(&global).await?;
// let global_account = bincode::deserialize::<accounts::GlobalAccount>(&account.data)?;
let global_account = Arc::new(global_account);
// ACCOUNT_CACHE.write().await.insert(global, global_account.clone());
Ok(global_account)
}
@@ -348,13 +219,3 @@ pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
s_u64.min(trade_info.real_token_reserves)
}
#[inline]
pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 {
amount + (amount * basis_points) / 10000
}
#[inline]
pub fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 {
amount - (amount * basis_points) / 10000
}
-2
View File
@@ -1,3 +1 @@
pub mod buy;
pub mod sell;
pub mod common;
-184
View File
@@ -1,184 +0,0 @@
use crate::trading::{
core::params::PumpFunSellParams, factory::Protocol, SellParams, TradeFactory,
};
use crate::{
common::{PriorityFee, SolanaRpcClient},
swqos::SwqosClient,
};
use anyhow::anyhow;
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
pub async fn sell(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_token: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::PumpFun);
// 创建PumpFun协议参数
let protocol_params = Box::new(PumpFunSellParams {});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator,
amount_token: Some(amount_token),
slippage_basis_points: None,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
// 执行卖出交易
executor.sell(sell_params).await?;
Ok(())
}
/// Sell tokens by percentage
pub async fn sell_by_percent(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
percent: u64,
amount_token: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let amount = amount_token * percent / 100;
sell(
rpc,
payer,
mint,
creator,
amount,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
/// Sell tokens by amount
pub async fn sell_by_amount(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
sell(
rpc,
payer,
mint,
creator,
amount,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
pub async fn sell_by_percent_with_tip(
rpc: Arc<SolanaRpcClient>,
fee_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
percent: u64,
amount_token: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let amount = amount_token * percent / 100;
sell_with_tip(
rpc,
fee_clients,
payer,
mint,
creator,
amount,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
pub async fn sell_by_amount_with_tip(
rpc: Arc<SolanaRpcClient>,
fee_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
sell_with_tip(
rpc,
fee_clients,
payer,
mint,
creator,
amount,
priority_fee,
lookup_table_key,
recent_blockhash,
)
.await
}
/// Sell tokens using Jito
pub async fn sell_with_tip(
rpc: Arc<SolanaRpcClient>,
fee_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_token: u64,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::PumpFun);
// 创建PumpFun协议参数
let protocol_params = Box::new(PumpFunSellParams {});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator,
amount_token: Some(amount_token),
slippage_basis_points: None,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
let sell_with_tip_params = sell_params.with_tip(fee_clients);
// 执行卖出交易
executor.sell_with_tip(sell_with_tip_params).await?;
Ok(())
}
-111
View File
@@ -1,111 +0,0 @@
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
use crate::swqos::SwqosClient;
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, BuyParams, TradeFactory};
use crate::{common::PriorityFee, SolanaRpcClient};
// Constants for compute budget
// Increased from 64KB to 256KB to handle larger transactions
const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024;
// Buy tokens from a Pumpswap pool
pub async fn buy(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
auto_handle_wsol: bool,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::PumpSwap);
// 创建协议特定参数
let protocol_params = Box::new(PumpSwapParams {
pool: pool,
pool_base_token_account: pool_base_token_account,
pool_quote_token_account: pool_quote_token_account,
user_base_token_account: user_base_token_account,
user_quote_token_account: user_quote_token_account,
auto_handle_wsol: auto_handle_wsol,
});
// 创建买入参数
let buy_params = BuyParams {
rpc: Some(rpc.clone()),
payer: payer,
mint: mint,
creator: creator,
amount_sol: amount_sol,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash: recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
// 执行买入
executor.buy(buy_params).await?;
Ok(())
}
// Buy tokens using a MEV service
pub async fn buy_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_sol: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
auto_handle_wsol: bool,
) -> Result<(), anyhow::Error> {
// 创建执行器
let executor = TradeFactory::create_executor(Protocol::PumpSwap);
// 创建协议特定参数
let protocol_params = Box::new(PumpSwapParams {
pool: pool,
pool_base_token_account: pool_base_token_account,
pool_quote_token_account: pool_quote_token_account,
user_base_token_account: user_base_token_account,
user_quote_token_account: user_quote_token_account,
auto_handle_wsol: auto_handle_wsol,
});
// 创建买入参数
let buy_params = BuyParams {
rpc: Some(rpc.clone()),
payer: payer,
mint: mint,
creator: creator,
amount_sol: amount_sol,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee,
lookup_table_key: lookup_table_key,
recent_blockhash: recent_blockhash,
data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT,
protocol_params,
};
let buy_with_tip_params = buy_params.with_tip(swqos_clients);
// 执行买入
executor.buy_with_tip(buy_with_tip_params).await?;
Ok(())
}
+3 -41
View File
@@ -1,47 +1,9 @@
use anyhow::anyhow;
use solana_sdk::{
pubkey::Pubkey,
signature::{Keypair, Signer},
};
use crate::common::SolanaRpcClient;
use crate::trading::pumpswap;
// Calculate slippage for buy operations
pub fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 {
amount + (amount * basis_points / 10000)
}
// Calculate slippage for sell operations
pub fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 {
if amount <= basis_points / 10000 {
1
} else {
amount - (amount * basis_points / 10000)
}
}
// Get token balance for a specific mint and owner
pub async fn get_token_balance(
rpc: &SolanaRpcClient,
owner: &Keypair,
mint: &Pubkey,
) -> Result<(u64, Pubkey), anyhow::Error> {
let ata = spl_associated_token_account::get_associated_token_address(&owner.pubkey(), mint);
match rpc.get_token_account_balance(&ata).await {
Ok(balance) => {
let amount = balance.amount.parse::<u64>().map_err(|e| anyhow!(e))?;
Ok((amount, ata))
}
Err(_) => Ok((0, ata)),
}
}
use solana_sdk::pubkey::Pubkey;
// Find a pool for a specific mint
pub async fn find_pool(
rpc: &SolanaRpcClient,
mint: &Pubkey,
) -> Result<Pubkey, anyhow::Error> {
pub async fn find_pool(rpc: &SolanaRpcClient, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
let (pool_address, _) = pumpswap::pool::Pool::find_by_mint(rpc, mint).await?;
Ok(pool_address)
}
@@ -83,4 +45,4 @@ pub(crate) fn coin_creator_vault_ata(coin_creator: Pubkey) -> Pubkey {
&crate::constants::pumpswap::accounts::TOKEN_PROGRAM,
);
associated_token_creator_vault_authority
}
}
-2
View File
@@ -1,4 +1,2 @@
pub mod buy;
pub mod sell;
pub mod common;
pub mod pool;
-1
View File
@@ -2,7 +2,6 @@ use crate::{common::SolanaRpcClient, constants::pumpswap::accounts};
use anyhow::anyhow;
use solana_account_decoder::UiAccountEncoding;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct Pool {
-266
View File
@@ -1,266 +0,0 @@
use anyhow::anyhow;
use solana_hash::Hash;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
use crate::common::{PriorityFee, SolanaRpcClient};
use crate::swqos::SwqosClient;
use crate::trading::pumpswap::common::get_token_balance;
use crate::trading::{core::params::PumpSwapParams, factory::Protocol, SellParams, TradeFactory};
// Sell tokens to a Pumpswap pool
pub async fn sell(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::PumpSwap);
// 创建PumpFun协议参数
let protocol_params = Box::new(PumpSwapParams {
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
auto_handle_wsol: true,
});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator,
amount_token: amount_token,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
// 执行卖出交易
executor.sell(sell_params).await?;
Ok(())
}
// Sell tokens by percentage
pub async fn sell_by_percent(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
percent: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
let amount = balance_u64 * percent / 100;
sell(
rpc,
payer,
mint,
creator,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
)
.await
}
/// Sell tokens by amount
pub async fn sell_by_amount(
rpc: Arc<SolanaRpcClient>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
sell(
rpc,
payer,
mint,
creator,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
)
.await
}
// Sell tokens using a MEV service
pub async fn sell_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount_token: Option<u64>,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
let executor = TradeFactory::create_executor(Protocol::PumpSwap);
// 创建PumpFun协议参数
let protocol_params = Box::new(PumpSwapParams {
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
auto_handle_wsol: true,
});
// 创建卖出参数
let sell_params = SellParams {
rpc: Some(rpc.clone()),
payer: payer.clone(),
mint,
creator,
amount_token: amount_token,
slippage_basis_points: slippage_basis_points,
priority_fee: priority_fee.clone(),
lookup_table_key,
recent_blockhash,
protocol_params,
};
let sell_with_tip_params = sell_params.with_tip(swqos_clients);
// 执行卖出交易
executor.sell_with_tip(sell_with_tip_params).await?;
Ok(())
}
// Sell tokens by percentage using a MEV service
pub async fn sell_by_percent_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
percent: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
if percent == 0 || percent > 100 {
return Err(anyhow!("Percentage must be between 1 and 100"));
}
let (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
let amount = balance_u64 * percent / 100;
sell_with_tip(
rpc,
swqos_clients,
payer,
mint,
creator,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
)
.await
}
// Sell tokens by amount using a MEV service
pub async fn sell_by_amount_with_tip(
rpc: Arc<SolanaRpcClient>,
swqos_clients: Vec<Arc<SwqosClient>>,
payer: Arc<Keypair>,
mint: Pubkey,
creator: Pubkey,
amount: u64,
slippage_basis_points: Option<u64>,
priority_fee: PriorityFee,
lookup_table_key: Option<Pubkey>,
recent_blockhash: Hash,
// 可选(必须全部传)
pool: Option<Pubkey>,
pool_base_token_account: Option<Pubkey>,
pool_quote_token_account: Option<Pubkey>,
user_base_token_account: Option<Pubkey>,
user_quote_token_account: Option<Pubkey>,
) -> Result<(), anyhow::Error> {
sell_with_tip(
rpc,
swqos_clients,
payer,
mint,
creator,
Some(amount),
slippage_basis_points,
priority_fee,
lookup_table_key,
recent_blockhash,
pool,
pool_base_token_account,
pool_quote_token_account,
user_base_token_account,
user_quote_token_account,
)
.await
}