feat: track_volume only when cashback; PumpSwap sell cashback use quote_mint ATA; sync IDL
- Pump/PumpSwap buy: track_volume OptionBool = Some(true) only when is_cashback_coin, else Some(false) - PumpSwap sell cashback: use get_user_volume_accumulator_quote_ata(quote_mint) instead of WSOL-only ATA - Sync idl/pump_amm.json, idl/pump_fees.json from pump-public-docs Made-with: Cursor
This commit is contained in:
+1
-1
@@ -4835,7 +4835,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": 6052,
|
"code": 6052,
|
||||||
"name": "CashbackEarnedDoesNotMatchTokenInVault"
|
"name": "TokensInVaultLessThanCashbackEarned"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
|
|||||||
-1157
File diff suppressed because it is too large
Load Diff
@@ -118,10 +118,11 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buy_data = [0u8; 24];
|
// IDL: buy/buy_exact_sol_in 第三参数 track_volume: OptionBool,仅代币支持返现时传 Some(true)
|
||||||
|
let track_volume = if bonding_curve.is_cashback_coin { [1u8, 1u8] } else { [1u8, 0u8] }; // Some(true) / Some(false)
|
||||||
|
let mut buy_data = [0u8; 26];
|
||||||
if params.use_exact_sol_amount.unwrap_or(true) {
|
if params.use_exact_sol_amount.unwrap_or(true) {
|
||||||
// buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64)
|
// buy_exact_sol_in(spendable_sol_in: u64, min_tokens_out: u64, track_volume)
|
||||||
// Spend exactly the input SOL amount, get at least min_tokens_out
|
|
||||||
let min_tokens_out = calculate_with_slippage_sell(
|
let min_tokens_out = calculate_with_slippage_sell(
|
||||||
buy_token_amount,
|
buy_token_amount,
|
||||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||||
@@ -129,12 +130,13 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
buy_data[..8].copy_from_slice(&BUY_EXACT_SOL_IN_DISCRIMINATOR);
|
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[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());
|
buy_data[16..24].copy_from_slice(&min_tokens_out.to_le_bytes());
|
||||||
|
buy_data[24..26].copy_from_slice(&track_volume);
|
||||||
} else {
|
} else {
|
||||||
// buy(token_amount: u64, max_sol_cost: u64)
|
// buy(token_amount: u64, max_sol_cost: u64, track_volume)
|
||||||
// Buy exactly token_amount tokens, pay up to max_sol_cost
|
|
||||||
buy_data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
buy_data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
||||||
buy_data[8..16].copy_from_slice(&buy_token_amount.to_le_bytes());
|
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());
|
buy_data[16..24].copy_from_slice(&max_sol_cost.to_le_bytes());
|
||||||
|
buy_data[24..26].copy_from_slice(&track_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine fee recipient based on mayhem mode (pump-public-docs: 2nd account = Mayhem fee recipient; use any one randomly)
|
// Determine fee recipient based on mayhem mode (pump-public-docs: 2nd account = Mayhem fee recipient; use any one randomly)
|
||||||
@@ -163,7 +165,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
accounts::FEE_CONFIG_META,
|
accounts::FEE_CONFIG_META,
|
||||||
accounts::FEE_PROGRAM_META,
|
accounts::FEE_PROGRAM_META,
|
||||||
];
|
];
|
||||||
accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false)); // bonding_curve_v2 (readonly) at end
|
accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false)); // remainingAccounts: @pump-fun/pump-sdk 要求末尾传 bondingCurveV2Pda(mint),勿删
|
||||||
|
|
||||||
instructions.push(Instruction::new_with_bytes(
|
instructions.push(Instruction::new_with_bytes(
|
||||||
accounts::PUMPFUN,
|
accounts::PUMPFUN,
|
||||||
@@ -289,7 +291,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
get_user_volume_accumulator_pda(¶ms.payer.pubkey()).unwrap();
|
get_user_volume_accumulator_pda(¶ms.payer.pubkey()).unwrap();
|
||||||
accounts.push(AccountMeta::new(user_volume_accumulator, false));
|
accounts.push(AccountMeta::new(user_volume_accumulator, false));
|
||||||
}
|
}
|
||||||
// Program upgrade: bonding_curve_v2 (readonly) at end of account list
|
// remainingAccounts: @pump-fun/pump-sdk sell 要求末尾传 bondingCurveV2Pda(mint)(cashback 时在 user_volume_accumulator 之后),勿删
|
||||||
let bonding_curve_v2 = get_bonding_curve_v2_pda(¶ms.input_mint).unwrap();
|
let bonding_curve_v2 = get_bonding_curve_v2_pda(¶ms.input_mint).unwrap();
|
||||||
accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false));
|
accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false));
|
||||||
|
|
||||||
|
|||||||
+33
-32
@@ -2,8 +2,9 @@ use crate::{
|
|||||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||||
instruction::utils::pumpswap::{
|
instruction::utils::pumpswap::{
|
||||||
accounts, fee_recipient_ata, get_mayhem_fee_recipient_random, get_pool_v2_pda,
|
accounts, fee_recipient_ata, get_mayhem_fee_recipient_random, get_pool_v2_pda,
|
||||||
get_user_volume_accumulator_pda, get_user_volume_accumulator_wsol_ata, BUY_DISCRIMINATOR,
|
get_user_volume_accumulator_pda, get_user_volume_accumulator_quote_ata,
|
||||||
BUY_EXACT_QUOTE_IN_DISCRIMINATOR, SELL_DISCRIMINATOR,
|
get_user_volume_accumulator_wsol_ata, BUY_DISCRIMINATOR, BUY_EXACT_QUOTE_IN_DISCRIMINATOR,
|
||||||
|
SELL_DISCRIMINATOR,
|
||||||
},
|
},
|
||||||
trading::{
|
trading::{
|
||||||
common::wsol_manager,
|
common::wsol_manager,
|
||||||
@@ -192,48 +193,44 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
|||||||
accounts.push(AccountMeta::new(wsol_ata, false));
|
accounts.push(AccountMeta::new(wsol_ata, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Program upgrade: pool_v2 (readonly) at end of account list
|
// remainingAccounts: @pump-fun/pump-swap-sdk 要求末尾传 poolV2Pda(baseMint),勿删
|
||||||
accounts.push(AccountMeta::new_readonly(
|
accounts.push(AccountMeta::new_readonly(
|
||||||
get_pool_v2_pda(&base_mint).unwrap(),
|
get_pool_v2_pda(&base_mint).unwrap(),
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create instruction data
|
// Create instruction data(buy/buy_exact_quote_in 第三参数 track_volume: OptionBool,仅代币支持返现时传 Some(true);sell 仅两参数)
|
||||||
let mut data = [0u8; 24];
|
let track_volume = if protocol_params.is_cashback_coin { [1u8, 1u8] } else { [1u8, 0u8] }; // Some(true) / Some(false)
|
||||||
if quote_is_wsol_or_usdc {
|
let data: Vec<u8> = if quote_is_wsol_or_usdc {
|
||||||
|
let mut buf = [0u8; 26];
|
||||||
if params.use_exact_sol_amount.unwrap_or(true) {
|
if params.use_exact_sol_amount.unwrap_or(true) {
|
||||||
// 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(
|
let min_base_amount_out = crate::utils::calc::common::calculate_with_slippage_sell(
|
||||||
token_amount,
|
token_amount,
|
||||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||||
);
|
);
|
||||||
data[..8].copy_from_slice(&BUY_EXACT_QUOTE_IN_DISCRIMINATOR);
|
buf[..8].copy_from_slice(&BUY_EXACT_QUOTE_IN_DISCRIMINATOR);
|
||||||
// spendable_quote_in (exact SOL amount to spend)
|
buf[8..16].copy_from_slice(¶ms.input_amount.unwrap_or(0).to_le_bytes());
|
||||||
data[8..16].copy_from_slice(¶ms.input_amount.unwrap_or(0).to_le_bytes());
|
buf[16..24].copy_from_slice(&min_base_amount_out.to_le_bytes());
|
||||||
// min_base_amount_out (minimum tokens to receive)
|
buf[24..26].copy_from_slice(&track_volume);
|
||||||
data[16..24].copy_from_slice(&min_base_amount_out.to_le_bytes());
|
|
||||||
} else {
|
} else {
|
||||||
// buy(base_amount_out: u64, max_quote_amount_in: u64)
|
buf[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
||||||
// Buy exactly base_amount_out tokens, pay up to max_quote_amount_in
|
buf[8..16].copy_from_slice(&token_amount.to_le_bytes());
|
||||||
data[..8].copy_from_slice(&BUY_DISCRIMINATOR);
|
buf[16..24].copy_from_slice(&sol_amount.to_le_bytes());
|
||||||
// base_amount_out
|
buf[24..26].copy_from_slice(&track_volume);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
buf.to_vec()
|
||||||
} else {
|
} else {
|
||||||
data[..8].copy_from_slice(&SELL_DISCRIMINATOR);
|
let mut buf = [0u8; 24];
|
||||||
// base_amount_in
|
buf[..8].copy_from_slice(&SELL_DISCRIMINATOR);
|
||||||
data[8..16].copy_from_slice(&sol_amount.to_le_bytes());
|
buf[8..16].copy_from_slice(&sol_amount.to_le_bytes());
|
||||||
// min_quote_amount_out
|
buf[16..24].copy_from_slice(&token_amount.to_le_bytes());
|
||||||
data[16..24].copy_from_slice(&token_amount.to_le_bytes());
|
buf.to_vec()
|
||||||
}
|
};
|
||||||
|
|
||||||
let buy_instruction = Instruction {
|
let buy_instruction = Instruction {
|
||||||
program_id: accounts::AMM_PROGRAM,
|
program_id: accounts::AMM_PROGRAM,
|
||||||
accounts: accounts.clone(),
|
accounts: accounts.clone(),
|
||||||
data: data.to_vec(),
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
instructions.push(buy_instruction);
|
instructions.push(buy_instruction);
|
||||||
@@ -391,17 +388,21 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
|||||||
}
|
}
|
||||||
accounts.push(accounts::FEE_CONFIG_META);
|
accounts.push(accounts::FEE_CONFIG_META);
|
||||||
accounts.push(accounts::FEE_PROGRAM_META);
|
accounts.push(accounts::FEE_PROGRAM_META);
|
||||||
// Cashback: remaining_accounts[0] = WSOL ATA of UserVolumeAccumulator, remaining_accounts[1] = UserVolumeAccumulator PDA
|
// Cashback sell: 官方 remainingAccounts = [accumulator 的 quote_mint ATA, accumulator PDA, poolV2](用 quote_mint 非固定 WSOL)
|
||||||
if protocol_params.is_cashback_coin {
|
if protocol_params.is_cashback_coin {
|
||||||
if let (Some(wsol_ata), Some(accumulator)) = (
|
if let (Some(quote_ata), Some(accumulator)) = (
|
||||||
get_user_volume_accumulator_wsol_ata(¶ms.payer.pubkey()),
|
get_user_volume_accumulator_quote_ata(
|
||||||
|
¶ms.payer.pubkey(),
|
||||||
|
"e_mint,
|
||||||
|
"e_token_program,
|
||||||
|
),
|
||||||
get_user_volume_accumulator_pda(¶ms.payer.pubkey()),
|
get_user_volume_accumulator_pda(¶ms.payer.pubkey()),
|
||||||
) {
|
) {
|
||||||
accounts.push(AccountMeta::new(wsol_ata, false));
|
accounts.push(AccountMeta::new(quote_ata, false));
|
||||||
accounts.push(AccountMeta::new(accumulator, false));
|
accounts.push(AccountMeta::new(accumulator, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Program upgrade: pool_v2 (readonly) at end of account list
|
// remainingAccounts: @pump-fun/pump-swap-sdk sell 要求末尾传 poolV2Pda(baseMint),勿删
|
||||||
accounts.push(AccountMeta::new_readonly(
|
accounts.push(AccountMeta::new_readonly(
|
||||||
get_pool_v2_pda(&base_mint).unwrap(),
|
get_pool_v2_pda(&base_mint).unwrap(),
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WSOL ATA of UserVolumeAccumulator for Pump AMM (used for cashback remaining accounts).
|
/// WSOL ATA of UserVolumeAccumulator for Pump AMM (buy cashback: remaining_accounts[0] 官方用 NATIVE_MINT).
|
||||||
pub fn get_user_volume_accumulator_wsol_ata(user: &Pubkey) -> Option<Pubkey> {
|
pub fn get_user_volume_accumulator_wsol_ata(user: &Pubkey) -> Option<Pubkey> {
|
||||||
let accumulator = get_user_volume_accumulator_pda(user)?;
|
let accumulator = get_user_volume_accumulator_pda(user)?;
|
||||||
Some(crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
|
Some(crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
|
||||||
@@ -272,6 +272,20 @@ pub fn get_user_volume_accumulator_wsol_ata(user: &Pubkey) -> Option<Pubkey> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Quote-mint ATA of UserVolumeAccumulator(sell cashback 时官方用 quoteMint,非固定 WSOL).
|
||||||
|
pub fn get_user_volume_accumulator_quote_ata(
|
||||||
|
user: &Pubkey,
|
||||||
|
quote_mint: &Pubkey,
|
||||||
|
quote_token_program: &Pubkey,
|
||||||
|
) -> Option<Pubkey> {
|
||||||
|
let accumulator = get_user_volume_accumulator_pda(user)?;
|
||||||
|
Some(crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
|
||||||
|
&accumulator,
|
||||||
|
quote_mint,
|
||||||
|
quote_token_program,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
||||||
let seeds: &[&[u8]; 1] = &[&seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
let seeds: &[&[u8]; 1] = &[&seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||||
let program_id: &Pubkey = &&accounts::AMM_PROGRAM;
|
let program_id: &Pubkey = &&accounts::AMM_PROGRAM;
|
||||||
|
|||||||
Reference in New Issue
Block a user