From 6f819be4e0ea29786a516c45674f381f7f00d657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E5=A8=81=E5=A8=81?= <1415121722@qq.com> Date: Tue, 10 Jun 2025 19:39:06 +0800 Subject: [PATCH] Buy and sell according to the trading platform --- src/constants/mod.rs | 15 ++- src/constants/pumpfun/mod.rs | 7 -- src/lib.rs | 237 ++++++++++++++++++++++++----------- src/pumpfun/buy.rs | 2 +- src/pumpswap/buy.rs | 11 +- src/pumpswap/pool.rs | 4 +- src/pumpswap/sell.rs | 41 +++++- 7 files changed, 227 insertions(+), 90 deletions(-) diff --git a/src/constants/mod.rs b/src/constants/mod.rs index 648fc87..945a789 100644 --- a/src/constants/mod.rs +++ b/src/constants/mod.rs @@ -1,2 +1,15 @@ pub mod pumpfun; -pub mod pumpswap; \ No newline at end of file +pub mod pumpswap; + +pub mod trade_type { + pub const COPY_BUY: &'static str = "copy_buy"; + pub const COPY_SELL: &'static str = "copy_sell"; + pub const SNIPER_BUY: &'static str = "sniper_buy"; + pub const SNIPER_SELL: &'static str = "sniper_sell"; +} + +pub mod trade_platform { + pub const PUMPFUN: &'static str = "pumpfun"; + pub const PUMPFUN_SWAP: &'static str = "pumpswap"; + pub const RAYDIUM: &'static str = "raydium"; +} \ No newline at end of file diff --git a/src/constants/pumpfun/mod.rs b/src/constants/pumpfun/mod.rs index 8ddadc8..c59a2c9 100755 --- a/src/constants/pumpfun/mod.rs +++ b/src/constants/pumpfun/mod.rs @@ -173,10 +173,3 @@ impl Symbol { pub const SOLANA: &'static str = "solana"; } -pub mod trade_type { - pub const COPY_BUY: &'static str = "copy_buy"; - pub const COPY_SELL: &'static str = "copy_sell"; - pub const SNIPER_BUY: &'static str = "sniper_buy"; - pub const SNIPER_SELL: &'static str = "sniper_sell"; -} - diff --git a/src/lib.rs b/src/lib.rs index 026dcd4..2f19e10 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod common; pub mod ipfs; pub mod swqos; pub mod pumpfun; +pub mod pumpswap; use std::sync::Arc; use std::sync::Mutex; @@ -24,7 +25,8 @@ use common::{pumpfun::logs_data::TradeInfo, pumpfun::logs_events::PumpfunEvent, use common::pumpfun::logs_subscribe::SubscriptionHandle; use ipfs::TokenMetadataIPFS; -use constants::pumpfun::trade_type::{COPY_BUY, SNIPER_BUY}; +use constants::trade_type::{COPY_BUY, SNIPER_BUY}; +use constants::trade_platform::{PUMPFUN, PUMPFUN_SWAP, RAYDIUM}; pub struct PumpFun { pub payer: Arc, @@ -228,21 +230,36 @@ impl PumpFun { buy_sol_cost: u64, slippage_basis_points: Option, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::buy::buy( - self.rpc.clone(), - self.payer.clone(), - mint, - creator, - dev_buy_token, - dev_sol_cost, - buy_sol_cost, - slippage_basis_points, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - COPY_BUY.to_string(), - ).await + if trade_platform == PUMPFUN { + pumpfun::buy::buy( + self.rpc.clone(), + self.payer.clone(), + mint, + creator, + dev_buy_token, + dev_sol_cost, + buy_sol_cost, + slippage_basis_points, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + COPY_BUY.to_string(), + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::buy::buy( + self.rpc.clone(), + self.payer.clone(), + mint, + buy_sol_cost, + slippage_basis_points, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } /// Buy tokens using Jito @@ -281,21 +298,37 @@ impl PumpFun { buy_sol_cost: u64, slippage_basis_points: Option, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::buy::buy_with_tip( - self.fee_clients.clone(), - self.payer.clone(), - mint, - creator, - dev_buy_token, - dev_sol_cost, - buy_sol_cost, - slippage_basis_points, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - COPY_BUY.to_string(), - ).await + if trade_platform == PUMPFUN { + pumpfun::buy::buy_with_tip( + self.fee_clients.clone(), + self.payer.clone(), + mint, + creator, + dev_buy_token, + dev_sol_cost, + buy_sol_cost, + slippage_basis_points, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + COPY_BUY.to_string(), + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::buy::buy_with_tip( + self.rpc.clone(), + self.fee_clients.clone(), + self.payer.clone(), + mint, + buy_sol_cost, + slippage_basis_points, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } /// Sell tokens @@ -326,18 +359,33 @@ impl PumpFun { percent: u64, amount_token: u64, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::sell::sell_by_percent( - self.rpc.clone(), - self.payer.clone(), - mint.clone(), - creator, - percent, - amount_token, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - ).await + if trade_platform == PUMPFUN { + pumpfun::sell::sell_by_percent( + self.rpc.clone(), + self.payer.clone(), + mint.clone(), + creator, + percent, + amount_token, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::sell::sell_by_percent( + self.rpc.clone(), + self.payer.clone(), + mint.clone(), + percent, + None, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } /// Sell tokens by amount @@ -347,17 +395,32 @@ impl PumpFun { creator: Pubkey, amount: u64, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::sell::sell_by_amount( - self.rpc.clone(), - self.payer.clone(), - mint.clone(), - creator, - amount, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - ).await + if trade_platform == PUMPFUN { + pumpfun::sell::sell_by_amount( + self.rpc.clone(), + self.payer.clone(), + mint.clone(), + creator, + amount, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::sell::sell_by_amount( + self.rpc.clone(), + self.payer.clone(), + mint.clone(), + amount, + None, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } pub async fn sell_by_percent_with_tip( @@ -367,18 +430,34 @@ impl PumpFun { percent: u64, amount_token: u64, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::sell::sell_by_percent_with_tip( - self.fee_clients.clone(), - self.payer.clone(), - mint, - creator, - percent, - amount_token, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - ).await + if trade_platform == PUMPFUN { + pumpfun::sell::sell_by_percent_with_tip( + self.fee_clients.clone(), + self.payer.clone(), + mint, + creator, + percent, + amount_token, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::sell::sell_by_percent_with_tip( + self.rpc.clone(), + self.fee_clients.clone(), + self.payer.clone(), + mint, + percent, + None, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } pub async fn sell_by_amount_with_tip( @@ -387,17 +466,33 @@ impl PumpFun { creator: Pubkey, amount: u64, recent_blockhash: Hash, + trade_platform: String, ) -> Result<(), anyhow::Error> { - pumpfun::sell::sell_by_amount_with_tip( - self.fee_clients.clone(), - self.payer.clone(), - mint, - creator, - amount, - self.priority_fee.clone(), - self.cluster.clone().lookup_table_key, - recent_blockhash, - ).await + if trade_platform == PUMPFUN { + pumpfun::sell::sell_by_amount_with_tip( + self.fee_clients.clone(), + self.payer.clone(), + mint, + creator, + amount, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + recent_blockhash, + ).await + } else if trade_platform == PUMPFUN_SWAP { + pumpswap::sell::sell_by_amount_with_tip( + self.rpc.clone(), + self.fee_clients.clone(), + self.payer.clone(), + mint, + amount, + None, + self.priority_fee.clone(), + self.cluster.clone().lookup_table_key, + ).await + } else { + Err(anyhow::anyhow!("Unsupported trade platform: {}", trade_platform)) + } } /// Sell tokens using Jito diff --git a/src/pumpfun/buy.rs b/src/pumpfun/buy.rs index 24d7248..9ce2ceb 100755 --- a/src/pumpfun/buy.rs +++ b/src/pumpfun/buy.rs @@ -23,7 +23,7 @@ use crate::{ const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 250000; use super::common::{calculate_with_slippage_buy, get_buy_token_amount_from_sol_amount, init_bonding_curve_account, get_bonding_curve_account_v2, get_bonding_curve_pda}; -use crate::constants::pumpfun::trade_type::{SNIPER_BUY}; +use crate::constants::trade_type::{SNIPER_BUY}; use crate::PumpFun; /// 添加nonce消费指令到指令集合中 diff --git a/src/pumpswap/buy.rs b/src/pumpswap/buy.rs index d258fe5..f06f5ad 100644 --- a/src/pumpswap/buy.rs +++ b/src/pumpswap/buy.rs @@ -11,12 +11,13 @@ use solana_sdk::{ signature::{Keypair, Signer}, system_instruction, transaction::VersionedTransaction, + native_token::sol_to_lamports, }; use spl_associated_token_account::instruction::create_associated_token_account_idempotent; use crate::common::{address_lookup_cache::get_address_lookup_table_account, nonce_cache::{self, NonceCache}, PriorityFee, SolanaRpcClient}; use crate::pumpswap::common::{calculate_with_slippage_buy, find_pool, get_buy_token_amount}; -use crate::constants::{accounts, trade::DEFAULT_SLIPPAGE, BUY_DISCRIMINATOR}; +use crate::constants::pumpswap::{accounts, trade::DEFAULT_SLIPPAGE, BUY_DISCRIMINATOR}; use crate::swqos::FeeClient; // Constants for compute budget @@ -31,7 +32,7 @@ const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 256 * 1024; fn add_nonce_instruction(instructions: &mut Vec, payer: &Keypair) -> Result<(), anyhow::Error> { let nonce_cache = NonceCache::get_instance(); let nonce_info = nonce_cache.get_nonce_info(); - if let (Some(nonce_pubkey), Some(program_id)) = (nonce_info.nonce_account, nonce_info.program_id) { + if let Some(nonce_pubkey) = nonce_info.nonce_account { let nonce_value = nonce_info.current_nonce; // 暂不加锁 // if nonce_info.lock { @@ -47,7 +48,7 @@ fn add_nonce_instruction(instructions: &mut Vec, payer: &Keypair) - // nonce_cache.lock(); // 创建自定义nonce消费指令 let nonce_consume_ix = Instruction { - program_id, + program_id: crate::constants::pumpswap::accounts::AMM_PROGRAM, accounts: vec![ AccountMeta::new(nonce_pubkey, false), AccountMeta::new_readonly(payer.pubkey(), true), @@ -55,7 +56,7 @@ fn add_nonce_instruction(instructions: &mut Vec, payer: &Keypair) - // INSTR_CONSUME = 1, 使用传入的nonce值 data: { let mut data = vec![1]; // INSTR_CONSUME = 1 - data.extend_from_slice(&nonce_value.to_le_bytes()); // 添加nonce值 + data.extend_from_slice(&nonce_value.to_bytes()); // 添加nonce值 data }, }; @@ -248,7 +249,7 @@ pub async fn build_buy_transaction_with_tip( system_instruction::transfer( &payer.pubkey(), &tip_account, - priority_fee.buy_tip_fee, + sol_to_lamports(priority_fee.buy_tip_fee), ), ]; diff --git a/src/pumpswap/pool.rs b/src/pumpswap/pool.rs index 0f60514..c91952e 100644 --- a/src/pumpswap/pool.rs +++ b/src/pumpswap/pool.rs @@ -1,7 +1,7 @@ use solana_sdk::pubkey::Pubkey; use anyhow::anyhow; use solana_account_decoder::UiAccountEncoding; -use crate::{common::SolanaRpcClient, constants::accounts}; +use crate::{common::SolanaRpcClient, constants::pumpswap::accounts}; use std::str::FromStr; #[derive(Debug, Clone)] @@ -91,7 +91,7 @@ impl Pool { sort_results: None, }; - let program_id = crate::constants::accounts::AMM_PROGRAM; + let program_id = crate::constants::pumpswap::accounts::AMM_PROGRAM; println!("program_id: {:?}", program_id); let accounts = rpc.get_program_accounts_with_config(&program_id, config).await?; diff --git a/src/pumpswap/sell.rs b/src/pumpswap/sell.rs index a06ab58..a9755b2 100644 --- a/src/pumpswap/sell.rs +++ b/src/pumpswap/sell.rs @@ -5,17 +5,17 @@ use anyhow::anyhow; use solana_sdk::{ compute_budget::ComputeBudgetInstruction, instruction::Instruction, - pubkey::Pubkey, signature::{Keypair, Signer}, system_instruction, transaction::VersionedTransaction, + native_token::sol_to_lamports, }; use spl_associated_token_account::instruction::create_associated_token_account_idempotent; use crate::common::{address_lookup_cache::get_address_lookup_table_account, PriorityFee, SolanaRpcClient}; use crate::pumpswap::common::{calculate_with_slippage_sell, find_pool, get_sell_sol_amount, get_token_balance}; -use crate::constants::{accounts, trade::DEFAULT_SLIPPAGE, SELL_DISCRIMINATOR}; +use crate::constants::pumpswap::{accounts, trade::DEFAULT_SLIPPAGE, SELL_DISCRIMINATOR}; use crate::swqos::FeeClient; // Constants for compute budget @@ -73,6 +73,23 @@ pub async fn sell_by_percent( sell(rpc, payer, mint, Some(amount), slippage_basis_points, priority_fee, lookup_table_key).await } +/// Sell tokens by amount +pub async fn sell_by_amount( + rpc: Arc, + payer: Arc, + mint: Pubkey, + amount: u64, + slippage_basis_points: Option, + priority_fee: PriorityFee, + lookup_table_key: Option +) -> Result<(), anyhow::Error> { + if amount == 0 { + return Err(anyhow!("Amount must be greater than 0")); + } + + sell(rpc, payer, mint, Some(amount), slippage_basis_points, priority_fee, lookup_table_key).await +} + // Sell tokens using a MEV service pub async fn sell_with_tip( rpc: Arc, @@ -144,6 +161,24 @@ pub async fn sell_by_percent_with_tip( sell_with_tip(rpc, fee_clients, payer, mint, Some(amount), slippage_basis_points, priority_fee, lookup_table_key).await } +// Sell tokens by amount using a MEV service +pub async fn sell_by_amount_with_tip( + rpc: Arc, + fee_clients: Vec>, + payer: Arc, + mint: Pubkey, + amount: u64, + slippage_basis_points: Option, + priority_fee: PriorityFee, + lookup_table_key: Option +) -> Result<(), anyhow::Error> { + if amount == 0 { + return Err(anyhow!("Amount must be greater than 0")); + } + + sell_with_tip(rpc, fee_clients, payer, mint, Some(amount), slippage_basis_points, priority_fee, lookup_table_key).await +} + // Build a transaction for selling tokens pub async fn build_sell_transaction( _rpc: Arc, @@ -206,7 +241,7 @@ pub async fn build_sell_transaction_with_tip( system_instruction::transfer( &payer.pubkey(), &tip_account, - priority_fee.sell_tip_fee, + sol_to_lamports(priority_fee.sell_tip_fee), ), ];