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