feat: Pump.fun mayhem_mode, Solana 3.1.12, TradeConfig builder alignment

- PumpFunParams::from_trade/from_dev_trade: add mayhem_mode Option; infer Mayhem
  via is_mayhem_fee_recipient when None (fixes fee recipient / NotAuthorized 6000
  when AMM protocol fee pubkey is used).
- Add is_mayhem_fee_recipient and is_amm_fee_recipient helpers in pumpfun utils.
- Bump solana-* crates to 3.1.12; align tonic/prost; use solana-message for
  AddressLookupTableAccount; add solana-system-interface 3.0.
- Update examples and latency script for new PumpFunParams signature.
- SWQoS and transaction builder adjustments for dependency changes.

Made-with: Cursor
This commit is contained in:
0xfnzero
2026-04-11 18:43:18 +08:00
parent 8f2f99f3d9
commit 4b451af5ff
24 changed files with 272 additions and 77 deletions
+4 -3
View File
@@ -1,9 +1,10 @@
use solana_hash::Hash;
use solana_sdk::{
instruction::Instruction, message::AddressLookupTableAccount, pubkey::Pubkey,
instruction::Instruction, pubkey::Pubkey,
signature::Keypair, signer::Signer, transaction::VersionedTransaction,
};
use solana_system_interface::instruction::transfer;
use solana_message::AddressLookupTableAccount;
use solana_system_interface::instruction as system_instruction;
use std::sync::Arc;
use super::nonce_manager::{add_nonce_instruction, get_transaction_blockhash};
@@ -51,7 +52,7 @@ pub async fn build_transaction(
if with_tip && tip_amount > 0.0 {
let tip_lamports = sol_f64_to_lamports(tip_amount);
instructions.push(transfer(&payer.pubkey(), tip_account, tip_lamports));
instructions.push(system_instruction::transfer(&payer.pubkey(), tip_account, tip_lamports));
}
super::compute_budget_manager::extend_compute_budget_instructions(
+2 -2
View File
@@ -1,5 +1,5 @@
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
use solana_system_interface::instruction::transfer;
use solana_system_interface::instruction as system_instruction;
use crate::common::{
fast_fn::{
@@ -88,7 +88,7 @@ pub async fn transfer_sol(
return Err(anyhow!("Insufficient balance"));
}
let transfer_instruction = transfer(&payer.pubkey(), receive_wallet, amount);
let transfer_instruction = system_instruction::transfer(&payer.pubkey(), receive_wallet, amount);
let recent_blockhash = rpc.get_latest_blockhash().await?;
+4 -4
View File
@@ -7,8 +7,8 @@ use crate::common::{
spl_token::close_account,
};
use smallvec::SmallVec;
use solana_sdk::{instruction::Instruction, message::AccountMeta, pubkey::Pubkey};
use solana_system_interface::instruction::transfer;
use solana_sdk::{instruction::Instruction, instruction::AccountMeta, pubkey::Pubkey};
use solana_system_interface::instruction as system_instruction;
#[inline]
pub fn handle_wsol(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 3]> {
@@ -27,7 +27,7 @@ pub fn handle_wsol(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 3]>
&crate::constants::TOKEN_PROGRAM,
));
insts.extend([
transfer(&payer, &wsol_token_account, amount_in),
system_instruction::transfer(&payer, &wsol_token_account, amount_in),
// sync_native
Instruction {
program_id: crate::constants::TOKEN_PROGRAM,
@@ -91,7 +91,7 @@ pub fn wrap_sol_only(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 2
let mut insts = SmallVec::<[Instruction; 2]>::new();
insts.extend([
transfer(&payer, &wsol_token_account, amount_in),
system_instruction::transfer(&payer, &wsol_token_account, amount_in),
// sync_native
Instruction {
program_id: crate::constants::TOKEN_PROGRAM,
+1 -1
View File
@@ -15,7 +15,7 @@ use anyhow::{anyhow, Result};
use crossbeam_queue::ArrayQueue;
use once_cell::sync::OnceCell;
use solana_hash::Hash;
use solana_sdk::message::AddressLookupTableAccount;
use solana_message::AddressLookupTableAccount;
use solana_sdk::{
instruction::Instruction, pubkey::Pubkey, signature::Keypair, signature::Signature,
};
+2 -1
View File
@@ -1,9 +1,10 @@
use anyhow::Result;
use solana_hash::Hash;
use solana_sdk::{
instruction::Instruction, message::AddressLookupTableAccount, pubkey::Pubkey,
instruction::Instruction, pubkey::Pubkey,
signature::Keypair, signature::Signature,
};
use solana_message::AddressLookupTableAccount;
use std::{
sync::Arc,
time::{Duration, Instant},
+11 -4
View File
@@ -4,7 +4,7 @@ use crate::common::spl_associated_token_account::get_associated_token_address_wi
use crate::common::{GasFeeStrategy, SolanaRpcClient};
use crate::constants::TOKEN_PROGRAM;
use core_affinity::CoreId;
use crate::instruction::utils::pumpfun::global_constants::MAYHEM_FEE_RECIPIENT;
use crate::instruction::utils::pumpfun::is_mayhem_fee_recipient;
/// Concurrency + core binding config for parallel submit (precomputed at SDK init, one param on hot path). Uses Arc so no borrow of SwapParams.
#[derive(Clone)]
@@ -18,7 +18,7 @@ use crate::swqos::{SwqosClient, TradeType};
use crate::trading::common::get_multi_token_balances;
use crate::trading::MiddlewareManager;
use solana_hash::Hash;
use solana_sdk::message::AddressLookupTableAccount;
use solana_message::AddressLookupTableAccount;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use std::sync::Arc;
@@ -152,6 +152,8 @@ impl PumpFunParams {
/// When building from event/parser (e.g. sol-parser-sdk), pass `is_cashback_coin` from the event
/// so that sell instructions include the correct remaining accounts for cashback.
/// `mayhem_mode`: `Some` when known from Create/Trade event (`is_mayhem_mode` / `mayhem_mode`).
/// `None` falls back to detecting Mayhem via reserved fee recipient pubkeys only (not AMM protocol fee accounts).
pub fn from_dev_trade(
mint: Pubkey,
token_amount: u64,
@@ -164,8 +166,10 @@ impl PumpFunParams {
fee_recipient: Pubkey,
token_program: Pubkey,
is_cashback_coin: bool,
mayhem_mode: Option<bool>,
) -> Self {
let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT;
let is_mayhem_mode =
mayhem_mode.unwrap_or_else(|| is_mayhem_fee_recipient(&fee_recipient));
let bonding_curve_account = BondingCurveAccount::from_dev_trade(
bonding_curve,
&mint,
@@ -186,6 +190,7 @@ impl PumpFunParams {
/// When building from event/parser (e.g. sol-parser-sdk), pass `is_cashback_coin` from the event
/// so that sell instructions include the correct remaining accounts for cashback.
/// `mayhem_mode`: `Some` when known from Create/Trade event. `None` → infer from `fee_recipient` (Mayhem list only).
pub fn from_trade(
bonding_curve: Pubkey,
associated_bonding_curve: Pubkey,
@@ -200,8 +205,10 @@ impl PumpFunParams {
fee_recipient: Pubkey,
token_program: Pubkey,
is_cashback_coin: bool,
mayhem_mode: Option<bool>,
) -> Self {
let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT;
let is_mayhem_mode =
mayhem_mode.unwrap_or_else(|| is_mayhem_fee_recipient(&fee_recipient));
let bonding_curve = BondingCurveAccount::from_trade(
bonding_curve,
mint,
+2 -1
View File
@@ -22,9 +22,10 @@ use once_cell::sync::Lazy;
use solana_sdk::{
hash::Hash,
instruction::Instruction,
message::{v0, AddressLookupTableAccount, Message, VersionedMessage},
message::{v0, Message, VersionedMessage},
pubkey::Pubkey,
};
use solana_message::AddressLookupTableAccount;
use std::sync::Arc;
/// 预分配的交易构建器
pub struct PreallocatedTxBuilder {