diff --git a/src/accounts/bonding_curve.rs b/src/accounts/bonding_curve.rs index 2c7ef40..849f46c 100755 --- a/src/accounts/bonding_curve.rs +++ b/src/accounts/bonding_curve.rs @@ -28,7 +28,7 @@ use serde::{Serialize, Deserialize}; use solana_sdk::pubkey::Pubkey; -use crate::{constants::pumpfun::global_constants::{INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES, TOKEN_TOTAL_SUPPLY}, pumpfun::common::{get_bonding_curve_pda, get_creator_vault_pda}}; +use crate::{constants::pumpfun::global_constants::{INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES, TOKEN_TOTAL_SUPPLY}, trading::pumpfun::common::{get_bonding_curve_pda, get_creator_vault_pda}}; /// Represents the global configuration account for token pricing and fees #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/trading/protocols/bonk.rs b/src/instruction/bonk.rs similarity index 99% rename from src/trading/protocols/bonk.rs rename to src/instruction/bonk.rs index 5a9ec04..43000c5 100755 --- a/src/trading/protocols/bonk.rs +++ b/src/instruction/bonk.rs @@ -6,7 +6,7 @@ use crate::{ constants::bonk::{ accounts, trade::DEFAULT_SLIPPAGE, BUY_EXECT_IN_DISCRIMINATOR, SELL_EXECT_IN_DISCRIMINATOR, }, - bonk::{ + trading::bonk::{ common::{get_amount_out, get_pool_pda, get_token_balance, get_vault_pda}, pool::Pool, }, diff --git a/src/instruction/mod.rs b/src/instruction/mod.rs index 239a101..80bb1a8 100755 --- a/src/instruction/mod.rs +++ b/src/instruction/mod.rs @@ -11,7 +11,7 @@ //! - `sell`: Instruction to sell tokens back to the bonding curve in exchange for SOL. use crate::{ constants, - pumpfun::common::{ + trading::pumpfun::common::{ get_bonding_curve_pda, get_global_pda, get_metadata_pda, get_mint_authority_pda }, }; @@ -24,6 +24,10 @@ use solana_sdk::{ signer::Signer, }; +pub mod pumpfun; +pub mod pumpswap; +pub mod bonk; + pub struct Create { pub _name: String, pub _symbol: String, diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs new file mode 100755 index 0000000..b2e1b80 --- /dev/null +++ b/src/instruction/pumpfun.rs @@ -0,0 +1,298 @@ +use anyhow::{anyhow, Result}; +use solana_sdk::{ + instruction::Instruction, native_token::sol_to_lamports, +}; +use spl_associated_token_account::{ + get_associated_token_address, instruction::create_associated_token_account, +}; +use spl_token::instruction::close_account; + +use crate::{ + constants, trading::pumpfun::common::{ + get_bonding_curve_pda, get_global_pda, get_metadata_pda, get_mint_authority_pda + } +}; + +use solana_sdk::{ + instruction::AccountMeta, + pubkey::Pubkey, + signature::Keypair, + signer::Signer, +}; + +use crate::{ + constants::pumpfun::{global_constants::FEE_RECIPIENT, trade::DEFAULT_SLIPPAGE}, + trading::pumpfun::common::{ + calculate_with_slippage_buy, get_buy_token_amount_from_sol_amount, get_creator_vault_pda, + }, + trading::core::{ + params::{BuyParams, PumpFunParams, SellParams}, + traits::InstructionBuilder, + }, +}; + +/// PumpFun协议的指令构建器 +pub struct PumpFunInstructionBuilder; + +#[async_trait::async_trait] +impl InstructionBuilder for PumpFunInstructionBuilder { + async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { + // 获取PumpFun特定参数 + let protocol_params = params + .protocol_params + .as_any() + .downcast_ref::() + .ok_or_else(|| anyhow!("Invalid protocol params for PumpFun"))?; + + if params.amount_sol == 0 { + return Err(anyhow!("Amount cannot be zero")); + } + + let bonding_curve = if protocol_params.bonding_curve.is_some() { + protocol_params.bonding_curve.clone().unwrap() + } else { + return Err(anyhow!("Bonding curve not found")); + }; + + let max_sol_cost = calculate_with_slippage_buy( + params.amount_sol, + params + .slippage_basis_points + .unwrap_or(DEFAULT_SLIPPAGE), + ); + let creator_vault_pda = bonding_curve.get_creator_vault_pda(); + + let mut buy_token_amount = + get_buy_token_amount_from_sol_amount(&bonding_curve, params.amount_sol); + if buy_token_amount <= 100 * 1_000_000_u64 { + buy_token_amount = if max_sol_cost > sol_to_lamports(0.01) { + 25547619 * 1_000_000_u64 + } else { + 255476 * 1_000_000_u64 + }; + } + + let mut instructions = vec![]; + + // 创建关联代币账户 + instructions.push(create_associated_token_account( + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + ¶ms.mint, + &constants::pumpfun::accounts::TOKEN_PROGRAM, + )); + + // 创建买入指令 + instructions.push(buy( + params.payer.as_ref(), + ¶ms.mint, + &bonding_curve.account, + &creator_vault_pda, + &FEE_RECIPIENT, + Buy { + _amount: buy_token_amount, + _max_sol_cost: max_sol_cost, + }, + )); + + Ok(instructions) + } + + async fn build_sell_instructions(&self, params: &SellParams) -> Result> { + let amount_token = if let Some(amount) = params.amount_token { + if amount == 0 { + return Err(anyhow!("Amount cannot be zero")); + } + amount + } else { + return Err(anyhow!("Amount token is required")); + }; + let creator_vault_pda = get_creator_vault_pda(¶ms.creator).unwrap(); + let ata = get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint); + + // 获取代币余额 + let balance_u64 = if let Some(rpc) = ¶ms.rpc { + let balance = rpc.get_token_account_balance(&ata).await?; + balance + .amount + .parse::() + .map_err(|_| anyhow!("Failed to parse token balance"))? + } else { + return Err(anyhow!("RPC client is required to get token balance")); + }; + + let mut amount_token = amount_token; + if amount_token > balance_u64 { + amount_token = balance_u64; + } + + let mut instructions = vec![sell( + params.payer.as_ref(), + ¶ms.mint, + &creator_vault_pda, + &FEE_RECIPIENT, + Sell { + _amount: amount_token, + _min_sol_output: 1, + }, + )]; + + // 如果卖出全部代币,关闭账户 + if amount_token >= balance_u64 { + instructions.push(close_account( + &spl_token::ID, + &ata, + ¶ms.payer.pubkey(), + ¶ms.payer.pubkey(), + &[¶ms.payer.pubkey()], + )?); + } + + Ok(instructions) + } +} + +pub struct Create { + pub _name: String, + pub _symbol: String, + pub _uri: String, + pub _creator: Pubkey, +} + +impl Create { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 4 + self._name.len() + 4 + self._symbol.len() + 4 + self._uri.len() + 32); + + // 追加 discriminator + data.extend_from_slice(&[24, 30, 200, 40, 5, 28, 7, 119]); // discriminator + + // 添加 name 字符串长度和内容 + data.extend_from_slice(&(self._name.len() as u32).to_le_bytes()); // 添加 name 长度 + data.extend_from_slice(self._name.as_bytes()); // 添加 name 内容 + + // 添加 symbol 字符串长度和内容 + data.extend_from_slice(&(self._symbol.len() as u32).to_le_bytes()); // 添加 symbol 长度 + data.extend_from_slice(self._symbol.as_bytes()); // 添加 symbol 内容 + + // 添加 uri 字符串长度和内容 + data.extend_from_slice(&(self._uri.len() as u32).to_le_bytes()); // 添加 uri 长度 + data.extend_from_slice(self._uri.as_bytes()); // 添加 uri 内容 + + data.extend_from_slice(&self._creator.to_bytes()); + + data + } +} + +pub struct Buy { + pub _amount: u64, + pub _max_sol_cost: u64, +} + +impl Buy { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 8 + 8); + data.extend_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // discriminator + data.extend_from_slice(&self._amount.to_le_bytes()); + data.extend_from_slice(&self._max_sol_cost.to_le_bytes()); + data + } +} + +pub struct Sell { + pub _amount: u64, + pub _min_sol_output: u64, +} + +impl Sell { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 8 + 8); + data.extend_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // discriminator + data.extend_from_slice(&self._amount.to_le_bytes()); + data.extend_from_slice(&self._min_sol_output.to_le_bytes()); + data + } +} + +pub fn create(payer: &Keypair, mint: &Keypair, args: Create) -> Instruction { + let bonding_curve: Pubkey = get_bonding_curve_pda(&mint.pubkey()).unwrap(); + Instruction::new_with_bytes( + constants::pumpfun::accounts::PUMPFUN, + &args.data(), + vec![ + AccountMeta::new(mint.pubkey(), true), + AccountMeta::new(get_mint_authority_pda(), false), + AccountMeta::new(bonding_curve, false), + AccountMeta::new( + get_associated_token_address(&bonding_curve, &mint.pubkey()), + false, + ), + AccountMeta::new_readonly(get_global_pda(), false), + AccountMeta::new_readonly(constants::pumpfun::accounts::MPL_TOKEN_METADATA, false), + AccountMeta::new(get_metadata_pda(&mint.pubkey()), false), + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::ASSOCIATED_TOKEN_PROGRAM, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::RENT, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false), + ], + ) +} + +pub fn buy( + payer: &Keypair, + mint: &Pubkey, + bonding_curve_pda: &Pubkey, + creator_vault_pda: &Pubkey, + fee_recipient: &Pubkey, + args: Buy, +) -> Instruction { + Instruction::new_with_bytes( + constants::pumpfun::accounts::PUMPFUN, + &args.data(), + vec![ + AccountMeta::new_readonly(constants::pumpfun::global_constants::GLOBAL_ACCOUNT, false), + AccountMeta::new(*fee_recipient, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new(*bonding_curve_pda, false), + AccountMeta::new(get_associated_token_address(bonding_curve_pda, mint), false), + AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false), + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false), + AccountMeta::new(*creator_vault_pda, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false), + ], + ) +} + +pub fn sell( + payer: &Keypair, + mint: &Pubkey, + creator_vault_pda: &Pubkey, + fee_recipient: &Pubkey, + args: Sell, +) -> Instruction { + let bonding_curve: Pubkey = get_bonding_curve_pda(mint).unwrap(); + Instruction::new_with_bytes( + constants::pumpfun::accounts::PUMPFUN, + &args.data(), + vec![ + AccountMeta::new_readonly(constants::pumpfun::global_constants::GLOBAL_ACCOUNT, false), + AccountMeta::new(*fee_recipient, false), + AccountMeta::new_readonly(*mint, false), + AccountMeta::new(bonding_curve, false), + AccountMeta::new(get_associated_token_address(&bonding_curve, mint), false), + AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false), + AccountMeta::new(payer.pubkey(), true), + AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false), + AccountMeta::new(*creator_vault_pda, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false), + AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false), + ], + ) +} diff --git a/src/trading/protocols/pumpswap.rs b/src/instruction/pumpswap.rs similarity index 99% rename from src/trading/protocols/pumpswap.rs rename to src/instruction/pumpswap.rs index 199d46a..660fd95 100755 --- a/src/trading/protocols/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -1,13 +1,12 @@ use anyhow::{anyhow, Result}; use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer}; use spl_associated_token_account::instruction::create_associated_token_account_idempotent; -use std::sync::Arc; use crate::{ constants::pumpswap::{ accounts, trade::DEFAULT_SLIPPAGE, BUY_DISCRIMINATOR, SELL_DISCRIMINATOR, }, - pumpswap::common::{ + trading::pumpswap::common::{ calculate_with_slippage_buy, calculate_with_slippage_sell, coin_creator_vault_ata, coin_creator_vault_authority, find_pool, get_buy_token_amount, get_sell_sol_amount, get_token_balance, diff --git a/src/lib.rs b/src/lib.rs index 4f6fea2..ef86a5c 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,6 @@ pub mod event_parser; pub mod grpc; pub mod instruction; pub mod protos; -pub mod pumpfun; -pub mod pumpswap; -pub mod bonk; pub mod swqos; pub mod trading; @@ -141,7 +138,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::buy::buy( + trading::pumpfun::buy::buy( self.rpc.clone(), self.payer.clone(), mint, @@ -160,7 +157,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::buy::buy( + trading::pumpswap::buy::buy( self.rpc.clone(), self.payer.clone(), mint, @@ -183,7 +180,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::buy::buy( + trading::bonk::buy::buy( self.rpc.clone(), self.payer.clone(), mint, @@ -229,7 +226,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::buy::buy_with_tip( + trading::pumpfun::buy::buy_with_tip( self.swqos_clients.clone(), self.payer.clone(), mint, @@ -248,7 +245,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::buy::buy_with_tip( + trading::pumpswap::buy::buy_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -272,7 +269,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::buy::buy( + trading::bonk::buy::buy( self.rpc.clone(), self.payer.clone(), mint, @@ -308,7 +305,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::sell::sell_by_percent( + trading::pumpfun::sell::sell_by_percent( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -325,7 +322,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::sell::sell_by_percent( + trading::pumpswap::sell::sell_by_percent( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -347,7 +344,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::sell::sell_by_percent( + trading::bonk::sell::sell_by_percent( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -381,7 +378,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::sell::sell_by_amount( + trading::pumpfun::sell::sell_by_amount( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -397,7 +394,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::sell::sell_by_amount( + trading::pumpswap::sell::sell_by_amount( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -419,7 +416,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::sell::sell_by_amount( + trading::bonk::sell::sell_by_amount( self.rpc.clone(), self.payer.clone(), mint.clone(), @@ -453,7 +450,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::sell::sell_by_percent_with_tip( + trading::pumpfun::sell::sell_by_percent_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -471,7 +468,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::sell::sell_by_percent_with_tip( + trading::pumpswap::sell::sell_by_percent_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -494,7 +491,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::sell::sell_by_percent_with_tip( + trading::bonk::sell::sell_by_percent_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -528,7 +525,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpfun::sell::sell_by_amount_with_tip( + trading::pumpfun::sell::sell_by_amount_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -545,7 +542,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - pumpswap::sell::sell_by_amount_with_tip( + trading::pumpswap::sell::sell_by_amount_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -568,7 +565,7 @@ impl SolanaTrade { .as_any() .downcast_ref::() { - bonk::sell::sell_by_amount_with_tip( + trading::bonk::sell::sell_by_amount_with_tip( self.rpc.clone(), self.swqos_clients.clone(), self.payer.clone(), @@ -591,12 +588,12 @@ impl SolanaTrade { #[inline] pub async fn get_sol_balance(&self, payer: &Pubkey) -> Result { - pumpfun::common::get_sol_balance(&self.rpc, payer).await + trading::pumpfun::common::get_sol_balance(&self.rpc, payer).await } #[inline] pub async fn get_payer_sol_balance(&self) -> Result { - pumpfun::common::get_sol_balance(&self.rpc, &self.payer.pubkey()).await + trading::pumpfun::common::get_sol_balance(&self.rpc, &self.payer.pubkey()).await } #[inline] @@ -609,12 +606,12 @@ impl SolanaTrade { "get_token_balance payer: {}, mint: {}, rpc_url: {}", payer, mint, self.trade_config.rpc_url ); - pumpfun::common::get_token_balance(&self.rpc, payer, mint).await + trading::pumpfun::common::get_token_balance(&self.rpc, payer, mint).await } #[inline] pub async fn get_payer_token_balance(&self, mint: &Pubkey) -> Result { - pumpfun::common::get_token_balance(&self.rpc, &self.payer.pubkey(), mint).await + trading::pumpfun::common::get_token_balance(&self.rpc, &self.payer.pubkey(), mint).await } #[inline] @@ -629,12 +626,12 @@ impl SolanaTrade { #[inline] pub fn get_token_price(&self, virtual_sol_reserves: u64, virtual_token_reserves: u64) -> f64 { - pumpfun::common::get_token_price(virtual_sol_reserves, virtual_token_reserves) + 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 { - pumpfun::common::get_buy_price(amount, trade_info) + trading::pumpfun::common::get_buy_price(amount, trade_info) } #[inline] @@ -644,23 +641,23 @@ impl SolanaTrade { receive_wallet: &Pubkey, amount: u64, ) -> Result<(), anyhow::Error> { - pumpfun::common::transfer_sol(&self.rpc, payer, receive_wallet, amount).await + 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> { - pumpfun::common::close_token_account(&self.rpc, self.payer.as_ref(), mint).await + 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 { let (bonding_curve, _) = - pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?; + 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(pumpfun::common::get_token_price( + Ok(trading::pumpfun::common::get_token_price( virtual_sol_reserves, virtual_token_reserves, )) @@ -669,7 +666,7 @@ impl SolanaTrade { #[inline] pub async fn get_real_sol_reserves(&self, mint: &Pubkey) -> Result { let (bonding_curve, _) = - pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?; + trading::pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?; let actual_sol_reserves = bonding_curve.real_sol_reserves; @@ -679,7 +676,7 @@ impl SolanaTrade { #[inline] pub async fn get_creator(&self, mint: &Pubkey) -> Result { let (bonding_curve, _) = - pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?; + trading::pumpfun::common::get_bonding_curve_account_v2(&self.rpc, mint).await?; let creator = bonding_curve.creator; @@ -691,7 +688,7 @@ impl SolanaTrade { &self, pool_address: &Pubkey, ) -> Result { - let pool = pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; + let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; let (base_amount, quote_amount) = pool.get_token_balances(&self.rpc).await?; @@ -713,7 +710,7 @@ impl SolanaTrade { &self, pool_address: &Pubkey, ) -> Result { - let pool = pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; + let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; let (_, quote_amount) = pool.get_token_balances(&self.rpc).await?; @@ -725,7 +722,7 @@ impl SolanaTrade { &self, pool_address: &Pubkey, ) -> Result { - let pool = pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; + let pool = trading::pumpswap::pool::Pool::fetch(&self.rpc, pool_address).await?; let (base_amount, _) = pool.get_token_balances(&self.rpc).await?; diff --git a/src/bonk/buy.rs b/src/trading/bonk/buy.rs similarity index 100% rename from src/bonk/buy.rs rename to src/trading/bonk/buy.rs diff --git a/src/bonk/common.rs b/src/trading/bonk/common.rs similarity index 100% rename from src/bonk/common.rs rename to src/trading/bonk/common.rs diff --git a/src/bonk/mod.rs b/src/trading/bonk/mod.rs similarity index 100% rename from src/bonk/mod.rs rename to src/trading/bonk/mod.rs diff --git a/src/bonk/pool.rs b/src/trading/bonk/pool.rs similarity index 100% rename from src/bonk/pool.rs rename to src/trading/bonk/pool.rs diff --git a/src/bonk/sell.rs b/src/trading/bonk/sell.rs similarity index 100% rename from src/bonk/sell.rs rename to src/trading/bonk/sell.rs diff --git a/src/trading/factory.rs b/src/trading/factory.rs index ff0393d..32bd2f5 100755 --- a/src/trading/factory.rs +++ b/src/trading/factory.rs @@ -1,11 +1,10 @@ use anyhow::{anyhow, Result}; use std::sync::Arc; -use crate::trading::protocols::bonk::BonkInstructionBuilder; +use crate::instruction::{bonk::BonkInstructionBuilder, pumpfun::PumpFunInstructionBuilder, pumpswap::PumpSwapInstructionBuilder}; use super::{ core::{executor::GenericTradeExecutor, traits::TradeExecutor}, - protocols::{pumpfun::PumpFunInstructionBuilder, pumpswap::PumpSwapInstructionBuilder}, }; /// 支持的交易协议 diff --git a/src/trading/mod.rs b/src/trading/mod.rs index d28d8a6..566f7b1 100755 --- a/src/trading/mod.rs +++ b/src/trading/mod.rs @@ -1,7 +1,9 @@ pub mod common; pub mod core; pub mod factory; -pub mod protocols; +pub mod bonk; +pub mod pumpfun; +pub mod pumpswap; pub use core::params::{BuyParams, BuyWithTipParams, SellParams, SellWithTipParams}; pub use core::traits::{InstructionBuilder, TradeExecutor}; diff --git a/src/trading/protocols/mod.rs b/src/trading/protocols/mod.rs deleted file mode 100755 index 48cf9ee..0000000 --- a/src/trading/protocols/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod pumpfun; -pub mod pumpswap; -pub mod bonk; \ No newline at end of file diff --git a/src/trading/protocols/pumpfun.rs b/src/trading/protocols/pumpfun.rs deleted file mode 100755 index fb007eb..0000000 --- a/src/trading/protocols/pumpfun.rs +++ /dev/null @@ -1,144 +0,0 @@ -use anyhow::{anyhow, Result}; -use solana_sdk::{ - instruction::Instruction, native_token::sol_to_lamports, pubkey::Pubkey, signer::Signer, -}; -use spl_associated_token_account::{ - get_associated_token_address, instruction::create_associated_token_account, -}; -use spl_token::instruction::close_account; -use std::sync::Arc; - -use crate::{ - accounts::BondingCurveAccount, - constants::{self, pumpfun::{global_constants::FEE_RECIPIENT, trade::DEFAULT_SLIPPAGE}, trade_type::SNIPER_BUY}, - instruction, - pumpfun::common::{ - calculate_with_slippage_buy, get_bonding_curve_account_v2, get_bonding_curve_pda, - get_buy_token_amount_from_sol_amount, get_creator_vault_pda, init_bonding_curve_account, - }, - trading::core::{ - params::{BuyParams, PumpFunParams, SellParams}, - traits::InstructionBuilder, - }, -}; - -/// PumpFun协议的指令构建器 -pub struct PumpFunInstructionBuilder; - -#[async_trait::async_trait] -impl InstructionBuilder for PumpFunInstructionBuilder { - async fn build_buy_instructions(&self, params: &BuyParams) -> Result> { - // 获取PumpFun特定参数 - let protocol_params = params - .protocol_params - .as_any() - .downcast_ref::() - .ok_or_else(|| anyhow!("Invalid protocol params for PumpFun"))?; - - if params.amount_sol == 0 { - return Err(anyhow!("Amount cannot be zero")); - } - - let bonding_curve = if protocol_params.bonding_curve.is_some() { - protocol_params.bonding_curve.clone().unwrap() - } else { - return Err(anyhow!("Bonding curve not found")); - }; - - let max_sol_cost = calculate_with_slippage_buy( - params.amount_sol, - params - .slippage_basis_points - .unwrap_or(DEFAULT_SLIPPAGE), - ); - let creator_vault_pda = bonding_curve.get_creator_vault_pda(); - - let mut buy_token_amount = - get_buy_token_amount_from_sol_amount(&bonding_curve, params.amount_sol); - if buy_token_amount <= 100 * 1_000_000_u64 { - buy_token_amount = if max_sol_cost > sol_to_lamports(0.01) { - 25547619 * 1_000_000_u64 - } else { - 255476 * 1_000_000_u64 - }; - } - - let mut instructions = vec![]; - - // 创建关联代币账户 - instructions.push(create_associated_token_account( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - ¶ms.mint, - &constants::pumpfun::accounts::TOKEN_PROGRAM, - )); - - // 创建买入指令 - instructions.push(instruction::buy( - params.payer.as_ref(), - ¶ms.mint, - &bonding_curve.account, - &creator_vault_pda, - &FEE_RECIPIENT, - instruction::Buy { - _amount: buy_token_amount, - _max_sol_cost: max_sol_cost, - }, - )); - - Ok(instructions) - } - - async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - let amount_token = if let Some(amount) = params.amount_token { - if amount == 0 { - return Err(anyhow!("Amount cannot be zero")); - } - amount - } else { - return Err(anyhow!("Amount token is required")); - }; - let creator_vault_pda = get_creator_vault_pda(¶ms.creator).unwrap(); - let ata = get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint); - - // 获取代币余额 - let balance_u64 = if let Some(rpc) = ¶ms.rpc { - let balance = rpc.get_token_account_balance(&ata).await?; - balance - .amount - .parse::() - .map_err(|_| anyhow!("Failed to parse token balance"))? - } else { - return Err(anyhow!("RPC client is required to get token balance")); - }; - - let mut amount_token = amount_token; - if amount_token > balance_u64 { - amount_token = balance_u64; - } - - let mut instructions = vec![instruction::sell( - params.payer.as_ref(), - ¶ms.mint, - &creator_vault_pda, - &FEE_RECIPIENT, - instruction::Sell { - _amount: amount_token, - _min_sol_output: 1, - }, - )]; - - // 如果卖出全部代币,关闭账户 - if amount_token >= balance_u64 { - instructions.push(close_account( - &spl_token::ID, - &ata, - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &[¶ms.payer.pubkey()], - )?); - } - - Ok(instructions) - } -} diff --git a/src/pumpfun/buy.rs b/src/trading/pumpfun/buy.rs similarity index 100% rename from src/pumpfun/buy.rs rename to src/trading/pumpfun/buy.rs diff --git a/src/pumpfun/common.rs b/src/trading/pumpfun/common.rs similarity index 100% rename from src/pumpfun/common.rs rename to src/trading/pumpfun/common.rs diff --git a/src/pumpfun/mod.rs b/src/trading/pumpfun/mod.rs similarity index 100% rename from src/pumpfun/mod.rs rename to src/trading/pumpfun/mod.rs diff --git a/src/pumpfun/sell.rs b/src/trading/pumpfun/sell.rs similarity index 100% rename from src/pumpfun/sell.rs rename to src/trading/pumpfun/sell.rs diff --git a/src/pumpswap/buy.rs b/src/trading/pumpswap/buy.rs similarity index 100% rename from src/pumpswap/buy.rs rename to src/trading/pumpswap/buy.rs diff --git a/src/pumpswap/common.rs b/src/trading/pumpswap/common.rs similarity index 91% rename from src/pumpswap/common.rs rename to src/trading/pumpswap/common.rs index 8f3f3a6..b2d411f 100755 --- a/src/pumpswap/common.rs +++ b/src/trading/pumpswap/common.rs @@ -4,6 +4,7 @@ use solana_sdk::{ 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 { @@ -41,7 +42,7 @@ pub async fn find_pool( rpc: &SolanaRpcClient, mint: &Pubkey, ) -> Result { - let (pool_address, _) = crate::pumpswap::pool::Pool::find_by_mint(rpc, mint).await?; + let (pool_address, _) = pumpswap::pool::Pool::find_by_mint(rpc, mint).await?; Ok(pool_address) } @@ -51,7 +52,7 @@ pub async fn get_buy_token_amount( pool: &Pubkey, sol_amount: u64, ) -> Result { - let pool_data = crate::pumpswap::pool::Pool::fetch(rpc, pool).await?; + let pool_data = pumpswap::pool::Pool::fetch(rpc, pool).await?; pool_data.calculate_buy_amount(rpc, sol_amount).await } @@ -61,7 +62,7 @@ pub async fn get_sell_sol_amount( pool: &Pubkey, token_amount: u64, ) -> Result { - let pool_data = crate::pumpswap::pool::Pool::fetch(rpc, pool).await?; + let pool_data = pumpswap::pool::Pool::fetch(rpc, pool).await?; pool_data.calculate_sell_amount(rpc, token_amount).await } diff --git a/src/pumpswap/mod.rs b/src/trading/pumpswap/mod.rs similarity index 100% rename from src/pumpswap/mod.rs rename to src/trading/pumpswap/mod.rs diff --git a/src/pumpswap/pool.rs b/src/trading/pumpswap/pool.rs similarity index 100% rename from src/pumpswap/pool.rs rename to src/trading/pumpswap/pool.rs diff --git a/src/pumpswap/sell.rs b/src/trading/pumpswap/sell.rs similarity index 99% rename from src/pumpswap/sell.rs rename to src/trading/pumpswap/sell.rs index 4baf49a..86c4306 100755 --- a/src/pumpswap/sell.rs +++ b/src/trading/pumpswap/sell.rs @@ -4,8 +4,8 @@ use solana_sdk::{pubkey::Pubkey, signature::Keypair}; use std::sync::Arc; use crate::common::{PriorityFee, SolanaRpcClient}; -use crate::pumpswap::common::get_token_balance; 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