Merge pull request #57 from Estereg/dev

feat(protocols): Fix account structures to match IDL specifications
This commit is contained in:
sgxiang
2025-12-22 21:34:50 +08:00
committed by GitHub
34 changed files with 597 additions and 409 deletions
+1 -30
View File
@@ -16,24 +16,13 @@ crate-type = ["cdylib", "rlib"]
solana-sdk = "3.0.0"
solana-client = "3.1.4"
solana-program = "3.0.0"
solana-rpc-client = "3.1.4"
solana-rpc-client-api = "3.1.4"
solana-transaction-status = "3.1.4"
solana-account-decoder = "3.1.4"
solana-hash = "4.0.1"
solana-entry = "3.1.4"
solana-rpc-client-nonce-utils = "3.1.4"
solana-perf = "3.1.4"
solana-metrics = "3.1.4"
spl-associated-token-account = "8.0.0"
borsh = { version = "1.6.0", features = ["derive"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.134"
serde-big-array = "0.5.1"
futures = "0.3.31"
futures-util = "0.3.31"
base64 = "0.22.1"
rand = "0.9.2"
bincode = "1.3"
anyhow = "1.0.90"
yellowstone-grpc-client = { version = "10.2.0" }
@@ -41,37 +30,19 @@ yellowstone-grpc-proto = { version = "10.1.1" }
tokio = { version = "1.42.0", features = ["full", "rt-multi-thread"]}
tonic = { version = "0.14.2", features = ["transport"] }
rustls = { version = "0.23.35", features = ["ring"], default-features = false }
rustls-native-certs = "0.8.2"
tokio-rustls = "0.26.4"
log = "0.4.29"
chrono = "0.4.42"
regex = "1"
tracing = "0.1.43"
thiserror = "2.0.17"
async-trait = "0.1.86"
lazy_static = "1.5.0"
once_cell = "1.20.3"
dashmap = "6.1.0"
prost = "0.14.1"
prost-types = "0.14.1"
num_enum = "0.7.5"
num-derive = "0.4.2"
num-traits = "0.2.19"
hex = "0.4.3"
bytemuck = { version = "1.24.0" }
arrayref = "0.3.6"
borsh-derive = "1.6.0"
indicatif = "0.18.3"
maplit = "1.0.2"
env_logger = "0.11.8"
crossbeam = "0.8.4"
crossbeam-queue = "0.3.12"
parking_lot = "0.12.5"
wide = "1.1.0"
spl-token = "9.0.0"
spl-token-2022 = "10.0.0"
solana-commitment-config = { version = "3.1.0", features = ["serde"] }
tonic-prost = "0.14.2"
[dev-dependencies]
criterion = { version = "0.8.1", features = ["html_reports"] }
tonic-prost = "0.14.2"
+6 -6
View File
@@ -1,18 +1,18 @@
// 流处理相关的常量定义
// Constants related to stream processing
// 默认配置常量
// Default configuration constants
pub const DEFAULT_CONNECT_TIMEOUT: u64 = 10;
pub const DEFAULT_REQUEST_TIMEOUT: u64 = 60;
pub const DEFAULT_CHANNEL_SIZE: usize = 1000;
pub const DEFAULT_MAX_DECODING_MESSAGE_SIZE: usize = 1024 * 1024 * 10;
// 性能监控相关常量
// Performance monitoring related constants
pub const DEFAULT_METRICS_WINDOW_SECONDS: u64 = 5;
pub const DEFAULT_METRICS_PRINT_INTERVAL_SECONDS: u64 = 10;
pub const SLOW_PROCESSING_THRESHOLD_US: f64 = 3000.0;
// gRPC 延迟监控
// Solana 不存储毫秒,所以我们用500ms来校准以获得更好的近似值
// gRPC latency monitoring
// Solana doesn't store milliseconds, so we use 500ms to calibrate for better approximation
pub const SOLANA_BLOCK_TIME_ADJUSTMENT_MS: i64 = 500;
// 默认最大延迟阈值(毫秒)
// Default maximum latency threshold (milliseconds)
pub const MAX_LATENCY_THRESHOLD_MS: i64 = 1000;
+2 -2
View File
@@ -10,9 +10,9 @@ use crate::streaming::shred::TransactionWithSlot;
use solana_sdk::pubkey::Pubkey;
use std::sync::Arc;
/// 创建带 metrics 统计的 callback 包装器
/// Create callback wrapper with metrics statistics
///
/// 用于 Transaction 事件处理,在调用原始 callback 的同时更新 metrics
/// Used for Transaction event processing, updates metrics while calling the original callback
#[inline]
fn create_metrics_callback(
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
+36 -36
View File
@@ -200,7 +200,7 @@ pub struct HighPerformanceMetrics {
start_nanos: AtomicU64,
event_metrics: [AtomicEventMetrics; 3],
processing_stats: AtomicProcessingTimeStats,
// 丢弃事件指标
// Dropped events metrics
dropped_events_count: AtomicU64,
}
@@ -219,7 +219,7 @@ impl HighPerformanceMetrics {
}
}
/// 获取运行时长(秒)
/// Get uptime (seconds)
#[inline]
pub fn get_uptime_seconds(&self) -> f64 {
let now_nanos =
@@ -244,7 +244,7 @@ impl HighPerformanceMetrics {
(now_nanos - start) as f64 / 1_000_000_000.0
}
/// 获取事件指标快照
/// Get event metrics snapshot
#[inline]
pub fn get_event_metrics(&self, event_type: EventType) -> EventMetricsSnapshot {
let index = event_type.as_index();
@@ -254,19 +254,19 @@ impl HighPerformanceMetrics {
EventMetricsSnapshot { process_count, events_processed, processing_stats }
}
/// 获取处理时间统计
/// Get processing time statistics
#[inline]
pub fn get_processing_stats(&self) -> ProcessingTimeStats {
self.processing_stats.get_stats()
}
/// 获取丢弃事件计数
/// Get dropped events count
#[inline]
pub fn get_dropped_events_count(&self) -> u64 {
self.dropped_events_count.load(Ordering::Relaxed)
}
/// 更新窗口指标(后台任务调用)
/// Update window metrics (called by background task)
fn update_window_metrics(&self, event_type: EventType, window_duration_nanos: u64) {
let now_nanos =
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()
@@ -291,7 +291,7 @@ static BACKGROUND_TASK_STARTED: AtomicBool = AtomicBool::new(false);
/// Metrics enabled flag
static METRICS_ENABLED: AtomicBool = AtomicBool::new(true);
/// 高性能指标管理器 (Singleton)
/// High-performance metrics manager (Singleton)
#[derive(Clone, Copy)]
pub struct MetricsManager;
@@ -334,7 +334,7 @@ impl MetricsManager {
METRICS_ENABLED.load(Ordering::Relaxed)
}
/// 记录处理次数(非阻塞)
/// Record process count (non-blocking)
#[inline]
pub fn record_process(&self, event_type: EventType) {
if self.is_enabled() {
@@ -342,7 +342,7 @@ impl MetricsManager {
}
}
/// 记录事件处理(非阻塞)
/// Record event processing (non-blocking)
#[inline]
pub fn record_events(&self, event_type: EventType, count: u64, processing_time_us: f64) {
if !self.is_enabled() {
@@ -351,17 +351,17 @@ impl MetricsManager {
let index = event_type.as_index();
// 原子更新事件计数
// Atomically update event count
GLOBAL_METRICS.event_metrics[index].add_events_processed(count);
// 原子更新该事件类型的处理时间统计
// Atomically update processing time statistics for this event type
GLOBAL_METRICS.event_metrics[index].update_processing_stats(processing_time_us, count);
// 保持全局处理时间统计的兼容性
// Maintain compatibility with global processing time statistics
GLOBAL_METRICS.processing_stats.update(processing_time_us, count);
}
/// 记录慢处理操作
/// Record slow processing operation
#[inline]
pub fn log_slow_processing(&self, processing_time_us: f64, event_count: usize) {
if processing_time_us > SLOW_PROCESSING_THRESHOLD_US {
@@ -369,12 +369,12 @@ impl MetricsManager {
}
}
/// 检查并警告高延迟 (校准后的 gRPC latency)
/// Check and warn about high latency (calibrated gRPC latency)
/// latency = recv_time - (block_time + 500ms)
#[inline]
pub fn check_and_warn_high_latency(&self, recv_us: i64, block_time_ms: i64) {
let recv_ms = recv_us / 1000;
// 校准延迟: recv_time - (block_time + 500ms)
// Calibrate latency: recv_time - (block_time + 500ms)
let adjusted_latency_ms = recv_ms - (block_time_ms + SOLANA_BLOCK_TIME_ADJUSTMENT_MS);
if adjusted_latency_ms > MAX_LATENCY_THRESHOLD_MS {
@@ -388,38 +388,38 @@ impl MetricsManager {
}
}
/// 获取运行时长
/// Get uptime
pub fn get_uptime(&self) -> std::time::Duration {
std::time::Duration::from_secs_f64(GLOBAL_METRICS.get_uptime_seconds())
}
/// 获取事件指标
/// Get event metrics
pub fn get_event_metrics(&self, event_type: EventType) -> EventMetricsSnapshot {
GLOBAL_METRICS.get_event_metrics(event_type)
}
/// 获取处理时间统计
/// Get processing time statistics
pub fn get_processing_stats(&self) -> ProcessingTimeStats {
GLOBAL_METRICS.get_processing_stats()
}
/// 获取丢弃事件计数
/// Get dropped events count
pub fn get_dropped_events_count(&self) -> u64 {
GLOBAL_METRICS.get_dropped_events_count()
}
/// 打印性能指标(非阻塞)
/// Print performance metrics (non-blocking)
pub fn print_metrics(&self) {
println!("\n📊 Performance Metrics");
println!(" Run Time: {:?}", self.get_uptime());
// 打印丢弃事件指标
// Print dropped events metrics
let dropped_count = self.get_dropped_events_count();
if dropped_count > 0 {
println!("\n⚠️ Dropped Events: {}", dropped_count);
}
// 打印事件指标表格(包含处理时间统计)
// Print event metrics table (including processing time statistics)
println!("┌─────────────┬──────────────┬──────────────────┬─────────────┬─────────────┐");
println!("│ Event Type │ Process Count│ Events Processed │ Last(μs) │ Avg(μs) │");
println!("├─────────────┼──────────────┼──────────────────┼─────────────┼─────────────┤");
@@ -440,7 +440,7 @@ impl MetricsManager {
println!();
}
/// 启动自动性能监控任务
/// Start automatic performance monitoring task
pub async fn start_auto_monitoring(&self) -> Option<tokio::task::JoinHandle<()>> {
if !self.is_enabled() {
return None;
@@ -458,7 +458,7 @@ impl MetricsManager {
Some(handle)
}
/// 获取完整的性能指标(兼容性方法)
/// Get complete performance metrics (compatibility method)
pub fn get_metrics(&self) -> PerformanceMetrics {
PerformanceMetrics {
uptime: self.get_uptime(),
@@ -470,25 +470,25 @@ impl MetricsManager {
}
}
/// 兼容性方法 - 添加交易处理计数
/// Compatibility method - add transaction process count
#[inline]
pub fn add_tx_process_count(&self) {
self.record_process(EventType::Transaction);
}
/// 兼容性方法 - 添加账户处理计数
/// Compatibility method - add account process count
#[inline]
pub fn add_account_process_count(&self) {
self.record_process(EventType::Account);
}
/// 兼容性方法 - 添加区块元数据处理计数
/// Compatibility method - add block meta process count
#[inline]
pub fn add_block_meta_process_count(&self) {
self.record_process(EventType::BlockMeta);
}
/// 兼容性方法 - 更新指标
/// Compatibility method - update metrics
#[inline]
pub fn update_metrics(
&self,
@@ -500,7 +500,7 @@ impl MetricsManager {
self.log_slow_processing(processing_time_us, events_processed as usize);
}
/// 更新指标并检查延迟
/// Update metrics and check latency
#[inline]
pub fn update_metrics_with_latency(
&self,
@@ -514,39 +514,39 @@ impl MetricsManager {
self.update_metrics(event_type, events_processed, processing_time_us);
}
/// 增加丢弃事件计数
/// Increment dropped events count
#[inline]
pub fn increment_dropped_events(&self) {
if !self.is_enabled() {
return;
}
// 原子地增加丢弃事件计数
// Atomically increment dropped events count
let new_count = GLOBAL_METRICS.dropped_events_count.fetch_add(1, Ordering::Relaxed) + 1;
// 每丢弃1000个事件记录一次警告日志
// Log warning every 1000 dropped events
if new_count % 1000 == 0 {
log::debug!("Dropped events count reached: {}", new_count);
}
}
/// 批量增加丢弃事件计数
/// Batch increment dropped events count
#[inline]
pub fn increment_dropped_events_by(&self, count: u64) {
if !self.is_enabled() || count == 0 {
return;
}
// 原子地增加丢弃事件计数
// Atomically increment dropped events count
let new_count =
GLOBAL_METRICS.dropped_events_count.fetch_add(count, Ordering::Relaxed) + count;
// 记录批量丢弃事件的日志
// Log batch dropped events
if count > 1 {
log::debug!("Dropped batch of {} events, total dropped: {}", count, new_count);
}
// 每丢弃1000个事件记录一次警告日志
// Log warning every 1000 dropped events
if new_count % 1000 == 0 || (new_count / 1000) != ((new_count - count) / 1000) {
log::debug!("Dropped events count reached: {}", new_count);
}
+2 -2
View File
@@ -1,4 +1,4 @@
// 公用模块 - 包含流处理相关的通用功能
// Common modules - contains common functionality related to stream processing
pub mod config;
pub mod metrics;
pub mod constants;
@@ -6,7 +6,7 @@ pub mod subscription;
pub mod event_processor;
pub mod simd_utils;
// 重新导出主要类型
// Re-export main types
pub use config::*;
pub use metrics::*;
pub use constants::*;
@@ -1,33 +1,33 @@
use std::fmt::Debug;
use std::time::Instant;
/// 高性能时钟管理器,减少系统调用开销并最小化延迟
/// High-performance clock manager, reduces system call overhead and minimizes latency
#[derive(Debug)]
pub struct HighPerformanceClock {
/// 基准时间点(程序启动时的单调时钟时间)
/// Base time point (monotonic clock time at program startup)
base_instant: Instant,
/// 基准时间点对应的UTC时间戳(微秒)
/// UTC timestamp (microseconds) corresponding to base time point
base_timestamp_us: i64,
/// 上次校准时间(用于检测是否需要重新校准)
/// Last calibration time (used to detect if recalibration is needed)
last_calibration: Instant,
/// 校准间隔(秒)
/// Calibration interval (seconds)
calibration_interval_secs: u64,
}
impl HighPerformanceClock {
/// 创建新的高性能时钟
/// Create new high-performance clock
pub fn new() -> Self {
Self::new_with_calibration_interval(300) // 默认5分钟校准一次
Self::new_with_calibration_interval(300) // Default: calibrate every 5 minutes
}
/// 创建带自定义校准间隔的高性能时钟
/// Create high-performance clock with custom calibration interval
pub fn new_with_calibration_interval(calibration_interval_secs: u64) -> Self {
// 通过多次采样来减少初始化误差
// Reduce initialization error through multiple samples
let mut best_offset = i64::MAX;
let mut best_instant = Instant::now();
let mut best_timestamp = chrono::Utc::now().timestamp_micros();
// 进行3次采样,选择延迟最小的
// Perform 3 samples, choose the one with minimum latency
for _ in 0..3 {
let instant_before = Instant::now();
let timestamp = chrono::Utc::now().timestamp_micros();
@@ -50,35 +50,35 @@ impl HighPerformanceClock {
}
}
/// 获取当前时间戳(微秒),使用单调时钟计算,避免系统调用
/// Get current timestamp (microseconds), calculated using monotonic clock to avoid system calls
#[inline(always)]
pub fn now_micros(&self) -> i64 {
let elapsed = self.base_instant.elapsed();
self.base_timestamp_us + elapsed.as_micros() as i64
}
/// 获取高精度当前时间戳(微秒),在必要时进行校准
/// Get high-precision current timestamp (microseconds), calibrate when necessary
pub fn now_micros_with_calibration(&mut self) -> i64 {
// 检查是否需要重新校准
// Check if recalibration is needed
if self.last_calibration.elapsed().as_secs() >= self.calibration_interval_secs {
self.recalibrate();
}
self.now_micros()
}
/// 重新校准时钟,减少累积漂移
/// Recalibrate clock to reduce accumulated drift
fn recalibrate(&mut self) {
let current_monotonic = Instant::now();
let current_utc = chrono::Utc::now().timestamp_micros();
// 计算预期的UTC时间戳(基于单调时钟)
// Calculate expected UTC timestamp (based on monotonic clock)
let expected_utc = self.base_timestamp_us
+ current_monotonic.duration_since(self.base_instant).as_micros() as i64;
// 计算漂移量
// Calculate drift amount
let drift_us = current_utc - expected_utc;
// 如果漂移超过1毫秒,进行校准
// If drift exceeds 1 millisecond, perform calibration
if drift_us.abs() > 1000 {
self.base_instant = current_monotonic;
self.base_timestamp_us = current_utc;
@@ -87,20 +87,20 @@ impl HighPerformanceClock {
self.last_calibration = current_monotonic;
}
/// 计算从指定时间戳到现在的消耗时间(微秒)
/// Calculate elapsed time (microseconds) from specified timestamp to now
#[inline(always)]
pub fn elapsed_micros_since(&self, start_timestamp_us: i64) -> i64 {
self.now_micros() - start_timestamp_us
}
/// 获取高精度纳秒时间戳
/// Get high-precision nanosecond timestamp
#[inline(always)]
pub fn now_nanos(&self) -> i128 {
let elapsed = self.base_instant.elapsed();
(self.base_timestamp_us as i128 * 1000) + elapsed.as_nanos() as i128
}
/// 重置时钟(强制重新初始化)
/// Reset clock (force re-initialization)
pub fn reset(&mut self) {
*self = Self::new_with_calibration_interval(self.calibration_interval_secs);
}
@@ -112,11 +112,11 @@ impl Default for HighPerformanceClock {
}
}
/// 全局高性能时钟实例
/// Global high-performance clock instance
static HIGH_PERF_CLOCK: once_cell::sync::OnceCell<HighPerformanceClock> =
once_cell::sync::OnceCell::new();
/// 获取全局高性能时钟实例(最简单的实现)
/// Get global high-performance clock instance (simplest implementation)
#[inline(always)]
pub fn get_high_perf_clock() -> i64 {
let clock = HIGH_PERF_CLOCK.get_or_init(HighPerformanceClock::new);
+18 -8
View File
@@ -30,7 +30,7 @@ impl EventMetadataPool {
}
pub fn release(&self, metadata: EventMetadata) {
// 如果队列已满,push 会失败,但不会阻塞
// If queue is full, push will fail but won't block
let _ = self.pool.push(metadata);
}
}
@@ -63,6 +63,7 @@ pub enum EventType {
// PumpSwap events
#[default]
PumpSwapBuy,
PumpSwapBuyExactQuoteIn,
PumpSwapSell,
PumpSwapCreatePool,
PumpSwapDeposit,
@@ -168,6 +169,7 @@ impl fmt::Display for EventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EventType::PumpSwapBuy => write!(f, "PumpSwapBuy"),
EventType::PumpSwapBuyExactQuoteIn => write!(f, "PumpSwapBuyExactQuoteIn"),
EventType::PumpSwapSell => write!(f, "PumpSwapSell"),
EventType::PumpSwapCreatePool => write!(f, "PumpSwapCreatePool"),
EventType::PumpSwapDeposit => write!(f, "PumpSwapDeposit"),
@@ -301,7 +303,7 @@ pub struct SwapData {
pub struct EventMetadata {
pub signature: Signature,
pub slot: u64,
pub transaction_index: Option<u64>, // 新增:交易在slot中的索引
pub transaction_index: Option<u64>, // New: transaction index within the slot
pub block_time: i64,
pub block_time_ms: i64,
pub recv_us: i64,
@@ -380,7 +382,7 @@ pub fn parse_swap_data_from_next_instructions(
description: None,
};
// 先根据 event 取出关键信息
// First extract key information from event
// let mut user: Option<Pubkey> = None;
let mut from_mint: Option<Pubkey> = None;
let mut to_mint: Option<Pubkey> = None;
@@ -407,6 +409,10 @@ pub fn parse_swap_data_from_next_instructions(
swap_data.from_mint = e.quote_mint;
swap_data.to_mint = e.base_mint;
}
DexEvent::PumpSwapBuyExactQuoteInEvent(e) => {
swap_data.from_mint = e.quote_mint;
swap_data.to_mint = e.base_mint;
}
DexEvent::PumpSwapSellEvent(e) => {
swap_data.from_mint = e.base_mint;
swap_data.to_mint = e.quote_mint;
@@ -457,7 +463,7 @@ pub fn parse_swap_data_from_next_instructions(
let to_mint = to_mint.unwrap_or_default();
let from_mint = from_mint.unwrap_or_default();
// 单次循环完成提取和判断
// Single loop to complete extraction and validation
for instruction in inner_instruction.instructions.iter().skip((current_index + 1) as usize) {
let compiled = &instruction.instruction;
let program_id = accounts[compiled.program_id_index as usize];
@@ -466,7 +472,7 @@ pub fn parse_swap_data_from_next_instructions(
}
let data = &compiled.data;
// 使用 SIMD 验证数据格式
// Use SIMD to validate data format
if !SimdUtils::validate_data_format(data, 8) {
continue;
}
@@ -550,7 +556,7 @@ pub fn parse_swap_data_from_next_grpc_instructions(
description: None,
};
// 先根据 event 取出关键信息
// First extract key information from event
// let mut user: Option<Pubkey> = None;
let mut from_mint: Option<Pubkey> = None;
let mut to_mint: Option<Pubkey> = None;
@@ -577,6 +583,10 @@ pub fn parse_swap_data_from_next_grpc_instructions(
swap_data.from_mint = e.quote_mint;
swap_data.to_mint = e.base_mint;
}
DexEvent::PumpSwapBuyExactQuoteInEvent(e) => {
swap_data.from_mint = e.quote_mint;
swap_data.to_mint = e.base_mint;
}
DexEvent::PumpSwapSellEvent(e) => {
swap_data.from_mint = e.base_mint;
swap_data.to_mint = e.quote_mint;
@@ -627,7 +637,7 @@ pub fn parse_swap_data_from_next_grpc_instructions(
let to_mint = to_mint.unwrap_or_default();
let from_mint = from_mint.unwrap_or_default();
// 单次循环完成提取和判断
// Single loop to complete extraction and validation
for instruction in inner_instruction.instructions.iter().skip((current_index + 1) as usize) {
let compiled = &instruction;
let program_id = accounts[compiled.program_id_index as usize];
@@ -636,7 +646,7 @@ pub fn parse_swap_data_from_next_grpc_instructions(
}
let data = &compiled.data;
// 使用 SIMD 验证数据格式
// Use SIMD to validate data format
if !SimdUtils::validate_data_format(data, 8) {
continue;
}
+10 -10
View File
@@ -1,11 +1,11 @@
use std::time::{SystemTime, UNIX_EPOCH};
/// 获取当前时间戳
/// Get current timestamp
pub fn current_timestamp() -> i64 {
SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64
}
/// 从字节数组中提取鉴别器和剩余数据
/// Extract discriminator and remaining data from byte array
pub fn extract_discriminator(length: usize, data: &[u8]) -> Option<(&[u8], &[u8])> {
if data.len() < length {
return None;
@@ -13,18 +13,18 @@ pub fn extract_discriminator(length: usize, data: &[u8]) -> Option<(&[u8], &[u8]
Some((&data[..length], &data[length..]))
}
/// 从日志中提取程序数据
/// Extract program data from log
pub fn extract_program_data(log: &str) -> Option<&str> {
const PROGRAM_DATA_PREFIX: &str = "Program data: ";
log.strip_prefix(PROGRAM_DATA_PREFIX)
}
/// 从日志中提取程序日志
/// Extract program log from log
pub fn extract_program_log<'a>(log: &'a str, prefix: &str) -> Option<&'a str> {
log.strip_prefix(prefix)
}
/// 安全地从字节数组中读取u64
/// Safely read u64 from byte array
pub fn read_u64_le(data: &[u8], offset: usize) -> Option<u64> {
if data.len() < offset + 8 {
return None;
@@ -71,7 +71,7 @@ pub fn read_option_bool(data: &[u8], offset: &mut usize) -> Option<Option<bool>>
Some(Some(value != 0))
}
/// 安全地从字节数组中读取u32
/// Safely read u32 from byte array
pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
if data.len() < offset + 4 {
return None;
@@ -80,7 +80,7 @@ pub fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
Some(u32::from_le_bytes(bytes))
}
/// 安全地从字节数组中读取u16
/// Safely read u16 from byte array
pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
if data.len() < offset + 2 {
return None;
@@ -89,17 +89,17 @@ pub fn read_u16_le(data: &[u8], offset: usize) -> Option<u16> {
Some(u16::from_le_bytes(bytes))
}
/// 安全地从字节数组中读取u8
/// Safely read u8 from byte array
pub fn read_u8(data: &[u8], offset: usize) -> Option<u8> {
data.get(offset).copied()
}
/// 验证账户索引的有效性
/// Validate account index validity
pub fn validate_account_indices(indices: &[u8], account_count: usize) -> bool {
indices.iter().all(|&idx| (idx as usize) < account_count)
}
/// 格式化公钥为短字符串
/// Format pubkey as short string
pub fn format_pubkey_short(pubkey: &solana_sdk::pubkey::Pubkey) -> String {
let s = pubkey.to_string();
if s.len() <= 8 {
@@ -14,7 +14,7 @@ use spl_token_2022::{
state::{Account as Account2022, Mint as Mint2022},
};
/// 通用账户事件
/// Generic account event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct TokenAccountEvent {
pub metadata: EventMetadata,
@@ -63,39 +63,39 @@ impl AccountEventParser {
) -> Option<DexEvent> {
use crate::streaming::event_parser::core::dispatcher::EventDispatcher;
// 1. 尝试从账户 discriminator 解析(协议特定账户)
// 1. Try to parse from account discriminator (protocol-specific accounts)
if account.data.len() >= 8 {
let discriminator = &account.data[0..8];
// 尝试识别协议类型
// Try to identify protocol type
if let Some(protocol) = EventDispatcher::match_protocol_by_program_id(&account.owner) {
// 检查是否在请求的协议列表中
// Check if in the requested protocol list
if protocols.contains(&protocol) {
// 构建临时元数据(protocol会被dispatcher设置,event_type会在parser中设置)
// Build temporary metadata (protocol will be set by dispatcher, event_type will be set by parser)
let metadata = EventMetadata {
slot: account.slot,
signature: account.signature,
protocol: ProtocolType::Common, // 会被 EventDispatcher::dispatch_account 设置
event_type: EventType::default(), // 会被具体 parser 设置
protocol: ProtocolType::Common, // Will be set by EventDispatcher::dispatch_account
event_type: EventType::default(), // Will be set by specific parser
program_id: account.owner,
recv_us: account.recv_us,
handle_us: elapsed_micros_since(account.recv_us),
..Default::default()
};
// 使用 dispatcher 解析
// Use dispatcher to parse
if let Some(event) = EventDispatcher::dispatch_account(
protocol,
discriminator,
&account,
metadata,
) {
// 应用事件类型过滤
// Apply event type filter
if let Some(filter) = event_type_filter {
if filter.include.contains(&event.metadata().event_type) {
return Some(event);
}
// 不匹配过滤器,继续尝试其他解析方式
// Doesn't match filter, continue trying other parsing methods
} else {
return Some(event);
}
@@ -104,8 +104,8 @@ impl AccountEventParser {
}
}
// 2. 尝试解析特殊账户类型(TokenNonce等)
// 这些是通用的,不属于特定协议
// 2. Try to parse special account types (Token, Nonce, etc.)
// These are generic and don't belong to specific protocols
let metadata = EventMetadata {
slot: account.slot,
signature: account.signature,
@@ -117,7 +117,7 @@ impl AccountEventParser {
..Default::default()
};
// 尝试解析 Nonce 账户
// Try to parse Nonce account
if let Some(event) = Self::parse_nonce_account_event(&account, metadata.clone()) {
if let Some(filter) = event_type_filter {
if filter.include.contains(&event.metadata().event_type) {
@@ -128,7 +128,7 @@ impl AccountEventParser {
}
}
// 尝试解析 Token 账户
// Try to parse Token account
if let Some(event) = Self::parse_token_account_event(&account, metadata) {
if let Some(filter) = event_type_filter {
if filter.include.contains(&event.metadata().event_type) {
@@ -11,21 +11,21 @@ use solana_sdk::pubkey::Pubkey;
pub const COMPUTE_BUDGET_PROGRAM_ID: Pubkey =
solana_sdk::pubkey!("ComputeBudget111111111111111111111111111111");
/// SetComputeUnitLimit 事件
/// SetComputeUnitLimit event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct SetComputeUnitLimitEvent {
#[borsh(skip)]
pub metadata: EventMetadata,
/// 请求的计算单元数量
/// Number of compute units requested
pub units: u32,
}
/// SetComputeUnitPrice 事件
/// SetComputeUnitPrice event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct SetComputeUnitPriceEvent {
#[borsh(skip)]
pub metadata: EventMetadata,
/// 每个计算单元的价格 (micro-lamports)
/// Price per compute unit (micro-lamports)
pub micro_lamports: u64,
}
@@ -43,7 +43,7 @@ impl CommonEventParser {
DexEvent::BlockMetaEvent(block_meta_event)
}
/// 解析 Compute Budget 指令
/// Parse Compute Budget instruction
pub fn parse_compute_budget_instruction(
instruction_data: &[u8],
mut metadata: EventMetadata,
@@ -52,10 +52,10 @@ impl CommonEventParser {
return None;
}
// 设置 protocol Common
// Set protocol to Common
metadata.protocol = ProtocolType::Common;
// Compute Budget 指令使用单字节判别器
// Compute Budget instructions use single-byte discriminator
match instruction_data[0] {
// SetComputeUnitLimit: discriminator = 2
2 => {
+48 -48
View File
@@ -1,11 +1,11 @@
//! 中心事件解析调度器
//! Central event parsing dispatcher
//!
//! 根据协议类型路由到对应的解析函数,替代原有的静态 CONFIGS 数组架构
//! Routes to corresponding parsing functions based on protocol type, replacing the original static CONFIGS array architecture
//!
//! ## 设计原则
//! - **单一职责**: 每个函数只负责一件事(路由、解析、合并分离)
//! - **灵活性**: 调用方可以选择是否合并,或自定义合并逻辑
//! - **可测试性**: 每个函数都可以独立测试
//! ## Design Principles
//! - **Single Responsibility**: Each function is responsible for one thing (routing, parsing, merging separated)
//! - **Flexibility**: Callers can choose whether to merge or customize merge logic
//! - **Testability**: Each function can be tested independently
use crate::streaming::event_parser::{
common::EventMetadata,
@@ -19,23 +19,23 @@ use crate::streaming::event_parser::{
};
use solana_sdk::pubkey::Pubkey;
/// 中心事件解析调度器
/// Central event parsing dispatcher
///
/// 负责将解析请求路由到对应协议的解析函数
/// Responsible for routing parsing requests to corresponding protocol parsing functions
pub struct EventDispatcher;
impl EventDispatcher {
/// 解析 instruction 事件(只解析,不合并)
/// Parse instruction event (parse only, no merging)
///
/// # 参数
/// - `protocol`: 协议类型
/// - `instruction_discriminator`: 指令判别器 (8 bytes)
/// - `instruction_data`: 指令数据
/// - `accounts`: 账户公钥列表
/// - `metadata`: 事件元数据
/// # Parameters
/// - `protocol`: Protocol type
/// - `instruction_discriminator`: Instruction discriminator (8 bytes)
/// - `instruction_data`: Instruction data
/// - `accounts`: Account public key list
/// - `metadata`: Event metadata
///
/// # 返回
/// 解析成功返回 `Some(DexEvent)`,否则返回 `None`
/// # Returns
/// Returns `Some(DexEvent)` on successful parsing, otherwise `None`
#[inline]
pub fn dispatch_instruction(
protocol: Protocol,
@@ -44,7 +44,7 @@ impl EventDispatcher {
accounts: &[Pubkey],
mut metadata: EventMetadata,
) -> Option<DexEvent> {
// 根据协议类型设置 metadata.protocol
// Set metadata.protocol based on protocol type
use crate::streaming::event_parser::common::ProtocolType;
metadata.protocol = match protocol {
Protocol::PumpFun => ProtocolType::PumpFun,
@@ -102,16 +102,16 @@ impl EventDispatcher {
}
}
/// 解析 inner instruction 事件(只解析,不合并)
/// Parse inner instruction event (parse only, no merging)
///
/// # 参数
/// - `protocol`: 协议类型
/// - `inner_instruction_discriminator`: 内联指令判别器 (16 bytes)
/// - `inner_instruction_data`: 内联指令数据
/// - `metadata`: 事件元数据
/// # Parameters
/// - `protocol`: Protocol type
/// - `inner_instruction_discriminator`: Inner instruction discriminator (16 bytes)
/// - `inner_instruction_data`: Inner instruction data
/// - `metadata`: Event metadata
///
/// # 返回
/// 解析成功返回 `Some(DexEvent)`,否则返回 `None`
/// # Returns
/// Returns `Some(DexEvent)` on successful parsing, otherwise `None`
#[inline]
pub fn dispatch_inner_instruction(
protocol: Protocol,
@@ -119,7 +119,7 @@ impl EventDispatcher {
inner_instruction_data: &[u8],
mut metadata: EventMetadata,
) -> Option<DexEvent> {
// 根据协议类型设置 metadata.protocol
// Set metadata.protocol based on protocol type
use crate::streaming::event_parser::common::ProtocolType;
metadata.protocol = match protocol {
Protocol::PumpFun => ProtocolType::PumpFun,
@@ -170,7 +170,7 @@ impl EventDispatcher {
}
}
/// 通过 program_id 匹配协议类型
/// Match protocol type by program_id
#[inline]
pub fn match_protocol_by_program_id(program_id: &Pubkey) -> Option<Protocol> {
if program_id == &pumpfun::PUMPFUN_PROGRAM_ID {
@@ -192,20 +192,20 @@ impl EventDispatcher {
}
}
/// 检查是否为 Compute Budget Program
/// Check if it's a Compute Budget Program
#[inline]
pub fn is_compute_budget_program(program_id: &Pubkey) -> bool {
program_id == &COMPUTE_BUDGET_PROGRAM_ID
}
/// 解析 Compute Budget 指令
/// Parse Compute Budget instruction
///
/// # 参数
/// - `instruction_data`: 指令数据
/// - `metadata`: 事件元数据
/// # Parameters
/// - `instruction_data`: Instruction data
/// - `metadata`: Event metadata
///
/// # 返回
/// 解析成功返回 `Some(DexEvent)`,否则返回 `None`
/// # Returns
/// Returns `Some(DexEvent)` on successful parsing, otherwise `None`
#[inline]
pub fn dispatch_compute_budget_instruction(
instruction_data: &[u8],
@@ -214,7 +214,7 @@ impl EventDispatcher {
CommonEventParser::parse_compute_budget_instruction(instruction_data, metadata)
}
/// 获取指定协议的 program_id
/// Get program_id for specified protocol
#[inline]
pub fn get_program_id(protocol: Protocol) -> Pubkey {
match protocol {
@@ -228,30 +228,30 @@ impl EventDispatcher {
}
}
/// 批量获取 program_ids
/// Batch get program_ids
pub fn get_program_ids(protocols: &[Protocol]) -> Vec<Pubkey> {
protocols.iter().map(|p| Self::get_program_id(p.clone())).collect()
}
/// 解析账户数据
/// Parse account data
///
/// 根据账户的 discriminator 路由到对应协议的账户解析函数
/// Route to corresponding protocol account parsing function based on account discriminator
///
/// # 参数
/// - `protocol`: 协议类型
/// - `discriminator`: 账户判别器
/// - `account`: 账户信息
/// - `metadata`: 事件元数据
/// # Parameters
/// - `protocol`: Protocol type
/// - `discriminator`: Account discriminator
/// - `account`: Account information
/// - `metadata`: Event metadata
///
/// # 返回
/// 解析成功返回 `Some(DexEvent)`,否则返回 `None`
/// # Returns
/// Returns `Some(DexEvent)` on successful parsing, otherwise `None`
pub fn dispatch_account(
protocol: Protocol,
discriminator: &[u8],
account: &crate::streaming::grpc::AccountPretty,
mut metadata: crate::streaming::event_parser::common::EventMetadata,
) -> Option<DexEvent> {
// 根据协议类型设置 metadata.protocol
// Set metadata.protocol based on protocol type
use crate::streaming::event_parser::common::ProtocolType;
metadata.protocol = match protocol {
Protocol::PumpFun => ProtocolType::PumpFun,
@@ -281,7 +281,7 @@ impl EventDispatcher {
raydium_amm_v4::parse_raydium_amm_v4_account_data(discriminator, account, metadata)
}
Protocol::MeteoraDammV2 => {
// Meteora DAMM 目前不需要解析账户数据,返回 None
// Meteora DAMM currently doesn't need to parse account data, return None
None
}
}
+54 -47
View File
@@ -42,7 +42,7 @@ impl EventParser {
transaction_index: Option<u64>,
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 创建适配器回调,将所有权回调转换为引用回调
// Create adapter callback to convert ownership callback to reference callback
let adapter_callback = Arc::new(move |event: &DexEvent| {
callback(event.clone());
});
@@ -69,7 +69,7 @@ impl EventParser {
Vec::with_capacity(message.account_keys.len() + address_table_lookups.len());
accounts_bytes.extend_from_slice(&message.account_keys);
accounts_bytes.extend(address_table_lookups);
// 转换为 Pubkey
// Convert to Pubkey
let accounts: Vec<Pubkey> = accounts_bytes
.iter()
.filter_map(|account| {
@@ -80,7 +80,7 @@ impl EventParser {
}
})
.collect();
// 解析指令事件
// Parse instruction events
let instructions = &message.instructions;
Self::parse_instruction_events_from_grpc_transaction(
protocols,
@@ -122,28 +122,28 @@ impl EventParser {
transaction_index: Option<u64>,
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 创建适配器回调,将所有权回调转换为引用回调
// Create adapter callback to convert ownership callback to reference callback
let adapter_callback = Arc::new(move |event: &DexEvent| {
callback(event.clone());
});
// 获取交易的指令和账户
// Get transaction instructions and accounts
let compiled_instructions = transaction.message.instructions();
let mut accounts: Vec<Pubkey> = accounts.to_vec();
// 检查交易中是否包含程序
// Check if transaction contains the program
let has_program = accounts
.iter()
.any(|account| Self::should_handle(protocols, event_type_filter, account));
if has_program {
// 解析每个指令
// Parse each instruction
for (index, instruction) in compiled_instructions.iter().enumerate() {
if let Some(program_id) = accounts.get(instruction.program_id_index as usize) {
let program_id = *program_id; // 克隆程序ID,避免借用冲突
let program_id = *program_id; // Clone program ID to avoid borrow conflicts
let inner_instructions = inner_instructions
.iter()
.find(|inner_instruction| inner_instruction.index == index as u8);
if Self::should_handle(protocols, event_type_filter, &program_id) {
let max_idx = instruction.accounts.iter().max().unwrap_or(&0);
// 补齐accounts(使用Pubkey::default())
// Pad accounts (using Pubkey::default())
if *max_idx as usize >= accounts.len() {
accounts.resize(*max_idx as usize + 1, Pubkey::default());
}
@@ -216,22 +216,22 @@ impl EventParser {
transaction_index: Option<u64>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 获取交易的指令和账户
// Get transaction instructions and accounts
let mut accounts = accounts.to_vec();
// 检查交易中是否包含程序
// Check if transaction contains the program
let has_program = accounts
.iter()
.any(|account| Self::should_handle(protocols, event_type_filter, account));
if has_program {
// 解析每个指令
// Parse each instruction
for (index, instruction) in compiled_instructions.iter().enumerate() {
if let Some(program_id) = accounts.get(instruction.program_id_index as usize) {
let program_id = *program_id; // 克隆程序ID,避免借用冲突
let program_id = *program_id; // Clone program ID to avoid borrow conflicts
let inner_instructions = inner_instructions
.iter()
.find(|inner_instruction| inner_instruction.index == index as u32);
let max_idx = instruction.accounts.iter().max().unwrap_or(&0);
// 补齐accounts(使用Pubkey::default())
// Pad accounts (using Pubkey::default())
if *max_idx as usize >= accounts.len() {
accounts.resize(*max_idx as usize + 1, Pubkey::default());
}
@@ -311,7 +311,7 @@ impl EventParser {
inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 添加边界检查以防止越界访问
// Add bounds check to prevent out-of-bounds access
let program_id_index = instruction.program_id_index as usize;
if program_id_index >= accounts.len() {
return Ok(());
@@ -328,11 +328,11 @@ impl EventParser {
_ => 8,
};
// 检查指令数据长度(至少需要 disc_len 字节的 discriminator
// Check instruction data length (at least disc_len bytes for discriminator)
if !is_cu_program && instruction.data.len() < disc_len {
return Ok(());
}
// 创建元数据
// Create metadata
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
let metadata = EventMetadata::new(
@@ -359,24 +359,24 @@ impl EventParser {
return Ok(());
}
// 使用 EventDispatcher 匹配协议
// Use EventDispatcher to match protocol
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
Some(p) => p,
None => return Ok(()),
};
// 提取 discriminator 和数据
// Extract discriminator and data
let instruction_discriminator = &instruction.data[..disc_len];
let instruction_data = &instruction.data[disc_len..];
// 构建账户公钥列表
// Build account pubkey list
let account_pubkeys: Vec<Pubkey> = instruction
.accounts
.iter()
.filter_map(|&idx| accounts.get(idx as usize).copied())
.collect();
// 使用 EventDispatcher 解析 instruction 事件
// Use EventDispatcher to parse instruction event
let mut event = match EventDispatcher::dispatch_instruction(
protocol.clone(),
instruction_discriminator,
@@ -388,23 +388,23 @@ impl EventParser {
None => return Ok(()),
};
// 处理 inner instructions - 查找对应的 CPI log 进行 merge
// inner_index 有值时,只查找索引大于当前 inner_index 的 CPI log
// Process inner instructions - find corresponding CPI log for merge
// When inner_index has a value, only search for CPI logs with index greater than current inner_index
let mut inner_instruction_event: Option<DexEvent> = None;
if let Some(inner_instructions_ref) = inner_instructions {
let current_inner_idx = inner_index.unwrap_or(-1) as i32;
// 并行执行两个任务: 解析 inner event 和提取 swap_data
// Execute two tasks in parallel: parse inner event and extract swap_data
let (inner_event_result, swap_data_result) = std::thread::scope(|s| {
let inner_event_handle = s.spawn(|| {
for (idx, inner_instruction) in inner_instructions_ref.instructions.iter().enumerate() {
// 只查找索引大于当前 inner_index 的 CPI log
// Only search for CPI logs with index greater than current inner_index
if (idx as i32) <= current_inner_idx {
continue;
}
let inner_data = &inner_instruction.data;
// 检查长度(需要 16 字节的 discriminator
// Check length (needs 16 bytes for discriminator)
if inner_data.len() < 16 {
continue;
}
@@ -436,7 +436,7 @@ impl EventParser {
}
});
// 等待两个任务完成
// Wait for both tasks to complete
(inner_event_handle.join().unwrap(), swap_data_handle.join().unwrap())
});
@@ -446,7 +446,7 @@ impl EventParser {
}
}
// 特殊处理: PumpFun MIGRATE 指令需要 inner instruction data
// Special handling: PumpFun MIGRATE instruction requires inner instruction data
if matches!(protocol, Protocol::PumpFun) {
const PUMPFUN_MIGRATE_IX: &[u8] = &[155, 234, 231, 146, 236, 158, 162, 30];
if instruction_discriminator == PUMPFUN_MIGRATE_IX && inner_instruction_event.is_none()
@@ -455,12 +455,12 @@ impl EventParser {
}
}
// 合并事件
// Merge events
if let Some(inner_instruction_event) = inner_instruction_event {
merge(&mut event, inner_instruction_event);
}
// 设置处理时间(使用高性能时钟)
// Set processing time (using high-performance clock)
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
event = Self::process_event(event, bot_wallet);
callback(&event);
@@ -493,7 +493,7 @@ impl EventParser {
inner_instructions: Option<&InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 添加边界检查以防止越界访问
// Add bounds check to prevent out-of-bounds access
let program_id_index = instruction.program_id_index as usize;
if program_id_index >= accounts.len() {
return Ok(());
@@ -510,12 +510,12 @@ impl EventParser {
_ => 8,
};
// 检查指令数据长度(至少需要 8 字节的 discriminator
// Check instruction data length (at least 8 bytes for discriminator)
if !is_cu_program && instruction.data.len() < disc_len {
return Ok(());
}
// 创建元数据
// Create metadata
let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 });
let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000;
let metadata = EventMetadata::new(
@@ -542,24 +542,24 @@ impl EventParser {
return Ok(());
}
// 使用 EventDispatcher 匹配协议
// Use EventDispatcher to match protocol
let protocol = match EventDispatcher::match_protocol_by_program_id(&program_id) {
Some(p) => p,
None => return Ok(()),
};
// 提取 discriminator 和数据
// Extract discriminator and data
let instruction_discriminator = &instruction.data[..disc_len];
let instruction_data = &instruction.data[disc_len..];
// 构建账户公钥列表
// Build account pubkey list
let account_pubkeys: Vec<Pubkey> = instruction
.accounts
.iter()
.filter_map(|&idx| accounts.get(idx as usize).copied())
.collect();
// 使用 EventDispatcher 解析 instruction 事件
// Use EventDispatcher to parse instruction event
let mut event = match EventDispatcher::dispatch_instruction(
protocol.clone(),
instruction_discriminator,
@@ -571,23 +571,23 @@ impl EventParser {
None => return Ok(()),
};
// 处理 inner instructions - 查找对应的 CPI log 进行 merge
// inner_index 有值时,只查找索引大于当前 inner_index 的 CPI log
// Process inner instructions - find corresponding CPI log for merge
// When inner_index has a value, only search for CPI logs with index greater than current inner_index
let mut inner_instruction_event: Option<DexEvent> = None;
if let Some(inner_instructions_ref) = inner_instructions {
let current_inner_idx = inner_index.unwrap_or(-1) as i32;
// 并行执行两个任务: 解析 inner event 和提取 swap_data
// Execute two tasks in parallel: parse inner event and extract swap_data
let (inner_event_result, swap_data_result) = std::thread::scope(|s| {
let inner_event_handle = s.spawn(|| {
for (idx, inner_instruction) in inner_instructions_ref.instructions.iter().enumerate() {
// 只查找索引大于当前 inner_index 的 CPI log
// Only search for CPI logs with index greater than current inner_index
if (idx as i32) <= current_inner_idx {
continue;
}
let inner_data = &inner_instruction.instruction.data;
// 检查长度(需要 16 字节的 discriminator
// Check length (needs 16 bytes for discriminator)
if inner_data.len() < 16 {
continue;
}
@@ -619,7 +619,7 @@ impl EventParser {
}
});
// 等待两个任务完成
// Wait for both tasks to complete
(inner_event_handle.join().unwrap(), swap_data_handle.join().unwrap())
});
@@ -629,7 +629,7 @@ impl EventParser {
}
}
// 特殊处理: PumpFun MIGRATE 指令需要 inner instruction data
// Special handling: PumpFun MIGRATE instruction requires inner instruction data
if matches!(protocol, Protocol::PumpFun) {
const PUMPFUN_MIGRATE_IX: &[u8] = &[155, 234, 231, 146, 236, 158, 162, 30];
if instruction_discriminator == PUMPFUN_MIGRATE_IX && inner_instruction_event.is_none()
@@ -638,12 +638,12 @@ impl EventParser {
}
}
// 合并事件
// Merge events
if let Some(inner_instruction_event) = inner_instruction_event {
merge(&mut event, inner_instruction_event);
}
// 设置处理时间(使用高性能时钟)
// Set processing time (using high-performance clock)
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
event = Self::process_event(event, bot_wallet);
callback(&event);
@@ -663,7 +663,7 @@ impl EventParser {
_event_type_filter: Option<&EventTypeFilter>,
program_id: &Pubkey,
) -> bool {
// 使用 EventDispatcher 来匹配协议
// Use EventDispatcher to match protocol
if let Some(protocol) = EventDispatcher::match_protocol_by_program_id(program_id) {
protocols.contains(&protocol)
} else if EventDispatcher::is_compute_budget_program(program_id) {
@@ -730,6 +730,13 @@ impl EventParser {
}
DexEvent::PumpSwapBuyEvent(trade_info)
}
DexEvent::PumpSwapBuyExactQuoteInEvent(mut trade_info) => {
if let Some(swap_data) = trade_info.metadata.swap_data.as_mut() {
swap_data.from_amount = trade_info.user_quote_amount_in;
swap_data.to_amount = trade_info.base_amount_out;
}
DexEvent::PumpSwapBuyExactQuoteInEvent(trade_info)
}
DexEvent::PumpSwapSellEvent(mut trade_info) => {
if let Some(swap_data) = trade_info.metadata.swap_data.as_mut() {
swap_data.from_amount = trade_info.base_amount_in;
@@ -140,6 +140,48 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
e.coin_creator = cpie.coin_creator;
e.coin_creator_fee_basis_points = cpie.coin_creator_fee_basis_points;
e.coin_creator_fee = cpie.coin_creator_fee;
e.track_volume = cpie.track_volume;
e.total_unclaimed_tokens = cpie.total_unclaimed_tokens;
e.total_claimed_tokens = cpie.total_claimed_tokens;
e.current_sol_volume = cpie.current_sol_volume;
e.last_update_timestamp = cpie.last_update_timestamp;
e.min_base_amount_out = cpie.min_base_amount_out;
e.ix_name = cpie.ix_name;
}
_ => {}
},
DexEvent::PumpSwapBuyExactQuoteInEvent(e) => match cpi_log_event {
DexEvent::PumpSwapBuyExactQuoteInEvent(cpie) => {
e.timestamp = cpie.timestamp;
e.base_amount_out = cpie.base_amount_out;
e.max_quote_amount_in = cpie.max_quote_amount_in;
e.user_base_token_reserves = cpie.user_base_token_reserves;
e.user_quote_token_reserves = cpie.user_quote_token_reserves;
e.pool_base_token_reserves = cpie.pool_base_token_reserves;
e.pool_quote_token_reserves = cpie.pool_quote_token_reserves;
e.quote_amount_in = cpie.quote_amount_in;
e.lp_fee_basis_points = cpie.lp_fee_basis_points;
e.lp_fee = cpie.lp_fee;
e.protocol_fee_basis_points = cpie.protocol_fee_basis_points;
e.protocol_fee = cpie.protocol_fee;
e.quote_amount_in_with_lp_fee = cpie.quote_amount_in_with_lp_fee;
e.user_quote_amount_in = cpie.user_quote_amount_in;
e.pool = cpie.pool;
e.user = cpie.user;
e.user_base_token_account = cpie.user_base_token_account;
e.user_quote_token_account = cpie.user_quote_token_account;
e.protocol_fee_recipient = cpie.protocol_fee_recipient;
e.protocol_fee_recipient_token_account = cpie.protocol_fee_recipient_token_account;
e.coin_creator = cpie.coin_creator;
e.coin_creator_fee_basis_points = cpie.coin_creator_fee_basis_points;
e.coin_creator_fee = cpie.coin_creator_fee;
e.track_volume = cpie.track_volume;
e.total_unclaimed_tokens = cpie.total_unclaimed_tokens;
e.total_claimed_tokens = cpie.total_claimed_tokens;
e.current_sol_volume = cpie.current_sol_volume;
e.last_update_timestamp = cpie.last_update_timestamp;
e.min_base_amount_out = cpie.min_base_amount_out;
e.ix_name = cpie.ix_name;
}
_ => {}
},
@@ -38,6 +38,7 @@ pub enum DexEvent {
// PumpSwap events
PumpSwapBuyEvent(PumpSwapBuyEvent),
PumpSwapBuyExactQuoteInEvent(PumpSwapBuyExactQuoteInEvent),
PumpSwapSellEvent(PumpSwapSellEvent),
PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent),
PumpSwapDepositEvent(PumpSwapDepositEvent),
@@ -107,6 +108,7 @@ impl DexEvent {
DexEvent::PumpFunBondingCurveAccountEvent(e) => &e.metadata,
DexEvent::PumpFunGlobalAccountEvent(e) => &e.metadata,
DexEvent::PumpSwapBuyEvent(e) => &e.metadata,
DexEvent::PumpSwapBuyExactQuoteInEvent(e) => &e.metadata,
DexEvent::PumpSwapSellEvent(e) => &e.metadata,
DexEvent::PumpSwapCreatePoolEvent(e) => &e.metadata,
DexEvent::PumpSwapDepositEvent(e) => &e.metadata,
@@ -166,6 +168,7 @@ impl DexEvent {
DexEvent::PumpFunBondingCurveAccountEvent(e) => &mut e.metadata,
DexEvent::PumpFunGlobalAccountEvent(e) => &mut e.metadata,
DexEvent::PumpSwapBuyEvent(e) => &mut e.metadata,
DexEvent::PumpSwapBuyExactQuoteInEvent(e) => &mut e.metadata,
DexEvent::PumpSwapSellEvent(e) => &mut e.metadata,
DexEvent::PumpSwapCreatePoolEvent(e) => &mut e.metadata,
DexEvent::PumpSwapDepositEvent(e) => &mut e.metadata,
@@ -3,7 +3,7 @@ use borsh::BorshDeserialize;
use serde::{Deserialize, Serialize};
use solana_sdk::signature::Signature;
/// Block元数据事件
/// Block metadata event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BlockMetaEvent {
#[borsh(skip)]
@@ -227,8 +227,8 @@ pub struct BonkMigrateToCpswapEvent {
pub remaining_accounts: Vec<Pubkey>,
}
/// 池状态
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
/// Pool state account event
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BonkPoolStateAccountEvent {
pub metadata: EventMetadata,
pub pubkey: Pubkey,
@@ -239,8 +239,8 @@ pub struct BonkPoolStateAccountEvent {
pub pool_state: PoolState,
}
/// 全局配置
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
/// Global config account event
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BonkGlobalConfigAccountEvent {
pub metadata: EventMetadata,
pub pubkey: Pubkey,
@@ -251,8 +251,8 @@ pub struct BonkGlobalConfigAccountEvent {
pub global_config: GlobalConfig,
}
/// 平台配置
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
/// Platform config account event
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BonkPlatformConfigAccountEvent {
pub metadata: EventMetadata,
pub pubkey: Pubkey,
@@ -15,9 +15,9 @@ use crate::streaming::event_parser::{
pub const BONK_PROGRAM_ID: Pubkey =
solana_sdk::pubkey!("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj");
/// 解析 Bonk instruction data
/// Parse Bonk instruction data
///
/// 根据判别器路由到具体的 instruction 解析函数
/// Route to specific instruction parser based on discriminator
pub fn parse_bonk_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -56,9 +56,9 @@ pub fn parse_bonk_instruction_data(
}
}
/// 解析 Bonk inner instruction data
/// Parse Bonk inner instruction data
///
/// 根据判别器路由到具体的 inner instruction 解析函数
/// Route to specific inner instruction parser based on discriminator
pub fn parse_bonk_inner_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -75,9 +75,9 @@ pub fn parse_bonk_inner_instruction_data(
}
}
/// 解析 Bonk 账户数据
/// Parse Bonk account data
///
/// 根据判别器路由到具体的账户解析函数
/// Route to specific account parser based on discriminator
pub fn parse_bonk_account_data(
discriminator: &[u8],
account: &crate::streaming::grpc::AccountPretty,
@@ -94,7 +94,7 @@ pub struct VestingSchedule {
pub allocated_share_amount: u64,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PoolState {
pub epoch: u64,
pub auth_bump: u8,
@@ -120,10 +120,13 @@ pub struct PoolState {
pub base_vault: Pubkey,
pub quote_vault: Pubkey,
pub creator: Pubkey,
pub padding: [u64; 8],
pub token_program_flag: u8,
pub amm_creator_fee_on: AmmFeeOn,
#[serde(with = "serde_big_array::BigArray")]
pub padding: [u8; 62],
}
pub const POOL_STATE_SIZE: usize = 8 + 1 * 5 + 8 * 10 + 32 * 7 + 8 * 8 + 8 * 5;
pub const POOL_STATE_SIZE: usize = 8 + 1 * 5 + 8 * 10 + 8 * 5 + 32 * 7 + 1 + 1 + 62;
pub fn pool_state_decode(data: &[u8]) -> Option<PoolState> {
if data.len() < POOL_STATE_SIZE {
@@ -207,6 +210,28 @@ pub fn global_config_parser(
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BondingCurveParam {
pub migrate_type: u8,
pub migrate_cpmm_fee_on: u8,
pub supply: u64,
pub total_base_sell: u64,
pub total_quote_fund_raising: u64,
pub total_locked_amount: u64,
pub cliff_period: u64,
pub unlock_period: u64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PlatformCurveParam {
pub epoch: u64,
pub index: u8,
pub global_config: Pubkey,
pub bonding_curve_param: BondingCurveParam,
#[serde(with = "serde_big_array::BigArray")]
pub padding: [u64; 50],
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PlatformConfig {
pub epoch: u64,
pub platform_fee_wallet: Pubkey,
@@ -215,19 +240,27 @@ pub struct PlatformConfig {
pub creator_scale: u64,
pub burn_scale: u64,
pub fee_rate: u64,
pub name: Vec<u8>,
pub web: Vec<u8>,
pub img: Vec<u8>,
pub padding: Vec<u8>,
#[serde(with = "serde_big_array::BigArray")]
pub name: [u8; 64],
#[serde(with = "serde_big_array::BigArray")]
pub web: [u8; 256],
#[serde(with = "serde_big_array::BigArray")]
pub img: [u8; 256],
pub cpswap_config: Pubkey,
pub creator_fee_rate: u64,
pub transfer_fee_extension_auth: Pubkey,
#[serde(with = "serde_big_array::BigArray")]
pub padding: [u8; 180],
pub curve_params: Vec<PlatformCurveParam>,
}
pub const PLATFORM_CONFIG_SIZE: usize = 8 + 32 * 2 + 8 * 4 + 8 * 64 + 8 * 256 + 8 * 256 + 8 * 256;
pub const PLATFORM_CONFIG_MIN_SIZE: usize = 936;
pub fn platform_config_decode(data: &[u8]) -> Option<PlatformConfig> {
if data.len() < PLATFORM_CONFIG_SIZE {
if data.len() < PLATFORM_CONFIG_MIN_SIZE {
return None;
}
borsh::from_slice::<PlatformConfig>(&data[..PLATFORM_CONFIG_SIZE]).ok()
borsh::from_slice::<PlatformConfig>(data).ok()
}
pub fn platform_config_parser(
@@ -236,11 +269,11 @@ pub fn platform_config_parser(
) -> Option<DexEvent> {
metadata.event_type = EventType::AccountBonkPlatformConfig;
if account.data.len() < PLATFORM_CONFIG_SIZE + 8 {
if account.data.len() < PLATFORM_CONFIG_MIN_SIZE + 8 {
return None;
}
if let Some(platform_config) =
platform_config_decode(&account.data[8..PLATFORM_CONFIG_SIZE + 8])
platform_config_decode(&account.data[8..])
{
Some(DexEvent::BonkPlatformConfigAccountEvent(BonkPlatformConfigAccountEvent {
metadata,
@@ -9,13 +9,13 @@ use crate::streaming::event_parser::{
};
use solana_sdk::pubkey::Pubkey;
/// PumpFun程序ID
/// PumpFun program ID
pub const PUMPFUN_PROGRAM_ID: Pubkey =
solana_sdk::pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
/// 解析 PumpFun instruction data
/// Parse PumpFun instruction data
///
/// 根据判别器路由到具体的 instruction 解析函数
/// Route to specific instruction parser based on discriminator
pub fn parse_pumpfun_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -34,9 +34,9 @@ pub fn parse_pumpfun_instruction_data(
}
}
/// 解析 PumpFun inner instruction data
/// Parse PumpFun inner instruction data
///
/// 根据判别器路由到具体的 inner instruction 解析函数
/// Route to specific inner instruction parser based on discriminator
pub fn parse_pumpfun_inner_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -52,9 +52,9 @@ pub fn parse_pumpfun_inner_instruction_data(
}
}
/// 解析 PumpFun 账户数据
/// Parse PumpFun account data
///
/// 根据判别器路由到具体的账户解析函数
/// Route to specific account parser based on discriminator
pub fn parse_pumpfun_account_data(
discriminator: &[u8],
account: &crate::streaming::grpc::AccountPretty,
@@ -5,7 +5,7 @@ use solana_sdk::pubkey::Pubkey;
use crate::streaming::event_parser::common::EventMetadata;
use crate::streaming::event_parser::protocols::pumpswap::types::{GlobalConfig, Pool};
/// 买入事件
/// Buy event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapBuyEvent {
#[borsh(skip)]
@@ -38,6 +38,66 @@ pub struct PumpSwapBuyEvent {
pub total_claimed_tokens: u64,
pub current_sol_volume: u64,
pub last_update_timestamp: i64,
pub min_base_amount_out: u64,
pub ix_name: String,
#[borsh(skip)]
pub base_mint: Pubkey,
#[borsh(skip)]
pub quote_mint: Pubkey,
#[borsh(skip)]
pub pool_base_token_account: Pubkey,
#[borsh(skip)]
pub pool_quote_token_account: Pubkey,
#[borsh(skip)]
pub coin_creator_vault_ata: Pubkey,
#[borsh(skip)]
pub coin_creator_vault_authority: Pubkey,
#[borsh(skip)]
pub base_token_program: Pubkey,
#[borsh(skip)]
pub quote_token_program: Pubkey,
}
/// Buy event (ix: `buy_exact_quote_in`)
///
/// Same on-chain layout as `PumpSwapBuyEvent` (IDL `BuyEvent`),
/// but represented as a different type to distinguish in the streamer.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapBuyExactQuoteInEvent {
#[borsh(skip)]
pub metadata: EventMetadata,
pub timestamp: i64,
pub base_amount_out: u64,
pub max_quote_amount_in: u64,
pub user_base_token_reserves: u64,
pub user_quote_token_reserves: u64,
pub pool_base_token_reserves: u64,
pub pool_quote_token_reserves: u64,
pub quote_amount_in: u64,
pub lp_fee_basis_points: u64,
pub lp_fee: u64,
pub protocol_fee_basis_points: u64,
pub protocol_fee: u64,
pub quote_amount_in_with_lp_fee: u64,
pub user_quote_amount_in: u64,
pub pool: Pubkey,
pub user: Pubkey,
pub user_base_token_account: Pubkey,
pub user_quote_token_account: Pubkey,
pub protocol_fee_recipient: Pubkey,
pub protocol_fee_recipient_token_account: Pubkey,
pub coin_creator: Pubkey,
pub coin_creator_fee_basis_points: u64,
pub coin_creator_fee: u64,
pub track_volume: bool,
pub total_unclaimed_tokens: u64,
pub total_claimed_tokens: u64,
pub current_sol_volume: u64,
pub last_update_timestamp: i64,
pub min_base_amount_out: u64,
pub ix_name: String,
#[borsh(skip)]
pub spendable_quote_in: u64,
#[borsh(skip)]
pub base_mint: Pubkey,
#[borsh(skip)]
@@ -62,10 +122,19 @@ pub fn pump_swap_buy_event_log_decode(data: &[u8]) -> Option<PumpSwapBuyEvent> {
if data.len() < PUMP_SWAP_BUY_EVENT_LOG_SIZE {
return None;
}
borsh::from_slice::<PumpSwapBuyEvent>(&data[..PUMP_SWAP_BUY_EVENT_LOG_SIZE]).ok()
// Use the entire buffer to correctly deserialize the String ix_name field
borsh::from_slice::<PumpSwapBuyEvent>(data).ok()
}
/// 卖出事件
pub fn pump_swap_buy_exact_quote_in_event_log_decode(data: &[u8]) -> Option<PumpSwapBuyExactQuoteInEvent> {
if data.len() < PUMP_SWAP_BUY_EVENT_LOG_SIZE {
return None;
}
// Use the entire buffer to correctly deserialize the String ix_name field
borsh::from_slice::<PumpSwapBuyExactQuoteInEvent>(data).ok()
}
/// Sell event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapSellEvent {
#[borsh(skip)]
@@ -120,7 +189,7 @@ pub fn pump_swap_sell_event_log_decode(data: &[u8]) -> Option<PumpSwapSellEvent>
borsh::from_slice::<PumpSwapSellEvent>(&data[..PUMP_SWAP_SELL_EVENT_LOG_SIZE]).ok()
}
/// 创建池子事件
/// Create pool event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapCreatePoolEvent {
#[borsh(skip)]
@@ -162,7 +231,7 @@ pub fn pump_swap_create_pool_event_log_decode(data: &[u8]) -> Option<PumpSwapCre
borsh::from_slice::<PumpSwapCreatePoolEvent>(&data[..PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE]).ok()
}
/// 存款事件
/// Deposit event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapDepositEvent {
#[borsh(skip)]
@@ -202,7 +271,7 @@ pub fn pump_swap_deposit_event_log_decode(data: &[u8]) -> Option<PumpSwapDeposit
borsh::from_slice::<PumpSwapDepositEvent>(&data[..PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE]).ok()
}
/// 提款事件
/// Withdraw event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapWithdrawEvent {
#[borsh(skip)]
@@ -242,7 +311,7 @@ pub fn pump_swap_withdraw_event_log_decode(data: &[u8]) -> Option<PumpSwapWithdr
borsh::from_slice::<PumpSwapWithdrawEvent>(&data[..PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE]).ok()
}
/// 全局配置
/// Global config account event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapGlobalConfigAccountEvent {
#[borsh(skip)]
@@ -255,7 +324,7 @@ pub struct PumpSwapGlobalConfigAccountEvent {
pub global_config: GlobalConfig,
}
///
/// Pool account event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapPoolAccountEvent {
#[borsh(skip)]
@@ -268,9 +337,9 @@ pub struct PumpSwapPoolAccountEvent {
pub pool: Pool,
}
/// 事件鉴别器常量
/// Event discriminator constants
pub mod discriminators {
// 事件鉴别器
// Event discriminators
// pub const BUY_EVENT: &str = "0xe445a52e51cb9a1d67f4521f2cf57777";
pub const BUY_EVENT: &[u8] =
&[228, 69, 165, 46, 81, 203, 154, 29, 103, 244, 82, 31, 44, 245, 119, 119];
@@ -287,14 +356,15 @@ pub mod discriminators {
pub const WITHDRAW_EVENT: &[u8] =
&[228, 69, 165, 46, 81, 203, 154, 29, 22, 9, 133, 26, 160, 44, 71, 192];
// 指令鉴别器
// Instruction discriminators
pub const BUY_IX: &[u8] = &[102, 6, 61, 18, 1, 218, 235, 234];
pub const BUY_EXACT_QUOTE_IN_IX: &[u8] = &[198, 46, 21, 82, 180, 217, 232, 112];
pub const SELL_IX: &[u8] = &[51, 230, 133, 164, 1, 127, 131, 173];
pub const CREATE_POOL_IX: &[u8] = &[233, 146, 209, 142, 207, 104, 64, 188];
pub const DEPOSIT_IX: &[u8] = &[242, 35, 198, 137, 82, 225, 242, 182];
pub const WITHDRAW_IX: &[u8] = &[183, 18, 70, 156, 148, 109, 161, 34];
// 账户鉴别器
// Account discriminators
pub const GLOBAL_CONFIG_ACCOUNT: &[u8] = &[149, 8, 156, 202, 160, 252, 176, 217];
pub const POOL_ACCOUNT: &[u8] = &[241, 154, 109, 4, 17, 177, 109, 188];
}
@@ -1,22 +1,24 @@
use crate::streaming::event_parser::{
common::{read_u64_le, EventMetadata, EventType},
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,
pump_swap_withdraw_event_log_decode, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
discriminators, pump_swap_buy_event_log_decode,
pump_swap_buy_exact_quote_in_event_log_decode,
pump_swap_create_pool_event_log_decode, pump_swap_deposit_event_log_decode,
pump_swap_sell_event_log_decode, pump_swap_withdraw_event_log_decode, PumpSwapBuyEvent,
PumpSwapBuyExactQuoteInEvent, PumpSwapCreatePoolEvent, PumpSwapDepositEvent,
PumpSwapSellEvent, PumpSwapWithdrawEvent,
},
DexEvent,
};
use solana_sdk::pubkey::Pubkey;
/// PumpSwap程序ID
/// PumpSwap program ID
pub const PUMPSWAP_PROGRAM_ID: Pubkey =
solana_sdk::pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");
/// 解析 PumpSwap instruction data
/// Parse PumpSwap instruction data
///
/// 根据判别器路由到具体的 instruction 解析函数
/// Routes to specific instruction parser based on discriminator
pub fn parse_pumpswap_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -25,6 +27,7 @@ pub fn parse_pumpswap_instruction_data(
) -> Option<DexEvent> {
match discriminator {
discriminators::BUY_IX => parse_buy_instruction(data, accounts, metadata),
discriminators::BUY_EXACT_QUOTE_IN_IX => parse_buy_exact_quote_in_instruction(data, accounts, metadata),
discriminators::SELL_IX => parse_sell_instruction(data, accounts, metadata),
discriminators::CREATE_POOL_IX => {
parse_create_pool_instruction(data, accounts, metadata)
@@ -35,9 +38,9 @@ pub fn parse_pumpswap_instruction_data(
}
}
/// 解析 PumpSwap inner instruction data
/// Parse PumpSwap inner instruction data
///
/// 根据判别器路由到具体的 inner instruction 解析函数
/// Routes to specific inner instruction parser based on discriminator
pub fn parse_pumpswap_inner_instruction_data(
discriminator: &[u8],
data: &[u8],
@@ -56,9 +59,9 @@ pub fn parse_pumpswap_inner_instruction_data(
}
/// 解析 PumpSwap 账户数据
/// Parse PumpSwap account data
///
/// 根据判别器路由到具体的账户解析函数
/// Routes to specific account parser based on discriminator
pub fn parse_pumpswap_account_data(
discriminator: &[u8],
account: &crate::streaming::grpc::AccountPretty,
@@ -75,17 +78,25 @@ pub fn parse_pumpswap_account_data(
}
}
/// 解析买入日志事件
/// Parse buy event log
fn parse_buy_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
// Note: event_type will be set by instruction parser
if let Some(event) = pump_swap_buy_event_log_decode(data) {
Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { metadata, ..event }))
} else {
None
// First try to decode as buy_exact_quote_in to read ix_name
if let Some(event) = pump_swap_buy_exact_quote_in_event_log_decode(data) {
if event.ix_name == "buy_exact_quote_in" {
return Some(DexEvent::PumpSwapBuyExactQuoteInEvent(PumpSwapBuyExactQuoteInEvent {
metadata,
..event
}));
}
}
// If not buy_exact_quote_in, decode as normal buy
pump_swap_buy_event_log_decode(data).map(|event| {
DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { metadata, ..event })
})
}
/// 解析卖出日志事件
/// Parse sell event log
fn parse_sell_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
// Note: event_type will be set by instruction parser
if let Some(event) = pump_swap_sell_event_log_decode(data) {
@@ -95,7 +106,7 @@ fn parse_sell_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<
}
}
/// 解析创建池子日志事件
/// Parse create pool event log
fn parse_create_pool_inner_instruction(
data: &[u8],
metadata: EventMetadata,
@@ -108,7 +119,7 @@ fn parse_create_pool_inner_instruction(
}
}
/// 解析存款日志事件
/// Parse deposit event log
fn parse_deposit_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
// Note: event_type will be set by instruction parser
if let Some(event) = pump_swap_deposit_event_log_decode(data) {
@@ -118,7 +129,7 @@ fn parse_deposit_inner_instruction(data: &[u8], metadata: EventMetadata) -> Opti
}
}
/// 解析提款日志事件
/// Parse withdraw event log
fn parse_withdraw_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
// Note: event_type will be set by instruction parser
if let Some(event) = pump_swap_withdraw_event_log_decode(data) {
@@ -128,7 +139,7 @@ fn parse_withdraw_inner_instruction(data: &[u8], metadata: EventMetadata) -> Opt
}
}
/// 解析买入指令事件
/// Parse buy instruction
fn parse_buy_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -165,7 +176,46 @@ fn parse_buy_instruction(
}))
}
/// 解析卖出指令事件
/// Parse `buy_exact_quote_in` instruction
fn parse_buy_exact_quote_in_instruction(
data: &[u8],
accounts: &[Pubkey],
mut metadata: EventMetadata,
) -> Option<DexEvent> {
metadata.event_type = EventType::PumpSwapBuyExactQuoteIn;
if data.len() < 16 || accounts.len() < 13 {
return None;
}
let spendable_quote_in = read_u64_le(data, 0)?;
let min_base_amount_out = read_u64_le(data, 8)?;
Some(DexEvent::PumpSwapBuyExactQuoteInEvent(
PumpSwapBuyExactQuoteInEvent {
metadata,
spendable_quote_in,
min_base_amount_out,
pool: accounts[0],
user: accounts[1],
base_mint: accounts[3],
quote_mint: accounts[4],
user_base_token_account: accounts[5],
user_quote_token_account: accounts[6],
pool_base_token_account: accounts[7],
pool_quote_token_account: accounts[8],
protocol_fee_recipient: accounts[9],
protocol_fee_recipient_token_account: accounts[10],
base_token_program: accounts[11],
quote_token_program: accounts[12],
coin_creator_vault_ata: accounts.get(17).copied().unwrap_or_default(),
coin_creator_vault_authority: accounts.get(18).copied().unwrap_or_default(),
..Default::default()
},
))
}
/// Parse sell instruction
fn parse_sell_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -202,7 +252,7 @@ fn parse_sell_instruction(
}))
}
/// 解析创建池子指令事件
/// Parse create pool instruction
fn parse_create_pool_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -243,7 +293,7 @@ fn parse_create_pool_instruction(
}))
}
/// 解析存款指令事件
/// Parse deposit instruction
fn parse_deposit_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -277,7 +327,7 @@ fn parse_deposit_instruction(
}))
}
/// 解析提款指令事件
/// Parse withdraw instruction
fn parse_withdraw_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -20,12 +20,9 @@ pub struct GlobalConfig {
pub protocol_fee_recipients: [Pubkey; 8],
pub coin_creator_fee_basis_points: u64,
pub admin_set_coin_creator_authority: Pubkey,
pub whitelist_pda: Pubkey,
pub reserved_fee_recipient: Pubkey,
pub mayhem_mode_enabled: bool,
}
pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32 + 32 + 32 + 1;
pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32;
pub fn global_config_decode(data: &[u8]) -> Option<GlobalConfig> {
if data.len() < GLOBAL_CONFIG_SIZE {
@@ -70,10 +67,9 @@ pub struct Pool {
pub pool_quote_token_account: Pubkey,
pub lp_supply: u64,
pub coin_creator: Pubkey,
pub is_mayhem_mode: bool,
}
pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1;
pub const POOL_SIZE: usize = 1 + 2 + 32 * 6 + 8 + 32;
pub fn pool_decode(data: &[u8]) -> Option<Pool> {
if data.len() < POOL_SIZE {
@@ -24,10 +24,11 @@ pub struct AmmConfig {
pub create_pool_fee: u64,
pub protocol_owner: Pubkey,
pub fund_owner: Pubkey,
pub padding: [u64; 16],
pub creator_fee_rate: u64,
pub padding: [u64; 15],
}
pub const AMM_CONFIG_SIZE: usize = 228;
pub const AMM_CONFIG_SIZE: usize = 236;
pub fn amm_config_decode(data: &[u8]) -> Option<AmmConfig> {
if data.len() < AMM_CONFIG_SIZE {
@@ -81,10 +82,15 @@ pub struct PoolState {
pub fund_fees_token1: u64,
pub open_time: u64,
pub recent_epoch: u64,
pub padding: [u64; 31],
pub creator_fee_on: u8,
pub enable_creator_fee: bool,
pub padding1: [u8; 6],
pub creator_fees_token0: u64,
pub creator_fees_token1: u64,
pub padding: [u64; 28],
}
pub const POOL_STATE_SIZE: usize = 629;
pub const POOL_STATE_SIZE: usize = 589;
pub fn pool_state_decode(data: &[u8]) -> Option<PoolState> {
if data.len() < POOL_STATE_SIZE {
@@ -7,7 +7,7 @@ use crate::streaming::event_parser::protocols::{
use anyhow::{anyhow, Result};
use solana_sdk::pubkey::Pubkey;
/// 支持的协议
/// Supported protocols
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Protocol {
PumpSwap,
+1 -1
View File
@@ -6,7 +6,7 @@ use crate::streaming::common::constants::{
DEFAULT_CONNECT_TIMEOUT, DEFAULT_REQUEST_TIMEOUT, DEFAULT_MAX_DECODING_MESSAGE_SIZE
};
/// gRPC连接池 - 简化版本
/// gRPC connection pool - simplified version
pub struct GrpcConnectionPool {
endpoint: String,
x_token: Option<String>,
+3 -3
View File
@@ -1,16 +1,16 @@
// gRPC 相关模块
// gRPC related modules
pub mod connection;
pub mod pool;
pub mod subscription;
pub mod types;
// 重新导出主要类型
// Re-export main types
pub use connection::*;
pub use pool::*;
pub use subscription::*;
pub use types::*;
// 从公用模块重新导出
// Re-export from common modules
pub use crate::streaming::common::{
ConnectionConfig, MetricsManager, PerformanceMetrics, StreamClientConfig as ClientConfig,
};
+37 -37
View File
@@ -9,13 +9,13 @@ use yellowstone_grpc_proto::{
prost_types::Timestamp,
};
/// 通用对象池特征
/// Generic object pool trait
pub trait ObjectPool<T> {
fn acquire(&self) -> PooledObject<T>;
fn return_object(&self, obj: Box<T>);
}
/// 带自动归还的智能指针
/// Smart pointer with automatic return
pub struct PooledObject<T> {
object: Option<Box<T>>,
pool: Arc<Mutex<VecDeque<Box<T>>>>,
@@ -36,7 +36,7 @@ impl<T> Drop for PooledObject<T> {
if pool.len() < self.max_size {
pool.push_back(obj);
}
// 超过最大容量时直接丢弃
// Discard when exceeding max capacity
}
}
}
@@ -55,7 +55,7 @@ impl<T> std::ops::DerefMut for PooledObject<T> {
}
}
/// AccountPretty 对象池
/// AccountPretty object pool
pub struct AccountPrettyPool {
pool: Arc<Mutex<VecDeque<Box<AccountPretty>>>>,
max_size: usize,
@@ -65,7 +65,7 @@ impl AccountPrettyPool {
pub fn new(initial_size: usize, max_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(initial_size);
// 预分配对象
// Pre-allocate objects
for _ in 0..initial_size {
pool.push_back(Box::new(AccountPretty::default()));
}
@@ -84,7 +84,7 @@ impl AccountPrettyPool {
}
}
/// 带自动归还的 AccountPretty
/// AccountPretty with automatic return
pub struct PooledAccountPretty {
account: Box<AccountPretty>,
pool: Arc<Mutex<VecDeque<Box<AccountPretty>>>>,
@@ -92,7 +92,7 @@ pub struct PooledAccountPretty {
}
impl PooledAccountPretty {
/// 从 gRPC 更新重置数据
/// Reset data from gRPC update
pub fn reset_from_update(&mut self, account_update: SubscribeUpdateAccount) {
let account_info = account_update.account.unwrap();
@@ -109,7 +109,7 @@ impl PooledAccountPretty {
self.account.owner = Pubkey::try_from(account_info.owner.as_slice()).expect("valid pubkey");
self.account.rent_epoch = account_info.rent_epoch;
// 优化数据字段的重用
// Optimize data field reuse
let new_data = account_info.data;
if self.account.data.capacity() >= new_data.len() {
self.account.data.clear();
@@ -126,7 +126,7 @@ impl Drop for PooledAccountPretty {
fn drop(&mut self) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
// 清理敏感数据
// Clear sensitive data
self.account.data.clear();
self.account.signature = Signature::default();
self.account.pubkey = Pubkey::default();
@@ -150,7 +150,7 @@ impl std::ops::DerefMut for PooledAccountPretty {
}
}
/// BlockMetaPretty 对象池
/// BlockMetaPretty object pool
pub struct BlockMetaPrettyPool {
pool: Arc<Mutex<VecDeque<Box<BlockMetaPretty>>>>,
max_size: usize,
@@ -160,7 +160,7 @@ impl BlockMetaPrettyPool {
pub fn new(initial_size: usize, max_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(initial_size);
// 预分配对象
// Pre-allocate objects
for _ in 0..initial_size {
pool.push_back(Box::new(BlockMetaPretty::default()));
}
@@ -179,7 +179,7 @@ impl BlockMetaPrettyPool {
}
}
/// 带自动归还的 BlockMetaPretty
/// BlockMetaPretty with automatic return
pub struct PooledBlockMetaPretty {
block_meta: Box<BlockMetaPretty>,
pool: Arc<Mutex<VecDeque<Box<BlockMetaPretty>>>>,
@@ -187,7 +187,7 @@ pub struct PooledBlockMetaPretty {
}
impl PooledBlockMetaPretty {
/// 从 gRPC 更新重置数据
/// Reset data from gRPC update
pub fn reset_from_update(
&mut self,
block_update: SubscribeUpdateBlockMeta,
@@ -204,7 +204,7 @@ impl Drop for PooledBlockMetaPretty {
fn drop(&mut self) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
// 清理数据
// Clear data
self.block_meta.block_hash.clear();
self.block_meta.block_time = None;
pool.push_back(std::mem::take(&mut self.block_meta));
@@ -226,7 +226,7 @@ impl std::ops::DerefMut for PooledBlockMetaPretty {
}
}
/// TransactionPretty 对象池
/// TransactionPretty object pool
pub struct TransactionPrettyPool {
pool: Arc<Mutex<VecDeque<Box<TransactionPretty>>>>,
max_size: usize,
@@ -236,7 +236,7 @@ impl TransactionPrettyPool {
pub fn new(initial_size: usize, max_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(initial_size);
// 预分配对象
// Pre-allocate objects
for _ in 0..initial_size {
pool.push_back(Box::new(TransactionPretty::default()));
}
@@ -259,7 +259,7 @@ impl TransactionPrettyPool {
}
}
/// 带自动归还的 TransactionPretty
/// TransactionPretty with automatic return
pub struct PooledTransactionPretty {
transaction: Box<TransactionPretty>,
pool: Arc<Mutex<VecDeque<Box<TransactionPretty>>>>,
@@ -267,7 +267,7 @@ pub struct PooledTransactionPretty {
}
impl PooledTransactionPretty {
/// 从 gRPC 更新重置数据
/// Reset data from gRPC update
pub fn reset_from_update(
&mut self,
tx_update: SubscribeUpdateTransaction,
@@ -278,7 +278,7 @@ impl PooledTransactionPretty {
self.transaction.slot = tx_update.slot;
self.transaction.transaction_index = Some(tx.index);
self.transaction.block_time = block_time;
self.transaction.block_hash.clear(); // 重置 block_hash
self.transaction.block_hash.clear(); // Reset block_hash
self.transaction.signature =
Signature::try_from(tx.signature.as_slice()).expect("valid signature");
self.transaction.is_vote = tx.is_vote;
@@ -291,7 +291,7 @@ impl Drop for PooledTransactionPretty {
fn drop(&mut self) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
// 清理数据
// Clear data
self.transaction.block_hash.clear();
self.transaction.block_time = None;
self.transaction.signature = Signature::default();
@@ -314,7 +314,7 @@ impl std::ops::DerefMut for PooledTransactionPretty {
}
}
/// EventPretty 对象池(组合池)
/// EventPretty object pool (composite pool)
pub struct EventPrettyPool {
account_pool: AccountPrettyPool,
block_pool: BlockMetaPrettyPool,
@@ -330,23 +330,23 @@ impl EventPrettyPool {
}
}
/// 获取账户事件对象
/// Get account event object
pub fn acquire_account(&self) -> PooledAccountPretty {
self.account_pool.acquire()
}
/// 获取区块事件对象
/// Get block event object
pub fn acquire_block(&self) -> PooledBlockMetaPretty {
self.block_pool.acquire()
}
/// 获取交易事件对象
/// Get transaction event object
pub fn acquire_transaction(&self) -> PooledTransactionPretty {
self.transaction_pool.acquire()
}
}
/// 对象池管理器(单例)
/// Object pool manager (singleton)
pub struct PoolManager {
event_pool: EventPrettyPool,
}
@@ -367,18 +367,18 @@ impl Default for PoolManager {
}
}
/// 工厂函数用于创建优化的 EventPretty
/// Factory functions for creating optimized EventPretty
impl EventPrettyPool {
/// 创建账户事件 - 使用对象池优化
/// Create account event - optimized with object pool
pub fn create_account_event_optimized(&self, update: SubscribeUpdateAccount) -> AccountPretty {
let mut pooled_account = self.acquire_account();
pooled_account.reset_from_update(update);
// 移动数据而不是克隆,避免多余的内存分配
// Move data instead of cloning to avoid unnecessary memory allocation
let result = std::mem::replace(pooled_account.deref_mut(), AccountPretty::default());
result
}
/// 创建区块事件 - 使用对象池优化
/// Create block event - optimized with object pool
pub fn create_block_event_optimized(
&self,
update: SubscribeUpdateBlockMeta,
@@ -386,12 +386,12 @@ impl EventPrettyPool {
) -> BlockMetaPretty {
let mut pooled_block = self.acquire_block();
pooled_block.reset_from_update(update, block_time);
// 移动数据而不是克隆
// Move data instead of cloning
let result = std::mem::replace(pooled_block.deref_mut(), BlockMetaPretty::default());
result
}
/// 创建交易事件 - 使用对象池优化
/// Create transaction event - optimized with object pool
pub fn create_transaction_event_optimized(
&self,
update: SubscribeUpdateTransaction,
@@ -399,27 +399,27 @@ impl EventPrettyPool {
) -> TransactionPretty {
let mut pooled_tx = self.acquire_transaction();
pooled_tx.reset_from_update(update, block_time);
// 移动数据而不是克隆
// Move data instead of cloning
let result = std::mem::replace(pooled_tx.deref_mut(), TransactionPretty::default());
result
}
}
// 全局池管理器实例
// Global pool manager instance
lazy_static::lazy_static! {
pub static ref GLOBAL_POOL_MANAGER: PoolManager = PoolManager::new();
}
/// 便捷的全局工厂函数
/// Convenient global factory functions
pub mod factory {
use super::*;
/// 使用对象池创建账户事件(推荐用于高性能场景)
/// Create account event using object pool (recommended for high-performance scenarios)
pub fn create_account_pretty_pooled(update: SubscribeUpdateAccount) -> AccountPretty {
GLOBAL_POOL_MANAGER.get_event_pool().create_account_event_optimized(update)
}
/// 使用对象池创建区块事件(推荐用于高性能场景)
/// Create block event using object pool (recommended for high-performance scenarios)
pub fn create_block_meta_pretty_pooled(
update: SubscribeUpdateBlockMeta,
block_time: Option<Timestamp>,
@@ -427,7 +427,7 @@ pub mod factory {
GLOBAL_POOL_MANAGER.get_event_pool().create_block_event_optimized(update, block_time)
}
/// 使用对象池创建交易事件(推荐用于高性能场景)
/// Create transaction event using object pool (recommended for high-performance scenarios)
pub fn create_transaction_pretty_pooled(
update: SubscribeUpdateTransaction,
block_time: Option<Timestamp>,
+3 -3
View File
@@ -68,7 +68,7 @@ impl fmt::Debug for BlockMetaPretty {
#[derive(Clone)]
pub struct TransactionPretty {
pub slot: u64,
pub transaction_index: Option<u64>, // 新增:交易在slot中的索引
pub transaction_index: Option<u64>, // New: transaction index within the slot
pub block_hash: String,
pub block_time: Option<Timestamp>,
pub signature: Signature,
@@ -149,11 +149,11 @@ impl Default for TransactionPretty {
// ),
// ) -> Self {
// let tx = transaction.expect("should be defined");
// // 根据用户说明,交易索引在 transaction.index
// // According to user notes, transaction index is in transaction.index
// let transaction_index = tx.index;
// Self {
// slot,
// transaction_index: Some(transaction_index), // 提取交易索引
// transaction_index: Some(transaction_index), // Extract transaction index
// block_time,
// block_hash: String::new(),
// signature: Signature::try_from(tx.signature.as_slice()).expect("valid signature"),
+10 -10
View File
@@ -8,7 +8,7 @@ use crate::streaming::common::{
MetricsManager, PerformanceMetrics, StreamClientConfig, SubscriptionHandle,
};
/// ShredStream gRPC 客户端
/// ShredStream gRPC client
#[derive(Clone)]
pub struct ShredStreamGrpc {
pub shredstream_client: Arc<ShredstreamProxyClient<Channel>>,
@@ -17,12 +17,12 @@ pub struct ShredStreamGrpc {
}
impl ShredStreamGrpc {
/// 创建客户端,使用默认配置
/// Create client with default configuration
pub async fn new(endpoint: String) -> AnyResult<Self> {
Self::new_with_config(endpoint, StreamClientConfig::default()).await
}
/// 创建客户端,使用自定义配置
/// Create client with custom configuration
pub async fn new_with_config(endpoint: String, config: StreamClientConfig) -> AnyResult<Self> {
let shredstream_client = ShredstreamProxyClient::connect(endpoint.clone()).await?;
MetricsManager::init(config.enable_metrics);
@@ -33,37 +33,37 @@ impl ShredStreamGrpc {
})
}
/// 获取当前配置
/// Get current configuration
pub fn get_config(&self) -> &StreamClientConfig {
&self.config
}
/// 更新配置
/// Update configuration
pub fn update_config(&mut self, config: StreamClientConfig) {
self.config = config;
}
/// 获取性能指标
/// Get performance metrics
pub fn get_metrics(&self) -> PerformanceMetrics {
MetricsManager::global().get_metrics()
}
/// 启用或禁用性能监控
/// Enable or disable performance monitoring
pub fn set_enable_metrics(&mut self, enabled: bool) {
self.config.enable_metrics = enabled;
}
/// 打印性能指标
/// Print performance metrics
pub fn print_metrics(&self) {
MetricsManager::global().print_metrics();
}
/// 启动自动性能监控任务
/// Start automatic performance monitoring task
pub async fn start_auto_metrics_monitoring(&self) {
MetricsManager::global().start_auto_monitoring().await;
}
/// 停止当前订阅
/// Stop current subscription
pub async fn stop(&self) {
let mut handle_guard = self.subscription_handle.lock().await;
if let Some(handle) = handle_guard.take() {
+3 -3
View File
@@ -1,14 +1,14 @@
// ShredStream 相关模块
// ShredStream related modules
pub mod connection;
pub mod pool;
pub mod types;
// 重新导出主要类型
// Re-export main types
pub use connection::*;
pub use pool::*;
pub use types::*;
// 从公用模块重新导出
// Re-export from common modules
pub use crate::streaming::common::{
ConnectionConfig, MetricsEventType, MetricsManager, PerformanceMetrics, StreamClientConfig,
};
+15 -15
View File
@@ -6,7 +6,7 @@ use solana_sdk::transaction::VersionedTransaction;
use super::TransactionWithSlot;
/// TransactionWithSlot 对象池
/// TransactionWithSlot object pool
pub struct TransactionWithSlotPool {
pool: Arc<Mutex<VecDeque<Box<TransactionWithSlot>>>>,
max_size: usize,
@@ -16,7 +16,7 @@ impl TransactionWithSlotPool {
pub fn new(initial_size: usize, max_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(initial_size);
// 预分配对象
// Pre-allocate objects
for _ in 0..initial_size {
pool.push_back(Box::new(TransactionWithSlot::default()));
}
@@ -39,7 +39,7 @@ impl TransactionWithSlotPool {
}
}
/// 带自动归还的 TransactionWithSlot
/// TransactionWithSlot with automatic return
pub struct PooledTransactionWithSlot {
transaction: Box<TransactionWithSlot>,
pool: Arc<Mutex<VecDeque<Box<TransactionWithSlot>>>>,
@@ -47,7 +47,7 @@ pub struct PooledTransactionWithSlot {
}
impl PooledTransactionWithSlot {
/// 从原始数据重置
/// Reset from raw data
pub fn reset_from_data(
&mut self,
transaction: VersionedTransaction,
@@ -59,9 +59,9 @@ impl PooledTransactionWithSlot {
self.transaction.recv_us = recv_us;
}
/// 使用优化的工厂方法创建 TransactionWithSlot(移动数据而不是克隆)
/// Create TransactionWithSlot using optimized factory method (move data instead of cloning)
pub fn into_transaction_with_slot(mut self) -> TransactionWithSlot {
// 移动数据而不是克隆,避免多余的内存分配
// Move data instead of cloning to avoid unnecessary memory allocation
std::mem::replace(self.deref_mut(), TransactionWithSlot::default())
}
}
@@ -70,10 +70,10 @@ impl Drop for PooledTransactionWithSlot {
fn drop(&mut self) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
// 清理敏感数据
// Clear sensitive data
self.transaction.slot = 0;
self.transaction.recv_us = 0;
// 重置交易为默认值以清理敏感数据
// Reset transaction to default to clear sensitive data
self.transaction.transaction = VersionedTransaction::default();
pool.push_back(std::mem::take(&mut self.transaction));
}
@@ -94,7 +94,7 @@ impl std::ops::DerefMut for PooledTransactionWithSlot {
}
}
/// Shred 对象池管理器
/// Shred object pool manager
pub struct ShredPoolManager {
transaction_pool: TransactionWithSlotPool,
}
@@ -103,8 +103,8 @@ impl ShredPoolManager {
pub fn new() -> Self {
Self {
transaction_pool: TransactionWithSlotPool::new(
5000, // 初始大小 - Shred 事件通常较多
15000, // 最大大小
5000, // Initial size - Shred events are usually numerous
15000, // Max size
),
}
}
@@ -113,7 +113,7 @@ impl ShredPoolManager {
&self.transaction_pool
}
/// 创建优化的 TransactionWithSlot
/// Create optimized TransactionWithSlot
pub fn create_transaction_with_slot_optimized(
&self,
transaction: VersionedTransaction,
@@ -132,16 +132,16 @@ impl Default for ShredPoolManager {
}
}
// 全局 Shred 池管理器实例
// Global Shred pool manager instance
lazy_static::lazy_static! {
pub static ref GLOBAL_SHRED_POOL_MANAGER: ShredPoolManager = ShredPoolManager::new();
}
/// 便捷的全局工厂函数
/// Convenient global factory functions
pub mod factory {
use super::*;
/// 使用对象池创建 TransactionWithSlot(推荐用于高性能场景)
/// Create TransactionWithSlot using object pool (recommended for high-performance scenarios)
pub fn create_transaction_with_slot_pooled(
transaction: VersionedTransaction,
slot: u64,
+2 -2
View File
@@ -1,6 +1,6 @@
use solana_sdk::transaction::VersionedTransaction;
/// 携带槽位信息的交易
/// Transaction with slot information
#[derive(Debug, Clone, Default)]
pub struct TransactionWithSlot {
pub transaction: VersionedTransaction,
@@ -9,7 +9,7 @@ pub struct TransactionWithSlot {
}
impl TransactionWithSlot {
/// 创建新的带槽位的交易
/// Create new transaction with slot
pub fn new(
transaction: VersionedTransaction,
slot: u64,
+5 -5
View File
@@ -17,7 +17,7 @@ use solana_entry::entry::Entry;
use super::ShredStreamGrpc;
impl ShredStreamGrpc {
/// 订阅ShredStream事件(支持批处理和即时处理)
/// Subscribe to ShredStream events (supports batch and real-time processing)
pub async fn shredstream_subscribe<F>(
&self,
protocols: Vec<Protocol>,
@@ -28,16 +28,16 @@ impl ShredStreamGrpc {
where
F: Fn(DexEvent) + Send + Sync + 'static,
{
// 如果已有活跃订阅,先停止它
// If there's an active subscription, stop it first
self.stop().await;
let mut metrics_handle = None;
// 启动自动性能监控(如果启用)
// Start automatic performance monitoring (if enabled)
if self.config.enable_metrics {
metrics_handle = MetricsManager::global().start_auto_monitoring().await;
}
// 启动流处理
// Start stream processing
let mut client = (*self.shredstream_client).clone();
let request = tonic::Request::new(SubscribeEntriesRequest {});
let mut stream = client.subscribe_entries(request).await?.into_inner();
@@ -83,7 +83,7 @@ impl ShredStreamGrpc {
}
});
// 保存订阅句柄
// Save subscription handle
let subscription_handle = SubscriptionHandle::new(stream_task, None, metrics_handle);
let mut handle_guard = self.subscription_handle.lock().await;
*handle_guard = Some(subscription_handle);
+17 -17
View File
@@ -21,7 +21,7 @@ use yellowstone_grpc_proto::geyser::{
CommitmentLevel, SubscribeRequest, SubscribeRequestFilterAccountsFilter, SubscribeRequestPing,
};
/// 交易过滤器
/// Transaction filter
#[derive(Debug, Clone)]
pub struct TransactionFilter {
pub account_include: Vec<String>,
@@ -29,7 +29,7 @@ pub struct TransactionFilter {
pub account_required: Vec<String>,
}
/// 账户过滤器
/// Account filter
#[derive(Debug, Clone)]
pub struct AccountFilter {
pub account: Vec<String>,
@@ -52,12 +52,12 @@ pub struct YellowstoneGrpc {
}
impl YellowstoneGrpc {
/// 创建客户端,使用默认配置
/// Create client with default configuration
pub fn new(endpoint: String, x_token: Option<String>) -> AnyResult<Self> {
Self::new_with_config(endpoint, x_token, StreamClientConfig::default())
}
/// 创建客户端,使用自定义配置
/// Create client with custom configuration
pub fn new_with_config(
endpoint: String,
x_token: Option<String>,
@@ -81,32 +81,32 @@ impl YellowstoneGrpc {
})
}
/// 获取配置
/// Get configuration
pub fn get_config(&self) -> &StreamClientConfig {
&self.config
}
/// 更新配置
/// Update configuration
pub fn update_config(&mut self, config: StreamClientConfig) {
self.config = config;
}
/// 获取性能指标
/// Get performance metrics
pub fn get_metrics(&self) -> PerformanceMetrics {
MetricsManager::global().get_metrics()
}
/// 打印性能指标
/// Print performance metrics
pub fn print_metrics(&self) {
MetricsManager::global().print_metrics();
}
/// 启用或禁用性能监控
/// Enable or disable performance monitoring
pub fn set_enable_metrics(&mut self, enabled: bool) {
self.config.enable_metrics = enabled;
}
/// 停止当前订阅
/// Stop current subscription
pub async fn stop(&self) {
let mut handle_guard = self.subscription_handle.lock().await;
if let Some(handle) = handle_guard.take() {
@@ -153,7 +153,7 @@ impl YellowstoneGrpc {
}
let mut metrics_handle = None;
// 启动自动性能监控(如果启用)
// Start automatic performance monitoring (if enabled)
if self.config.enable_metrics {
metrics_handle = MetricsManager::global().start_auto_monitoring().await;
}
@@ -165,13 +165,13 @@ impl YellowstoneGrpc {
.subscription_manager
.subscribe_with_account_request(account_filter, event_type_filter.as_ref());
// 订阅事件
// Subscribe to events
let (subscribe_tx, mut stream, subscribe_request) = self
.subscription_manager
.subscribe_with_request(transactions, accounts, commitment, event_type_filter.as_ref())
.await?;
// Arc<Mutex<>> 包装 subscribe_tx 以支持多线程共享
// Wrap subscribe_tx with Arc<Mutex<>> to support multi-threaded sharing
let subscribe_tx = Arc::new(Mutex::new(subscribe_tx));
*self.current_request.write().await = Some(subscribe_request);
let (control_tx, mut control_rx) = mpsc::channel(100);
@@ -238,7 +238,7 @@ impl YellowstoneGrpc {
}
}
Some(UpdateOneof::Ping(_)) => {
// 只在需要时获取锁,并立即释放
// Only acquire lock when needed and release immediately
if let Ok(mut tx_guard) = subscribe_tx.try_lock() {
let _ = tx_guard
.send(SubscribeRequest {
@@ -274,7 +274,7 @@ impl YellowstoneGrpc {
}
});
// 保存订阅句柄
// Save subscription handle
let subscription_handle = SubscriptionHandle::new(stream_handle, None, metrics_handle);
let mut handle_guard = self.subscription_handle.lock().await;
*handle_guard = Some(subscription_handle);
@@ -343,7 +343,7 @@ impl YellowstoneGrpc {
}
}
// 实现 Clone trait 以支持模块间共享
// Implement Clone trait to support sharing between modules
impl Clone for YellowstoneGrpc {
fn clone(&self) -> Self {
Self {
@@ -351,7 +351,7 @@ impl Clone for YellowstoneGrpc {
x_token: self.x_token.clone(),
config: self.config.clone(),
subscription_manager: self.subscription_manager.clone(),
subscription_handle: self.subscription_handle.clone(), // 共享同一个 Arc<Mutex<>>
subscription_handle: self.subscription_handle.clone(), // Share the same Arc<Mutex<>>
active_subscription: self.active_subscription.clone(),
control_tx: self.control_tx.clone(),
event_type_filter: self.event_type_filter.clone(),