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
+26 -4
View File
@@ -1,17 +1,39 @@
use crate::common::SolanaRpcClient;
use anyhow::Result;
use solana_address_lookup_table_interface::state::AddressLookupTable;
use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey};
use solana_message::AddressLookupTableAccount;
use solana_sdk::pubkey::Pubkey;
pub async fn fetch_address_lookup_table_account(
rpc: &SolanaRpcClient,
lookup_table_address: &Pubkey,
) -> Result<AddressLookupTableAccount, anyhow::Error> {
let account = rpc.get_account(lookup_table_address).await?;
let lookup_table = AddressLookupTable::deserialize(&account.data)?;
// Parse address lookup table manually
// Layout: 4 bytes (type) + 4 bytes (deactivation_slot) + 4 bytes (last_extended_slot) + 1 byte (last_extended_slot_start_index) + 1 byte (authority) + padding
// Then addresses start at offset 56, each address is 32 bytes
// First 4 bytes indicate if initialized (should be 1 or 2)
if account.data.len() < 56 {
return Err(anyhow::anyhow!("Address lookup table account data too short"));
}
// Read number of addresses (stored at offset 20 as u32, but we need to scan the bitmap)
// Actually simpler: addresses start at offset 56, count from bitmap at offset 8-20
let mut addresses = Vec::new();
let mut offset = 56;
while offset + 32 <= account.data.len() {
let addr_bytes: [u8; 32] = account.data[offset..offset + 32].try_into()?;
// Skip zero addresses (unused slots)
if addr_bytes != [0u8; 32] {
addresses.push(Pubkey::from(addr_bytes));
}
offset += 32;
}
let address_lookup_table_account = AddressLookupTableAccount {
key: *lookup_table_address,
addresses: lookup_table.addresses.to_vec(),
addresses,
};
Ok(address_lookup_table_account)
}
+14 -14
View File
@@ -1,8 +1,5 @@
use crate::common::SolanaRpcClient;
use solana_hash::Hash;
use solana_nonce::state::State;
use solana_nonce::versions::Versions;
use solana_sdk::account_utils::StateMut;
use solana_sdk::pubkey::Pubkey;
use tracing::error;
@@ -21,18 +18,21 @@ pub async fn fetch_nonce_info(
nonce_account: Pubkey,
) -> Option<DurableNonceInfo> {
match rpc.get_account(&nonce_account).await {
Ok(account) => match account.state() {
Ok(Versions::Current(state)) => {
if let State::Initialized(data) = *state {
let blockhash = data.durable_nonce.as_hash();
return Some(DurableNonceInfo {
nonce_account: Some(nonce_account),
current_nonce: Some(*blockhash),
});
}
Ok(account) => {
// Parse nonce account manually: first 4 bytes is version, then 4 bytes authority type
// For initialized nonce: version=0, authority_type=0, then authority (32 bytes), then blockhash (32 bytes), then fee_calculator
if account.data.len() >= 80 {
// Skip version (4) + authority_type (4) + authority (32) = 40 bytes
// Then blockhash is at offset 40
let blockhash_bytes: [u8; 32] = account.data[40..72].try_into().ok()?;
return Some(DurableNonceInfo {
nonce_account: Some(nonce_account),
current_nonce: Some(Hash::from(blockhash_bytes)),
});
} else {
error!("Nonce account data too short");
}
_ => (),
},
}
Err(e) => {
error!("Failed to get nonce account information: {:?}", e);
}
+4 -3
View File
@@ -3,7 +3,7 @@ use anyhow::anyhow;
use fnv::FnvHasher;
use once_cell::sync::Lazy;
use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
use solana_system_interface::instruction::create_account_with_seed;
use solana_system_interface::instruction as system_instruction;
use std::hash::Hasher;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
@@ -92,9 +92,10 @@ pub fn create_associated_token_account_use_seed(
let ata_like = Pubkey::create_with_seed(payer, seed, token_program)?;
let len = 165;
// 但账户的 owner 仍然使用正确的 token_programToken 或 Token-2022
// 🔧 修复:create_account_with_seed 的第3个参数必须是 payer(与第92行生成地址时使用的 base 一致
// 否则创建的账户地址与 ata_like 不匹配,导致 initializeAccount3 失败
let create_acc =
create_account_with_seed(payer, &ata_like, owner, seed, rent, len, token_program);
system_instruction::create_account_with_seed(payer, &ata_like, payer, seed, rent, len, token_program);
let init_acc = if is_2022_token {
crate::common::spl_token_2022::initialize_account3(&token_program, &ata_like, mint, owner)?
+1 -1
View File
@@ -1,5 +1,5 @@
use solana_sdk::{
message::{AccountMeta, Instruction},
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
+5 -5
View File
@@ -1,6 +1,6 @@
use solana_program::pubkey;
use solana_sdk::{
message::{AccountMeta, Instruction},
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
};
@@ -18,14 +18,14 @@ pub fn close_account(
let mut data = Vec::with_capacity(1);
data.push(9);
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
accounts.push(solana_sdk::message::AccountMeta::new(*account_pubkey, false));
accounts.push(solana_sdk::message::AccountMeta::new(*destination_pubkey, false));
accounts.push(solana_sdk::message::AccountMeta::new_readonly(
accounts.push(AccountMeta::new(*account_pubkey, false));
accounts.push(AccountMeta::new(*destination_pubkey, false));
accounts.push(AccountMeta::new_readonly(
*owner_pubkey,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(solana_sdk::message::AccountMeta::new_readonly(**signer_pubkey, true));
accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
}
Ok(Instruction { program_id: *token_program_id, accounts, data })
}
+1 -1
View File
@@ -1,6 +1,6 @@
use solana_program::pubkey;
use solana_sdk::{
message::{AccountMeta, Instruction},
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
};