fix slippage and get_buy_token_amount_from_sol_amount
This commit is contained in:
@@ -191,14 +191,12 @@ impl PumpFun {
|
|||||||
&self,
|
&self,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount_token: Option<u64>,
|
amount_token: Option<u64>,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::sell::sell(
|
pumpfun::sell::sell(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint.clone(),
|
mint.clone(),
|
||||||
amount_token,
|
amount_token,
|
||||||
slippage_basis_points,
|
|
||||||
self.priority_fee.clone(),
|
self.priority_fee.clone(),
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
@@ -208,14 +206,12 @@ impl PumpFun {
|
|||||||
&self,
|
&self,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
percent: u64,
|
percent: u64,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::sell::sell_by_percent(
|
pumpfun::sell::sell_by_percent(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint.clone(),
|
mint.clone(),
|
||||||
percent,
|
percent,
|
||||||
slippage_basis_points,
|
|
||||||
self.priority_fee.clone(),
|
self.priority_fee.clone(),
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
@@ -224,7 +220,6 @@ impl PumpFun {
|
|||||||
&self,
|
&self,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
percent: u64,
|
percent: u64,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::sell::sell_by_percent_with_tip(
|
pumpfun::sell::sell_by_percent_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
@@ -232,7 +227,6 @@ impl PumpFun {
|
|||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
percent,
|
percent,
|
||||||
slippage_basis_points,
|
|
||||||
self.priority_fee.clone(),
|
self.priority_fee.clone(),
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
@@ -242,7 +236,6 @@ impl PumpFun {
|
|||||||
&self,
|
&self,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount_token: Option<u64>,
|
amount_token: Option<u64>,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
pumpfun::sell::sell_with_tip(
|
pumpfun::sell::sell_with_tip(
|
||||||
self.rpc.clone(),
|
self.rpc.clone(),
|
||||||
@@ -250,7 +243,6 @@ impl PumpFun {
|
|||||||
self.payer.clone(),
|
self.payer.clone(),
|
||||||
mint,
|
mint,
|
||||||
amount_token,
|
amount_token,
|
||||||
slippage_basis_points,
|
|
||||||
self.priority_fee.clone(),
|
self.priority_fee.clone(),
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -7,11 +7,11 @@ use spl_associated_token_account::instruction::create_associated_token_account;
|
|||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use std::{str::FromStr, time::Instant, sync::Arc};
|
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;
|
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(
|
pub async fn buy(
|
||||||
rpc: Arc<SolanaRpcClient>,
|
rpc: Arc<SolanaRpcClient>,
|
||||||
@@ -146,11 +146,18 @@ pub async fn build_buy_instructions(
|
|||||||
return Err(anyhow!("Amount cannot be zero"));
|
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 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 {
|
||||||
let (buy_token_amount, max_sol_cost) = get_buy_token_amount(&bonding_curve_account, buy_sol_cost, slippage_basis_points)?;
|
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![];
|
let mut instructions = vec![];
|
||||||
instructions.push(create_associated_token_account(
|
instructions.push(create_associated_token_account(
|
||||||
|
|||||||
+44
-1
@@ -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
|
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 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;
|
use borsh::BorshDeserialize;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@@ -188,6 +188,49 @@ pub fn get_buy_token_amount(
|
|||||||
Ok((buy_token, max_sol_cost))
|
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]
|
#[inline]
|
||||||
pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option<u64>) -> u64 {
|
pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option<u64>) -> u64 {
|
||||||
let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||||
|
|||||||
+8
-22
@@ -1,7 +1,6 @@
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use solana_client::rpc_config::RpcSimulateTransactionConfig;
|
|
||||||
use solana_sdk::{
|
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 solana_hash::Hash;
|
||||||
use spl_associated_token_account::get_associated_token_address;
|
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 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> {
|
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);
|
let ata = get_associated_token_address(&payer.pubkey(), mint);
|
||||||
@@ -32,10 +31,9 @@ pub async fn sell(
|
|||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount_token: Option<u64>,
|
amount_token: Option<u64>,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
priority_fee: PriorityFee,
|
priority_fee: PriorityFee,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> 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?;
|
let transaction = build_sell_transaction(rpc.clone(), payer.clone(), priority_fee, instructions).await?;
|
||||||
rpc.send_and_confirm_transaction(&transaction).await?;
|
rpc.send_and_confirm_transaction(&transaction).await?;
|
||||||
|
|
||||||
@@ -48,7 +46,6 @@ pub async fn sell_by_percent(
|
|||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
percent: u64,
|
percent: u64,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
priority_fee: PriorityFee,
|
priority_fee: PriorityFee,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
if percent == 0 || percent > 100 {
|
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 (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
|
||||||
let amount = balance_u64 * percent / 100;
|
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(
|
pub async fn sell_by_percent_with_tip(
|
||||||
@@ -66,7 +63,6 @@ pub async fn sell_by_percent_with_tip(
|
|||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
percent: u64,
|
percent: u64,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
priority_fee: PriorityFee,
|
priority_fee: PriorityFee,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
if percent == 0 || percent > 100 {
|
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 (balance_u64, _) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
|
||||||
let amount = balance_u64 * percent / 100;
|
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
|
/// Sell tokens using Jito
|
||||||
@@ -85,13 +81,12 @@ pub async fn sell_with_tip(
|
|||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount_token: Option<u64>,
|
amount_token: Option<u64>,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
priority_fee: PriorityFee,
|
priority_fee: PriorityFee,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
let mut transactions = vec![];
|
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?;
|
let recent_blockhash = rpc.get_latest_blockhash().await?;
|
||||||
for fee_client in fee_clients.clone() {
|
for fee_client in fee_clients.clone() {
|
||||||
@@ -186,7 +181,6 @@ pub async fn build_sell_instructions(
|
|||||||
payer: Arc<Keypair>,
|
payer: Arc<Keypair>,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount_token: Option<u64>,
|
amount_token: Option<u64>,
|
||||||
slippage_basis_points: Option<u64>,
|
|
||||||
) -> Result<Vec<Instruction>, anyhow::Error> {
|
) -> Result<Vec<Instruction>, anyhow::Error> {
|
||||||
let (balance_u64, ata) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
|
let (balance_u64, ata) = get_token_balance(rpc.as_ref(), payer.as_ref(), &mint).await?;
|
||||||
let amount = amount_token.unwrap_or(balance_u64);
|
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 global_account = get_global_account(rpc.as_ref()).await?;
|
||||||
let (bonding_curve_account, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).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 creator_vault_pda = get_creator_vault_pda(&bonding_curve_account.creator).unwrap();
|
||||||
|
|
||||||
let instructions = vec![
|
let instructions = vec![
|
||||||
@@ -216,7 +202,7 @@ pub async fn build_sell_instructions(
|
|||||||
&global_account.fee_recipient,
|
&global_account.fee_recipient,
|
||||||
instruction::Sell {
|
instruction::Sell {
|
||||||
_amount: amount,
|
_amount: amount,
|
||||||
_min_sol_output: min_sol_output_with_slippage,
|
_min_sol_output: 0,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user