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.
This commit is contained in:
+46
-24
@@ -226,6 +226,7 @@ fn build_sell_v1(params: &SwapParams) -> Result<Vec<Instruction>> {
|
|||||||
use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_ix_data;
|
use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_ix_data;
|
||||||
use crate::instruction::utils::pumpfun::{
|
use crate::instruction::utils::pumpfun::{
|
||||||
get_bonding_curve_v2_pda, get_protocol_extra_fee_recipient_random,
|
get_bonding_curve_v2_pda, get_protocol_extra_fee_recipient_random,
|
||||||
|
is_phantom_default_creator_vault,
|
||||||
};
|
};
|
||||||
|
|
||||||
let protocol_params = params
|
let protocol_params = params
|
||||||
@@ -246,19 +247,29 @@ fn build_sell_v1(params: &SwapParams) -> Result<Vec<Instruction>> {
|
|||||||
let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||||
|
|
||||||
let bonding_curve = &protocol_params.bonding_curve;
|
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_vault(来自 gRPC / pump fee 事件)。
|
||||||
&creator,
|
// 不要通过 effective_creator 推导覆盖,避免 creator 被修改后推导出错 → Anchor 2006。
|
||||||
protocol_params.creator_vault,
|
let creator_vault_account = if protocol_params.creator_vault != Pubkey::default()
|
||||||
¶ms.input_mint,
|
&& !is_phantom_default_creator_vault(&protocol_params.creator_vault)
|
||||||
protocol_params.fee_sharing_creator_vault_if_active,
|
{
|
||||||
)
|
protocol_params.creator_vault
|
||||||
.ok_or_else(|| {
|
} else {
|
||||||
anyhow!(
|
resolve_creator_vault_for_ix_with_fee_sharing(
|
||||||
"creator_vault PDA derivation failed (creator={})",
|
&bonding_curve.creator,
|
||||||
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(
|
let sol_amount = get_sell_sol_amount_from_token_amount(
|
||||||
bonding_curve.virtual_token_reserves as u128,
|
bonding_curve.virtual_token_reserves as u128,
|
||||||
@@ -569,6 +580,7 @@ fn build_sell_v2(params: &SwapParams) -> Result<Vec<Instruction>> {
|
|||||||
use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_v2_ix_data;
|
use crate::instruction::pumpfun_ix_data::encode_pumpfun_sell_v2_ix_data;
|
||||||
use crate::instruction::utils::pumpfun::{
|
use crate::instruction::utils::pumpfun::{
|
||||||
get_buyback_fee_recipient_random, get_fee_sharing_config_pda,
|
get_buyback_fee_recipient_random, get_fee_sharing_config_pda,
|
||||||
|
is_phantom_default_creator_vault,
|
||||||
};
|
};
|
||||||
|
|
||||||
let protocol_params = params
|
let protocol_params = params
|
||||||
@@ -589,19 +601,29 @@ fn build_sell_v2(params: &SwapParams) -> Result<Vec<Instruction>> {
|
|||||||
let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
let slippage_bp = params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||||
|
|
||||||
let bonding_curve = &protocol_params.bonding_curve;
|
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_vault(来自 gRPC / pump fee 事件)。
|
||||||
&creator,
|
// 不要通过 effective_creator 推导覆盖,避免 creator 被修改后推导出错 → Anchor 2006。
|
||||||
protocol_params.creator_vault,
|
let creator_vault_account = if protocol_params.creator_vault != Pubkey::default()
|
||||||
¶ms.input_mint,
|
&& !is_phantom_default_creator_vault(&protocol_params.creator_vault)
|
||||||
protocol_params.fee_sharing_creator_vault_if_active,
|
{
|
||||||
)
|
protocol_params.creator_vault
|
||||||
.ok_or_else(|| {
|
} else {
|
||||||
anyhow!(
|
resolve_creator_vault_for_ix_with_fee_sharing(
|
||||||
"creator_vault PDA derivation failed (creator={})",
|
&bonding_curve.creator,
|
||||||
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(
|
let sol_amount = get_sell_sol_amount_from_token_amount(
|
||||||
bonding_curve.virtual_token_reserves as u128,
|
bonding_curve.virtual_token_reserves as u128,
|
||||||
|
|||||||
Reference in New Issue
Block a user