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