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::{
|
use crate::{
|
||||||
instruction::utils::pumpfun::{
|
instruction::utils::pumpfun::{
|
||||||
accounts, get_bonding_curve_pda, get_bonding_curve_v2_pda, get_creator_vault_pda,
|
accounts, get_bonding_curve_pda, get_bonding_curve_v2_pda,
|
||||||
get_mayhem_fee_recipient_meta_random, get_standard_fee_recipient_meta_random,
|
get_user_volume_accumulator_pda, pump_fun_fee_recipient_meta,
|
||||||
get_user_volume_accumulator_pda,
|
resolve_creator_vault_for_ix,
|
||||||
global_constants::{self},
|
global_constants::{self},
|
||||||
BUY_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR, SELL_DISCRIMINATOR,
|
BUY_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR, SELL_DISCRIMINATOR,
|
||||||
},
|
},
|
||||||
@@ -43,19 +43,16 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let bonding_curve = &protocol_params.bonding_curve;
|
let bonding_curve = &protocol_params.bonding_curve;
|
||||||
// creator_vault must match PDA(["creator-vault", bonding_curve.creator_on_chain]). Events/gRPC
|
// creator_vault must be PDA(creator) per bonding curve. Event vault: use only if == derived;
|
||||||
// sometimes have a stale `creator` but a correct `creator_vault` from parsed ix; prefer non-default.
|
// if stream sends a mismatched vault (wrong token / stale), fall back to derived.
|
||||||
let creator = bonding_curve.creator;
|
let creator = bonding_curve.creator;
|
||||||
let creator_vault_pda = if protocol_params.creator_vault != Pubkey::default() {
|
let creator_vault_pda = resolve_creator_vault_for_ix(&creator, protocol_params.creator_vault)
|
||||||
protocol_params.creator_vault
|
.ok_or_else(|| {
|
||||||
} else {
|
|
||||||
get_creator_vault_pda(&creator).ok_or_else(|| {
|
|
||||||
anyhow!(
|
anyhow!(
|
||||||
"creator_vault PDA derivation failed (creator={})",
|
"creator_vault PDA derivation failed (creator={})",
|
||||||
creator
|
creator
|
||||||
)
|
)
|
||||||
})?
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Trade calculation and account address preparation
|
// Trade calculation and account address preparation
|
||||||
@@ -156,19 +153,9 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
buy_data[24..26].copy_from_slice(&track_volume);
|
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 =
|
let fee_recipient_meta =
|
||||||
if protocol_params.fee_recipient != Pubkey::default() {
|
pump_fun_fee_recipient_meta(protocol_params.fee_recipient, is_mayhem_mode);
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
let bonding_curve_v2 = get_bonding_curve_v2_pda(¶ms.output_mint).ok_or_else(|| {
|
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)
|
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 bonding_curve = &protocol_params.bonding_curve;
|
||||||
let creator = bonding_curve.creator;
|
let creator = bonding_curve.creator;
|
||||||
let creator_vault_pda = if protocol_params.creator_vault != Pubkey::default() {
|
let creator_vault_pda = resolve_creator_vault_for_ix(&creator, protocol_params.creator_vault)
|
||||||
protocol_params.creator_vault
|
.ok_or_else(|| {
|
||||||
} else {
|
|
||||||
get_creator_vault_pda(&creator).ok_or_else(|| {
|
|
||||||
anyhow!(
|
anyhow!(
|
||||||
"creator_vault PDA derivation failed (creator={})",
|
"creator_vault PDA derivation failed (creator={})",
|
||||||
creator
|
creator
|
||||||
)
|
)
|
||||||
})?
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Trade calculation and account address preparation
|
// 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());
|
sell_data[16..24].copy_from_slice(&min_sol_output.to_le_bytes());
|
||||||
|
|
||||||
let fee_recipient_meta =
|
let fee_recipient_meta =
|
||||||
if protocol_params.fee_recipient != Pubkey::default() {
|
pump_fun_fee_recipient_meta(protocol_params.fee_recipient, is_mayhem_mode);
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut accounts: Vec<AccountMeta> = vec![
|
let mut accounts: Vec<AccountMeta> = vec![
|
||||||
global_constants::GLOBAL_ACCOUNT_META,
|
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;
|
pub struct Symbol;
|
||||||
|
|
||||||
impl 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]
|
#[inline]
|
||||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||||
crate::common::fast_fn::get_cached_pda(
|
crate::common::fast_fn::get_cached_pda(
|
||||||
|
|||||||
+11
-13
@@ -127,7 +127,7 @@ impl std::fmt::Debug for SwapParams {
|
|||||||
pub struct PumpFunParams {
|
pub struct PumpFunParams {
|
||||||
pub bonding_curve: Arc<BondingCurveAccount>,
|
pub bonding_curve: Arc<BondingCurveAccount>,
|
||||||
pub associated_bonding_curve: Pubkey,
|
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 creator_vault: Pubkey,
|
||||||
pub token_program: Pubkey,
|
pub token_program: Pubkey,
|
||||||
/// Whether to close token account when selling, only effective during sell operations
|
/// Whether to close token account when selling, only effective during sell operations
|
||||||
@@ -182,12 +182,11 @@ impl PumpFunParams {
|
|||||||
is_mayhem_mode,
|
is_mayhem_mode,
|
||||||
is_cashback_coin,
|
is_cashback_coin,
|
||||||
);
|
);
|
||||||
let creator_vault_resolved = if creator_vault != Pubkey::default() {
|
let creator_vault_resolved = crate::instruction::utils::pumpfun::resolve_creator_vault_for_ix(
|
||||||
creator_vault
|
&bonding_curve_account.creator,
|
||||||
} else {
|
creator_vault,
|
||||||
crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve_account.creator)
|
)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
Self {
|
Self {
|
||||||
bonding_curve: Arc::new(bonding_curve_account),
|
bonding_curve: Arc::new(bonding_curve_account),
|
||||||
associated_bonding_curve: associated_bonding_curve,
|
associated_bonding_curve: associated_bonding_curve,
|
||||||
@@ -230,12 +229,11 @@ impl PumpFunParams {
|
|||||||
is_mayhem_mode,
|
is_mayhem_mode,
|
||||||
is_cashback_coin,
|
is_cashback_coin,
|
||||||
);
|
);
|
||||||
let creator_vault_resolved = if creator_vault != Pubkey::default() {
|
let creator_vault_resolved = crate::instruction::utils::pumpfun::resolve_creator_vault_for_ix(
|
||||||
creator_vault
|
&bonding_curve.creator,
|
||||||
} else {
|
creator_vault,
|
||||||
crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve.creator)
|
)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
Self {
|
Self {
|
||||||
bonding_curve: Arc::new(bonding_curve),
|
bonding_curve: Arc::new(bonding_curve),
|
||||||
associated_bonding_curve: associated_bonding_curve,
|
associated_bonding_curve: associated_bonding_curve,
|
||||||
|
|||||||
Reference in New Issue
Block a user