From 51253c7aaffd73cc6a3894b1757a837a82c257a2 Mon Sep 17 00:00:00 2001 From: ysq Date: Sun, 7 Sep 2025 19:50:27 +0800 Subject: [PATCH] refactor: simplify instruction builders and optimize transaction construction - Remove redundant helper methods in instruction builders (bonk, pumpfun, pumpswap, raydium) - Inline buy/sell instruction construction for better performance - Remove unnecessary Buy/Sell structs and helper functions in pumpfun - Consolidate instruction building logic to reduce code duplication - Fix pointer dereference issue in parallel execution - Improve code maintainability and reduce complexity --- src/instruction/bonk.rs | 20 +--- src/instruction/pumpfun.rs | 176 ++++++++++++------------------ src/instruction/pumpswap.rs | 110 +++++-------------- src/instruction/raydium_amm_v4.rs | 20 +--- src/instruction/raydium_cpmm.rs | 20 +--- src/trading/core/parallel.rs | 2 +- 6 files changed, 101 insertions(+), 247 deletions(-) diff --git a/src/instruction/bonk.rs b/src/instruction/bonk.rs index 39d3efc..9345f9f 100755 --- a/src/instruction/bonk.rs +++ b/src/instruction/bonk.rs @@ -31,20 +31,6 @@ impl InstructionBuilder for BonkInstructionBuilder { if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } - self.build_buy_instructions_with_accounts(params).await - } - - async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - self.build_sell_instructions_with_accounts(params).await - } -} - -impl BonkInstructionBuilder { - /// Build buy instructions with provided account information - async fn build_buy_instructions_with_accounts( - &self, - params: &BuyParams, - ) -> Result> { let protocol_params = params .protocol_params .as_any() @@ -179,11 +165,7 @@ impl BonkInstructionBuilder { Ok(instructions) } - /// Build sell instructions with provided account information - async fn build_sell_instructions_with_accounts( - &self, - params: &SellParams, - ) -> Result> { + async fn build_sell_instructions(&self, params: &SellParams) -> Result> { if params.rpc.is_none() { return Err(anyhow!("RPC is not set")); } diff --git a/src/instruction/pumpfun.rs b/src/instruction/pumpfun.rs index 1893749..8f1d59c 100755 --- a/src/instruction/pumpfun.rs +++ b/src/instruction/pumpfun.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use solana_sdk::instruction::Instruction; +use solana_sdk::{instruction::Instruction, signer::Signer}; use spl_associated_token_account::{ get_associated_token_address, instruction::create_associated_token_account, }; @@ -17,7 +17,7 @@ use crate::{ }, }; -use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signature::Keypair, signer::Signer}; +use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; use crate::{ constants::trade::trade::DEFAULT_SLIPPAGE, @@ -85,14 +85,43 @@ impl InstructionBuilder for PumpFunInstructionBuilder { &accounts::TOKEN_PROGRAM, )); + // Create buy instruction data + let mut buy_data = Vec::with_capacity(8 + 8 + 8); + buy_data.extend_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // discriminator + buy_data.extend_from_slice(&buy_token_amount.to_le_bytes()); + buy_data.extend_from_slice(&max_sol_cost.to_le_bytes()); + // Create buy instruction - instructions.push(buy( - params.payer.as_ref(), - ¶ms.mint, - &bonding_curve.account, - &creator_vault_pda, - &FEE_RECIPIENT, - Buy { _amount: buy_token_amount, _max_sol_cost: max_sol_cost }, + instructions.push(Instruction::new_with_bytes( + accounts::PUMPFUN, + &buy_data, + vec![ + AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false), + AccountMeta::new(FEE_RECIPIENT, false), + AccountMeta::new_readonly(params.mint, false), + AccountMeta::new(bonding_curve.account, false), + AccountMeta::new( + get_associated_token_address(&bonding_curve.account, ¶ms.mint), + false, + ), + AccountMeta::new( + get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint), + false, + ), + AccountMeta::new(params.payer.pubkey(), true), + AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), + AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), + AccountMeta::new(creator_vault_pda, false), + AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), + AccountMeta::new_readonly(accounts::PUMPFUN, false), + AccountMeta::new(get_global_volume_accumulator_pda().unwrap(), false), + AccountMeta::new( + get_user_volume_accumulator_pda(¶ms.payer.pubkey()).unwrap(), + false, + ), + AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false), + AccountMeta::new_readonly(accounts::FEE_PROGRAM, false), + ], )); Ok(instructions) @@ -137,12 +166,37 @@ impl InstructionBuilder for PumpFunInstructionBuilder { params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), ); - let mut instructions = vec![sell( - params.payer.as_ref(), - ¶ms.mint, - &creator_vault_pda, - &FEE_RECIPIENT, - Sell { _amount: token_amount, _min_sol_output: min_sol_output }, + // Create sell instruction data + let mut sell_data = Vec::with_capacity(8 + 8 + 8); + sell_data.extend_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // discriminator + sell_data.extend_from_slice(&token_amount.to_le_bytes()); + sell_data.extend_from_slice(&min_sol_output.to_le_bytes()); + + let bonding_curve = get_bonding_curve_pda(¶ms.mint).unwrap(); + + // Create sell instruction + let mut instructions = vec![Instruction::new_with_bytes( + accounts::PUMPFUN, + &sell_data, + vec![ + AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false), + AccountMeta::new(FEE_RECIPIENT, false), + AccountMeta::new_readonly(params.mint, false), + AccountMeta::new(bonding_curve, false), + AccountMeta::new(get_associated_token_address(&bonding_curve, ¶ms.mint), false), + AccountMeta::new( + get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint), + false, + ), + AccountMeta::new(params.payer.pubkey(), true), + AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), + AccountMeta::new(creator_vault_pda, false), + AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), + AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), + AccountMeta::new_readonly(accounts::PUMPFUN, false), + AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false), + AccountMeta::new_readonly(accounts::FEE_PROGRAM, false), + ], )]; // If selling all tokens, close the account @@ -159,95 +213,3 @@ impl InstructionBuilder for PumpFunInstructionBuilder { Ok(instructions) } } - -pub struct Buy { - pub _amount: u64, - pub _max_sol_cost: u64, -} - -impl Buy { - pub fn data(&self) -> Vec { - let mut data = Vec::with_capacity(8 + 8 + 8); - data.extend_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // discriminator - data.extend_from_slice(&self._amount.to_le_bytes()); - data.extend_from_slice(&self._max_sol_cost.to_le_bytes()); - data - } -} - -pub struct Sell { - pub _amount: u64, - pub _min_sol_output: u64, -} - -impl Sell { - pub fn data(&self) -> Vec { - let mut data = Vec::with_capacity(8 + 8 + 8); - data.extend_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // discriminator - data.extend_from_slice(&self._amount.to_le_bytes()); - data.extend_from_slice(&self._min_sol_output.to_le_bytes()); - data - } -} - -pub fn buy( - payer: &Keypair, - mint: &Pubkey, - bonding_curve_pda: &Pubkey, - creator_vault_pda: &Pubkey, - fee_recipient: &Pubkey, - args: Buy, -) -> Instruction { - Instruction::new_with_bytes( - accounts::PUMPFUN, - &args.data(), - vec![ - AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false), - AccountMeta::new(*fee_recipient, false), - AccountMeta::new_readonly(*mint, false), - AccountMeta::new(*bonding_curve_pda, false), - AccountMeta::new(get_associated_token_address(bonding_curve_pda, mint), false), - AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), - AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), - AccountMeta::new(*creator_vault_pda, false), - AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), - AccountMeta::new_readonly(accounts::PUMPFUN, false), - AccountMeta::new(get_global_volume_accumulator_pda().unwrap(), false), - AccountMeta::new(get_user_volume_accumulator_pda(&payer.pubkey()).unwrap(), false), - AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false), - AccountMeta::new_readonly(accounts::FEE_PROGRAM, false), - ], - ) -} - -pub fn sell( - payer: &Keypair, - mint: &Pubkey, - creator_vault_pda: &Pubkey, - fee_recipient: &Pubkey, - args: Sell, -) -> Instruction { - let bonding_curve: Pubkey = get_bonding_curve_pda(mint).unwrap(); - Instruction::new_with_bytes( - accounts::PUMPFUN, - &args.data(), - vec![ - AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false), - AccountMeta::new(*fee_recipient, false), - AccountMeta::new_readonly(*mint, false), - AccountMeta::new(bonding_curve, false), - AccountMeta::new(get_associated_token_address(&bonding_curve, mint), false), - AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false), - AccountMeta::new(payer.pubkey(), true), - AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false), - AccountMeta::new(*creator_vault_pda, false), - AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false), - AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false), - AccountMeta::new_readonly(accounts::PUMPFUN, false), - AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false), - AccountMeta::new_readonly(accounts::FEE_PROGRAM, false), - ], - ) -} diff --git a/src/instruction/pumpswap.rs b/src/instruction/pumpswap.rs index 13136ff..3431cad 100755 --- a/src/instruction/pumpswap.rs +++ b/src/instruction/pumpswap.rs @@ -35,78 +35,18 @@ impl InstructionBuilder for PumpSwapInstructionBuilder { return Err(anyhow!("Amount cannot be zero")); } - // Build instructions based on whether account information is provided + // Build instructions based on account information + let pool = protocol_params.pool; let base_mint = protocol_params.base_mint; let quote_mint = protocol_params.quote_mint; let pool_base_token_reserves = protocol_params.pool_base_token_reserves; let pool_quote_token_reserves = protocol_params.pool_quote_token_reserves; - let coin_creator_vault_ata = protocol_params.coin_creator_vault_ata; - let coin_creator_vault_authority = protocol_params.coin_creator_vault_authority; + let params_coin_creator_vault_ata = protocol_params.coin_creator_vault_ata; + let params_coin_creator_vault_authority = protocol_params.coin_creator_vault_authority; + let auto_handle_wsol = protocol_params.auto_handle_wsol; + let base_token_program = protocol_params.base_token_program; + let quote_token_program = protocol_params.quote_token_program; - self.build_buy_instructions_with_accounts( - params, - protocol_params.pool, - base_mint, - quote_mint, - pool_base_token_reserves, - pool_quote_token_reserves, - coin_creator_vault_ata, - coin_creator_vault_authority, - protocol_params.auto_handle_wsol, - protocol_params.base_token_program, - protocol_params.quote_token_program, - ) - .await - } - - async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - // Get PumpSwap specific parameters - let protocol_params = params - .protocol_params - .as_any() - .downcast_ref::() - .ok_or_else(|| anyhow!("Invalid protocol params for PumpSwap"))?; - // Build instructions based on whether account information is provided - let base_mint = protocol_params.base_mint; - let quote_mint = protocol_params.quote_mint; - let pool_base_token_reserves = protocol_params.pool_base_token_reserves; - let pool_quote_token_reserves = protocol_params.pool_quote_token_reserves; - let coin_creator_vault_ata = protocol_params.coin_creator_vault_ata; - let coin_creator_vault_authority = protocol_params.coin_creator_vault_authority; - - self.build_sell_instructions_with_accounts( - params, - protocol_params.pool, - base_mint, - quote_mint, - pool_base_token_reserves, - pool_quote_token_reserves, - coin_creator_vault_ata, - coin_creator_vault_authority, - protocol_params.auto_handle_wsol, - protocol_params.base_token_program, - protocol_params.quote_token_program, - ) - .await - } -} - -impl PumpSwapInstructionBuilder { - /// Build buy instructions with provided account information - async fn build_buy_instructions_with_accounts( - &self, - params: &BuyParams, - pool: Pubkey, - base_mint: Pubkey, - quote_mint: Pubkey, - pool_base_token_reserves: u64, - pool_quote_token_reserves: u64, - params_coin_creator_vault_ata: Pubkey, - params_coin_creator_vault_authority: Pubkey, - auto_handle_wsol: bool, - base_token_program: Pubkey, - quote_token_program: Pubkey, - ) -> Result> { if base_mint != accounts::WSOL_TOKEN_ACCOUNT && quote_mint != accounts::WSOL_TOKEN_ACCOUNT { return Err(anyhow!("Invalid base mint and quote mint")); } @@ -310,21 +250,26 @@ impl PumpSwapInstructionBuilder { Ok(instructions) } - /// Build sell instructions with provided account information - async fn build_sell_instructions_with_accounts( - &self, - params: &SellParams, - pool: Pubkey, - base_mint: Pubkey, - quote_mint: Pubkey, - pool_base_token_reserves: u64, - pool_quote_token_reserves: u64, - params_coin_creator_vault_ata: Pubkey, - params_coin_creator_vault_authority: Pubkey, - auto_handle_wsol: bool, - base_token_program: Pubkey, - quote_token_program: Pubkey, - ) -> Result> { + async fn build_sell_instructions(&self, params: &SellParams) -> Result> { + // Get PumpSwap specific parameters + let protocol_params = params + .protocol_params + .as_any() + .downcast_ref::() + .ok_or_else(|| anyhow!("Invalid protocol params for PumpSwap"))?; + + // Build instructions based on account information + let pool = protocol_params.pool; + let base_mint = protocol_params.base_mint; + let quote_mint = protocol_params.quote_mint; + let pool_base_token_reserves = protocol_params.pool_base_token_reserves; + let pool_quote_token_reserves = protocol_params.pool_quote_token_reserves; + let params_coin_creator_vault_ata = protocol_params.coin_creator_vault_ata; + let params_coin_creator_vault_authority = protocol_params.coin_creator_vault_authority; + let auto_handle_wsol = protocol_params.auto_handle_wsol; + let base_token_program = protocol_params.base_token_program; + let quote_token_program = protocol_params.quote_token_program; + if base_mint != accounts::WSOL_TOKEN_ACCOUNT && quote_mint != accounts::WSOL_TOKEN_ACCOUNT { return Err(anyhow!("Invalid base mint and quote mint")); } @@ -503,3 +448,4 @@ impl PumpSwapInstructionBuilder { Ok(instructions) } } + diff --git a/src/instruction/raydium_amm_v4.rs b/src/instruction/raydium_amm_v4.rs index 265c59b..3c000eb 100755 --- a/src/instruction/raydium_amm_v4.rs +++ b/src/instruction/raydium_amm_v4.rs @@ -23,20 +23,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } - self.build_buy_instructions_with_accounts(params).await - } - - async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - self.build_sell_instructions_with_accounts(params).await - } -} - -impl RaydiumAmmV4InstructionBuilder { - /// Build buy instructions with provided account information - async fn build_buy_instructions_with_accounts( - &self, - params: &BuyParams, - ) -> Result> { let protocol_params = params .protocol_params .as_any() @@ -147,11 +133,7 @@ impl RaydiumAmmV4InstructionBuilder { Ok(instructions) } - /// Build sell instructions with provided account information - async fn build_sell_instructions_with_accounts( - &self, - params: &SellParams, - ) -> Result> { + async fn build_sell_instructions(&self, params: &SellParams) -> Result> { let protocol_params = params .protocol_params .as_any() diff --git a/src/instruction/raydium_cpmm.rs b/src/instruction/raydium_cpmm.rs index 39b1fce..b07361b 100755 --- a/src/instruction/raydium_cpmm.rs +++ b/src/instruction/raydium_cpmm.rs @@ -26,20 +26,6 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder { if params.sol_amount == 0 { return Err(anyhow!("Amount cannot be zero")); } - self.build_buy_instructions_with_accounts(params).await - } - - async fn build_sell_instructions(&self, params: &SellParams) -> Result> { - self.build_sell_instructions_with_accounts(params).await - } -} - -impl RaydiumCpmmInstructionBuilder { - /// Build buy instructions with provided account information - async fn build_buy_instructions_with_accounts( - &self, - params: &BuyParams, - ) -> Result> { let protocol_params = params .protocol_params .as_any() @@ -160,11 +146,7 @@ impl RaydiumCpmmInstructionBuilder { Ok(instructions) } - /// Build sell instructions with provided account information - async fn build_sell_instructions_with_accounts( - &self, - params: &SellParams, - ) -> Result> { + async fn build_sell_instructions(&self, params: &SellParams) -> Result> { let protocol_params = params .protocol_params .as_any() diff --git a/src/trading/core/parallel.rs b/src/trading/core/parallel.rs index 12e4e05..886ec3a 100755 --- a/src/trading/core/parallel.rs +++ b/src/trading/core/parallel.rs @@ -69,7 +69,7 @@ pub async fn parallel_execute_with_tips( let transaction = build_transaction( payer, &priority_fee, - (*instructions).clone(), + instructions.as_ref().clone(), lookup_table_key, recent_blockhash, data_size_limit,