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    PumpSwap,
29    RaydiumClmm,
30    RaydiumCpmm,
31    RaydiumAmm,
32    OrcaWhirlpool,
33    MeteoraAmm,
34    MeteoraDamm,
35    MeteoraDlmm,
36}
37
38// ============================================================================
39// Parser function wrappers - delegate to protocol-specific parsers
40// ============================================================================
41
42// PumpFun parsers
43#[inline(always)]
44fn parse_pumpfun_create(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
45    crate::logs::pump::parse_create_from_data(data, metadata)
46}
47
48#[inline(always)]
49fn parse_pumpfun_trade(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
50    crate::logs::pump::parse_trade_from_data(data, metadata, false)
51}
52
53#[inline(always)]
54fn parse_pumpfun_migrate(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
55    crate::logs::pump::parse_migrate_from_data(data, metadata)
56}
57
58// PumpSwap parsers
59#[inline(always)]
60fn parse_pumpswap_buy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
61    crate::logs::pump_amm::parse_buy_from_data(data, metadata)
62}
63
64#[inline(always)]
65fn parse_pumpswap_sell(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
66    crate::logs::pump_amm::parse_sell_from_data(data, metadata)
67}
68
69#[inline(always)]
70fn parse_pumpswap_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
71    crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
72}
73
74#[inline(always)]
75fn parse_pumpswap_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
76    crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
77}
78
79#[inline(always)]
80fn parse_pumpswap_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
81    crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
82}
83
84// Raydium CLMM parsers
85#[inline(always)]
86fn parse_raydium_clmm_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
87    crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)
88}
89
90#[inline(always)]
91fn parse_raydium_clmm_increase_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
92    crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
93}
94
95#[inline(always)]
96fn parse_raydium_clmm_decrease_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
97    crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
98}
99
100#[inline(always)]
101fn parse_raydium_clmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
102    crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
103}
104
105#[inline(always)]
106fn parse_raydium_clmm_collect_fee(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
107    crate::logs::raydium_clmm::parse_collect_fee_from_data(data, metadata)
108}
109
110// Raydium CPMM parsers
111#[inline(always)]
112fn parse_raydium_cpmm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
113    crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
114}
115
116#[inline(always)]
117fn parse_raydium_cpmm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
118    crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
119}
120
121#[inline(always)]
122fn parse_raydium_cpmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
123    crate::logs::raydium_cpmm::parse_create_pool_from_data(data, metadata)
124}
125
126#[inline(always)]
127fn parse_raydium_cpmm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
128    crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
129}
130
131#[inline(always)]
132fn parse_raydium_cpmm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
133    crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
134}
135
136// Raydium AMM V4 parsers
137#[inline(always)]
138fn parse_raydium_amm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
139    crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
140}
141
142#[inline(always)]
143fn parse_raydium_amm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
144    crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
145}
146
147#[inline(always)]
148fn parse_raydium_amm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
149    crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
150}
151
152#[inline(always)]
153fn parse_raydium_amm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
154    crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
155}
156
157#[inline(always)]
158fn parse_raydium_amm_initialize2(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
159    crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
160}
161
162// Orca Whirlpool parsers
163#[inline(always)]
164fn parse_orca_traded(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
165    crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
166}
167
168#[inline(always)]
169fn parse_orca_liquidity_increased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
170    crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
171}
172
173#[inline(always)]
174fn parse_orca_liquidity_decreased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
175    crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
176}
177
178#[inline(always)]
179fn parse_orca_pool_initialized(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
180    crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
181}
182
183// Meteora AMM parsers
184#[inline(always)]
185fn parse_meteora_amm_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
186    crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
187}
188
189#[inline(always)]
190fn parse_meteora_amm_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
191    crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
192}
193
194#[inline(always)]
195fn parse_meteora_amm_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
196    crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
197}
198
199#[inline(always)]
200fn parse_meteora_amm_bootstrap_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
201    crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
202}
203
204#[inline(always)]
205fn parse_meteora_amm_pool_created(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
206    crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
207}
208
209// ============================================================================
210// Const lookup table - Sorted by discriminator for binary search
211// ============================================================================
212
213macro_rules! disc_entry {
214    ($disc:expr, $name:expr, $parser:expr, $protocol:expr) => {
215        DiscriminatorInfo {
216            discriminator: $disc,
217            parser: $parser,
218            protocol: $protocol,
219            name: $name,
220        }
221    };
222}
223
224/// Compile-time constant array: discriminator -> parser info
225/// MUST be kept sorted by discriminator for binary search!
226///
227/// Expected latency: 3-8ns (binary search on 31 entries = max 5 comparisons)
228pub const DISCRIMINATOR_LUT: &[DiscriminatorInfo] = &[
229    // Raydium AMM V4 events (sorted first - smallest discriminators)
230    disc_entry!(
231        0x0100000000000000,
232        "Raydium AMM Initialize2",
233        parse_raydium_amm_initialize2,
234        Protocol::RaydiumAmm
235    ),
236    disc_entry!(
237        0x0300000000000000,
238        "Raydium AMM Deposit",
239        parse_raydium_amm_deposit,
240        Protocol::RaydiumAmm
241    ),
242    disc_entry!(
243        0x0400000000000000,
244        "Raydium AMM Withdraw",
245        parse_raydium_amm_withdraw,
246        Protocol::RaydiumAmm
247    ),
248    disc_entry!(
249        0x0900000000000000,
250        "Raydium AMM Swap Base In",
251        parse_raydium_amm_swap_base_in,
252        Protocol::RaydiumAmm
253    ),
254    disc_entry!(
255        0x0B00000000000000,
256        "Raydium AMM Swap Base Out",
257        parse_raydium_amm_swap_base_out,
258        Protocol::RaydiumAmm
259    ),
260    // Raydium CLMM events
261    disc_entry!(
262        0x012C5B686FD026A0,
263        "Raydium CLMM Decrease Liquidity",
264        parse_raydium_clmm_decrease_liquidity,
265        Protocol::RaydiumClmm
266    ),
267    disc_entry!(
268        0x0AB0EE45DF591D85,
269        "Raydium CLMM Increase Liquidity",
270        parse_raydium_clmm_increase_liquidity,
271        Protocol::RaydiumClmm
272    ),
273    // Raydium CPMM events
274    disc_entry!(
275        0x22A16D949C4612B7,
276        "Raydium CPMM Withdraw",
277        parse_raydium_cpmm_withdraw,
278        Protocol::RaydiumCpmm
279    ),
280    // PumpSwap events
281    disc_entry!(0x2ADC03A50A372F3E, "PumpSwap Sell", parse_pumpswap_sell, Protocol::PumpSwap),
282    // Meteora AMM events
283    disc_entry!(
284        0x3A981F67E861F474,
285        "Meteora AMM Remove Liquidity",
286        parse_meteora_amm_remove_liquidity,
287        Protocol::MeteoraAmm
288    ),
289    disc_entry!(
290        0x529DDC6858292CCA,
291        "Meteora AMM Pool Created",
292        parse_meteora_amm_pool_created,
293        Protocol::MeteoraAmm
294    ),
295    // PumpSwap and PumpFun events
296    disc_entry!(
297        0x74A776A0D20C31B1,
298        "PumpSwap Create Pool",
299        parse_pumpswap_create_pool,
300        Protocol::PumpSwap
301    ),
302    disc_entry!(0x7663EBDE4DA91B1B, "PumpFun Create", parse_pumpfun_create, Protocol::PumpFun),
303    disc_entry!(0x7777F52C1F52F467, "PumpSwap Buy", parse_pumpswap_buy, Protocol::PumpSwap),
304    disc_entry!(
305        0x77AB68BB63CF98A4,
306        "Raydium CLMM Collect Fee",
307        parse_raydium_clmm_collect_fee,
308        Protocol::RaydiumClmm
309    ),
310    disc_entry!(
311        0x906B8E1F533DF878,
312        "PumpSwap Add Liquidity",
313        parse_pumpswap_add_liquidity,
314        Protocol::PumpSwap
315    ),
316    disc_entry!(0x94EA945DB95DE9BD, "PumpFun Migrate", parse_pumpfun_migrate, Protocol::PumpFun),
317    disc_entry!(
318        0x96A02B93AF49CAE1,
319        "Orca Whirlpool Traded",
320        parse_orca_traded,
321        Protocol::OrcaWhirlpool
322    ),
323    disc_entry!(
324        0xA19BFE6690071E1E,
325        "Orca Whirlpool Liquidity Increased",
326        parse_orca_liquidity_increased,
327        Protocol::OrcaWhirlpool
328    ),
329    disc_entry!(
330        0xABB5CA7047241A6,
331        "Orca Whirlpool Liquidity Decreased",
332        parse_orca_liquidity_decreased,
333        Protocol::OrcaWhirlpool
334    ),
335    disc_entry!(
336        0xADB44AA35662D937,
337        "Raydium CPMM Swap Base Out",
338        parse_raydium_cpmm_swap_base_out,
339        Protocol::RaydiumCpmm
340    ),
341    disc_entry!(
342        0xB6F2E15289C623F2,
343        "Raydium CPMM Deposit",
344        parse_raydium_cpmm_deposit,
345        Protocol::RaydiumCpmm
346    ),
347    disc_entry!(
348        0xBA3D34E35A7D5E1F,
349        "Meteora AMM Add Liquidity",
350        parse_meteora_amm_add_liquidity,
351        Protocol::MeteoraAmm
352    ),
353    disc_entry!(
354        0xBC4068CF8ED192E9,
355        "Raydium CLMM Create Pool",
356        parse_raydium_clmm_create_pool,
357        Protocol::RaydiumClmm
358    ),
359    disc_entry!(
360        0xC0472CA01A850916,
361        "PumpSwap Remove Liquidity",
362        parse_pumpswap_remove_liquidity,
363        Protocol::PumpSwap
364    ),
365    disc_entry!(
366        0xC40AD0CDBE6CE351,
367        "Meteora AMM Swap",
368        parse_meteora_amm_swap,
369        Protocol::MeteoraAmm
370    ),
371    disc_entry!(
372        0xC887759EE19EC6F8,
373        "Raydium CLMM Swap",
374        parse_raydium_clmm_swap,
375        Protocol::RaydiumClmm
376    ),
377    disc_entry!(
378        0xDE331EC4DA5ABE8F,
379        "Raydium CPMM Swap Base In",
380        parse_raydium_cpmm_swap_base_in,
381        Protocol::RaydiumCpmm
382    ),
383    disc_entry!(
384        0xE5FEC60C57AD7664,
385        "Orca Whirlpool Initialize",
386        parse_orca_pool_initialized,
387        Protocol::OrcaWhirlpool
388    ),
389    disc_entry!(0xEE61E64ED37FDBBD, "PumpFun Trade", parse_pumpfun_trade, Protocol::PumpFun),
390    disc_entry!(
391        0xF70E375C88267F79,
392        "Meteora AMM Bootstrap Liquidity",
393        parse_meteora_amm_bootstrap_liquidity,
394        Protocol::MeteoraAmm
395    ),
396];
397
398/// Fast lookup by discriminator - O(log n) binary search
399///
400/// With 31 entries, this requires at most 5 comparisons
401#[inline(always)]
402pub fn lookup_discriminator(discriminator: u64) -> Option<&'static DiscriminatorInfo> {
403    DISCRIMINATOR_LUT
404        .binary_search_by_key(&discriminator, |info| info.discriminator)
405        .ok()
406        .map(|idx| &DISCRIMINATOR_LUT[idx])
407}
408
409/// Get event name from discriminator
410#[inline(always)]
411pub fn discriminator_to_name(discriminator: u64) -> Option<&'static str> {
412    lookup_discriminator(discriminator).map(|info| info.name)
413}
414
415/// Get protocol from discriminator
416#[inline(always)]
417pub fn discriminator_to_protocol(discriminator: u64) -> Option<Protocol> {
418    lookup_discriminator(discriminator).map(|info| info.protocol)
419}
420
421/// Parse event using discriminator lookup
422#[inline(always)]
423pub fn parse_with_discriminator(
424    discriminator: u64,
425    data: &[u8],
426    metadata: EventMetadata,
427) -> Option<DexEvent> {
428    let info = lookup_discriminator(discriminator)?;
429    (info.parser)(data, metadata)
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn test_lut_is_sorted() {
438        // Verify the LUT is sorted (required for binary search)
439        for i in 1..DISCRIMINATOR_LUT.len() {
440            assert!(
441                DISCRIMINATOR_LUT[i - 1].discriminator < DISCRIMINATOR_LUT[i].discriminator,
442                "LUT not sorted at index {}: {:x} >= {:x}",
443                i,
444                DISCRIMINATOR_LUT[i - 1].discriminator,
445                DISCRIMINATOR_LUT[i].discriminator
446            );
447        }
448    }
449
450    #[test]
451    fn test_discriminator_lookup() {
452        // PumpFun Create
453        let disc = 0x7663EBDE4DA91B1B;
454        let info = lookup_discriminator(disc).unwrap();
455        assert_eq!(info.name, "PumpFun Create");
456        assert_eq!(info.protocol, Protocol::PumpFun);
457
458        // Raydium CLMM Swap
459        let disc = 0xC887759EE19EC6F8;
460        let info = lookup_discriminator(disc).unwrap();
461        assert_eq!(info.name, "Raydium CLMM Swap");
462        assert_eq!(info.protocol, Protocol::RaydiumClmm);
463
464        // Unknown discriminator
465        let disc = 0xFFFFFFFFFFFFFFFF;
466        assert!(lookup_discriminator(disc).is_none());
467    }
468
469    #[test]
470    fn test_event_name_lookup() {
471        // PumpSwap Buy
472        let disc = 0x7777F52C1F52F467;
473        assert_eq!(discriminator_to_name(disc), Some("PumpSwap Buy"));
474
475        // Raydium AMM Swap Base In
476        let disc = 0x0900000000000000;
477        assert_eq!(discriminator_to_name(disc), Some("Raydium AMM Swap Base In"));
478    }
479
480    #[test]
481    fn test_protocol_lookup() {
482        assert_eq!(discriminator_to_protocol(0x7663EBDE4DA91B1B), Some(Protocol::PumpFun));
483        assert_eq!(discriminator_to_protocol(0xC887759EE19EC6F8), Some(Protocol::RaydiumClmm));
484        assert_eq!(discriminator_to_protocol(0x96A02B93AF49CAE1), Some(Protocol::OrcaWhirlpool));
485    }
486}