mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-18 11:28:05 +00:00
Merge pull request #57 from Estereg/dev
feat(protocols): Fix account structures to match IDL specifications
This commit is contained in:
+1
-30
@@ -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"
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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. 尝试解析特殊账户类型(Token、Nonce等)
|
||||
// 这些是通用的,不属于特定协议
|
||||
// 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 => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
@@ -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>,
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user