From ccf633026023ee28a463cb5396737c09375feb7e Mon Sep 17 00:00:00 2001 From: ysq Date: Mon, 8 Sep 2025 23:24:15 +0800 Subject: [PATCH] feat(raydium): optimize instruction builder with arrays and fast functions - Use fixed arrays instead of Vec for better performance - Extract wSOL handling to common functions - Apply fast variants of token account operations - Pre-allocate capacities and optimize data construction --- src/instruction/raydium_amm_v4.rs | 198 +++++++++++++----------------- 1 file changed, 84 insertions(+), 114 deletions(-) diff --git a/src/instruction/raydium_amm_v4.rs b/src/instruction/raydium_amm_v4.rs index 93414df..1defe73 100755 --- a/src/instruction/raydium_amm_v4.rs +++ b/src/instruction/raydium_amm_v4.rs @@ -1,9 +1,3 @@ -use anyhow::{anyhow, Result}; -use solana_sdk::{instruction::Instruction, signer::Signer}; -use solana_system_interface::instruction::transfer; -use spl_associated_token_account::instruction::create_associated_token_account_idempotent; -use spl_token::instruction::close_account; - use crate::{ constants::trade::trade::DEFAULT_SLIPPAGE, instruction::utils::raydium_amm_v4::{accounts, SWAP_BASE_IN_DISCRIMINATOR}, @@ -13,6 +7,11 @@ use crate::{ }, utils::calc::raydium_amm_v4::compute_swap_amount, }; +use anyhow::{anyhow, Result}; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + signer::Signer, +}; /// Instruction builder for RaydiumCpmm protocol pub struct RaydiumAmmV4InstructionBuilder; @@ -29,11 +28,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { .downcast_ref::() .ok_or_else(|| anyhow!("Invalid protocol params for RaydiumCpmm"))?; - let wsol_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - ); - let is_base_in = protocol_params.coin_mint == crate::constants::WSOL_TOKEN_ACCOUNT; let amount_in: u64 = params.sol_amount; @@ -46,91 +40,69 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { ); let minimum_amount_out = swap_result.min_amount_out; - let mut instructions = vec![]; + let mut instructions = Vec::with_capacity(6); if protocol_params.auto_handle_wsol { // Handle wSOL - instructions.push( - // Create wSOL ATA account if it doesn't exist - create_associated_token_account_idempotent( - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - &crate::constants::TOKEN_PROGRAM, - ), - ); - instructions.push( - // Transfer SOL to wSOL ATA account - transfer(¶ms.payer.pubkey(), &wsol_token_account, amount_in), - ); - - // Sync wSOL balance - instructions.push( - spl_token::instruction::sync_native( - &crate::constants::TOKEN_PROGRAM, - &wsol_token_account, - ) - .unwrap(), - ); + instructions + .extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), amount_in)); } - instructions.push(create_associated_token_account_idempotent( + instructions.push(crate::common::fast_fn::create_associated_token_account_idempotent_fast( ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), ¶ms.mint, &crate::constants::TOKEN_PROGRAM, )); - let user_source_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - ); + let user_source_token_account = + crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + ¶ms.payer.pubkey(), + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + ); let user_destination_token_account = - spl_associated_token_account::get_associated_token_address( + crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, ); // Create buy instruction - let accounts = vec![ + let accounts: [AccountMeta; 17] = [ crate::constants::TOKEN_PROGRAM_META, // Token Program (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Amm + AccountMeta::new(protocol_params.amm, false), // Amm accounts::AUTHORITY_META, // Authority (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Amm Open Orders - solana_sdk::instruction::AccountMeta::new(protocol_params.token_coin, false), // Pool Coin Token Account - solana_sdk::instruction::AccountMeta::new(protocol_params.token_pc, false), // Pool Pc Token Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Program - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Market - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Bids - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Asks - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Event Queue - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Coin Vault Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Pc Vault Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Vault Signer - solana_sdk::instruction::AccountMeta::new(user_source_token_account, false), // User Source Token Account - solana_sdk::instruction::AccountMeta::new(user_destination_token_account, false), // User Destination Token Account - solana_sdk::instruction::AccountMeta::new(params.payer.pubkey(), true), // User Source Owner + AccountMeta::new(protocol_params.amm, false), // Amm Open Orders + AccountMeta::new(protocol_params.token_coin, false), // Pool Coin Token Account + AccountMeta::new(protocol_params.token_pc, false), // Pool Pc Token Account + AccountMeta::new(protocol_params.amm, false), // Serum Program + AccountMeta::new(protocol_params.amm, false), // Serum Market + AccountMeta::new(protocol_params.amm, false), // Serum Bids + AccountMeta::new(protocol_params.amm, false), // Serum Asks + AccountMeta::new(protocol_params.amm, false), // Serum Event Queue + AccountMeta::new(protocol_params.amm, false), // Serum Coin Vault Account + AccountMeta::new(protocol_params.amm, false), // Serum Pc Vault Account + AccountMeta::new(protocol_params.amm, false), // Serum Vault Signer + AccountMeta::new(user_source_token_account, false), // User Source Token Account + AccountMeta::new(user_destination_token_account, false), // User Destination Token Account + AccountMeta::new(params.payer.pubkey(), true), // User Source Owner ]; // Create instruction data - let mut data = vec![]; - data.extend_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); - data.extend_from_slice(&amount_in.to_le_bytes()); - data.extend_from_slice(&minimum_amount_out.to_le_bytes()); + let mut data = [0u8; 17]; + data[..1].copy_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); + data[1..9].copy_from_slice(&amount_in.to_le_bytes()); + data[9..17].copy_from_slice(&minimum_amount_out.to_le_bytes()); - instructions.push(Instruction { program_id: accounts::RAYDIUM_AMM_V4, accounts, data }); + instructions.push(Instruction::new_with_bytes( + accounts::RAYDIUM_AMM_V4, + &data, + accounts.to_vec(), + )); if protocol_params.auto_handle_wsol { // Close wSOL ATA account, reclaim rent - instructions.push( - spl_token::instruction::close_account( - &crate::constants::TOKEN_PROGRAM, - &wsol_token_account, - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &[], - ) - .unwrap(), - ); + instructions.push(crate::trading::common::close_wsol(¶ms.payer.pubkey())); } Ok(instructions) @@ -147,11 +119,6 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { return Err(anyhow!("Token amount is not set")); } - let wsol_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - &crate::constants::WSOL_TOKEN_ACCOUNT, - ); - let is_base_in = protocol_params.pc_mint == crate::constants::WSOL_TOKEN_ACCOUNT; let swap_result = compute_swap_amount( protocol_params.coin_reserve, @@ -162,12 +129,12 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { ); let minimum_amount_out = swap_result.min_amount_out; - let mut instructions = vec![]; + let mut instructions = Vec::with_capacity(3); // Handle wSOL instructions.push( // Create wSOL ATA account if it doesn't exist - create_associated_token_account_idempotent( + crate::common::fast_fn::create_associated_token_account_idempotent_fast( ¶ms.payer.pubkey(), ¶ms.payer.pubkey(), &crate::constants::WSOL_TOKEN_ACCOUNT, @@ -175,55 +142,58 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder { ), ); - let user_source_token_account = spl_associated_token_account::get_associated_token_address( - ¶ms.payer.pubkey(), - ¶ms.mint, - ); + let user_source_token_account = + crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + ¶ms.payer.pubkey(), + ¶ms.mint, + &crate::constants::TOKEN_PROGRAM, + ); let user_destination_token_account = - spl_associated_token_account::get_associated_token_address( + crate::common::fast_fn::get_associated_token_address_with_program_id_fast( ¶ms.payer.pubkey(), &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, ); // Create buy instruction - let accounts = vec![ + let accounts: [AccountMeta; 17] = [ crate::constants::TOKEN_PROGRAM_META, // Token Program (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Amm + AccountMeta::new(protocol_params.amm, false), // Amm accounts::AUTHORITY_META, // Authority (readonly) - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Amm Open Orders - solana_sdk::instruction::AccountMeta::new(protocol_params.token_coin, false), // Pool Coin Token Account - solana_sdk::instruction::AccountMeta::new(protocol_params.token_pc, false), // Pool Pc Token Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Program - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Market - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Bids - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Asks - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Event Queue - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Coin Vault Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Pc Vault Account - solana_sdk::instruction::AccountMeta::new(protocol_params.amm, false), // Serum Vault Signer - solana_sdk::instruction::AccountMeta::new(user_source_token_account, false), // User Source Token Account - solana_sdk::instruction::AccountMeta::new(user_destination_token_account, false), // User Destination Token Account - solana_sdk::instruction::AccountMeta::new(params.payer.pubkey(), true), // User Source Owner + AccountMeta::new(protocol_params.amm, false), // Amm Open Orders + AccountMeta::new(protocol_params.token_coin, false), // Pool Coin Token Account + AccountMeta::new(protocol_params.token_pc, false), // Pool Pc Token Account + AccountMeta::new(protocol_params.amm, false), // Serum Program + AccountMeta::new(protocol_params.amm, false), // Serum Market + AccountMeta::new(protocol_params.amm, false), // Serum Bids + AccountMeta::new(protocol_params.amm, false), // Serum Asks + AccountMeta::new(protocol_params.amm, false), // Serum Event Queue + AccountMeta::new(protocol_params.amm, false), // Serum Coin Vault Account + AccountMeta::new(protocol_params.amm, false), // Serum Pc Vault Account + AccountMeta::new(protocol_params.amm, false), // Serum Vault Signer + AccountMeta::new(user_source_token_account, false), // User Source Token Account + AccountMeta::new(user_destination_token_account, false), // User Destination Token Account + AccountMeta::new(params.payer.pubkey(), true), // User Source Owner ]; // Create instruction data - let mut data = vec![]; - data.extend_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); - data.extend_from_slice(¶ms.token_amount.unwrap_or(0).to_le_bytes()); - data.extend_from_slice(&minimum_amount_out.to_le_bytes()); + let mut data = [0u8; 17]; + data[..1].copy_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); + data[1..9].copy_from_slice(¶ms.token_amount.unwrap_or(0).to_le_bytes()); + data[9..17].copy_from_slice(&minimum_amount_out.to_le_bytes()); - instructions.push(Instruction { program_id: accounts::RAYDIUM_AMM_V4, accounts, data }); + // let mut data = vec![]; + // data.extend_from_slice(&SWAP_BASE_IN_DISCRIMINATOR); + // data.extend_from_slice(&amount_in.to_le_bytes()); + // data.extend_from_slice(&minimum_amount_out.to_le_bytes()); + + instructions.push(Instruction::new_with_bytes( + accounts::RAYDIUM_AMM_V4, + &data, + accounts.to_vec(), + )); if protocol_params.auto_handle_wsol { - instructions.push( - close_account( - &crate::constants::TOKEN_PROGRAM, - &wsol_token_account, - ¶ms.payer.pubkey(), - ¶ms.payer.pubkey(), - &[¶ms.payer.pubkey()], - ) - .unwrap(), - ); + instructions.push(crate::trading::common::close_wsol(¶ms.payer.pubkey())); } Ok(instructions)