feat: Add caching system and performance optimizations
- Add fast_fn module with global caches for PDA, ATA, and instruction operations - Implement memory-efficient caches using CLRU and parking_lot for thread-safe access - Optimize compute budget instruction generation with caching - Replace std::sync::Mutex with parking_lot::Mutex for better performance - Add dedicated caching for PumpFun PDAs and associated token addresses - Improve transaction building with batch signature optimization - Add new dependencies: clru, smallvec, parking_lot for cache infrastructure - Remove unused utility functions in pumpfun module - Enhance error messages for tip fee configuration validation
This commit is contained in:
+50
-26
@@ -1,10 +1,10 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use solana_sdk::{instruction::Instruction, signer::Signer};
|
||||
use spl_associated_token_account::{
|
||||
get_associated_token_address, instruction::create_associated_token_account,
|
||||
use crate::{
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
trading::core::{
|
||||
params::{BuyParams, PumpFunParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
};
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
instruction::utils::pumpfun::{
|
||||
accounts, get_bonding_curve_pda, get_creator, get_user_volume_accumulator_pda,
|
||||
@@ -15,16 +15,10 @@ use crate::{
|
||||
pumpfun::{get_buy_token_amount_from_sol_amount, get_sell_sol_amount_from_token_amount},
|
||||
},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use solana_sdk::instruction::AccountMeta;
|
||||
|
||||
use crate::{
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
trading::core::{
|
||||
params::{BuyParams, PumpFunParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
};
|
||||
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer};
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
/// Instruction builder for PumpFun protocol
|
||||
pub struct PumpFunInstructionBuilder;
|
||||
@@ -63,7 +57,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
let mut instructions = Vec::with_capacity(2);
|
||||
|
||||
// Create associated token account
|
||||
instructions.push(create_associated_token_account(
|
||||
instructions.push(crate::common::fast_fn::create_associated_token_account_fast(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.mint,
|
||||
@@ -76,17 +70,30 @@ 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());
|
||||
|
||||
let bonding_curve = if bonding_curve.account == Pubkey::default() {
|
||||
get_bonding_curve_pda(¶ms.mint).unwrap()
|
||||
} else {
|
||||
bonding_curve.account
|
||||
};
|
||||
let associated_bonding_curve = if protocol_params.associated_bonding_curve
|
||||
== Pubkey::default()
|
||||
{
|
||||
crate::common::fast_fn::get_associated_token_address_fast(&bonding_curve, ¶ms.mint)
|
||||
} else {
|
||||
protocol_params.associated_bonding_curve
|
||||
};
|
||||
|
||||
let accounts: [AccountMeta; 16] = [
|
||||
global_constants::GLOBAL_ACCOUNT_META,
|
||||
global_constants::FEE_RECIPIENT_META,
|
||||
AccountMeta::new_readonly(params.mint, false),
|
||||
AccountMeta::new(bonding_curve.account, false),
|
||||
AccountMeta::new(bonding_curve, false),
|
||||
AccountMeta::new(associated_bonding_curve, false),
|
||||
AccountMeta::new(
|
||||
get_associated_token_address(&bonding_curve.account, ¶ms.mint),
|
||||
false,
|
||||
),
|
||||
AccountMeta::new(
|
||||
get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint),
|
||||
crate::common::fast_fn::get_associated_token_address_fast(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.mint,
|
||||
),
|
||||
false,
|
||||
),
|
||||
AccountMeta::new(params.payer.pubkey(), true),
|
||||
@@ -132,7 +139,10 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
} else {
|
||||
return Err(anyhow!("Amount token is required"));
|
||||
};
|
||||
let ata = get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint);
|
||||
let ata = crate::common::fast_fn::get_associated_token_address_fast(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.mint,
|
||||
);
|
||||
let creator_vault_pda = protocol_params.creator_vault;
|
||||
let creator = get_creator(&creator_vault_pda);
|
||||
|
||||
@@ -153,16 +163,30 @@ 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());
|
||||
|
||||
let bonding_curve = get_bonding_curve_pda(¶ms.mint).unwrap();
|
||||
let bonding_curve = if bonding_curve.account == Pubkey::default() {
|
||||
get_bonding_curve_pda(¶ms.mint).unwrap()
|
||||
} else {
|
||||
bonding_curve.account
|
||||
};
|
||||
let associated_bonding_curve = if protocol_params.associated_bonding_curve
|
||||
== Pubkey::default()
|
||||
{
|
||||
crate::common::fast_fn::get_associated_token_address_fast(&bonding_curve, ¶ms.mint)
|
||||
} else {
|
||||
protocol_params.associated_bonding_curve
|
||||
};
|
||||
|
||||
let accounts: [AccountMeta; 14] = [
|
||||
global_constants::GLOBAL_ACCOUNT_META,
|
||||
global_constants::FEE_RECIPIENT_META,
|
||||
AccountMeta::new_readonly(params.mint, false),
|
||||
AccountMeta::new(bonding_curve, false),
|
||||
AccountMeta::new(get_associated_token_address(&bonding_curve, ¶ms.mint), false),
|
||||
AccountMeta::new(associated_bonding_curve, false),
|
||||
AccountMeta::new(
|
||||
get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint),
|
||||
crate::common::fast_fn::get_associated_token_address_fast(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.mint,
|
||||
),
|
||||
false,
|
||||
),
|
||||
AccountMeta::new(params.payer.pubkey(), true),
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use crate::common::{global::GlobalAccount, SolanaRpcClient};
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use crate::{
|
||||
common::{bonding_curve::BondingCurveAccount, global::GlobalAccount, SolanaRpcClient},
|
||||
constants::{self, trade::trade::DEFAULT_SLIPPAGE},
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
@@ -10,12 +7,6 @@ use tokio::sync::RwLock;
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
/// Seed for the global state PDA
|
||||
pub const GLOBAL_SEED: &[u8] = b"global";
|
||||
|
||||
/// Seed for the mint authority PDA
|
||||
pub const MINT_AUTHORITY_SEED: &[u8] = b"mint-authority";
|
||||
|
||||
/// Seed for bonding curve PDAs
|
||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
||||
|
||||
@@ -115,9 +106,9 @@ pub mod accounts {
|
||||
pub const FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||
|
||||
pub const GLOBAL_VOLUME_ACCUMULATOR: Pubkey =
|
||||
pubkey!("Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y"); // get_global_volume_accumulator_pda().unwrap();
|
||||
pubkey!("Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y");
|
||||
|
||||
pub const FEE_CONFIG: Pubkey = pubkey!("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"); // get_fee_config_pda().unwrap();
|
||||
pub const FEE_CONFIG: Pubkey = pubkey!("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt");
|
||||
|
||||
// META
|
||||
pub const PUMPFUN_META: solana_sdk::instruction::AccountMeta =
|
||||
@@ -166,28 +157,17 @@ lazy_static::lazy_static! {
|
||||
static ref ACCOUNT_CACHE: RwLock<HashMap<Pubkey, Arc<GlobalAccount>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_pda() -> Pubkey {
|
||||
static GLOBAL_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(&[seeds::GLOBAL_SEED], &accounts::PUMPFUN).0
|
||||
});
|
||||
*GLOBAL_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mint_authority_pda() -> Pubkey {
|
||||
static MINT_AUTHORITY_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(&[seeds::MINT_AUTHORITY_SEED], &accounts::PUMPFUN).0
|
||||
});
|
||||
*MINT_AUTHORITY_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::BONDING_CURVE_SEED, mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
crate::common::fast_fn::get_cached_pda(
|
||||
crate::common::fast_fn::PdaCacheKey::PumpFunBondingCurve(*mint),
|
||||
|| {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::BONDING_CURVE_SEED, mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -208,60 +188,28 @@ pub fn get_creator(creator_vault_pda: &Pubkey) -> Pubkey {
|
||||
|
||||
#[inline]
|
||||
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::CREATOR_VAULT_SEED, creator.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
crate::common::fast_fn::get_cached_pda(
|
||||
crate::common::fast_fn::PdaCacheKey::PumpFunCreatorVault(*creator),
|
||||
|| {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::CREATOR_VAULT_SEED, creator.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 1] = &[seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_fee_config_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::FEE_CONFIG_SEED, accounts::PUMPFUN.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::FEE_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_metadata_pda(mint: &Pubkey) -> Pubkey {
|
||||
Pubkey::find_program_address(
|
||||
&[seeds::METADATA_SEED, accounts::MPL_TOKEN_METADATA.as_ref(), mint.as_ref()],
|
||||
&accounts::MPL_TOKEN_METADATA,
|
||||
crate::common::fast_fn::get_cached_pda(
|
||||
crate::common::fast_fn::PdaCacheKey::PumpFunUserVolume(*user),
|
||||
|| {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
},
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_global_account(/*rpc: &SolanaRpcClient*/
|
||||
) -> Result<Arc<GlobalAccount>, anyhow::Error> {
|
||||
let global_account = GlobalAccount::new();
|
||||
let global_account = Arc::new(global_account);
|
||||
Ok(global_account)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_initial_buy_price(
|
||||
global_account: &Arc<GlobalAccount>,
|
||||
amount_sol: u64,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let buy_amount = global_account.get_initial_buy_price(amount_sol);
|
||||
Ok(buy_amount)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -283,25 +231,6 @@ pub async fn fetch_bonding_curve_account(
|
||||
Ok((Arc::new(bonding_curve), bonding_curve_pda))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn init_bonding_curve_account(
|
||||
mint: &Pubkey,
|
||||
dev_buy_token: u64,
|
||||
dev_sol_cost: u64,
|
||||
creator: Pubkey,
|
||||
) -> Result<Arc<BondingCurveAccount>, anyhow::Error> {
|
||||
let bonding_curve =
|
||||
BondingCurveAccount::from_dev_trade(mint, dev_buy_token, dev_sol_cost, creator);
|
||||
let bonding_curve = Arc::new(bonding_curve);
|
||||
Ok(bonding_curve)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option<u64>) -> u64 {
|
||||
let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||
amount_sol + (amount_sol * slippage / 10000)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||
if amount == 0 {
|
||||
|
||||
@@ -30,10 +30,6 @@ pub mod seeds {
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
use crate::instruction::utils::pumpswap::{
|
||||
get_fee_config_pda, get_global_volume_accumulator_pda,
|
||||
};
|
||||
|
||||
/// Public key for the fee recipient
|
||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user