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:
@@ -92,3 +92,6 @@ indicatif = "0.18.0"
|
|||||||
solana-system-interface = { version = "1.0.0", features = ["bincode"] }
|
solana-system-interface = { version = "1.0.0", features = ["bincode"] }
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
|
clru = "0.6"
|
||||||
|
smallvec = "1.15.1"
|
||||||
|
parking_lot = "0.12"
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
use clru::CLruCache;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use solana_sdk::{
|
||||||
|
instruction::{AccountMeta, Instruction},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
use spl_associated_token_account::{
|
||||||
|
get_associated_token_address, ID as ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
|
const MAX_PDA_CACHE_SIZE: usize = 10000;
|
||||||
|
const MAX_ATA_CACHE_SIZE: usize = 10000;
|
||||||
|
const MAX_INSTRUCTION_CACHE_SIZE: usize = 10000;
|
||||||
|
|
||||||
|
// --------------------- Instruction Cache ---------------------
|
||||||
|
|
||||||
|
/// 指令缓存键,用于唯一标识指令类型和参数
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum InstructionCacheKey {
|
||||||
|
/// Associated Token Account 创建指令
|
||||||
|
CreateAssociatedTokenAccount {
|
||||||
|
payer: Pubkey,
|
||||||
|
owner: Pubkey,
|
||||||
|
mint: Pubkey,
|
||||||
|
token_program: Pubkey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全局指令缓存,用于存储常用指令
|
||||||
|
static INSTRUCTION_CACHE: Lazy<RwLock<CLruCache<InstructionCacheKey, Instruction>>> =
|
||||||
|
Lazy::new(|| {
|
||||||
|
RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_INSTRUCTION_CACHE_SIZE).unwrap()))
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 获取缓存的指令,如果不存在则计算并缓存
|
||||||
|
pub fn get_cached_instruction<F>(cache_key: InstructionCacheKey, compute_fn: F) -> Instruction
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Instruction,
|
||||||
|
{
|
||||||
|
// 尝试从缓存中获取(使用读锁)
|
||||||
|
{
|
||||||
|
let cache = INSTRUCTION_CACHE.read();
|
||||||
|
if let Some(cached_instruction) = cache.peek(&cache_key) {
|
||||||
|
return cached_instruction.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存未命中,计算新的指令
|
||||||
|
let instruction = compute_fn();
|
||||||
|
|
||||||
|
// 将计算结果存入缓存(使用写锁)
|
||||||
|
{
|
||||||
|
let mut cache = INSTRUCTION_CACHE.write();
|
||||||
|
cache.put(cache_key, instruction.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- Associated Token Account ---------------------
|
||||||
|
|
||||||
|
pub fn create_associated_token_account_fast(
|
||||||
|
payer: &Pubkey,
|
||||||
|
owner: &Pubkey,
|
||||||
|
mint: &Pubkey,
|
||||||
|
token_program: &Pubkey,
|
||||||
|
) -> Instruction {
|
||||||
|
// 创建缓存键
|
||||||
|
let cache_key = InstructionCacheKey::CreateAssociatedTokenAccount {
|
||||||
|
payer: *payer,
|
||||||
|
owner: *owner,
|
||||||
|
mint: *mint,
|
||||||
|
token_program: *token_program,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用缓存获取指令
|
||||||
|
get_cached_instruction(cache_key, || {
|
||||||
|
// 使用缓存的方式获取 Associated Token Address
|
||||||
|
let associated_token_address = get_associated_token_address_fast(owner, mint);
|
||||||
|
|
||||||
|
// 创建 Associated Token Account 指令
|
||||||
|
// 参考 spl_associated_token_account::instruction::create_associated_token_account 的实现
|
||||||
|
Instruction {
|
||||||
|
program_id: ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
accounts: vec![
|
||||||
|
AccountMeta::new(*payer, true), // 支付者(签名者,可写)
|
||||||
|
AccountMeta::new(associated_token_address, false), // ATA地址(可写,非签名者)
|
||||||
|
AccountMeta::new_readonly(*owner, false), // Token账户拥有者(只读,非签名者)
|
||||||
|
AccountMeta::new_readonly(*mint, false), // Token mint地址(只读,非签名者)
|
||||||
|
crate::constants::SYSTEM_PROGRAM_META,
|
||||||
|
AccountMeta::new_readonly(*token_program, false), // Token程序(只读,非签名者)
|
||||||
|
],
|
||||||
|
data: vec![], // ATA创建指令不需要额外数据
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- PDA ---------------------
|
||||||
|
|
||||||
|
/// PDA 缓存键,用于唯一标识 PDA 计算的输入参数
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PdaCacheKey {
|
||||||
|
PumpFunUserVolume(Pubkey),
|
||||||
|
PumpFunBondingCurve(Pubkey),
|
||||||
|
PumpFunCreatorVault(Pubkey),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全局 PDA 缓存,用于存储计算结果
|
||||||
|
static PDA_CACHE: Lazy<RwLock<CLruCache<PdaCacheKey, Pubkey>>> =
|
||||||
|
Lazy::new(|| RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_PDA_CACHE_SIZE).unwrap())));
|
||||||
|
|
||||||
|
/// 获取缓存的 PDA,如果不存在则计算并缓存
|
||||||
|
pub fn get_cached_pda<F>(cache_key: PdaCacheKey, compute_fn: F) -> Option<Pubkey>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Option<Pubkey>,
|
||||||
|
{
|
||||||
|
// 尝试从缓存中获取(使用读锁)
|
||||||
|
{
|
||||||
|
let cache = PDA_CACHE.read();
|
||||||
|
if let Some(cached_pda) = cache.peek(&cache_key) {
|
||||||
|
return Some(*cached_pda);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存未命中,计算新的 PDA
|
||||||
|
let pda_result = compute_fn();
|
||||||
|
|
||||||
|
// 如果计算成功,将结果存入缓存(使用写锁)
|
||||||
|
if let Some(pda) = pda_result {
|
||||||
|
let mut cache = PDA_CACHE.write();
|
||||||
|
cache.put(cache_key, pda);
|
||||||
|
}
|
||||||
|
|
||||||
|
pda_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- ATA ---------------------
|
||||||
|
|
||||||
|
/// ATA 缓存键,用于 Associated Token Address 缓存
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
struct AtaCacheKey {
|
||||||
|
wallet_address: Pubkey,
|
||||||
|
token_mint_address: Pubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全局 ATA 缓存,用于存储 Associated Token Address 计算结果
|
||||||
|
static ATA_CACHE: Lazy<RwLock<CLruCache<AtaCacheKey, Pubkey>>> =
|
||||||
|
Lazy::new(|| RwLock::new(CLruCache::new(NonZeroUsize::new(MAX_ATA_CACHE_SIZE).unwrap())));
|
||||||
|
|
||||||
|
/// 获取缓存的 Associated Token Address,如果不存在则计算并缓存
|
||||||
|
pub fn get_associated_token_address_fast(
|
||||||
|
wallet_address: &Pubkey,
|
||||||
|
token_mint_address: &Pubkey,
|
||||||
|
) -> Pubkey {
|
||||||
|
let cache_key =
|
||||||
|
AtaCacheKey { wallet_address: *wallet_address, token_mint_address: *token_mint_address };
|
||||||
|
|
||||||
|
// 尝试从缓存中获取(使用读锁)
|
||||||
|
{
|
||||||
|
let cache = ATA_CACHE.read();
|
||||||
|
if let Some(cached_ata) = cache.peek(&cache_key) {
|
||||||
|
return *cached_ata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存未命中,计算新的 ATA
|
||||||
|
let ata = get_associated_token_address(wallet_address, token_mint_address);
|
||||||
|
|
||||||
|
// 将计算结果存入缓存(使用写锁)
|
||||||
|
{
|
||||||
|
let mut cache = ATA_CACHE.write();
|
||||||
|
cache.put(cache_key, ata);
|
||||||
|
}
|
||||||
|
|
||||||
|
ata
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------- 初始化账号 ---------------------
|
||||||
|
|
||||||
|
pub fn fast_init(payer: &Pubkey) {
|
||||||
|
crate::instruction::utils::pumpfun::get_user_volume_accumulator_pda(payer);
|
||||||
|
}
|
||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
// pub mod address_lookup;
|
|
||||||
pub mod nonce_cache;
|
|
||||||
pub mod types;
|
|
||||||
pub mod address_lookup_cache;
|
pub mod address_lookup_cache;
|
||||||
pub mod subscription_handle;
|
|
||||||
pub mod bonding_curve;
|
pub mod bonding_curve;
|
||||||
|
pub mod fast_fn;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
|
pub mod nonce_cache;
|
||||||
|
pub mod subscription_handle;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use parking_lot::Mutex;
|
||||||
use solana_hash::Hash;
|
use solana_hash::Hash;
|
||||||
use solana_sdk::account_utils::StateMut;
|
use solana_sdk::account_utils::StateMut;
|
||||||
use solana_sdk::nonce::state::Versions;
|
use solana_sdk::nonce::state::Versions;
|
||||||
@@ -5,7 +6,7 @@ use solana_sdk::nonce::State;
|
|||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_streamer_sdk::common::SolanaRpcClient;
|
use solana_streamer_sdk::common::SolanaRpcClient;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
/// NonceInfo structure to store nonce-related information
|
/// NonceInfo structure to store nonce-related information
|
||||||
@@ -54,7 +55,7 @@ impl NonceCache {
|
|||||||
|
|
||||||
/// Get a copy of NonceInfo
|
/// Get a copy of NonceInfo
|
||||||
pub fn get_nonce_info(&self) -> NonceInfo {
|
pub fn get_nonce_info(&self) -> NonceInfo {
|
||||||
let nonce_info = self.nonce_info.lock().unwrap();
|
let nonce_info = self.nonce_info.lock();
|
||||||
NonceInfo {
|
NonceInfo {
|
||||||
nonce_account: nonce_info.nonce_account,
|
nonce_account: nonce_info.nonce_account,
|
||||||
current_nonce: nonce_info.current_nonce,
|
current_nonce: nonce_info.current_nonce,
|
||||||
@@ -71,7 +72,7 @@ impl NonceCache {
|
|||||||
next_buy_time: Option<i64>,
|
next_buy_time: Option<i64>,
|
||||||
used: Option<bool>,
|
used: Option<bool>,
|
||||||
) {
|
) {
|
||||||
let mut current = self.nonce_info.lock().unwrap();
|
let mut current = self.nonce_info.lock();
|
||||||
|
|
||||||
// Only update the passed fields
|
// Only update the passed fields
|
||||||
if let Some(account) = nonce_account {
|
if let Some(account) = nonce_account {
|
||||||
|
|||||||
+50
-26
@@ -1,10 +1,10 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use crate::{
|
||||||
use solana_sdk::{instruction::Instruction, signer::Signer};
|
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||||
use spl_associated_token_account::{
|
trading::core::{
|
||||||
get_associated_token_address, instruction::create_associated_token_account,
|
params::{BuyParams, PumpFunParams, SellParams},
|
||||||
|
traits::InstructionBuilder,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use spl_token::instruction::close_account;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
instruction::utils::pumpfun::{
|
instruction::utils::pumpfun::{
|
||||||
accounts, get_bonding_curve_pda, get_creator, get_user_volume_accumulator_pda,
|
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},
|
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 solana_sdk::instruction::AccountMeta;
|
||||||
|
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer};
|
||||||
use crate::{
|
use spl_token::instruction::close_account;
|
||||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
|
||||||
trading::core::{
|
|
||||||
params::{BuyParams, PumpFunParams, SellParams},
|
|
||||||
traits::InstructionBuilder,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Instruction builder for PumpFun protocol
|
/// Instruction builder for PumpFun protocol
|
||||||
pub struct PumpFunInstructionBuilder;
|
pub struct PumpFunInstructionBuilder;
|
||||||
@@ -63,7 +57,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
let mut instructions = Vec::with_capacity(2);
|
let mut instructions = Vec::with_capacity(2);
|
||||||
|
|
||||||
// Create associated token account
|
// 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.payer.pubkey(),
|
¶ms.payer.pubkey(),
|
||||||
¶ms.mint,
|
¶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[8..16].copy_from_slice(&buy_token_amount.to_le_bytes());
|
||||||
buy_data[16..24].copy_from_slice(&max_sol_cost.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] = [
|
let accounts: [AccountMeta; 16] = [
|
||||||
global_constants::GLOBAL_ACCOUNT_META,
|
global_constants::GLOBAL_ACCOUNT_META,
|
||||||
global_constants::FEE_RECIPIENT_META,
|
global_constants::FEE_RECIPIENT_META,
|
||||||
AccountMeta::new_readonly(params.mint, false),
|
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(
|
AccountMeta::new(
|
||||||
get_associated_token_address(&bonding_curve.account, ¶ms.mint),
|
crate::common::fast_fn::get_associated_token_address_fast(
|
||||||
false,
|
¶ms.payer.pubkey(),
|
||||||
),
|
¶ms.mint,
|
||||||
AccountMeta::new(
|
),
|
||||||
get_associated_token_address(¶ms.payer.pubkey(), ¶ms.mint),
|
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
AccountMeta::new(params.payer.pubkey(), true),
|
AccountMeta::new(params.payer.pubkey(), true),
|
||||||
@@ -132,7 +139,10 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
|||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Amount token is required"));
|
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_vault_pda = protocol_params.creator_vault;
|
||||||
let creator = get_creator(&creator_vault_pda);
|
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[8..16].copy_from_slice(&token_amount.to_le_bytes());
|
||||||
sell_data[16..24].copy_from_slice(&min_sol_output.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] = [
|
let accounts: [AccountMeta; 14] = [
|
||||||
global_constants::GLOBAL_ACCOUNT_META,
|
global_constants::GLOBAL_ACCOUNT_META,
|
||||||
global_constants::FEE_RECIPIENT_META,
|
global_constants::FEE_RECIPIENT_META,
|
||||||
AccountMeta::new_readonly(params.mint, false),
|
AccountMeta::new_readonly(params.mint, false),
|
||||||
AccountMeta::new(bonding_curve, 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(
|
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,
|
false,
|
||||||
),
|
),
|
||||||
AccountMeta::new(params.payer.pubkey(), true),
|
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::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 anyhow::anyhow;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
@@ -10,12 +7,6 @@ use tokio::sync::RwLock;
|
|||||||
|
|
||||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||||
pub mod seeds {
|
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
|
/// Seed for bonding curve PDAs
|
||||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
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 FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||||
|
|
||||||
pub const GLOBAL_VOLUME_ACCUMULATOR: Pubkey =
|
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
|
// META
|
||||||
pub const PUMPFUN_META: solana_sdk::instruction::AccountMeta =
|
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());
|
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]
|
#[inline]
|
||||||
pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option<Pubkey> {
|
pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option<Pubkey> {
|
||||||
let seeds: &[&[u8]; 2] = &[seeds::BONDING_CURVE_SEED, mint.as_ref()];
|
crate::common::fast_fn::get_cached_pda(
|
||||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
crate::common::fast_fn::PdaCacheKey::PumpFunBondingCurve(*mint),
|
||||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
|| {
|
||||||
pda.map(|pubkey| pubkey.0)
|
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]
|
#[inline]
|
||||||
@@ -208,60 +188,28 @@ pub fn get_creator(creator_vault_pda: &Pubkey) -> Pubkey {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||||
let seeds: &[&[u8]; 2] = &[seeds::CREATOR_VAULT_SEED, creator.as_ref()];
|
crate::common::fast_fn::get_cached_pda(
|
||||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
crate::common::fast_fn::PdaCacheKey::PumpFunCreatorVault(*creator),
|
||||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
|| {
|
||||||
pda.map(|pubkey| pubkey.0)
|
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]
|
#[inline]
|
||||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||||
let seeds: &[&[u8]; 2] = &[seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
crate::common::fast_fn::get_cached_pda(
|
||||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
crate::common::fast_fn::PdaCacheKey::PumpFunUserVolume(*user),
|
||||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
|| {
|
||||||
pda.map(|pubkey| pubkey.0)
|
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);
|
||||||
#[inline]
|
pda.map(|pubkey| pubkey.0)
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
.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]
|
#[inline]
|
||||||
@@ -283,25 +231,6 @@ pub async fn fetch_bonding_curve_account(
|
|||||||
Ok((Arc::new(bonding_curve), bonding_curve_pda))
|
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]
|
#[inline]
|
||||||
pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
|
|||||||
@@ -30,10 +30,6 @@ pub mod seeds {
|
|||||||
pub mod accounts {
|
pub mod accounts {
|
||||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
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
|
/// Public key for the fee recipient
|
||||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -5,6 +5,7 @@ pub mod protos;
|
|||||||
pub mod swqos;
|
pub mod swqos;
|
||||||
pub mod trading;
|
pub mod trading;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
use solana_sdk::signer::Signer;
|
||||||
pub use solana_streamer_sdk;
|
pub use solana_streamer_sdk;
|
||||||
|
|
||||||
use crate::constants::trade::trade::DEFAULT_SLIPPAGE;
|
use crate::constants::trade::trade::DEFAULT_SLIPPAGE;
|
||||||
@@ -24,8 +25,8 @@ use common::{PriorityFee, SolanaRpcClient, TradeConfig};
|
|||||||
use rustls::crypto::{ring::default_provider, CryptoProvider};
|
use rustls::crypto::{ring::default_provider, CryptoProvider};
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
|
||||||
use swqos::SwqosClient;
|
use swqos::SwqosClient;
|
||||||
|
|
||||||
pub struct SolanaTrade {
|
pub struct SolanaTrade {
|
||||||
@@ -57,6 +58,8 @@ impl Clone for SolanaTrade {
|
|||||||
impl SolanaTrade {
|
impl SolanaTrade {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn new(payer: Arc<Keypair>, trade_config: TradeConfig) -> Self {
|
pub async fn new(payer: Arc<Keypair>, trade_config: TradeConfig) -> Self {
|
||||||
|
crate::common::fast_fn::fast_init(&payer.try_pubkey().unwrap());
|
||||||
|
|
||||||
if CryptoProvider::get_default().is_none() {
|
if CryptoProvider::get_default().is_none() {
|
||||||
let _ = default_provider()
|
let _ = default_provider()
|
||||||
.install_default()
|
.install_default()
|
||||||
@@ -93,7 +96,7 @@ impl SolanaTrade {
|
|||||||
middleware_manager: None,
|
middleware_manager: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut current = INSTANCE.lock().unwrap();
|
let mut current = INSTANCE.lock();
|
||||||
*current = Some(Arc::new(instance.clone()));
|
*current = Some(Arc::new(instance.clone()));
|
||||||
|
|
||||||
instance
|
instance
|
||||||
@@ -111,7 +114,7 @@ impl SolanaTrade {
|
|||||||
|
|
||||||
/// Get the current instance
|
/// Get the current instance
|
||||||
pub fn get_instance() -> Arc<Self> {
|
pub fn get_instance() -> Arc<Self> {
|
||||||
let instance = INSTANCE.lock().unwrap();
|
let instance = INSTANCE.lock();
|
||||||
instance
|
instance
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("PumpFun instance not initialized. Please call new() first.")
|
.expect("PumpFun instance not initialized. Please call new() first.")
|
||||||
|
|||||||
@@ -1,28 +1,59 @@
|
|||||||
|
use crate::common::PriorityFee;
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruction};
|
use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruction};
|
||||||
|
|
||||||
use crate::common::PriorityFee;
|
/// 缓存键,包含计算预算指令的所有参数
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct ComputeBudgetCacheKey {
|
||||||
|
data_size_limit: u32,
|
||||||
|
unit_price: u64,
|
||||||
|
unit_limit: u32,
|
||||||
|
is_buy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// 为交易添加计算预算指令
|
/// 全局缓存,存储计算预算指令
|
||||||
pub fn add_compute_budget_instructions(
|
/// 使用 DashMap 提供高性能的无锁并发访问
|
||||||
instructions: &mut Vec<Instruction>,
|
static COMPUTE_BUDGET_CACHE: Lazy<DashMap<ComputeBudgetCacheKey, SmallVec<[Instruction; 3]>>> =
|
||||||
|
Lazy::new(|| DashMap::new());
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn compute_budget_instructions(
|
||||||
priority_fee: &PriorityFee,
|
priority_fee: &PriorityFee,
|
||||||
data_size_limit: u32,
|
data_size_limit: u32,
|
||||||
is_rpc: bool,
|
is_rpc: bool,
|
||||||
is_buy: bool,
|
is_buy: bool,
|
||||||
) {
|
) -> SmallVec<[Instruction; 3]> {
|
||||||
if is_buy {
|
let (unit_price, unit_limit) = if is_rpc {
|
||||||
instructions
|
(priority_fee.rpc_unit_price, priority_fee.rpc_unit_limit)
|
||||||
.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
|
||||||
}
|
|
||||||
if is_rpc {
|
|
||||||
instructions
|
|
||||||
.push(ComputeBudgetInstruction::set_compute_unit_price(priority_fee.rpc_unit_price));
|
|
||||||
instructions
|
|
||||||
.push(ComputeBudgetInstruction::set_compute_unit_limit(priority_fee.rpc_unit_limit));
|
|
||||||
} else {
|
} else {
|
||||||
instructions
|
(priority_fee.tip_unit_price, priority_fee.tip_unit_limit)
|
||||||
.push(ComputeBudgetInstruction::set_compute_unit_price(priority_fee.tip_unit_price));
|
};
|
||||||
instructions
|
|
||||||
.push(ComputeBudgetInstruction::set_compute_unit_limit(priority_fee.tip_unit_limit));
|
// 创建缓存键
|
||||||
|
let cache_key = ComputeBudgetCacheKey { data_size_limit, unit_price, unit_limit, is_buy };
|
||||||
|
|
||||||
|
// 先尝试从缓存中获取
|
||||||
|
if let Some(cached_insts) = COMPUTE_BUDGET_CACHE.get(&cache_key) {
|
||||||
|
return cached_insts.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 缓存未命中,生成新的指令
|
||||||
|
let mut insts = SmallVec::<[Instruction; 3]>::new();
|
||||||
|
|
||||||
|
if is_buy {
|
||||||
|
insts.push(ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(data_size_limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
insts.extend([
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_price(unit_price),
|
||||||
|
ComputeBudgetInstruction::set_compute_unit_limit(unit_limit),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 将结果存入缓存
|
||||||
|
let insts_clone = insts.clone();
|
||||||
|
COMPUTE_BUDGET_CACHE.insert(cache_key, insts_clone);
|
||||||
|
|
||||||
|
insts
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
address_lookup_manager::get_address_lookup_table_accounts,
|
address_lookup_manager::get_address_lookup_table_accounts,
|
||||||
compute_budget_manager::add_compute_budget_instructions,
|
compute_budget_manager::compute_budget_instructions,
|
||||||
nonce_manager::{add_nonce_instruction, get_transaction_blockhash},
|
nonce_manager::{add_nonce_instruction, get_transaction_blockhash},
|
||||||
};
|
};
|
||||||
use crate::{common::PriorityFee, trading::MiddlewareManager};
|
use crate::{common::PriorityFee, trading::MiddlewareManager};
|
||||||
@@ -43,7 +43,12 @@ pub async fn build_transaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加计算预算指令
|
// 添加计算预算指令
|
||||||
add_compute_budget_instructions(&mut instructions, priority_fee, data_size_limit, true, is_buy);
|
instructions.extend(compute_budget_instructions(
|
||||||
|
priority_fee,
|
||||||
|
data_size_limit,
|
||||||
|
!with_tip,
|
||||||
|
is_buy,
|
||||||
|
));
|
||||||
|
|
||||||
// 添加业务指令
|
// 添加业务指令
|
||||||
instructions.extend(business_instructions);
|
instructions.extend(business_instructions);
|
||||||
@@ -102,9 +107,8 @@ async fn build_versioned_transaction(
|
|||||||
&address_lookup_table_accounts,
|
&address_lookup_table_accounts,
|
||||||
blockhash,
|
blockhash,
|
||||||
)?;
|
)?;
|
||||||
|
let versioned_msg = VersionedMessage::V0(v0_message);
|
||||||
let versioned_message: VersionedMessage = VersionedMessage::V0(v0_message.clone());
|
let msg_bytes = versioned_msg.serialize();
|
||||||
let transaction = VersionedTransaction::try_new(versioned_message, &[payer.as_ref()])?;
|
let signature = payer.try_sign_message(&msg_bytes).expect("sign failed");
|
||||||
|
Ok(VersionedTransaction { signatures: vec![signature], message: versioned_msg })
|
||||||
Ok(transaction)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ pub async fn parallel_execute_with_tips(
|
|||||||
&& (swqos_clients.len() > priority_fee.buy_tip_fees.len()
|
&& (swqos_clients.len() > priority_fee.buy_tip_fees.len()
|
||||||
|| priority_fee.buy_tip_fees.is_empty())
|
|| priority_fee.buy_tip_fees.is_empty())
|
||||||
{
|
{
|
||||||
return Err(anyhow!("Number of tip clients exceeds the configured buy tip fees"));
|
return Err(anyhow!("Number of tip clients exceeds the configured buy tip fees. Please configure buy_tip_fees to match swqos_clients"));
|
||||||
}
|
}
|
||||||
if !is_buy
|
if !is_buy
|
||||||
&& !with_tip
|
&& !with_tip
|
||||||
&& (swqos_clients.len() > priority_fee.sell_tip_fees.len()
|
&& (swqos_clients.len() > priority_fee.sell_tip_fees.len()
|
||||||
|| priority_fee.sell_tip_fees.is_empty())
|
|| priority_fee.sell_tip_fees.is_empty())
|
||||||
{
|
{
|
||||||
return Err(anyhow!("Number of tip clients exceeds the configured sell tip fees"));
|
return Err(anyhow!("Number of tip clients exceeds the configured sell tip fees. Please configure sell_tip_fees to match swqos_clients"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let instructions = Arc::new(instructions);
|
let instructions = Arc::new(instructions);
|
||||||
@@ -82,7 +82,11 @@ pub async fn parallel_execute_with_tips(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!("Building transaction instructions: {:?} {:?}", swqos_type, start.elapsed());
|
println!(
|
||||||
|
"[{:?}] - Building transaction instructions: {:?}",
|
||||||
|
swqos_type,
|
||||||
|
start.elapsed()
|
||||||
|
);
|
||||||
|
|
||||||
start = Instant::now();
|
start = Instant::now();
|
||||||
|
|
||||||
@@ -93,7 +97,11 @@ pub async fn parallel_execute_with_tips(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!("Submitting transaction instructions: {:?} {:?}", swqos_type, start.elapsed());
|
println!(
|
||||||
|
"[{:?}] - Submitting transaction instructions: {:?}",
|
||||||
|
swqos_type,
|
||||||
|
start.elapsed()
|
||||||
|
);
|
||||||
|
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ pub struct SellParams {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PumpFunParams {
|
pub struct PumpFunParams {
|
||||||
pub bonding_curve: Arc<BondingCurveAccount>,
|
pub bonding_curve: Arc<BondingCurveAccount>,
|
||||||
|
pub associated_bonding_curve: Pubkey,
|
||||||
pub creator_vault: Pubkey,
|
pub creator_vault: Pubkey,
|
||||||
/// Whether to close token account when selling, only effective during sell operations
|
/// Whether to close token account when selling, only effective during sell operations
|
||||||
pub close_token_account_when_sell: Option<bool>,
|
pub close_token_account_when_sell: Option<bool>,
|
||||||
@@ -59,6 +60,7 @@ impl PumpFunParams {
|
|||||||
pub fn immediate_sell(creator_vault: Pubkey, close_token_account_when_sell: bool) -> Self {
|
pub fn immediate_sell(creator_vault: Pubkey, close_token_account_when_sell: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bonding_curve: Arc::new(BondingCurveAccount { ..Default::default() }),
|
bonding_curve: Arc::new(BondingCurveAccount { ..Default::default() }),
|
||||||
|
associated_bonding_curve: Pubkey::default(),
|
||||||
creator_vault: creator_vault,
|
creator_vault: creator_vault,
|
||||||
close_token_account_when_sell: Some(close_token_account_when_sell),
|
close_token_account_when_sell: Some(close_token_account_when_sell),
|
||||||
}
|
}
|
||||||
@@ -76,6 +78,7 @@ impl PumpFunParams {
|
|||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
bonding_curve: Arc::new(bonding_curve),
|
bonding_curve: Arc::new(bonding_curve),
|
||||||
|
associated_bonding_curve: event.associated_bonding_curve,
|
||||||
creator_vault: event.creator_vault,
|
creator_vault: event.creator_vault,
|
||||||
close_token_account_when_sell: close_token_account_when_sell,
|
close_token_account_when_sell: close_token_account_when_sell,
|
||||||
}
|
}
|
||||||
@@ -88,6 +91,7 @@ impl PumpFunParams {
|
|||||||
let bonding_curve = BondingCurveAccount::from_trade(event);
|
let bonding_curve = BondingCurveAccount::from_trade(event);
|
||||||
Self {
|
Self {
|
||||||
bonding_curve: Arc::new(bonding_curve),
|
bonding_curve: Arc::new(bonding_curve),
|
||||||
|
associated_bonding_curve: event.associated_bonding_curve,
|
||||||
creator_vault: event.creator_vault,
|
creator_vault: event.creator_vault,
|
||||||
close_token_account_when_sell: close_token_account_when_sell,
|
close_token_account_when_sell: close_token_account_when_sell,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user