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        };
327
328        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
329            metadata,
330            timestamp,
331            base_amount_out,
332            max_quote_amount_in,
333            user_base_token_reserves,
334            user_quote_token_reserves,
335            pool_base_token_reserves,
336            pool_quote_token_reserves,
337            quote_amount_in,
338            lp_fee_basis_points,
339            lp_fee,
340            protocol_fee_basis_points,
341            protocol_fee,
342            quote_amount_in_with_lp_fee,
343            user_quote_amount_in,
344            pool,
345            user,
346            user_base_token_account,
347            user_quote_token_account,
348            protocol_fee_recipient,
349            protocol_fee_recipient_token_account,
350            coin_creator,
351            coin_creator_fee_basis_points,
352            coin_creator_fee,
353            track_volume,
354            total_unclaimed_tokens,
355            total_claimed_tokens,
356            current_sol_volume,
357            last_update_timestamp,
358            min_base_amount_out,
359            ix_name,
360            cashback_fee_basis_points,
361            cashback,
362            ..Default::default()
363        }))
364    }
365}
366
367/// 解析卖出事件 (极限优化)
368#[inline(always)]
369fn parse_sell_event_optimized(
370    data: &[u8],
371    signature: Signature,
372    slot: u64,
373    tx_index: u64,
374    block_time_us: Option<i64>,
375    grpc_recv_us: i64,
376) -> Option<DexEvent> {
377    // 一次性边界检查 (13个u64 + 1个i64 + 7个Pubkey + 2个u64 cashback 字段)
378    const REQUIRED_LEN: usize = 13 * 8 + 8 + 7 * 32 + 8 + 8;
379    if data.len() < REQUIRED_LEN {
380        return None;
381    }
382
383    unsafe {
384        let timestamp = read_i64_unchecked(data, 0);
385        let base_amount_in = read_u64_unchecked(data, 8);
386        let min_quote_amount_out = read_u64_unchecked(data, 16);
387        let user_base_token_reserves = read_u64_unchecked(data, 24);
388        let user_quote_token_reserves = read_u64_unchecked(data, 32);
389        let pool_base_token_reserves = read_u64_unchecked(data, 40);
390        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
391        let quote_amount_out = read_u64_unchecked(data, 56);
392        let lp_fee_basis_points = read_u64_unchecked(data, 64);
393        let lp_fee = read_u64_unchecked(data, 72);
394        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
395        let protocol_fee = read_u64_unchecked(data, 88);
396        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
397        let user_quote_amount_out = read_u64_unchecked(data, 104);
398
399        let pool = read_pubkey_unchecked(data, 112);
400        let user = read_pubkey_unchecked(data, 144);
401        let user_base_token_account = read_pubkey_unchecked(data, 176);
402        let user_quote_token_account = read_pubkey_unchecked(data, 208);
403        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
404        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
405        let coin_creator = read_pubkey_unchecked(data, 304);
406
407        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
408        let coin_creator_fee = read_u64_unchecked(data, 344);
409        // SellEvent 新增字段 (PUMP_CASHBACK_README): cashback_fee_basis_points, cashback
410        let cashback_fee_basis_points = read_u64_unchecked(data, 352);
411        let cashback = read_u64_unchecked(data, 360);
412
413        let metadata = EventMetadata {
414            signature,
415            slot,
416            tx_index,
417            block_time_us: block_time_us.unwrap_or(0),
418            grpc_recv_us,
419        };
420
421        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
422            metadata,
423            timestamp,
424            base_amount_in,
425            min_quote_amount_out,
426            user_base_token_reserves,
427            user_quote_token_reserves,
428            pool_base_token_reserves,
429            pool_quote_token_reserves,
430            quote_amount_out,
431            lp_fee_basis_points,
432            lp_fee,
433            protocol_fee_basis_points,
434            protocol_fee,
435            quote_amount_out_without_lp_fee,
436            user_quote_amount_out,
437            pool,
438            user,
439            user_base_token_account,
440            user_quote_token_account,
441            protocol_fee_recipient,
442            protocol_fee_recipient_token_account,
443            coin_creator,
444            coin_creator_fee_basis_points,
445            coin_creator_fee,
446            cashback_fee_basis_points,
447            cashback,
448            ..Default::default()
449        }))
450    }
451}
452
453/// 解析池创建事件 (极限优化)
454#[inline(always)]
455fn parse_create_pool_event_optimized(
456    data: &[u8],
457    signature: Signature,
458    slot: u64,
459    tx_index: u64,
460    block_time_us: Option<i64>,
461    grpc_recv_us: i64,
462) -> Option<DexEvent> {
463    // 一次性边界检查 (含 IDL 最后一列 is_mayhem_mode: bool)
464    const REQUIRED_LEN: usize = 8 + 2 + 32 * 6 + 2 + 8 * 7 + 1 + 1;
465    if data.len() < REQUIRED_LEN {
466        return None;
467    }
468
469    unsafe {
470        let timestamp = read_i64_unchecked(data, 0);
471        let index = read_u16_unchecked(data, 8);
472
473        let creator = read_pubkey_unchecked(data, 10);
474        let base_mint = read_pubkey_unchecked(data, 42);
475        let quote_mint = read_pubkey_unchecked(data, 74);
476
477        let base_mint_decimals = read_u8_unchecked(data, 106);
478        let quote_mint_decimals = read_u8_unchecked(data, 107);
479
480        let base_amount_in = read_u64_unchecked(data, 108);
481        let quote_amount_in = read_u64_unchecked(data, 116);
482        let pool_base_amount = read_u64_unchecked(data, 124);
483        let pool_quote_amount = read_u64_unchecked(data, 132);
484        let minimum_liquidity = read_u64_unchecked(data, 140);
485        let initial_liquidity = read_u64_unchecked(data, 148);
486        let lp_token_amount_out = read_u64_unchecked(data, 156);
487
488        let pool_bump = read_u8_unchecked(data, 164);
489
490        let pool = read_pubkey_unchecked(data, 165);
491        let lp_mint = read_pubkey_unchecked(data, 197);
492        let user_base_token_account = read_pubkey_unchecked(data, 229);
493        let user_quote_token_account = read_pubkey_unchecked(data, 261);
494        let coin_creator = read_pubkey_unchecked(data, 293);
495        let is_mayhem_mode = read_bool_unchecked(data, 325);
496
497        let metadata = EventMetadata {
498            signature,
499            slot,
500            tx_index,
501            block_time_us: block_time_us.unwrap_or(0),
502            grpc_recv_us,
503        };
504
505        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
506            metadata,
507            timestamp,
508            index,
509            creator,
510            base_mint,
511            quote_mint,
512            base_mint_decimals,
513            quote_mint_decimals,
514            base_amount_in,
515            quote_amount_in,
516            pool_base_amount,
517            pool_quote_amount,
518            minimum_liquidity,
519            initial_liquidity,
520            lp_token_amount_out,
521            pool_bump,
522            pool,
523            lp_mint,
524            user_base_token_account,
525            user_quote_token_account,
526            coin_creator,
527            is_mayhem_mode,
528        }))
529    }
530}
531
532/// 解析添加流动性事件 (极限优化)
533#[inline(always)]
534fn parse_add_liquidity_event_optimized(
535    data: &[u8],
536    signature: Signature,
537    slot: u64,
538    tx_index: u64,
539    block_time_us: Option<i64>,
540    grpc_recv_us: i64,
541) -> Option<DexEvent> {
542    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
543    if data.len() < REQUIRED_LEN {
544        return None;
545    }
546
547    unsafe {
548        let timestamp = read_i64_unchecked(data, 0);
549        let lp_token_amount_out = read_u64_unchecked(data, 8);
550        let max_base_amount_in = read_u64_unchecked(data, 16);
551        let max_quote_amount_in = read_u64_unchecked(data, 24);
552        let user_base_token_reserves = read_u64_unchecked(data, 32);
553        let user_quote_token_reserves = read_u64_unchecked(data, 40);
554        let pool_base_token_reserves = read_u64_unchecked(data, 48);
555        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
556        let base_amount_in = read_u64_unchecked(data, 64);
557        let quote_amount_in = read_u64_unchecked(data, 72);
558        let lp_mint_supply = read_u64_unchecked(data, 80);
559
560        let pool = read_pubkey_unchecked(data, 88);
561        let user = read_pubkey_unchecked(data, 120);
562        let user_base_token_account = read_pubkey_unchecked(data, 152);
563        let user_quote_token_account = read_pubkey_unchecked(data, 184);
564        let user_pool_token_account = read_pubkey_unchecked(data, 216);
565
566        let metadata = EventMetadata {
567            signature,
568            slot,
569            tx_index,
570            block_time_us: block_time_us.unwrap_or(0),
571            grpc_recv_us,
572        };
573
574        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
575            metadata,
576            timestamp,
577            lp_token_amount_out,
578            max_base_amount_in,
579            max_quote_amount_in,
580            user_base_token_reserves,
581            user_quote_token_reserves,
582            pool_base_token_reserves,
583            pool_quote_token_reserves,
584            base_amount_in,
585            quote_amount_in,
586            lp_mint_supply,
587            pool,
588            user,
589            user_base_token_account,
590            user_quote_token_account,
591            user_pool_token_account,
592        }))
593    }
594}
595
596/// 解析移除流动性事件 (极限优化)
597#[inline(always)]
598fn parse_remove_liquidity_event_optimized(
599    data: &[u8],
600    signature: Signature,
601    slot: u64,
602    tx_index: u64,
603    block_time_us: Option<i64>,
604    grpc_recv_us: i64,
605) -> Option<DexEvent> {
606    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
607    if data.len() < REQUIRED_LEN {
608        return None;
609    }
610
611    unsafe {
612        let timestamp = read_i64_unchecked(data, 0);
613        let lp_token_amount_in = read_u64_unchecked(data, 8);
614        let min_base_amount_out = read_u64_unchecked(data, 16);
615        let min_quote_amount_out = read_u64_unchecked(data, 24);
616        let user_base_token_reserves = read_u64_unchecked(data, 32);
617        let user_quote_token_reserves = read_u64_unchecked(data, 40);
618        let pool_base_token_reserves = read_u64_unchecked(data, 48);
619        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
620        let base_amount_out = read_u64_unchecked(data, 64);
621        let quote_amount_out = read_u64_unchecked(data, 72);
622        let lp_mint_supply = read_u64_unchecked(data, 80);
623
624        let pool = read_pubkey_unchecked(data, 88);
625        let user = read_pubkey_unchecked(data, 120);
626        let user_base_token_account = read_pubkey_unchecked(data, 152);
627        let user_quote_token_account = read_pubkey_unchecked(data, 184);
628        let user_pool_token_account = read_pubkey_unchecked(data, 216);
629
630        let metadata = EventMetadata {
631            signature,
632            slot,
633            tx_index,
634            block_time_us: block_time_us.unwrap_or(0),
635            grpc_recv_us,
636        };
637
638        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
639            metadata,
640            timestamp,
641            lp_token_amount_in,
642            min_base_amount_out,
643            min_quote_amount_out,
644            user_base_token_reserves,
645            user_quote_token_reserves,
646            pool_base_token_reserves,
647            pool_quote_token_reserves,
648            base_amount_out,
649            quote_amount_out,
650            lp_mint_supply,
651            pool,
652            user,
653            user_base_token_account,
654            user_quote_token_account,
655            user_pool_token_account,
656        }))
657    }
658}
659
660// ============================================================================
661// 快速过滤 API (用于事件过滤场景)
662// ============================================================================
663
664/// 快速判断事件类型 (只解析 discriminator)
665///
666/// 性能: <50ns
667#[inline(always)]
668pub fn get_event_type_fast(log: &str) -> Option<u64> {
669    extract_discriminator_simd(log)
670}
671
672/// 检查是否为特定事件类型 (SIMD 优化)
673#[inline(always)]
674pub fn is_event_type(log: &str, discriminator: u64) -> bool {
675    extract_discriminator_simd(log) == Some(discriminator)
676}
677
678// ============================================================================
679// Public API for optimized parsing from pre-decoded data
680// These functions accept already-decoded data (without discriminator)
681// ============================================================================
682
683/// Parse PumpSwap Buy event from pre-decoded data
684#[inline(always)]
685pub fn parse_buy_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
686    // Updated size check for new fields
687    const MIN_REQUIRED_LEN: usize = 14 * 8 + 7 * 32 + 1 + 5 * 8 + 4;
688    if data.len() < MIN_REQUIRED_LEN {
689        return None;
690    }
691
692    unsafe {
693        let timestamp = read_i64_unchecked(data, 0);
694        let base_amount_out = read_u64_unchecked(data, 8);
695        let max_quote_amount_in = read_u64_unchecked(data, 16);
696        let user_base_token_reserves = read_u64_unchecked(data, 24);
697        let user_quote_token_reserves = read_u64_unchecked(data, 32);
698        let pool_base_token_reserves = read_u64_unchecked(data, 40);
699        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
700        let quote_amount_in = read_u64_unchecked(data, 56);
701        let lp_fee_basis_points = read_u64_unchecked(data, 64);
702        let lp_fee = read_u64_unchecked(data, 72);
703        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
704        let protocol_fee = read_u64_unchecked(data, 88);
705        let quote_amount_in_with_lp_fee = read_u64_unchecked(data, 96);
706        let user_quote_amount_in = read_u64_unchecked(data, 104);
707
708        let pool = read_pubkey_unchecked(data, 112);
709        let user = read_pubkey_unchecked(data, 144);
710        let user_base_token_account = read_pubkey_unchecked(data, 176);
711        let user_quote_token_account = read_pubkey_unchecked(data, 208);
712        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
713        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
714        let coin_creator = read_pubkey_unchecked(data, 304);
715
716        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
717        let coin_creator_fee = read_u64_unchecked(data, 344);
718        let track_volume = read_bool_unchecked(data, 352);
719        let total_unclaimed_tokens = read_u64_unchecked(data, 353);
720        let total_claimed_tokens = read_u64_unchecked(data, 361);
721        let current_sol_volume = read_u64_unchecked(data, 369);
722        let last_update_timestamp = read_i64_unchecked(data, 377);
723
724        // New fields from IDL update
725        let mut offset = 385;
726        let min_base_amount_out = read_u64_unchecked(data, offset);
727        offset += 8;
728
729        // ix_name: String (4-byte length prefix + content)
730        let ix_name = if offset + 4 <= data.len() {
731            let len = read_u32_unchecked(data, offset) as usize;
732            offset += 4;
733            if offset + len <= data.len() {
734                let string_bytes = &data[offset..offset + len];
735                let s = std::str::from_utf8_unchecked(string_bytes);
736                s.to_string()
737            } else {
738                String::new()
739            }
740        } else {
741            String::new()
742        };
743
744        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
745            metadata,
746            timestamp,
747            base_amount_out,
748            max_quote_amount_in,
749            user_base_token_reserves,
750            user_quote_token_reserves,
751            pool_base_token_reserves,
752            pool_quote_token_reserves,
753            quote_amount_in,
754            lp_fee_basis_points,
755            lp_fee,
756            protocol_fee_basis_points,
757            protocol_fee,
758            quote_amount_in_with_lp_fee,
759            user_quote_amount_in,
760            pool,
761            user,
762            user_base_token_account,
763            user_quote_token_account,
764            protocol_fee_recipient,
765            protocol_fee_recipient_token_account,
766            coin_creator,
767            coin_creator_fee_basis_points,
768            coin_creator_fee,
769            track_volume,
770            total_unclaimed_tokens,
771            total_claimed_tokens,
772            current_sol_volume,
773            last_update_timestamp,
774            min_base_amount_out,
775            ix_name,
776            ..Default::default()
777        }))
778    }
779}
780
781/// Parse PumpSwap Sell event from pre-decoded data
782#[inline(always)]
783pub fn parse_sell_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
784    const REQUIRED_LEN: usize = 13 * 8 + 8 + 7 * 32;
785    if data.len() < REQUIRED_LEN {
786        return None;
787    }
788
789    unsafe {
790        let timestamp = read_i64_unchecked(data, 0);
791        let base_amount_in = read_u64_unchecked(data, 8);
792        let min_quote_amount_out = read_u64_unchecked(data, 16);
793        let user_base_token_reserves = read_u64_unchecked(data, 24);
794        let user_quote_token_reserves = read_u64_unchecked(data, 32);
795        let pool_base_token_reserves = read_u64_unchecked(data, 40);
796        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
797        let quote_amount_out = read_u64_unchecked(data, 56);
798        let lp_fee_basis_points = read_u64_unchecked(data, 64);
799        let lp_fee = read_u64_unchecked(data, 72);
800        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
801        let protocol_fee = read_u64_unchecked(data, 88);
802        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
803        let user_quote_amount_out = read_u64_unchecked(data, 104);
804
805        let pool = read_pubkey_unchecked(data, 112);
806        let user = read_pubkey_unchecked(data, 144);
807        let user_base_token_account = read_pubkey_unchecked(data, 176);
808        let user_quote_token_account = read_pubkey_unchecked(data, 208);
809        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
810        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
811        let coin_creator = read_pubkey_unchecked(data, 304);
812
813        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
814        let coin_creator_fee = read_u64_unchecked(data, 344);
815
816        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
817            metadata,
818            timestamp,
819            base_amount_in,
820            min_quote_amount_out,
821            user_base_token_reserves,
822            user_quote_token_reserves,
823            pool_base_token_reserves,
824            pool_quote_token_reserves,
825            quote_amount_out,
826            lp_fee_basis_points,
827            lp_fee,
828            protocol_fee_basis_points,
829            protocol_fee,
830            quote_amount_out_without_lp_fee,
831            user_quote_amount_out,
832            pool,
833            user,
834            user_base_token_account,
835            user_quote_token_account,
836            protocol_fee_recipient,
837            protocol_fee_recipient_token_account,
838            coin_creator,
839            coin_creator_fee_basis_points,
840            coin_creator_fee,
841            ..Default::default()
842        }))
843    }
844}
845
846/// Parse PumpSwap CreatePool event from pre-decoded data
847#[inline(always)]
848pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
849    const REQUIRED_LEN: usize = 8 + 2 + 32*6 + 2 + 8*7 + 1;
850    if data.len() < REQUIRED_LEN {
851        return None;
852    }
853
854    unsafe {
855        let timestamp = read_i64_unchecked(data, 0);
856        let index = read_u16_unchecked(data, 8);
857
858        let creator = read_pubkey_unchecked(data, 10);
859        let base_mint = read_pubkey_unchecked(data, 42);
860        let quote_mint = read_pubkey_unchecked(data, 74);
861
862        let base_mint_decimals = read_u8_unchecked(data, 106);
863        let quote_mint_decimals = read_u8_unchecked(data, 107);
864
865        let base_amount_in = read_u64_unchecked(data, 108);
866        let quote_amount_in = read_u64_unchecked(data, 116);
867        let pool_base_amount = read_u64_unchecked(data, 124);
868        let pool_quote_amount = read_u64_unchecked(data, 132);
869        let minimum_liquidity = read_u64_unchecked(data, 140);
870        let initial_liquidity = read_u64_unchecked(data, 148);
871        let lp_token_amount_out = read_u64_unchecked(data, 156);
872
873        let pool_bump = read_u8_unchecked(data, 164);
874
875        let pool = read_pubkey_unchecked(data, 165);
876        let lp_mint = read_pubkey_unchecked(data, 197);
877        let user_base_token_account = read_pubkey_unchecked(data, 229);
878        let user_quote_token_account = read_pubkey_unchecked(data, 261);
879        let coin_creator = read_pubkey_unchecked(data, 293);
880        let is_mayhem_mode = data.len() > 325 && read_bool_unchecked(data, 325);
881
882        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
883            metadata,
884            timestamp,
885            index,
886            creator,
887            base_mint,
888            quote_mint,
889            base_mint_decimals,
890            quote_mint_decimals,
891            base_amount_in,
892            quote_amount_in,
893            pool_base_amount,
894            pool_quote_amount,
895            minimum_liquidity,
896            initial_liquidity,
897            lp_token_amount_out,
898            pool_bump,
899            pool,
900            lp_mint,
901            user_base_token_account,
902            user_quote_token_account,
903            coin_creator,
904            is_mayhem_mode,
905        }))
906    }
907}
908
909/// Parse PumpSwap AddLiquidity event from pre-decoded data
910#[inline(always)]
911pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
912    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
913    if data.len() < REQUIRED_LEN {
914        return None;
915    }
916
917    unsafe {
918        let timestamp = read_i64_unchecked(data, 0);
919        let lp_token_amount_out = read_u64_unchecked(data, 8);
920        let max_base_amount_in = read_u64_unchecked(data, 16);
921        let max_quote_amount_in = read_u64_unchecked(data, 24);
922        let user_base_token_reserves = read_u64_unchecked(data, 32);
923        let user_quote_token_reserves = read_u64_unchecked(data, 40);
924        let pool_base_token_reserves = read_u64_unchecked(data, 48);
925        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
926        let base_amount_in = read_u64_unchecked(data, 64);
927        let quote_amount_in = read_u64_unchecked(data, 72);
928        let lp_mint_supply = read_u64_unchecked(data, 80);
929
930        let pool = read_pubkey_unchecked(data, 88);
931        let user = read_pubkey_unchecked(data, 120);
932        let user_base_token_account = read_pubkey_unchecked(data, 152);
933        let user_quote_token_account = read_pubkey_unchecked(data, 184);
934        let user_pool_token_account = read_pubkey_unchecked(data, 216);
935
936        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
937            metadata,
938            timestamp,
939            lp_token_amount_out,
940            max_base_amount_in,
941            max_quote_amount_in,
942            user_base_token_reserves,
943            user_quote_token_reserves,
944            pool_base_token_reserves,
945            pool_quote_token_reserves,
946            base_amount_in,
947            quote_amount_in,
948            lp_mint_supply,
949            pool,
950            user,
951            user_base_token_account,
952            user_quote_token_account,
953            user_pool_token_account,
954        }))
955    }
956}
957
958/// Parse PumpSwap RemoveLiquidity event from pre-decoded data
959#[inline(always)]
960pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
961    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
962    if data.len() < REQUIRED_LEN {
963        return None;
964    }
965
966    unsafe {
967        let timestamp = read_i64_unchecked(data, 0);
968        let lp_token_amount_in = read_u64_unchecked(data, 8);
969        let min_base_amount_out = read_u64_unchecked(data, 16);
970        let min_quote_amount_out = read_u64_unchecked(data, 24);
971        let user_base_token_reserves = read_u64_unchecked(data, 32);
972        let user_quote_token_reserves = read_u64_unchecked(data, 40);
973        let pool_base_token_reserves = read_u64_unchecked(data, 48);
974        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
975        let base_amount_out = read_u64_unchecked(data, 64);
976        let quote_amount_out = read_u64_unchecked(data, 72);
977        let lp_mint_supply = read_u64_unchecked(data, 80);
978
979        let pool = read_pubkey_unchecked(data, 88);
980        let user = read_pubkey_unchecked(data, 120);
981        let user_base_token_account = read_pubkey_unchecked(data, 152);
982        let user_quote_token_account = read_pubkey_unchecked(data, 184);
983        let user_pool_token_account = read_pubkey_unchecked(data, 216);
984
985        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
986            metadata,
987            timestamp,
988            lp_token_amount_in,
989            min_base_amount_out,
990            min_quote_amount_out,
991            user_base_token_reserves,
992            user_quote_token_reserves,
993            pool_base_token_reserves,
994            pool_quote_token_reserves,
995            base_amount_out,
996            quote_amount_out,
997            lp_mint_supply,
998            pool,
999            user,
1000            user_base_token_account,
1001            user_quote_token_account,
1002            user_pool_token_account,
1003        }))
1004    }
1005}
1006
1007// ============================================================================
1008// 性能统计 API (可选)
1009// ============================================================================
1010
1011#[cfg(feature = "perf-stats")]
1012pub fn get_perf_stats() -> (usize, usize) {
1013    let count = PARSE_COUNT.load(Ordering::Relaxed);
1014    let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
1015    (count, total_ns)
1016}
1017
1018#[cfg(feature = "perf-stats")]
1019pub fn reset_perf_stats() {
1020    PARSE_COUNT.store(0, Ordering::Relaxed);
1021    PARSE_TIME_NS.store(0, Ordering::Relaxed);
1022}
1023
1024#[cfg(test)]
1025mod tests {
1026    use super::*;
1027
1028    #[test]
1029    fn test_discriminator_simd() {
1030        // 测试 SIMD discriminator 提取
1031        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1032        let disc = extract_discriminator_simd(log);
1033        assert!(disc.is_some());
1034    }
1035
1036    #[test]
1037    fn test_parse_performance() {
1038        // 性能测试
1039        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1040        let sig = Signature::default();
1041
1042        let start = std::time::Instant::now();
1043        for _ in 0..1000 {
1044            let _ = parse_log(log, sig, 0, 0, Some(0), 0);
1045        }
1046        let elapsed = start.elapsed();
1047
1048        println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
1049    }
1050}