Skip to main content

sol_parser_sdk/logs/
pump.rs

1//! PumpFun 极限优化解析器 - 微秒/纳秒级性能
2//!
3//! 优化策略:
4//! - 零拷贝解析 (zero-copy)
5//! - 栈分配替代堆分配
6//! - unsafe 消除边界检查
7//! - 编译器自动向量化 (target-cpu=native)
8//! - 内联所有热路径
9//! - 编译时计算
10//! - 内存预取 (CPU cache optimization)
11
12use crate::core::events::*;
13use memchr::memmem;
14use once_cell::sync::Lazy;
15use solana_sdk::{pubkey::Pubkey, signature::Signature};
16
17#[cfg(feature = "perf-stats")]
18use std::sync::atomic::{AtomicUsize, Ordering};
19
20// ============================================================================
21// 性能计数器 (可选,用于性能分析)
22// ============================================================================
23
24#[cfg(feature = "perf-stats")]
25pub static PARSE_COUNT: AtomicUsize = AtomicUsize::new(0);
26#[cfg(feature = "perf-stats")]
27pub static PARSE_TIME_NS: AtomicUsize = AtomicUsize::new(0);
28
29// ============================================================================
30// 编译时常量和查找表
31// ============================================================================
32
33/// PumpFun discriminator 常量 (编译时计算)
34pub mod discriminators {
35    pub const CREATE_EVENT: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
36    pub const TRADE_EVENT: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
37    pub const MIGRATE_EVENT: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
38}
39
40/// Base64 查找表预计算 (用于快速解码)
41static BASE64_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: "));
42
43// ============================================================================
44// 零拷贝解析核心 - 使用栈分配
45// ============================================================================
46
47/// 零拷贝提取 program data (栈分配,无堆分配)
48///
49/// 优化: 使用固定大小栈缓冲区,避免 Vec 分配
50/// 缓冲区大小增加到 2KB 以防止 base64-simd 缓冲区溢出panic
51#[inline(always)]
52fn extract_program_data_zero_copy<'a>(log: &'a str, buf: &'a mut [u8; 2048]) -> Option<&'a [u8]> {
53    let log_bytes = log.as_bytes();
54    let pos = BASE64_FINDER.find(log_bytes)?;
55
56    let data_part = &log[pos + 14..];
57    let trimmed = data_part.trim();
58
59    // Validate input size before decoding (base64: 4 chars -> 3 bytes, so max input = (2048/3)*4 = ~2730 chars)
60    // Add safety margin to prevent base64-simd assertion failures
61    if trimmed.len() > 2700 {
62        return None;
63    }
64
65    // SIMD-accelerated base64 decoding (AVX2/SSE4/NEON)
66    use base64_simd::AsOut;
67    let decoded_slice =
68        base64_simd::STANDARD.decode(trimmed.as_bytes(), buf.as_mut().as_out()).ok()?;
69
70    Some(decoded_slice)
71}
72
73/// 快速 discriminator 提取 (SIMD 优化)
74#[inline(always)]
75fn extract_discriminator_simd(log: &str) -> Option<u64> {
76    let log_bytes = log.as_bytes();
77    let pos = BASE64_FINDER.find(log_bytes)?;
78
79    let data_part = &log[pos + 14..];
80    let trimmed = data_part.trim();
81
82    if trimmed.len() < 12 {
83        return None;
84    }
85
86    // 只解码前16字节以获取 discriminator (SIMD-accelerated)
87    use base64_simd::AsOut;
88    let mut buf = [0u8; 12];
89    base64_simd::STANDARD.decode(&trimmed.as_bytes()[..16], buf.as_mut().as_out()).ok()?;
90
91    // 使用 unsafe 读取 u64 (零拷贝,无边界检查)
92    unsafe {
93        let ptr = buf.as_ptr() as *const u64;
94        Some(ptr.read_unaligned())
95    }
96}
97
98// ============================================================================
99// Unsafe 读取函数 - 消除边界检查
100// ============================================================================
101
102/// 读取 u64 (unsafe, 无边界检查)
103#[inline(always)]
104unsafe fn read_u64_unchecked(data: &[u8], offset: usize) -> u64 {
105    let ptr = data.as_ptr().add(offset) as *const u64;
106    u64::from_le(ptr.read_unaligned())
107}
108
109/// 读取 i64 (unsafe, 无边界检查)
110#[inline(always)]
111unsafe fn read_i64_unchecked(data: &[u8], offset: usize) -> i64 {
112    let ptr = data.as_ptr().add(offset) as *const i64;
113    i64::from_le(ptr.read_unaligned())
114}
115
116/// 读取 bool (unsafe, 无边界检查)
117#[inline(always)]
118unsafe fn read_bool_unchecked(data: &[u8], offset: usize) -> bool {
119    *data.get_unchecked(offset) == 1
120}
121
122/// 读取 Pubkey (unsafe, 无边界检查)
123///
124/// 优化: 添加内存预取,假设连续读取多个 Pubkey
125#[inline(always)]
126unsafe fn read_pubkey_unchecked(data: &[u8], offset: usize) -> Pubkey {
127    // 预取下一个可能的 Pubkey 位置 (假设连续读取)
128    // 使用 T0 提示 (最高优先级) 将数据预取到 L1 cache
129    #[cfg(target_arch = "x86_64")]
130    {
131        use std::arch::x86_64::_mm_prefetch;
132        use std::arch::x86_64::_MM_HINT_T0;
133        if offset + 64 < data.len() {
134            _mm_prefetch((data.as_ptr().add(offset + 32)) as *const i8, _MM_HINT_T0);
135        }
136    }
137
138    let ptr = data.as_ptr().add(offset);
139    let mut bytes = [0u8; 32];
140    std::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), 32);
141    Pubkey::new_from_array(bytes)
142}
143
144/// 读取 u32 长度前缀的字符串 (零拷贝,返回 &str)
145///
146/// 优化: 直接返回 &str,避免 String 分配
147#[inline(always)]
148unsafe fn read_str_unchecked(data: &[u8], offset: usize) -> Option<(&str, usize)> {
149    if data.len() < offset + 4 {
150        return None;
151    }
152
153    let len = read_u32_unchecked(data, offset) as usize;
154    if data.len() < offset + 4 + len {
155        return None;
156    }
157
158    let string_bytes = &data[offset + 4..offset + 4 + len];
159    let s = std::str::from_utf8_unchecked(string_bytes);
160    Some((s, 4 + len))
161}
162
163/// 读取 u32 (unsafe, 无边界检查)
164#[inline(always)]
165unsafe fn read_u32_unchecked(data: &[u8], offset: usize) -> u32 {
166    let ptr = data.as_ptr().add(offset) as *const u32;
167    u32::from_le(ptr.read_unaligned())
168}
169
170// ============================================================================
171// 极限优化的事件解析函数
172// ============================================================================
173
174/// 主解析函数 (极限优化版本)
175///
176/// 性能目标: <100ns
177#[inline(always)]
178pub fn parse_log(
179    log: &str,
180    signature: Signature,
181    slot: u64,
182    tx_index: u64,
183    block_time_us: Option<i64>,
184    grpc_recv_us: i64,
185    is_created_buy: bool,
186) -> Option<DexEvent> {
187    #[cfg(feature = "perf-stats")]
188    let start = std::time::Instant::now();
189
190    // 使用栈分配的缓冲区 (增加到 2KB 以防止 base64-simd 缓冲区溢出)
191    let mut buf = [0u8; 2048];
192    let program_data = extract_program_data_zero_copy(log, &mut buf)?;
193
194    if program_data.len() < 8 {
195        return None;
196    }
197
198    // 使用 unsafe 读取 discriminator (SIMD 优化)
199    let discriminator = unsafe { read_u64_unchecked(program_data, 0) };
200    let data = &program_data[8..];
201
202    let result = match discriminator {
203        discriminators::CREATE_EVENT => parse_create_event_optimized(
204            data,
205            signature,
206            slot,
207            tx_index,
208            block_time_us,
209            grpc_recv_us,
210        ),
211        discriminators::TRADE_EVENT => parse_trade_event_optimized(
212            data,
213            signature,
214            slot,
215            tx_index,
216            block_time_us,
217            grpc_recv_us,
218            is_created_buy,
219        ),
220        discriminators::MIGRATE_EVENT => parse_migrate_event_optimized(
221            data,
222            signature,
223            slot,
224            tx_index,
225            block_time_us,
226            grpc_recv_us,
227        ),
228        _ => None,
229    };
230
231    #[cfg(feature = "perf-stats")]
232    {
233        PARSE_COUNT.fetch_add(1, Ordering::Relaxed);
234        PARSE_TIME_NS.fetch_add(start.elapsed().as_nanos() as usize, Ordering::Relaxed);
235    }
236
237    result
238}
239
240/// 解析 CreateEvent (极限优化)
241///
242/// 优化:
243/// - 使用 unsafe 消除所有边界检查
244/// - 零拷贝字符串解析
245/// - 内联所有调用
246#[inline(always)]
247fn parse_create_event_optimized(
248    data: &[u8],
249    signature: Signature,
250    slot: u64,
251    tx_index: u64,
252    block_time_us: Option<i64>,
253    grpc_recv_us: i64,
254) -> Option<DexEvent> {
255    unsafe {
256        let mut offset = 0;
257
258        // 读取字符串字段 (零拷贝)
259        let (name, name_len) = read_str_unchecked(data, offset)?;
260        offset += name_len;
261
262        let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
263        offset += symbol_len;
264
265        let (uri, uri_len) = read_str_unchecked(data, offset)?;
266        offset += uri_len;
267
268        // 快速边界检查
269        if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
270            return None;
271        }
272
273        // 读取 Pubkey 字段
274        let mint = read_pubkey_unchecked(data, offset);
275        offset += 32;
276
277        let bonding_curve = read_pubkey_unchecked(data, offset);
278        offset += 32;
279
280        let user = read_pubkey_unchecked(data, offset);
281        offset += 32;
282
283        let creator = read_pubkey_unchecked(data, offset);
284        offset += 32;
285
286        // 读取数值字段
287        let timestamp = read_i64_unchecked(data, offset);
288        offset += 8;
289
290        let virtual_token_reserves = read_u64_unchecked(data, offset);
291        offset += 8;
292
293        let virtual_sol_reserves = read_u64_unchecked(data, offset);
294        offset += 8;
295
296        let real_token_reserves = read_u64_unchecked(data, offset);
297        offset += 8;
298
299        let token_total_supply = read_u64_unchecked(data, offset);
300        offset += 8;
301
302        let token_program = if offset + 32 <= data.len() {
303            read_pubkey_unchecked(data, offset)
304        } else {
305            Pubkey::default()
306        };
307        offset += 32;
308
309        let is_mayhem_mode =
310            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
311        offset += 1;
312        let is_cashback_enabled =
313            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
314
315        let metadata = EventMetadata {
316            signature,
317            slot,
318            tx_index,
319            block_time_us: block_time_us.unwrap_or(0),
320            grpc_recv_us,
321            recent_blockhash: None,
322        };
323
324        // 将 &str 转换为 String (这是唯一的堆分配)
325        // 优化: 可以考虑使用 SmallString 或 Cow<'static, str> 进一步优化
326        Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
327            metadata,
328            name: name.to_string(),
329            symbol: symbol.to_string(),
330            uri: uri.to_string(),
331            mint,
332            bonding_curve,
333            user,
334            creator,
335            timestamp,
336            virtual_token_reserves,
337            virtual_sol_reserves,
338            real_token_reserves,
339            token_total_supply,
340            token_program,
341            is_mayhem_mode,
342            is_cashback_enabled,
343        }))
344    }
345}
346
347/// 解析 TradeEvent (极限优化)
348///
349/// 根据 ix_name 返回不同的事件类型:
350/// - "buy" -> DexEvent::PumpFunBuy
351/// - "sell" -> DexEvent::PumpFunSell
352/// - "buy_exact_sol_in" -> DexEvent::PumpFunBuyExactSolIn
353/// - 其他/空 -> DexEvent::PumpFunTrade (兼容旧版本)
354#[inline(always)]
355fn parse_trade_event_optimized(
356    data: &[u8],
357    signature: Signature,
358    slot: u64,
359    tx_index: u64,
360    block_time_us: Option<i64>,
361    grpc_recv_us: i64,
362    is_created_buy: bool,
363) -> Option<DexEvent> {
364    unsafe {
365        // 快速边界检查
366        if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
367            return None;
368        }
369
370        let mut offset = 0;
371
372        let mint = read_pubkey_unchecked(data, offset);
373        offset += 32;
374
375        let sol_amount = read_u64_unchecked(data, offset);
376        offset += 8;
377
378        let token_amount = read_u64_unchecked(data, offset);
379        offset += 8;
380
381        let is_buy = read_bool_unchecked(data, offset);
382        offset += 1;
383
384        let user = read_pubkey_unchecked(data, offset);
385        offset += 32;
386
387        let timestamp = read_i64_unchecked(data, offset);
388        offset += 8;
389
390        let virtual_sol_reserves = read_u64_unchecked(data, offset);
391        offset += 8;
392
393        let virtual_token_reserves = read_u64_unchecked(data, offset);
394        offset += 8;
395
396        let real_sol_reserves = read_u64_unchecked(data, offset);
397        offset += 8;
398
399        let real_token_reserves = read_u64_unchecked(data, offset);
400        offset += 8;
401
402        let fee_recipient = read_pubkey_unchecked(data, offset);
403        offset += 32;
404
405        let fee_basis_points = read_u64_unchecked(data, offset);
406        offset += 8;
407
408        let fee = read_u64_unchecked(data, offset);
409        offset += 8;
410
411        let creator = read_pubkey_unchecked(data, offset);
412        offset += 32;
413
414        let creator_fee_basis_points = read_u64_unchecked(data, offset);
415        offset += 8;
416
417        let creator_fee = read_u64_unchecked(data, offset);
418        offset += 8;
419
420        // 可选字段
421        let track_volume =
422            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
423        offset += 1;
424
425        let total_unclaimed_tokens =
426            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
427        offset += 8;
428
429        let total_claimed_tokens =
430            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
431        offset += 8;
432
433        let current_sol_volume =
434            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
435        offset += 8;
436
437        let last_update_timestamp =
438            if offset + 8 <= data.len() { read_i64_unchecked(data, offset) } else { 0 };
439        offset += 8;
440
441        // ix_name: String (4-byte length prefix + content)
442        // Values: "buy" | "sell" | "buy_exact_sol_in"
443        let ix_name = if offset + 4 <= data.len() {
444            if let Some((s, len)) = read_str_unchecked(data, offset) {
445                offset += len;
446                s.to_string()
447            } else {
448                String::new()
449            }
450        } else {
451            String::new()
452        };
453
454        // mayhem_mode: bool (1 byte), cashback_fee_basis_points (8), cashback (8) - PUMP_CASHBACK_README
455        let mayhem_mode =
456            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
457        offset += 1;
458        let cashback_fee_basis_points =
459            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
460        offset += 8;
461        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
462
463        let metadata = EventMetadata {
464            signature,
465            slot,
466            tx_index,
467            block_time_us: block_time_us.unwrap_or(0),
468            grpc_recv_us,
469            recent_blockhash: None,
470        };
471
472        let trade_event = PumpFunTradeEvent {
473            metadata,
474            mint,
475            sol_amount,
476            token_amount,
477            is_buy,
478            is_created_buy,
479            user,
480            timestamp,
481            virtual_sol_reserves,
482            virtual_token_reserves,
483            real_sol_reserves,
484            real_token_reserves,
485            fee_recipient,
486            fee_basis_points,
487            fee,
488            creator,
489            creator_fee_basis_points,
490            creator_fee,
491            track_volume,
492            total_unclaimed_tokens,
493            total_claimed_tokens,
494            current_sol_volume,
495            last_update_timestamp,
496            ix_name: ix_name.clone(),
497            mayhem_mode,
498            cashback_fee_basis_points,
499            cashback,
500            is_cashback_coin: cashback_fee_basis_points > 0,
501            bonding_curve: Pubkey::default(),
502            associated_bonding_curve: Pubkey::default(),
503            creator_vault: Pubkey::default(),
504            token_program: Pubkey::default(),
505            account: None,
506        };
507
508        // 根据 ix_name 返回不同的事件类型,支持用户过滤特定交易类型
509        match ix_name.as_str() {
510            "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
511            "sell" => Some(DexEvent::PumpFunSell(trade_event)),
512            "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
513            _ => Some(DexEvent::PumpFunTrade(trade_event)), // 兼容旧版本或未知类型
514        }
515    }
516}
517
518/// 解析 MigrateEvent (极限优化)
519#[inline(always)]
520fn parse_migrate_event_optimized(
521    data: &[u8],
522    signature: Signature,
523    slot: u64,
524    tx_index: u64,
525    block_time_us: Option<i64>,
526    grpc_recv_us: i64,
527) -> Option<DexEvent> {
528    unsafe {
529        // 快速边界检查
530        if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
531            return None;
532        }
533
534        let mut offset = 0;
535
536        let user = read_pubkey_unchecked(data, offset);
537        offset += 32;
538
539        let mint = read_pubkey_unchecked(data, offset);
540        offset += 32;
541
542        let mint_amount = read_u64_unchecked(data, offset);
543        offset += 8;
544
545        let sol_amount = read_u64_unchecked(data, offset);
546        offset += 8;
547
548        let pool_migration_fee = read_u64_unchecked(data, offset);
549        offset += 8;
550
551        let bonding_curve = read_pubkey_unchecked(data, offset);
552        offset += 32;
553
554        let timestamp = read_i64_unchecked(data, offset);
555        offset += 8;
556
557        let pool = read_pubkey_unchecked(data, offset);
558
559        let metadata = EventMetadata {
560            signature,
561            slot,
562            tx_index,
563            block_time_us: block_time_us.unwrap_or(0),
564            grpc_recv_us,
565            recent_blockhash: None,
566        };
567
568        Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
569            metadata,
570            user,
571            mint,
572            mint_amount,
573            sol_amount,
574            pool_migration_fee,
575            bonding_curve,
576            timestamp,
577            pool,
578        }))
579    }
580}
581
582// ============================================================================
583// 快速过滤 API (用于事件过滤场景)
584// ============================================================================
585
586/// 快速判断事件类型 (只解析 discriminator)
587///
588/// 性能: <50ns
589#[inline(always)]
590pub fn get_event_type_fast(log: &str) -> Option<u64> {
591    extract_discriminator_simd(log)
592}
593
594/// 检查是否为特定事件类型 (SIMD 优化)
595#[inline(always)]
596pub fn is_event_type(log: &str, discriminator: u64) -> bool {
597    extract_discriminator_simd(log) == Some(discriminator)
598}
599
600// ============================================================================
601// Public API for optimized parsing from pre-decoded data
602// These functions accept already-decoded data (without discriminator)
603// ============================================================================
604
605/// Parse PumpFun Trade event from pre-decoded data
606///
607/// `data` should be the decoded bytes AFTER the 8-byte discriminator
608///
609/// Returns different event types based on ix_name:
610/// - "buy" -> DexEvent::PumpFunBuy
611/// - "sell" -> DexEvent::PumpFunSell
612/// - "buy_exact_sol_in" -> DexEvent::PumpFunBuyExactSolIn
613/// - other/empty -> DexEvent::PumpFunTrade (backward compatible)
614#[inline(always)]
615pub fn parse_trade_from_data(
616    data: &[u8],
617    metadata: EventMetadata,
618    is_created_buy: bool,
619) -> Option<DexEvent> {
620    unsafe {
621        // 快速边界检查
622        if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
623            return None;
624        }
625
626        let mut offset = 0;
627
628        let mint = read_pubkey_unchecked(data, offset);
629        offset += 32;
630
631        let sol_amount = read_u64_unchecked(data, offset);
632        offset += 8;
633
634        let token_amount = read_u64_unchecked(data, offset);
635        offset += 8;
636
637        let is_buy = read_bool_unchecked(data, offset);
638        offset += 1;
639
640        let user = read_pubkey_unchecked(data, offset);
641        offset += 32;
642
643        let timestamp = read_i64_unchecked(data, offset);
644        offset += 8;
645
646        let virtual_sol_reserves = read_u64_unchecked(data, offset);
647        offset += 8;
648
649        let virtual_token_reserves = read_u64_unchecked(data, offset);
650        offset += 8;
651
652        let real_sol_reserves = read_u64_unchecked(data, offset);
653        offset += 8;
654
655        let real_token_reserves = read_u64_unchecked(data, offset);
656        offset += 8;
657
658        let fee_recipient = read_pubkey_unchecked(data, offset);
659        offset += 32;
660
661        let fee_basis_points = read_u64_unchecked(data, offset);
662        offset += 8;
663
664        let fee = read_u64_unchecked(data, offset);
665        offset += 8;
666
667        let creator = read_pubkey_unchecked(data, offset);
668        offset += 32;
669
670        let creator_fee_basis_points = read_u64_unchecked(data, offset);
671        offset += 8;
672
673        let creator_fee = read_u64_unchecked(data, offset);
674        offset += 8;
675
676        // 可选字段
677        let track_volume =
678            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
679        offset += 1;
680
681        let total_unclaimed_tokens =
682            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
683        offset += 8;
684
685        let total_claimed_tokens =
686            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
687        offset += 8;
688
689        let current_sol_volume =
690            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
691        offset += 8;
692
693        let last_update_timestamp =
694            if offset + 8 <= data.len() { read_i64_unchecked(data, offset) } else { 0 };
695        offset += 8;
696
697        let ix_name = if offset + 4 <= data.len() {
698            if let Some((s, len)) = read_str_unchecked(data, offset) {
699                offset += len;
700                s.to_string()
701            } else {
702                String::new()
703            }
704        } else {
705            String::new()
706        };
707
708        // mayhem_mode (1), cashback_fee_basis_points (8), cashback (8) - PUMP_CASHBACK_README
709        let mayhem_mode =
710            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
711        offset += 1;
712        let cashback_fee_basis_points =
713            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
714        offset += 8;
715        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
716
717        let trade_event = PumpFunTradeEvent {
718            metadata,
719            mint,
720            sol_amount,
721            token_amount,
722            is_buy,
723            is_created_buy,
724            user,
725            timestamp,
726            virtual_sol_reserves,
727            virtual_token_reserves,
728            real_sol_reserves,
729            real_token_reserves,
730            fee_recipient,
731            fee_basis_points,
732            fee,
733            creator,
734            creator_fee_basis_points,
735            creator_fee,
736            track_volume,
737            total_unclaimed_tokens,
738            total_claimed_tokens,
739            current_sol_volume,
740            last_update_timestamp,
741            ix_name: ix_name.clone(),
742            mayhem_mode,
743            cashback_fee_basis_points,
744            cashback,
745            is_cashback_coin: cashback_fee_basis_points > 0,
746            bonding_curve: Pubkey::default(),
747            associated_bonding_curve: Pubkey::default(),
748            creator_vault: Pubkey::default(),
749            token_program: Pubkey::default(),
750            account: None,
751        };
752
753        // 根据 ix_name 返回不同的事件类型
754        match ix_name.as_str() {
755            "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
756            "sell" => Some(DexEvent::PumpFunSell(trade_event)),
757            "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
758            _ => Some(DexEvent::PumpFunTrade(trade_event)),
759        }
760    }
761}
762
763/// Parse only PumpFun Buy events from pre-decoded data
764///
765/// Returns None if the event is not a buy event
766#[inline(always)]
767pub fn parse_buy_from_data(
768    data: &[u8],
769    metadata: EventMetadata,
770    is_created_buy: bool,
771) -> Option<DexEvent> {
772    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
773    match &event {
774        DexEvent::PumpFunBuy(_) => Some(event),
775        _ => None,
776    }
777}
778
779/// Parse only PumpFun Sell events from pre-decoded data
780///
781/// Returns None if the event is not a sell event
782#[inline(always)]
783pub fn parse_sell_from_data(
784    data: &[u8],
785    metadata: EventMetadata,
786    is_created_buy: bool,
787) -> Option<DexEvent> {
788    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
789    match &event {
790        DexEvent::PumpFunSell(_) => Some(event),
791        _ => None,
792    }
793}
794
795/// Parse only PumpFun BuyExactSolIn events from pre-decoded data
796///
797/// Returns None if the event is not a buy_exact_sol_in event
798#[inline(always)]
799pub fn parse_buy_exact_sol_in_from_data(
800    data: &[u8],
801    metadata: EventMetadata,
802    is_created_buy: bool,
803) -> Option<DexEvent> {
804    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
805    match &event {
806        DexEvent::PumpFunBuyExactSolIn(_) => Some(event),
807        _ => None,
808    }
809}
810
811/// Parse PumpFun Create event from pre-decoded data
812#[inline(always)]
813pub fn parse_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
814    unsafe {
815        let mut offset = 0;
816
817        let (name, name_len) = read_str_unchecked(data, offset)?;
818        offset += name_len;
819
820        let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
821        offset += symbol_len;
822
823        let (uri, uri_len) = read_str_unchecked(data, offset)?;
824        offset += uri_len;
825
826        if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
827            return None;
828        }
829
830        let mint = read_pubkey_unchecked(data, offset);
831        offset += 32;
832
833        let bonding_curve = read_pubkey_unchecked(data, offset);
834        offset += 32;
835
836        let user = read_pubkey_unchecked(data, offset);
837        offset += 32;
838
839        let creator = read_pubkey_unchecked(data, offset);
840        offset += 32;
841
842        let timestamp = read_i64_unchecked(data, offset);
843        offset += 8;
844
845        let virtual_token_reserves = read_u64_unchecked(data, offset);
846        offset += 8;
847
848        let virtual_sol_reserves = read_u64_unchecked(data, offset);
849        offset += 8;
850
851        let real_token_reserves = read_u64_unchecked(data, offset);
852        offset += 8;
853
854        let token_total_supply = read_u64_unchecked(data, offset);
855        offset += 8;
856
857        let token_program = if offset + 32 <= data.len() {
858            read_pubkey_unchecked(data, offset)
859        } else {
860            Pubkey::default()
861        };
862        offset += 32;
863
864        let is_mayhem_mode =
865            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
866        offset += 1;
867        let is_cashback_enabled =
868            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
869
870        Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
871            metadata,
872            name: name.to_string(),
873            symbol: symbol.to_string(),
874            uri: uri.to_string(),
875            mint,
876            bonding_curve,
877            user,
878            creator,
879            timestamp,
880            virtual_token_reserves,
881            virtual_sol_reserves,
882            real_token_reserves,
883            token_total_supply,
884            token_program,
885            is_mayhem_mode,
886            is_cashback_enabled,
887        }))
888    }
889}
890
891/// Parse PumpFun Migrate event from pre-decoded data
892#[inline(always)]
893pub fn parse_migrate_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
894    unsafe {
895        if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
896            return None;
897        }
898
899        let mut offset = 0;
900
901        let user = read_pubkey_unchecked(data, offset);
902        offset += 32;
903
904        let mint = read_pubkey_unchecked(data, offset);
905        offset += 32;
906
907        let mint_amount = read_u64_unchecked(data, offset);
908        offset += 8;
909
910        let sol_amount = read_u64_unchecked(data, offset);
911        offset += 8;
912
913        let pool_migration_fee = read_u64_unchecked(data, offset);
914        offset += 8;
915
916        let bonding_curve = read_pubkey_unchecked(data, offset);
917        offset += 32;
918
919        let timestamp = read_i64_unchecked(data, offset);
920        offset += 8;
921
922        let pool = read_pubkey_unchecked(data, offset);
923
924        Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
925            metadata,
926            user,
927            mint,
928            mint_amount,
929            sol_amount,
930            pool_migration_fee,
931            bonding_curve,
932            timestamp,
933            pool,
934        }))
935    }
936}
937
938// ============================================================================
939// 性能统计 API (可选)
940// ============================================================================
941
942#[cfg(feature = "perf-stats")]
943pub fn get_perf_stats() -> (usize, usize) {
944    let count = PARSE_COUNT.load(Ordering::Relaxed);
945    let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
946    (count, total_ns)
947}
948
949#[cfg(feature = "perf-stats")]
950pub fn reset_perf_stats() {
951    PARSE_COUNT.store(0, Ordering::Relaxed);
952    PARSE_TIME_NS.store(0, Ordering::Relaxed);
953}
954
955#[cfg(test)]
956mod tests {
957    use super::*;
958
959    #[test]
960    fn test_discriminator_simd() {
961        // 测试 SIMD discriminator 提取
962        let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
963        let disc = extract_discriminator_simd(log);
964        assert!(disc.is_some());
965    }
966
967    #[test]
968    fn test_parse_performance() {
969        // 性能测试
970        let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
971        let sig = Signature::default();
972
973        let start = std::time::Instant::now();
974        for _ in 0..1000 {
975            let _ = parse_log(log, sig, 0, 0, Some(0), 0, false);
976        }
977        let elapsed = start.elapsed();
978
979        println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
980    }
981}