Skip to main content

sol_parser_sdk/logs/
pump.rs

1//! Pump.fun `Program log` → [`DexEvent`](crate::core::events::DexEvent) (SIMD / zero-copy hot path).
2#![allow(dead_code)]
3#![allow(unused_imports)]
4#![allow(unused_variables)]
5
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9use memchr::memmem;
10use once_cell::sync::Lazy;
11
12#[cfg(feature = "perf-stats")]
13use std::sync::atomic::{AtomicUsize, Ordering};
14
15#[cfg(feature = "perf-stats")]
16pub static PARSE_COUNT: AtomicUsize = AtomicUsize::new(0);
17#[cfg(feature = "perf-stats")]
18pub static PARSE_TIME_NS: AtomicUsize = AtomicUsize::new(0);
19
20// --- discriminators ------------------------------------------------
21
22pub const CREATE_EVENT: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
23pub const TRADE_EVENT: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
24pub const MIGRATE_EVENT: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
25/// `createFeeSharingConfigEvent`(pump-fees IDL)
26pub const CREATE_FEE_SHARING_CONFIG_EVENT: u64 = crate::logs::pump_fees::discriminant_u64(
27    &crate::logs::pump_fees::CREATE_FEE_SHARING_CONFIG_EVENT_DISC,
28);
29/// `migrateBondingCurveCreatorEvent`(pump.fun IDL)
30pub const MIGRATE_BONDING_CURVE_CREATOR_EVENT: u64 =
31    u64::from_le_bytes([155, 167, 104, 220, 213, 108, 243, 3]);
32
33#[inline]
34pub fn normalize_pumpfun_ix_name(ix_name: &str) -> &str {
35    match ix_name {
36        "buy_v2" => "buy",
37        "sell_v2" => "sell",
38        "buy_exact_quote_in_v2" => "buy_exact_quote_in",
39        other => other,
40    }
41}
42
43// --- binary_read ---------------------------------------------------
44
45#[inline(always)]
46pub unsafe fn read_u64_unchecked(data: &[u8], offset: usize) -> u64 {
47    let ptr = data.as_ptr().add(offset) as *const u64;
48    u64::from_le(ptr.read_unaligned())
49}
50
51#[inline(always)]
52pub unsafe fn read_i64_unchecked(data: &[u8], offset: usize) -> i64 {
53    let ptr = data.as_ptr().add(offset) as *const i64;
54    i64::from_le(ptr.read_unaligned())
55}
56
57#[inline(always)]
58pub unsafe fn read_bool_unchecked(data: &[u8], offset: usize) -> bool {
59    *data.get_unchecked(offset) == 1
60}
61
62#[inline(always)]
63pub unsafe fn read_pubkey_unchecked(data: &[u8], offset: usize) -> Pubkey {
64    #[cfg(target_arch = "x86_64")]
65    {
66        use std::arch::x86_64::_mm_prefetch;
67        use std::arch::x86_64::_MM_HINT_T0;
68        if offset + 64 < data.len() {
69            _mm_prefetch((data.as_ptr().add(offset + 32)) as *const i8, _MM_HINT_T0);
70        }
71    }
72
73    let ptr = data.as_ptr().add(offset);
74    let mut bytes = [0u8; 32];
75    std::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), 32);
76    Pubkey::new_from_array(bytes)
77}
78
79#[inline(always)]
80pub unsafe fn read_str_unchecked(data: &[u8], offset: usize) -> Option<(&str, usize)> {
81    if data.len() < offset + 4 {
82        return None;
83    }
84
85    let len = read_u32_unchecked(data, offset) as usize;
86    if data.len() < offset + 4 + len {
87        return None;
88    }
89
90    let string_bytes = &data[offset + 4..offset + 4 + len];
91    let s = std::str::from_utf8_unchecked(string_bytes);
92    Some((s, 4 + len))
93}
94
95#[inline(always)]
96pub unsafe fn read_u32_unchecked(data: &[u8], offset: usize) -> u32 {
97    let ptr = data.as_ptr().add(offset) as *const u32;
98    u32::from_le(ptr.read_unaligned())
99}
100
101#[inline(always)]
102pub unsafe fn read_u16_unchecked(data: &[u8], offset: usize) -> u16 {
103    let ptr = data.as_ptr().add(offset) as *const u16;
104    u16::from_le(ptr.read_unaligned())
105}
106
107const MAX_TRADE_SHAREHOLDERS: usize = 64;
108
109#[inline(always)]
110unsafe fn read_optional_u64(data: &[u8], offset: &mut usize) -> u64 {
111    if *offset + 8 <= data.len() {
112        let v = read_u64_unchecked(data, *offset);
113        *offset += 8;
114        v
115    } else {
116        0
117    }
118}
119
120#[inline(always)]
121unsafe fn read_optional_pubkey(data: &[u8], offset: &mut usize) -> Pubkey {
122    if *offset + 32 <= data.len() {
123        let v = read_pubkey_unchecked(data, *offset);
124        *offset += 32;
125        v
126    } else {
127        Pubkey::default()
128    }
129}
130
131#[inline(always)]
132unsafe fn read_trade_shareholders(
133    data: &[u8],
134    offset: &mut usize,
135) -> Option<Vec<PumpFeesShareholder>> {
136    if *offset + 4 > data.len() {
137        return Some(Vec::new());
138    }
139    let n = read_u32_unchecked(data, *offset) as usize;
140    if n > MAX_TRADE_SHAREHOLDERS {
141        return None;
142    }
143    let bytes = 4usize.checked_add(n.checked_mul(34)?)?;
144    if *offset + bytes > data.len() {
145        return None;
146    }
147    *offset += 4;
148    let mut out = Vec::with_capacity(n);
149    for _ in 0..n {
150        let address = read_pubkey_unchecked(data, *offset);
151        *offset += 32;
152        let share_bps = read_u16_unchecked(data, *offset);
153        *offset += 2;
154        out.push(PumpFeesShareholder { address, share_bps });
155    }
156    Some(out)
157}
158
159#[inline(always)]
160pub(crate) unsafe fn read_trade_event_extensions(
161    data: &[u8],
162    offset: &mut usize,
163) -> Option<(u64, u64, Vec<PumpFeesShareholder>, Pubkey, u64, u64, u64)> {
164    let buyback_fee_basis_points = read_optional_u64(data, offset);
165    let buyback_fee = read_optional_u64(data, offset);
166    let shareholders = read_trade_shareholders(data, offset)?;
167    let quote_mint = normalize_pumpfun_quote_mint(read_optional_pubkey(data, offset));
168    let quote_amount = read_optional_u64(data, offset);
169    let virtual_quote_reserves = read_optional_u64(data, offset);
170    let real_quote_reserves = read_optional_u64(data, offset);
171    Some((
172        buyback_fee_basis_points,
173        buyback_fee,
174        shareholders,
175        quote_mint,
176        quote_amount,
177        virtual_quote_reserves,
178        real_quote_reserves,
179    ))
180}
181
182// --- log_decode ----------------------------------------------------
183
184static BASE64_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: "));
185/// `b"Program data: "`.len() — base64 payload starts immediately after this tag.
186const PROGRAM_DATA_TAG_LEN: usize = 14;
187
188#[inline(always)]
189pub fn extract_program_data_zero_copy<'a>(
190    log: &'a str,
191    buf: &'a mut [u8; 2048],
192) -> Option<&'a [u8]> {
193    let log_bytes = log.as_bytes();
194    let pos = BASE64_FINDER.find(log_bytes)?;
195
196    let data_part = &log[pos + PROGRAM_DATA_TAG_LEN..];
197    let trimmed = data_part.trim();
198
199    if trimmed.len() > 2700 {
200        return None;
201    }
202
203    use base64_simd::AsOut;
204    let decoded_slice =
205        base64_simd::STANDARD.decode(trimmed.as_bytes(), buf.as_mut().as_out()).ok()?;
206
207    Some(decoded_slice)
208}
209
210#[inline(always)]
211pub fn extract_discriminator_simd(log: &str) -> Option<u64> {
212    let log_bytes = log.as_bytes();
213    let pos = BASE64_FINDER.find(log_bytes)?;
214
215    let data_part = &log[pos + PROGRAM_DATA_TAG_LEN..];
216    let trimmed = data_part.trim();
217
218    if trimmed.len() < 16 {
219        return None;
220    }
221
222    use base64_simd::AsOut;
223    let mut buf = [0u8; 12];
224    let prefix = trimmed.as_bytes().get(..16)?;
225    base64_simd::STANDARD.decode(prefix, buf.as_mut().as_out()).ok()?;
226
227    unsafe {
228        let ptr = buf.as_ptr() as *const u64;
229        Some(ptr.read_unaligned())
230    }
231}
232
233// --- main parser ---------------------------------------------------
234/// 主解析函数 (极限优化版本)
235///
236/// 性能目标: <100ns
237#[inline(always)]
238pub fn parse_log(
239    log: &str,
240    signature: Signature,
241    slot: u64,
242    tx_index: u64,
243    block_time_us: Option<i64>,
244    grpc_recv_us: i64,
245    is_created_buy: bool,
246) -> Option<DexEvent> {
247    #[cfg(feature = "perf-stats")]
248    let start = std::time::Instant::now();
249
250    // 使用栈分配的缓冲区 (增加到 2KB 以防止 base64-simd 缓冲区溢出)
251    let mut buf = [0u8; 2048];
252    let program_data = extract_program_data_zero_copy(log, &mut buf)?;
253
254    if program_data.len() < 8 {
255        return None;
256    }
257
258    // 使用 unsafe 读取 discriminator (SIMD 优化)
259    let discriminator = unsafe { read_u64_unchecked(program_data, 0) };
260    let data = &program_data[8..];
261
262    let result = match discriminator {
263        CREATE_EVENT => parse_create_event_optimized(
264            data,
265            signature,
266            slot,
267            tx_index,
268            block_time_us,
269            grpc_recv_us,
270        ),
271        TRADE_EVENT => parse_trade_event_optimized(
272            data,
273            signature,
274            slot,
275            tx_index,
276            block_time_us,
277            grpc_recv_us,
278            is_created_buy,
279        ),
280        MIGRATE_EVENT => parse_migrate_event_optimized(
281            data,
282            signature,
283            slot,
284            tx_index,
285            block_time_us,
286            grpc_recv_us,
287        ),
288        CREATE_FEE_SHARING_CONFIG_EVENT => parse_create_fee_sharing_config_event_optimized(
289            data,
290            signature,
291            slot,
292            tx_index,
293            block_time_us,
294            grpc_recv_us,
295        ),
296        MIGRATE_BONDING_CURVE_CREATOR_EVENT => parse_migrate_bonding_curve_creator_event_optimized(
297            data,
298            signature,
299            slot,
300            tx_index,
301            block_time_us,
302            grpc_recv_us,
303        ),
304        _ => None,
305    };
306
307    #[cfg(feature = "perf-stats")]
308    {
309        PARSE_COUNT.fetch_add(1, Ordering::Relaxed);
310        PARSE_TIME_NS.fetch_add(start.elapsed().as_nanos() as usize, Ordering::Relaxed);
311    }
312
313    result
314}
315
316/// 解析 CreateEvent (极限优化)
317///
318/// 优化:
319/// - 使用 unsafe 消除所有边界检查
320/// - 零拷贝字符串解析
321/// - 内联所有调用
322#[inline(always)]
323fn parse_create_event_optimized(
324    data: &[u8],
325    signature: Signature,
326    slot: u64,
327    tx_index: u64,
328    block_time_us: Option<i64>,
329    grpc_recv_us: i64,
330) -> Option<DexEvent> {
331    unsafe {
332        let mut offset = 0;
333
334        // 读取字符串字段 (零拷贝)
335        let (name, name_len) = read_str_unchecked(data, offset)?;
336        offset += name_len;
337
338        let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
339        offset += symbol_len;
340
341        let (uri, uri_len) = read_str_unchecked(data, offset)?;
342        offset += uri_len;
343
344        // 快速边界检查
345        if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
346            return None;
347        }
348
349        // 读取 Pubkey 字段
350        let mint = read_pubkey_unchecked(data, offset);
351        offset += 32;
352
353        let bonding_curve = read_pubkey_unchecked(data, offset);
354        offset += 32;
355
356        let user = read_pubkey_unchecked(data, offset);
357        offset += 32;
358
359        let creator = read_pubkey_unchecked(data, offset);
360        offset += 32;
361
362        // 读取数值字段
363        let timestamp = read_i64_unchecked(data, offset);
364        offset += 8;
365
366        let virtual_token_reserves = read_u64_unchecked(data, offset);
367        offset += 8;
368
369        let virtual_sol_reserves = read_u64_unchecked(data, offset);
370        offset += 8;
371
372        let real_token_reserves = read_u64_unchecked(data, offset);
373        offset += 8;
374
375        let token_total_supply = read_u64_unchecked(data, offset);
376        offset += 8;
377
378        let token_program = if offset + 32 <= data.len() {
379            read_pubkey_unchecked(data, offset)
380        } else {
381            Pubkey::default()
382        };
383        offset += 32;
384
385        let is_mayhem_mode =
386            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
387        offset += 1;
388        let is_cashback_enabled =
389            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
390        offset += 1;
391        let quote_mint = normalize_pumpfun_quote_mint(if offset + 32 <= data.len() {
392            read_pubkey_unchecked(data, offset)
393        } else {
394            Pubkey::default()
395        });
396        offset += 32;
397        let virtual_quote_reserves =
398            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
399
400        let metadata = EventMetadata {
401            signature,
402            slot,
403            tx_index,
404            block_time_us: block_time_us.unwrap_or(0),
405            grpc_recv_us,
406            recent_blockhash: None,
407        };
408
409        // 将 &str 转换为 String (这是唯一的堆分配)
410        // 优化: 可以考虑使用 SmallString 或 Cow<'static, str> 进一步优化
411        Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
412            metadata,
413            name: name.to_string(),
414            symbol: symbol.to_string(),
415            uri: uri.to_string(),
416            mint,
417            bonding_curve,
418            user,
419            creator,
420            timestamp,
421            virtual_token_reserves,
422            virtual_sol_reserves,
423            real_token_reserves,
424            token_total_supply,
425            token_program,
426            is_mayhem_mode,
427            is_cashback_enabled,
428            quote_mint,
429            virtual_quote_reserves,
430            ..Default::default()
431        }))
432    }
433}
434
435/// 解析 TradeEvent (极限优化)
436///
437/// 根据 ix_name 返回不同的事件类型:
438/// - "buy" -> DexEvent::PumpFunBuy
439/// - "sell" -> DexEvent::PumpFunSell
440/// - "buy_exact_sol_in" -> DexEvent::PumpFunBuyExactSolIn
441/// - "buy_exact_quote_in" -> DexEvent::PumpFunBuy (exact quote args preserved on fields)
442/// - 其他/空 -> DexEvent::PumpFunTrade (兼容旧版本)
443#[inline(always)]
444fn parse_trade_event_optimized(
445    data: &[u8],
446    signature: Signature,
447    slot: u64,
448    tx_index: u64,
449    block_time_us: Option<i64>,
450    grpc_recv_us: i64,
451    is_created_buy: bool,
452) -> Option<DexEvent> {
453    unsafe {
454        // 快速边界检查
455        if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
456            return None;
457        }
458
459        let mut offset = 0;
460
461        let mint = read_pubkey_unchecked(data, offset);
462        offset += 32;
463
464        let sol_amount = read_u64_unchecked(data, offset);
465        offset += 8;
466
467        let token_amount = read_u64_unchecked(data, offset);
468        offset += 8;
469
470        let is_buy = read_bool_unchecked(data, offset);
471        offset += 1;
472
473        let user = read_pubkey_unchecked(data, offset);
474        offset += 32;
475
476        let timestamp = read_i64_unchecked(data, offset);
477        offset += 8;
478
479        let virtual_sol_reserves = read_u64_unchecked(data, offset);
480        offset += 8;
481
482        let virtual_token_reserves = read_u64_unchecked(data, offset);
483        offset += 8;
484
485        let real_sol_reserves = read_u64_unchecked(data, offset);
486        offset += 8;
487
488        let real_token_reserves = read_u64_unchecked(data, offset);
489        offset += 8;
490
491        let fee_recipient = read_pubkey_unchecked(data, offset);
492        offset += 32;
493
494        let fee_basis_points = read_u64_unchecked(data, offset);
495        offset += 8;
496
497        let fee = read_u64_unchecked(data, offset);
498        offset += 8;
499
500        let creator = read_pubkey_unchecked(data, offset);
501        offset += 32;
502
503        let creator_fee_basis_points = read_u64_unchecked(data, offset);
504        offset += 8;
505
506        let creator_fee = read_u64_unchecked(data, offset);
507        offset += 8;
508
509        // 可选字段
510        let track_volume =
511            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
512        offset += 1;
513
514        let total_unclaimed_tokens =
515            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
516        offset += 8;
517
518        let total_claimed_tokens =
519            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
520        offset += 8;
521
522        let current_sol_volume =
523            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
524        offset += 8;
525
526        let last_update_timestamp =
527            if offset + 8 <= data.len() { read_i64_unchecked(data, offset) } else { 0 };
528        offset += 8;
529
530        // ix_name: String (4-byte length prefix + content)
531        // Values: "buy" | "sell" | "buy_exact_sol_in" | "buy_exact_quote_in"
532        let ix_name = if offset + 4 <= data.len() {
533            if let Some((s, len)) = read_str_unchecked(data, offset) {
534                offset += len;
535                s.to_string()
536            } else {
537                String::new()
538            }
539        } else {
540            String::new()
541        };
542        let ix_kind = normalize_pumpfun_ix_name(&ix_name);
543
544        // mayhem_mode: bool (1 byte), cashback_fee_basis_points (8), cashback (8) - PUMP_CASHBACK_README
545        let mayhem_mode =
546            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
547        offset += 1;
548        let cashback_fee_basis_points =
549            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
550        offset += 8;
551        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
552        offset += 8;
553        let (
554            buyback_fee_basis_points,
555            buyback_fee,
556            shareholders,
557            quote_mint,
558            quote_amount,
559            virtual_quote_reserves,
560            real_quote_reserves,
561        ) = read_trade_event_extensions(data, &mut offset)?;
562
563        let metadata = EventMetadata {
564            signature,
565            slot,
566            tx_index,
567            block_time_us: block_time_us.unwrap_or(0),
568            grpc_recv_us,
569            recent_blockhash: None,
570        };
571
572        let trade_event = PumpFunTradeEvent {
573            metadata,
574            mint,
575            sol_amount,
576            token_amount,
577            is_buy,
578            is_created_buy,
579            user,
580            timestamp,
581            virtual_sol_reserves,
582            virtual_token_reserves,
583            real_sol_reserves,
584            real_token_reserves,
585            fee_recipient,
586            fee_basis_points,
587            fee,
588            creator,
589            creator_fee_basis_points,
590            creator_fee,
591            track_volume,
592            total_unclaimed_tokens,
593            total_claimed_tokens,
594            current_sol_volume,
595            last_update_timestamp,
596            ix_name: ix_name.clone(),
597            mayhem_mode,
598            cashback_fee_basis_points,
599            cashback,
600            buyback_fee_basis_points,
601            buyback_fee,
602            shareholders,
603            quote_mint,
604            quote_amount,
605            virtual_quote_reserves,
606            real_quote_reserves,
607            is_cashback_coin: cashback_fee_basis_points > 0,
608            amount: 0,
609            max_sol_cost: 0,
610            min_sol_output: 0,
611            spendable_sol_in: 0,
612            spendable_quote_in: 0,
613            min_tokens_out: 0,
614            global: Pubkey::default(),
615            bonding_curve: Pubkey::default(),
616            associated_bonding_curve: Pubkey::default(),
617            associated_user: Pubkey::default(),
618            system_program: Pubkey::default(),
619            creator_vault: Pubkey::default(),
620            event_authority: Pubkey::default(),
621            program: Pubkey::default(),
622            global_volume_accumulator: Pubkey::default(),
623            user_volume_accumulator: Pubkey::default(),
624            fee_config: Pubkey::default(),
625            fee_program: Pubkey::default(),
626            token_program: Pubkey::default(),
627            account: None,
628            ..Default::default()
629        };
630
631        // 根据 ix_name 返回不同的事件类型,支持用户过滤特定交易类型
632        match ix_kind {
633            "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
634            "sell" => Some(DexEvent::PumpFunSell(trade_event)),
635            "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
636            "buy_exact_quote_in" => Some(DexEvent::PumpFunBuy(trade_event)),
637            _ => Some(DexEvent::PumpFunTrade(trade_event)), // 兼容旧版本或未知类型
638        }
639    }
640}
641
642/// 解析 MigrateEvent (极限优化)
643#[inline(always)]
644fn parse_migrate_event_optimized(
645    data: &[u8],
646    signature: Signature,
647    slot: u64,
648    tx_index: u64,
649    block_time_us: Option<i64>,
650    grpc_recv_us: i64,
651) -> Option<DexEvent> {
652    unsafe {
653        // 快速边界检查
654        if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
655            return None;
656        }
657
658        let mut offset = 0;
659
660        let user = read_pubkey_unchecked(data, offset);
661        offset += 32;
662
663        let mint = read_pubkey_unchecked(data, offset);
664        offset += 32;
665
666        let mint_amount = read_u64_unchecked(data, offset);
667        offset += 8;
668
669        let sol_amount = read_u64_unchecked(data, offset);
670        offset += 8;
671
672        let pool_migration_fee = read_u64_unchecked(data, offset);
673        offset += 8;
674
675        let bonding_curve = read_pubkey_unchecked(data, offset);
676        offset += 32;
677
678        let timestamp = read_i64_unchecked(data, offset);
679        offset += 8;
680
681        let pool = read_pubkey_unchecked(data, offset);
682
683        let metadata = EventMetadata {
684            signature,
685            slot,
686            tx_index,
687            block_time_us: block_time_us.unwrap_or(0),
688            grpc_recv_us,
689            recent_blockhash: None,
690        };
691
692        Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
693            metadata,
694            user,
695            mint,
696            mint_amount,
697            sol_amount,
698            pool_migration_fee,
699            bonding_curve,
700            timestamp,
701            pool,
702        }))
703    }
704}
705
706#[inline(always)]
707fn parse_migrate_bonding_curve_creator_event_optimized(
708    data: &[u8],
709    signature: Signature,
710    slot: u64,
711    tx_index: u64,
712    block_time_us: Option<i64>,
713    grpc_recv_us: i64,
714) -> Option<DexEvent> {
715    let metadata = EventMetadata {
716        signature,
717        slot,
718        tx_index,
719        block_time_us: block_time_us.unwrap_or(0),
720        grpc_recv_us,
721        recent_blockhash: None,
722    };
723    parse_migrate_bonding_curve_creator_from_data(data, metadata)
724}
725
726#[inline(always)]
727fn parse_create_fee_sharing_config_event_optimized(
728    data: &[u8],
729    signature: Signature,
730    slot: u64,
731    tx_index: u64,
732    block_time_us: Option<i64>,
733    grpc_recv_us: i64,
734) -> Option<DexEvent> {
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    crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
744}
745
746// ============================================================================
747// 快速过滤 API (用于事件过滤场景)
748// ============================================================================
749
750/// 快速判断事件类型 (只解析 discriminator)
751///
752/// 性能: <50ns
753#[inline(always)]
754pub fn get_event_type_fast(log: &str) -> Option<u64> {
755    extract_discriminator_simd(log)
756}
757
758/// 检查是否为特定事件类型 (SIMD 优化)
759#[inline(always)]
760pub fn is_event_type(log: &str, discriminator: u64) -> bool {
761    extract_discriminator_simd(log) == Some(discriminator)
762}
763
764// ============================================================================
765// Public API for optimized parsing from pre-decoded data
766// These functions accept already-decoded data (without discriminator)
767// ============================================================================
768
769/// Parse PumpFun Trade event from pre-decoded data
770///
771/// `data` should be the decoded bytes AFTER the 8-byte discriminator
772///
773/// Returns different event types based on ix_name:
774/// - "buy" -> DexEvent::PumpFunBuy
775/// - "sell" -> DexEvent::PumpFunSell
776/// - "buy_exact_sol_in" -> DexEvent::PumpFunBuyExactSolIn
777/// - "buy_exact_quote_in" -> DexEvent::PumpFunBuy (exact quote args preserved on fields)
778/// - other/empty -> DexEvent::PumpFunTrade (backward compatible)
779#[inline(always)]
780pub fn parse_trade_from_data(
781    data: &[u8],
782    metadata: EventMetadata,
783    is_created_buy: bool,
784) -> Option<DexEvent> {
785    unsafe {
786        // 快速边界检查
787        if data.len() < 32 + 8 + 8 + 1 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 8 + 8 + 32 + 8 + 8 {
788            return None;
789        }
790
791        let mut offset = 0;
792
793        let mint = read_pubkey_unchecked(data, offset);
794        offset += 32;
795
796        let sol_amount = read_u64_unchecked(data, offset);
797        offset += 8;
798
799        let token_amount = read_u64_unchecked(data, offset);
800        offset += 8;
801
802        let is_buy = read_bool_unchecked(data, offset);
803        offset += 1;
804
805        let user = read_pubkey_unchecked(data, offset);
806        offset += 32;
807
808        let timestamp = read_i64_unchecked(data, offset);
809        offset += 8;
810
811        let virtual_sol_reserves = read_u64_unchecked(data, offset);
812        offset += 8;
813
814        let virtual_token_reserves = read_u64_unchecked(data, offset);
815        offset += 8;
816
817        let real_sol_reserves = read_u64_unchecked(data, offset);
818        offset += 8;
819
820        let real_token_reserves = read_u64_unchecked(data, offset);
821        offset += 8;
822
823        let fee_recipient = read_pubkey_unchecked(data, offset);
824        offset += 32;
825
826        let fee_basis_points = read_u64_unchecked(data, offset);
827        offset += 8;
828
829        let fee = read_u64_unchecked(data, offset);
830        offset += 8;
831
832        let creator = read_pubkey_unchecked(data, offset);
833        offset += 32;
834
835        let creator_fee_basis_points = read_u64_unchecked(data, offset);
836        offset += 8;
837
838        let creator_fee = read_u64_unchecked(data, offset);
839        offset += 8;
840
841        // 可选字段
842        let track_volume =
843            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
844        offset += 1;
845
846        let total_unclaimed_tokens =
847            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
848        offset += 8;
849
850        let total_claimed_tokens =
851            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
852        offset += 8;
853
854        let current_sol_volume =
855            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
856        offset += 8;
857
858        let last_update_timestamp =
859            if offset + 8 <= data.len() { read_i64_unchecked(data, offset) } else { 0 };
860        offset += 8;
861
862        let ix_name = if offset + 4 <= data.len() {
863            if let Some((s, len)) = read_str_unchecked(data, offset) {
864                offset += len;
865                s.to_string()
866            } else {
867                String::new()
868            }
869        } else {
870            String::new()
871        };
872        let ix_kind = normalize_pumpfun_ix_name(&ix_name);
873
874        // mayhem_mode (1), cashback_fee_basis_points (8), cashback (8) - PUMP_CASHBACK_README
875        let mayhem_mode =
876            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
877        offset += 1;
878        let cashback_fee_basis_points =
879            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
880        offset += 8;
881        let cashback = if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
882        offset += 8;
883        let (
884            buyback_fee_basis_points,
885            buyback_fee,
886            shareholders,
887            quote_mint,
888            quote_amount,
889            virtual_quote_reserves,
890            real_quote_reserves,
891        ) = read_trade_event_extensions(data, &mut offset)?;
892
893        let trade_event = PumpFunTradeEvent {
894            metadata,
895            mint,
896            sol_amount,
897            token_amount,
898            is_buy,
899            is_created_buy,
900            user,
901            timestamp,
902            virtual_sol_reserves,
903            virtual_token_reserves,
904            real_sol_reserves,
905            real_token_reserves,
906            fee_recipient,
907            fee_basis_points,
908            fee,
909            creator,
910            creator_fee_basis_points,
911            creator_fee,
912            track_volume,
913            total_unclaimed_tokens,
914            total_claimed_tokens,
915            current_sol_volume,
916            last_update_timestamp,
917            ix_name: ix_name.clone(),
918            mayhem_mode,
919            cashback_fee_basis_points,
920            cashback,
921            buyback_fee_basis_points,
922            buyback_fee,
923            shareholders,
924            quote_mint,
925            quote_amount,
926            virtual_quote_reserves,
927            real_quote_reserves,
928            is_cashback_coin: cashback_fee_basis_points > 0,
929            amount: 0,
930            max_sol_cost: 0,
931            min_sol_output: 0,
932            spendable_sol_in: 0,
933            spendable_quote_in: 0,
934            min_tokens_out: 0,
935            global: Pubkey::default(),
936            bonding_curve: Pubkey::default(),
937            associated_bonding_curve: Pubkey::default(),
938            associated_user: Pubkey::default(),
939            system_program: Pubkey::default(),
940            creator_vault: Pubkey::default(),
941            event_authority: Pubkey::default(),
942            program: Pubkey::default(),
943            global_volume_accumulator: Pubkey::default(),
944            user_volume_accumulator: Pubkey::default(),
945            fee_config: Pubkey::default(),
946            fee_program: Pubkey::default(),
947            token_program: Pubkey::default(),
948            account: None,
949            ..Default::default()
950        };
951
952        // 根据 ix_name 返回不同的事件类型
953        match ix_kind {
954            "buy" => Some(DexEvent::PumpFunBuy(trade_event)),
955            "sell" => Some(DexEvent::PumpFunSell(trade_event)),
956            "buy_exact_sol_in" => Some(DexEvent::PumpFunBuyExactSolIn(trade_event)),
957            "buy_exact_quote_in" => Some(DexEvent::PumpFunBuy(trade_event)),
958            _ => Some(DexEvent::PumpFunTrade(trade_event)),
959        }
960    }
961}
962
963/// Parse only PumpFun Buy events from pre-decoded data
964///
965/// Returns None if the event is not a buy event
966#[inline(always)]
967pub fn parse_buy_from_data(
968    data: &[u8],
969    metadata: EventMetadata,
970    is_created_buy: bool,
971) -> Option<DexEvent> {
972    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
973    match &event {
974        DexEvent::PumpFunBuy(_) => Some(event),
975        _ => None,
976    }
977}
978
979/// Parse only PumpFun Sell events from pre-decoded data
980///
981/// Returns None if the event is not a sell event
982#[inline(always)]
983pub fn parse_sell_from_data(
984    data: &[u8],
985    metadata: EventMetadata,
986    is_created_buy: bool,
987) -> Option<DexEvent> {
988    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
989    match &event {
990        DexEvent::PumpFunSell(_) => Some(event),
991        _ => None,
992    }
993}
994
995/// Parse only PumpFun BuyExactSolIn events from pre-decoded data
996///
997/// Returns None if the event is not a buy_exact_sol_in event
998#[inline(always)]
999pub fn parse_buy_exact_sol_in_from_data(
1000    data: &[u8],
1001    metadata: EventMetadata,
1002    is_created_buy: bool,
1003) -> Option<DexEvent> {
1004    let event = parse_trade_from_data(data, metadata, is_created_buy)?;
1005    match &event {
1006        DexEvent::PumpFunBuyExactSolIn(_) => Some(event),
1007        _ => None,
1008    }
1009}
1010
1011/// Parse PumpFun Create event from pre-decoded data
1012#[inline(always)]
1013pub fn parse_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1014    unsafe {
1015        let mut offset = 0;
1016
1017        let (name, name_len) = read_str_unchecked(data, offset)?;
1018        offset += name_len;
1019
1020        let (symbol, symbol_len) = read_str_unchecked(data, offset)?;
1021        offset += symbol_len;
1022
1023        let (uri, uri_len) = read_str_unchecked(data, offset)?;
1024        offset += uri_len;
1025
1026        if data.len() < offset + 32 + 32 + 32 + 32 + 8 + 8 + 8 + 8 + 8 + 32 + 1 {
1027            return None;
1028        }
1029
1030        let mint = read_pubkey_unchecked(data, offset);
1031        offset += 32;
1032
1033        let bonding_curve = read_pubkey_unchecked(data, offset);
1034        offset += 32;
1035
1036        let user = read_pubkey_unchecked(data, offset);
1037        offset += 32;
1038
1039        let creator = read_pubkey_unchecked(data, offset);
1040        offset += 32;
1041
1042        let timestamp = read_i64_unchecked(data, offset);
1043        offset += 8;
1044
1045        let virtual_token_reserves = read_u64_unchecked(data, offset);
1046        offset += 8;
1047
1048        let virtual_sol_reserves = read_u64_unchecked(data, offset);
1049        offset += 8;
1050
1051        let real_token_reserves = read_u64_unchecked(data, offset);
1052        offset += 8;
1053
1054        let token_total_supply = read_u64_unchecked(data, offset);
1055        offset += 8;
1056
1057        let token_program = if offset + 32 <= data.len() {
1058            read_pubkey_unchecked(data, offset)
1059        } else {
1060            Pubkey::default()
1061        };
1062        offset += 32;
1063
1064        let is_mayhem_mode =
1065            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
1066        offset += 1;
1067        let is_cashback_enabled =
1068            if offset < data.len() { read_bool_unchecked(data, offset) } else { false };
1069        offset += 1;
1070        let quote_mint = normalize_pumpfun_quote_mint(if offset + 32 <= data.len() {
1071            read_pubkey_unchecked(data, offset)
1072        } else {
1073            Pubkey::default()
1074        });
1075        offset += 32;
1076        let virtual_quote_reserves =
1077            if offset + 8 <= data.len() { read_u64_unchecked(data, offset) } else { 0 };
1078
1079        Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
1080            metadata,
1081            name: name.to_string(),
1082            symbol: symbol.to_string(),
1083            uri: uri.to_string(),
1084            mint,
1085            bonding_curve,
1086            user,
1087            creator,
1088            timestamp,
1089            virtual_token_reserves,
1090            virtual_sol_reserves,
1091            real_token_reserves,
1092            token_total_supply,
1093            token_program,
1094            is_mayhem_mode,
1095            is_cashback_enabled,
1096            quote_mint,
1097            virtual_quote_reserves,
1098            ..Default::default()
1099        }))
1100    }
1101}
1102
1103/// Parse PumpFun Migrate event from pre-decoded data
1104#[inline(always)]
1105pub fn parse_migrate_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1106    unsafe {
1107        if data.len() < 32 + 32 + 8 + 8 + 8 + 32 + 8 + 32 {
1108            return None;
1109        }
1110
1111        let mut offset = 0;
1112
1113        let user = read_pubkey_unchecked(data, offset);
1114        offset += 32;
1115
1116        let mint = read_pubkey_unchecked(data, offset);
1117        offset += 32;
1118
1119        let mint_amount = read_u64_unchecked(data, offset);
1120        offset += 8;
1121
1122        let sol_amount = read_u64_unchecked(data, offset);
1123        offset += 8;
1124
1125        let pool_migration_fee = read_u64_unchecked(data, offset);
1126        offset += 8;
1127
1128        let bonding_curve = read_pubkey_unchecked(data, offset);
1129        offset += 32;
1130
1131        let timestamp = read_i64_unchecked(data, offset);
1132        offset += 8;
1133
1134        let pool = read_pubkey_unchecked(data, offset);
1135
1136        Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
1137            metadata,
1138            user,
1139            mint,
1140            mint_amount,
1141            sol_amount,
1142            pool_migration_fee,
1143            bonding_curve,
1144            timestamp,
1145            pool,
1146        }))
1147    }
1148}
1149
1150/// `migrateBondingCurveCreatorEvent`:`data` 为去掉 8 字节 discriminator 之后的 Borsh 体。
1151#[inline(always)]
1152pub fn parse_migrate_bonding_curve_creator_from_data(
1153    data: &[u8],
1154    metadata: EventMetadata,
1155) -> Option<DexEvent> {
1156    unsafe {
1157        const NEED: usize = 8 + 32 * 5;
1158        if data.len() < NEED {
1159            return None;
1160        }
1161
1162        let mut offset = 0usize;
1163        let timestamp = read_i64_unchecked(data, offset);
1164        offset += 8;
1165        let mint = read_pubkey_unchecked(data, offset);
1166        offset += 32;
1167        let bonding_curve = read_pubkey_unchecked(data, offset);
1168        offset += 32;
1169        let sharing_config = read_pubkey_unchecked(data, offset);
1170        offset += 32;
1171        let old_creator = read_pubkey_unchecked(data, offset);
1172        offset += 32;
1173        let new_creator = read_pubkey_unchecked(data, offset);
1174
1175        Some(DexEvent::PumpFunMigrateBondingCurveCreator(PumpFunMigrateBondingCurveCreatorEvent {
1176            metadata,
1177            timestamp,
1178            mint,
1179            bonding_curve,
1180            sharing_config,
1181            old_creator,
1182            new_creator,
1183        }))
1184    }
1185}
1186
1187/// `createFeeSharingConfigEvent`:委托 [`pump_fees::parse_create_fee_sharing_config_from_data`](crate::logs::pump_fees)。
1188#[inline]
1189pub fn parse_create_fee_sharing_config_from_data(
1190    data: &[u8],
1191    metadata: EventMetadata,
1192) -> Option<DexEvent> {
1193    crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
1194}
1195
1196#[inline(always)]
1197fn read_i64_at(data: &[u8], o: &mut usize) -> Option<i64> {
1198    if data.len() < *o + 8 {
1199        return None;
1200    }
1201    let v = i64::from_le_bytes(data[*o..*o + 8].try_into().ok()?);
1202    *o += 8;
1203    Some(v)
1204}
1205
1206#[inline(always)]
1207fn read_u16_at(data: &[u8], o: &mut usize) -> Option<u16> {
1208    if data.len() < *o + 2 {
1209        return None;
1210    }
1211    let v = u16::from_le_bytes(data[*o..*o + 2].try_into().ok()?);
1212    *o += 2;
1213    Some(v)
1214}
1215
1216#[inline(always)]
1217fn read_u32_at(data: &[u8], o: &mut usize) -> Option<u32> {
1218    if data.len() < *o + 4 {
1219        return None;
1220    }
1221    let v = u32::from_le_bytes(data[*o..*o + 4].try_into().ok()?);
1222    *o += 4;
1223    Some(v)
1224}
1225
1226#[inline(always)]
1227fn read_pubkey_at(data: &[u8], o: &mut usize) -> Option<Pubkey> {
1228    if data.len() < *o + 32 {
1229        return None;
1230    }
1231    let pk = Pubkey::new_from_array(data[*o..*o + 32].try_into().ok()?);
1232    *o += 32;
1233    Some(pk)
1234}
1235
1236// ============================================================================
1237// 性能统计 API (可选)
1238// ============================================================================
1239
1240#[cfg(feature = "perf-stats")]
1241pub fn get_perf_stats() -> (usize, usize) {
1242    let count = PARSE_COUNT.load(Ordering::Relaxed);
1243    let total_ns = PARSE_TIME_NS.load(Ordering::Relaxed);
1244    (count, total_ns)
1245}
1246
1247#[cfg(feature = "perf-stats")]
1248pub fn reset_perf_stats() {
1249    PARSE_COUNT.store(0, Ordering::Relaxed);
1250    PARSE_TIME_NS.store(0, Ordering::Relaxed);
1251}
1252
1253#[cfg(test)]
1254mod tests {
1255    use super::*;
1256    use crate::core::events::{DexEvent, EventMetadata};
1257
1258    #[test]
1259    fn test_discriminator_simd() {
1260        // 测试 SIMD discriminator 提取
1261        let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
1262        let disc = extract_discriminator_simd(log);
1263        assert!(disc.is_some());
1264    }
1265
1266    #[test]
1267    fn test_parse_performance() {
1268        // 性能测试
1269        let log = "Program data: G3Kp5Dfe605nAAAAAAAAAAA=";
1270        let sig = Signature::default();
1271
1272        let start = std::time::Instant::now();
1273        for _ in 0..1000 {
1274            let _ = parse_log(log, sig, 0, 0, Some(0), 0, false);
1275        }
1276        let elapsed = start.elapsed();
1277
1278        println!("Average parse time: {} ns", elapsed.as_nanos() / 1000);
1279    }
1280
1281    #[test]
1282    fn migrate_bonding_curve_creator_roundtrip_from_data() {
1283        let ts: i64 = 1_777_920_719;
1284        let mint = Pubkey::new_unique();
1285        let bonding_curve = Pubkey::new_unique();
1286        let sharing_config = Pubkey::new_unique();
1287        let old_creator = Pubkey::new_unique();
1288        let new_creator = Pubkey::new_unique();
1289
1290        let mut buf = Vec::with_capacity(200);
1291        buf.extend_from_slice(&ts.to_le_bytes());
1292        buf.extend_from_slice(mint.as_ref());
1293        buf.extend_from_slice(bonding_curve.as_ref());
1294        buf.extend_from_slice(sharing_config.as_ref());
1295        buf.extend_from_slice(old_creator.as_ref());
1296        buf.extend_from_slice(new_creator.as_ref());
1297
1298        let metadata = EventMetadata {
1299            signature: Signature::default(),
1300            slot: 0,
1301            tx_index: 0,
1302            block_time_us: 0,
1303            grpc_recv_us: 0,
1304            recent_blockhash: None,
1305        };
1306
1307        let ev = parse_migrate_bonding_curve_creator_from_data(&buf, metadata).expect("parse");
1308        match ev {
1309            DexEvent::PumpFunMigrateBondingCurveCreator(e) => {
1310                assert_eq!(e.timestamp, ts);
1311                assert_eq!(e.mint, mint);
1312                assert_eq!(e.bonding_curve, bonding_curve);
1313                assert_eq!(e.sharing_config, sharing_config);
1314                assert_eq!(e.old_creator, old_creator);
1315                assert_eq!(e.new_creator, new_creator);
1316            }
1317            _ => panic!("wrong variant"),
1318        }
1319    }
1320}