From 4198720c51fbeadcdbba66b8e729ffd8a4bebaca Mon Sep 17 00:00:00 2001 From: William Date: Wed, 14 May 2025 22:11:16 +0800 Subject: [PATCH] fix slippage and get_buy_token_amount_from_sol_amount --- src/lib.rs | 8 -------- src/pumpfun/buy.rs | 19 ++++++++++++------ src/pumpfun/common.rs | 45 ++++++++++++++++++++++++++++++++++++++++++- src/pumpfun/sell.rs | 30 ++++++++--------------------- 4 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1c6163e..021831f 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,14 +191,12 @@ impl PumpFun { &self, mint: Pubkey, amount_token: Option, - slippage_basis_points: Option, ) -> Result<(), anyhow::Error> { pumpfun::sell::sell( self.rpc.clone(), self.payer.clone(), mint.clone(), amount_token, - slippage_basis_points, self.priority_fee.clone(), ).await } @@ -208,14 +206,12 @@ impl PumpFun { &self, mint: Pubkey, percent: u64, - slippage_basis_points: Option, ) -> Result<(), anyhow::Error> { pumpfun::sell::sell_by_percent( self.rpc.clone(), self.payer.clone(), mint.clone(), percent, - slippage_basis_points, self.priority_fee.clone(), ).await } @@ -224,7 +220,6 @@ impl PumpFun { &self, mint: Pubkey, percent: u64, - slippage_basis_points: Option, ) -> Result<(), anyhow::Error> { pumpfun::sell::sell_by_percent_with_tip( self.rpc.clone(), @@ -232,7 +227,6 @@ impl PumpFun { self.payer.clone(), mint, percent, - slippage_basis_points, self.priority_fee.clone(), ).await } @@ -242,7 +236,6 @@ impl PumpFun { &self, mint: Pubkey, amount_token: Option, - slippage_basis_points: Option, ) -> Result<(), anyhow::Error> { pumpfun::sell::sell_with_tip( self.rpc.clone(), @@ -250,7 +243,6 @@ impl PumpFun { self.payer.clone(), mint, amount_token, - slippage_basis_points, self.priority_fee.clone(), ).await } diff --git a/src/pumpfun/buy.rs b/src/pumpfun/buy.rs index 92a6efc..334db94 100755 --- a/src/pumpfun/buy.rs +++ b/src/pumpfun/buy.rs @@ -7,11 +7,11 @@ use spl_associated_token_account::instruction::create_associated_token_account; use tokio::task::JoinHandle; use std::{str::FromStr, time::Instant, sync::Arc}; -use crate::{common::{PriorityFee, SolanaRpcClient}, constants::{self, global_constants::FEE_RECIPIENT, trade::DEFAULT_SLIPPAGE}, instruction, swqos::FeeClient}; +use crate::{common::{PriorityFee, SolanaRpcClient}, constants::{self, global_constants::FEE_RECIPIENT}, instruction, swqos::FeeClient}; const MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT: u32 = 250000; -use super::common::{calculate_with_slippage_buy, get_bonding_curve_account, get_buy_token_amount, get_creator_vault_pda, get_global_account, get_initial_buy_price}; +use super::common::{calculate_with_slippage_buy, get_bonding_curve_account, get_buy_token_amount_from_sol_amount, get_creator_vault_pda}; pub async fn buy( rpc: Arc, @@ -146,11 +146,18 @@ pub async fn build_buy_instructions( return Err(anyhow!("Amount cannot be zero")); } - let (bonding_curve_account, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).await?; + let (bonding_curve, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).await?; + let creator_vault_pda = get_creator_vault_pda(&bonding_curve.creator).unwrap(); + let max_sol_cost = calculate_with_slippage_buy(buy_sol_cost, slippage_basis_points.unwrap_or(100)); - let creator_vault_pda = get_creator_vault_pda(&bonding_curve_account.creator).unwrap(); - - let (buy_token_amount, max_sol_cost) = get_buy_token_amount(&bonding_curve_account, buy_sol_cost, slippage_basis_points)?; + let mut buy_token_amount = get_buy_token_amount_from_sol_amount(&bonding_curve, buy_sol_cost); + 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( diff --git a/src/pumpfun/common.rs b/src/pumpfun/common.rs index 6be013b..fa2995c 100755 --- a/src/pumpfun/common.rs +++ b/src/pumpfun/common.rs @@ -6,7 +6,7 @@ use solana_sdk::{ commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, instruction::Instruction, program_pack::Pack, pubkey::Pubkey, signature::Keypair, signer::Signer, system_instruction, transaction::Transaction }; use spl_associated_token_account::get_associated_token_address; -use crate::{accounts, common::{logs_data::TradeInfo, PriorityFee, SolanaRpcClient}, constants::{self, trade::DEFAULT_SLIPPAGE}}; +use crate::{accounts::{self, BondingCurveAccount}, common::{logs_data::TradeInfo, PriorityFee, SolanaRpcClient}, constants::{self, global_constants::{CREATOR_FEE, FEE_BASIS_POINTS}, trade::DEFAULT_SLIPPAGE}}; use borsh::BorshDeserialize; lazy_static::lazy_static! { @@ -188,6 +188,49 @@ pub fn get_buy_token_amount( Ok((buy_token, max_sol_cost)) } +pub fn get_buy_token_amount_from_sol_amount( + bonding_curve: &BondingCurveAccount, + amount: u64, +) -> u64 { + if amount == 0 { + return 0; + } + + if bonding_curve.virtual_token_reserves == 0 { + return 0; + } + + let total_fee_basis_points = FEE_BASIS_POINTS + + if bonding_curve.creator != Pubkey::default() { + CREATOR_FEE + } else { + 0 + }; + + // 转为 u128 防止溢出 + let amount_128 = amount as u128; + let total_fee_basis_points_128 = total_fee_basis_points as u128; + let input_amount = amount_128 + .checked_mul(10_000) + .unwrap() + .checked_div(total_fee_basis_points_128 + 10_000) + .unwrap(); + + let virtual_token_reserves = bonding_curve.virtual_token_reserves as u128; + let virtual_sol_reserves = bonding_curve.virtual_sol_reserves as u128; + let real_token_reserves = bonding_curve.real_token_reserves as u128; + + let denominator = virtual_sol_reserves + input_amount; + + let tokens_received = input_amount + .checked_mul(virtual_token_reserves) + .unwrap() + .checked_div(denominator) + .unwrap(); + + tokens_received.min(real_token_reserves) as u64 +} + #[inline] pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option) -> u64 { let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); diff --git a/src/pumpfun/sell.rs b/src/pumpfun/sell.rs index 02bf8d5..893c775 100755 --- a/src/pumpfun/sell.rs +++ b/src/pumpfun/sell.rs @@ -1,7 +1,6 @@ use anyhow::anyhow; -use solana_client::rpc_config::RpcSimulateTransactionConfig; use solana_sdk::{ - commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, instruction::Instruction, message::{v0, VersionedMessage}, native_token::sol_to_lamports, pubkey::Pubkey, signature::{Keypair, Signature}, signer::Signer, system_instruction, transaction::{Transaction, VersionedTransaction} + compute_budget::ComputeBudgetInstruction, instruction::Instruction, message::{v0, VersionedMessage}, native_token::sol_to_lamports, pubkey::Pubkey, signature::Keypair, signer::Signer, system_instruction, transaction::{Transaction, VersionedTransaction} }; use solana_hash::Hash; use spl_associated_token_account::get_associated_token_address; @@ -10,9 +9,9 @@ use tokio::task::JoinHandle; use std::{str::FromStr, time::Instant, sync::Arc}; -use crate::{common::{PriorityFee, SolanaRpcClient}, constants::trade::{DEFAULT_COMPUTE_UNIT_PRICE, DEFAULT_SLIPPAGE}, instruction, swqos::FeeClient}; +use crate::{common::{PriorityFee, SolanaRpcClient}, instruction, swqos::FeeClient}; -use super::common::{calculate_with_slippage_sell, get_bonding_curve_account, get_creator_vault_pda, get_global_account}; +use super::common::{get_bonding_curve_account, get_creator_vault_pda, get_global_account}; async fn get_token_balance(rpc: &SolanaRpcClient, payer: &Keypair, mint: &Pubkey) -> Result<(u64, Pubkey), anyhow::Error> { let ata = get_associated_token_address(&payer.pubkey(), mint); @@ -32,10 +31,9 @@ pub async fn sell( payer: Arc, mint: Pubkey, amount_token: Option, - slippage_basis_points: Option, priority_fee: PriorityFee, ) -> Result<(), anyhow::Error> { - let instructions = build_sell_instructions(rpc.clone(), payer.clone(), mint.clone(), amount_token, slippage_basis_points).await?; + let instructions = build_sell_instructions(rpc.clone(), payer.clone(), mint.clone(), amount_token).await?; let transaction = build_sell_transaction(rpc.clone(), payer.clone(), priority_fee, instructions).await?; rpc.send_and_confirm_transaction(&transaction).await?; @@ -48,7 +46,6 @@ pub async fn sell_by_percent( payer: Arc, mint: Pubkey, percent: u64, - slippage_basis_points: Option, priority_fee: PriorityFee, ) -> Result<(), anyhow::Error> { if percent == 0 || percent > 100 { @@ -57,7 +54,7 @@ pub async fn sell_by_percent( let (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?; let amount = balance_u64 * percent / 100; - sell(rpc, payer, mint, Some(amount), slippage_basis_points, priority_fee).await + sell(rpc, payer, mint, Some(amount), priority_fee).await } pub async fn sell_by_percent_with_tip( @@ -66,7 +63,6 @@ pub async fn sell_by_percent_with_tip( payer: Arc, mint: Pubkey, percent: u64, - slippage_basis_points: Option, priority_fee: PriorityFee, ) -> Result<(), anyhow::Error> { if percent == 0 || percent > 100 { @@ -75,7 +71,7 @@ pub async fn sell_by_percent_with_tip( let (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?; let amount = balance_u64 * percent / 100; - sell_with_tip(rpc, fee_clients, payer, mint, Some(amount), slippage_basis_points, priority_fee).await + sell_with_tip(rpc, fee_clients, payer, mint, Some(amount), priority_fee).await } /// Sell tokens using Jito @@ -85,13 +81,12 @@ pub async fn sell_with_tip( payer: Arc, mint: Pubkey, amount_token: Option, - slippage_basis_points: Option, priority_fee: PriorityFee, ) -> Result<(), anyhow::Error> { let start_time = Instant::now(); let mut transactions = vec![]; - let instructions = build_sell_instructions(rpc.clone(), payer.clone(), mint.clone(), amount_token, slippage_basis_points).await?; + let instructions = build_sell_instructions(rpc.clone(), payer.clone(), mint.clone(), amount_token).await?; let recent_blockhash = rpc.get_latest_blockhash().await?; for fee_client in fee_clients.clone() { @@ -186,7 +181,6 @@ pub async fn build_sell_instructions( payer: Arc, mint: Pubkey, amount_token: Option, - slippage_basis_points: Option, ) -> Result, anyhow::Error> { let (balance_u64, ata) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?; let amount = amount_token.unwrap_or(balance_u64); @@ -197,14 +191,6 @@ pub async fn build_sell_instructions( let global_account = get_global_account(rpc.as_ref()).await?; let (bonding_curve_account, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).await?; - let min_sol_output = bonding_curve_account - .get_sell_price(amount, global_account.fee_basis_points) - .map_err(|e| anyhow!(e))?; - let min_sol_output_with_slippage = calculate_with_slippage_sell( - min_sol_output, - slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), - ); - let creator_vault_pda = get_creator_vault_pda(&bonding_curve_account.creator).unwrap(); let instructions = vec![ @@ -216,7 +202,7 @@ pub async fn build_sell_instructions( &global_account.fee_recipient, instruction::Sell { _amount: amount, - _min_sol_output: min_sol_output_with_slippage, + _min_sol_output: 0, }, ),