diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index f2e9df6..25e7ec4 100755 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -9,8 +9,8 @@ use crate::{ use crate::{ instruction::utils::pumpfun::{ accounts, get_bonding_curve_pda, get_bonding_curve_v2_pda, - get_user_volume_accumulator_pda, pump_fun_fee_recipient_meta, - resolve_creator_vault_for_ix, + get_protocol_extra_fee_recipient_random, get_user_volume_accumulator_pda, + pump_fun_fee_recipient_meta, resolve_creator_vault_for_ix, global_constants::{self}, BUY_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR, SELL_DISCRIMINATOR, }, @@ -177,6 +177,8 @@ impl InstructionBuilder for PumpFunInstructionBuilder { accounts::FEE_PROGRAM_META, ]; accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false)); // remainingAccounts: @pump-fun/pump-sdk 要求末尾传 bondingCurveV2Pda(mint),勿删 + // Apr 2026: extra protocol fee recipient after bonding-curve-v2 (writable) + accounts.push(AccountMeta::new(get_protocol_extra_fee_recipient_random(), false)); instructions.push(Instruction::new_with_bytes(accounts::PUMPFUN, &buy_data, accounts)); @@ -304,6 +306,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder { anyhow!("bonding_curve_v2 PDA derivation failed for mint {}", params.input_mint) })?; accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false)); + accounts.push(AccountMeta::new(get_protocol_extra_fee_recipient_random(), false)); instructions.push(Instruction::new_with_bytes(accounts::PUMPFUN, &sell_data, accounts)); diff --git a/src/instruction/pumpswap.rs b/src/instruction/pumpswap.rs index 1aa49b6..f12f15b 100755 --- a/src/instruction/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -2,9 +2,9 @@ use crate::{ constants::trade::trade::DEFAULT_SLIPPAGE, instruction::utils::pumpswap::{ accounts, fee_recipient_ata, get_mayhem_fee_recipient_random, get_pool_v2_pda, - get_user_volume_accumulator_pda, get_user_volume_accumulator_quote_ata, - get_user_volume_accumulator_wsol_ata, BUY_DISCRIMINATOR, BUY_EXACT_QUOTE_IN_DISCRIMINATOR, - SELL_DISCRIMINATOR, + get_protocol_extra_fee_recipient_random, get_user_volume_accumulator_pda, + get_user_volume_accumulator_quote_ata, get_user_volume_accumulator_wsol_ata, + BUY_DISCRIMINATOR, BUY_EXACT_QUOTE_IN_DISCRIMINATOR, SELL_DISCRIMINATOR, }, trading::{ common::wsol_manager, @@ -166,7 +166,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { } // Create buy instruction - let mut accounts = Vec::with_capacity(23); + let mut accounts = Vec::with_capacity(28); accounts.extend([ AccountMeta::new(pool, false), // pool_id AccountMeta::new(params.payer.pubkey(), true), // user (signer) @@ -206,6 +206,13 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { let pool_v2 = get_pool_v2_pda(&base_mint) .ok_or_else(|| anyhow!("pool_v2 PDA derivation failed for base_mint {}", base_mint))?; accounts.push(AccountMeta::new_readonly(pool_v2, false)); + // Apr 2026: protocol fee recipient + quote ATA (after pool-v2) + let protocol_extra = get_protocol_extra_fee_recipient_random(); + accounts.push(AccountMeta::new_readonly(protocol_extra, false)); + accounts.push(AccountMeta::new( + crate::instruction::utils::pumpswap::fee_recipient_ata(protocol_extra, quote_mint), + false, + )); // Create instruction data(buy/buy_exact_quote_in 第三参数 track_volume: OptionBool,仅代币支持返现时传 Some(true);sell 仅两参数) let track_volume = if protocol_params.is_cashback_coin { [1u8, 1u8] } else { [1u8, 0u8] }; // Some(true) / Some(false) @@ -359,7 +366,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { } // Create sell instruction - let mut accounts = Vec::with_capacity(23); + let mut accounts = Vec::with_capacity(28); accounts.extend([ AccountMeta::new(pool, false), // pool_id AccountMeta::new(params.payer.pubkey(), true), // user (signer) @@ -407,6 +414,12 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { let pool_v2 = get_pool_v2_pda(&base_mint) .ok_or_else(|| anyhow!("pool_v2 PDA derivation failed for base_mint {}", base_mint))?; accounts.push(AccountMeta::new_readonly(pool_v2, false)); + let protocol_extra = get_protocol_extra_fee_recipient_random(); + accounts.push(AccountMeta::new_readonly(protocol_extra, false)); + accounts.push(AccountMeta::new( + crate::instruction::utils::pumpswap::fee_recipient_ata(protocol_extra, quote_mint), + false, + )); // Create instruction data let mut data = [0u8; 24]; diff --git a/src/instruction/utils/pumpfun.rs b/src/instruction/utils/pumpfun.rs index b324c2b..8fc8a65 100644 --- a/src/instruction/utils/pumpfun.rs +++ b/src/instruction/utils/pumpfun.rs @@ -136,6 +136,19 @@ pub mod global_constants { pub const PUMPFUN_AMM_FEE_6: Pubkey = pubkey!("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"); // Pump.fun AMM: Protocol Fee 6 pub const PUMPFUN_AMM_FEE_7: Pubkey = pubkey!("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"); // Pump.fun AMM: Protocol Fee 7 + + /// Protocol extra fee recipients (Apr 2026 breaking upgrade). One is appended after `bonding-curve-v2`, **writable**. + /// See: + pub const PROTOCOL_EXTRA_FEE_RECIPIENTS: [Pubkey; 8] = [ + pubkey!("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"), + pubkey!("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"), + pubkey!("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"), + pubkey!("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"), + pubkey!("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"), + pubkey!("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"), + pubkey!("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"), + pubkey!("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"), + ]; } /// Constants related to program accounts and authorities @@ -258,6 +271,14 @@ pub fn get_standard_fee_recipient_meta_random() -> AccountMeta { } } +/// Random entry from [`global_constants::PROTOCOL_EXTRA_FEE_RECIPIENTS`] (must be last account after bonding-curve-v2, writable). +#[inline] +pub fn get_protocol_extra_fee_recipient_random() -> Pubkey { + *global_constants::PROTOCOL_EXTRA_FEE_RECIPIENTS + .choose(&mut rand::rng()) + .unwrap_or(&global_constants::PROTOCOL_EXTRA_FEE_RECIPIENTS[0]) +} + /// 账户 #2 fee recipient:优先使用 gRPC/ShredStream 解析值(同笔 create_v2+buy 的 `observed_fee_recipient` 或 `tradeEvent.feeRecipient`);未提供时按 mayhem 从静态池随机。 #[inline] pub fn pump_fun_fee_recipient_meta(from_stream: Pubkey, is_mayhem_mode: bool) -> AccountMeta { diff --git a/src/instruction/utils/pumpswap.rs b/src/instruction/utils/pumpswap.rs index 64acec7..c426b39 100644 --- a/src/instruction/utils/pumpswap.rs +++ b/src/instruction/utils/pumpswap.rs @@ -91,6 +91,18 @@ pub mod accounts { /// Default Mayhem fee recipient (first of MAYHEM_FEE_RECIPIENTS) pub const MAYHEM_FEE_RECIPIENT: Pubkey = MAYHEM_FEE_RECIPIENTS[0]; + /// Protocol extra fee recipients (Apr 2026 breaking upgrade). After `pool-v2`: recipient (readonly), then quote ATA (writable). + pub const PROTOCOL_EXTRA_FEE_RECIPIENTS: [Pubkey; 8] = [ + pubkey!("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"), + pubkey!("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"), + pubkey!("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"), + pubkey!("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"), + pubkey!("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"), + pubkey!("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"), + pubkey!("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"), + pubkey!("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"), + ]; + // META pub const GLOBAL_ACCOUNT_META: solana_sdk::instruction::AccountMeta = @@ -171,6 +183,14 @@ pub fn get_mayhem_fee_recipient_random() -> (Pubkey, AccountMeta) { (recipient, meta) } +/// Random entry from [`accounts::PROTOCOL_EXTRA_FEE_RECIPIENTS`] (readonly; paired with [`fee_recipient_ata`] as last account). +#[inline] +pub fn get_protocol_extra_fee_recipient_random() -> Pubkey { + *accounts::PROTOCOL_EXTRA_FEE_RECIPIENTS + .choose(&mut rand::rng()) + .unwrap_or(&accounts::PROTOCOL_EXTRA_FEE_RECIPIENTS[0]) +} + /// Pool v2 PDA (seeds: ["pool-v2", base_mint]). Required at end of buy/sell/buy_exact_quote_in accounts. #[inline] pub fn get_pool_v2_pda(base_mint: &Pubkey) -> Option {