From 4cee5e6a06f89de874eab5e6254cd1e12f897c27 Mon Sep 17 00:00:00 2001 From: ysq Date: Mon, 10 Nov 2025 15:30:24 +0800 Subject: [PATCH] feat: add support for pump mayhem mode --- Cargo.toml | 2 +- README.md | 4 +- README_CN.md | 4 +- examples/address_lookup/src/main.rs | 1 + examples/nonce_cache/src/main.rs | 1 + examples/pumpfun_copy_trading/src/main.rs | 2 + examples/pumpfun_sniper_trading/src/main.rs | 1 + examples/pumpswap_trading/src/main.rs | 10 +- src/common/bonding_curve.rs | 6 ++ src/instruction/pumpfun.rs | 64 ++++++++++-- src/instruction/pumpswap.rs | 106 ++++++++++++-------- src/instruction/utils/pumpfun.rs | 18 ++-- src/instruction/utils/pumpswap.rs | 11 ++ src/instruction/utils/pumpswap_types.rs | 3 +- src/trading/core/params.rs | 15 +++ 15 files changed, 180 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6fd79e9..1bed9cb 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "3.2.1" +version = "3.3.0" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index ec24d64..ed84f13 100644 --- a/README.md +++ b/README.md @@ -87,14 +87,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.2.1" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.0" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "3.2.1" +sol-trade-sdk = "3.3.0" ``` ## 🛠️ Usage Examples diff --git a/README_CN.md b/README_CN.md index 35387ce..d0e54b4 100755 --- a/README_CN.md +++ b/README_CN.md @@ -87,14 +87,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.2.1" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.0" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "3.2.1" +sol-trade-sdk = "3.3.0" ``` ## 🛠️ 使用示例 diff --git a/examples/address_lookup/src/main.rs b/examples/address_lookup/src/main.rs index 39b73d2..a2d8d9b 100644 --- a/examples/address_lookup/src/main.rs +++ b/examples/address_lookup/src/main.rs @@ -148,6 +148,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul trade_info.real_token_reserves, trade_info.real_sol_reserves, None, + trade_info.fee_recipient, )), address_lookup_table_account: address_lookup_table_account, wait_transaction_confirmed: true, diff --git a/examples/nonce_cache/src/main.rs b/examples/nonce_cache/src/main.rs index 7531a80..db6ca43 100644 --- a/examples/nonce_cache/src/main.rs +++ b/examples/nonce_cache/src/main.rs @@ -148,6 +148,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul trade_info.real_token_reserves, trade_info.real_sol_reserves, None, + trade_info.fee_recipient, )), address_lookup_table_account: None, wait_transaction_confirmed: true, diff --git a/examples/pumpfun_copy_trading/src/main.rs b/examples/pumpfun_copy_trading/src/main.rs index 4d7ac66..08b4cfc 100644 --- a/examples/pumpfun_copy_trading/src/main.rs +++ b/examples/pumpfun_copy_trading/src/main.rs @@ -144,6 +144,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul trade_info.real_token_reserves, trade_info.real_sol_reserves, None, + trade_info.fee_recipient, )), address_lookup_table_account: None, wait_transaction_confirmed: true, @@ -187,6 +188,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul trade_info.real_token_reserves, trade_info.real_sol_reserves, Some(true), + trade_info.fee_recipient, )), address_lookup_table_account: None, wait_transaction_confirmed: true, diff --git a/examples/pumpfun_sniper_trading/src/main.rs b/examples/pumpfun_sniper_trading/src/main.rs index 231de9a..018de5e 100644 --- a/examples/pumpfun_sniper_trading/src/main.rs +++ b/examples/pumpfun_sniper_trading/src/main.rs @@ -110,6 +110,7 @@ async fn pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyR trade_info.associated_bonding_curve, trade_info.creator_vault, None, + trade_info.fee_recipient, )), address_lookup_table_account: None, wait_transaction_confirmed: true, diff --git a/examples/pumpswap_trading/src/main.rs b/examples/pumpswap_trading/src/main.rs index a71829f..0f8615c 100644 --- a/examples/pumpswap_trading/src/main.rs +++ b/examples/pumpswap_trading/src/main.rs @@ -46,7 +46,7 @@ async fn main() -> Result<(), Box> { PUMPSWAP_PROGRAM_ID.to_string(), // Listen to PumpSwap program ID ]; let account_exclude = vec![]; - + let account_required = vec![]; // Listen to transaction data @@ -148,9 +148,11 @@ async fn pumpswap_trade_with_grpc_buy_event(trade_info: PumpSwapBuyEvent) -> Any trade_info.coin_creator_vault_authority, trade_info.base_token_program, trade_info.quote_token_program, + trade_info.protocol_fee_recipient, ); let mint = if trade_info.base_mint == sol_trade_sdk::constants::USDC_TOKEN_ACCOUNT - || trade_info.base_mint == sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT { + || trade_info.base_mint == sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT + { trade_info.quote_mint } else { trade_info.base_mint @@ -172,9 +174,11 @@ async fn pumpswap_trade_with_grpc_sell_event(trade_info: PumpSwapSellEvent) -> A trade_info.coin_creator_vault_authority, trade_info.base_token_program, trade_info.quote_token_program, + trade_info.protocol_fee_recipient, ); let mint = if trade_info.base_mint == sol_trade_sdk::constants::USDC_TOKEN_ACCOUNT - || trade_info.base_mint == sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT { + || trade_info.base_mint == sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT + { trade_info.quote_mint } else { trade_info.base_mint diff --git a/src/common/bonding_curve.rs b/src/common/bonding_curve.rs index 6a52797..c2087ba 100755 --- a/src/common/bonding_curve.rs +++ b/src/common/bonding_curve.rs @@ -58,6 +58,8 @@ pub struct BondingCurveAccount { pub complete: bool, /// Creator of the bonding curve pub creator: Pubkey, + /// Whether this is a mayhem mode token (Token2022) + pub is_mayhem_mode: bool, } impl BondingCurveAccount { @@ -67,6 +69,7 @@ impl BondingCurveAccount { dev_token_amount: u64, dev_sol_amount: u64, creator: Pubkey, + is_mayhem_mode: bool, ) -> Self { let account = if bonding_curve != Pubkey::default() { bonding_curve @@ -83,6 +86,7 @@ impl BondingCurveAccount { token_total_supply: TOKEN_TOTAL_SUPPLY, complete: false, creator: creator, + is_mayhem_mode: is_mayhem_mode, } } @@ -94,6 +98,7 @@ impl BondingCurveAccount { virtual_sol_reserves: u64, real_token_reserves: u64, real_sol_reserves: u64, + is_mayhem_mode: bool, ) -> Self { let account = if bonding_curve != Pubkey::default() { bonding_curve @@ -110,6 +115,7 @@ impl BondingCurveAccount { token_total_supply: TOKEN_TOTAL_SUPPLY, complete: false, creator: creator, + is_mayhem_mode: is_mayhem_mode, } } diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index a6fa544..deac854 100755 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -68,12 +68,25 @@ impl InstructionBuilder for PumpFunInstructionBuilder { bonding_curve.account }; + // Determine token program based on mayhem mode + let is_mayhem_mode = bonding_curve.is_mayhem_mode; + let token_program = if is_mayhem_mode { + crate::constants::TOKEN_PROGRAM_2022 + } else { + crate::constants::TOKEN_PROGRAM + }; + let token_program_meta = if is_mayhem_mode { + crate::constants::TOKEN_PROGRAM_2022_META + } else { + crate::constants::TOKEN_PROGRAM_META + }; + let associated_bonding_curve = if protocol_params.associated_bonding_curve == Pubkey::default() { crate::common::fast_fn::get_associated_token_address_with_program_id_fast( &bonding_curve_addr, ¶ms.output_mint, - &crate::constants::TOKEN_PROGRAM, + &token_program, ) } else { protocol_params.associated_bonding_curve @@ -83,7 +96,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder { crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed( ¶ms.payer.pubkey(), ¶ms.output_mint, - &crate::constants::TOKEN_PROGRAM, + &token_program, params.open_seed_optimize, ); @@ -102,7 +115,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder { ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), ¶ms.output_mint, - &crate::constants::TOKEN_PROGRAM, + &token_program, params.open_seed_optimize, ), ); @@ -113,16 +126,23 @@ impl InstructionBuilder for PumpFunInstructionBuilder { buy_data[8..16].copy_from_slice(&buy_token_amount.to_le_bytes()); buy_data[16..24].copy_from_slice(&max_sol_cost.to_le_bytes()); + // Determine fee recipient based on mayhem mode + let fee_recipient_meta = if is_mayhem_mode { + global_constants::MAYHEM_FEE_RECIPIENT_META + } else { + global_constants::FEE_RECIPIENT_META + }; + let accounts: [AccountMeta; 16] = [ global_constants::GLOBAL_ACCOUNT_META, - global_constants::FEE_RECIPIENT_META, + fee_recipient_meta, AccountMeta::new_readonly(params.output_mint, false), AccountMeta::new(bonding_curve_addr, false), AccountMeta::new(associated_bonding_curve, false), AccountMeta::new(user_token_account, false), AccountMeta::new(params.payer.pubkey(), true), crate::constants::SYSTEM_PROGRAM_META, - crate::constants::TOKEN_PROGRAM_META, + token_program_meta, AccountMeta::new(creator_vault_pda, false), accounts::EVENT_AUTHORITY_META, accounts::PUMPFUN_META, @@ -188,12 +208,25 @@ impl InstructionBuilder for PumpFunInstructionBuilder { bonding_curve.account }; + // Determine token program based on mayhem mode + let is_mayhem_mode = bonding_curve.is_mayhem_mode; + let token_program = if is_mayhem_mode { + crate::constants::TOKEN_PROGRAM_2022 + } else { + crate::constants::TOKEN_PROGRAM + }; + let token_program_meta = if is_mayhem_mode { + crate::constants::TOKEN_PROGRAM_2022_META + } else { + crate::constants::TOKEN_PROGRAM_META + }; + let associated_bonding_curve = if protocol_params.associated_bonding_curve == Pubkey::default() { crate::common::fast_fn::get_associated_token_address_with_program_id_fast( &bonding_curve_addr, ¶ms.input_mint, - &crate::constants::TOKEN_PROGRAM, + &token_program, ) } else { protocol_params.associated_bonding_curve @@ -203,7 +236,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder { crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed( ¶ms.payer.pubkey(), ¶ms.input_mint, - &crate::constants::TOKEN_PROGRAM, + &token_program, params.open_seed_optimize, ); @@ -217,9 +250,16 @@ impl InstructionBuilder for PumpFunInstructionBuilder { sell_data[8..16].copy_from_slice(&token_amount.to_le_bytes()); sell_data[16..24].copy_from_slice(&min_sol_output.to_le_bytes()); + // Determine fee recipient based on mayhem mode + let fee_recipient_meta = if is_mayhem_mode { + global_constants::MAYHEM_FEE_RECIPIENT_META + } else { + global_constants::FEE_RECIPIENT_META + }; + let accounts: [AccountMeta; 14] = [ global_constants::GLOBAL_ACCOUNT_META, - global_constants::FEE_RECIPIENT_META, + fee_recipient_meta, AccountMeta::new_readonly(params.input_mint, false), AccountMeta::new(bonding_curve_addr, false), AccountMeta::new(associated_bonding_curve, false), @@ -227,7 +267,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder { AccountMeta::new(params.payer.pubkey(), true), crate::constants::SYSTEM_PROGRAM_META, AccountMeta::new(creator_vault_pda, false), - crate::constants::TOKEN_PROGRAM_META, + token_program_meta, accounts::EVENT_AUTHORITY_META, accounts::PUMPFUN_META, accounts::FEE_CONFIG_META, @@ -241,9 +281,11 @@ impl InstructionBuilder for PumpFunInstructionBuilder { )); // Optional: Close token account - if protocol_params.close_token_account_when_sell.unwrap_or(false) || params.close_input_mint_ata { + if protocol_params.close_token_account_when_sell.unwrap_or(false) + || params.close_input_mint_ata + { instructions.push(close_account( - &crate::constants::TOKEN_PROGRAM, + &token_program, &user_token_account, ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), diff --git a/src/instruction/pumpswap.rs b/src/instruction/pumpswap.rs index 3aa4e9e..6cb9a88 100755 --- a/src/instruction/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -53,10 +53,14 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { let pool_base_token_account = protocol_params.pool_base_token_account; let pool_quote_token_account = protocol_params.pool_quote_token_account; - let is_wsol = (base_mint == crate::constants::WSOL_TOKEN_ACCOUNT && quote_mint != crate::constants::USDC_TOKEN_ACCOUNT) - || (quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT && base_mint != crate::constants::USDC_TOKEN_ACCOUNT); - let is_usdc = (base_mint == crate::constants::USDC_TOKEN_ACCOUNT && quote_mint != crate::constants::WSOL_TOKEN_ACCOUNT) - || (quote_mint == crate::constants::USDC_TOKEN_ACCOUNT && base_mint != crate::constants::WSOL_TOKEN_ACCOUNT); + let is_wsol = (base_mint == crate::constants::WSOL_TOKEN_ACCOUNT + && quote_mint != crate::constants::USDC_TOKEN_ACCOUNT) + || (quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT + && base_mint != crate::constants::USDC_TOKEN_ACCOUNT); + let is_usdc = (base_mint == crate::constants::USDC_TOKEN_ACCOUNT + && quote_mint != crate::constants::WSOL_TOKEN_ACCOUNT) + || (quote_mint == crate::constants::USDC_TOKEN_ACCOUNT + && base_mint != crate::constants::WSOL_TOKEN_ACCOUNT); if !is_wsol && !is_usdc { return Err(anyhow!("Pool must contain WSOL or USDC")); } @@ -64,7 +68,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { // ======================================== // Trade calculation and account address preparation // ======================================== - let quote_is_wsol_or_usdc = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT + let quote_is_wsol_or_usdc = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT || quote_mint == crate::constants::USDC_TOKEN_ACCOUNT; let mut creator = Pubkey::default(); if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY { @@ -113,7 +117,17 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { "e_token_program, params.open_seed_optimize, ); - let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); + + // Determine fee recipient based on mayhem mode + let is_mayhem_mode = protocol_params.is_mayhem_mode; + let fee_recipient = + if is_mayhem_mode { accounts::MAYHEM_FEE_RECIPIENT } else { accounts::FEE_RECIPIENT }; + let fee_recipient_meta = if is_mayhem_mode { + accounts::MAYHEM_FEE_RECIPIENT_META + } else { + accounts::FEE_RECIPIENT_META + }; + let fee_recipient_ata = fee_recipient_ata(fee_recipient, quote_mint); // ======================================== // Build instructions @@ -140,18 +154,18 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { // Create buy instruction let mut accounts = Vec::with_capacity(23); accounts.extend([ - AccountMeta::new(pool, false), // pool_id - AccountMeta::new(params.payer.pubkey(), true), // user (signer) - accounts::GLOBAL_ACCOUNT_META, // global (readonly) - AccountMeta::new_readonly(base_mint, false), // base_mint (readonly) - AccountMeta::new_readonly(quote_mint, false), // quote_mint (readonly) - AccountMeta::new(user_base_token_account, false), // user_base_token_account - AccountMeta::new(user_quote_token_account, false), // user_quote_token_account - AccountMeta::new(pool_base_token_account, false), // pool_base_token_account - AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account - accounts::FEE_RECIPIENT_META, // fee_recipient (readonly) - AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata - AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) + AccountMeta::new(pool, false), // pool_id + AccountMeta::new(params.payer.pubkey(), true), // user (signer) + accounts::GLOBAL_ACCOUNT_META, // global (readonly) + AccountMeta::new_readonly(base_mint, false), // base_mint (readonly) + AccountMeta::new_readonly(quote_mint, false), // quote_mint (readonly) + AccountMeta::new(user_base_token_account, false), // user_base_token_account + AccountMeta::new(user_quote_token_account, false), // user_quote_token_account + AccountMeta::new(pool_base_token_account, false), // pool_base_token_account + AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account + fee_recipient_meta, // fee_recipient (readonly) + AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata + AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) AccountMeta::new_readonly(quote_token_program, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) crate::constants::SYSTEM_PROGRAM_META, // System Program (readonly) accounts::ASSOCIATED_TOKEN_PROGRAM_META, // ASSOCIATED_TOKEN_PROGRAM_ID (readonly) @@ -222,10 +236,14 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { let base_token_program = protocol_params.base_token_program; let quote_token_program = protocol_params.quote_token_program; - let is_wsol = (base_mint == crate::constants::WSOL_TOKEN_ACCOUNT && quote_mint != crate::constants::USDC_TOKEN_ACCOUNT) - || (quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT && base_mint != crate::constants::USDC_TOKEN_ACCOUNT); - let is_usdc = (base_mint == crate::constants::USDC_TOKEN_ACCOUNT && quote_mint != crate::constants::WSOL_TOKEN_ACCOUNT) - || (quote_mint == crate::constants::USDC_TOKEN_ACCOUNT && base_mint != crate::constants::WSOL_TOKEN_ACCOUNT); + let is_wsol = (base_mint == crate::constants::WSOL_TOKEN_ACCOUNT + && quote_mint != crate::constants::USDC_TOKEN_ACCOUNT) + || (quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT + && base_mint != crate::constants::USDC_TOKEN_ACCOUNT); + let is_usdc = (base_mint == crate::constants::USDC_TOKEN_ACCOUNT + && quote_mint != crate::constants::WSOL_TOKEN_ACCOUNT) + || (quote_mint == crate::constants::USDC_TOKEN_ACCOUNT + && base_mint != crate::constants::WSOL_TOKEN_ACCOUNT); if !is_wsol && !is_usdc { return Err(anyhow!("Pool must contain WSOL or USDC")); } @@ -237,7 +255,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { // ======================================== // Trade calculation and account address preparation // ======================================== - let quote_is_wsol_or_usdc = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT + let quote_is_wsol_or_usdc = quote_mint == crate::constants::WSOL_TOKEN_ACCOUNT || quote_mint == crate::constants::USDC_TOKEN_ACCOUNT; let mut creator = Pubkey::default(); if params_coin_creator_vault_authority != accounts::DEFAULT_COIN_CREATOR_VAULT_AUTHORITY { @@ -272,7 +290,17 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { sol_amount = params.fixed_output_amount.unwrap(); } - let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint); + // Determine fee recipient based on mayhem mode + let is_mayhem_mode = protocol_params.is_mayhem_mode; + let fee_recipient = + if is_mayhem_mode { accounts::MAYHEM_FEE_RECIPIENT } else { accounts::FEE_RECIPIENT }; + let fee_recipient_meta = if is_mayhem_mode { + accounts::MAYHEM_FEE_RECIPIENT_META + } else { + accounts::FEE_RECIPIENT_META + }; + let fee_recipient_ata = fee_recipient_ata(fee_recipient, quote_mint); + let user_base_token_account = crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed( ¶ms.payer.pubkey(), @@ -300,18 +328,18 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { // Create sell instruction let mut accounts = Vec::with_capacity(23); accounts.extend([ - AccountMeta::new(pool, false), // pool_id - AccountMeta::new(params.payer.pubkey(), true), // user (signer) - accounts::GLOBAL_ACCOUNT_META, // global (readonly) - AccountMeta::new_readonly(base_mint, false), // mint (readonly) - AccountMeta::new_readonly(quote_mint, false), // WSOL_TOKEN_ACCOUNT (readonly) - AccountMeta::new(user_base_token_account, false), // user_base_token_account - AccountMeta::new(user_quote_token_account, false), // user_quote_token_account - AccountMeta::new(pool_base_token_account, false), // pool_base_token_account - AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account - accounts::FEE_RECIPIENT_META, // fee_recipient (readonly) - AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata - AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) + AccountMeta::new(pool, false), // pool_id + AccountMeta::new(params.payer.pubkey(), true), // user (signer) + accounts::GLOBAL_ACCOUNT_META, // global (readonly) + AccountMeta::new_readonly(base_mint, false), // mint (readonly) + AccountMeta::new_readonly(quote_mint, false), // WSOL_TOKEN_ACCOUNT (readonly) + AccountMeta::new(user_base_token_account, false), // user_base_token_account + AccountMeta::new(user_quote_token_account, false), // user_quote_token_account + AccountMeta::new(pool_base_token_account, false), // pool_base_token_account + AccountMeta::new(pool_quote_token_account, false), // pool_quote_token_account + fee_recipient_meta, // fee_recipient (readonly) + AccountMeta::new(fee_recipient_ata, false), // fee_recipient_ata + AccountMeta::new_readonly(base_token_program, false), // TOKEN_PROGRAM_ID (readonly) AccountMeta::new_readonly(quote_token_program, false), // TOKEN_PROGRAM_ID (readonly, duplicated as in JS) crate::constants::SYSTEM_PROGRAM_META, // System Program (readonly) accounts::ASSOCIATED_TOKEN_PROGRAM_META, // ASSOCIATED_TOKEN_PROGRAM_ID (readonly) @@ -358,11 +386,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { } if params.close_input_mint_ata { instructions.push(crate::common::spl_token::close_account( - if quote_is_wsol_or_usdc { - &base_token_program - } else { - "e_token_program - }, + if quote_is_wsol_or_usdc { &base_token_program } else { "e_token_program }, if quote_is_wsol_or_usdc { &user_base_token_account } else { diff --git a/src/instruction/utils/pumpfun.rs b/src/instruction/utils/pumpfun.rs index 0ecd18d..838f568 100644 --- a/src/instruction/utils/pumpfun.rs +++ b/src/instruction/utils/pumpfun.rs @@ -1,8 +1,7 @@ -use crate::common::{bonding_curve::BondingCurveAccount, global::GlobalAccount, SolanaRpcClient}; +use crate::common::{bonding_curve::BondingCurveAccount, SolanaRpcClient}; use anyhow::anyhow; use solana_sdk::pubkey::Pubkey; -use std::{collections::HashMap, sync::Arc}; -use tokio::sync::RwLock; +use std::sync::Arc; /// Constants used as seeds for deriving PDAs (Program Derived Addresses) pub mod seeds { @@ -58,6 +57,15 @@ pub mod global_constants { is_writable: true, }; + pub const MAYHEM_FEE_RECIPIENT: Pubkey = + pubkey!("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"); + pub const MAYHEM_FEE_RECIPIENT_META: solana_sdk::instruction::AccountMeta = + solana_sdk::instruction::AccountMeta { + pubkey: FEE_RECIPIENT, + is_signer: false, + is_writable: true, + }; + /// Public key for the global PDA pub const GLOBAL_ACCOUNT: Pubkey = pubkey!("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf"); pub const GLOBAL_ACCOUNT_META: solana_sdk::instruction::AccountMeta = @@ -152,10 +160,6 @@ impl Symbol { pub const SOLANA: &'static str = "solana"; } -lazy_static::lazy_static! { - static ref ACCOUNT_CACHE: RwLock>> = RwLock::new(HashMap::new()); -} - #[inline] pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option { crate::common::fast_fn::get_cached_pda( diff --git a/src/instruction/utils/pumpswap.rs b/src/instruction/utils/pumpswap.rs index 4a377c6..31409df 100644 --- a/src/instruction/utils/pumpswap.rs +++ b/src/instruction/utils/pumpswap.rs @@ -65,6 +65,10 @@ pub mod accounts { pub const DEFAULT_COIN_CREATOR_VAULT_AUTHORITY: Pubkey = pubkey!("8N3GDaZ2iwN65oxVatKTLPNooAVUJTbfiVJ1ahyqwjSk"); + /// Mayhem fee recipient (for mayhem mode coins) + pub const MAYHEM_FEE_RECIPIENT: Pubkey = + pubkey!("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"); + // META pub const GLOBAL_ACCOUNT_META: solana_sdk::instruction::AccountMeta = @@ -81,6 +85,13 @@ pub mod accounts { is_writable: false, }; + pub const MAYHEM_FEE_RECIPIENT_META: solana_sdk::instruction::AccountMeta = + solana_sdk::instruction::AccountMeta { + pubkey: MAYHEM_FEE_RECIPIENT, + is_signer: false, + is_writable: false, + }; + pub const ASSOCIATED_TOKEN_PROGRAM_META: solana_sdk::instruction::AccountMeta = solana_sdk::instruction::AccountMeta { pubkey: ASSOCIATED_TOKEN_PROGRAM, diff --git a/src/instruction/utils/pumpswap_types.rs b/src/instruction/utils/pumpswap_types.rs index 250a75d..d1e91ae 100644 --- a/src/instruction/utils/pumpswap_types.rs +++ b/src/instruction/utils/pumpswap_types.rs @@ -14,9 +14,10 @@ pub struct Pool { pub pool_quote_token_account: Pubkey, pub lp_supply: u64, pub coin_creator: Pubkey, + pub is_mayhem_mode: bool, } -pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32; +pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1; pub fn pool_decode(data: &[u8]) -> Option { if data.len() < POOL_SIZE { diff --git a/src/trading/core/params.rs b/src/trading/core/params.rs index 61c74f2..c04a825 100755 --- a/src/trading/core/params.rs +++ b/src/trading/core/params.rs @@ -4,6 +4,8 @@ use crate::common::nonce_cache::DurableNonceInfo; use crate::common::spl_associated_token_account::get_associated_token_address_with_program_id; use crate::common::{GasFeeStrategy, SolanaRpcClient}; use crate::constants::TOKEN_PROGRAM; +use crate::instruction::utils::pumpfun::global_constants::MAYHEM_FEE_RECIPIENT; +use crate::instruction::utils::pumpswap::accounts::MAYHEM_FEE_RECIPIENT as MAYHEM_FEE_RECIPIENT_SWAP; use crate::swqos::{SwqosClient, TradeType}; use crate::trading::common::get_multi_token_balances; use crate::trading::MiddlewareManager; @@ -79,13 +81,16 @@ impl PumpFunParams { associated_bonding_curve: Pubkey, creator_vault: Pubkey, close_token_account_when_sell: Option, + fee_recipient: Pubkey, ) -> Self { + let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT; let bonding_curve_account = BondingCurveAccount::from_dev_trade( bonding_curve, &mint, token_amount, max_sol_cost, creator, + is_mayhem_mode, ); Self { bonding_curve: Arc::new(bonding_curve_account), @@ -106,7 +111,9 @@ impl PumpFunParams { real_token_reserves: u64, real_sol_reserves: u64, close_token_account_when_sell: Option, + fee_recipient: Pubkey, ) -> Self { + let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT; let bonding_curve = BondingCurveAccount::from_trade( bonding_curve, mint, @@ -115,6 +122,7 @@ impl PumpFunParams { virtual_sol_reserves, real_token_reserves, real_sol_reserves, + is_mayhem_mode, ); Self { bonding_curve: Arc::new(bonding_curve), @@ -140,6 +148,7 @@ impl PumpFunParams { token_total_supply: account.0.token_total_supply, complete: account.0.complete, creator: account.0.creator, + is_mayhem_mode: account.0.is_mayhem_mode, }; let associated_bonding_curve = get_associated_token_address_with_program_id( &bonding_curve.account, @@ -201,6 +210,8 @@ pub struct PumpSwapParams { pub base_token_program: Pubkey, /// Quote token program ID pub quote_token_program: Pubkey, + /// Whether the pool is in mayhem mode + pub is_mayhem_mode: bool, } impl PumpSwapParams { @@ -216,7 +227,9 @@ impl PumpSwapParams { coin_creator_vault_authority: Pubkey, base_token_program: Pubkey, quote_token_program: Pubkey, + fee_recipient: Pubkey, ) -> Self { + let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT_SWAP; Self { pool, base_mint, @@ -229,6 +242,7 @@ impl PumpSwapParams { coin_creator_vault_authority, base_token_program, quote_token_program, + is_mayhem_mode, } } @@ -295,6 +309,7 @@ impl PumpSwapParams { } else { crate::constants::TOKEN_PROGRAM_2022 }, + is_mayhem_mode: pool_data.is_mayhem_mode, }) } }