refactor: convert EventParser to static methods and optimize caching

- Replace instance-based EventParser with static method design
  - Introduce ParserCache module for protocol config caching
  - Simplify EventProcessor to pure functional handlers
  - Optimize metrics: remove min/max tracking, keep last + avg only
  - Reduce atomic operations in hot path for better performance
This commit is contained in:
ysq
2025-10-12 17:20:50 +08:00
parent 312a675a40
commit e21f2cfd17
17 changed files with 990 additions and 811 deletions
+10 -10
View File
@@ -96,16 +96,16 @@ async fn get_single_transaction_details(signature_str: &str) -> Result<()> {
Protocol::RaydiumCpmm,
Protocol::RaydiumAmmV4,
];
let parser: Arc<EventParser> = Arc::new(EventParser::new(protocols, None));
parser
.parse_encoded_confirmed_transaction_with_status_meta(
signature,
transaction,
Arc::new(move |event: &UnifiedEvent| {
println!("{:?}\n", event);
}),
)
.await?;
EventParser::parse_encoded_confirmed_transaction_with_status_meta(
&protocols,
None,
signature,
transaction,
Arc::new(move |event: &UnifiedEvent| {
println!("{:?}\n", event);
}),
)
.await?;
}
Err(e) => {
println!("Failed to get transaction: {}", e);
+122 -181
View File
@@ -1,208 +1,149 @@
use std::sync::Arc;
use solana_sdk::pubkey::Pubkey;
use crate::common::AnyResult;
use crate::streaming::common::{MetricsEventType, StreamClientConfig as ClientConfig};
use crate::streaming::common::MetricsEventType;
use crate::streaming::event_parser::common::filter::EventTypeFilter;
use crate::streaming::event_parser::core::account_event_parser::AccountEventParser;
use crate::streaming::event_parser::core::common_event_parser::CommonEventParser;
use crate::streaming::event_parser::core::event_parser::EventParser;
use crate::streaming::event_parser::{core::traits::UnifiedEvent, Protocol};
use crate::streaming::grpc::{EventPretty, MetricsManager};
use crate::streaming::shred::TransactionWithSlot;
use once_cell::sync::OnceCell;
use solana_sdk::pubkey::Pubkey;
use std::sync::Arc;
pub enum EventSource {
Grpc,
Shred,
/// 创建带 metrics 统计的 callback 包装器
///
/// 用于 Transaction 事件处理,在调用原始 callback 的同时更新 metrics
#[inline]
fn create_metrics_callback(
callback: Arc<dyn Fn(UnifiedEvent) + Send + Sync>,
) -> Arc<dyn Fn(UnifiedEvent) + Send + Sync> {
Arc::new(move |event: UnifiedEvent| {
let processing_time_us = event.metadata().handle_us as f64;
callback(event);
MetricsManager::global().update_metrics(
MetricsEventType::Transaction,
1,
processing_time_us,
);
})
}
/// High-performance Event processor
pub struct EventProcessor {
pub(crate) config: ClientConfig,
pub(crate) parser_cache: OnceCell<Arc<EventParser>>,
pub(crate) protocols: Vec<Protocol>,
pub(crate) event_type_filter: Option<EventTypeFilter>,
pub(crate) callback: Option<Arc<dyn Fn(UnifiedEvent) + Send + Sync>>,
}
/// Process GRPC transaction events
pub async fn process_grpc_transaction(
event_pretty: EventPretty,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
callback: Arc<dyn Fn(UnifiedEvent) + Send + Sync>,
bot_wallet: Option<Pubkey>,
) -> AnyResult<()> {
match event_pretty {
EventPretty::Account(account_pretty) => {
MetricsManager::global().add_account_process_count();
impl EventProcessor {
pub fn new(config: ClientConfig) -> Self {
Self {
config,
parser_cache: OnceCell::new(),
protocols: vec![],
event_type_filter: None,
callback: None,
}
}
pub fn set_protocols_and_event_type_filter(
&mut self,
_source: EventSource,
protocols: Vec<Protocol>,
event_type_filter: Option<EventTypeFilter>,
callback: Option<Arc<dyn Fn(UnifiedEvent) + Send + Sync>>,
) {
self.protocols = protocols;
self.event_type_filter = event_type_filter;
self.callback = callback;
let protocols_ref = &self.protocols;
let event_type_filter_ref = self.event_type_filter.as_ref();
self.parser_cache.get_or_init(|| {
Arc::new(EventParser::new(protocols_ref.clone(), event_type_filter_ref.cloned()))
});
}
pub fn get_parser(&self) -> Arc<EventParser> {
self.parser_cache.get().unwrap().clone()
}
fn invoke_callback(&self, event: UnifiedEvent) {
if let Some(callback) = self.callback.as_ref() {
callback(event);
}
}
pub async fn process_grpc_transaction(
&self,
event_pretty: EventPretty,
bot_wallet: Option<Pubkey>,
) -> AnyResult<()> {
if self.callback.is_none() {
return Ok(());
}
match event_pretty {
EventPretty::Account(account_pretty) => {
MetricsManager::global().add_account_process_count();
let account_event = AccountEventParser::parse_account_event(
&self.protocols,
account_pretty,
self.event_type_filter.as_ref(),
);
if let Some(mut event) = account_event {
let processing_time_us = event.metadata().handle_us as f64;
self.invoke_callback(event);
self.update_metrics(MetricsEventType::Account, 1, processing_time_us);
}
}
EventPretty::Transaction(transaction_pretty) => {
MetricsManager::global().add_tx_process_count();
let slot = transaction_pretty.slot;
let signature = transaction_pretty.signature;
let block_time = transaction_pretty.block_time;
let recv_us = transaction_pretty.recv_us;
let transaction_index = transaction_pretty.transaction_index;
let grpc_tx = transaction_pretty.grpc_tx;
let parser = self.get_parser();
let callback = self.callback.clone().unwrap();
let adapter_callback = Arc::new(move |mut event: UnifiedEvent| {
let processing_time_us = event.metadata().handle_us as f64;
callback(event);
MetricsManager::global().update_metrics(
MetricsEventType::Transaction,
1,
processing_time_us,
);
});
parser
.parse_grpc_transaction_owned(
grpc_tx,
signature,
Some(slot),
block_time,
recv_us,
bot_wallet,
transaction_index,
adapter_callback,
)
.await?;
}
EventPretty::BlockMeta(block_meta_pretty) => {
MetricsManager::global().add_block_meta_process_count();
let block_time_ms = block_meta_pretty
.block_time
.map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000)
.unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
let mut block_meta_event = CommonEventParser::generate_block_meta_event(
block_meta_pretty.slot,
block_meta_pretty.block_hash,
block_time_ms,
block_meta_pretty.recv_us,
);
let processing_time_us = block_meta_event.metadata().handle_us as f64;
self.invoke_callback(block_meta_event);
self.update_metrics(MetricsEventType::BlockMeta, 1, processing_time_us);
}
}
Ok(())
}
pub async fn process_shred_transaction(
&self,
transaction_with_slot: TransactionWithSlot,
bot_wallet: Option<Pubkey>,
) -> AnyResult<()> {
if self.callback.is_none() {
return Ok(());
}
MetricsManager::global().add_tx_process_count();
let tx = transaction_with_slot.transaction;
let slot = transaction_with_slot.slot;
if tx.signatures.is_empty() {
return Ok(());
}
let signature = tx.signatures[0];
let recv_us = transaction_with_slot.recv_us;
let parser = self.get_parser();
let callback = self.callback.clone().unwrap();
let adapter_callback = Arc::new(move |mut event: UnifiedEvent| {
let processing_time_us = event.metadata().handle_us as f64;
callback(event);
MetricsManager::global().update_metrics(
MetricsEventType::Transaction,
1,
processing_time_us,
let account_event = AccountEventParser::parse_account_event(
protocols,
account_pretty,
event_type_filter,
);
});
parser
.parse_versioned_transaction_owned(
tx,
if let Some(event) = account_event {
let processing_time_us = event.metadata().handle_us as f64;
callback(event);
update_metrics(MetricsEventType::Account, 1, processing_time_us);
}
}
EventPretty::Transaction(transaction_pretty) => {
MetricsManager::global().add_tx_process_count();
let slot = transaction_pretty.slot;
let signature = transaction_pretty.signature;
let block_time = transaction_pretty.block_time;
let recv_us = transaction_pretty.recv_us;
let transaction_index = transaction_pretty.transaction_index;
let grpc_tx = transaction_pretty.grpc_tx;
let adapter_callback = create_metrics_callback(callback.clone());
EventParser::parse_grpc_transaction_owned(
protocols,
event_type_filter,
grpc_tx,
signature,
Some(slot),
None,
block_time,
recv_us,
bot_wallet,
None,
&[],
transaction_index,
adapter_callback,
)
.await?;
}
EventPretty::BlockMeta(block_meta_pretty) => {
MetricsManager::global().add_block_meta_process_count();
Ok(())
}
let block_time_ms = block_meta_pretty
.block_time
.map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000)
.unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
fn update_metrics(&self, ty: MetricsEventType, count: u64, time_us: f64) {
MetricsManager::global().update_metrics(ty, count, time_us);
}
}
let block_meta_event = CommonEventParser::generate_block_meta_event(
block_meta_pretty.slot,
block_meta_pretty.block_hash,
block_time_ms,
block_meta_pretty.recv_us,
);
impl Clone for EventProcessor {
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
parser_cache: self.parser_cache.clone(),
protocols: self.protocols.clone(),
event_type_filter: self.event_type_filter.clone(),
callback: self.callback.clone(),
let processing_time_us = block_meta_event.metadata().handle_us as f64;
callback(block_meta_event);
update_metrics(MetricsEventType::BlockMeta, 1, processing_time_us);
}
}
Ok(())
}
/// Process Shred transaction events
pub async fn process_shred_transaction(
transaction_with_slot: TransactionWithSlot,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
callback: Arc<dyn Fn(UnifiedEvent) + Send + Sync>,
bot_wallet: Option<Pubkey>,
) -> AnyResult<()> {
MetricsManager::global().add_tx_process_count();
let tx = transaction_with_slot.transaction;
let slot = transaction_with_slot.slot;
if tx.signatures.is_empty() {
return Ok(());
}
let signature = tx.signatures[0];
let recv_us = transaction_with_slot.recv_us;
let adapter_callback = create_metrics_callback(callback);
EventParser::parse_versioned_transaction_owned(
protocols,
event_type_filter,
tx,
signature,
Some(slot),
None,
recv_us,
bot_wallet,
None,
&[],
adapter_callback,
)
.await?;
Ok(())
}
/// Update metrics for event processing
#[inline]
fn update_metrics(ty: MetricsEventType, count: u64, time_us: f64) {
MetricsManager::global().update_metrics(ty, count, time_us);
}
+18 -85
View File
@@ -104,21 +104,15 @@ impl AtomicEventMetrics {
/// High-performance atomic processing time statistics
#[derive(Debug)]
struct AtomicProcessingTimeStats {
min_time_bits: AtomicU64,
max_time_bits: AtomicU64,
min_time_timestamp_nanos: AtomicU64, // Timestamp of min value update (nanoseconds)
max_time_timestamp_nanos: AtomicU64, // Timestamp of max value update (nanoseconds)
total_time_us: AtomicU64, // Store integer part of microseconds
last_time_bits: AtomicU64, // Last processing time (f64 as u64 bits)
total_time_us: AtomicU64, // Store integer part of microseconds
total_events: AtomicU64,
}
impl AtomicProcessingTimeStats {
const fn new_const() -> Self {
Self {
min_time_bits: AtomicU64::new(f64::INFINITY.to_bits()),
max_time_bits: AtomicU64::new(0),
min_time_timestamp_nanos: AtomicU64::new(0),
max_time_timestamp_nanos: AtomicU64::new(0),
last_time_bits: AtomicU64::new(0),
total_time_us: AtomicU64::new(0),
total_events: AtomicU64::new(0),
}
@@ -129,33 +123,8 @@ impl AtomicProcessingTimeStats {
fn update(&self, time_us: f64, event_count: u64) {
let time_bits = time_us.to_bits();
// Fast path: Update min value without timestamp check (checked in background task)
let mut current_min = self.min_time_bits.load(Ordering::Relaxed);
while time_bits < current_min {
match self.min_time_bits.compare_exchange_weak(
current_min,
time_bits,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(x) => current_min = x,
}
}
// Fast path: Update max value without timestamp check (checked in background task)
let mut current_max = self.max_time_bits.load(Ordering::Relaxed);
while time_bits > current_max {
match self.max_time_bits.compare_exchange_weak(
current_max,
time_bits,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(x) => current_max = x,
}
}
// Update last processing time (simple store, no compare-exchange needed)
self.last_time_bits.store(time_bits, Ordering::Relaxed);
// Update cumulative values (convert microseconds to integers to avoid floating point accumulation issues)
let total_time_us_int = (time_us * event_count as f64) as u64;
@@ -163,55 +132,26 @@ impl AtomicProcessingTimeStats {
self.total_events.fetch_add(event_count, Ordering::Relaxed);
}
/// Reset min/max if they are older than 10 seconds (called by background task)
#[inline]
fn reset_stale_min_max(&self) {
let now_nanos =
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()
as u64;
// Check and reset min if stale
let min_timestamp = self.min_time_timestamp_nanos.load(Ordering::Relaxed);
if now_nanos.saturating_sub(min_timestamp) > 10_000_000_000 {
self.min_time_bits.store(f64::INFINITY.to_bits(), Ordering::Relaxed);
self.min_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed);
}
// Check and reset max if stale
let max_timestamp = self.max_time_timestamp_nanos.load(Ordering::Relaxed);
if now_nanos.saturating_sub(max_timestamp) > 10_000_000_000 {
self.max_time_bits.store(0, Ordering::Relaxed);
self.max_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed);
}
}
/// Get statistics (non-blocking)
#[inline]
fn get_stats(&self) -> ProcessingTimeStats {
let min_bits = self.min_time_bits.load(Ordering::Relaxed);
let max_bits = self.max_time_bits.load(Ordering::Relaxed);
let last_bits = self.last_time_bits.load(Ordering::Relaxed);
let total_time_us_int = self.total_time_us.load(Ordering::Relaxed);
let total_events = self.total_events.load(Ordering::Relaxed);
let min_time = f64::from_bits(min_bits);
let max_time = f64::from_bits(max_bits);
let last_time = f64::from_bits(last_bits);
let avg_time =
if total_events > 0 { total_time_us_int as f64 / total_events as f64 } else { 0.0 };
ProcessingTimeStats {
min_us: if min_time == f64::INFINITY { 0.0 } else { min_time },
max_us: max_time,
avg_us: avg_time,
}
ProcessingTimeStats { last_us: last_time, avg_us: avg_time }
}
}
/// Processing time statistics result
#[derive(Debug, Clone)]
pub struct ProcessingTimeStats {
pub min_us: f64,
pub max_us: f64,
pub avg_us: f64,
pub last_us: f64, // Last processing time in microseconds
pub avg_us: f64, // Average processing time in microseconds
}
/// Event metrics snapshot
@@ -236,7 +176,7 @@ pub struct PerformanceMetrics {
impl PerformanceMetrics {
/// Create default performance metrics (compatibility method)
pub fn new() -> Self {
let default_stats = ProcessingTimeStats { min_us: 0.0, max_us: 0.0, avg_us: 0.0 };
let default_stats = ProcessingTimeStats { last_us: 0.0, avg_us: 0.0 };
let default_metrics = EventMetricsSnapshot {
process_count: 0,
events_processed: 0,
@@ -384,12 +324,6 @@ impl MetricsManager {
GLOBAL_METRICS.update_window_metrics(EventType::Account, window_duration_nanos);
GLOBAL_METRICS
.update_window_metrics(EventType::BlockMeta, window_duration_nanos);
// Reset stale min/max values (10 second expiry) - moved from hot path
GLOBAL_METRICS.processing_stats.reset_stale_min_max();
for event_metric in &GLOBAL_METRICS.event_metrics {
event_metric.processing_stats.reset_stale_min_max();
}
}
});
}
@@ -467,24 +401,23 @@ impl MetricsManager {
}
// 打印事件指标表格(包含处理时间统计)
println!("┌─────────────┬──────────────┬──────────────────┬─────────────┬─────────────┬─────────────");
println!("│ Event Type │ Process Count│ Events Processed │ Avg Time(μs)│ Min 10s(μs) │ Max 10s(μs)");
println!("├─────────────┼──────────────┼──────────────────┼─────────────┼─────────────┼─────────────");
println!("┌─────────────┬──────────────┬──────────────────┬─────────────┬─────────────┐");
println!("│ Event Type │ Process Count│ Events Processed │ Last(μs) │ Avg(μs) ");
println!("├─────────────┼──────────────┼──────────────────┼─────────────┼─────────────┤");
for event_type in [EventType::Transaction, EventType::Account, EventType::BlockMeta] {
let metrics = self.get_event_metrics(event_type);
println!(
"{:11}{:12}{:16}{:9.2}{:9.2}{:9.2}",
"{:11}{:12}{:16}{:9.2}{:9.2}",
event_type.name(),
metrics.process_count,
metrics.events_processed,
metrics.processing_stats.avg_us,
metrics.processing_stats.min_us,
metrics.processing_stats.max_us
metrics.processing_stats.last_us,
metrics.processing_stats.avg_us
);
}
println!("└─────────────┴──────────────┴──────────────────┴─────────────┴─────────────┴─────────────");
println!("└─────────────┴──────────────┴──────────────────┴─────────────┴─────────────┘");
println!();
}
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::streaming::event_parser::common::{
types::EventType, ACCOUNT_EVENT_TYPES, BLOCK_EVENT_TYPES,
};
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct EventTypeFilter {
pub include: Vec<EventType>,
}
+1 -1
View File
@@ -56,7 +56,7 @@ pub enum ProtocolType {
/// Event type enumeration
#[derive(
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
)]
pub enum EventType {
// PumpSwap events
@@ -1,14 +1,9 @@
use crate::streaming::common::SimdUtils;
use crate::streaming::event_parser::common::filter::EventTypeFilter;
use crate::streaming::event_parser::common::high_performance_clock::elapsed_micros_since;
use crate::streaming::event_parser::common::{EventMetadata, EventType, ProtocolType};
use crate::streaming::event_parser::common::EventMetadata;
use crate::streaming::event_parser::core::parser_cache::get_account_configs;
use crate::streaming::event_parser::core::traits::UnifiedEvent;
use crate::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
use crate::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
use crate::streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID;
use crate::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID;
use crate::streaming::event_parser::protocols::raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID;
use crate::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID;
use crate::streaming::event_parser::Protocol;
use crate::streaming::grpc::AccountPretty;
use serde::{Deserialize, Serialize};
@@ -20,18 +15,6 @@ use spl_token_2022::{
extension::StateWithExtensions,
state::{Account as Account2022, Mint as Mint2022},
};
use std::collections::HashMap;
use std::sync::OnceLock;
/// 通用事件解析器配置
#[derive(Debug, Clone)]
pub struct AccountEventParseConfig {
pub program_id: Pubkey,
pub protocol_type: ProtocolType,
pub event_type: EventType,
pub account_discriminator: &'static [u8],
pub account_parser: AccountEventParserFn,
}
/// 通用账户事件
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
@@ -72,188 +55,21 @@ pub struct TokenInfoEvent {
pub decimals: u8,
}
/// 账户事件解析器
pub type AccountEventParserFn =
fn(account: &AccountPretty, metadata: EventMetadata) -> Option<UnifiedEvent>;
static PROTOCOL_CONFIGS_CACHE: OnceLock<HashMap<Protocol, Vec<AccountEventParseConfig>>> =
OnceLock::new();
// 通用账户解析配置的静态缓存
static COMMON_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
// Nonce account config
static NONCE_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
pub struct AccountEventParser {}
impl AccountEventParser {
pub fn configs(
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
) -> Vec<AccountEventParseConfig> {
let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| {
let mut map: HashMap<Protocol, Vec<AccountEventParseConfig>> = HashMap::new();
map.insert(Protocol::PumpSwap, vec![
AccountEventParseConfig {
program_id: PUMPSWAP_PROGRAM_ID,
protocol_type: ProtocolType::PumpSwap,
event_type: EventType::AccountPumpSwapGlobalConfig,
account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::GLOBAL_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpswap::types::global_config_parser,
},
AccountEventParseConfig {
program_id: PUMPSWAP_PROGRAM_ID,
protocol_type: ProtocolType::PumpSwap,
event_type: EventType::AccountPumpSwapPool,
account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::POOL_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpswap::types::pool_parser,
},
]);
map.insert(Protocol::PumpFun, vec![
AccountEventParseConfig {
program_id: PUMPFUN_PROGRAM_ID,
protocol_type: ProtocolType::PumpFun,
event_type: EventType::AccountPumpFunBondingCurve,
account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::BONDING_CURVE_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser,
},
AccountEventParseConfig {
program_id: PUMPFUN_PROGRAM_ID,
protocol_type: ProtocolType::PumpFun,
event_type: EventType::AccountPumpFunGlobal,
account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::GLOBAL_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpfun::types::global_parser,
},
]);
map.insert(Protocol::Bonk, vec![
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkPoolState,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::POOL_STATE_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::pool_state_parser,
},
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkGlobalConfig,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::GLOBAL_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::global_config_parser,
},
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkPlatformConfig,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::PLATFORM_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::platform_config_parser,
},
]);
map.insert(Protocol::RaydiumCpmm, vec![
AccountEventParseConfig {
program_id: RAYDIUM_CPMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumCpmm,
event_type: EventType::AccountRaydiumCpmmAmmConfig,
account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::AMM_CONFIG,
account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::amm_config_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CPMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumCpmm,
event_type: EventType::AccountRaydiumCpmmPoolState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::POOL_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::pool_state_parser,
},
]);
map.insert(Protocol::RaydiumClmm, vec![
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmAmmConfig,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::AMM_CONFIG,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::amm_config_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmPoolState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::POOL_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::pool_state_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmTickArrayState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::TICK_ARRAY_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_state_parser,
},
]);
map.insert(Protocol::RaydiumAmmV4, vec![
AccountEventParseConfig {
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumAmmV4,
event_type: EventType::AccountRaydiumAmmV4AmmInfo,
account_discriminator: crate::streaming::event_parser::protocols::raydium_amm_v4::discriminators::AMM_INFO,
account_parser: crate::streaming::event_parser::protocols::raydium_amm_v4::types::amm_info_parser,
},
]);
map
});
let mut configs = Vec::new();
let empty_vec = Vec::new();
// 预估容量以减少重新分配
let estimated_capacity = protocols.len() * 3; // 大多数协议有2-3个配置
configs.reserve(estimated_capacity);
for protocol in protocols {
let protocol_configs = protocols_map.get(protocol).unwrap_or(&empty_vec);
// 如果没有过滤器,直接扩展所有配置
if event_type_filter.is_none() {
configs.extend(protocol_configs.iter().cloned());
} else {
// 有过滤器时才进行过滤
let filter = event_type_filter.unwrap();
configs.extend(
protocol_configs
.iter()
.filter(|config| filter.include.contains(&config.event_type))
.cloned(),
);
}
}
if event_type_filter.is_none()
|| event_type_filter.unwrap().include.contains(&EventType::NonceAccount)
{
let nonce_config = NONCE_CONFIG.get_or_init(|| AccountEventParseConfig {
program_id: Pubkey::default(),
protocol_type: ProtocolType::Common,
event_type: EventType::NonceAccount,
account_discriminator: &[1, 0, 0, 0, 1, 0, 0, 0],
account_parser: Self::parse_nonce_account_event,
});
configs.push(nonce_config.clone());
}
let common_config = COMMON_CONFIG.get_or_init(|| AccountEventParseConfig {
program_id: Pubkey::default(),
protocol_type: ProtocolType::Common,
event_type: EventType::TokenAccount,
account_discriminator: &[],
account_parser: Self::parse_token_account_event,
});
configs.push(common_config.clone());
configs
}
pub fn parse_account_event(
protocols: &[Protocol],
account: AccountPretty,
event_type_filter: Option<&EventTypeFilter>,
) -> Option<UnifiedEvent> {
let configs = Self::configs(protocols, event_type_filter);
// 直接从 parser_cache 获取配置
let configs = get_account_configs(
protocols,
event_type_filter,
Self::parse_nonce_account_event,
Self::parse_token_account_event,
);
for config in configs {
if config.program_id == Pubkey::default()
|| (account.owner == config.program_id
@@ -275,7 +91,9 @@ impl AccountEventParser {
..Default::default()
},
);
return event;
if event.is_some() {
return event;
}
}
}
None
+168 -284
View File
@@ -5,18 +5,17 @@ use crate::streaming::{
filter::EventTypeFilter,
high_performance_clock::{elapsed_micros_since, get_high_perf_clock},
parse_swap_data_from_next_grpc_instructions, parse_swap_data_from_next_instructions,
EventMetadata, EventType, ProtocolType,
EventMetadata,
},
core::global_state::{
add_bonk_dev_address, add_dev_address, is_bonk_dev_address_in_signature,
is_dev_address_in_signature,
},
protocols::{
bonk::parser::BONK_PROGRAM_ID, pumpfun::parser::PUMPFUN_PROGRAM_ID,
pumpswap::parser::PUMPSWAP_PROGRAM_ID,
raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID,
raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID,
raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
core::{
global_state::{
add_bonk_dev_address, add_dev_address, is_bonk_dev_address_in_signature,
is_dev_address_in_signature,
},
parser_cache::{
build_account_pubkeys_with_cache, get_global_instruction_configs,
get_global_program_ids, GenericEventParseConfig,
},
},
Protocol, UnifiedEvent,
},
@@ -29,170 +28,16 @@ use solana_sdk::{
use solana_transaction_status::{
EncodedConfirmedTransactionWithStatusMeta, InnerInstruction, InnerInstructions, UiInstruction,
};
use std::{
collections::HashMap,
sync::{Arc, LazyLock},
};
use std::sync::Arc;
use yellowstone_grpc_proto::geyser::SubscribeUpdateTransactionInfo;
/// 高性能账户公钥缓存,避免重复Vec分配
#[derive(Debug)]
pub struct AccountPubkeyCache {
/// 预分配的账户公钥向量,避免每次重新分配
cache: Vec<Pubkey>,
}
impl AccountPubkeyCache {
/// 创建新的账户公钥缓存
pub fn new() -> Self {
Self {
cache: Vec::with_capacity(32), // 预分配32个位置,覆盖大多数交易
}
}
/// 从指令账户索引构建账户公钥向量,重用缓存内存
#[inline]
pub fn build_account_pubkeys(
&mut self,
instruction_accounts: &[u8],
all_accounts: &[Pubkey],
) -> &[Pubkey] {
self.cache.clear();
// 确保容量足够,避免动态扩容
if self.cache.capacity() < instruction_accounts.len() {
self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
}
// 快速填充账户公钥
for &idx in instruction_accounts.iter() {
if (idx as usize) < all_accounts.len() {
self.cache.push(all_accounts[idx as usize]);
}
}
&self.cache
}
}
impl Default for AccountPubkeyCache {
fn default() -> Self {
Self::new()
}
}
/// 内联指令事件解析器
pub type InnerInstructionEventParser =
fn(data: &[u8], metadata: EventMetadata) -> Option<UnifiedEvent>;
/// 指令事件解析器
pub type InstructionEventParser =
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<UnifiedEvent>;
/// 通用事件解析器配置
#[derive(Debug, Clone)]
pub struct GenericEventParseConfig {
pub program_id: Pubkey,
pub protocol_type: ProtocolType,
pub inner_instruction_discriminator: &'static [u8],
pub instruction_discriminator: &'static [u8],
pub event_type: EventType,
pub inner_instruction_parser: Option<InnerInstructionEventParser>,
pub instruction_parser: Option<InstructionEventParser>,
pub requires_inner_instruction: bool,
}
pub static EVENT_PARSERS: LazyLock<HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])>> =
LazyLock::new(|| {
// 预分配容量,避免动态扩容
let mut parsers: HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])> =
HashMap::with_capacity(6);
parsers.insert(
Protocol::PumpSwap,
(
PUMPSWAP_PROGRAM_ID,
crate::streaming::event_parser::protocols::pumpswap::parser::CONFIGS,
),
);
parsers.insert(
Protocol::PumpFun,
(
PUMPFUN_PROGRAM_ID,
crate::streaming::event_parser::protocols::pumpfun::parser::CONFIGS,
),
);
parsers.insert(
Protocol::Bonk,
(BONK_PROGRAM_ID, crate::streaming::event_parser::protocols::bonk::parser::CONFIGS),
);
parsers.insert(
Protocol::RaydiumCpmm,
(
RAYDIUM_CPMM_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_cpmm::parser::CONFIGS,
),
);
parsers.insert(
Protocol::RaydiumClmm,
(
RAYDIUM_CLMM_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_clmm::parser::CONFIGS,
),
);
parsers.insert(
Protocol::RaydiumAmmV4,
(
RAYDIUM_AMM_V4_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_amm_v4::parser::CONFIGS,
),
);
parsers
});
/// 通用事件解析器基类
pub struct EventParser {
pub program_ids: Vec<Pubkey>,
// pub inner_instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
pub instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
/// 账户公钥缓存,避免重复分配
pub account_cache: parking_lot::Mutex<AccountPubkeyCache>,
}
pub struct EventParser {}
impl EventParser {
pub fn new(protocols: Vec<Protocol>, event_type_filter: Option<EventTypeFilter>) -> Self {
let mut instruction_configs = HashMap::with_capacity(protocols.len());
let mut program_ids = Vec::with_capacity(protocols.len());
// Configure all event types
for protocol in protocols {
let parse = EVENT_PARSERS.get(&protocol).unwrap();
// Merge instruction_configs, append configurations to existing Vec
parse
.1
.iter()
.filter(|config| {
event_type_filter
.as_ref()
.map(|filter| filter.include.contains(&config.event_type))
.unwrap_or(true)
})
.for_each(|config| {
instruction_configs
.entry(config.instruction_discriminator.to_vec())
.or_insert_with(Vec::new)
.push(config.clone());
});
// Append program_ids (this is already appending)
program_ids.push(parse.0);
}
let account_cache = parking_lot::Mutex::new(AccountPubkeyCache::new());
Self { program_ids, instruction_configs, account_cache }
}
#[allow(clippy::too_many_arguments)]
async fn parse_instruction_events_from_grpc_transaction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
compiled_instructions: &[yellowstone_grpc_proto::prelude::CompiledInstruction],
signature: Signature,
slot: Option<u64>,
@@ -207,7 +52,9 @@ impl EventParser {
// 获取交易的指令和账户
let mut accounts = accounts.to_vec();
// 检查交易中是否包含程序
let has_program = accounts.iter().any(|account| self.should_handle(account));
let has_program = accounts
.iter()
.any(|account| Self::should_handle(protocols, event_type_filter, account));
if has_program {
// 解析每个指令
for (index, instruction) in compiled_instructions.iter().enumerate() {
@@ -221,8 +68,10 @@ impl EventParser {
if *max_idx as usize >= accounts.len() {
accounts.resize(*max_idx as usize + 1, Pubkey::default());
}
if self.should_handle(&program_id) {
self.parse_events_from_grpc_instruction(
if Self::should_handle(protocols, event_type_filter, &program_id) {
Self::parse_events_from_grpc_instruction(
protocols,
event_type_filter,
instruction,
&accounts,
signature,
@@ -250,7 +99,9 @@ impl EventParser {
accounts: inner_accounts.to_vec(),
data: data.to_vec(),
};
self.parse_events_from_grpc_instruction(
Self::parse_events_from_grpc_instruction(
protocols,
event_type_filter,
&instruction,
&accounts,
signature,
@@ -275,7 +126,8 @@ impl EventParser {
/// 从VersionedTransaction中解析指令事件的通用方法
#[allow(clippy::too_many_arguments)]
async fn parse_instruction_events_from_versioned_transaction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
transaction: &VersionedTransaction,
signature: Signature,
slot: Option<u64>,
@@ -291,7 +143,9 @@ impl EventParser {
let compiled_instructions = transaction.message.instructions();
let mut accounts: Vec<Pubkey> = accounts.to_vec();
// 检查交易中是否包含程序
let has_program = accounts.iter().any(|account| self.should_handle(account));
let has_program = accounts
.iter()
.any(|account| Self::should_handle(protocols, event_type_filter, account));
if has_program {
// 解析每个指令
for (index, instruction) in compiled_instructions.iter().enumerate() {
@@ -300,13 +154,15 @@ impl EventParser {
let inner_instructions = inner_instructions
.iter()
.find(|inner_instruction| inner_instruction.index == index as u8);
if self.should_handle(&program_id) {
if Self::should_handle(protocols, event_type_filter, &program_id) {
let max_idx = instruction.accounts.iter().max().unwrap_or(&0);
// 补齐accounts(使用Pubkey::default())
if *max_idx as usize >= accounts.len() {
accounts.resize(*max_idx as usize + 1, Pubkey::default());
}
self.parse_events_from_instruction(
Self::parse_events_from_instruction(
protocols,
event_type_filter,
instruction,
&accounts,
signature,
@@ -326,7 +182,9 @@ impl EventParser {
for (inner_index, inner_instruction) in
inner_instructions.instructions.iter().enumerate()
{
self.parse_events_from_instruction(
Self::parse_events_from_instruction(
protocols,
event_type_filter,
&inner_instruction.instruction,
&accounts,
signature,
@@ -349,7 +207,8 @@ impl EventParser {
}
pub async fn parse_versioned_transaction_owned(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
versioned_tx: VersionedTransaction,
signature: Signature,
slot: Option<u64>,
@@ -364,7 +223,9 @@ impl EventParser {
let adapter_callback = Arc::new(move |event: &UnifiedEvent| {
callback(event.clone());
});
self.parse_versioned_transaction(
Self::parse_versioned_transaction(
protocols,
event_type_filter,
&versioned_tx,
signature,
slot,
@@ -380,7 +241,8 @@ impl EventParser {
}
async fn parse_versioned_transaction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
versioned_tx: &VersionedTransaction,
signature: Signature,
slot: Option<u64>,
@@ -392,7 +254,9 @@ impl EventParser {
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + Send + Sync>,
) -> anyhow::Result<()> {
let accounts: Vec<Pubkey> = versioned_tx.message.static_account_keys().to_vec();
self.parse_instruction_events_from_versioned_transaction(
Self::parse_instruction_events_from_versioned_transaction(
protocols,
event_type_filter,
versioned_tx,
signature,
slot,
@@ -409,7 +273,8 @@ impl EventParser {
}
pub async fn parse_grpc_transaction_owned(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
grpc_tx: SubscribeUpdateTransactionInfo,
signature: Signature,
slot: Option<u64>,
@@ -424,7 +289,9 @@ impl EventParser {
callback(event.clone());
});
// 调用原始方法
self.parse_grpc_transaction(
Self::parse_grpc_transaction(
protocols,
event_type_filter,
grpc_tx,
signature,
slot,
@@ -438,7 +305,8 @@ impl EventParser {
}
async fn parse_grpc_transaction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
grpc_tx: SubscribeUpdateTransactionInfo,
signature: Signature,
slot: Option<u64>,
@@ -458,7 +326,7 @@ impl EventParser {
if let Some(meta) = grpc_tx.meta {
inner_instructions = meta.inner_instructions;
address_table_lookups.reserve(
meta.loaded_writable_addresses.len() + meta.loaded_writable_addresses.len(),
meta.loaded_writable_addresses.len() + meta.loaded_readonly_addresses.len(),
);
let loaded_writable_addresses = meta.loaded_writable_addresses;
let loaded_readonly_addresses = meta.loaded_readonly_addresses;
@@ -487,7 +355,9 @@ impl EventParser {
let inner_instructions_arc = Arc::new(inner_instructions);
// 解析指令事件
let instructions = &message.instructions;
self.parse_instruction_events_from_grpc_transaction(
Self::parse_instruction_events_from_grpc_transaction(
protocols,
event_type_filter,
&instructions,
signature,
slot,
@@ -507,7 +377,8 @@ impl EventParser {
}
pub async fn parse_encoded_confirmed_transaction_with_status_meta(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
signature: Signature,
transaction: EncodedConfirmedTransactionWithStatusMeta,
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + Send + Sync>,
@@ -600,7 +471,9 @@ impl EventParser {
let bot_wallet = None;
let transaction_index = None;
// 解析指令事件
self.parse_instruction_events_from_versioned_transaction(
Self::parse_instruction_events_from_versioned_transaction(
protocols,
event_type_filter,
&versioned_tx,
signature,
Some(slot),
@@ -620,7 +493,6 @@ impl EventParser {
/// 通用的内联指令解析方法
#[allow(clippy::too_many_arguments)]
fn parse_inner_instruction_event(
&self,
config: &GenericEventParseConfig,
data: &[u8],
signature: Signature,
@@ -656,7 +528,6 @@ impl EventParser {
/// 通用的指令解析方法
#[allow(clippy::too_many_arguments)]
fn parse_instruction_event(
&self,
config: &GenericEventParseConfig,
data: &[u8],
account_pubkeys: &[Pubkey],
@@ -693,7 +564,6 @@ impl EventParser {
/// 从内联指令中解析事件数据
#[allow(clippy::too_many_arguments)]
fn parse_events_from_inner_instruction(
&self,
inner_instruction: &CompiledInstruction,
signature: Signature,
slot: u64,
@@ -724,7 +594,7 @@ impl EventParser {
let data = &inner_instruction.data[16..];
let mut events = Vec::new();
if let Some(event) = self.parse_inner_instruction_event(
if let Some(event) = Self::parse_inner_instruction_event(
config,
data,
signature,
@@ -743,7 +613,6 @@ impl EventParser {
/// 从内联指令中解析事件数据
#[allow(clippy::too_many_arguments)]
fn parse_events_from_grpc_inner_instruction(
&self,
inner_instruction: &yellowstone_grpc_proto::prelude::InnerInstruction,
signature: Signature,
slot: u64,
@@ -774,7 +643,7 @@ impl EventParser {
let data = &inner_instruction.data[16..];
let mut events = Vec::new();
if let Some(event) = self.parse_inner_instruction_event(
if let Some(event) = Self::parse_inner_instruction_event(
config,
data,
signature,
@@ -793,7 +662,8 @@ impl EventParser {
/// 从指令中解析事件
#[allow(clippy::too_many_arguments)]
fn parse_events_from_instruction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
instruction: &CompiledInstruction,
accounts: &[Pubkey],
signature: Signature,
@@ -807,13 +677,18 @@ impl EventParser {
inner_instructions: Option<&InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + Send + Sync>,
) -> anyhow::Result<()> {
let program_id = accounts[instruction.program_id_index as usize];
if !self.should_handle(&program_id) {
// 添加边界检查以防止越界访问
let program_id_index = instruction.program_id_index as usize;
if program_id_index >= accounts.len() {
return Ok(());
}
let program_id = accounts[program_id_index];
if !Self::should_handle(protocols, event_type_filter, &program_id) {
return Ok(());
}
// 一维化并行处理:将所有 (discriminator, config) 组合展开并行处理
let all_processing_params: Vec<_> = self
.instruction_configs
let instruction_configs = get_global_instruction_configs(protocols, event_type_filter);
let all_processing_params: Vec<_> = instruction_configs
.iter()
.filter(|(disc, _)| {
// Use SIMD-optimized data validation and discriminator matching
@@ -833,18 +708,15 @@ impl EventParser {
return Ok(());
}
// 使用缓存构建账户公钥列表,避免重复分配 (只需构建一次)
let account_pubkeys = {
let mut cache_guard = self.account_cache.lock();
cache_guard.build_account_pubkeys(&instruction.accounts, accounts).to_vec()
};
// 使用线程局部缓存构建账户公钥列表,避免重复分配 (只需构建一次)
let account_pubkeys = build_account_pubkeys_with_cache(&instruction.accounts, accounts);
// 并行处理所有 (discriminator, config) 组合
let all_results: Vec<_> = all_processing_params
.iter()
.filter_map(|(disc, config)| {
let data = &instruction.data[disc.len()..];
self.parse_instruction_event(
Self::parse_instruction_event(
config,
data,
&account_pubkeys,
@@ -870,7 +742,7 @@ impl EventParser {
let (inner_event_result, swap_data_result) = std::thread::scope(|s| {
let inner_event_handle = s.spawn(|| {
for inner_instruction in inner_instructions_ref.instructions.iter() {
let result = self.parse_events_from_inner_instruction(
let result = Self::parse_events_from_inner_instruction(
&inner_instruction.instruction,
signature,
slot,
@@ -881,7 +753,7 @@ impl EventParser {
transaction_index,
&config,
);
if result.len() > 0 {
if !result.is_empty() {
return Some(result[0].clone());
}
}
@@ -922,7 +794,7 @@ impl EventParser {
}
// 设置处理时间(使用高性能时钟)
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
event = process_event(event, bot_wallet);
event = Self::process_event(event, bot_wallet);
callback(&event);
}
Ok(())
@@ -932,7 +804,8 @@ impl EventParser {
/// TODO: - wait refactor
#[allow(clippy::too_many_arguments)]
fn parse_events_from_grpc_instruction(
&self,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
instruction: &yellowstone_grpc_proto::prelude::CompiledInstruction,
accounts: &[Pubkey],
signature: Signature,
@@ -946,13 +819,18 @@ impl EventParser {
inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + Send + Sync>,
) -> anyhow::Result<()> {
let program_id = accounts[instruction.program_id_index as usize];
if !self.should_handle(&program_id) {
// 添加边界检查以防止越界访问
let program_id_index = instruction.program_id_index as usize;
if program_id_index >= accounts.len() {
return Ok(());
}
let program_id = accounts[program_id_index];
if !Self::should_handle(protocols, event_type_filter, &program_id) {
return Ok(());
}
// 一维化并行处理:将所有 (discriminator, config) 组合展开并行处理
let all_processing_params: Vec<_> = self
.instruction_configs
let instruction_configs = get_global_instruction_configs(protocols, event_type_filter);
let all_processing_params: Vec<_> = instruction_configs
.iter()
.filter(|(disc, _)| {
// Use SIMD-optimized data validation and discriminator matching
@@ -972,18 +850,15 @@ impl EventParser {
return Ok(());
}
// 使用缓存构建账户公钥列表,避免重复分配 (只需构建一次)
let account_pubkeys = {
let mut cache_guard = self.account_cache.lock();
cache_guard.build_account_pubkeys(&instruction.accounts, accounts).to_vec()
};
// 使用线程局部缓存构建账户公钥列表,避免重复分配 (只需构建一次)
let account_pubkeys = build_account_pubkeys_with_cache(&instruction.accounts, accounts);
// 并行处理所有 (discriminator, config) 组合
let all_results: Vec<_> = all_processing_params
.iter()
.filter_map(|(disc, config)| {
let data = &instruction.data[disc.len()..];
self.parse_instruction_event(
Self::parse_instruction_event(
config,
data,
&account_pubkeys,
@@ -1009,7 +884,7 @@ impl EventParser {
let (inner_event_result, swap_data_result) = std::thread::scope(|s| {
let inner_event_handle = s.spawn(|| {
for inner_instruction in inner_instructions_ref.instructions.iter() {
let result = self.parse_events_from_grpc_inner_instruction(
let result = Self::parse_events_from_grpc_inner_instruction(
&inner_instruction,
signature,
slot,
@@ -1020,7 +895,7 @@ impl EventParser {
transaction_index,
&config,
);
if result.len() > 0 {
if !result.is_empty() {
return Some(result[0].clone());
}
}
@@ -1061,81 +936,90 @@ impl EventParser {
}
// 设置处理时间(使用高性能时钟)
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
event = process_event(event, bot_wallet);
event = Self::process_event(event, bot_wallet);
callback(&event);
}
Ok(())
}
fn should_handle(&self, program_id: &Pubkey) -> bool {
self.program_ids.contains(program_id)
fn should_handle(
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
program_id: &Pubkey,
) -> bool {
get_global_program_ids(protocols, event_type_filter).contains(program_id)
}
// fn supported_program_ids(&self) -> Vec<Pubkey> {
// self.program_ids.clone()
// }
}
fn process_event(mut event: UnifiedEvent, bot_wallet: Option<Pubkey>) -> UnifiedEvent {
let signature = event.metadata().signature; // Copy the signature to avoid borrowing issues
match event {
UnifiedEvent::PumpFunCreateTokenEvent(token_info) => {
add_dev_address(&signature, token_info.user);
if token_info.creator != Pubkey::default() && token_info.creator != token_info.user {
add_dev_address(&signature, token_info.creator);
fn process_event(mut event: UnifiedEvent, bot_wallet: Option<Pubkey>) -> UnifiedEvent {
let signature = event.metadata().signature; // Copy the signature to avoid borrowing issues
match event {
UnifiedEvent::PumpFunCreateTokenEvent(token_info) => {
add_dev_address(&signature, token_info.user);
if token_info.creator != Pubkey::default() && token_info.creator != token_info.user
{
add_dev_address(&signature, token_info.creator);
}
UnifiedEvent::PumpFunCreateTokenEvent(token_info)
}
UnifiedEvent::PumpFunCreateTokenEvent(token_info)
}
UnifiedEvent::PumpFunTradeEvent(mut trade_info) => {
if is_dev_address_in_signature(&signature, &trade_info.user)
|| is_dev_address_in_signature(&signature, &trade_info.creator)
{
trade_info.is_dev_create_token_trade = true;
} else if Some(trade_info.user) == bot_wallet {
trade_info.is_bot = true;
} else {
trade_info.is_dev_create_token_trade = false;
UnifiedEvent::PumpFunTradeEvent(mut trade_info) => {
if is_dev_address_in_signature(&signature, &trade_info.user)
|| is_dev_address_in_signature(&signature, &trade_info.creator)
{
trade_info.is_dev_create_token_trade = true;
} else if Some(trade_info.user) == bot_wallet {
trade_info.is_bot = true;
} else {
trade_info.is_dev_create_token_trade = false;
}
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
if trade_info.is_buy {
trade_info.sol_amount
} else {
trade_info.token_amount
};
trade_info.metadata.swap_data.as_mut().unwrap().to_amount = if trade_info.is_buy
{
trade_info.token_amount
} else {
trade_info.sol_amount
};
}
UnifiedEvent::PumpFunTradeEvent(trade_info)
}
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
if trade_info.is_buy { trade_info.sol_amount } else { trade_info.token_amount };
trade_info.metadata.swap_data.as_mut().unwrap().to_amount =
if trade_info.is_buy { trade_info.token_amount } else { trade_info.sol_amount };
UnifiedEvent::PumpSwapBuyEvent(mut trade_info) => {
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
trade_info.user_quote_amount_in;
trade_info.metadata.swap_data.as_mut().unwrap().to_amount =
trade_info.base_amount_out;
}
UnifiedEvent::PumpSwapBuyEvent(trade_info)
}
UnifiedEvent::PumpFunTradeEvent(trade_info)
}
UnifiedEvent::PumpSwapBuyEvent(mut trade_info) => {
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
trade_info.user_quote_amount_in;
trade_info.metadata.swap_data.as_mut().unwrap().to_amount =
trade_info.base_amount_out;
UnifiedEvent::PumpSwapSellEvent(mut trade_info) => {
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
trade_info.base_amount_in;
trade_info.metadata.swap_data.as_mut().unwrap().to_amount =
trade_info.user_quote_amount_out;
}
UnifiedEvent::PumpSwapSellEvent(trade_info)
}
UnifiedEvent::PumpSwapBuyEvent(trade_info)
}
UnifiedEvent::PumpSwapSellEvent(mut trade_info) => {
if trade_info.metadata.swap_data.is_some() {
trade_info.metadata.swap_data.as_mut().unwrap().from_amount =
trade_info.base_amount_in;
trade_info.metadata.swap_data.as_mut().unwrap().to_amount =
trade_info.user_quote_amount_out;
UnifiedEvent::BonkPoolCreateEvent(pool_info) => {
add_bonk_dev_address(&signature, pool_info.creator);
UnifiedEvent::BonkPoolCreateEvent(pool_info)
}
UnifiedEvent::PumpSwapSellEvent(trade_info)
}
UnifiedEvent::BonkPoolCreateEvent(pool_info) => {
add_bonk_dev_address(&signature, pool_info.creator);
UnifiedEvent::BonkPoolCreateEvent(pool_info)
}
UnifiedEvent::BonkTradeEvent(mut trade_info) => {
if is_bonk_dev_address_in_signature(&signature, &trade_info.payer) {
trade_info.is_dev_create_token_trade = true;
} else if Some(trade_info.payer) == bot_wallet {
trade_info.is_bot = true;
} else {
trade_info.is_dev_create_token_trade = false;
UnifiedEvent::BonkTradeEvent(mut trade_info) => {
if is_bonk_dev_address_in_signature(&signature, &trade_info.payer) {
trade_info.is_dev_create_token_trade = true;
} else if Some(trade_info.payer) == bot_wallet {
trade_info.is_bot = true;
} else {
trade_info.is_dev_create_token_trade = false;
}
UnifiedEvent::BonkTradeEvent(trade_info)
}
UnifiedEvent::BonkTradeEvent(trade_info)
_ => event,
}
_ => event,
}
}
+5
View File
@@ -1,7 +1,12 @@
pub mod account_event_parser;
pub mod common_event_parser;
pub mod global_state;
pub mod parser_cache;
pub mod traits;
pub use traits::UnifiedEvent;
pub use parser_cache::{
AccountEventParseConfig, AccountEventParserFn, GenericEventParseConfig,
InnerInstructionEventParser, InstructionEventParser, get_account_configs,
};
pub mod event_parser;
@@ -0,0 +1,604 @@
//! # 事件解析器配置缓存模块
//!
//! 本模块统一管理所有事件解析器的配置和缓存,包括:
//! - 指令事件解析器(Instruction Event Parser
//! - 账户事件解析器(Account Event Parser
//! - 高性能缓存工具
//!
//! ## 设计目标
//! - **统一配置管理**:所有协议的解析器配置集中管理
//! - **高性能缓存**:避免重复初始化和内存分配
//! - **易于扩展**:添加新协议只需修改配置映射
use crate::streaming::{
event_parser::{
common::{filter::EventTypeFilter, EventMetadata, EventType, ProtocolType},
protocols::{
bonk::parser::BONK_PROGRAM_ID,
pumpfun::parser::PUMPFUN_PROGRAM_ID,
pumpswap::parser::PUMPSWAP_PROGRAM_ID,
raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID,
raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID,
raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
},
Protocol, UnifiedEvent,
},
grpc::AccountPretty,
};
use solana_sdk::pubkey::Pubkey;
use std::{
collections::HashMap,
sync::{Arc, LazyLock, OnceLock},
};
// ============================================================================
// 第一部分:指令事件解析器配置(Instruction Event Parser
// ============================================================================
/// 内联指令事件解析器函数类型
///
/// 用于解析内联指令(Inner Instruction)生成的事件
pub type InnerInstructionEventParser =
fn(data: &[u8], metadata: EventMetadata) -> Option<UnifiedEvent>;
/// 指令事件解析器函数类型
///
/// 用于解析普通指令(Instruction)生成的事件,需要账户信息
pub type InstructionEventParser =
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<UnifiedEvent>;
/// 指令事件解析器配置
///
/// 定义如何解析特定协议的指令事件
#[derive(Debug, Clone)]
pub struct GenericEventParseConfig {
/// 程序IDProgram ID
pub program_id: Pubkey,
/// 协议类型
pub protocol_type: ProtocolType,
/// 内联指令判别器(8字节)
pub inner_instruction_discriminator: &'static [u8],
/// 普通指令判别器(8字节)
pub instruction_discriminator: &'static [u8],
/// 事件类型
pub event_type: EventType,
/// 内联指令解析器
pub inner_instruction_parser: Option<InnerInstructionEventParser>,
/// 普通指令解析器
pub instruction_parser: Option<InstructionEventParser>,
/// 是否需要内联指令数据
pub requires_inner_instruction: bool,
}
/// 全局指令事件解析器注册表
///
/// 存储所有协议的指令解析器配置,启动时初始化一次
pub static EVENT_PARSERS: LazyLock<HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])>> =
LazyLock::new(|| {
let mut parsers = HashMap::with_capacity(6);
// 注册各协议的解析器配置
parsers.insert(
Protocol::PumpSwap,
(
PUMPSWAP_PROGRAM_ID,
crate::streaming::event_parser::protocols::pumpswap::parser::CONFIGS,
),
);
parsers.insert(
Protocol::PumpFun,
(
PUMPFUN_PROGRAM_ID,
crate::streaming::event_parser::protocols::pumpfun::parser::CONFIGS,
),
);
parsers.insert(
Protocol::Bonk,
(BONK_PROGRAM_ID, crate::streaming::event_parser::protocols::bonk::parser::CONFIGS),
);
parsers.insert(
Protocol::RaydiumCpmm,
(
RAYDIUM_CPMM_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_cpmm::parser::CONFIGS,
),
);
parsers.insert(
Protocol::RaydiumClmm,
(
RAYDIUM_CLMM_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_clmm::parser::CONFIGS,
),
);
parsers.insert(
Protocol::RaydiumAmmV4,
(
RAYDIUM_AMM_V4_PROGRAM_ID,
crate::streaming::event_parser::protocols::raydium_amm_v4::parser::CONFIGS,
),
);
parsers
});
// ----------------------------------------------------------------------------
// 指令解析器缓存工具
// ----------------------------------------------------------------------------
/// 缓存键:用于标识不同的协议和过滤器组合
///
/// 通过对协议和事件类型排序,确保相同组合生成相同的哈希键
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CacheKey {
/// 协议列表(已排序)
pub protocols: Vec<Protocol>,
/// 事件类型过滤器(可选,已排序)
pub event_types: Option<Vec<EventType>>,
}
impl CacheKey {
/// 创建新的缓存键
///
/// # 参数
/// - `protocols`: 协议列表
/// - `filter`: 事件类型过滤器
///
/// # 优化
/// 使用 `sort_by_cached_key` 避免重复调用 `format!`
pub fn new(mut protocols: Vec<Protocol>, filter: Option<&EventTypeFilter>) -> Self {
// 排序协议列表,确保相同协议组合生成相同的key
protocols.sort_by_cached_key(|p| format!("{:?}", p));
let event_types = filter.map(|f| {
let mut types = f.include.clone();
types.sort_by_cached_key(|t| format!("{:?}", t));
types
});
Self { protocols, event_types }
}
}
/// 全局程序ID缓存(使用读写锁保护)
static GLOBAL_PROGRAM_IDS_CACHE: LazyLock<
parking_lot::RwLock<HashMap<CacheKey, Arc<Vec<Pubkey>>>>,
> = LazyLock::new(|| parking_lot::RwLock::new(HashMap::new()));
/// 全局指令配置缓存(使用读写锁保护)
static GLOBAL_INSTRUCTION_CONFIGS_CACHE: LazyLock<
parking_lot::RwLock<HashMap<CacheKey, Arc<HashMap<Vec<u8>, Vec<GenericEventParseConfig>>>>>,
> = LazyLock::new(|| parking_lot::RwLock::new(HashMap::new()));
/// 获取指定协议的程序ID列表
///
/// # 参数
/// - `protocols`: 协议列表
/// - `filter`: 事件类型过滤器(可选)
///
/// # 返回
/// Arc包装的程序ID向量,支持零成本共享
///
/// # 缓存策略
/// - 快速路径:从缓存读取(读锁)
/// - 慢速路径:构建并缓存(写锁)
pub fn get_global_program_ids(
protocols: &[Protocol],
filter: Option<&EventTypeFilter>,
) -> Arc<Vec<Pubkey>> {
let cache_key = CacheKey::new(protocols.to_vec(), filter);
// 快速路径:尝试读取缓存
{
let cache = GLOBAL_PROGRAM_IDS_CACHE.read();
if let Some(program_ids) = cache.get(&cache_key) {
return program_ids.clone();
}
}
// 慢速路径:构建并缓存
let mut program_ids = Vec::with_capacity(protocols.len());
for protocol in protocols {
if let Some(parse) = EVENT_PARSERS.get(protocol) {
program_ids.push(parse.0);
}
}
let program_ids = Arc::new(program_ids);
// 缓存结果(写锁)
GLOBAL_PROGRAM_IDS_CACHE.write().insert(cache_key, program_ids.clone());
program_ids
}
/// 获取指定协议的指令配置
///
/// # 参数
/// - `protocols`: 协议列表
/// - `filter`: 事件类型过滤器(可选)
///
/// # 返回
/// Arc包装的指令配置映射表,按判别器(Discriminator)索引
///
/// # 缓存策略
/// - 快速路径:从缓存读取(读锁)
/// - 慢速路径:构建并缓存(写锁)
pub fn get_global_instruction_configs(
protocols: &[Protocol],
filter: Option<&EventTypeFilter>,
) -> Arc<HashMap<Vec<u8>, Vec<GenericEventParseConfig>>> {
let cache_key = CacheKey::new(protocols.to_vec(), filter);
// 快速路径:尝试读取缓存
{
let cache = GLOBAL_INSTRUCTION_CONFIGS_CACHE.read();
if let Some(configs) = cache.get(&cache_key) {
return configs.clone();
}
}
// 慢速路径:构建并缓存
let mut instruction_configs = HashMap::with_capacity(protocols.len() * 4);
for protocol in protocols {
if let Some(parse) = EVENT_PARSERS.get(protocol) {
parse
.1
.iter()
.filter(|config| {
filter.as_ref().map(|f| f.include.contains(&config.event_type)).unwrap_or(true)
})
.for_each(|config| {
instruction_configs
.entry(config.instruction_discriminator.to_vec())
.or_insert_with(Vec::new)
.push(config.clone());
});
}
}
let instruction_configs = Arc::new(instruction_configs);
// 缓存结果(写锁)
GLOBAL_INSTRUCTION_CONFIGS_CACHE.write().insert(cache_key, instruction_configs.clone());
instruction_configs
}
// ============================================================================
// 第二部分:账户公钥缓存工具(Account Pubkey Cache
// ============================================================================
/// 高性能账户公钥缓存
///
/// 通过重用内存避免重复Vec分配,提升性能
#[derive(Debug)]
pub struct AccountPubkeyCache {
/// 预分配的账户公钥向量
cache: Vec<Pubkey>,
}
impl AccountPubkeyCache {
/// 创建新的账户公钥缓存
///
/// 预分配32个位置,覆盖大多数交易场景
pub fn new() -> Self {
Self {
cache: Vec::with_capacity(32),
}
}
/// 从指令账户索引构建账户公钥向量
///
/// # 参数
/// - `instruction_accounts`: 指令账户索引列表
/// - `all_accounts`: 所有账户公钥列表
///
/// # 返回
/// 账户公钥切片引用
///
/// # 性能优化
/// - 重用内部缓存,避免重新分配
/// - 仅在必要时扩容
#[inline]
pub fn build_account_pubkeys(
&mut self,
instruction_accounts: &[u8],
all_accounts: &[Pubkey],
) -> &[Pubkey] {
self.cache.clear();
// 确保容量足够,避免动态扩容
if self.cache.capacity() < instruction_accounts.len() {
self.cache.reserve(instruction_accounts.len() - self.cache.capacity());
}
// 快速填充账户公钥(带边界检查)
for &idx in instruction_accounts.iter() {
if (idx as usize) < all_accounts.len() {
self.cache.push(all_accounts[idx as usize]);
}
}
&self.cache
}
}
impl Default for AccountPubkeyCache {
fn default() -> Self {
Self::new()
}
}
/// 线程局部账户公钥缓存
///
/// 每个线程独立维护一个缓存实例,避免锁竞争
thread_local! {
static THREAD_LOCAL_ACCOUNT_CACHE: std::cell::RefCell<AccountPubkeyCache> =
std::cell::RefCell::new(AccountPubkeyCache::new());
}
/// 从线程局部缓存构建账户公钥列表
///
/// # 参数
/// - `instruction_accounts`: 指令账户索引列表
/// - `all_accounts`: 所有账户公钥列表
///
/// # 返回
/// 账户公钥向量
///
/// # 线程安全
/// 使用线程局部存储,每个线程独立缓存
#[inline]
pub fn build_account_pubkeys_with_cache(
instruction_accounts: &[u8],
all_accounts: &[Pubkey],
) -> Vec<Pubkey> {
THREAD_LOCAL_ACCOUNT_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
cache.build_account_pubkeys(instruction_accounts, all_accounts).to_vec()
})
}
// ============================================================================
// 第三部分:账户事件解析器配置(Account Event Parser
// ============================================================================
/// 账户事件解析器函数类型
///
/// 用于解析账户状态变更生成的事件
pub type AccountEventParserFn =
fn(account: &AccountPretty, metadata: EventMetadata) -> Option<UnifiedEvent>;
/// 账户事件解析器配置
///
/// 定义如何解析特定协议的账户事件
#[derive(Debug, Clone)]
pub struct AccountEventParseConfig {
/// 程序IDProgram ID
pub program_id: Pubkey,
/// 协议类型
pub protocol_type: ProtocolType,
/// 事件类型
pub event_type: EventType,
/// 账户判别器(Account Discriminator
pub account_discriminator: &'static [u8],
/// 账户解析器函数
pub account_parser: AccountEventParserFn,
}
/// 协议账户配置缓存
///
/// 存储各协议的账户解析器配置
static PROTOCOL_CONFIGS_CACHE: OnceLock<HashMap<Protocol, Vec<AccountEventParseConfig>>> =
OnceLock::new();
/// Nonce 账户配置缓存
static NONCE_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
/// 通用 Token 账户配置缓存
static COMMON_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
/// 获取账户解析器配置列表
///
/// # 参数
/// - `protocols`: 协议列表
/// - `event_type_filter`: 事件类型过滤器(可选)
/// - `nonce_parser`: Nonce账户解析器
/// - `token_parser`: Token账户解析器
///
/// # 返回
/// 账户解析器配置向量
///
/// # 配置组成
/// 1. 协议特定配置(根据protocols参数)
/// 2. Nonce账户配置(通用)
/// 3. Token账户配置(通用,作为兜底)
///
/// # 示例
/// ```ignore
/// let configs = get_account_configs(
/// &[Protocol::PumpFun, Protocol::Bonk],
/// None,
/// parse_nonce_account,
/// parse_token_account,
/// );
/// ```
pub fn get_account_configs(
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
nonce_parser: AccountEventParserFn,
token_parser: AccountEventParserFn,
) -> Vec<AccountEventParseConfig> {
// 初始化协议配置缓存(仅在首次调用时执行)
let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| {
let mut map = HashMap::new();
// PumpSwap 协议
map.insert(Protocol::PumpSwap, vec![
AccountEventParseConfig {
program_id: PUMPSWAP_PROGRAM_ID,
protocol_type: ProtocolType::PumpSwap,
event_type: EventType::AccountPumpSwapGlobalConfig,
account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::GLOBAL_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpswap::types::global_config_parser,
},
AccountEventParseConfig {
program_id: PUMPSWAP_PROGRAM_ID,
protocol_type: ProtocolType::PumpSwap,
event_type: EventType::AccountPumpSwapPool,
account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::POOL_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpswap::types::pool_parser,
},
]);
// PumpFun 协议
map.insert(Protocol::PumpFun, vec![
AccountEventParseConfig {
program_id: PUMPFUN_PROGRAM_ID,
protocol_type: ProtocolType::PumpFun,
event_type: EventType::AccountPumpFunBondingCurve,
account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::BONDING_CURVE_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser,
},
AccountEventParseConfig {
program_id: PUMPFUN_PROGRAM_ID,
protocol_type: ProtocolType::PumpFun,
event_type: EventType::AccountPumpFunGlobal,
account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::GLOBAL_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::pumpfun::types::global_parser,
},
]);
// Bonk 协议
map.insert(Protocol::Bonk, vec![
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkPoolState,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::POOL_STATE_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::pool_state_parser,
},
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkGlobalConfig,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::GLOBAL_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::global_config_parser,
},
AccountEventParseConfig {
program_id: BONK_PROGRAM_ID,
protocol_type: ProtocolType::Bonk,
event_type: EventType::AccountBonkPlatformConfig,
account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::PLATFORM_CONFIG_ACCOUNT,
account_parser: crate::streaming::event_parser::protocols::bonk::types::platform_config_parser,
},
]);
// Raydium CPMM 协议
map.insert(Protocol::RaydiumCpmm, vec![
AccountEventParseConfig {
program_id: RAYDIUM_CPMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumCpmm,
event_type: EventType::AccountRaydiumCpmmAmmConfig,
account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::AMM_CONFIG,
account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::amm_config_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CPMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumCpmm,
event_type: EventType::AccountRaydiumCpmmPoolState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::POOL_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::pool_state_parser,
},
]);
// Raydium CLMM 协议
map.insert(Protocol::RaydiumClmm, vec![
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmAmmConfig,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::AMM_CONFIG,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::amm_config_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmPoolState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::POOL_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::pool_state_parser,
},
AccountEventParseConfig {
program_id: RAYDIUM_CLMM_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumClmm,
event_type: EventType::AccountRaydiumClmmTickArrayState,
account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::TICK_ARRAY_STATE,
account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_state_parser,
},
]);
// Raydium AMM V4 协议
map.insert(Protocol::RaydiumAmmV4, vec![
AccountEventParseConfig {
program_id: RAYDIUM_AMM_V4_PROGRAM_ID,
protocol_type: ProtocolType::RaydiumAmmV4,
event_type: EventType::AccountRaydiumAmmV4AmmInfo,
account_discriminator: crate::streaming::event_parser::protocols::raydium_amm_v4::discriminators::AMM_INFO,
account_parser: crate::streaming::event_parser::protocols::raydium_amm_v4::types::amm_info_parser,
},
]);
map
});
// 构建配置列表
let mut configs = Vec::new();
let empty_vec = Vec::new();
// 预估容量(大多数协议有2-3个配置)
let estimated_capacity = protocols.len() * 3;
configs.reserve(estimated_capacity);
// 1. 添加协议特定配置
for protocol in protocols {
let protocol_configs = protocols_map.get(protocol).unwrap_or(&empty_vec);
if event_type_filter.is_none() {
// 无过滤器:添加所有配置
configs.extend(protocol_configs.iter().cloned());
} else {
// 有过滤器:仅添加匹配的配置
let filter = event_type_filter.unwrap();
configs.extend(
protocol_configs
.iter()
.filter(|config| filter.include.contains(&config.event_type))
.cloned(),
);
}
}
// 2. 添加 Nonce 账户配置(通用配置)
if event_type_filter.is_none()
|| event_type_filter.unwrap().include.contains(&EventType::NonceAccount)
{
let nonce_config = NONCE_CONFIG.get_or_init(|| AccountEventParseConfig {
program_id: Pubkey::default(),
protocol_type: ProtocolType::Common,
event_type: EventType::NonceAccount,
account_discriminator: &[1, 0, 0, 0, 1, 0, 0, 0],
account_parser: nonce_parser,
});
configs.push(nonce_config.clone());
}
// 3. 添加通用 Token 账户配置(兜底配置,始终添加)
let common_config = COMMON_CONFIG.get_or_init(|| AccountEventParseConfig {
program_id: Pubkey::default(),
protocol_type: ProtocolType::Common,
event_type: EventType::TokenAccount,
account_discriminator: &[],
account_parser: token_parser,
});
configs.push(common_config.clone());
configs
}
@@ -2,7 +2,7 @@ use solana_sdk::pubkey::Pubkey;
use crate::streaming::event_parser::{
common::{utils::*, EventMetadata, EventType, ProtocolType},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::bonk::{
bonk_pool_create_event_log_decode, bonk_trade_event_log_decode, discriminators, AmmFeeOn,
BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent, BonkPoolCreateEvent, BonkTradeEvent,
@@ -1,6 +1,6 @@
use crate::streaming::event_parser::{
common::{EventMetadata, EventType, ProtocolType},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::pumpfun::{
discriminators, pumpfun_create_token_event_log_decode, pumpfun_migrate_event_log_decode,
pumpfun_trade_event_log_decode, PumpFunCreateTokenEvent, PumpFunMigrateEvent,
@@ -1,6 +1,6 @@
use crate::streaming::event_parser::{
common::{read_u64_le, EventMetadata, EventType, ProtocolType},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::pumpswap::{
discriminators, pump_swap_buy_event_log_decode, pump_swap_create_pool_event_log_decode,
pump_swap_deposit_event_log_decode, pump_swap_sell_event_log_decode,
@@ -1,6 +1,6 @@
use crate::streaming::event_parser::{
common::{read_u64_le, EventMetadata, EventType, ProtocolType},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::raydium_amm_v4::{
discriminators, RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event,
RaydiumAmmV4SwapEvent, RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent,
@@ -3,7 +3,7 @@ use crate::streaming::event_parser::{
read_i32_le, read_option_bool, read_u128_le, read_u64_le, read_u8_le, EventMetadata,
EventType, ProtocolType,
},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::raydium_clmm::{
discriminators, RaydiumClmmClosePositionEvent, RaydiumClmmCreatePoolEvent,
RaydiumClmmDecreaseLiquidityV2Event, RaydiumClmmIncreaseLiquidityV2Event,
@@ -2,7 +2,7 @@ use solana_sdk::pubkey::Pubkey;
use crate::streaming::event_parser::{
common::{read_u64_le, EventMetadata, EventType, ProtocolType},
core::event_parser::GenericEventParseConfig,
core::GenericEventParseConfig,
protocols::raydium_cpmm::{
discriminators, RaydiumCpmmDepositEvent, RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent,
RaydiumCpmmWithdrawEvent,
+14 -18
View File
@@ -5,7 +5,7 @@ use solana_sdk::pubkey::Pubkey;
use crate::common::AnyResult;
use crate::protos::shredstream::SubscribeEntriesRequest;
use crate::streaming::common::{EventProcessor, SubscriptionHandle};
use crate::streaming::common::{process_shred_transaction, SubscriptionHandle};
use crate::streaming::event_parser::common::filter::EventTypeFilter;
use crate::streaming::event_parser::common::high_performance_clock::get_high_perf_clock;
use crate::streaming::event_parser::{Protocol, UnifiedEvent};
@@ -37,20 +37,14 @@ impl ShredStreamGrpc {
metrics_handle = MetricsManager::global().start_auto_monitoring().await;
}
// 创建事件处理器
let mut event_processor = EventProcessor::new(self.config.clone());
event_processor.set_protocols_and_event_type_filter(
super::common::EventSource::Shred,
protocols,
event_type_filter,
Some(Arc::new(callback)),
);
// 启动流处理
let mut client = (*self.shredstream_client).clone();
let request = tonic::Request::new(SubscribeEntriesRequest {});
let mut stream = client.subscribe_entries(request).await?.into_inner();
let event_processor_clone = event_processor.clone();
// Wrap callback once before the async block
let callback = Arc::new(callback);
let stream_task = tokio::spawn(async move {
while let Some(message) = stream.next().await {
match message {
@@ -64,13 +58,15 @@ impl ShredStreamGrpc {
msg.slot,
get_high_perf_clock(),
);
// 直接处理,背压控制在 EventProcessor 内部处理
if let Err(e) = event_processor_clone
.process_shred_transaction(
transaction_with_slot,
bot_wallet,
)
.await
// Process transaction - clone Arc and Vec for each call
if let Err(e) = process_shred_transaction(
transaction_with_slot,
&protocols,
event_type_filter.as_ref(),
callback.clone(),
bot_wallet,
)
.await
{
error!("Error handling message: {e:?}");
}
+29 -31
View File
@@ -1,6 +1,7 @@
use crate::common::AnyResult;
use crate::streaming::common::{
EventProcessor, MetricsManager, PerformanceMetrics, StreamClientConfig, SubscriptionHandle,
process_grpc_transaction, MetricsManager, PerformanceMetrics, StreamClientConfig,
SubscriptionHandle,
};
use crate::streaming::event_parser::common::filter::EventTypeFilter;
use crate::streaming::event_parser::{Protocol, UnifiedEvent};
@@ -41,7 +42,6 @@ pub struct YellowstoneGrpc {
pub x_token: Option<String>,
pub config: StreamClientConfig,
pub subscription_manager: SubscriptionManager,
pub event_processor: EventProcessor,
pub subscription_handle: Arc<Mutex<Option<SubscriptionHandle>>>,
// Dynamic subscription management fields
pub active_subscription: Arc<AtomicBool>,
@@ -67,14 +67,12 @@ impl YellowstoneGrpc {
let subscription_manager =
SubscriptionManager::new(endpoint.clone(), x_token.clone(), config.clone());
MetricsManager::init(config.enable_metrics);
let event_processor = EventProcessor::new(config.clone());
Ok(Self {
endpoint,
x_token,
config,
subscription_manager,
event_processor,
subscription_handle: Arc::new(Mutex::new(None)),
active_subscription: Arc::new(AtomicBool::new(false)),
control_tx: Arc::new(tokio::sync::Mutex::new(None)),
@@ -179,14 +177,9 @@ impl YellowstoneGrpc {
let (control_tx, mut control_rx) = mpsc::channel(100);
*self.control_tx.lock().await = Some(control_tx);
// 启动流处理任务
let mut event_processor = self.event_processor.clone();
event_processor.set_protocols_and_event_type_filter(
super::common::EventSource::Grpc,
protocols,
event_type_filter,
Some(Arc::new(callback)),
);
// Wrap callback once before the async block
let callback = Arc::new(callback);
let stream_handle = tokio::spawn(async move {
loop {
tokio::select! {
@@ -198,12 +191,14 @@ impl YellowstoneGrpc {
Some(UpdateOneof::Account(account)) => {
let account_pretty = factory::create_account_pretty_pooled(account);
log::debug!("Received account: {:?}", account_pretty);
if let Err(e) = event_processor
.process_grpc_transaction(
EventPretty::Account(account_pretty),
bot_wallet,
)
.await
if let Err(e) = process_grpc_transaction(
EventPretty::Account(account_pretty),
&protocols,
event_type_filter.as_ref(),
callback.clone(),
bot_wallet,
)
.await
{
error!("Error processing account event: {e:?}");
}
@@ -211,12 +206,14 @@ impl YellowstoneGrpc {
Some(UpdateOneof::BlockMeta(sut)) => {
let block_meta_pretty = factory::create_block_meta_pretty_pooled(sut, created_at);
log::debug!("Received block meta: {:?}", block_meta_pretty);
if let Err(e) = event_processor
.process_grpc_transaction(
EventPretty::BlockMeta(block_meta_pretty),
bot_wallet,
)
.await
if let Err(e) = process_grpc_transaction(
EventPretty::BlockMeta(block_meta_pretty),
&protocols,
event_type_filter.as_ref(),
callback.clone(),
bot_wallet,
)
.await
{
error!("Error processing block meta event: {e:?}");
}
@@ -228,12 +225,14 @@ impl YellowstoneGrpc {
transaction_pretty.signature,
transaction_pretty.slot
);
if let Err(e) = event_processor
.process_grpc_transaction(
EventPretty::Transaction(transaction_pretty),
bot_wallet,
)
.await
if let Err(e) = process_grpc_transaction(
EventPretty::Transaction(transaction_pretty),
&protocols,
event_type_filter.as_ref(),
callback.clone(),
bot_wallet,
)
.await
{
error!("Error processing transaction event: {e:?}");
}
@@ -352,7 +351,6 @@ impl Clone for YellowstoneGrpc {
x_token: self.x_token.clone(),
config: self.config.clone(),
subscription_manager: self.subscription_manager.clone(),
event_processor: self.event_processor.clone(),
subscription_handle: self.subscription_handle.clone(), // 共享同一个 Arc<Mutex<>>
active_subscription: self.active_subscription.clone(),
control_tx: self.control_tx.clone(),