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_liquidity_change(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
159    crate::logs::raydium_clmm::parse_liquidity_change_from_data(data, metadata)
160}
161
162#[inline(always)]
163fn parse_raydium_clmm_config_change(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
164    crate::logs::raydium_clmm::parse_config_change_from_data(data, metadata)
165}
166
167#[inline(always)]
168fn parse_raydium_clmm_create_personal_position(
169    data: &[u8],
170    metadata: EventMetadata,
171) -> Option<DexEvent> {
172    crate::logs::raydium_clmm::parse_create_personal_position_from_data(data, metadata)
173}
174
175#[inline(always)]
176fn parse_raydium_clmm_liquidity_calculate(
177    data: &[u8],
178    metadata: EventMetadata,
179) -> Option<DexEvent> {
180    crate::logs::raydium_clmm::parse_liquidity_calculate_from_data(data, metadata)
181}
182
183#[inline(always)]
184fn parse_raydium_clmm_open_limit_order(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
185    crate::logs::raydium_clmm::parse_open_limit_order_from_data(data, metadata)
186}
187
188#[inline(always)]
189fn parse_raydium_clmm_increase_limit_order(
190    data: &[u8],
191    metadata: EventMetadata,
192) -> Option<DexEvent> {
193    crate::logs::raydium_clmm::parse_increase_limit_order_from_data(data, metadata)
194}
195
196#[inline(always)]
197fn parse_raydium_clmm_decrease_limit_order(
198    data: &[u8],
199    metadata: EventMetadata,
200) -> Option<DexEvent> {
201    crate::logs::raydium_clmm::parse_decrease_limit_order_from_data(data, metadata)
202}
203
204#[inline(always)]
205fn parse_raydium_clmm_settle_limit_order(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
206    crate::logs::raydium_clmm::parse_settle_limit_order_from_data(data, metadata)
207}
208
209#[inline(always)]
210fn parse_raydium_clmm_update_reward_infos(
211    data: &[u8],
212    metadata: EventMetadata,
213) -> Option<DexEvent> {
214    crate::logs::raydium_clmm::parse_update_reward_infos_from_data(data, metadata)
215}
216
217#[inline(always)]
218fn parse_raydium_clmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
219    crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
220}
221
222#[inline(always)]
223fn parse_raydium_clmm_collect_personal_fee(
224    data: &[u8],
225    metadata: EventMetadata,
226) -> Option<DexEvent> {
227    crate::logs::raydium_clmm::parse_collect_personal_fee_from_data(data, metadata)
228}
229
230#[inline(always)]
231fn parse_raydium_clmm_collect_protocol_fee(
232    data: &[u8],
233    metadata: EventMetadata,
234) -> Option<DexEvent> {
235    crate::logs::raydium_clmm::parse_collect_protocol_fee_from_data(data, metadata)
236}
237
238// Raydium CPMM parsers
239#[inline(always)]
240fn parse_raydium_cpmm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
241    crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
242}
243
244#[inline(always)]
245fn parse_raydium_cpmm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
246    crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
247}
248
249#[inline(always)]
250fn parse_raydium_cpmm_create_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
251    crate::logs::raydium_cpmm::parse_create_pool_from_data(data, metadata)
252}
253
254#[inline(always)]
255fn parse_raydium_cpmm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
256    crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
257}
258
259#[inline(always)]
260fn parse_raydium_cpmm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
261    crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
262}
263
264// Raydium AMM V4 parsers
265#[inline(always)]
266fn parse_raydium_amm_swap_base_in(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
267    crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
268}
269
270#[inline(always)]
271fn parse_raydium_amm_swap_base_out(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
272    crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
273}
274
275#[inline(always)]
276fn parse_raydium_amm_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
277    crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
278}
279
280#[inline(always)]
281fn parse_raydium_amm_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
282    crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
283}
284
285#[inline(always)]
286fn parse_raydium_amm_initialize2(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
287    crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
288}
289
290// Orca Whirlpool parsers
291#[inline(always)]
292fn parse_orca_traded(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
293    crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
294}
295
296#[inline(always)]
297fn parse_orca_liquidity_increased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
298    crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
299}
300
301#[inline(always)]
302fn parse_orca_liquidity_decreased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
303    crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
304}
305
306#[inline(always)]
307fn parse_orca_pool_initialized(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
308    crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
309}
310
311// Meteora AMM parsers
312#[inline(always)]
313fn parse_meteora_amm_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
314    crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
315}
316
317#[inline(always)]
318fn parse_meteora_amm_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
319    crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
320}
321
322#[inline(always)]
323fn parse_meteora_amm_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
324    crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
325}
326
327#[inline(always)]
328fn parse_meteora_amm_bootstrap_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
329    crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
330}
331
332#[inline(always)]
333fn parse_meteora_amm_pool_created(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
334    crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
335}
336
337// ============================================================================
338// Const lookup table - Sorted by discriminator for binary search
339// ============================================================================
340
341macro_rules! disc_entry {
342    ($disc:expr, $name:expr, $parser:expr, $protocol:expr) => {
343        DiscriminatorInfo {
344            discriminator: $disc,
345            parser: $parser,
346            protocol: $protocol,
347            name: $name,
348        }
349    };
350}
351
352/// Compile-time constant array: discriminator -> parser info
353/// MUST be kept sorted by discriminator for binary search!
354///
355/// Expected latency: 3–8 ns (binary search on 33 discriminators ⇒ at most 6 comparisons)
356pub const DISCRIMINATOR_LUT: &[DiscriminatorInfo] = &[
357    // 按 discriminator 数值升序(binary_search 要求)
358    disc_entry!(
359        0x0100000000000000,
360        "Raydium AMM Initialize2",
361        parse_raydium_amm_initialize2,
362        Protocol::RaydiumAmm
363    ),
364    disc_entry!(
365        0x0300000000000000,
366        "Raydium AMM Deposit",
367        parse_raydium_amm_deposit,
368        Protocol::RaydiumAmm
369    ),
370    disc_entry!(
371        0x03F36CD5DC68A79B_u64,
372        "PumpFun Migrate Bonding Curve Creator",
373        parse_pumpfun_migrate_bonding_curve_creator,
374        Protocol::PumpFun
375    ),
376    disc_entry!(
377        0x0400000000000000,
378        "Raydium AMM Withdraw",
379        parse_raydium_amm_withdraw,
380        Protocol::RaydiumAmm
381    ),
382    disc_entry!(
383        0x0900000000000000,
384        "Raydium AMM Swap Base In",
385        parse_raydium_amm_swap_base_in,
386        Protocol::RaydiumAmm
387    ),
388    disc_entry!(
389        0xABB5CA7047241A6,
390        "Orca Whirlpool Liquidity Decreased",
391        parse_orca_liquidity_decreased,
392        Protocol::OrcaWhirlpool
393    ),
394    disc_entry!(
395        0x0B00000000000000,
396        "Raydium AMM Swap Base Out",
397        parse_raydium_amm_swap_base_out,
398        Protocol::RaydiumAmm
399    ),
400    // Raydium CPMM events
401    disc_entry!(
402        0x22A16D949C4612B7,
403        "Raydium CPMM Withdraw",
404        parse_raydium_cpmm_withdraw,
405        Protocol::RaydiumCpmm
406    ),
407    // PumpSwap events
408    disc_entry!(0x2ADC03A50A372F3E, "PumpSwap Sell", parse_pumpswap_sell, Protocol::PumpSwap),
409    disc_entry!(
410        0x385532443A56DE3A,
411        "Raydium CLMM Decrease Liquidity",
412        parse_raydium_clmm_decrease_liquidity,
413        Protocol::RaydiumClmm
414    ),
415    // Meteora AMM events
416    disc_entry!(
417        0x3A981F67E861F474,
418        "Meteora AMM Remove Liquidity",
419        parse_meteora_amm_remove_liquidity,
420        Protocol::MeteoraAmm
421    ),
422    disc_entry!(
423        0x3DD5292D4F1157CE,
424        "Raydium CLMM Collect Protocol Fee",
425        parse_raydium_clmm_collect_protocol_fee,
426        Protocol::RaydiumClmm
427    ),
428    disc_entry!(
429        0x3E99BE0E3C651772_u64,
430        "Pump Fees Revoke Fee Sharing Authority",
431        parse_pumpfees_revoke,
432        Protocol::PumpFees
433    ),
434    disc_entry!(
435        0x3F3563702F4B5E19,
436        "Raydium CLMM Create Pool",
437        parse_raydium_clmm_create_pool,
438        Protocol::RaydiumClmm
439    ),
440    disc_entry!(
441        0x529DDC6858292CCA,
442        "Meteora AMM Pool Created",
443        parse_meteora_amm_pool_created,
444        Protocol::MeteoraAmm
445    ),
446    disc_entry!(
447        0x541E2220D4694F31,
448        "Raydium CLMM Increase Liquidity",
449        parse_raydium_clmm_increase_liquidity,
450        Protocol::RaydiumClmm
451    ),
452    disc_entry!(
453        0x58FB74B8C8AA6985_u64,
454        "Pump Fees Create Fee Sharing Config",
455        parse_pumpfun_create_fee_sharing_config,
456        Protocol::PumpFees
457    ),
458    disc_entry!(
459        0x6953A151C069AEA6,
460        "Raydium CLMM Collect Personal Fee",
461        parse_raydium_clmm_collect_personal_fee,
462        Protocol::RaydiumClmm
463    ),
464    disc_entry!(
465        0x6B99589ECEAFF07E,
466        "Raydium CLMM Liquidity Change",
467        parse_raydium_clmm_liquidity_change,
468        Protocol::RaydiumClmm
469    ),
470    // PumpSwap and PumpFun events
471    disc_entry!(
472        0x74A776A0D20C31B1,
473        "PumpSwap Create Pool",
474        parse_pumpswap_create_pool,
475        Protocol::PumpSwap
476    ),
477    disc_entry!(0x7663EBDE4DA91B1B, "PumpFun Create", parse_pumpfun_create, Protocol::PumpFun),
478    disc_entry!(0x7777F52C1F52F467, "PumpSwap Buy", parse_pumpswap_buy, Protocol::PumpSwap),
479    disc_entry!(
480        0x7EE2380AE6F48A59_u64,
481        "Pump Fees Initialize Fee Config",
482        parse_pumpfees_initialize_fee_config,
483        Protocol::PumpFees
484    ),
485    disc_entry!(
486        0x906B8E1F533DF878,
487        "PumpSwap Add Liquidity",
488        parse_pumpswap_add_liquidity,
489        Protocol::PumpSwap
490    ),
491    disc_entry!(0x94EA945DB95DE9BD, "PumpFun Migrate", parse_pumpfun_migrate, Protocol::PumpFun),
492    disc_entry!(
493        0x96A02B93AF49CAE1,
494        "Orca Whirlpool Traded",
495        parse_orca_traded,
496        Protocol::OrcaWhirlpool
497    ),
498    disc_entry!(
499        0x975F706A7707BDF7,
500        "Raydium CLMM Config Change",
501        parse_raydium_clmm_config_change,
502        Protocol::RaydiumClmm
503    ),
504    disc_entry!(
505        0xA19BFE6690071E1E,
506        "Orca Whirlpool Liquidity Increased",
507        parse_orca_liquidity_increased,
508        Protocol::OrcaWhirlpool
509    ),
510    disc_entry!(
511        0xA2B45439E69470ED,
512        "Raydium CLMM Liquidity Calculate",
513        parse_raydium_clmm_liquidity_calculate,
514        Protocol::RaydiumClmm
515    ),
516    disc_entry!(
517        0xA3D4EDDBDD283046,
518        "Raydium CLMM Decrease Limit Order",
519        parse_raydium_clmm_decrease_limit_order,
520        Protocol::RaydiumClmm
521    ),
522    disc_entry!(
523        0xADB44AA35662D937,
524        "Raydium CPMM Swap Base Out",
525        parse_raydium_cpmm_swap_base_out,
526        Protocol::RaydiumCpmm
527    ),
528    disc_entry!(
529        0xB6F2E15289C623F2,
530        "Raydium CPMM Deposit",
531        parse_raydium_cpmm_deposit,
532        Protocol::RaydiumCpmm
533    ),
534    disc_entry!(
535        0xBA3D34E35A7D5E1F,
536        "Meteora AMM Add Liquidity",
537        parse_meteora_amm_add_liquidity,
538        Protocol::MeteoraAmm
539    ),
540    disc_entry!(
541        0xC0472CA01A850916,
542        "PumpSwap Remove Liquidity",
543        parse_pumpswap_remove_liquidity,
544        Protocol::PumpSwap
545    ),
546    disc_entry!(
547        0xC20A7C7DA44D7758,
548        "Raydium CLMM Settle Limit Order",
549        parse_raydium_clmm_settle_limit_order,
550        Protocol::RaydiumClmm
551    ),
552    disc_entry!(
553        0xC40AD0CDBE6CE351,
554        "Meteora AMM Swap",
555        parse_meteora_amm_swap,
556        Protocol::MeteoraAmm
557    ),
558    disc_entry!(
559        0xC81357C7CC0D780B,
560        "Raydium CLMM Increase Limit Order",
561        parse_raydium_clmm_increase_limit_order,
562        Protocol::RaydiumClmm
563    ),
564    disc_entry!(
565        0xCBE1E45BB8C4BA15_u64,
566        "Pump Fees Update Fee Shares",
567        parse_pumpfees_update_fee_shares,
568        Protocol::PumpFees
569    ),
570    disc_entry!(
571        0xCC21BA7ABBA959AB_u64,
572        "Pump Fees Upsert Fee Tiers",
573        parse_pumpfees_upsert_fee_tiers,
574        Protocol::PumpFees
575    ),
576    disc_entry!(
577        0xCE9ADFC4F9571E64,
578        "Raydium CLMM Create Personal Position",
579        parse_raydium_clmm_create_personal_position,
580        Protocol::RaydiumClmm
581    ),
582    disc_entry!(
583        0xD0BCF43E2341175A_u64,
584        "Pump Fees Update Fee Config",
585        parse_pumpfees_update_fee_config,
586        Protocol::PumpFees
587    ),
588    disc_entry!(
589        0xD89EA9395547186A,
590        "Raydium CLMM Open Limit Order",
591        parse_raydium_clmm_open_limit_order,
592        Protocol::RaydiumClmm
593    ),
594    disc_entry!(
595        0xDE331EC4DA5ABE8F,
596        "Raydium CPMM Swap Base In",
597        parse_raydium_cpmm_swap_base_in,
598        Protocol::RaydiumCpmm
599    ),
600    disc_entry!(
601        0xE2710826E8CDC640,
602        "Raydium CLMM Swap",
603        parse_raydium_clmm_swap,
604        Protocol::RaydiumClmm
605    ),
606    disc_entry!(
607        0xE5FEC60C57AD7664,
608        "Orca Whirlpool Initialize",
609        parse_orca_pool_initialized,
610        Protocol::OrcaWhirlpool
611    ),
612    disc_entry!(
613        0xEA423FF657AB98E1_u64,
614        "Pump Fees Update Admin",
615        parse_pumpfees_update_admin,
616        Protocol::PumpFees
617    ),
618    disc_entry!(
619        0xEC08B84DF5C68F7C_u64,
620        "Pump Fees Transfer Fee Sharing Authority",
621        parse_pumpfees_transfer,
622        Protocol::PumpFees
623    ),
624    disc_entry!(
625        0xEC2541724EBA7F6D,
626        "Raydium CLMM Update Reward Infos",
627        parse_raydium_clmm_update_reward_infos,
628        Protocol::RaydiumClmm
629    ),
630    disc_entry!(0xEE61E64ED37FDBBD, "PumpFun Trade", parse_pumpfun_trade, Protocol::PumpFun),
631    disc_entry!(
632        0xF3D63778E297CCCB_u64,
633        "Pump Fees Reset Fee Sharing Config",
634        parse_pumpfees_reset,
635        Protocol::PumpFees
636    ),
637    disc_entry!(
638        0xF70E375C88267F79,
639        "Meteora AMM Bootstrap Liquidity",
640        parse_meteora_amm_bootstrap_liquidity,
641        Protocol::MeteoraAmm
642    ),
643];
644
645/// Fast lookup by discriminator - O(log n) binary search
646///
647/// With 33 entries, this requires at most 6 comparisons
648#[inline(always)]
649pub fn lookup_discriminator(discriminator: u64) -> Option<&'static DiscriminatorInfo> {
650    DISCRIMINATOR_LUT
651        .binary_search_by_key(&discriminator, |info| info.discriminator)
652        .ok()
653        .map(|idx| &DISCRIMINATOR_LUT[idx])
654}
655
656/// Get event name from discriminator
657#[inline(always)]
658pub fn discriminator_to_name(discriminator: u64) -> Option<&'static str> {
659    lookup_discriminator(discriminator).map(|info| info.name)
660}
661
662/// Get protocol from discriminator
663#[inline(always)]
664pub fn discriminator_to_protocol(discriminator: u64) -> Option<Protocol> {
665    lookup_discriminator(discriminator).map(|info| info.protocol)
666}
667
668/// Parse event using discriminator lookup
669#[inline(always)]
670pub fn parse_with_discriminator(
671    discriminator: u64,
672    data: &[u8],
673    metadata: EventMetadata,
674) -> Option<DexEvent> {
675    let info = lookup_discriminator(discriminator)?;
676    (info.parser)(data, metadata)
677}
678
679#[cfg(test)]
680mod tests {
681    use super::*;
682
683    #[test]
684    fn test_lut_is_sorted() {
685        // Verify the LUT is sorted (required for binary search)
686        for i in 1..DISCRIMINATOR_LUT.len() {
687            assert!(
688                DISCRIMINATOR_LUT[i - 1].discriminator < DISCRIMINATOR_LUT[i].discriminator,
689                "LUT not sorted at index {}: {:x} >= {:x}",
690                i,
691                DISCRIMINATOR_LUT[i - 1].discriminator,
692                DISCRIMINATOR_LUT[i].discriminator
693            );
694        }
695    }
696
697    #[test]
698    fn test_discriminator_lookup() {
699        // PumpFun Create
700        let disc = 0x7663EBDE4DA91B1B;
701        let info = lookup_discriminator(disc).unwrap();
702        assert_eq!(info.name, "PumpFun Create");
703        assert_eq!(info.protocol, Protocol::PumpFun);
704
705        // Raydium CLMM Swap
706        let disc = 0xE2710826E8CDC640;
707        let info = lookup_discriminator(disc).unwrap();
708        assert_eq!(info.name, "Raydium CLMM Swap");
709        assert_eq!(info.protocol, Protocol::RaydiumClmm);
710
711        // Unknown discriminator
712        let disc = 0xFFFFFFFFFFFFFFFF;
713        assert!(lookup_discriminator(disc).is_none());
714    }
715
716    #[test]
717    fn test_event_name_lookup() {
718        // PumpSwap Buy
719        let disc = 0x7777F52C1F52F467;
720        assert_eq!(discriminator_to_name(disc), Some("PumpSwap Buy"));
721
722        // Raydium AMM Swap Base In
723        let disc = 0x0900000000000000;
724        assert_eq!(discriminator_to_name(disc), Some("Raydium AMM Swap Base In"));
725    }
726
727    #[test]
728    fn test_protocol_lookup() {
729        assert_eq!(discriminator_to_protocol(0x7663EBDE4DA91B1B), Some(Protocol::PumpFun));
730        assert_eq!(discriminator_to_protocol(0xE2710826E8CDC640), Some(Protocol::RaydiumClmm));
731        assert_eq!(discriminator_to_protocol(0x96A02B93AF49CAE1), Some(Protocol::OrcaWhirlpool));
732    }
733}