Skip to main content

sol_parser_sdk/logs/
pump_amm.rs

1//! PumpSwap (Pump AMM) 极限优化解析器 - 纳秒/微秒级性能
2//!
3//! 优化策略:
4//! - 零拷贝解析 (zero-copy)
5//! - 栈分配替代堆分配
6//! - unsafe 消除边界检查
7//! - 编译器自动向量化 (target-cpu=native)
8//! - 内联所有热路径
9//! - 编译时计算
10//! - 预计算查找表
11//! - L1 cache 优化 (1KB 栈缓冲区)
12
13use crate::core::events::*;
14use memchr::memmem;
15use once_cell::sync::Lazy;
16use solana_sdk::{pubkey::Pubkey, signature::Signature};
17
18#[cfg(feature = "perf-stats")]
19use std::sync::atomic::{AtomicUsize, Ordering};
20
21// ============================================================================
22// 性能计数器 (可选,用于性能分析)
23// ============================================================================
24
25#[cfg(feature = "perf-stats")]
26pub static PARSE_COUNT: AtomicUsize = AtomicUsize::new(0);
27#[cfg(feature = "perf-stats")]
28pub static PARSE_TIME_NS: AtomicUsize = AtomicUsize::new(0);
29
30// ============================================================================
31// 编译时常量和查找表
32// ============================================================================
33
34/// PumpSwap discriminator constants (compile-time computed)
35pub mod discriminators {
36    // Use u64 direct comparison to avoid array comparison
37    // Event discriminators from pump_amm.json
38    pub const BUY: u64 = u64::from_le_bytes([103, 244, 82, 31, 44, 245, 119, 119]);           // BuyEvent
39    pub const SELL: u64 = u64::from_le_bytes([62, 47, 55, 10, 165, 3, 220, 42]);              // SellEvent
40    pub const CREATE_POOL: u64 = u64::from_le_bytes([177, 49, 12, 210, 160, 118, 167, 116]); // CreatePoolEvent
41    pub const ADD_LIQUIDITY: u64 = u64::from_le_bytes([120, 248, 61, 83, 31, 142, 107, 144]); // DepositEvent
42    pub const REMOVE_LIQUIDITY: u64 = u64::from_le_bytes([22, 9, 133, 26, 160, 44, 71, 192]); // WithdrawEvent
43}
44
45/// Base64 查找器预计算 (用于快速定位)
46static BASE64_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: "));
47
48// ============================================================================
49// 零拷贝解析核心 - 使用栈分配
50// ============================================================================
51
52/// 零拷贝提取 program data (栈分配,无堆分配)
53///
54/// 优化: 使用固定大小栈缓冲区,避免 Vec 分配
55/// 缓冲区大小增加到 2KB 以防止 base64-simd 缓冲区溢出panic
56#[inline(always)]
57fn extract_program_data_zero_copy<'a>(log: &'a str, buf: &'a mut [u8; 2048]) -> Option<&'a [u8]> {
58    let log_bytes = log.as_bytes();
59    let pos = BASE64_FINDER.find(log_bytes)?;
60
61    let data_part = &log[pos + 14..];
62    let trimmed = data_part.trim();
63
64    // Validate input size before decoding (base64: 4 chars -> 3 bytes, so max input = (2048/3)*4 = ~2730 chars)
65    // Add safety margin to prevent base64-simd assertion failures
66    if trimmed.len() > 2700 {
67        return None;
68    }
69
70    // SIMD-accelerated base64 decoding (AVX2/SSE4/NEON)
71    use base64_simd::AsOut;
72    let decoded_slice = base64_simd::STANDARD
73        .decode(trimmed.as_bytes(), buf.as_mut().as_out())
74        .ok()?;
75
76    Some(decoded_slice)
77}
78
79/// 快速 discriminator 提取 (SIMD 优化)
80#[inline(always)]
81fn extract_discriminator_simd(log: &str) -> Option<u64> {
82    let log_bytes = log.as_bytes();
83    let pos = BASE64_FINDER.find(log_bytes)?;
84
85    let data_part = &log[pos + 14..];
86    let trimmed = data_part.trim();
87
88    if trimmed.len() < 12 {
89        return None;
90    }
91
92    // 只解码前16字节以获取 discriminator (SIMD-accelerated)
93    use base64_simd::AsOut;
94    let mut buf = [0u8; 12];
95    base64_simd::STANDARD
96        .decode(&trimmed.as_bytes()[..16], buf.as_mut().as_out())
97        .ok()?;
98
99    // 使用 unsafe 读取 u64 (零拷贝,无边界检查)
100    unsafe {
101        let ptr = buf.as_ptr() as *const u64;
102        Some(ptr.read_unaligned())
103    }
104}
105
106// ============================================================================
107// Unsafe 读取函数 - 消除边界检查
108// ============================================================================
109
110/// 读取 u64 (unsafe, 无边界检查)
111#[inline(always)]
112unsafe fn read_u64_unchecked(data: &[u8], offset: usize) -> u64 {
113    let ptr = data.as_ptr().add(offset) as *const u64;
114    u64::from_le(ptr.read_unaligned())
115}
116
117/// 读取 i64 (unsafe, 无边界检查)
118#[inline(always)]
119unsafe fn read_i64_unchecked(data: &[u8], offset: usize) -> i64 {
120    let ptr = data.as_ptr().add(offset) as *const i64;
121    i64::from_le(ptr.read_unaligned())
122}
123
124/// Read u16 (unsafe, no bounds check)
125#[inline(always)]
126unsafe fn read_u16_unchecked(data: &[u8], offset: usize) -> u16 {
127    let ptr = data.as_ptr().add(offset) as *const u16;
128    u16::from_le(ptr.read_unaligned())
129}
130
131/// Read u32 (unsafe, no bounds check)
132#[allow(dead_code)]
133#[inline(always)]
134unsafe fn read_u32_unchecked(data: &[u8], offset: usize) -> u32 {
135    let ptr = data.as_ptr().add(offset) as *const u32;
136    u32::from_le(ptr.read_unaligned())
137}
138
139/// Read u8 (unsafe, no bounds check)
140#[inline(always)]
141unsafe fn read_u8_unchecked(data: &[u8], offset: usize) -> u8 {
142    *data.get_unchecked(offset)
143}
144
145/// 读取 bool (unsafe, 无边界检查)
146#[inline(always)]
147unsafe fn read_bool_unchecked(data: &[u8], offset: usize) -> bool {
148    *data.get_unchecked(offset) == 1
149}
150
151/// 读取 Pubkey (unsafe, 无边界检查)
152///
153/// 优化: 添加内存预取,假设连续读取多个 Pubkey
154#[inline(always)]
155unsafe fn read_pubkey_unchecked(data: &[u8], offset: usize) -> Pubkey {
156    // 预取下一个可能的 Pubkey 位置 (假设连续读取)
157    // 使用 T0 提示 (最高优先级) 将数据预取到 L1 cache
158    #[cfg(target_arch = "x86_64")]
159    {
160        use std::arch::x86_64::_mm_prefetch;
161        use std::arch::x86_64::_MM_HINT_T0;
162        if offset + 64 < data.len() {
163            _mm_prefetch((data.as_ptr().add(offset + 32)) as *const i8, _MM_HINT_T0);
164        }
165    }
166
167    let ptr = data.as_ptr().add(offset);
168    let mut bytes = [0u8; 32];
169    std::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), 32);
170    Pubkey::new_from_array(bytes)
171}
172
173// ============================================================================
174// Optimized event parsing functions
175// ============================================================================
176
177/// Main parse function (optimized)
178///
179/// Performance target: <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) -> Option<DexEvent> {
189    #[cfg(feature = "perf-stats")]
190    let start = std::time::Instant::now();
191
192    // Stack-allocated buffer (增加到 2KB 以防止 base64-simd 缓冲区溢出)
193    let mut buf = [0u8; 2048];
194    let program_data = extract_program_data_zero_copy(log, &mut buf)?;
195
196    if program_data.len() < 8 {
197        return None;
198    }
199
200    // Read discriminator using unsafe (SIMD optimized)
201    let discriminator = unsafe { read_u64_unchecked(program_data, 0) };
202    let data = &program_data[8..];
203
204    let result = match discriminator {
205        discriminators::BUY => {
206            parse_buy_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
207        }
208        discriminators::SELL => {
209            parse_sell_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
210        }
211        discriminators::CREATE_POOL => {
212            parse_create_pool_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
213        }
214        discriminators::ADD_LIQUIDITY => {
215            parse_add_liquidity_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
216        }
217        discriminators::REMOVE_LIQUIDITY => {
218            parse_remove_liquidity_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
219        }
220        _ => None,
221    };
222
223    #[cfg(feature = "perf-stats")]
224    {
225        PARSE_COUNT.fetch_add(1, Ordering::Relaxed);
226        PARSE_TIME_NS.fetch_add(start.elapsed().as_nanos() as usize, Ordering::Relaxed);
227    }
228
229    result
230}
231
232/// Parse buy event (optimized) - BuyEvent from pump_amm.json
233///
234/// Optimizations:
235/// - Use unsafe to eliminate all bounds checks
236/// - Batch bounds check instead of per-field check
237/// - Inline all calls
238#[inline(always)]
239fn parse_buy_event_optimized(
240    data: &[u8],
241    signature: Signature,
242    slot: u64,
243    tx_index: u64,
244    block_time_us: Option<i64>,
245    grpc_recv_us: i64,
246) -> Option<DexEvent> {
247    // Updated size check for new fields: min_base_amount_out (u64) + ix_name (String, variable length)
248    // Minimum size: 14个u64 + 7个Pubkey + 1个bool + 5个u64 (new fields) + 4 bytes (min string length)
249    const MIN_REQUIRED_LEN: usize = 14 * 8 + 7 * 32 + 1 + 5 * 8 + 4;
250    if data.len() < MIN_REQUIRED_LEN {
251        return None;
252    }
253
254    unsafe {
255        let timestamp = read_i64_unchecked(data, 0);
256        let base_amount_out = read_u64_unchecked(data, 8);
257        let max_quote_amount_in = read_u64_unchecked(data, 16);
258        let user_base_token_reserves = read_u64_unchecked(data, 24);
259        let user_quote_token_reserves = read_u64_unchecked(data, 32);
260        let pool_base_token_reserves = read_u64_unchecked(data, 40);
261        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
262        let quote_amount_in = read_u64_unchecked(data, 56);
263        let lp_fee_basis_points = read_u64_unchecked(data, 64);
264        let lp_fee = read_u64_unchecked(data, 72);
265        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
266        let protocol_fee = read_u64_unchecked(data, 88);
267        let quote_amount_in_with_lp_fee = read_u64_unchecked(data, 96);
268        let user_quote_amount_in = read_u64_unchecked(data, 104);
269
270        let pool = read_pubkey_unchecked(data, 112);
271        let user = read_pubkey_unchecked(data, 144);
272        let user_base_token_account = read_pubkey_unchecked(data, 176);
273        let user_quote_token_account = read_pubkey_unchecked(data, 208);
274        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
275        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
276        let coin_creator = read_pubkey_unchecked(data, 304);
277
278        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
279        let coin_creator_fee = read_u64_unchecked(data, 344);
280        let track_volume = read_bool_unchecked(data, 352);
281        let total_unclaimed_tokens = read_u64_unchecked(data, 353);
282        let total_claimed_tokens = read_u64_unchecked(data, 361);
283        let current_sol_volume = read_u64_unchecked(data, 369);
284        let last_update_timestamp = read_i64_unchecked(data, 377);
285
286        // New fields from IDL update
287        let mut offset = 385;
288        let min_base_amount_out = read_u64_unchecked(data, offset);
289        offset += 8;
290
291        // ix_name: String (4-byte length prefix + content)
292        let ix_name = if offset + 4 <= data.len() {
293            let len = read_u32_unchecked(data, offset) as usize;
294            offset += 4;
295            if offset + len <= data.len() {
296                let string_bytes = &data[offset..offset + len];
297                let s = std::str::from_utf8_unchecked(string_bytes);
298                offset += len;
299                s.to_string()
300            } else {
301                String::new()
302            }
303        } else {
304            String::new()
305        };
306
307        // BuyEvent 新增字段 (PUMP_CASHBACK_README): cashback_fee_basis_points, cashback
308        let cashback_fee_basis_points = if offset + 8 <= data.len() {
309            read_u64_unchecked(data, offset)
310        } else {
311            0
312        };
313        offset += 8;
314        let cashback = if offset + 8 <= data.len() {
315            read_u64_unchecked(data, offset)
316        } else {
317            0
318        };
319
320        let metadata = EventMetadata {
321            signature,
322            slot,
323            tx_index,
324            block_time_us: block_time_us.unwrap_or(0),
325            grpc_recv_us,
326            recent_blockhash: None,
327        };
328
329        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
330            metadata,
331            timestamp,
332            base_amount_out,
333            max_quote_amount_in,
334            user_base_token_reserves,
335            user_quote_token_reserves,
336            pool_base_token_reserves,
337            pool_quote_token_reserves,
338            quote_amount_in,
339            lp_fee_basis_points,
340            lp_fee,
341            protocol_fee_basis_points,
342            protocol_fee,
343            quote_amount_in_with_lp_fee,
344            user_quote_amount_in,
345            pool,
346            user,
347            user_base_token_account,
348            user_quote_token_account,
349            protocol_fee_recipient,
350            protocol_fee_recipient_token_account,
351            coin_creator,
352            coin_creator_fee_basis_points,
353            coin_creator_fee,
354            track_volume,
355            total_unclaimed_tokens,
356            total_claimed_tokens,
357            current_sol_volume,
358            last_update_timestamp,
359            min_base_amount_out,
360            ix_name,
361            cashback_fee_basis_points,
362            cashback,
363            ..Default::default()
364        }))
365    }
366}
367
368/// 解析卖出事件 (极限优化)
369#[inline(always)]
370fn parse_sell_event_optimized(
371    data: &[u8],
372    signature: Signature,
373    slot: u64,
374    tx_index: u64,
375    block_time_us: Option<i64>,
376    grpc_recv_us: i64,
377) -> Option<DexEvent> {
378    // 一次性边界检查 (13个u64 + 1个i64 + 7个Pubkey + 2个u64 cashback 字段)
379    const REQUIRED_LEN: usize = 13 * 8 + 8 + 7 * 32 + 8 + 8;
380    if data.len() < REQUIRED_LEN {
381        return None;
382    }
383
384    unsafe {
385        let timestamp = read_i64_unchecked(data, 0);
386        let base_amount_in = read_u64_unchecked(data, 8);
387        let min_quote_amount_out = read_u64_unchecked(data, 16);
388        let user_base_token_reserves = read_u64_unchecked(data, 24);
389        let user_quote_token_reserves = read_u64_unchecked(data, 32);
390        let pool_base_token_reserves = read_u64_unchecked(data, 40);
391        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
392        let quote_amount_out = read_u64_unchecked(data, 56);
393        let lp_fee_basis_points = read_u64_unchecked(data, 64);
394        let lp_fee = read_u64_unchecked(data, 72);
395        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
396        let protocol_fee = read_u64_unchecked(data, 88);
397        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
398        let user_quote_amount_out = read_u64_unchecked(data, 104);
399
400        let pool = read_pubkey_unchecked(data, 112);
401        let user = read_pubkey_unchecked(data, 144);
402        let user_base_token_account = read_pubkey_unchecked(data, 176);
403        let user_quote_token_account = read_pubkey_unchecked(data, 208);
404        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
405        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
406        let coin_creator = read_pubkey_unchecked(data, 304);
407
408        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
409        let coin_creator_fee = read_u64_unchecked(data, 344);
410        // SellEvent 新增字段 (PUMP_CASHBACK_README): cashback_fee_basis_points, cashback
411        let cashback_fee_basis_points = read_u64_unchecked(data, 352);
412        let cashback = read_u64_unchecked(data, 360);
413
414        let metadata = EventMetadata {
415            signature,
416            slot,
417            tx_index,
418            block_time_us: block_time_us.unwrap_or(0),
419            grpc_recv_us,
420            recent_blockhash: None,
421        };
422
423        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
424            metadata,
425            timestamp,
426            base_amount_in,
427            min_quote_amount_out,
428            user_base_token_reserves,
429            user_quote_token_reserves,
430            pool_base_token_reserves,
431            pool_quote_token_reserves,
432            quote_amount_out,
433            lp_fee_basis_points,
434            lp_fee,
435            protocol_fee_basis_points,
436            protocol_fee,
437            quote_amount_out_without_lp_fee,
438            user_quote_amount_out,
439            pool,
440            user,
441            user_base_token_account,
442            user_quote_token_account,
443            protocol_fee_recipient,
444            protocol_fee_recipient_token_account,
445            coin_creator,
446            coin_creator_fee_basis_points,
447            coin_creator_fee,
448            cashback_fee_basis_points,
449            cashback,
450            ..Default::default()
451        }))
452    }
453}
454
455/// 解析池创建事件 (极限优化)
456#[inline(always)]
457fn parse_create_pool_event_optimized(
458    data: &[u8],
459    signature: Signature,
460    slot: u64,
461    tx_index: u64,
462    block_time_us: Option<i64>,
463    grpc_recv_us: i64,
464) -> Option<DexEvent> {
465    // 一次性边界检查 (含 IDL 最后一列 is_mayhem_mode: bool)
466    const REQUIRED_LEN: usize = 8 + 2 + 32 * 6 + 2 + 8 * 7 + 1 + 1;
467    if data.len() < REQUIRED_LEN {
468        return None;
469    }
470
471    unsafe {
472        let timestamp = read_i64_unchecked(data, 0);
473        let index = read_u16_unchecked(data, 8);
474
475        let creator = read_pubkey_unchecked(data, 10);
476        let base_mint = read_pubkey_unchecked(data, 42);
477        let quote_mint = read_pubkey_unchecked(data, 74);
478
479        let base_mint_decimals = read_u8_unchecked(data, 106);
480        let quote_mint_decimals = read_u8_unchecked(data, 107);
481
482        let base_amount_in = read_u64_unchecked(data, 108);
483        let quote_amount_in = read_u64_unchecked(data, 116);
484        let pool_base_amount = read_u64_unchecked(data, 124);
485        let pool_quote_amount = read_u64_unchecked(data, 132);
486        let minimum_liquidity = read_u64_unchecked(data, 140);
487        let initial_liquidity = read_u64_unchecked(data, 148);
488        let lp_token_amount_out = read_u64_unchecked(data, 156);
489
490        let pool_bump = read_u8_unchecked(data, 164);
491
492        let pool = read_pubkey_unchecked(data, 165);
493        let lp_mint = read_pubkey_unchecked(data, 197);
494        let user_base_token_account = read_pubkey_unchecked(data, 229);
495        let user_quote_token_account = read_pubkey_unchecked(data, 261);
496        let coin_creator = read_pubkey_unchecked(data, 293);
497        let is_mayhem_mode = read_bool_unchecked(data, 325);
498
499        let metadata = EventMetadata {
500            signature,
501            slot,
502            tx_index,
503            block_time_us: block_time_us.unwrap_or(0),
504            grpc_recv_us,
505            recent_blockhash: None,
506        };
507
508        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
509            metadata,
510            timestamp,
511            index,
512            creator,
513            base_mint,
514            quote_mint,
515            base_mint_decimals,
516            quote_mint_decimals,
517            base_amount_in,
518            quote_amount_in,
519            pool_base_amount,
520            pool_quote_amount,
521            minimum_liquidity,
522            initial_liquidity,
523            lp_token_amount_out,
524            pool_bump,
525            pool,
526            lp_mint,
527            user_base_token_account,
528            user_quote_token_account,
529            coin_creator,
530            is_mayhem_mode,
531        }))
532    }
533}
534
535/// 解析添加流动性事件 (极限优化)
536#[inline(always)]
537fn parse_add_liquidity_event_optimized(
538    data: &[u8],
539    signature: Signature,
540    slot: u64,
541    tx_index: u64,
542    block_time_us: Option<i64>,
543    grpc_recv_us: i64,
544) -> Option<DexEvent> {
545    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
546    if data.len() < REQUIRED_LEN {
547        return None;
548    }
549
550    unsafe {
551        let timestamp = read_i64_unchecked(data, 0);
552        let lp_token_amount_out = read_u64_unchecked(data, 8);
553        let max_base_amount_in = read_u64_unchecked(data, 16);
554        let max_quote_amount_in = read_u64_unchecked(data, 24);
555        let user_base_token_reserves = read_u64_unchecked(data, 32);
556        let user_quote_token_reserves = read_u64_unchecked(data, 40);
557        let pool_base_token_reserves = read_u64_unchecked(data, 48);
558        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
559        let base_amount_in = read_u64_unchecked(data, 64);
560        let quote_amount_in = read_u64_unchecked(data, 72);
561        let lp_mint_supply = read_u64_unchecked(data, 80);
562
563        let pool = read_pubkey_unchecked(data, 88);
564        let user = read_pubkey_unchecked(data, 120);
565        let user_base_token_account = read_pubkey_unchecked(data, 152);
566        let user_quote_token_account = read_pubkey_unchecked(data, 184);
567        let user_pool_token_account = read_pubkey_unchecked(data, 216);
568
569        let metadata = EventMetadata {
570            signature,
571            slot,
572            tx_index,
573            block_time_us: block_time_us.unwrap_or(0),
574            grpc_recv_us,
575            recent_blockhash: None,
576        };
577
578        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
579            metadata,
580            timestamp,
581            lp_token_amount_out,
582            max_base_amount_in,
583            max_quote_amount_in,
584            user_base_token_reserves,
585            user_quote_token_reserves,
586            pool_base_token_reserves,
587            pool_quote_token_reserves,
588            base_amount_in,
589            quote_amount_in,
590            lp_mint_supply,
591            pool,
592            user,
593            user_base_token_account,
594            user_quote_token_account,
595            user_pool_token_account,
596        }))
597    }
598}
599
600/// 解析移除流动性事件 (极限优化)
601#[inline(always)]
602fn parse_remove_liquidity_event_optimized(
603    data: &[u8],
604    signature: Signature,
605    slot: u64,
606    tx_index: u64,
607    block_time_us: Option<i64>,
608    grpc_recv_us: i64,
609) -> Option<DexEvent> {
610    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
611    if data.len() < REQUIRED_LEN {
612        return None;
613    }
614
615    unsafe {
616        let timestamp = read_i64_unchecked(data, 0);
617        let lp_token_amount_in = read_u64_unchecked(data, 8);
618        let min_base_amount_out = read_u64_unchecked(data, 16);
619        let min_quote_amount_out = read_u64_unchecked(data, 24);
620        let user_base_token_reserves = read_u64_unchecked(data, 32);
621        let user_quote_token_reserves = read_u64_unchecked(data, 40);
622        let pool_base_token_reserves = read_u64_unchecked(data, 48);
623        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
624        let base_amount_out = read_u64_unchecked(data, 64);
625        let quote_amount_out = read_u64_unchecked(data, 72);
626        let lp_mint_supply = read_u64_unchecked(data, 80);
627
628        let pool = read_pubkey_unchecked(data, 88);
629        let user = read_pubkey_unchecked(data, 120);
630        let user_base_token_account = read_pubkey_unchecked(data, 152);
631        let user_quote_token_account = read_pubkey_unchecked(data, 184);
632        let user_pool_token_account = read_pubkey_unchecked(data, 216);
633
634        let metadata = EventMetadata {
635            signature,
636            slot,
637            tx_index,
638            block_time_us: block_time_us.unwrap_or(0),
639            grpc_recv_us,
640            recent_blockhash: None,
641        };
642
643        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
644            metadata,
645            timestamp,
646            lp_token_amount_in,
647            min_base_amount_out,
648            min_quote_amount_out,
649            user_base_token_reserves,
650            user_quote_token_reserves,
651            pool_base_token_reserves,
652            pool_quote_token_reserves,
653            base_amount_out,
654            quote_amount_out,
655            lp_mint_supply,
656            pool,
657            user,
658            user_base_token_account,
659            user_quote_token_account,
660            user_pool_token_account,
661        }))
662    }
663}
664
665// ============================================================================
666// 快速过滤 API (用于事件过滤场景)
667// ============================================================================
668
669/// 快速判断事件类型 (只解析 discriminator)
670///
671/// 性能: <50ns
672#[inline(always)]
673pub fn get_event_type_fast(log: &str) -> Option<u64> {
674    extract_discriminator_simd(log)
675}
676
677/// 检查是否为特定事件类型 (SIMD 优化)
678#[inline(always)]
679pub fn is_event_type(log: &str, discriminator: u64) -> bool {
680    extract_discriminator_simd(log) == Some(discriminator)
681}
682
683// ============================================================================
684// Public API for optimized parsing from pre-decoded data
685// These functions accept already-decoded data (without discriminator)
686// ============================================================================
687
688/// Parse PumpSwap Buy event from pre-decoded data
689#[inline(always)]
690pub fn parse_buy_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
691    // Updated size check for new fields
692    const MIN_REQUIRED_LEN: usize = 14 * 8 + 7 * 32 + 1 + 5 * 8 + 4;
693    if data.len() < MIN_REQUIRED_LEN {
694        return None;
695    }
696
697    unsafe {
698        let timestamp = read_i64_unchecked(data, 0);
699        let base_amount_out = read_u64_unchecked(data, 8);
700        let max_quote_amount_in = read_u64_unchecked(data, 16);
701        let user_base_token_reserves = read_u64_unchecked(data, 24);
702        let user_quote_token_reserves = read_u64_unchecked(data, 32);
703        let pool_base_token_reserves = read_u64_unchecked(data, 40);
704        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
705        let quote_amount_in = read_u64_unchecked(data, 56);
706        let lp_fee_basis_points = read_u64_unchecked(data, 64);
707        let lp_fee = read_u64_unchecked(data, 72);
708        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
709        let protocol_fee = read_u64_unchecked(data, 88);
710        let quote_amount_in_with_lp_fee = read_u64_unchecked(data, 96);
711        let user_quote_amount_in = read_u64_unchecked(data, 104);
712
713        let pool = read_pubkey_unchecked(data, 112);
714        let user = read_pubkey_unchecked(data, 144);
715        let user_base_token_account = read_pubkey_unchecked(data, 176);
716        let user_quote_token_account = read_pubkey_unchecked(data, 208);
717        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
718        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
719        let coin_creator = read_pubkey_unchecked(data, 304);
720
721        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
722        let coin_creator_fee = read_u64_unchecked(data, 344);
723        let track_volume = read_bool_unchecked(data, 352);
724        let total_unclaimed_tokens = read_u64_unchecked(data, 353);
725        let total_claimed_tokens = read_u64_unchecked(data, 361);
726        let current_sol_volume = read_u64_unchecked(data, 369);
727        let last_update_timestamp = read_i64_unchecked(data, 377);
728
729        // New fields from IDL update
730        let mut offset = 385;
731        let min_base_amount_out = read_u64_unchecked(data, offset);
732        offset += 8;
733
734        // ix_name: String (4-byte length prefix + content)
735        let ix_name = if offset + 4 <= data.len() {
736            let len = read_u32_unchecked(data, offset) as usize;
737            offset += 4;
738            if offset + len <= data.len() {
739                let string_bytes = &data[offset..offset + len];
740                let s = std::str::from_utf8_unchecked(string_bytes);
741                s.to_string()
742            } else {
743                String::new()
744            }
745        } else {
746            String::new()
747        };
748
749        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
750            metadata,
751            timestamp,
752            base_amount_out,
753            max_quote_amount_in,
754            user_base_token_reserves,
755            user_quote_token_reserves,
756            pool_base_token_reserves,
757            pool_quote_token_reserves,
758            quote_amount_in,
759            lp_fee_basis_points,
760            lp_fee,
761            protocol_fee_basis_points,
762            protocol_fee,
763            quote_amount_in_with_lp_fee,
764            user_quote_amount_in,
765            pool,
766            user,
767            user_base_token_account,
768            user_quote_token_account,
769            protocol_fee_recipient,
770            protocol_fee_recipient_token_account,
771            coin_creator,
772            coin_creator_fee_basis_points,
773            coin_creator_fee,
774            track_volume,
775            total_unclaimed_tokens,
776            total_claimed_tokens,
777            current_sol_volume,
778            last_update_timestamp,
779            min_base_amount_out,
780            ix_name,
781            ..Default::default()
782        }))
783    }
784}
785
786/// Parse PumpSwap Sell event from pre-decoded data
787#[inline(always)]
788pub fn parse_sell_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
789    const REQUIRED_LEN: usize = 13 * 8 + 8 + 7 * 32;
790    if data.len() < REQUIRED_LEN {
791        return None;
792    }
793
794    unsafe {
795        let timestamp = read_i64_unchecked(data, 0);
796        let base_amount_in = read_u64_unchecked(data, 8);
797        let min_quote_amount_out = read_u64_unchecked(data, 16);
798        let user_base_token_reserves = read_u64_unchecked(data, 24);
799        let user_quote_token_reserves = read_u64_unchecked(data, 32);
800        let pool_base_token_reserves = read_u64_unchecked(data, 40);
801        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
802        let quote_amount_out = read_u64_unchecked(data, 56);
803        let lp_fee_basis_points = read_u64_unchecked(data, 64);
804        let lp_fee = read_u64_unchecked(data, 72);
805        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
806        let protocol_fee = read_u64_unchecked(data, 88);
807        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
808        let user_quote_amount_out = read_u64_unchecked(data, 104);
809
810        let pool = read_pubkey_unchecked(data, 112);
811        let user = read_pubkey_unchecked(data, 144);
812        let user_base_token_account = read_pubkey_unchecked(data, 176);
813        let user_quote_token_account = read_pubkey_unchecked(data, 208);
814        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
815        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
816        let coin_creator = read_pubkey_unchecked(data, 304);
817
818        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
819        let coin_creator_fee = read_u64_unchecked(data, 344);
820
821        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
822            metadata,
823            timestamp,
824            base_amount_in,
825            min_quote_amount_out,
826            user_base_token_reserves,
827            user_quote_token_reserves,
828            pool_base_token_reserves,
829            pool_quote_token_reserves,
830            quote_amount_out,
831            lp_fee_basis_points,
832            lp_fee,
833            protocol_fee_basis_points,
834            protocol_fee,
835            quote_amount_out_without_lp_fee,
836            user_quote_amount_out,
837            pool,
838            user,
839            user_base_token_account,
840            user_quote_token_account,
841            protocol_fee_recipient,
842            protocol_fee_recipient_token_account,
843            coin_creator,
844            coin_creator_fee_basis_points,
845            coin_creator_fee,
846            ..Default::default()
847        }))
848    }
849}
850
851/// Parse PumpSwap CreatePool event from pre-decoded data
852#[inline(always)]
853pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
854    const REQUIRED_LEN: usize = 8 + 2 + 32*6 + 2 + 8*7 + 1;
855    if data.len() < REQUIRED_LEN {
856        return None;
857    }
858
859    unsafe {
860        let timestamp = read_i64_unchecked(data, 0);
861        let index = read_u16_unchecked(data, 8);
862
863        let creator = read_pubkey_unchecked(data, 10);
864        let base_mint = read_pubkey_unchecked(data, 42);
865        let quote_mint = read_pubkey_unchecked(data, 74);
866
867        let base_mint_decimals = read_u8_unchecked(data, 106);
868        let quote_mint_decimals = read_u8_unchecked(data, 107);
869
870        let base_amount_in = read_u64_unchecked(data, 108);
871        let quote_amount_in = read_u64_unchecked(data, 116);
872        let pool_base_amount = read_u64_unchecked(data, 124);
873        let pool_quote_amount = read_u64_unchecked(data, 132);
874        let minimum_liquidity = read_u64_unchecked(data, 140);
875        let initial_liquidity = read_u64_unchecked(data, 148);
876        let lp_token_amount_out = read_u64_unchecked(data, 156);
877
878        let pool_bump = read_u8_unchecked(data, 164);
879
880        let pool = read_pubkey_unchecked(data, 165);
881        let lp_mint = read_pubkey_unchecked(data, 197);
882        let user_base_token_account = read_pubkey_unchecked(data, 229);
883        let user_quote_token_account = read_pubkey_unchecked(data, 261);
884        let coin_creator = read_pubkey_unchecked(data, 293);
885        let is_mayhem_mode = data.len() > 325 && read_bool_unchecked(data, 325);
886
887        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
888            metadata,
889            timestamp,
890            index,
891            creator,
892            base_mint,
893            quote_mint,
894            base_mint_decimals,
895            quote_mint_decimals,
896            base_amount_in,
897            quote_amount_in,
898            pool_base_amount,
899            pool_quote_amount,
900            minimum_liquidity,
901            initial_liquidity,
902            lp_token_amount_out,
903            pool_bump,
904            pool,
905            lp_mint,
906            user_base_token_account,
907            user_quote_token_account,
908            coin_creator,
909            is_mayhem_mode,
910        }))
911    }
912}
913
914/// Parse PumpSwap AddLiquidity event from pre-decoded data
915#[inline(always)]
916pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
917    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
918    if data.len() < REQUIRED_LEN {
919        return None;
920    }
921
922    unsafe {
923        let timestamp = read_i64_unchecked(data, 0);
924        let lp_token_amount_out = read_u64_unchecked(data, 8);
925        let max_base_amount_in = read_u64_unchecked(data, 16);
926        let max_quote_amount_in = read_u64_unchecked(data, 24);
927        let user_base_token_reserves = read_u64_unchecked(data, 32);
928        let user_quote_token_reserves = read_u64_unchecked(data, 40);
929        let pool_base_token_reserves = read_u64_unchecked(data, 48);
930        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
931        let base_amount_in = read_u64_unchecked(data, 64);
932        let quote_amount_in = read_u64_unchecked(data, 72);
933        let lp_mint_supply = read_u64_unchecked(data, 80);
934
935        let pool = read_pubkey_unchecked(data, 88);
936        let user = read_pubkey_unchecked(data, 120);
937        let user_base_token_account = read_pubkey_unchecked(data, 152);
938        let user_quote_token_account = read_pubkey_unchecked(data, 184);
939        let user_pool_token_account = read_pubkey_unchecked(data, 216);
940
941        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
942            metadata,
943            timestamp,
944            lp_token_amount_out,
945            max_base_amount_in,
946            max_quote_amount_in,
947            user_base_token_reserves,
948            user_quote_token_reserves,
949            pool_base_token_reserves,
950            pool_quote_token_reserves,
951            base_amount_in,
952            quote_amount_in,
953            lp_mint_supply,
954            pool,
955            user,
956            user_base_token_account,
957            user_quote_token_account,
958            user_pool_token_account,
959        }))
960    }
961}
962
963/// Parse PumpSwap RemoveLiquidity event from pre-decoded data
964#[inline(always)]
965pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
966    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
967    if data.len() < REQUIRED_LEN {
968        return None;
969    }
970
971    unsafe {
972        let timestamp = read_i64_unchecked(data, 0);
973        let lp_token_amount_in = read_u64_unchecked(data, 8);
974        let min_base_amount_out = read_u64_unchecked(data, 16);
975        let min_quote_amount_out = read_u64_unchecked(data, 24);
976        let user_base_token_reserves = read_u64_unchecked(data, 32);
977        let user_quote_token_reserves = read_u64_unchecked(data, 40);
978        let pool_base_token_reserves = read_u64_unchecked(data, 48);
979        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
980        let base_amount_out = read_u64_unchecked(data, 64);
981        let quote_amount_out = read_u64_unchecked(data, 72);
982        let lp_mint_supply = read_u64_unchecked(data, 80);
983
984        let pool = read_pubkey_unchecked(data, 88);
985        let user = read_pubkey_unchecked(data, 120);
986        let user_base_token_account = read_pubkey_unchecked(data, 152);
987        let user_quote_token_account = read_pubkey_unchecked(data, 184);
988        let user_pool_token_account = read_pubkey_unchecked(data, 216);
989
990        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
991            metadata,
992            timestamp,
993            lp_token_amount_in,
994            min_base_amount_out,
995            min_quote_amount_out,
996            user_base_token_reserves,
997            user_quote_token_reserves,
998            pool_base_token_reserves,
999            pool_quote_token_reserves,
1000            base_amount_out,
1001            quote_amount_out,
1002            lp_mint_supply,
1003            pool,
1004            user,
1005            user_base_token_account,
1006            user_quote_token_account,
1007            user_pool_token_account,
1008        }))
1009    }
1010}
1011
1012// ============================================================================
1013// 性能统计 API (可选)
1014// ============================================================================
1015
1016#[cfg(feature = "perf-stats")]
1017pub fn get_perf_stats() -> (usize, usize) {
1018    let count = PARSE_COUNT.load(Ordering::Relaxed);
1019    let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
1020    (count, total_ns)
1021}
1022
1023#[cfg(feature = "perf-stats")]
1024pub fn reset_perf_stats() {
1025    PARSE_COUNT.store(0, Ordering::Relaxed);
1026    PARSE_TIME_NS.store(0, Ordering::Relaxed);
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031    use super::*;
1032
1033    #[test]
1034    fn test_discriminator_simd() {
1035        // 测试 SIMD discriminator 提取
1036        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1037        let disc = extract_discriminator_simd(log);
1038        assert!(disc.is_some());
1039    }
1040
1041    #[test]
1042    fn test_parse_performance() {
1043        // 性能测试
1044        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1045        let sig = Signature::default();
1046
1047        let start = std::time::Instant::now();
1048        for _ in 0..1000 {
1049            let _ = parse_log(log, sig, 0, 0, Some(0), 0);
1050        }
1051        let elapsed = start.elapsed();
1052
1053        println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
1054    }
1055}