From 8dec2f8d8c94e0faa997b573f7880f090028ef11 Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Sat, 9 May 2026 14:41:22 +0800 Subject: [PATCH] fix(sell): trust observed creator_vault directly to prevent Anchor 2006 In build_sell_v1/v2, the creator_vault was previously derived via effective_creator_for_trade() passed to resolve_creator_vault_for_ix_with_fee_sharing. When observed_trade_creator differs from bonding_curve.creator (e.g. creator updated post-deployment), the derived vault PDA mismatches the on-chain seeds constraint, causing Anchor error 2006 (ConstraintSeeds) on sell. The pump.fun sell instruction validates creator_vault seeds as PDA("creator-vault", bonding_curve.creator), but buy does not check seeds, so buy succeeds while sell fails with 2006. Fix: when protocol_params.creator_vault is a valid non-default/non-phantom value (observed from gRPC / update_fee_shares), use it directly without re-derivation. Only fall back to resolve via bonding_curve.creator when creator_vault is missing. This ensures the authoritative observed vault is never overridden by a stale or mismatched creator. --- src/instruction/pumpfun.rs | 70 +++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index d15552d..94f52b0 100644 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -226,6 +226,7 @@ fn build_sell_v1(params: &SwapParams) -> Result> { use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_ix_data; use crate::instruction::utils::pumpfun::{ get_bonding_curve_v2_pda, get_protocol_extra_fee_recipient_random, + is_phantom_default_creator_vault, }; let protocol_params = params @@ -246,19 +247,29 @@ fn build_sell_v1(params: &SwapParams) -> Result> { let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); let bonding_curve = &protocol_params.bonding_curve; - let creator = protocol_params.effective_creator_for_trade(); - let creator_vault_account = resolve_creator_vault_for_ix_with_fee_sharing( - &creator, - protocol_params.creator_vault, - ¶ms.input_mint, - protocol_params.fee_sharing_creator_vault_if_active, - ) - .ok_or_else(|| { - anyhow!( - "creator_vault PDA derivation failed (creator={})", - creator + + // 卖出时:优先信任观测到的 creator_vault(来自 gRPC / pump fee 事件)。 + // 不要通过 effective_creator 推导覆盖,避免 creator 被修改后推导出错 → Anchor 2006。 + let creator_vault_account = if protocol_params.creator_vault != Pubkey::default() + && !is_phantom_default_creator_vault(&protocol_params.creator_vault) + { + protocol_params.creator_vault + } else { + resolve_creator_vault_for_ix_with_fee_sharing( + &bonding_curve.creator, + protocol_params.creator_vault, + ¶ms.input_mint, + protocol_params.fee_sharing_creator_vault_if_active, ) - })?; + .ok_or_else(|| { + anyhow!( + "creator_vault PDA derivation failed (curve_creator={})", + bonding_curve.creator + ) + })? + }; + + let creator = protocol_params.effective_creator_for_trade(); let sol_amount = get_sell_sol_amount_from_token_amount( bonding_curve.virtual_token_reserves as u128, @@ -569,6 +580,7 @@ fn build_sell_v2(params: &SwapParams) -> Result> { use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_v2_ix_data; use crate::instruction::utils::pumpfun::{ get_buyback_fee_recipient_random, get_fee_sharing_config_pda, + is_phantom_default_creator_vault, }; let protocol_params = params @@ -589,19 +601,29 @@ fn build_sell_v2(params: &SwapParams) -> Result> { let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); let bonding_curve = &protocol_params.bonding_curve; - let creator = protocol_params.effective_creator_for_trade(); - let creator_vault_account = resolve_creator_vault_for_ix_with_fee_sharing( - &creator, - protocol_params.creator_vault, - ¶ms.input_mint, - protocol_params.fee_sharing_creator_vault_if_active, - ) - .ok_or_else(|| { - anyhow!( - "creator_vault PDA derivation failed (creator={})", - creator + + // 卖出时:优先信任观测到的 creator_vault(来自 gRPC / pump fee 事件)。 + // 不要通过 effective_creator 推导覆盖,避免 creator 被修改后推导出错 → Anchor 2006。 + let creator_vault_account = if protocol_params.creator_vault != Pubkey::default() + && !is_phantom_default_creator_vault(&protocol_params.creator_vault) + { + protocol_params.creator_vault + } else { + resolve_creator_vault_for_ix_with_fee_sharing( + &bonding_curve.creator, + protocol_params.creator_vault, + ¶ms.input_mint, + protocol_params.fee_sharing_creator_vault_if_active, ) - })?; + .ok_or_else(|| { + anyhow!( + "creator_vault PDA derivation failed (curve_creator={})", + bonding_curve.creator + ) + })? + }; + + let creator = protocol_params.effective_creator_for_trade(); let sol_amount = get_sell_sol_amount_from_token_amount( bonding_curve.virtual_token_reserves as u128,