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