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/// 跳过 ASCII 空白后拷贝 base64 前缀(Explorer / 部分日志会在 base64 中插空格)
50#[inline(always)]
51fn copy_b64_skip_ws_prefix(src: &[u8], out: &mut [u8], max_copy: usize) -> Option<usize> {
52    let cap = max_copy.min(out.len());
53    let mut j = 0usize;
54    for &b in src {
55        if b.is_ascii_whitespace() {
56            continue;
57        }
58        if j >= cap {
59            break;
60        }
61        out[j] = b;
62        j += 1;
63    }
64    if j < 12 {
65        return None;
66    }
67    Some(j)
68}
69
70// ============================================================================
71// 零拷贝解析核心 - 使用栈分配
72// ============================================================================
73
74/// 零拷贝提取 program data (栈分配,无堆分配)
75///
76/// 优化: 使用固定大小栈缓冲区,避免 Vec 分配
77/// 缓冲区大小增加到 2KB 以防止 base64-simd 缓冲区溢出panic
78#[inline(always)]
79fn extract_program_data_zero_copy<'a>(log: &'a str, buf: &'a mut [u8; 2048]) -> Option<&'a [u8]> {
80    let log_bytes = log.as_bytes();
81    let pos = BASE64_FINDER.find(log_bytes)?;
82
83    let data_part = &log[pos + 14..];
84    let trimmed = data_part.trim();
85    let body = trimmed.as_bytes();
86
87    if body.len() > 2700 {
88        return None;
89    }
90
91    use base64_simd::AsOut;
92    const COMPACT_CAP: usize = 2730;
93    let decoded_slice = if body.iter().any(|&b| b.is_ascii_whitespace()) {
94        let mut compact = [0u8; COMPACT_CAP];
95        let n = copy_b64_skip_ws_prefix(body, &mut compact, COMPACT_CAP)?;
96        base64_simd::STANDARD.decode(&compact[..n], buf.as_mut().as_out()).ok()?
97    } else {
98        base64_simd::STANDARD.decode(body, buf.as_mut().as_out()).ok()?
99    };
100
101    Some(decoded_slice)
102}
103
104/// 快速 discriminator 提取 (SIMD 优化)
105#[inline(always)]
106fn extract_discriminator_simd(log: &str) -> Option<u64> {
107    let log_bytes = log.as_bytes();
108    let pos = BASE64_FINDER.find(log_bytes)?;
109
110    let data_part = &log[pos + 14..];
111    let body = data_part.trim().as_bytes();
112
113    use base64_simd::AsOut;
114    let mut compact = [0u8; 24];
115    let n = copy_b64_skip_ws_prefix(body, &mut compact, 16)?;
116    let mut dec = [0u8; 12];
117    base64_simd::STANDARD.decode(&compact[..n], dec.as_mut().as_out()).ok()?;
118
119    unsafe {
120        let ptr = dec.as_ptr() as *const u64;
121        Some(ptr.read_unaligned())
122    }
123}
124
125// ============================================================================
126// Unsafe 读取函数 - 消除边界检查
127// ============================================================================
128
129/// 读取 u64 (unsafe, 无边界检查)
130#[inline(always)]
131unsafe fn read_u64_unchecked(data: &[u8], offset: usize) -> u64 {
132    let ptr = data.as_ptr().add(offset) as *const u64;
133    u64::from_le(ptr.read_unaligned())
134}
135
136#[inline(always)]
137unsafe fn read_i128_unchecked(data: &[u8], offset: usize) -> i128 {
138    let ptr = data.as_ptr().add(offset) as *const i128;
139    i128::from_le(ptr.read_unaligned())
140}
141
142/// 读取 i64 (unsafe, 无边界检查)
143#[inline(always)]
144unsafe fn read_i64_unchecked(data: &[u8], offset: usize) -> i64 {
145    let ptr = data.as_ptr().add(offset) as *const i64;
146    i64::from_le(ptr.read_unaligned())
147}
148
149/// Read u16 (unsafe, no bounds check)
150#[inline(always)]
151unsafe fn read_u16_unchecked(data: &[u8], offset: usize) -> u16 {
152    let ptr = data.as_ptr().add(offset) as *const u16;
153    u16::from_le(ptr.read_unaligned())
154}
155
156/// Read u32 (unsafe, no bounds check)
157#[allow(dead_code)]
158#[inline(always)]
159unsafe fn read_u32_unchecked(data: &[u8], offset: usize) -> u32 {
160    let ptr = data.as_ptr().add(offset) as *const u32;
161    u32::from_le(ptr.read_unaligned())
162}
163
164/// Read u8 (unsafe, no bounds check)
165#[inline(always)]
166unsafe fn read_u8_unchecked(data: &[u8], offset: usize) -> u8 {
167    *data.get_unchecked(offset)
168}
169
170/// 读取 bool (unsafe, 无边界检查)
171#[inline(always)]
172unsafe fn read_bool_unchecked(data: &[u8], offset: usize) -> bool {
173    *data.get_unchecked(offset) == 1
174}
175
176/// 读取 Pubkey (unsafe, 无边界检查)
177///
178/// 优化: 添加内存预取,假设连续读取多个 Pubkey
179#[inline(always)]
180unsafe fn read_pubkey_unchecked(data: &[u8], offset: usize) -> Pubkey {
181    // 预取下一个可能的 Pubkey 位置 (假设连续读取)
182    // 使用 T0 提示 (最高优先级) 将数据预取到 L1 cache
183    #[cfg(target_arch = "x86_64")]
184    {
185        use std::arch::x86_64::_mm_prefetch;
186        use std::arch::x86_64::_MM_HINT_T0;
187        if offset + 64 < data.len() {
188            _mm_prefetch((data.as_ptr().add(offset + 32)) as *const i8, _MM_HINT_T0);
189        }
190    }
191
192    let ptr = data.as_ptr().add(offset);
193    let mut bytes = [0u8; 32];
194    std::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), 32);
195    Pubkey::new_from_array(bytes)
196}
197
198const BUY_LEGACY_LEN: usize = 385;
199const BUY_MIN_AND_STRING_PREFIX_LEN: usize = 397;
200const BUY_CASHBACK_TAIL_LEN: usize = 16;
201const BUY_CURRENT_TAIL_LEN: usize = 57;
202const SELL_LEGACY_LEN: usize = 352;
203const SELL_CASHBACK_LEN: usize = 368;
204const SELL_CURRENT_LEN: usize = 409;
205
206#[inline]
207pub(crate) fn validate_buy_payload_layout(data: &[u8]) -> Option<()> {
208    if data.len() == BUY_LEGACY_LEN {
209        return Some(());
210    }
211    if data.len() < BUY_MIN_AND_STRING_PREFIX_LEN {
212        return None;
213    }
214
215    let name_len = u32::from_le_bytes(data.get(393..397)?.try_into().ok()?) as usize;
216    let name_end = BUY_MIN_AND_STRING_PREFIX_LEN.checked_add(name_len)?;
217    let name = data.get(BUY_MIN_AND_STRING_PREFIX_LEN..name_end)?;
218    std::str::from_utf8(name).ok()?;
219
220    match data.len() - name_end {
221        0 | BUY_CASHBACK_TAIL_LEN => Some(()),
222        BUY_CURRENT_TAIL_LEN.. => Some(()),
223        _ => None,
224    }
225}
226
227#[inline]
228pub(crate) fn validate_sell_payload_layout(data: &[u8]) -> Option<()> {
229    match data.len() {
230        SELL_LEGACY_LEN | SELL_CASHBACK_LEN => Some(()),
231        SELL_CURRENT_LEN.. => Some(()),
232        _ => None,
233    }
234}
235
236// ============================================================================
237// Optimized event parsing functions
238// ============================================================================
239
240/// Main parse function (optimized)
241///
242/// Performance target: <100ns
243#[inline(always)]
244pub fn parse_log(
245    log: &str,
246    signature: Signature,
247    slot: u64,
248    tx_index: u64,
249    block_time_us: Option<i64>,
250    grpc_recv_us: i64,
251) -> Option<DexEvent> {
252    #[cfg(feature = "perf-stats")]
253    let start = std::time::Instant::now();
254
255    // Stack-allocated buffer (增加到 2KB 以防止 base64-simd 缓冲区溢出)
256    let mut buf = [0u8; 2048];
257    let program_data = extract_program_data_zero_copy(log, &mut buf)?;
258
259    if program_data.len() < 8 {
260        return None;
261    }
262
263    // Read discriminator using unsafe (SIMD optimized)
264    let discriminator = unsafe { read_u64_unchecked(program_data, 0) };
265    let data = &program_data[8..];
266
267    let result = match discriminator {
268        discriminators::BUY => {
269            parse_buy_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
270        }
271        discriminators::SELL => {
272            parse_sell_event_optimized(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
273        }
274        discriminators::CREATE_POOL => parse_create_pool_event_optimized(
275            data,
276            signature,
277            slot,
278            tx_index,
279            block_time_us,
280            grpc_recv_us,
281        ),
282        discriminators::ADD_LIQUIDITY => parse_add_liquidity_event_optimized(
283            data,
284            signature,
285            slot,
286            tx_index,
287            block_time_us,
288            grpc_recv_us,
289        ),
290        discriminators::REMOVE_LIQUIDITY => parse_remove_liquidity_event_optimized(
291            data,
292            signature,
293            slot,
294            tx_index,
295            block_time_us,
296            grpc_recv_us,
297        ),
298        _ => None,
299    };
300
301    #[cfg(feature = "perf-stats")]
302    {
303        PARSE_COUNT.fetch_add(1, Ordering::Relaxed);
304        PARSE_TIME_NS.fetch_add(start.elapsed().as_nanos() as usize, Ordering::Relaxed);
305    }
306
307    result
308}
309
310/// Parse buy event (optimized) - BuyEvent from pump_amm.json
311///
312/// Optimizations:
313/// - Use unsafe to eliminate all bounds checks
314/// - Batch bounds check instead of per-field check
315/// - Inline all calls
316#[inline(always)]
317fn parse_buy_event_optimized(
318    data: &[u8],
319    signature: Signature,
320    slot: u64,
321    tx_index: u64,
322    block_time_us: Option<i64>,
323    grpc_recv_us: i64,
324) -> Option<DexEvent> {
325    validate_buy_payload_layout(data)?;
326
327    unsafe {
328        let timestamp = read_i64_unchecked(data, 0);
329        let base_amount_out = read_u64_unchecked(data, 8);
330        let max_quote_amount_in = read_u64_unchecked(data, 16);
331        let user_base_token_reserves = read_u64_unchecked(data, 24);
332        let user_quote_token_reserves = read_u64_unchecked(data, 32);
333        let pool_base_token_reserves = read_u64_unchecked(data, 40);
334        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
335        let quote_amount_in = read_u64_unchecked(data, 56);
336        let lp_fee_basis_points = read_u64_unchecked(data, 64);
337        let lp_fee = read_u64_unchecked(data, 72);
338        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
339        let protocol_fee = read_u64_unchecked(data, 88);
340        let quote_amount_in_with_lp_fee = read_u64_unchecked(data, 96);
341        let user_quote_amount_in = read_u64_unchecked(data, 104);
342
343        let pool = read_pubkey_unchecked(data, 112);
344        let user = read_pubkey_unchecked(data, 144);
345        let user_base_token_account = read_pubkey_unchecked(data, 176);
346        let user_quote_token_account = read_pubkey_unchecked(data, 208);
347        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
348        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
349        let coin_creator = read_pubkey_unchecked(data, 304);
350
351        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
352        let coin_creator_fee = read_u64_unchecked(data, 344);
353        let track_volume = read_bool_unchecked(data, 352);
354        let total_unclaimed_tokens = read_u64_unchecked(data, 353);
355        let total_claimed_tokens = read_u64_unchecked(data, 361);
356        let current_sol_volume = read_u64_unchecked(data, 369);
357        let last_update_timestamp = read_i64_unchecked(data, 377);
358
359        // New fields from IDL update
360        let mut offset = BUY_LEGACY_LEN;
361        let min_base_amount_out = if data.len() == BUY_LEGACY_LEN {
362            0
363        } else {
364            let value = read_u64_unchecked(data, offset);
365            offset += 8;
366            value
367        };
368
369        // ix_name: String (4-byte length prefix + content)
370        let ix_name = if offset + 4 <= data.len() {
371            let len = read_u32_unchecked(data, offset) as usize;
372            offset += 4;
373            if offset + len <= data.len() {
374                let string_bytes = &data[offset..offset + len];
375                let s = std::str::from_utf8(string_bytes).ok()?;
376                offset += len;
377                s.to_string()
378            } else {
379                String::new()
380            }
381        } else {
382            String::new()
383        };
384
385        // BuyEvent 新增字段 (PUMP_CASHBACK_README): cashback_fee_basis_points, cashback
386        let cashback_fee_basis_points =
387            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
388        offset += 8;
389        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
390        offset += 8;
391        let buyback_fee_basis_points =
392            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
393        offset += 8;
394        let buyback_fee =
395            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
396        offset += 8;
397        let virtual_quote_reserves =
398            if offset + 16 <= data.len() { read_i128_unchecked(data, offset) } else { 0 };
399        offset += 16;
400        let can_boost = if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
401        offset += 1;
402        let base_supply =
403            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
404
405        let metadata = EventMetadata {
406            signature,
407            slot,
408            tx_index,
409            block_time_us: block_time_us.unwrap_or(0),
410            grpc_recv_us,
411            recent_blockhash: None,
412        };
413
414        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
415            metadata,
416            timestamp,
417            base_amount_out,
418            max_quote_amount_in,
419            user_base_token_reserves,
420            user_quote_token_reserves,
421            pool_base_token_reserves,
422            pool_quote_token_reserves,
423            quote_amount_in,
424            lp_fee_basis_points,
425            lp_fee,
426            protocol_fee_basis_points,
427            protocol_fee,
428            quote_amount_in_with_lp_fee,
429            user_quote_amount_in,
430            pool,
431            user,
432            user_base_token_account,
433            user_quote_token_account,
434            protocol_fee_recipient,
435            protocol_fee_recipient_token_account,
436            coin_creator,
437            coin_creator_fee_basis_points,
438            coin_creator_fee,
439            track_volume,
440            total_unclaimed_tokens,
441            total_claimed_tokens,
442            current_sol_volume,
443            last_update_timestamp,
444            min_base_amount_out,
445            ix_name,
446            cashback_fee_basis_points,
447            cashback,
448            buyback_fee_basis_points,
449            buyback_fee,
450            virtual_quote_reserves,
451            can_boost,
452            base_supply,
453            ..Default::default()
454        }))
455    }
456}
457
458/// 解析卖出事件 (极限优化)
459#[inline(always)]
460fn parse_sell_event_optimized(
461    data: &[u8],
462    signature: Signature,
463    slot: u64,
464    tx_index: u64,
465    block_time_us: Option<i64>,
466    grpc_recv_us: i64,
467) -> Option<DexEvent> {
468    validate_sell_payload_layout(data)?;
469
470    unsafe {
471        let timestamp = read_i64_unchecked(data, 0);
472        let base_amount_in = read_u64_unchecked(data, 8);
473        let min_quote_amount_out = read_u64_unchecked(data, 16);
474        let user_base_token_reserves = read_u64_unchecked(data, 24);
475        let user_quote_token_reserves = read_u64_unchecked(data, 32);
476        let pool_base_token_reserves = read_u64_unchecked(data, 40);
477        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
478        let quote_amount_out = read_u64_unchecked(data, 56);
479        let lp_fee_basis_points = read_u64_unchecked(data, 64);
480        let lp_fee = read_u64_unchecked(data, 72);
481        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
482        let protocol_fee = read_u64_unchecked(data, 88);
483        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
484        let user_quote_amount_out = read_u64_unchecked(data, 104);
485
486        let pool = read_pubkey_unchecked(data, 112);
487        let user = read_pubkey_unchecked(data, 144);
488        let user_base_token_account = read_pubkey_unchecked(data, 176);
489        let user_quote_token_account = read_pubkey_unchecked(data, 208);
490        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
491        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
492        let coin_creator = read_pubkey_unchecked(data, 304);
493
494        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
495        let coin_creator_fee = read_u64_unchecked(data, 344);
496        // SellEvent 新增字段 (PUMP_CASHBACK_README): cashback_fee_basis_points, cashback
497        let cashback_fee_basis_points =
498            if data.len() >= 360 { read_u64_unchecked(data, 352) } else { 0 };
499        let cashback = if data.len() >= 368 { read_u64_unchecked(data, 360) } else { 0 };
500        let buyback_fee_basis_points =
501            if data.len() >= 376 { read_u64_unchecked(data, 368) } else { 0 };
502        let buyback_fee = if data.len() >= 384 { read_u64_unchecked(data, 376) } else { 0 };
503        let virtual_quote_reserves =
504            if data.len() >= 400 { read_i128_unchecked(data, 384) } else { 0 };
505        let can_boost = if data.len() >= 401 { read_bool_unchecked(data, 400) } else { false };
506        let base_supply = if data.len() >= 409 { read_u64_unchecked(data, 401) } else { 0 };
507
508        let metadata = EventMetadata {
509            signature,
510            slot,
511            tx_index,
512            block_time_us: block_time_us.unwrap_or(0),
513            grpc_recv_us,
514            recent_blockhash: None,
515        };
516
517        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
518            metadata,
519            timestamp,
520            base_amount_in,
521            min_quote_amount_out,
522            user_base_token_reserves,
523            user_quote_token_reserves,
524            pool_base_token_reserves,
525            pool_quote_token_reserves,
526            quote_amount_out,
527            lp_fee_basis_points,
528            lp_fee,
529            protocol_fee_basis_points,
530            protocol_fee,
531            quote_amount_out_without_lp_fee,
532            user_quote_amount_out,
533            pool,
534            user,
535            user_base_token_account,
536            user_quote_token_account,
537            protocol_fee_recipient,
538            protocol_fee_recipient_token_account,
539            coin_creator,
540            coin_creator_fee_basis_points,
541            coin_creator_fee,
542            cashback_fee_basis_points,
543            cashback,
544            buyback_fee_basis_points,
545            buyback_fee,
546            virtual_quote_reserves,
547            can_boost,
548            base_supply,
549            ..Default::default()
550        }))
551    }
552}
553
554/// 解析池创建事件 (极限优化)
555#[inline(always)]
556fn parse_create_pool_event_optimized(
557    data: &[u8],
558    signature: Signature,
559    slot: u64,
560    tx_index: u64,
561    block_time_us: Option<i64>,
562    grpc_recv_us: i64,
563) -> Option<DexEvent> {
564    // 一次性边界检查 (含 IDL 最后一列 is_mayhem_mode: bool)
565    const CREATE_POOL_EVENT_LEN: usize = 326;
566    const REQUIRED_LEN: usize = CREATE_POOL_EVENT_LEN;
567    if data.len() < REQUIRED_LEN {
568        return None;
569    }
570
571    unsafe {
572        let timestamp = read_i64_unchecked(data, 0);
573        let index = read_u16_unchecked(data, 8);
574
575        let creator = read_pubkey_unchecked(data, 10);
576        let base_mint = read_pubkey_unchecked(data, 42);
577        let quote_mint = read_pubkey_unchecked(data, 74);
578
579        let base_mint_decimals = read_u8_unchecked(data, 106);
580        let quote_mint_decimals = read_u8_unchecked(data, 107);
581
582        let base_amount_in = read_u64_unchecked(data, 108);
583        let quote_amount_in = read_u64_unchecked(data, 116);
584        let pool_base_amount = read_u64_unchecked(data, 124);
585        let pool_quote_amount = read_u64_unchecked(data, 132);
586        let minimum_liquidity = read_u64_unchecked(data, 140);
587        let initial_liquidity = read_u64_unchecked(data, 148);
588        let lp_token_amount_out = read_u64_unchecked(data, 156);
589
590        let pool_bump = read_u8_unchecked(data, 164);
591
592        let pool = read_pubkey_unchecked(data, 165);
593        let lp_mint = read_pubkey_unchecked(data, 197);
594        let user_base_token_account = read_pubkey_unchecked(data, 229);
595        let user_quote_token_account = read_pubkey_unchecked(data, 261);
596        let coin_creator = read_pubkey_unchecked(data, 293);
597        let is_mayhem_mode = read_bool_unchecked(data, 325);
598
599        let metadata = EventMetadata {
600            signature,
601            slot,
602            tx_index,
603            block_time_us: block_time_us.unwrap_or(0),
604            grpc_recv_us,
605            recent_blockhash: None,
606        };
607
608        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
609            metadata,
610            timestamp,
611            index,
612            creator,
613            base_mint,
614            quote_mint,
615            base_mint_decimals,
616            quote_mint_decimals,
617            base_amount_in,
618            quote_amount_in,
619            pool_base_amount,
620            pool_quote_amount,
621            minimum_liquidity,
622            initial_liquidity,
623            lp_token_amount_out,
624            pool_bump,
625            pool,
626            lp_mint,
627            user_base_token_account,
628            user_quote_token_account,
629            coin_creator,
630            is_mayhem_mode,
631            is_cashback_coin: false,
632        }))
633    }
634}
635
636/// 解析添加流动性事件 (极限优化)
637#[inline(always)]
638fn parse_add_liquidity_event_optimized(
639    data: &[u8],
640    signature: Signature,
641    slot: u64,
642    tx_index: u64,
643    block_time_us: Option<i64>,
644    grpc_recv_us: i64,
645) -> Option<DexEvent> {
646    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
647    if data.len() < REQUIRED_LEN {
648        return None;
649    }
650
651    unsafe {
652        let timestamp = read_i64_unchecked(data, 0);
653        let lp_token_amount_out = read_u64_unchecked(data, 8);
654        let max_base_amount_in = read_u64_unchecked(data, 16);
655        let max_quote_amount_in = read_u64_unchecked(data, 24);
656        let user_base_token_reserves = read_u64_unchecked(data, 32);
657        let user_quote_token_reserves = read_u64_unchecked(data, 40);
658        let pool_base_token_reserves = read_u64_unchecked(data, 48);
659        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
660        let base_amount_in = read_u64_unchecked(data, 64);
661        let quote_amount_in = read_u64_unchecked(data, 72);
662        let lp_mint_supply = read_u64_unchecked(data, 80);
663
664        let pool = read_pubkey_unchecked(data, 88);
665        let user = read_pubkey_unchecked(data, 120);
666        let user_base_token_account = read_pubkey_unchecked(data, 152);
667        let user_quote_token_account = read_pubkey_unchecked(data, 184);
668        let user_pool_token_account = read_pubkey_unchecked(data, 216);
669
670        let metadata = EventMetadata {
671            signature,
672            slot,
673            tx_index,
674            block_time_us: block_time_us.unwrap_or(0),
675            grpc_recv_us,
676            recent_blockhash: None,
677        };
678
679        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
680            metadata,
681            timestamp,
682            lp_token_amount_out,
683            max_base_amount_in,
684            max_quote_amount_in,
685            user_base_token_reserves,
686            user_quote_token_reserves,
687            pool_base_token_reserves,
688            pool_quote_token_reserves,
689            base_amount_in,
690            quote_amount_in,
691            lp_mint_supply,
692            pool,
693            user,
694            user_base_token_account,
695            user_quote_token_account,
696            user_pool_token_account,
697        }))
698    }
699}
700
701/// 解析移除流动性事件 (极限优化)
702#[inline(always)]
703fn parse_remove_liquidity_event_optimized(
704    data: &[u8],
705    signature: Signature,
706    slot: u64,
707    tx_index: u64,
708    block_time_us: Option<i64>,
709    grpc_recv_us: i64,
710) -> Option<DexEvent> {
711    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
712    if data.len() < REQUIRED_LEN {
713        return None;
714    }
715
716    unsafe {
717        let timestamp = read_i64_unchecked(data, 0);
718        let lp_token_amount_in = read_u64_unchecked(data, 8);
719        let min_base_amount_out = read_u64_unchecked(data, 16);
720        let min_quote_amount_out = read_u64_unchecked(data, 24);
721        let user_base_token_reserves = read_u64_unchecked(data, 32);
722        let user_quote_token_reserves = read_u64_unchecked(data, 40);
723        let pool_base_token_reserves = read_u64_unchecked(data, 48);
724        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
725        let base_amount_out = read_u64_unchecked(data, 64);
726        let quote_amount_out = read_u64_unchecked(data, 72);
727        let lp_mint_supply = read_u64_unchecked(data, 80);
728
729        let pool = read_pubkey_unchecked(data, 88);
730        let user = read_pubkey_unchecked(data, 120);
731        let user_base_token_account = read_pubkey_unchecked(data, 152);
732        let user_quote_token_account = read_pubkey_unchecked(data, 184);
733        let user_pool_token_account = read_pubkey_unchecked(data, 216);
734
735        let metadata = EventMetadata {
736            signature,
737            slot,
738            tx_index,
739            block_time_us: block_time_us.unwrap_or(0),
740            grpc_recv_us,
741            recent_blockhash: None,
742        };
743
744        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
745            metadata,
746            timestamp,
747            lp_token_amount_in,
748            min_base_amount_out,
749            min_quote_amount_out,
750            user_base_token_reserves,
751            user_quote_token_reserves,
752            pool_base_token_reserves,
753            pool_quote_token_reserves,
754            base_amount_out,
755            quote_amount_out,
756            lp_mint_supply,
757            pool,
758            user,
759            user_base_token_account,
760            user_quote_token_account,
761            user_pool_token_account,
762        }))
763    }
764}
765
766// ============================================================================
767// 快速过滤 API (用于事件过滤场景)
768// ============================================================================
769
770/// 快速判断事件类型 (只解析 discriminator)
771///
772/// 性能: <50ns
773#[inline(always)]
774pub fn get_event_type_fast(log: &str) -> Option<u64> {
775    extract_discriminator_simd(log)
776}
777
778/// 检查是否为特定事件类型 (SIMD 优化)
779#[inline(always)]
780pub fn is_event_type(log: &str, discriminator: u64) -> bool {
781    extract_discriminator_simd(log) == Some(discriminator)
782}
783
784// ============================================================================
785// Public API for optimized parsing from pre-decoded data
786// These functions accept already-decoded data (without discriminator)
787// ============================================================================
788
789/// Parse PumpSwap Buy event from pre-decoded data
790#[inline(always)]
791pub fn parse_buy_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
792    validate_buy_payload_layout(data)?;
793
794    unsafe {
795        let timestamp = read_i64_unchecked(data, 0);
796        let base_amount_out = read_u64_unchecked(data, 8);
797        let max_quote_amount_in = read_u64_unchecked(data, 16);
798        let user_base_token_reserves = read_u64_unchecked(data, 24);
799        let user_quote_token_reserves = read_u64_unchecked(data, 32);
800        let pool_base_token_reserves = read_u64_unchecked(data, 40);
801        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
802        let quote_amount_in = read_u64_unchecked(data, 56);
803        let lp_fee_basis_points = read_u64_unchecked(data, 64);
804        let lp_fee = read_u64_unchecked(data, 72);
805        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
806        let protocol_fee = read_u64_unchecked(data, 88);
807        let quote_amount_in_with_lp_fee = read_u64_unchecked(data, 96);
808        let user_quote_amount_in = read_u64_unchecked(data, 104);
809
810        let pool = read_pubkey_unchecked(data, 112);
811        let user = read_pubkey_unchecked(data, 144);
812        let user_base_token_account = read_pubkey_unchecked(data, 176);
813        let user_quote_token_account = read_pubkey_unchecked(data, 208);
814        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
815        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
816        let coin_creator = read_pubkey_unchecked(data, 304);
817
818        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
819        let coin_creator_fee = read_u64_unchecked(data, 344);
820        let track_volume = read_bool_unchecked(data, 352);
821        let total_unclaimed_tokens = read_u64_unchecked(data, 353);
822        let total_claimed_tokens = read_u64_unchecked(data, 361);
823        let current_sol_volume = read_u64_unchecked(data, 369);
824        let last_update_timestamp = read_i64_unchecked(data, 377);
825
826        // New fields from IDL update
827        let mut offset = BUY_LEGACY_LEN;
828        let min_base_amount_out = if data.len() == BUY_LEGACY_LEN {
829            0
830        } else {
831            let value = read_u64_unchecked(data, offset);
832            offset += 8;
833            value
834        };
835
836        // ix_name: String (4-byte length prefix + content)
837        let ix_name = if offset + 4 <= data.len() {
838            let len = read_u32_unchecked(data, offset) as usize;
839            offset += 4;
840            if offset + len <= data.len() {
841                let string_bytes = &data[offset..offset + len];
842                let s = std::str::from_utf8(string_bytes).ok()?;
843                offset += len;
844                s.to_string()
845            } else {
846                String::new()
847            }
848        } else {
849            String::new()
850        };
851
852        let cashback_fee_basis_points =
853            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
854        offset += 8;
855        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
856        offset += 8;
857        let buyback_fee_basis_points =
858            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
859        offset += 8;
860        let buyback_fee =
861            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
862        offset += 8;
863        let virtual_quote_reserves =
864            if offset + 16 <= data.len() { read_i128_unchecked(data, offset) } else { 0 };
865        offset += 16;
866        let can_boost = if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
867        offset += 1;
868        let base_supply =
869            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
870
871        Some(DexEvent::PumpSwapBuy(PumpSwapBuyEvent {
872            metadata,
873            timestamp,
874            base_amount_out,
875            max_quote_amount_in,
876            user_base_token_reserves,
877            user_quote_token_reserves,
878            pool_base_token_reserves,
879            pool_quote_token_reserves,
880            quote_amount_in,
881            lp_fee_basis_points,
882            lp_fee,
883            protocol_fee_basis_points,
884            protocol_fee,
885            quote_amount_in_with_lp_fee,
886            user_quote_amount_in,
887            pool,
888            user,
889            user_base_token_account,
890            user_quote_token_account,
891            protocol_fee_recipient,
892            protocol_fee_recipient_token_account,
893            coin_creator,
894            coin_creator_fee_basis_points,
895            coin_creator_fee,
896            track_volume,
897            total_unclaimed_tokens,
898            total_claimed_tokens,
899            current_sol_volume,
900            last_update_timestamp,
901            min_base_amount_out,
902            ix_name,
903            cashback_fee_basis_points,
904            cashback,
905            buyback_fee_basis_points,
906            buyback_fee,
907            virtual_quote_reserves,
908            can_boost,
909            base_supply,
910            ..Default::default()
911        }))
912    }
913}
914
915/// Parse PumpSwap Sell event from pre-decoded data
916#[inline(always)]
917pub fn parse_sell_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
918    validate_sell_payload_layout(data)?;
919    const CASHBACK_FEE_BASIS_POINTS_OFFSET: usize = 352;
920    const CASHBACK_OFFSET: usize = 360;
921    const CASHBACK_FIELDS_LEN: usize = 16;
922    unsafe {
923        let timestamp = read_i64_unchecked(data, 0);
924        let base_amount_in = read_u64_unchecked(data, 8);
925        let min_quote_amount_out = read_u64_unchecked(data, 16);
926        let user_base_token_reserves = read_u64_unchecked(data, 24);
927        let user_quote_token_reserves = read_u64_unchecked(data, 32);
928        let pool_base_token_reserves = read_u64_unchecked(data, 40);
929        let pool_quote_token_reserves = read_u64_unchecked(data, 48);
930        let quote_amount_out = read_u64_unchecked(data, 56);
931        let lp_fee_basis_points = read_u64_unchecked(data, 64);
932        let lp_fee = read_u64_unchecked(data, 72);
933        let protocol_fee_basis_points = read_u64_unchecked(data, 80);
934        let protocol_fee = read_u64_unchecked(data, 88);
935        let quote_amount_out_without_lp_fee = read_u64_unchecked(data, 96);
936        let user_quote_amount_out = read_u64_unchecked(data, 104);
937
938        let pool = read_pubkey_unchecked(data, 112);
939        let user = read_pubkey_unchecked(data, 144);
940        let user_base_token_account = read_pubkey_unchecked(data, 176);
941        let user_quote_token_account = read_pubkey_unchecked(data, 208);
942        let protocol_fee_recipient = read_pubkey_unchecked(data, 240);
943        let protocol_fee_recipient_token_account = read_pubkey_unchecked(data, 272);
944        let coin_creator = read_pubkey_unchecked(data, 304);
945
946        let coin_creator_fee_basis_points = read_u64_unchecked(data, 336);
947        let coin_creator_fee = read_u64_unchecked(data, 344);
948        let (cashback_fee_basis_points, cashback) =
949            if data.len() >= CASHBACK_FEE_BASIS_POINTS_OFFSET + CASHBACK_FIELDS_LEN {
950                (
951                    read_u64_unchecked(data, CASHBACK_FEE_BASIS_POINTS_OFFSET),
952                    read_u64_unchecked(data, CASHBACK_OFFSET),
953                )
954            } else {
955                (0, 0)
956            };
957        let buyback_fee_basis_points =
958            if data.len() >= 376 { read_u64_unchecked(data, 368) } else { 0 };
959        let buyback_fee = if data.len() >= 384 { read_u64_unchecked(data, 376) } else { 0 };
960        let virtual_quote_reserves =
961            if data.len() >= 400 { read_i128_unchecked(data, 384) } else { 0 };
962        let can_boost = if data.len() >= 401 { read_bool_unchecked(data, 400) } else { false };
963        let base_supply = if data.len() >= 409 { read_u64_unchecked(data, 401) } else { 0 };
964
965        Some(DexEvent::PumpSwapSell(PumpSwapSellEvent {
966            metadata,
967            timestamp,
968            base_amount_in,
969            min_quote_amount_out,
970            user_base_token_reserves,
971            user_quote_token_reserves,
972            pool_base_token_reserves,
973            pool_quote_token_reserves,
974            quote_amount_out,
975            lp_fee_basis_points,
976            lp_fee,
977            protocol_fee_basis_points,
978            protocol_fee,
979            quote_amount_out_without_lp_fee,
980            user_quote_amount_out,
981            pool,
982            user,
983            user_base_token_account,
984            user_quote_token_account,
985            protocol_fee_recipient,
986            protocol_fee_recipient_token_account,
987            coin_creator,
988            coin_creator_fee_basis_points,
989            coin_creator_fee,
990            cashback_fee_basis_points,
991            cashback,
992            buyback_fee_basis_points,
993            buyback_fee,
994            virtual_quote_reserves,
995            can_boost,
996            base_supply,
997            ..Default::default()
998        }))
999    }
1000}
1001
1002/// Parse PumpSwap CreatePool event from pre-decoded data
1003#[inline(always)]
1004pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1005    const CREATE_POOL_EVENT_LEN: usize = 326;
1006    const REQUIRED_LEN: usize = CREATE_POOL_EVENT_LEN;
1007    if data.len() < REQUIRED_LEN {
1008        return None;
1009    }
1010
1011    unsafe {
1012        let timestamp = read_i64_unchecked(data, 0);
1013        let index = read_u16_unchecked(data, 8);
1014
1015        let creator = read_pubkey_unchecked(data, 10);
1016        let base_mint = read_pubkey_unchecked(data, 42);
1017        let quote_mint = read_pubkey_unchecked(data, 74);
1018
1019        let base_mint_decimals = read_u8_unchecked(data, 106);
1020        let quote_mint_decimals = read_u8_unchecked(data, 107);
1021
1022        let base_amount_in = read_u64_unchecked(data, 108);
1023        let quote_amount_in = read_u64_unchecked(data, 116);
1024        let pool_base_amount = read_u64_unchecked(data, 124);
1025        let pool_quote_amount = read_u64_unchecked(data, 132);
1026        let minimum_liquidity = read_u64_unchecked(data, 140);
1027        let initial_liquidity = read_u64_unchecked(data, 148);
1028        let lp_token_amount_out = read_u64_unchecked(data, 156);
1029
1030        let pool_bump = read_u8_unchecked(data, 164);
1031
1032        let pool = read_pubkey_unchecked(data, 165);
1033        let lp_mint = read_pubkey_unchecked(data, 197);
1034        let user_base_token_account = read_pubkey_unchecked(data, 229);
1035        let user_quote_token_account = read_pubkey_unchecked(data, 261);
1036        let coin_creator = read_pubkey_unchecked(data, 293);
1037        let is_mayhem_mode = data.len() > 325 && read_bool_unchecked(data, 325);
1038
1039        Some(DexEvent::PumpSwapCreatePool(PumpSwapCreatePoolEvent {
1040            metadata,
1041            timestamp,
1042            index,
1043            creator,
1044            base_mint,
1045            quote_mint,
1046            base_mint_decimals,
1047            quote_mint_decimals,
1048            base_amount_in,
1049            quote_amount_in,
1050            pool_base_amount,
1051            pool_quote_amount,
1052            minimum_liquidity,
1053            initial_liquidity,
1054            lp_token_amount_out,
1055            pool_bump,
1056            pool,
1057            lp_mint,
1058            user_base_token_account,
1059            user_quote_token_account,
1060            coin_creator,
1061            is_mayhem_mode,
1062            is_cashback_coin: false,
1063        }))
1064    }
1065}
1066
1067/// Parse PumpSwap AddLiquidity event from pre-decoded data
1068#[inline(always)]
1069pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1070    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
1071    if data.len() < REQUIRED_LEN {
1072        return None;
1073    }
1074
1075    unsafe {
1076        let timestamp = read_i64_unchecked(data, 0);
1077        let lp_token_amount_out = read_u64_unchecked(data, 8);
1078        let max_base_amount_in = read_u64_unchecked(data, 16);
1079        let max_quote_amount_in = read_u64_unchecked(data, 24);
1080        let user_base_token_reserves = read_u64_unchecked(data, 32);
1081        let user_quote_token_reserves = read_u64_unchecked(data, 40);
1082        let pool_base_token_reserves = read_u64_unchecked(data, 48);
1083        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
1084        let base_amount_in = read_u64_unchecked(data, 64);
1085        let quote_amount_in = read_u64_unchecked(data, 72);
1086        let lp_mint_supply = read_u64_unchecked(data, 80);
1087
1088        let pool = read_pubkey_unchecked(data, 88);
1089        let user = read_pubkey_unchecked(data, 120);
1090        let user_base_token_account = read_pubkey_unchecked(data, 152);
1091        let user_quote_token_account = read_pubkey_unchecked(data, 184);
1092        let user_pool_token_account = read_pubkey_unchecked(data, 216);
1093
1094        Some(DexEvent::PumpSwapLiquidityAdded(PumpSwapLiquidityAdded {
1095            metadata,
1096            timestamp,
1097            lp_token_amount_out,
1098            max_base_amount_in,
1099            max_quote_amount_in,
1100            user_base_token_reserves,
1101            user_quote_token_reserves,
1102            pool_base_token_reserves,
1103            pool_quote_token_reserves,
1104            base_amount_in,
1105            quote_amount_in,
1106            lp_mint_supply,
1107            pool,
1108            user,
1109            user_base_token_account,
1110            user_quote_token_account,
1111            user_pool_token_account,
1112        }))
1113    }
1114}
1115
1116/// Parse PumpSwap RemoveLiquidity event from pre-decoded data
1117#[inline(always)]
1118pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1119    const REQUIRED_LEN: usize = 10 * 8 + 5 * 32;
1120    if data.len() < REQUIRED_LEN {
1121        return None;
1122    }
1123
1124    unsafe {
1125        let timestamp = read_i64_unchecked(data, 0);
1126        let lp_token_amount_in = read_u64_unchecked(data, 8);
1127        let min_base_amount_out = read_u64_unchecked(data, 16);
1128        let min_quote_amount_out = read_u64_unchecked(data, 24);
1129        let user_base_token_reserves = read_u64_unchecked(data, 32);
1130        let user_quote_token_reserves = read_u64_unchecked(data, 40);
1131        let pool_base_token_reserves = read_u64_unchecked(data, 48);
1132        let pool_quote_token_reserves = read_u64_unchecked(data, 56);
1133        let base_amount_out = read_u64_unchecked(data, 64);
1134        let quote_amount_out = read_u64_unchecked(data, 72);
1135        let lp_mint_supply = read_u64_unchecked(data, 80);
1136
1137        let pool = read_pubkey_unchecked(data, 88);
1138        let user = read_pubkey_unchecked(data, 120);
1139        let user_base_token_account = read_pubkey_unchecked(data, 152);
1140        let user_quote_token_account = read_pubkey_unchecked(data, 184);
1141        let user_pool_token_account = read_pubkey_unchecked(data, 216);
1142
1143        Some(DexEvent::PumpSwapLiquidityRemoved(PumpSwapLiquidityRemoved {
1144            metadata,
1145            timestamp,
1146            lp_token_amount_in,
1147            min_base_amount_out,
1148            min_quote_amount_out,
1149            user_base_token_reserves,
1150            user_quote_token_reserves,
1151            pool_base_token_reserves,
1152            pool_quote_token_reserves,
1153            base_amount_out,
1154            quote_amount_out,
1155            lp_mint_supply,
1156            pool,
1157            user,
1158            user_base_token_account,
1159            user_quote_token_account,
1160            user_pool_token_account,
1161        }))
1162    }
1163}
1164
1165// ============================================================================
1166// 性能统计 API (可选)
1167// ============================================================================
1168
1169#[cfg(feature = "perf-stats")]
1170pub fn get_perf_stats() -> (usize, usize) {
1171    let count = PARSE_COUNT.load(Ordering::Relaxed);
1172    let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
1173    (count, total_ns)
1174}
1175
1176#[cfg(feature = "perf-stats")]
1177pub fn reset_perf_stats() {
1178    PARSE_COUNT.store(0, Ordering::Relaxed);
1179    PARSE_TIME_NS.store(0, Ordering::Relaxed);
1180}
1181
1182#[cfg(test)]
1183mod tests {
1184    use super::*;
1185    use solana_sdk::{pubkey::Pubkey, signature::Signature};
1186
1187    fn metadata() -> EventMetadata {
1188        EventMetadata {
1189            signature: Signature::default(),
1190            slot: 0,
1191            tx_index: 0,
1192            block_time_us: 0,
1193            grpc_recv_us: 0,
1194            recent_blockhash: None,
1195        }
1196    }
1197
1198    fn write_u64(buf: &mut [u8], offset: usize, value: u64) {
1199        buf[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
1200    }
1201
1202    fn write_i64(buf: &mut [u8], offset: usize, value: i64) {
1203        buf[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
1204    }
1205
1206    fn write_i128(buf: &mut [u8], offset: usize, value: i128) {
1207        buf[offset..offset + 16].copy_from_slice(&value.to_le_bytes());
1208    }
1209
1210    fn write_pubkey(buf: &mut [u8], offset: usize, value: Pubkey) {
1211        buf[offset..offset + 32].copy_from_slice(value.as_ref());
1212    }
1213
1214    fn build_sell_payload(include_cashback: bool, include_boost: bool) -> Vec<u8> {
1215        let len = if include_boost {
1216            409
1217        } else if include_cashback {
1218            368
1219        } else {
1220            352
1221        };
1222        let mut data = vec![0u8; len];
1223
1224        write_i64(&mut data, 0, 1_713_498_953);
1225        write_u64(&mut data, 8, 11);
1226        write_u64(&mut data, 16, 22);
1227        write_u64(&mut data, 24, 33);
1228        write_u64(&mut data, 32, 44);
1229        write_u64(&mut data, 40, 55);
1230        write_u64(&mut data, 48, 66);
1231        write_u64(&mut data, 56, 77);
1232        write_u64(&mut data, 64, 88);
1233        write_u64(&mut data, 72, 99);
1234        write_u64(&mut data, 80, 111);
1235        write_u64(&mut data, 88, 122);
1236        write_u64(&mut data, 96, 133);
1237        write_u64(&mut data, 104, 144);
1238
1239        write_pubkey(&mut data, 112, Pubkey::new_from_array([1; 32]));
1240        write_pubkey(&mut data, 144, Pubkey::new_from_array([2; 32]));
1241        write_pubkey(&mut data, 176, Pubkey::new_from_array([3; 32]));
1242        write_pubkey(&mut data, 208, Pubkey::new_from_array([4; 32]));
1243        write_pubkey(&mut data, 240, Pubkey::new_from_array([5; 32]));
1244        write_pubkey(&mut data, 272, Pubkey::new_from_array([6; 32]));
1245        write_pubkey(&mut data, 304, Pubkey::new_from_array([7; 32]));
1246
1247        write_u64(&mut data, 336, 155);
1248        write_u64(&mut data, 344, 166);
1249
1250        if include_cashback {
1251            write_u64(&mut data, 352, 177);
1252            write_u64(&mut data, 360, 188);
1253        }
1254        if include_boost {
1255            write_u64(&mut data, 368, 199);
1256            write_u64(&mut data, 376, 211);
1257            write_i128(&mut data, 384, -222);
1258            data[400] = 1;
1259            write_u64(&mut data, 401, 233);
1260        }
1261
1262        data
1263    }
1264
1265    fn build_buy_payload_with_boost() -> Vec<u8> {
1266        let mut data = vec![0u8; 457];
1267        write_u64(&mut data, 385, 11);
1268        data[393..397].copy_from_slice(&3u32.to_le_bytes());
1269        data[397..400].copy_from_slice(b"buy");
1270        write_u64(&mut data, 400, 22);
1271        write_u64(&mut data, 408, 33);
1272        write_u64(&mut data, 416, 44);
1273        write_u64(&mut data, 424, 55);
1274        write_i128(&mut data, 432, -66);
1275        data[448] = 1;
1276        write_u64(&mut data, 449, 77);
1277        data
1278    }
1279
1280    fn build_create_pool_payload(is_mayhem_mode: bool) -> Vec<u8> {
1281        let mut data = vec![0u8; 326];
1282
1283        write_i64(&mut data, 0, 1_713_498_953);
1284        data[8..10].copy_from_slice(&42u16.to_le_bytes());
1285        write_pubkey(&mut data, 10, Pubkey::new_from_array([1; 32]));
1286        write_pubkey(&mut data, 42, Pubkey::new_from_array([2; 32]));
1287        write_pubkey(&mut data, 74, Pubkey::new_from_array([3; 32]));
1288        data[106] = 6;
1289        data[107] = 9;
1290        write_u64(&mut data, 108, 11);
1291        write_u64(&mut data, 116, 22);
1292        write_u64(&mut data, 124, 33);
1293        write_u64(&mut data, 132, 44);
1294        write_u64(&mut data, 140, 55);
1295        write_u64(&mut data, 148, 66);
1296        write_u64(&mut data, 156, 77);
1297        data[164] = 8;
1298        write_pubkey(&mut data, 165, Pubkey::new_from_array([4; 32]));
1299        write_pubkey(&mut data, 197, Pubkey::new_from_array([5; 32]));
1300        write_pubkey(&mut data, 229, Pubkey::new_from_array([6; 32]));
1301        write_pubkey(&mut data, 261, Pubkey::new_from_array([7; 32]));
1302        write_pubkey(&mut data, 293, Pubkey::new_from_array([8; 32]));
1303        data[325] = u8::from(is_mayhem_mode);
1304
1305        data
1306    }
1307
1308    #[test]
1309    fn test_discriminator_simd() {
1310        // 测试 SIMD discriminator 提取
1311        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1312        let disc = extract_discriminator_simd(log);
1313        assert!(disc.is_some());
1314    }
1315
1316    #[test]
1317    fn test_parse_performance() {
1318        // 性能测试
1319        let log = "Program data: Z/RS H8v1d3cAAAAAAAAAAA=";
1320        let sig = Signature::default();
1321
1322        let start = std::time::Instant::now();
1323        for _ in 0..1000 {
1324            let _ = parse_log(log, sig, 0, 0, Some(0), 0);
1325        }
1326        let elapsed = start.elapsed();
1327
1328        println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
1329    }
1330
1331    #[test]
1332    fn parse_sell_from_data_preserves_cashback_fields() {
1333        let event = parse_sell_from_data(&build_sell_payload(true, false), metadata())
1334            .expect("expected pumpswap sell event");
1335
1336        let DexEvent::PumpSwapSell(event) = event else {
1337            panic!("expected PumpSwapSell event");
1338        };
1339
1340        assert_eq!(event.cashback_fee_basis_points, 177);
1341        assert_eq!(event.cashback, 188);
1342        assert_eq!(event.coin_creator_fee_basis_points, 155);
1343        assert_eq!(event.coin_creator_fee, 166);
1344    }
1345
1346    #[test]
1347    fn parse_sell_from_data_keeps_legacy_payload_compatible() {
1348        let event = parse_sell_from_data(&build_sell_payload(false, false), metadata())
1349            .expect("expected legacy pumpswap sell event");
1350
1351        let DexEvent::PumpSwapSell(event) = event else {
1352            panic!("expected PumpSwapSell event");
1353        };
1354
1355        assert_eq!(event.cashback_fee_basis_points, 0);
1356        assert_eq!(event.cashback, 0);
1357        assert_eq!(event.coin_creator_fee_basis_points, 155);
1358        assert_eq!(event.coin_creator_fee, 166);
1359    }
1360
1361    #[test]
1362    fn parse_sell_from_data_preserves_boost_fields() {
1363        let event = parse_sell_from_data(&build_sell_payload(true, true), metadata())
1364            .expect("expected upgraded pumpswap sell event");
1365        let DexEvent::PumpSwapSell(event) = event else {
1366            panic!("expected PumpSwapSell event");
1367        };
1368
1369        assert_eq!(event.buyback_fee_basis_points, 199);
1370        assert_eq!(event.buyback_fee, 211);
1371        assert_eq!(event.virtual_quote_reserves, -222);
1372        assert!(event.can_boost);
1373        assert_eq!(event.base_supply, 233);
1374    }
1375
1376    #[test]
1377    fn parse_buy_from_data_preserves_boost_fields() {
1378        let event = parse_buy_from_data(&build_buy_payload_with_boost(), metadata())
1379            .expect("expected upgraded pumpswap buy event");
1380        let DexEvent::PumpSwapBuy(event) = event else {
1381            panic!("expected PumpSwapBuy event");
1382        };
1383
1384        assert_eq!(event.ix_name, "buy");
1385        assert_eq!(event.cashback_fee_basis_points, 22);
1386        assert_eq!(event.cashback, 33);
1387        assert_eq!(event.buyback_fee_basis_points, 44);
1388        assert_eq!(event.buyback_fee, 55);
1389        assert_eq!(event.virtual_quote_reserves, -66);
1390        assert!(event.can_boost);
1391        assert_eq!(event.base_supply, 77);
1392    }
1393
1394    #[test]
1395    fn parse_buy_from_data_rejects_truncated_min_base_payload() {
1396        assert!(parse_buy_from_data(&vec![0u8; 385], metadata()).is_some());
1397        for len in 386..397 {
1398            assert!(parse_buy_from_data(&vec![0u8; len], metadata()).is_none());
1399        }
1400        assert!(parse_buy_from_data(&vec![0u8; 397], metadata()).is_some());
1401    }
1402
1403    #[test]
1404    fn parse_buy_from_data_rejects_partial_or_invalid_variable_tail() {
1405        let current = build_buy_payload_with_boost();
1406        for len in 401..416 {
1407            assert!(parse_buy_from_data(&current[..len], metadata()).is_none());
1408        }
1409        assert!(parse_buy_from_data(&current[..416], metadata()).is_some());
1410        for len in 417..457 {
1411            assert!(parse_buy_from_data(&current[..len], metadata()).is_none());
1412        }
1413        assert!(parse_buy_from_data(&current, metadata()).is_some());
1414
1415        let mut invalid_utf8 = vec![0u8; 398];
1416        invalid_utf8[393..397].copy_from_slice(&1u32.to_le_bytes());
1417        invalid_utf8[397] = 0xff;
1418        assert!(parse_buy_from_data(&invalid_utf8, metadata()).is_none());
1419
1420        let mut truncated_name = vec![0u8; 398];
1421        truncated_name[393..397].copy_from_slice(&2u32.to_le_bytes());
1422        assert!(parse_buy_from_data(&truncated_name, metadata()).is_none());
1423    }
1424
1425    #[test]
1426    fn parse_sell_from_data_accepts_only_complete_schema_versions() {
1427        let current = build_sell_payload(true, true);
1428        assert!(parse_sell_from_data(&current[..352], metadata()).is_some());
1429        assert!(parse_sell_from_data(&current[..368], metadata()).is_some());
1430        assert!(parse_sell_from_data(&current, metadata()).is_some());
1431
1432        for len in (353..368).chain(369..409) {
1433            assert!(parse_sell_from_data(&current[..len], metadata()).is_none());
1434        }
1435    }
1436
1437    #[test]
1438    fn parse_create_pool_from_data_reads_mayhem_mode() {
1439        let event = parse_create_pool_from_data(&build_create_pool_payload(true), metadata())
1440            .expect("expected pumpswap create pool event");
1441
1442        let DexEvent::PumpSwapCreatePool(event) = event else {
1443            panic!("expected PumpSwapCreatePool event");
1444        };
1445
1446        assert_eq!(event.index, 42);
1447        assert!(event.is_mayhem_mode);
1448        assert!(!event.is_cashback_coin);
1449    }
1450}