fix(pumpfun): resolve creator_vault vs PDA(creator); validate fee recipient pool
- Add resolve_creator_vault_for_ix: use gRPC creator_vault only when it matches PDA(['creator-vault', bonding_curve.creator]); on mismatch use derived PDA to avoid ConstraintSeeds (2006) from stale/wrong stream vaults - pump_fun_fee_recipient_meta: only use event fee_recipient if authorized for is_mayhem_mode (avoids 6000 NotAuthorized when standard AMM fee is used on Mayhem coins) - Align PumpFunParams::from_trade/from_dev_trade cached creator_vault with the same resolution logic Made-with: Cursor
This commit is contained in:
+14
-40
@@ -8,9 +8,9 @@ use crate::{
|
||||
};
|
||||
use crate::{
|
||||
instruction::utils::pumpfun::{
|
||||
accounts, get_bonding_curve_pda, get_bonding_curve_v2_pda, get_creator_vault_pda,
|
||||
get_mayhem_fee_recipient_meta_random, get_standard_fee_recipient_meta_random,
|
||||
get_user_volume_accumulator_pda,
|
||||
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,
|
||||
global_constants::{self},
|
||||
BUY_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR, SELL_DISCRIMINATOR,
|
||||
},
|
||||
@@ -43,19 +43,16 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
}
|
||||
|
||||
let bonding_curve = &protocol_params.bonding_curve;
|
||||
// creator_vault must match PDA(["creator-vault", bonding_curve.creator_on_chain]). Events/gRPC
|
||||
// sometimes have a stale `creator` but a correct `creator_vault` from parsed ix; prefer non-default.
|
||||
// creator_vault must be PDA(creator) per bonding curve. Event vault: use only if == derived;
|
||||
// if stream sends a mismatched vault (wrong token / stale), fall back to derived.
|
||||
let creator = bonding_curve.creator;
|
||||
let creator_vault_pda = if protocol_params.creator_vault != Pubkey::default() {
|
||||
protocol_params.creator_vault
|
||||
} else {
|
||||
get_creator_vault_pda(&creator).ok_or_else(|| {
|
||||
let creator_vault_pda = resolve_creator_vault_for_ix(&creator, protocol_params.creator_vault)
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"creator_vault PDA derivation failed (creator={})",
|
||||
creator
|
||||
)
|
||||
})?
|
||||
};
|
||||
})?;
|
||||
|
||||
// ========================================
|
||||
// Trade calculation and account address preparation
|
||||
@@ -156,19 +153,9 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
buy_data[24..26].copy_from_slice(&track_volume);
|
||||
}
|
||||
|
||||
// Fee recipient: prefer gRPC/event pubkey (matches live trades); else @pump-fun/pump-sdk getFeeRecipient.
|
||||
// Fee recipient: event hint only if allowed for `is_mayhem_mode` (else 6000 NotAuthorized).
|
||||
let fee_recipient_meta =
|
||||
if protocol_params.fee_recipient != Pubkey::default() {
|
||||
AccountMeta {
|
||||
pubkey: protocol_params.fee_recipient,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
}
|
||||
} else if is_mayhem_mode {
|
||||
get_mayhem_fee_recipient_meta_random()
|
||||
} else {
|
||||
get_standard_fee_recipient_meta_random()
|
||||
};
|
||||
pump_fun_fee_recipient_meta(protocol_params.fee_recipient, is_mayhem_mode);
|
||||
|
||||
let bonding_curve_v2 = get_bonding_curve_v2_pda(¶ms.output_mint).ok_or_else(|| {
|
||||
anyhow!("bonding_curve_v2 PDA derivation failed for mint {}", params.output_mint)
|
||||
@@ -219,16 +206,13 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
|
||||
let bonding_curve = &protocol_params.bonding_curve;
|
||||
let creator = bonding_curve.creator;
|
||||
let creator_vault_pda = if protocol_params.creator_vault != Pubkey::default() {
|
||||
protocol_params.creator_vault
|
||||
} else {
|
||||
get_creator_vault_pda(&creator).ok_or_else(|| {
|
||||
let creator_vault_pda = resolve_creator_vault_for_ix(&creator, protocol_params.creator_vault)
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"creator_vault PDA derivation failed (creator={})",
|
||||
creator
|
||||
)
|
||||
})?
|
||||
};
|
||||
})?;
|
||||
|
||||
// ========================================
|
||||
// Trade calculation and account address preparation
|
||||
@@ -295,17 +279,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
sell_data[16..24].copy_from_slice(&min_sol_output.to_le_bytes());
|
||||
|
||||
let fee_recipient_meta =
|
||||
if protocol_params.fee_recipient != Pubkey::default() {
|
||||
AccountMeta {
|
||||
pubkey: protocol_params.fee_recipient,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
}
|
||||
} else if is_mayhem_mode {
|
||||
get_mayhem_fee_recipient_meta_random()
|
||||
} else {
|
||||
get_standard_fee_recipient_meta_random()
|
||||
};
|
||||
pump_fun_fee_recipient_meta(protocol_params.fee_recipient, is_mayhem_mode);
|
||||
|
||||
let mut accounts: Vec<AccountMeta> = vec![
|
||||
global_constants::GLOBAL_ACCOUNT_META,
|
||||
|
||||
@@ -255,6 +255,32 @@ pub fn get_standard_fee_recipient_meta_random() -> AccountMeta {
|
||||
}
|
||||
}
|
||||
|
||||
/// Use gRPC/event fee recipient only if authorized for this bonding curve mode; otherwise pick
|
||||
/// randomly from the same pool as `@pump-fun/pump-sdk` `getFeeRecipient` (avoids 6000 NotAuthorized
|
||||
/// when event fee is from a different mode, e.g. standard AMM fee on a Mayhem coin).
|
||||
#[inline]
|
||||
pub fn pump_fun_fee_recipient_meta(from_event: Pubkey, is_mayhem_mode: bool) -> AccountMeta {
|
||||
if from_event != Pubkey::default() {
|
||||
let authorized = if is_mayhem_mode {
|
||||
is_mayhem_fee_recipient(&from_event)
|
||||
} else {
|
||||
from_event == global_constants::FEE_RECIPIENT || is_amm_fee_recipient(&from_event)
|
||||
};
|
||||
if authorized {
|
||||
return AccountMeta {
|
||||
pubkey: from_event,
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
if is_mayhem_mode {
|
||||
get_mayhem_fee_recipient_meta_random()
|
||||
} else {
|
||||
get_standard_fee_recipient_meta_random()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Symbol;
|
||||
|
||||
impl Symbol {
|
||||
@@ -316,6 +342,20 @@ pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Creator vault for buy/sell must be `PDA(["creator-vault", bonding_curve.creator])` (same as on-chain).
|
||||
/// Use gRPC `from_event` only when it equals that PDA; if it differs (wrong mint / stale parse), use derived.
|
||||
#[inline]
|
||||
pub fn resolve_creator_vault_for_ix(creator: &Pubkey, from_event: Pubkey) -> Option<Pubkey> {
|
||||
let derived = get_creator_vault_pda(creator)?;
|
||||
if from_event == Pubkey::default() {
|
||||
Some(derived)
|
||||
} else if from_event == derived {
|
||||
Some(from_event)
|
||||
} else {
|
||||
Some(derived)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
crate::common::fast_fn::get_cached_pda(
|
||||
|
||||
+11
-13
@@ -127,7 +127,7 @@ impl std::fmt::Debug for SwapParams {
|
||||
pub struct PumpFunParams {
|
||||
pub bonding_curve: Arc<BondingCurveAccount>,
|
||||
pub associated_bonding_curve: Pubkey,
|
||||
/// From events/parsed ix when set; else derived from `bonding_curve.creator`. Buy/sell prefer non-default.
|
||||
/// `PDA(["creator-vault", bonding_curve.creator])`; from_trade resolves when event vault mismatches.
|
||||
pub creator_vault: Pubkey,
|
||||
pub token_program: Pubkey,
|
||||
/// Whether to close token account when selling, only effective during sell operations
|
||||
@@ -182,12 +182,11 @@ impl PumpFunParams {
|
||||
is_mayhem_mode,
|
||||
is_cashback_coin,
|
||||
);
|
||||
let creator_vault_resolved = if creator_vault != Pubkey::default() {
|
||||
creator_vault
|
||||
} else {
|
||||
crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve_account.creator)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let creator_vault_resolved = crate::instruction::utils::pumpfun::resolve_creator_vault_for_ix(
|
||||
&bonding_curve_account.creator,
|
||||
creator_vault,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
Self {
|
||||
bonding_curve: Arc::new(bonding_curve_account),
|
||||
associated_bonding_curve: associated_bonding_curve,
|
||||
@@ -230,12 +229,11 @@ impl PumpFunParams {
|
||||
is_mayhem_mode,
|
||||
is_cashback_coin,
|
||||
);
|
||||
let creator_vault_resolved = if creator_vault != Pubkey::default() {
|
||||
creator_vault
|
||||
} else {
|
||||
crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve.creator)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let creator_vault_resolved = crate::instruction::utils::pumpfun::resolve_creator_vault_for_ix(
|
||||
&bonding_curve.creator,
|
||||
creator_vault,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
Self {
|
||||
bonding_curve: Arc::new(bonding_curve),
|
||||
associated_bonding_curve: associated_bonding_curve,
|
||||
|
||||
Reference in New Issue
Block a user