fix(pumpswap): align remaining accounts with official SDK for buyback
- Append pool-v2 in buy/sell remaining accounts only when pool coin_creator is not default, matching @pump-fun/pump-swap-sdk. Always appending pool-v2 shifted buyback pubkey/ATA and caused BuybackFeeRecipientNotAuthorized (6053). - Add coin_creator to PumpSwapParams (filled from decoded Pool / from_trade). Update pumpswap_trading example to pass pool_data.coin_creator. - Document buyback fee recipient constants against GlobalConfig.buyback_fee_recipients. - Extend PumpSwap quote math to include cashback_fee_basis_points in creator-side fees. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -76,6 +76,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY {
|
||||
creator = params_coin_creator_vault_authority;
|
||||
}
|
||||
let cashback_fee_bps = protocol_params.cashback_fee_basis_points;
|
||||
|
||||
let (mut token_amount, sol_amount) = if quote_is_wsol_or_usdc {
|
||||
let result = buy_quote_input_internal(
|
||||
@@ -84,6 +85,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
&creator,
|
||||
cashback_fee_bps,
|
||||
)
|
||||
.unwrap();
|
||||
// base_amount_out, max_quote_amount_in
|
||||
@@ -95,6 +97,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
&creator,
|
||||
cashback_fee_bps,
|
||||
)
|
||||
.unwrap();
|
||||
// min_quote_amount_out, base_amount_in
|
||||
@@ -202,11 +205,15 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
accounts.push(AccountMeta::new(wsol_ata, false));
|
||||
}
|
||||
}
|
||||
// remainingAccounts: @pump-fun/pump-swap-sdk 要求末尾传 poolV2Pda(baseMint),勿删
|
||||
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)
|
||||
// `pool-v2` only when coin_creator ≠ default (@pump-fun/pump-swap-sdk remainingAccounts);
|
||||
// 否则多出的一格会把 buyback pubkey 错位,触发 BuybackFeeRecipientNotAuthorized(6053)。
|
||||
if protocol_params.coin_creator != Pubkey::default() {
|
||||
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));
|
||||
}
|
||||
// Trailing accounts: GlobalConfig.buyback_fee_recipients 中任 pubkey + quote ATA(与 pump-swap-sdk 静态池对齐;轮换时需查链上)。
|
||||
let protocol_extra = get_protocol_extra_fee_recipient_random();
|
||||
accounts.push(AccountMeta::new_readonly(protocol_extra, false));
|
||||
accounts.push(AccountMeta::new(
|
||||
@@ -299,6 +306,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY {
|
||||
creator = params_coin_creator_vault_authority;
|
||||
}
|
||||
let cashback_fee_bps = protocol_params.cashback_fee_basis_points;
|
||||
|
||||
let (token_amount, mut sol_amount) = if quote_is_wsol_or_usdc {
|
||||
let result = sell_base_input_internal(
|
||||
@@ -307,6 +315,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
&creator,
|
||||
cashback_fee_bps,
|
||||
)
|
||||
.unwrap();
|
||||
// base_amount_in, min_quote_amount_out
|
||||
@@ -318,6 +327,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
&creator,
|
||||
cashback_fee_bps,
|
||||
)
|
||||
.unwrap();
|
||||
// max_quote_amount_in, base_amount_out
|
||||
@@ -410,10 +420,12 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
accounts.push(AccountMeta::new(accumulator, false));
|
||||
}
|
||||
}
|
||||
// remainingAccounts: @pump-fun/pump-swap-sdk sell 要求末尾传 poolV2Pda(baseMint),勿删
|
||||
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));
|
||||
if protocol_params.coin_creator != Pubkey::default() {
|
||||
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(
|
||||
|
||||
@@ -91,7 +91,9 @@ 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).
|
||||
/// Buyback trailing fee recipients (`GlobalConfig.buyback_fee_recipients` on Pump AMM).
|
||||
/// Must match one of these for the pubkey passed after optional `pool-v2` (`@pump-fun/pump-swap-sdk` `getBuybackFeeRecipient`).
|
||||
/// Static mirror of pump-public-docs; if protocol rotates configs, decode global_config from RPC.
|
||||
pub const PROTOCOL_EXTRA_FEE_RECIPIENTS: [Pubkey; 8] = [
|
||||
pubkey!("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
|
||||
pubkey!("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
|
||||
|
||||
Reference in New Issue
Block a user