Add buy_exact_sol_in and buy_exact_quote_in instruction support
- Add BUY_EXACT_SOL_IN_DISCRIMINATOR for PumpFun - Add BUY_EXACT_QUOTE_IN_DISCRIMINATOR for PumpSwap - Add use_exact_in_instruction field to SwapParams - When enabled, uses exact input instructions that spend exactly the specified SOL/quote amount with slippage applied to output tokens - Defaults to false for backwards compatibility 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ use crate::{
|
||||
use crate::{
|
||||
instruction::utils::pumpfun::{
|
||||
accounts, get_bonding_curve_pda, get_creator, get_user_volume_accumulator_pda,
|
||||
global_constants::{self},
|
||||
global_constants::{self}, BUY_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR,
|
||||
},
|
||||
utils::calc::{
|
||||
common::{calculate_with_slippage_buy, calculate_with_slippage_sell},
|
||||
@@ -118,9 +118,23 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
}
|
||||
|
||||
let mut buy_data = [0u8; 24];
|
||||
buy_data[..8].copy_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // Method ID
|
||||
buy_data[8..16].copy_from_slice(&buy_token_amount.to_le_bytes());
|
||||
buy_data[16..24].copy_from_slice(&max_sol_cost.to_le_bytes());
|
||||
if params.use_exact_in_instruction {
|
||||
// buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64)
|
||||
// Spend exactly the input SOL amount, get at least min_tokens_out
|
||||
let min_tokens_out = calculate_with_slippage_sell(
|
||||
buy_token_amount,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
buy_data[..8].copy_from_slice(&BUY_EXACT_SOL_IN_DISCRIMINATOR);
|
||||
buy_data[8..16].copy_from_slice(¶ms.input_amount.unwrap_or(0).to_le_bytes());
|
||||
buy_data[16..24].copy_from_slice(&min_tokens_out.to_le_bytes());
|
||||
} else {
|
||||
// buy(token_amount: u64, max_sol_cost: u64)
|
||||
// Buy exactly token_amount tokens, pay up to max_sol_cost
|
||||
buy_data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
||||
buy_data[8..16].copy_from_slice(&buy_token_amount.to_le_bytes());
|
||||
buy_data[16..24].copy_from_slice(&max_sol_cost.to_le_bytes());
|
||||
}
|
||||
|
||||
// Determine fee recipient based on mayhem mode
|
||||
let fee_recipient_meta = if is_mayhem_mode {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
instruction::utils::pumpswap::{
|
||||
accounts, fee_recipient_ata, get_user_volume_accumulator_pda, BUY_DISCRIMINATOR,
|
||||
SELL_DISCRIMINATOR,
|
||||
BUY_EXACT_QUOTE_IN_DISCRIMINATOR, SELL_DISCRIMINATOR,
|
||||
},
|
||||
trading::{
|
||||
common::wsol_manager,
|
||||
@@ -187,11 +187,27 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
// Create instruction data
|
||||
let mut data = [0u8; 24];
|
||||
if quote_is_wsol_or_usdc {
|
||||
data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
||||
// base_amount_out
|
||||
data[8..16].copy_from_slice(&token_amount.to_le_bytes());
|
||||
// max_quote_amount_in
|
||||
data[16..24].copy_from_slice(&sol_amount.to_le_bytes());
|
||||
if params.use_exact_in_instruction {
|
||||
// buy_exact_quote_in(spendable_quote_in: u64, min_base_amount_out: u64)
|
||||
// Spend exactly the input SOL/quote amount, get at least min_base_amount_out
|
||||
let min_base_amount_out = crate::utils::calc::common::calculate_with_slippage_sell(
|
||||
token_amount,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
data[..8].copy_from_slice(&BUY_EXACT_QUOTE_IN_DISCRIMINATOR);
|
||||
// spendable_quote_in (exact SOL amount to spend)
|
||||
data[8..16].copy_from_slice(¶ms.input_amount.unwrap_or(0).to_le_bytes());
|
||||
// min_base_amount_out (minimum tokens to receive)
|
||||
data[16..24].copy_from_slice(&min_base_amount_out.to_le_bytes());
|
||||
} else {
|
||||
// buy(base_amount_out: u64, max_quote_amount_in: u64)
|
||||
// Buy exactly base_amount_out tokens, pay up to max_quote_amount_in
|
||||
data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
||||
// base_amount_out
|
||||
data[8..16].copy_from_slice(&token_amount.to_le_bytes());
|
||||
// max_quote_amount_in
|
||||
data[16..24].copy_from_slice(&sol_amount.to_le_bytes());
|
||||
}
|
||||
} else {
|
||||
data[..8].copy_from_slice(&SELL_DISCRIMINATOR);
|
||||
// base_amount_in
|
||||
|
||||
@@ -154,6 +154,11 @@ pub mod accounts {
|
||||
};
|
||||
}
|
||||
|
||||
/// Instruction discriminators for PumpFun program
|
||||
pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
|
||||
pub const BUY_EXACT_SOL_IN_DISCRIMINATOR: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
|
||||
pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
|
||||
|
||||
pub struct Symbol;
|
||||
|
||||
impl Symbol {
|
||||
|
||||
@@ -136,6 +136,7 @@ pub mod accounts {
|
||||
}
|
||||
|
||||
pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
|
||||
pub const BUY_EXACT_QUOTE_IN_DISCRIMINATOR: [u8; 8] = [198, 46, 21, 82, 180, 217, 232, 112];
|
||||
pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
|
||||
|
||||
// Find a pool for a specific mint
|
||||
|
||||
@@ -508,6 +508,7 @@ impl TradingClient {
|
||||
fixed_output_amount: params.fixed_output_token_amount,
|
||||
gas_fee_strategy: params.gas_fee_strategy,
|
||||
simulate: params.simulate,
|
||||
use_exact_in_instruction: false,
|
||||
};
|
||||
|
||||
// Validate protocol params
|
||||
@@ -617,6 +618,7 @@ impl TradingClient {
|
||||
fixed_output_amount: params.fixed_output_token_amount,
|
||||
gas_fee_strategy: params.gas_fee_strategy,
|
||||
simulate: params.simulate,
|
||||
use_exact_in_instruction: false,
|
||||
};
|
||||
|
||||
// Validate protocol params
|
||||
|
||||
@@ -67,6 +67,10 @@ pub struct SwapParams {
|
||||
pub fixed_output_amount: Option<u64>,
|
||||
pub gas_fee_strategy: GasFeeStrategy,
|
||||
pub simulate: bool,
|
||||
/// Use exact input instructions (buy_exact_sol_in for PumpFun, buy_exact_quote_in for PumpSwap).
|
||||
/// When true, the exact SOL/quote amount is spent and slippage is applied to output tokens.
|
||||
/// When false (default), uses regular buy instruction where slippage is applied to SOL/quote input.
|
||||
pub use_exact_in_instruction: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SwapParams {
|
||||
|
||||
Reference in New Issue
Block a user