Skip to main content

sol_parser_sdk/logs/
discriminator_lut.rs

1//! Discriminator Lookup Table (LUT) - Compile-time constant array
2//!
3//! Zero-latency optimization: Use const array with binary search for O(log n) discriminator -> event type mapping
4//! Expected latency reduction: 1-10ns (binary search on sorted array, better cache locality than match)
5
6use crate::core::events::{DexEvent, EventMetadata};
7use crate::grpc::types::EventType;
8
9/// Discriminator type alias for clarity
10pub type Discriminator = u64;
11
12/// Parser function type - takes decoded data and metadata, returns parsed event
13pub type ParserFn = fn(&[u8], EventMetadata) -> Option<DexEvent>;
14
15/// Event metadata for discriminator lookup
16#[derive(Debug, Clone, Copy)]
17pub struct DiscriminatorInfo {
18    pub discriminator: u64,
19    pub parser: ParserFn,
20    pub protocol: Protocol,
21    pub name: &'static str, // Human-readable name for debugging
22}
23
24/// Protocol enum for quick protocol identification
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Protocol {
27    PumpFun,
28    PumpFees,
29    PumpSwap,
30    RaydiumClmm,
31    RaydiumCpmm,
32    RaydiumAmm,
33    OrcaWhirlpool,
34    MeteoraAmm,
35    MeteoraDamm,
36    MeteoraDlmm,
37}
38
39// ============================================================================
40// Parser function wrappers - delegate to protocol-specific parsers
41// ============================================================================
42
43// PumpFun parsers
44#[inline(always)]
45fn parse_pumpfun_create(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
46    crate::logs::pump::parse_create_from_data(data, metadata)
47}
48
49#[inline(always)]
50fn parse_pumpfun_trade(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
51    crate::logs::pump::parse_trade_from_data(data, metadata, false)
52}
53
54#[inline(always)]
55fn parse_pumpfun_migrate(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
56    crate::logs::pump::parse_migrate_from_data(data, metadata)
57}
58
59#[inline(always)]
60fn parse_pumpfun_migrate_bonding_curve_creator(
61    data: &[u8],
62    metadata: EventMetadata,
63) -> Option<DexEvent> {
64    crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(data, metadata)
65}
66
67#[inline(always)]
68fn parse_pumpfees_initialize_fee_config(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
69    crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
70}
71
72#[inline(always)]
73fn parse_pumpfees_reset(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
74    crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
75}
76
77#[inline(always)]
78fn parse_pumpfees_revoke(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
79    crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(data, metadata)
80}
81
82#[inline(always)]
83fn parse_pumpfees_transfer(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
84    crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(data, metadata)
85}
86
87#[inline(always)]
88fn parse_pumpfees_update_admin(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
89    crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
90}
91
92#[inline(always)]
93fn parse_pumpfees_update_fee_config(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
94    crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
95}
96
97#[inline(always)]
98fn parse_pumpfees_update_fee_shares(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
99    crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
100}
101
102#[inline(always)]
103fn parse_pumpfees_upsert_fee_tiers(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
104    crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
105}
106
107#[inline(always)]
108fn parse_pumpfun_create_fee_sharing_config(
109    data: &[u8],
110    metadata: EventMetadata,
111) -> Option<DexEvent> {
112    crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
113}
114
115// PumpSwap parsers
116#[inline(always)]
117fn parse_pumpswap_buy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
118    crate::logs::pump_amm::parse_buy_from_data(data, metadata)
119}
120
121#[inline(always)]
122fn parse_pumpswap_sell(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
123    crate::logs::pump_amm::parse_sell_from_data(data, metadata)
124}
125
126#[inline(always)]
127fn parse_pumpswap_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
128    crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
129}
130
131#[inline(always)]
132fn parse_pumpswap_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
133    crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
134}
135
136#[inline(always)]
137fn parse_pumpswap_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
138    crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
139}
140
141// Raydium CLMM parsers
142#[inline(always)]
143fn parse_raydium_clmm_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
144    crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)
145}
146
147#[inline(always)]
148fn parse_raydium_clmm_increase_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
149    crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
150}
151
152#[inline(always)]
153fn parse_raydium_clmm_decrease_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
154    crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
155}
156
157#[inline(always)]
158fn parse_raydium_clmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
159    crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
160}
161
162#[inline(always)]
163fn parse_raydium_clmm_collect_fee(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
164    crate::logs::raydium_clmm::parse_collect_fee_from_data(data, metadata)
165}
166
167// Raydium CPMM parsers
168#[inline(always)]
169fn parse_raydium_cpmm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
170    crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
171}
172
173#[inline(always)]
174fn parse_raydium_cpmm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
175    crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
176}
177
178#[inline(always)]
179fn parse_raydium_cpmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
180    crate::logs::raydium_cpmm::parse_create_pool_from_data(data, metadata)
181}
182
183#[inline(always)]
184fn parse_raydium_cpmm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
185    crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
186}
187
188#[inline(always)]
189fn parse_raydium_cpmm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
190    crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
191}
192
193// Raydium AMM V4 parsers
194#[inline(always)]
195fn parse_raydium_amm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
196    crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
197}
198
199#[inline(always)]
200fn parse_raydium_amm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
201    crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
202}
203
204#[inline(always)]
205fn parse_raydium_amm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
206    crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
207}
208
209#[inline(always)]
210fn parse_raydium_amm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
211    crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
212}
213
214#[inline(always)]
215fn parse_raydium_amm_initialize2(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
216    crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
217}
218
219// Orca Whirlpool parsers
220#[inline(always)]
221fn parse_orca_traded(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
222    crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
223}
224
225#[inline(always)]
226fn parse_orca_liquidity_increased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
227    crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
228}
229
230#[inline(always)]
231fn parse_orca_liquidity_decreased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
232    crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
233}
234
235#[inline(always)]
236fn parse_orca_pool_initialized(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
237    crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
238}
239
240// Meteora AMM parsers
241#[inline(always)]
242fn parse_meteora_amm_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
243    crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
244}
245
246#[inline(always)]
247fn parse_meteora_amm_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
248    crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
249}
250
251#[inline(always)]
252fn parse_meteora_amm_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
253    crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
254}
255
256#[inline(always)]
257fn parse_meteora_amm_bootstrap_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
258    crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
259}
260
261#[inline(always)]
262fn parse_meteora_amm_pool_created(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
263    crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
264}
265
266// ============================================================================
267// Const lookup table - Sorted by discriminator for binary search
268// ============================================================================
269
270macro_rules! disc_entry {
271    ($disc:expr, $name:expr, $parser:expr, $protocol:expr) => {
272        DiscriminatorInfo {
273            discriminator: $disc,
274            parser: $parser,
275            protocol: $protocol,
276            name: $name,
277        }
278    };
279}
280
281/// Compile-time constant array: discriminator -> parser info
282/// MUST be kept sorted by discriminator for binary search!
283///
284/// Expected latency: 3–8 ns (binary search on 33 discriminators ⇒ at most 6 comparisons)
285pub const DISCRIMINATOR_LUT: &[DiscriminatorInfo] = &[
286    // 按 discriminator 数值升序(binary_search 要求)
287    disc_entry!(
288        0x0100000000000000,
289        "Raydium AMM Initialize2",
290        parse_raydium_amm_initialize2,
291        Protocol::RaydiumAmm
292    ),
293    disc_entry!(
294        0x012C5B686FD026A0,
295        "Raydium CLMM Decrease Liquidity",
296        parse_raydium_clmm_decrease_liquidity,
297        Protocol::RaydiumClmm
298    ),
299    disc_entry!(
300        0x0300000000000000,
301        "Raydium AMM Deposit",
302        parse_raydium_amm_deposit,
303        Protocol::RaydiumAmm
304    ),
305    disc_entry!(
306        0x03F36CD5DC68A79B_u64,
307        "PumpFun Migrate Bonding Curve Creator",
308        parse_pumpfun_migrate_bonding_curve_creator,
309        Protocol::PumpFun
310    ),
311    disc_entry!(
312        0x0400000000000000,
313        "Raydium AMM Withdraw",
314        parse_raydium_amm_withdraw,
315        Protocol::RaydiumAmm
316    ),
317    disc_entry!(
318        0x0900000000000000,
319        "Raydium AMM Swap Base In",
320        parse_raydium_amm_swap_base_in,
321        Protocol::RaydiumAmm
322    ),
323    disc_entry!(
324        0x0AB0EE45DF591D85,
325        "Raydium CLMM Increase Liquidity",
326        parse_raydium_clmm_increase_liquidity,
327        Protocol::RaydiumClmm
328    ),
329    disc_entry!(
330        0xABB5CA7047241A6,
331        "Orca Whirlpool Liquidity Decreased",
332        parse_orca_liquidity_decreased,
333        Protocol::OrcaWhirlpool
334    ),
335    disc_entry!(
336        0x0B00000000000000,
337        "Raydium AMM Swap Base Out",
338        parse_raydium_amm_swap_base_out,
339        Protocol::RaydiumAmm
340    ),
341    // Raydium CPMM events
342    disc_entry!(
343        0x22A16D949C4612B7,
344        "Raydium CPMM Withdraw",
345        parse_raydium_cpmm_withdraw,
346        Protocol::RaydiumCpmm
347    ),
348    // PumpSwap events
349    disc_entry!(0x2ADC03A50A372F3E, "PumpSwap Sell", parse_pumpswap_sell, Protocol::PumpSwap),
350    // Meteora AMM events
351    disc_entry!(
352        0x3A981F67E861F474,
353        "Meteora AMM Remove Liquidity",
354        parse_meteora_amm_remove_liquidity,
355        Protocol::MeteoraAmm
356    ),
357    disc_entry!(
358        0x3E99BE0E3C651772_u64,
359        "Pump Fees Revoke Fee Sharing Authority",
360        parse_pumpfees_revoke,
361        Protocol::PumpFees
362    ),
363    disc_entry!(
364        0x529DDC6858292CCA,
365        "Meteora AMM Pool Created",
366        parse_meteora_amm_pool_created,
367        Protocol::MeteoraAmm
368    ),
369    disc_entry!(
370        0x58FB74B8C8AA6985_u64,
371        "Pump Fees Create Fee Sharing Config",
372        parse_pumpfun_create_fee_sharing_config,
373        Protocol::PumpFees
374    ),
375    // PumpSwap and PumpFun events
376    disc_entry!(
377        0x74A776A0D20C31B1,
378        "PumpSwap Create Pool",
379        parse_pumpswap_create_pool,
380        Protocol::PumpSwap
381    ),
382    disc_entry!(0x7663EBDE4DA91B1B, "PumpFun Create", parse_pumpfun_create, Protocol::PumpFun),
383    disc_entry!(0x7777F52C1F52F467, "PumpSwap Buy", parse_pumpswap_buy, Protocol::PumpSwap),
384    disc_entry!(
385        0x77AB68BB63CF98A4,
386        "Raydium CLMM Collect Fee",
387        parse_raydium_clmm_collect_fee,
388        Protocol::RaydiumClmm
389    ),
390    disc_entry!(
391        0x7EE2380AE6F48A59_u64,
392        "Pump Fees Initialize Fee Config",
393        parse_pumpfees_initialize_fee_config,
394        Protocol::PumpFees
395    ),
396    disc_entry!(
397        0x906B8E1F533DF878,
398        "PumpSwap Add Liquidity",
399        parse_pumpswap_add_liquidity,
400        Protocol::PumpSwap
401    ),
402    disc_entry!(0x94EA945DB95DE9BD, "PumpFun Migrate", parse_pumpfun_migrate, Protocol::PumpFun),
403    disc_entry!(
404        0x96A02B93AF49CAE1,
405        "Orca Whirlpool Traded",
406        parse_orca_traded,
407        Protocol::OrcaWhirlpool
408    ),
409    disc_entry!(
410        0xA19BFE6690071E1E,
411        "Orca Whirlpool Liquidity Increased",
412        parse_orca_liquidity_increased,
413        Protocol::OrcaWhirlpool
414    ),
415    disc_entry!(
416        0xADB44AA35662D937,
417        "Raydium CPMM Swap Base Out",
418        parse_raydium_cpmm_swap_base_out,
419        Protocol::RaydiumCpmm
420    ),
421    disc_entry!(
422        0xB6F2E15289C623F2,
423        "Raydium CPMM Deposit",
424        parse_raydium_cpmm_deposit,
425        Protocol::RaydiumCpmm
426    ),
427    disc_entry!(
428        0xBA3D34E35A7D5E1F,
429        "Meteora AMM Add Liquidity",
430        parse_meteora_amm_add_liquidity,
431        Protocol::MeteoraAmm
432    ),
433    disc_entry!(
434        0xBC4068CF8ED192E9,
435        "Raydium CLMM Create Pool",
436        parse_raydium_clmm_create_pool,
437        Protocol::RaydiumClmm
438    ),
439    disc_entry!(
440        0xC0472CA01A850916,
441        "PumpSwap Remove Liquidity",
442        parse_pumpswap_remove_liquidity,
443        Protocol::PumpSwap
444    ),
445    disc_entry!(
446        0xC40AD0CDBE6CE351,
447        "Meteora AMM Swap",
448        parse_meteora_amm_swap,
449        Protocol::MeteoraAmm
450    ),
451    disc_entry!(
452        0xC887759EE19EC6F8,
453        "Raydium CLMM Swap",
454        parse_raydium_clmm_swap,
455        Protocol::RaydiumClmm
456    ),
457    disc_entry!(
458        0xCBE1E45BB8C4BA15_u64,
459        "Pump Fees Update Fee Shares",
460        parse_pumpfees_update_fee_shares,
461        Protocol::PumpFees
462    ),
463    disc_entry!(
464        0xCC21BA7ABBA959AB_u64,
465        "Pump Fees Upsert Fee Tiers",
466        parse_pumpfees_upsert_fee_tiers,
467        Protocol::PumpFees
468    ),
469    disc_entry!(
470        0xD0BCF43E2341175A_u64,
471        "Pump Fees Update Fee Config",
472        parse_pumpfees_update_fee_config,
473        Protocol::PumpFees
474    ),
475    disc_entry!(
476        0xDE331EC4DA5ABE8F,
477        "Raydium CPMM Swap Base In",
478        parse_raydium_cpmm_swap_base_in,
479        Protocol::RaydiumCpmm
480    ),
481    disc_entry!(
482        0xE5FEC60C57AD7664,
483        "Orca Whirlpool Initialize",
484        parse_orca_pool_initialized,
485        Protocol::OrcaWhirlpool
486    ),
487    disc_entry!(
488        0xEA423FF657AB98E1_u64,
489        "Pump Fees Update Admin",
490        parse_pumpfees_update_admin,
491        Protocol::PumpFees
492    ),
493    disc_entry!(
494        0xEC08B84DF5C68F7C_u64,
495        "Pump Fees Transfer Fee Sharing Authority",
496        parse_pumpfees_transfer,
497        Protocol::PumpFees
498    ),
499    disc_entry!(0xEE61E64ED37FDBBD, "PumpFun Trade", parse_pumpfun_trade, Protocol::PumpFun),
500    disc_entry!(
501        0xF3D63778E297CCCB_u64,
502        "Pump Fees Reset Fee Sharing Config",
503        parse_pumpfees_reset,
504        Protocol::PumpFees
505    ),
506    disc_entry!(
507        0xF70E375C88267F79,
508        "Meteora AMM Bootstrap Liquidity",
509        parse_meteora_amm_bootstrap_liquidity,
510        Protocol::MeteoraAmm
511    ),
512];
513
514/// Fast lookup by discriminator - O(log n) binary search
515///
516/// With 33 entries, this requires at most 6 comparisons
517#[inline(always)]
518pub fn lookup_discriminator(discriminator: u64) -> Option<&'static DiscriminatorInfo> {
519    DISCRIMINATOR_LUT
520        .binary_search_by_key(&discriminator, |info| info.discriminator)
521        .ok()
522        .map(|idx| &DISCRIMINATOR_LUT[idx])
523}
524
525/// Get event name from discriminator
526#[inline(always)]
527pub fn discriminator_to_name(discriminator: u64) -> Option<&'static str> {
528    lookup_discriminator(discriminator).map(|info| info.name)
529}
530
531/// Get protocol from discriminator
532#[inline(always)]
533pub fn discriminator_to_protocol(discriminator: u64) -> Option<Protocol> {
534    lookup_discriminator(discriminator).map(|info| info.protocol)
535}
536
537/// Parse event using discriminator lookup
538#[inline(always)]
539pub fn parse_with_discriminator(
540    discriminator: u64,
541    data: &[u8],
542    metadata: EventMetadata,
543) -> Option<DexEvent> {
544    let info = lookup_discriminator(discriminator)?;
545    (info.parser)(data, metadata)
546}
547
548#[cfg(test)]
549mod tests {
550    use super::*;
551
552    #[test]
553    fn test_lut_is_sorted() {
554        // Verify the LUT is sorted (required for binary search)
555        for i in 1..DISCRIMINATOR_LUT.len() {
556            assert!(
557                DISCRIMINATOR_LUT[i - 1].discriminator < DISCRIMINATOR_LUT[i].discriminator,
558                "LUT not sorted at index {}: {:x} >= {:x}",
559                i,
560                DISCRIMINATOR_LUT[i - 1].discriminator,
561                DISCRIMINATOR_LUT[i].discriminator
562            );
563        }
564    }
565
566    #[test]
567    fn test_discriminator_lookup() {
568        // PumpFun Create
569        let disc = 0x7663EBDE4DA91B1B;
570        let info = lookup_discriminator(disc).unwrap();
571        assert_eq!(info.name, "PumpFun Create");
572        assert_eq!(info.protocol, Protocol::PumpFun);
573
574        // Raydium CLMM Swap
575        let disc = 0xC887759EE19EC6F8;
576        let info = lookup_discriminator(disc).unwrap();
577        assert_eq!(info.name, "Raydium CLMM Swap");
578        assert_eq!(info.protocol, Protocol::RaydiumClmm);
579
580        // Unknown discriminator
581        let disc = 0xFFFFFFFFFFFFFFFF;
582        assert!(lookup_discriminator(disc).is_none());
583    }
584
585    #[test]
586    fn test_event_name_lookup() {
587        // PumpSwap Buy
588        let disc = 0x7777F52C1F52F467;
589        assert_eq!(discriminator_to_name(disc), Some("PumpSwap Buy"));
590
591        // Raydium AMM Swap Base In
592        let disc = 0x0900000000000000;
593        assert_eq!(discriminator_to_name(disc), Some("Raydium AMM Swap Base In"));
594    }
595
596    #[test]
597    fn test_protocol_lookup() {
598        assert_eq!(discriminator_to_protocol(0x7663EBDE4DA91B1B), Some(Protocol::PumpFun));
599        assert_eq!(discriminator_to_protocol(0xC887759EE19EC6F8), Some(Protocol::RaydiumClmm));
600        assert_eq!(discriminator_to_protocol(0x96A02B93AF49CAE1), Some(Protocol::OrcaWhirlpool));
601    }
602}