Skip to main content

sol_parser_sdk/instr/
all_inner.rs

1//! 所有协议的 Inner Instruction 解析器统一入口
2//!
3//! 采用简洁高效的实现方式,所有协议共享通用工具函数
4//!
5//! ## 解析器插件系统
6//!
7//! 所有协议支持两种可插拔的解析器实现:
8//!
9//! ### 1. Borsh 反序列化解析器(默认,推荐)
10//! - **启用**: `cargo build --features parse-borsh` (默认)
11//! - **优点**: 类型安全、代码简洁、易维护、自动验证
12//! - **适用**: 一般场景、需要稳定性和可维护性的项目
13//!
14//! ### 2. 零拷贝解析器(高性能)
15//! - **启用**: `cargo build --features parse-zero-copy --no-default-features`
16//! - **优点**: 最快、零拷贝、无验证开销、适合超高频场景
17//! - **适用**: 性能关键路径、每秒数万次解析的场景
18
19use crate::core::events::*;
20use crate::instr::inner_common::*;
21use solana_sdk::pubkey::Pubkey;
22
23
24// ============================================================================
25// Raydium CPMM
26// ============================================================================
27
28pub mod raydium_cpmm {
29    use super::*;
30
31    pub mod discriminators {
32        pub const SWAP_BASE_IN: [u8; 16] = [143, 190, 90, 218, 196, 30, 51, 222, 155, 167, 108, 32, 122, 76, 173, 64];
33        pub const SWAP_BASE_OUT: [u8; 16] = [55, 217, 98, 86, 163, 74, 180, 173, 155, 167, 108, 32, 122, 76, 173, 64];
34        pub const CREATE_POOL: [u8; 16] = [233, 146, 209, 142, 207, 104, 64, 188, 155, 167, 108, 32, 122, 76, 173, 64];
35        pub const DEPOSIT: [u8; 16] = [242, 35, 198, 137, 82, 225, 242, 182, 155, 167, 108, 32, 122, 76, 173, 64];
36        pub const WITHDRAW: [u8; 16] = [183, 18, 70, 156, 148, 109, 161, 34, 155, 167, 108, 32, 122, 76, 173, 64];
37    }
38
39    /// 主入口:根据 discriminator 解析事件
40    #[inline]
41    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
42        match disc {
43            &discriminators::SWAP_BASE_IN | &discriminators::SWAP_BASE_OUT => parse_swap(data, metadata),
44            &discriminators::DEPOSIT => parse_deposit(data, metadata),
45            &discriminators::WITHDRAW => parse_withdraw(data, metadata),
46            _ => None,
47        }
48    }
49
50    // ============================================================================
51    // Swap 事件解析器
52    // ============================================================================
53
54    /// 解析 Swap 事件(统一入口)
55    #[inline(always)]
56    fn parse_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
57        #[cfg(feature = "parse-borsh")]
58        {
59            parse_swap_borsh(data, metadata)
60        }
61
62        #[cfg(feature = "parse-zero-copy")]
63        {
64            parse_swap_zero_copy(data, metadata)
65        }
66    }
67
68    /// Borsh 反序列化解析器 - Swap 事件
69    #[cfg(feature = "parse-borsh")]
70    #[inline(always)]
71    fn parse_swap_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
72        // 数据结构:
73        // pool_id: Pubkey (32 bytes)
74        // input_amount: u64 (8 bytes)
75        // output_amount: u64 (8 bytes)
76        // Total: 48 bytes
77        const EVENT_SIZE: usize = 32 + 8 + 8;
78
79        if data.len() < EVENT_SIZE {
80            return None;
81        }
82
83        let event = borsh::from_slice::<RaydiumCpmmSwapEvent>(&data[..EVENT_SIZE]).ok()?;
84
85        Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
86            metadata,
87            ..event
88        }))
89    }
90
91    /// 零拷贝解析器 - Swap 事件
92    #[cfg(feature = "parse-zero-copy")]
93    #[inline(always)]
94    fn parse_swap_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
95        unsafe {
96            if !check_length(data, 32 + 8 + 8) {
97                return None;
98            }
99            let pool = read_pubkey_unchecked(data, 0);
100            let input_amount = read_u64_unchecked(data, 32);
101            let output_amount = read_u64_unchecked(data, 40);
102            Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
103                metadata,
104                pool_id: pool,
105                input_amount,
106                output_amount,
107                input_vault_before: 0,
108                output_vault_before: 0,
109                input_transfer_fee: 0,
110                output_transfer_fee: 0,
111                base_input: true,
112            }))
113        }
114    }
115
116    // ============================================================================
117    // Deposit 事件解析器
118    // ============================================================================
119
120    /// 解析 Deposit 事件(统一入口)
121    #[inline(always)]
122    fn parse_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
123        #[cfg(feature = "parse-borsh")]
124        {
125            parse_deposit_borsh(data, metadata)
126        }
127
128        #[cfg(feature = "parse-zero-copy")]
129        {
130            parse_deposit_zero_copy(data, metadata)
131        }
132    }
133
134    /// Borsh 反序列化解析器 - Deposit 事件
135    #[cfg(feature = "parse-borsh")]
136    #[inline(always)]
137    fn parse_deposit_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
138        // 数据结构:
139        // pool: Pubkey (32 bytes)
140        // token0_amount: u64 (8 bytes)
141        // token1_amount: u64 (8 bytes)
142        // lp_token_amount: u64 (8 bytes)
143        // Total: 56 bytes
144        const EVENT_SIZE: usize = 32 + 8 + 8 + 8;
145
146        if data.len() < EVENT_SIZE {
147            return None;
148        }
149
150        let event = borsh::from_slice::<RaydiumCpmmDepositEvent>(&data[..EVENT_SIZE]).ok()?;
151
152        Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
153            metadata,
154            ..event
155        }))
156    }
157
158    /// 零拷贝解析器 - Deposit 事件
159    #[cfg(feature = "parse-zero-copy")]
160    #[inline(always)]
161    fn parse_deposit_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
162        unsafe {
163            if !check_length(data, 32 + 8 + 8 + 8) {
164                return None;
165            }
166            let pool = read_pubkey_unchecked(data, 0);
167            let token0_amount = read_u64_unchecked(data, 32);
168            let token1_amount = read_u64_unchecked(data, 40);
169            let lp_token_amount = read_u64_unchecked(data, 48);
170            Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
171                metadata,
172                pool,
173                lp_token_amount,
174                token0_amount,
175                token1_amount,
176                user: Pubkey::default(),
177            }))
178        }
179    }
180
181    // ============================================================================
182    // Withdraw 事件解析器
183    // ============================================================================
184
185    /// 解析 Withdraw 事件(统一入口)
186    #[inline(always)]
187    fn parse_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
188        #[cfg(feature = "parse-borsh")]
189        {
190            parse_withdraw_borsh(data, metadata)
191        }
192
193        #[cfg(feature = "parse-zero-copy")]
194        {
195            parse_withdraw_zero_copy(data, metadata)
196        }
197    }
198
199    /// Borsh 反序列化解析器 - Withdraw 事件
200    #[cfg(feature = "parse-borsh")]
201    #[inline(always)]
202    fn parse_withdraw_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
203        // 数据结构:
204        // pool: Pubkey (32 bytes)
205        // lp_token_amount: u64 (8 bytes)
206        // token0_amount: u64 (8 bytes)
207        // token1_amount: u64 (8 bytes)
208        // Total: 56 bytes
209        const EVENT_SIZE: usize = 32 + 8 + 8 + 8;
210
211        if data.len() < EVENT_SIZE {
212            return None;
213        }
214
215        let event = borsh::from_slice::<RaydiumCpmmWithdrawEvent>(&data[..EVENT_SIZE]).ok()?;
216
217        Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
218            metadata,
219            ..event
220        }))
221    }
222
223    /// 零拷贝解析器 - Withdraw 事件
224    #[cfg(feature = "parse-zero-copy")]
225    #[inline(always)]
226    fn parse_withdraw_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
227        unsafe {
228            if !check_length(data, 32 + 8 + 8 + 8) {
229                return None;
230            }
231            let pool = read_pubkey_unchecked(data, 0);
232            let lp_token_amount = read_u64_unchecked(data, 32);
233            let token0_amount = read_u64_unchecked(data, 40);
234            let token1_amount = read_u64_unchecked(data, 48);
235            Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
236                metadata,
237                pool,
238                lp_token_amount,
239                token0_amount,
240                token1_amount,
241                user: Pubkey::default(),
242            }))
243        }
244    }
245}
246
247// ============================================================================
248// Raydium AMM V4
249// ============================================================================
250
251pub mod raydium_amm {
252    use super::*;
253
254    pub mod discriminators {
255        pub const SWAP_BASE_IN: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 9, 155, 167, 108, 32, 122, 76, 173, 64];
256        pub const SWAP_BASE_OUT: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 11, 155, 167, 108, 32, 122, 76, 173, 64];
257        pub const DEPOSIT: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 3, 155, 167, 108, 32, 122, 76, 173, 64];
258        pub const WITHDRAW: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 4, 155, 167, 108, 32, 122, 76, 173, 64];
259        pub const INITIALIZE2: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 1, 155, 167, 108, 32, 122, 76, 173, 64];
260    }
261
262    /// 主入口:根据 discriminator 解析事件
263    #[inline]
264    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
265        match disc {
266            &discriminators::SWAP_BASE_IN | &discriminators::SWAP_BASE_OUT => parse_swap(data, metadata),
267            &discriminators::DEPOSIT => parse_deposit(data, metadata),
268            &discriminators::WITHDRAW => parse_withdraw(data, metadata),
269            _ => None,
270        }
271    }
272
273    // ============================================================================
274    // Swap 事件解析器
275    // ============================================================================
276
277    /// 解析 Swap 事件(统一入口)
278    #[inline(always)]
279    fn parse_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
280        #[cfg(feature = "parse-borsh")]
281        {
282            parse_swap_borsh(data, metadata)
283        }
284
285        #[cfg(feature = "parse-zero-copy")]
286        {
287            parse_swap_zero_copy(data, metadata)
288        }
289    }
290
291    /// Borsh 反序列化解析器 - Swap 事件
292    #[cfg(feature = "parse-borsh")]
293    #[inline(always)]
294    fn parse_swap_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
295        // 数据结构:
296        // amm: Pubkey (32 bytes)
297        // amount_in: u64 (8 bytes)
298        // amount_out: u64 (8 bytes)
299        // Total: 48 bytes
300        const EVENT_SIZE: usize = 32 + 8 + 8;
301
302        if data.len() < EVENT_SIZE {
303            return None;
304        }
305
306        let event = borsh::from_slice::<RaydiumAmmV4SwapEvent>(&data[..EVENT_SIZE]).ok()?;
307
308        Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
309            metadata,
310            ..event
311        }))
312    }
313
314    /// 零拷贝解析器 - Swap 事件
315    #[cfg(feature = "parse-zero-copy")]
316    #[inline(always)]
317    fn parse_swap_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
318        unsafe {
319            if !check_length(data, 32 + 8 + 8) {
320                return None;
321            }
322            let amm = read_pubkey_unchecked(data, 0);
323            let amount_in = read_u64_unchecked(data, 32);
324            let amount_out = read_u64_unchecked(data, 40);
325            Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
326                metadata,
327                amm,
328                amount_in,
329                amount_out,
330                minimum_amount_out: 0,
331                max_amount_in: 0,
332                token_program: Pubkey::default(),
333                amm_authority: Pubkey::default(),
334                amm_open_orders: Pubkey::default(),
335                amm_target_orders: None,
336                pool_coin_token_account: Pubkey::default(),
337                pool_pc_token_account: Pubkey::default(),
338                serum_program: Pubkey::default(),
339                serum_market: Pubkey::default(),
340                serum_bids: Pubkey::default(),
341                serum_asks: Pubkey::default(),
342                serum_event_queue: Pubkey::default(),
343                serum_coin_vault_account: Pubkey::default(),
344                serum_pc_vault_account: Pubkey::default(),
345                serum_vault_signer: Pubkey::default(),
346                user_source_token_account: Pubkey::default(),
347                user_destination_token_account: Pubkey::default(),
348                user_source_owner: Pubkey::default(),
349            }))
350        }
351    }
352
353    // ============================================================================
354    // Deposit 事件解析器
355    // ============================================================================
356
357    /// 解析 Deposit 事件(统一入口)
358    #[inline(always)]
359    fn parse_deposit(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
360        #[cfg(feature = "parse-borsh")]
361        {
362            parse_deposit_borsh(data, metadata)
363        }
364
365        #[cfg(feature = "parse-zero-copy")]
366        {
367            parse_deposit_zero_copy(data, metadata)
368        }
369    }
370
371    /// Borsh 反序列化解析器 - Deposit 事件
372    #[cfg(feature = "parse-borsh")]
373    #[inline(always)]
374    fn parse_deposit_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
375        // 数据结构:
376        // amm: Pubkey (32 bytes)
377        // max_coin_amount: u64 (8 bytes)
378        // max_pc_amount: u64 (8 bytes)
379        // Total: 48 bytes
380        const EVENT_SIZE: usize = 32 + 8 + 8;
381
382        if data.len() < EVENT_SIZE {
383            return None;
384        }
385
386        let event = borsh::from_slice::<RaydiumAmmV4DepositEvent>(&data[..EVENT_SIZE]).ok()?;
387
388        Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
389            metadata,
390            ..event
391        }))
392    }
393
394    /// 零拷贝解析器 - Deposit 事件
395    #[cfg(feature = "parse-zero-copy")]
396    #[inline(always)]
397    fn parse_deposit_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
398        unsafe {
399            if !check_length(data, 32 + 8 + 8) {
400                return None;
401            }
402            let amm = read_pubkey_unchecked(data, 0);
403            let max_coin_amount = read_u64_unchecked(data, 32);
404            let max_pc_amount = read_u64_unchecked(data, 40);
405            Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
406                metadata,
407                amm,
408                max_coin_amount,
409                max_pc_amount,
410                base_side: 0,
411                token_program: Pubkey::default(),
412                amm_authority: Pubkey::default(),
413                amm_open_orders: Pubkey::default(),
414                amm_target_orders: Pubkey::default(),
415                lp_mint_address: Pubkey::default(),
416                pool_coin_token_account: Pubkey::default(),
417                pool_pc_token_account: Pubkey::default(),
418                serum_market: Pubkey::default(),
419                serum_event_queue: Pubkey::default(),
420                user_coin_token_account: Pubkey::default(),
421                user_pc_token_account: Pubkey::default(),
422                user_lp_token_account: Pubkey::default(),
423                user_owner: Pubkey::default(),
424            }))
425        }
426    }
427
428    // ============================================================================
429    // Withdraw 事件解析器
430    // ============================================================================
431
432    /// 解析 Withdraw 事件(统一入口)
433    #[inline(always)]
434    fn parse_withdraw(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
435        #[cfg(feature = "parse-borsh")]
436        {
437            parse_withdraw_borsh(data, metadata)
438        }
439
440        #[cfg(feature = "parse-zero-copy")]
441        {
442            parse_withdraw_zero_copy(data, metadata)
443        }
444    }
445
446    /// Borsh 反序列化解析器 - Withdraw 事件
447    #[cfg(feature = "parse-borsh")]
448    #[inline(always)]
449    fn parse_withdraw_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
450        // 数据结构:
451        // amm: Pubkey (32 bytes)
452        // amount: u64 (8 bytes)
453        // Total: 40 bytes
454        const EVENT_SIZE: usize = 32 + 8;
455
456        if data.len() < EVENT_SIZE {
457            return None;
458        }
459
460        let event = borsh::from_slice::<RaydiumAmmV4WithdrawEvent>(&data[..EVENT_SIZE]).ok()?;
461
462        Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
463            metadata,
464            ..event
465        }))
466    }
467
468    /// 零拷贝解析器 - Withdraw 事件
469    #[cfg(feature = "parse-zero-copy")]
470    #[inline(always)]
471    fn parse_withdraw_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
472        unsafe {
473            if !check_length(data, 32 + 8) {
474                return None;
475            }
476            let amm = read_pubkey_unchecked(data, 0);
477            let amount = read_u64_unchecked(data, 32);
478            Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
479                metadata,
480                amm,
481                amount,
482                token_program: Pubkey::default(),
483                amm_authority: Pubkey::default(),
484                amm_open_orders: Pubkey::default(),
485                amm_target_orders: Pubkey::default(),
486                lp_mint_address: Pubkey::default(),
487                pool_coin_token_account: Pubkey::default(),
488                pool_pc_token_account: Pubkey::default(),
489                pool_withdraw_queue: Pubkey::default(),
490                pool_temp_lp_token_account: Pubkey::default(),
491                serum_program: Pubkey::default(),
492                serum_market: Pubkey::default(),
493                serum_bids: Pubkey::default(),
494                serum_asks: Pubkey::default(),
495                serum_event_queue: Pubkey::default(),
496                serum_coin_vault_account: Pubkey::default(),
497                serum_pc_vault_account: Pubkey::default(),
498                serum_vault_signer: Pubkey::default(),
499                user_lp_token_account: Pubkey::default(),
500                user_coin_token_account: Pubkey::default(),
501                user_pc_token_account: Pubkey::default(),
502                user_owner: Pubkey::default(),
503            }))
504        }
505    }
506}
507
508// ============================================================================
509// Orca Whirlpool
510// ============================================================================
511
512pub mod orca {
513    //! Orca Whirlpool Inner Instruction 解析器
514    //!
515    //! ## 解析器插件系统
516    //!
517    //! 支持两种可插拔的解析器实现:
518    //!
519    //! ### 1. Borsh 反序列化解析器(默认,推荐)
520    //! - **启用**: `cargo build --features parse-borsh` (默认)
521    //! - 特点:类型安全、代码简洁、易于维护
522    //!
523    //! ### 2. 零拷贝解析器(高性能)
524    //! - **启用**: `cargo build --features parse-zero-copy --no-default-features`
525    //! - 特点:最高性能、零内存分配、直接读取内存
526
527    use super::*;
528
529    pub mod discriminators {
530        pub const TRADED: [u8; 16] = [225, 202, 73, 175, 147, 43, 160, 150, 155, 167, 108, 32, 122, 76, 173, 64];
531        pub const LIQUIDITY_INCREASED: [u8; 16] = [30, 7, 144, 181, 102, 254, 155, 161, 155, 167, 108, 32, 122, 76, 173, 64];
532        pub const LIQUIDITY_DECREASED: [u8; 16] = [166, 1, 36, 71, 112, 202, 181, 171, 155, 167, 108, 32, 122, 76, 173, 64];
533        pub const POOL_INITIALIZED: [u8; 16] = [100, 118, 173, 87, 12, 198, 254, 229, 155, 167, 108, 32, 122, 76, 173, 64];
534    }
535
536    /// 主入口:根据 discriminator 解析事件
537    #[inline]
538    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
539        match disc {
540            &discriminators::TRADED => parse_swap(data, metadata),
541            &discriminators::LIQUIDITY_INCREASED => parse_liquidity_increased(data, metadata),
542            &discriminators::LIQUIDITY_DECREASED => parse_liquidity_decreased(data, metadata),
543            _ => None,
544        }
545    }
546
547    // ============================================================================
548    // Swap Event (Traded)
549    // ============================================================================
550
551    /// 解析 Swap 事件(统一入口)
552    #[inline(always)]
553    fn parse_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
554        #[cfg(feature = "parse-borsh")]
555        { parse_swap_borsh(data, metadata) }
556
557        #[cfg(feature = "parse-zero-copy")]
558        { parse_swap_zero_copy(data, metadata) }
559    }
560
561    /// Borsh 解析器
562    #[cfg(feature = "parse-borsh")]
563    #[inline(always)]
564    fn parse_swap_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
565        // 数据结构:whirlpool(32) + input_amount(8) + output_amount(8) + a_to_b(1) = 49 bytes
566        const SWAP_EVENT_SIZE: usize = 32 + 8 + 8 + 1;
567        if data.len() < SWAP_EVENT_SIZE { return None; }
568
569        let mut event = borsh::from_slice::<OrcaWhirlpoolSwapEvent>(&data[..SWAP_EVENT_SIZE]).ok()?;
570        event.metadata = metadata;
571        Some(DexEvent::OrcaWhirlpoolSwap(event))
572    }
573
574    /// 零拷贝解析器
575    #[cfg(feature = "parse-zero-copy")]
576    #[inline(always)]
577    fn parse_swap_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
578        unsafe {
579            if !check_length(data, 32 + 8 + 8 + 1) { return None; }
580            let whirlpool = read_pubkey_unchecked(data, 0);
581            let input_amount = read_u64_unchecked(data, 32);
582            let output_amount = read_u64_unchecked(data, 40);
583            let a_to_b = read_bool_unchecked(data, 48);
584            Some(DexEvent::OrcaWhirlpoolSwap(OrcaWhirlpoolSwapEvent {
585                metadata, whirlpool, input_amount, output_amount, a_to_b,
586                pre_sqrt_price: 0, post_sqrt_price: 0,
587                input_transfer_fee: 0, output_transfer_fee: 0,
588                lp_fee: 0, protocol_fee: 0,
589            }))
590        }
591    }
592
593    // ============================================================================
594    // LiquidityIncreased Event
595    // ============================================================================
596
597    /// 解析 LiquidityIncreased 事件(统一入口)
598    #[inline(always)]
599    fn parse_liquidity_increased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
600        #[cfg(feature = "parse-borsh")]
601        { parse_liquidity_increased_borsh(data, metadata) }
602
603        #[cfg(feature = "parse-zero-copy")]
604        { parse_liquidity_increased_zero_copy(data, metadata) }
605    }
606
607    /// Borsh 解析器
608    #[cfg(feature = "parse-borsh")]
609    #[inline(always)]
610    fn parse_liquidity_increased_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
611        // 数据结构:whirlpool(32) + liquidity(16) + token_a_amount(8) + token_b_amount(8) = 64 bytes
612        const LIQUIDITY_EVENT_SIZE: usize = 32 + 16 + 8 + 8;
613        if data.len() < LIQUIDITY_EVENT_SIZE { return None; }
614
615        let mut event = borsh::from_slice::<OrcaWhirlpoolLiquidityIncreasedEvent>(&data[..LIQUIDITY_EVENT_SIZE]).ok()?;
616        event.metadata = metadata;
617        Some(DexEvent::OrcaWhirlpoolLiquidityIncreased(event))
618    }
619
620    /// 零拷贝解析器
621    #[cfg(feature = "parse-zero-copy")]
622    #[inline(always)]
623    fn parse_liquidity_increased_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
624        unsafe {
625            if !check_length(data, 32 + 16 + 8 + 8) { return None; }
626            let whirlpool = read_pubkey_unchecked(data, 0);
627            let liquidity = read_u128_unchecked(data, 32);
628            let token_a_amount = read_u64_unchecked(data, 48);
629            let token_b_amount = read_u64_unchecked(data, 56);
630            Some(DexEvent::OrcaWhirlpoolLiquidityIncreased(OrcaWhirlpoolLiquidityIncreasedEvent {
631                metadata, whirlpool, liquidity, token_a_amount, token_b_amount,
632                position: Pubkey::default(), tick_lower_index: 0, tick_upper_index: 0,
633                token_a_transfer_fee: 0, token_b_transfer_fee: 0,
634            }))
635        }
636    }
637
638    // ============================================================================
639    // LiquidityDecreased Event
640    // ============================================================================
641
642    /// 解析 LiquidityDecreased 事件(统一入口)
643    #[inline(always)]
644    fn parse_liquidity_decreased(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
645        #[cfg(feature = "parse-borsh")]
646        { parse_liquidity_decreased_borsh(data, metadata) }
647
648        #[cfg(feature = "parse-zero-copy")]
649        { parse_liquidity_decreased_zero_copy(data, metadata) }
650    }
651
652    /// Borsh 解析器
653    #[cfg(feature = "parse-borsh")]
654    #[inline(always)]
655    fn parse_liquidity_decreased_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
656        // 数据结构:whirlpool(32) + liquidity(16) + token_a_amount(8) + token_b_amount(8) = 64 bytes
657        const LIQUIDITY_EVENT_SIZE: usize = 32 + 16 + 8 + 8;
658        if data.len() < LIQUIDITY_EVENT_SIZE { return None; }
659
660        let mut event = borsh::from_slice::<OrcaWhirlpoolLiquidityDecreasedEvent>(&data[..LIQUIDITY_EVENT_SIZE]).ok()?;
661        event.metadata = metadata;
662        Some(DexEvent::OrcaWhirlpoolLiquidityDecreased(event))
663    }
664
665    /// 零拷贝解析器
666    #[cfg(feature = "parse-zero-copy")]
667    #[inline(always)]
668    fn parse_liquidity_decreased_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
669        unsafe {
670            if !check_length(data, 32 + 16 + 8 + 8) { return None; }
671            let whirlpool = read_pubkey_unchecked(data, 0);
672            let liquidity = read_u128_unchecked(data, 32);
673            let token_a_amount = read_u64_unchecked(data, 48);
674            let token_b_amount = read_u64_unchecked(data, 56);
675            Some(DexEvent::OrcaWhirlpoolLiquidityDecreased(OrcaWhirlpoolLiquidityDecreasedEvent {
676                metadata, whirlpool, liquidity, token_a_amount, token_b_amount,
677                position: Pubkey::default(), tick_lower_index: 0, tick_upper_index: 0,
678                token_a_transfer_fee: 0, token_b_transfer_fee: 0,
679            }))
680        }
681    }
682}
683
684// ============================================================================
685// Meteora AMM
686// ============================================================================
687
688pub mod meteora_amm {
689    use super::*;
690
691    pub mod discriminators {
692        pub const SWAP: [u8; 16] = [81, 108, 227, 190, 205, 208, 10, 196, 155, 167, 108, 32, 122, 76, 173, 64];
693        pub const ADD_LIQUIDITY: [u8; 16] = [31, 94, 125, 90, 227, 52, 61, 186, 155, 167, 108, 32, 122, 76, 173, 64];
694        pub const REMOVE_LIQUIDITY: [u8; 16] = [116, 244, 97, 232, 103, 31, 152, 58, 155, 167, 108, 32, 122, 76, 173, 64];
695        pub const POOL_CREATED: [u8; 16] = [202, 44, 41, 88, 104, 220, 157, 82, 155, 167, 108, 32, 122, 76, 173, 64];
696    }
697
698    #[inline]
699    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
700        unsafe {
701            match disc {
702                &discriminators::SWAP => {
703                    if !check_length(data, 8 + 8) { return None; }
704                    let in_amount = read_u64_unchecked(data, 0);
705                    let out_amount = read_u64_unchecked(data, 8);
706                    Some(DexEvent::MeteoraPoolsSwap(MeteoraPoolsSwapEvent {
707                        metadata, in_amount, out_amount, trade_fee: 0, admin_fee: 0, host_fee: 0,
708                    }))
709                }
710                &discriminators::ADD_LIQUIDITY => {
711                    if !check_length(data, 8 + 8 + 8) { return None; }
712                    let lp_mint_amount = read_u64_unchecked(data, 0);
713                    let token_a_amount = read_u64_unchecked(data, 8);
714                    let token_b_amount = read_u64_unchecked(data, 16);
715                    Some(DexEvent::MeteoraPoolsAddLiquidity(MeteoraPoolsAddLiquidityEvent {
716                        metadata, lp_mint_amount, token_a_amount, token_b_amount,
717                    }))
718                }
719                &discriminators::REMOVE_LIQUIDITY => {
720                    if !check_length(data, 8 + 8 + 8) { return None; }
721                    let lp_unmint_amount = read_u64_unchecked(data, 0);
722                    let token_a_out_amount = read_u64_unchecked(data, 8);
723                    let token_b_out_amount = read_u64_unchecked(data, 16);
724                    Some(DexEvent::MeteoraPoolsRemoveLiquidity(MeteoraPoolsRemoveLiquidityEvent {
725                        metadata, lp_unmint_amount, token_a_out_amount, token_b_out_amount,
726                    }))
727                }
728                _ => None,
729            }
730        }
731    }
732}
733
734// ============================================================================
735// Meteora DAMM V2
736// ============================================================================
737
738pub mod meteora_damm {
739    //! Meteora DAMM V2 Inner Instruction 解析器
740    //!
741    //! ## 解析器插件系统
742    //!
743    //! 支持两种可插拔的解析器实现:
744    //!
745    //! ### 1. Borsh 反序列化解析器(默认,推荐)
746    //! - **启用**: `cargo build --features parse-borsh` (默认)
747    //! - 特点:类型安全、代码简洁、易于维护
748    //!
749    //! ### 2. 零拷贝解析器(高性能)
750    //! - **启用**: `cargo build --features parse-zero-copy --no-default-features`
751    //! - 特点:最高性能、零内存分配、直接读取内存
752
753    use super::*;
754
755    pub mod discriminators {
756        pub const SWAP: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 27, 60, 21, 213, 138, 170, 187, 147];
757        pub const SWAP2: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 189, 66, 51, 168, 38, 80, 117, 153];
758        pub const ADD_LIQUIDITY: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 175, 242, 8, 157, 30, 247, 185, 169];
759        pub const REMOVE_LIQUIDITY: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 87, 46, 88, 98, 175, 96, 34, 91];
760        pub const CREATE_POSITION: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 156, 15, 119, 198, 29, 181, 221, 55];
761        pub const CLOSE_POSITION: [u8; 16] = [228, 69, 165, 46, 81, 203, 154, 29, 20, 145, 144, 68, 143, 142, 214, 178];
762    }
763
764    /// 主入口:根据 discriminator 解析事件
765    #[inline]
766    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
767        match disc {
768            &discriminators::SWAP => parse_swap(data, metadata),
769            &discriminators::SWAP2 => parse_swap2(data, metadata),
770            &discriminators::ADD_LIQUIDITY => parse_add_liquidity(data, metadata),
771            &discriminators::REMOVE_LIQUIDITY => parse_remove_liquidity(data, metadata),
772            &discriminators::CREATE_POSITION => parse_create_position(data, metadata),
773            &discriminators::CLOSE_POSITION => parse_close_position(data, metadata),
774            _ => None,
775        }
776    }
777
778    // ============================================================================
779    // Swap Event
780    // ============================================================================
781
782    /// 解析 Swap 事件(统一入口)
783    #[inline(always)]
784    fn parse_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
785        #[cfg(feature = "parse-borsh")]
786        { parse_swap_borsh(data, metadata) }
787
788        #[cfg(feature = "parse-zero-copy")]
789        { parse_swap_zero_copy(data, metadata) }
790    }
791
792    /// Borsh 解析器
793    #[cfg(feature = "parse-borsh")]
794    #[inline(always)]
795    fn parse_swap_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
796        // 数据结构:pool(32) + amount_in(8) + output_amount(8) = 48 bytes
797        const SWAP_EVENT_SIZE: usize = 32 + 8 + 8;
798        if data.len() < SWAP_EVENT_SIZE { return None; }
799
800        let event = borsh::from_slice::<MeteoraDammV2SwapEvent>(&data[..SWAP_EVENT_SIZE]).ok()?;
801        Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent { metadata, ..event }))
802    }
803
804    /// 零拷贝解析器
805    #[cfg(feature = "parse-zero-copy")]
806    #[inline(always)]
807    fn parse_swap_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
808        unsafe {
809            if !check_length(data, 32 + 8 + 8) { return None; }
810            let pool = read_pubkey_unchecked(data, 0);
811            let amount_in = read_u64_unchecked(data, 32);
812            let output_amount = read_u64_unchecked(data, 40);
813            Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent {
814                metadata, pool, amount_in, output_amount, ..Default::default()
815            }))
816        }
817    }
818
819    // ============================================================================
820    // Swap2 Event
821    // ============================================================================
822
823    /// 解析 Swap2 事件(统一入口)
824    #[inline(always)]
825    fn parse_swap2(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
826        #[cfg(feature = "parse-borsh")]
827        { parse_swap2_borsh(data, metadata) }
828
829        #[cfg(feature = "parse-zero-copy")]
830        { parse_swap2_zero_copy(data, metadata) }
831    }
832
833    /// Borsh 解析器 for Swap2
834    #[cfg(feature = "parse-borsh")]
835    #[inline(always)]
836    fn parse_swap2_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
837        // Swap2 事件结构:
838        // pool(32) + config(32) + trade_direction(1) + has_referral(1) +
839        // amount_0(8) + amount_1(8) + swap_mode(1) +
840        // included_fee_input_amount(8) + excluded_fee_input_amount(8) + amount_left(8) +
841        // output_amount(8) + next_sqrt_price(16) +
842        // trading_fee(8) + protocol_fee(8) + referral_fee(8) +
843        // quote_reserve_amount(8) + migration_threshold(8) + current_timestamp(8)
844        // = 32 + 32 + 1 + 1 + 8 + 8 + 1 + 8 + 8 + 8 + 8 + 16 + 8 + 8 + 8 + 8 + 8 + 8 = 177 bytes
845        const SWAP2_EVENT_MIN_SIZE: usize = 177;
846        if data.len() < SWAP2_EVENT_MIN_SIZE { return None; }
847
848        let mut offset = 0;
849
850        // 使用 unsafe 读取以提高性能
851        unsafe {
852            let pool = read_pubkey_unchecked(data, offset);
853            offset += 32;
854
855            let _config = read_pubkey_unchecked(data, offset);
856            offset += 32;
857
858            let trade_direction = read_u8_unchecked(data, offset);
859            offset += 1;
860
861            let has_referral = read_bool_unchecked(data, offset);
862            offset += 1;
863
864            let amount_0 = read_u64_unchecked(data, offset);
865            offset += 8;
866
867            let amount_1 = read_u64_unchecked(data, offset);
868            offset += 8;
869
870            let swap_mode = read_u8_unchecked(data, offset);
871            offset += 1;
872
873            let included_fee_input_amount = read_u64_unchecked(data, offset);
874            offset += 8;
875
876            let _excluded_fee_input_amount = read_u64_unchecked(data, offset);
877            offset += 8;
878
879            let _amount_left = read_u64_unchecked(data, offset);
880            offset += 8;
881
882            let output_amount = read_u64_unchecked(data, offset);
883            offset += 8;
884
885            let next_sqrt_price = read_u128_unchecked(data, offset);
886            offset += 16;
887
888            let lp_fee = read_u64_unchecked(data, offset);
889            offset += 8;
890
891            let protocol_fee = read_u64_unchecked(data, offset);
892            offset += 8;
893
894            let referral_fee = read_u64_unchecked(data, offset);
895            offset += 8;
896
897            let _quote_reserve_amount = read_u64_unchecked(data, offset);
898            offset += 8;
899
900            let _migration_threshold = read_u64_unchecked(data, offset);
901            offset += 8;
902
903            let current_timestamp = read_u64_unchecked(data, offset);
904
905            // 根据 swap_mode 确定 amount_in 和 minimum_amount_out
906            let (amount_in, minimum_amount_out) = if swap_mode == 0 {
907                (amount_0, amount_1)
908            } else {
909                (amount_1, amount_0)
910            };
911
912            Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent {
913                metadata,
914                pool,
915                trade_direction,
916                has_referral,
917                amount_in,
918                minimum_amount_out,
919                output_amount,
920                next_sqrt_price,
921                lp_fee,
922                protocol_fee,
923                partner_fee: 0,
924                referral_fee,
925                actual_amount_in: included_fee_input_amount,
926                current_timestamp,
927                ..Default::default()
928            }))
929        }
930    }
931
932    /// 零拷贝解析器 for Swap2
933    #[cfg(feature = "parse-zero-copy")]
934    #[inline(always)]
935    fn parse_swap2_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
936        // Swap2 事件结构:
937        // pool(32) + config(32) + trade_direction(1) + has_referral(1) +
938        // amount_0(8) + amount_1(8) + swap_mode(1) +
939        // included_fee_input_amount(8) + excluded_fee_input_amount(8) + amount_left(8) +
940        // output_amount(8) + next_sqrt_price(16) +
941        // trading_fee(8) + protocol_fee(8) + referral_fee(8) +
942        // quote_reserve_amount(8) + migration_threshold(8) + current_timestamp(8)
943        const SWAP2_EVENT_MIN_SIZE: usize = 177;
944
945        unsafe {
946            if !check_length(data, SWAP2_EVENT_MIN_SIZE) { return None; }
947
948            let pool = read_pubkey_unchecked(data, 0);
949            let trade_direction = read_u8_unchecked(data, 64);
950            let has_referral = read_bool_unchecked(data, 65);
951            let amount_0 = read_u64_unchecked(data, 66);
952            let amount_1 = read_u64_unchecked(data, 74);
953            let swap_mode = read_u8_unchecked(data, 82);
954            let included_fee_input_amount = read_u64_unchecked(data, 83);
955            let output_amount = read_u64_unchecked(data, 107);
956            let next_sqrt_price = read_u128_unchecked(data, 115);
957            let lp_fee = read_u64_unchecked(data, 131);
958            let protocol_fee = read_u64_unchecked(data, 139);
959            let referral_fee = read_u64_unchecked(data, 147);
960            let current_timestamp = read_u64_unchecked(data, 169);
961
962            // 根据 swap_mode 确定 amount_in 和 minimum_amount_out
963            let (amount_in, minimum_amount_out) = if swap_mode == 0 {
964                (amount_0, amount_1)
965            } else {
966                (amount_1, amount_0)
967            };
968
969            Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent {
970                metadata,
971                pool,
972                trade_direction,
973                has_referral,
974                amount_in,
975                minimum_amount_out,
976                output_amount,
977                next_sqrt_price,
978                lp_fee,
979                protocol_fee,
980                partner_fee: 0,
981                referral_fee,
982                actual_amount_in: included_fee_input_amount,
983                current_timestamp,
984                ..Default::default()
985            }))
986        }
987    }
988
989    // ============================================================================
990    // AddLiquidity Event
991    // ============================================================================
992
993    /// 解析 AddLiquidity 事件(统一入口)
994    #[inline(always)]
995    fn parse_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
996        #[cfg(feature = "parse-borsh")]
997        { parse_add_liquidity_borsh(data, metadata) }
998
999        #[cfg(feature = "parse-zero-copy")]
1000        { parse_add_liquidity_zero_copy(data, metadata) }
1001    }
1002
1003    /// Borsh 解析器
1004    #[cfg(feature = "parse-borsh")]
1005    #[inline(always)]
1006    fn parse_add_liquidity_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1007        // 数据结构:pool(32) + position(32) + owner(32) + token_a_amount(8) + token_b_amount(8) = 112 bytes
1008        const ADD_LIQUIDITY_EVENT_SIZE: usize = 32 + 32 + 32 + 8 + 8;
1009        if data.len() < ADD_LIQUIDITY_EVENT_SIZE { return None; }
1010
1011        let event = borsh::from_slice::<MeteoraDammV2AddLiquidityEvent>(&data[..ADD_LIQUIDITY_EVENT_SIZE]).ok()?;
1012        Some(DexEvent::MeteoraDammV2AddLiquidity(MeteoraDammV2AddLiquidityEvent { metadata, ..event }))
1013    }
1014
1015    /// 零拷贝解析器
1016    #[cfg(feature = "parse-zero-copy")]
1017    #[inline(always)]
1018    fn parse_add_liquidity_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1019        unsafe {
1020            if !check_length(data, 32 + 32 + 32 + 8 + 8) { return None; }
1021            let pool = read_pubkey_unchecked(data, 0);
1022            let position = read_pubkey_unchecked(data, 32);
1023            let owner = read_pubkey_unchecked(data, 64);
1024            let token_a_amount = read_u64_unchecked(data, 96);
1025            let token_b_amount = read_u64_unchecked(data, 104);
1026            Some(DexEvent::MeteoraDammV2AddLiquidity(MeteoraDammV2AddLiquidityEvent {
1027                metadata, pool, position, owner, token_a_amount, token_b_amount,
1028                liquidity_delta: 0, token_a_amount_threshold: 0, token_b_amount_threshold: 0,
1029                total_amount_a: 0, total_amount_b: 0,
1030            }))
1031        }
1032    }
1033
1034    // ============================================================================
1035    // RemoveLiquidity Event
1036    // ============================================================================
1037
1038    /// 解析 RemoveLiquidity 事件(统一入口)
1039    #[inline(always)]
1040    fn parse_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1041        #[cfg(feature = "parse-borsh")]
1042        { parse_remove_liquidity_borsh(data, metadata) }
1043
1044        #[cfg(feature = "parse-zero-copy")]
1045        { parse_remove_liquidity_zero_copy(data, metadata) }
1046    }
1047
1048    /// Borsh 解析器
1049    #[cfg(feature = "parse-borsh")]
1050    #[inline(always)]
1051    fn parse_remove_liquidity_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1052        // 数据结构:pool(32) + position(32) + owner(32) + token_a_amount(8) + token_b_amount(8) = 112 bytes
1053        const REMOVE_LIQUIDITY_EVENT_SIZE: usize = 32 + 32 + 32 + 8 + 8;
1054        if data.len() < REMOVE_LIQUIDITY_EVENT_SIZE { return None; }
1055
1056        let event = borsh::from_slice::<MeteoraDammV2RemoveLiquidityEvent>(&data[..REMOVE_LIQUIDITY_EVENT_SIZE]).ok()?;
1057        Some(DexEvent::MeteoraDammV2RemoveLiquidity(MeteoraDammV2RemoveLiquidityEvent { metadata, ..event }))
1058    }
1059
1060    /// 零拷贝解析器
1061    #[cfg(feature = "parse-zero-copy")]
1062    #[inline(always)]
1063    fn parse_remove_liquidity_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1064        unsafe {
1065            if !check_length(data, 32 + 32 + 32 + 8 + 8) { return None; }
1066            let pool = read_pubkey_unchecked(data, 0);
1067            let position = read_pubkey_unchecked(data, 32);
1068            let owner = read_pubkey_unchecked(data, 64);
1069            let token_a_amount = read_u64_unchecked(data, 96);
1070            let token_b_amount = read_u64_unchecked(data, 104);
1071            Some(DexEvent::MeteoraDammV2RemoveLiquidity(MeteoraDammV2RemoveLiquidityEvent {
1072                metadata, pool, position, owner, token_a_amount, token_b_amount,
1073                liquidity_delta: 0, token_a_amount_threshold: 0, token_b_amount_threshold: 0,
1074            }))
1075        }
1076    }
1077
1078    // ============================================================================
1079    // CreatePosition Event
1080    // ============================================================================
1081
1082    /// 解析 CreatePosition 事件(统一入口)
1083    #[inline(always)]
1084    fn parse_create_position(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1085        #[cfg(feature = "parse-borsh")]
1086        { parse_create_position_borsh(data, metadata) }
1087
1088        #[cfg(feature = "parse-zero-copy")]
1089        { parse_create_position_zero_copy(data, metadata) }
1090    }
1091
1092    /// Borsh 解析器
1093    #[cfg(feature = "parse-borsh")]
1094    #[inline(always)]
1095    fn parse_create_position_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1096        // 数据结构:pool(32) + owner(32) + position(32) + position_nft_mint(32) = 128 bytes
1097        const CREATE_POSITION_EVENT_SIZE: usize = 32 + 32 + 32 + 32;
1098        if data.len() < CREATE_POSITION_EVENT_SIZE { return None; }
1099
1100        let event = borsh::from_slice::<MeteoraDammV2CreatePositionEvent>(&data[..CREATE_POSITION_EVENT_SIZE]).ok()?;
1101        Some(DexEvent::MeteoraDammV2CreatePosition(MeteoraDammV2CreatePositionEvent { metadata, ..event }))
1102    }
1103
1104    /// 零拷贝解析器
1105    #[cfg(feature = "parse-zero-copy")]
1106    #[inline(always)]
1107    fn parse_create_position_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1108        unsafe {
1109            if !check_length(data, 32 + 32 + 32 + 32) { return None; }
1110            let pool = read_pubkey_unchecked(data, 0);
1111            let owner = read_pubkey_unchecked(data, 32);
1112            let position = read_pubkey_unchecked(data, 64);
1113            let position_nft_mint = read_pubkey_unchecked(data, 96);
1114            Some(DexEvent::MeteoraDammV2CreatePosition(MeteoraDammV2CreatePositionEvent {
1115                metadata, pool, owner, position, position_nft_mint,
1116            }))
1117        }
1118    }
1119
1120    // ============================================================================
1121    // ClosePosition Event
1122    // ============================================================================
1123
1124    /// 解析 ClosePosition 事件(统一入口)
1125    #[inline(always)]
1126    fn parse_close_position(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1127        #[cfg(feature = "parse-borsh")]
1128        { parse_close_position_borsh(data, metadata) }
1129
1130        #[cfg(feature = "parse-zero-copy")]
1131        { parse_close_position_zero_copy(data, metadata) }
1132    }
1133
1134    /// Borsh 解析器
1135    #[cfg(feature = "parse-borsh")]
1136    #[inline(always)]
1137    fn parse_close_position_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1138        // 数据结构:pool(32) + owner(32) + position(32) + position_nft_mint(32) = 128 bytes
1139        const CLOSE_POSITION_EVENT_SIZE: usize = 32 + 32 + 32 + 32;
1140        if data.len() < CLOSE_POSITION_EVENT_SIZE { return None; }
1141
1142        let event = borsh::from_slice::<MeteoraDammV2ClosePositionEvent>(&data[..CLOSE_POSITION_EVENT_SIZE]).ok()?;
1143        Some(DexEvent::MeteoraDammV2ClosePosition(MeteoraDammV2ClosePositionEvent { metadata, ..event }))
1144    }
1145
1146    /// 零拷贝解析器
1147    #[cfg(feature = "parse-zero-copy")]
1148    #[inline(always)]
1149    fn parse_close_position_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1150        unsafe {
1151            if !check_length(data, 32 + 32 + 32 + 32) { return None; }
1152            let pool = read_pubkey_unchecked(data, 0);
1153            let owner = read_pubkey_unchecked(data, 32);
1154            let position = read_pubkey_unchecked(data, 64);
1155            let position_nft_mint = read_pubkey_unchecked(data, 96);
1156            Some(DexEvent::MeteoraDammV2ClosePosition(MeteoraDammV2ClosePositionEvent {
1157                metadata, pool, owner, position, position_nft_mint,
1158            }))
1159        }
1160    }
1161}
1162
1163// ============================================================================
1164// Bonk (Raydium Launchpad)
1165// ============================================================================
1166
1167pub mod bonk {
1168    //! Bonk (Raydium Launchpad) Inner Instruction 解析器
1169    //!
1170    //! ## 解析器插件系统
1171    //!
1172    //! 支持两种可插拔的解析器实现:
1173    //!
1174    //! ### 1. Borsh 反序列化解析器(默认,推荐)
1175    //! - **启用**: `cargo build --features parse-borsh` (默认)
1176    //! - 特点:类型安全、代码简洁、易于维护
1177    //!
1178    //! ### 2. 零拷贝解析器(高性能)
1179    //! - **启用**: `cargo build --features parse-zero-copy --no-default-features`
1180    //! - 特点:最高性能、零内存分配、直接读取内存
1181
1182    use super::*;
1183
1184    pub mod discriminators {
1185        pub const POOL_CREATE: [u8; 16] = [100, 50, 200, 150, 75, 120, 90, 30, 155, 167, 108, 32, 122, 76, 173, 64];
1186        pub const TRADE: [u8; 16] = [80, 120, 100, 200, 150, 75, 60, 40, 155, 167, 108, 32, 122, 76, 173, 64];
1187        pub const MIGRATE: [u8; 16] = [90, 130, 110, 210, 160, 85, 70, 50, 155, 167, 108, 32, 122, 76, 173, 64];
1188    }
1189
1190    /// 主入口:根据 discriminator 解析事件
1191    #[inline]
1192    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1193        match disc {
1194            &discriminators::TRADE => parse_trade(data, metadata),
1195            _ => None,
1196        }
1197    }
1198
1199    // ============================================================================
1200    // Trade Event
1201    // ============================================================================
1202
1203    /// 解析 Trade 事件(统一入口)
1204    #[inline(always)]
1205    fn parse_trade(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1206        #[cfg(feature = "parse-borsh")]
1207        { parse_trade_borsh(data, metadata) }
1208
1209        #[cfg(feature = "parse-zero-copy")]
1210        { parse_trade_zero_copy(data, metadata) }
1211    }
1212
1213    /// Borsh 解析器
1214    #[cfg(feature = "parse-borsh")]
1215    #[inline(always)]
1216    fn parse_trade_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1217        // 数据结构:pool_state(32) + user(32) + amount_in(8) + amount_out(8) + is_buy(1) = 81 bytes
1218        const TRADE_EVENT_SIZE: usize = 32 + 32 + 8 + 8 + 1;
1219        if data.len() < TRADE_EVENT_SIZE { return None; }
1220
1221        let mut event = borsh::from_slice::<BonkTradeEvent>(&data[..TRADE_EVENT_SIZE]).ok()?;
1222        event.metadata = metadata;
1223        event.trade_direction = if event.is_buy { TradeDirection::Buy } else { TradeDirection::Sell };
1224        event.exact_in = true;
1225        Some(DexEvent::BonkTrade(event))
1226    }
1227
1228    /// 零拷贝解析器
1229    #[cfg(feature = "parse-zero-copy")]
1230    #[inline(always)]
1231    fn parse_trade_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1232        unsafe {
1233            if !check_length(data, 32 + 32 + 8 + 8 + 1) { return None; }
1234            let pool_state = read_pubkey_unchecked(data, 0);
1235            let user = read_pubkey_unchecked(data, 32);
1236            let amount_in = read_u64_unchecked(data, 64);
1237            let amount_out = read_u64_unchecked(data, 72);
1238            let is_buy = read_bool_unchecked(data, 80);
1239            Some(DexEvent::BonkTrade(BonkTradeEvent {
1240                metadata, pool_state, user, amount_in, amount_out, is_buy,
1241                trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
1242                exact_in: true,
1243            }))
1244        }
1245    }
1246}
1247
1248// ============================================================================
1249// Meteora DLMM
1250// ============================================================================
1251
1252pub mod meteora_dlmm {
1253    //! Meteora DLMM Inner Instruction 解析器
1254    //!
1255    //! ## 解析器插件系统
1256    //!
1257    //! 支持两种可插拔的解析器实现:
1258    //!
1259    //! ### 1. Borsh 反序列化解析器(默认,推荐)
1260    //! - **启用**: `cargo build --features parse-borsh` (默认)
1261    //! - 特点:类型安全、代码简洁、易于维护
1262    //!
1263    //! ### 2. 零拷贝解析器(高性能)
1264    //! - **启用**: `cargo build --features parse-zero-copy --no-default-features`
1265    //! - 特点:最高性能、零内存分配、直接读取内存
1266
1267    use super::*;
1268
1269    pub mod discriminators {
1270        // 16-byte discriminators: 8-byte event hash + 8-byte magic
1271        pub const SWAP: [u8; 16] = [143, 190, 90, 218, 196, 30, 51, 222, 155, 167, 108, 32, 122, 76, 173, 64];
1272        pub const ADD_LIQUIDITY: [u8; 16] = [181, 157, 89, 67, 143, 182, 52, 72, 155, 167, 108, 32, 122, 76, 173, 64];
1273        pub const REMOVE_LIQUIDITY: [u8; 16] = [80, 85, 209, 72, 24, 206, 35, 178, 155, 167, 108, 32, 122, 76, 173, 64];
1274        pub const INITIALIZE_POOL: [u8; 16] = [95, 180, 10, 172, 84, 174, 232, 40, 155, 167, 108, 32, 122, 76, 173, 64];
1275        pub const INITIALIZE_BIN_ARRAY: [u8; 16] = [11, 18, 155, 194, 33, 115, 238, 119, 155, 167, 108, 32, 122, 76, 173, 64];
1276        pub const CREATE_POSITION: [u8; 16] = [123, 233, 11, 43, 146, 180, 97, 119, 155, 167, 108, 32, 122, 76, 173, 64];
1277        pub const CLOSE_POSITION: [u8; 16] = [94, 168, 102, 45, 59, 122, 137, 54, 155, 167, 108, 32, 122, 76, 173, 64];
1278        pub const CLAIM_FEE: [u8; 16] = [152, 70, 208, 111, 104, 91, 44, 1, 155, 167, 108, 32, 122, 76, 173, 64];
1279    }
1280
1281    /// 主入口:根据 discriminator 解析事件
1282    #[inline]
1283    pub fn parse(disc: &[u8; 16], data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1284        match disc {
1285            &discriminators::SWAP => parse_swap(data, metadata),
1286            &discriminators::ADD_LIQUIDITY => parse_add_liquidity(data, metadata),
1287            &discriminators::REMOVE_LIQUIDITY => parse_remove_liquidity(data, metadata),
1288            &discriminators::INITIALIZE_POOL => parse_initialize_pool(data, metadata),
1289            &discriminators::INITIALIZE_BIN_ARRAY => parse_initialize_bin_array(data, metadata),
1290            &discriminators::CREATE_POSITION => parse_create_position(data, metadata),
1291            &discriminators::CLOSE_POSITION => parse_close_position(data, metadata),
1292            &discriminators::CLAIM_FEE => parse_claim_fee(data, metadata),
1293            _ => None,
1294        }
1295    }
1296
1297    // ============================================================================
1298    // Swap Event
1299    // ============================================================================
1300
1301    /// 解析 Swap 事件(统一入口)
1302    #[inline(always)]
1303    fn parse_swap(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1304        #[cfg(feature = "parse-borsh")]
1305        { parse_swap_borsh(data, metadata) }
1306
1307        #[cfg(feature = "parse-zero-copy")]
1308        { parse_swap_zero_copy(data, metadata) }
1309    }
1310
1311    /// Borsh 解析器 - Swap
1312    #[cfg(feature = "parse-borsh")]
1313    #[inline(always)]
1314    fn parse_swap_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1315        // pool(32) + from(32) + start_bin_id(4) + end_bin_id(4) + amount_in(8) + amount_out(8) + swap_for_y(1) + fee(8) + protocol_fee(8) + fee_bps(16) + host_fee(8) = 129 bytes
1316        const SWAP_EVENT_SIZE: usize = 32 + 32 + 4 + 4 + 8 + 8 + 1 + 8 + 8 + 16 + 8;
1317        if data.len() < SWAP_EVENT_SIZE { return None; }
1318
1319        let mut event = borsh::from_slice::<MeteoraDlmmSwapEvent>(&data[..SWAP_EVENT_SIZE]).ok()?;
1320        event.metadata = metadata;
1321        Some(DexEvent::MeteoraDlmmSwap(event))
1322    }
1323
1324    /// 零拷贝解析器 - Swap
1325    #[cfg(feature = "parse-zero-copy")]
1326    #[inline(always)]
1327    fn parse_swap_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1328        unsafe {
1329            if !check_length(data, 32 + 32 + 4 + 4 + 8 + 8 + 1 + 8 + 8 + 16 + 8) { return None; }
1330            let pool = read_pubkey_unchecked(data, 0);
1331            let from = read_pubkey_unchecked(data, 32);
1332            let start_bin_id = read_i32_unchecked(data, 64);
1333            let end_bin_id = read_i32_unchecked(data, 68);
1334            let amount_in = read_u64_unchecked(data, 72);
1335            let amount_out = read_u64_unchecked(data, 80);
1336            let swap_for_y = read_bool_unchecked(data, 88);
1337            let fee = read_u64_unchecked(data, 89);
1338            let protocol_fee = read_u64_unchecked(data, 97);
1339            let fee_bps = read_u128_unchecked(data, 105);
1340            let host_fee = read_u64_unchecked(data, 121);
1341            Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
1342                metadata, pool, from, start_bin_id, end_bin_id, amount_in, amount_out,
1343                swap_for_y, fee, protocol_fee, fee_bps, host_fee,
1344            }))
1345        }
1346    }
1347
1348    // ============================================================================
1349    // Add Liquidity Event
1350    // ============================================================================
1351
1352    /// 解析 Add Liquidity 事件(统一入口)
1353    #[inline(always)]
1354    fn parse_add_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1355        #[cfg(feature = "parse-borsh")]
1356        { parse_add_liquidity_borsh(data, metadata) }
1357
1358        #[cfg(feature = "parse-zero-copy")]
1359        { parse_add_liquidity_zero_copy(data, metadata) }
1360    }
1361
1362    /// Borsh 解析器 - Add Liquidity
1363    #[cfg(feature = "parse-borsh")]
1364    #[inline(always)]
1365    fn parse_add_liquidity_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1366        // pool(32) + from(32) + position(32) + amounts[2](16) + active_bin_id(4) = 116 bytes
1367        const ADD_LIQUIDITY_EVENT_SIZE: usize = 32 + 32 + 32 + 16 + 4;
1368        if data.len() < ADD_LIQUIDITY_EVENT_SIZE { return None; }
1369
1370        let mut event = borsh::from_slice::<MeteoraDlmmAddLiquidityEvent>(&data[..ADD_LIQUIDITY_EVENT_SIZE]).ok()?;
1371        event.metadata = metadata;
1372        Some(DexEvent::MeteoraDlmmAddLiquidity(event))
1373    }
1374
1375    /// 零拷贝解析器 - Add Liquidity
1376    #[cfg(feature = "parse-zero-copy")]
1377    #[inline(always)]
1378    fn parse_add_liquidity_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1379        unsafe {
1380            if !check_length(data, 32 + 32 + 32 + 16 + 4) { return None; }
1381            let pool = read_pubkey_unchecked(data, 0);
1382            let from = read_pubkey_unchecked(data, 32);
1383            let position = read_pubkey_unchecked(data, 64);
1384            let amount_0 = read_u64_unchecked(data, 96);
1385            let amount_1 = read_u64_unchecked(data, 104);
1386            let active_bin_id = read_i32_unchecked(data, 112);
1387            Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
1388                metadata, pool, from, position, amounts: [amount_0, amount_1], active_bin_id,
1389            }))
1390        }
1391    }
1392
1393    // ============================================================================
1394    // Remove Liquidity Event
1395    // ============================================================================
1396
1397    /// 解析 Remove Liquidity 事件(统一入口)
1398    #[inline(always)]
1399    fn parse_remove_liquidity(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1400        #[cfg(feature = "parse-borsh")]
1401        { parse_remove_liquidity_borsh(data, metadata) }
1402
1403        #[cfg(feature = "parse-zero-copy")]
1404        { parse_remove_liquidity_zero_copy(data, metadata) }
1405    }
1406
1407    /// Borsh 解析器 - Remove Liquidity
1408    #[cfg(feature = "parse-borsh")]
1409    #[inline(always)]
1410    fn parse_remove_liquidity_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1411        // pool(32) + from(32) + position(32) + amounts[2](16) + active_bin_id(4) = 116 bytes
1412        const REMOVE_LIQUIDITY_EVENT_SIZE: usize = 32 + 32 + 32 + 16 + 4;
1413        if data.len() < REMOVE_LIQUIDITY_EVENT_SIZE { return None; }
1414
1415        let mut event = borsh::from_slice::<MeteoraDlmmRemoveLiquidityEvent>(&data[..REMOVE_LIQUIDITY_EVENT_SIZE]).ok()?;
1416        event.metadata = metadata;
1417        Some(DexEvent::MeteoraDlmmRemoveLiquidity(event))
1418    }
1419
1420    /// 零拷贝解析器 - Remove Liquidity
1421    #[cfg(feature = "parse-zero-copy")]
1422    #[inline(always)]
1423    fn parse_remove_liquidity_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1424        unsafe {
1425            if !check_length(data, 32 + 32 + 32 + 16 + 4) { return None; }
1426            let pool = read_pubkey_unchecked(data, 0);
1427            let from = read_pubkey_unchecked(data, 32);
1428            let position = read_pubkey_unchecked(data, 64);
1429            let amount_0 = read_u64_unchecked(data, 96);
1430            let amount_1 = read_u64_unchecked(data, 104);
1431            let active_bin_id = read_i32_unchecked(data, 112);
1432            Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
1433                metadata, pool, from, position, amounts: [amount_0, amount_1], active_bin_id,
1434            }))
1435        }
1436    }
1437
1438    // ============================================================================
1439    // Initialize Pool Event
1440    // ============================================================================
1441
1442    /// 解析 Initialize Pool 事件(统一入口)
1443    #[inline(always)]
1444    fn parse_initialize_pool(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1445        #[cfg(feature = "parse-borsh")]
1446        { parse_initialize_pool_borsh(data, metadata) }
1447
1448        #[cfg(feature = "parse-zero-copy")]
1449        { parse_initialize_pool_zero_copy(data, metadata) }
1450    }
1451
1452    /// Borsh 解析器 - Initialize Pool
1453    #[cfg(feature = "parse-borsh")]
1454    #[inline(always)]
1455    fn parse_initialize_pool_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1456        // pool(32) + creator(32) + active_bin_id(4) + bin_step(2) = 70 bytes
1457        const INITIALIZE_POOL_EVENT_SIZE: usize = 32 + 32 + 4 + 2;
1458        if data.len() < INITIALIZE_POOL_EVENT_SIZE { return None; }
1459
1460        let mut event = borsh::from_slice::<MeteoraDlmmInitializePoolEvent>(&data[..INITIALIZE_POOL_EVENT_SIZE]).ok()?;
1461        event.metadata = metadata;
1462        Some(DexEvent::MeteoraDlmmInitializePool(event))
1463    }
1464
1465    /// 零拷贝解析器 - Initialize Pool
1466    #[cfg(feature = "parse-zero-copy")]
1467    #[inline(always)]
1468    fn parse_initialize_pool_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1469        unsafe {
1470            if !check_length(data, 32 + 32 + 4 + 2) { return None; }
1471            let pool = read_pubkey_unchecked(data, 0);
1472            let creator = read_pubkey_unchecked(data, 32);
1473            let active_bin_id = read_i32_unchecked(data, 64);
1474            let bin_step = read_u16_unchecked(data, 68);
1475            Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
1476                metadata, pool, creator, active_bin_id, bin_step,
1477            }))
1478        }
1479    }
1480
1481    // ============================================================================
1482    // Initialize Bin Array Event
1483    // ============================================================================
1484
1485    /// 解析 Initialize Bin Array 事件(统一入口)
1486    #[inline(always)]
1487    fn parse_initialize_bin_array(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1488        #[cfg(feature = "parse-borsh")]
1489        { parse_initialize_bin_array_borsh(data, metadata) }
1490
1491        #[cfg(feature = "parse-zero-copy")]
1492        { parse_initialize_bin_array_zero_copy(data, metadata) }
1493    }
1494
1495    /// Borsh 解析器 - Initialize Bin Array
1496    #[cfg(feature = "parse-borsh")]
1497    #[inline(always)]
1498    fn parse_initialize_bin_array_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1499        // pool(32) + bin_array(32) + index(8) = 72 bytes
1500        const INITIALIZE_BIN_ARRAY_EVENT_SIZE: usize = 32 + 32 + 8;
1501        if data.len() < INITIALIZE_BIN_ARRAY_EVENT_SIZE { return None; }
1502
1503        let mut event = borsh::from_slice::<MeteoraDlmmInitializeBinArrayEvent>(&data[..INITIALIZE_BIN_ARRAY_EVENT_SIZE]).ok()?;
1504        event.metadata = metadata;
1505        Some(DexEvent::MeteoraDlmmInitializeBinArray(event))
1506    }
1507
1508    /// 零拷贝解析器 - Initialize Bin Array
1509    #[cfg(feature = "parse-zero-copy")]
1510    #[inline(always)]
1511    fn parse_initialize_bin_array_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1512        unsafe {
1513            if !check_length(data, 32 + 32 + 8) { return None; }
1514            let pool = read_pubkey_unchecked(data, 0);
1515            let bin_array = read_pubkey_unchecked(data, 32);
1516            let index = read_i64_unchecked(data, 64);
1517            Some(DexEvent::MeteoraDlmmInitializeBinArray(MeteoraDlmmInitializeBinArrayEvent {
1518                metadata, pool, bin_array, index,
1519            }))
1520        }
1521    }
1522
1523    // ============================================================================
1524    // Create Position Event
1525    // ============================================================================
1526
1527    /// 解析 Create Position 事件(统一入口)
1528    #[inline(always)]
1529    fn parse_create_position(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1530        #[cfg(feature = "parse-borsh")]
1531        { parse_create_position_borsh(data, metadata) }
1532
1533        #[cfg(feature = "parse-zero-copy")]
1534        { parse_create_position_zero_copy(data, metadata) }
1535    }
1536
1537    /// Borsh 解析器 - Create Position
1538    #[cfg(feature = "parse-borsh")]
1539    #[inline(always)]
1540    fn parse_create_position_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1541        // pool(32) + position(32) + owner(32) + lower_bin_id(4) + width(4) = 104 bytes
1542        const CREATE_POSITION_EVENT_SIZE: usize = 32 + 32 + 32 + 4 + 4;
1543        if data.len() < CREATE_POSITION_EVENT_SIZE { return None; }
1544
1545        let mut event = borsh::from_slice::<MeteoraDlmmCreatePositionEvent>(&data[..CREATE_POSITION_EVENT_SIZE]).ok()?;
1546        event.metadata = metadata;
1547        Some(DexEvent::MeteoraDlmmCreatePosition(event))
1548    }
1549
1550    /// 零拷贝解析器 - Create Position
1551    #[cfg(feature = "parse-zero-copy")]
1552    #[inline(always)]
1553    fn parse_create_position_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1554        unsafe {
1555            if !check_length(data, 32 + 32 + 32 + 4 + 4) { return None; }
1556            let pool = read_pubkey_unchecked(data, 0);
1557            let position = read_pubkey_unchecked(data, 32);
1558            let owner = read_pubkey_unchecked(data, 64);
1559            let lower_bin_id = read_i32_unchecked(data, 96);
1560            let width = read_u32_unchecked(data, 100);
1561            Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
1562                metadata, pool, position, owner, lower_bin_id, width,
1563            }))
1564        }
1565    }
1566
1567    // ============================================================================
1568    // Close Position Event
1569    // ============================================================================
1570
1571    /// 解析 Close Position 事件(统一入口)
1572    #[inline(always)]
1573    fn parse_close_position(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1574        #[cfg(feature = "parse-borsh")]
1575        { parse_close_position_borsh(data, metadata) }
1576
1577        #[cfg(feature = "parse-zero-copy")]
1578        { parse_close_position_zero_copy(data, metadata) }
1579    }
1580
1581    /// Borsh 解析器 - Close Position
1582    #[cfg(feature = "parse-borsh")]
1583    #[inline(always)]
1584    fn parse_close_position_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1585        // pool(32) + position(32) + owner(32) = 96 bytes
1586        const CLOSE_POSITION_EVENT_SIZE: usize = 32 + 32 + 32;
1587        if data.len() < CLOSE_POSITION_EVENT_SIZE { return None; }
1588
1589        let mut event = borsh::from_slice::<MeteoraDlmmClosePositionEvent>(&data[..CLOSE_POSITION_EVENT_SIZE]).ok()?;
1590        event.metadata = metadata;
1591        Some(DexEvent::MeteoraDlmmClosePosition(event))
1592    }
1593
1594    /// 零拷贝解析器 - Close Position
1595    #[cfg(feature = "parse-zero-copy")]
1596    #[inline(always)]
1597    fn parse_close_position_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1598        unsafe {
1599            if !check_length(data, 32 + 32 + 32) { return None; }
1600            let pool = read_pubkey_unchecked(data, 0);
1601            let position = read_pubkey_unchecked(data, 32);
1602            let owner = read_pubkey_unchecked(data, 64);
1603            Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
1604                metadata, pool, position, owner,
1605            }))
1606        }
1607    }
1608
1609    // ============================================================================
1610    // Claim Fee Event
1611    // ============================================================================
1612
1613    /// 解析 Claim Fee 事件(统一入口)
1614    #[inline(always)]
1615    fn parse_claim_fee(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1616        #[cfg(feature = "parse-borsh")]
1617        { parse_claim_fee_borsh(data, metadata) }
1618
1619        #[cfg(feature = "parse-zero-copy")]
1620        { parse_claim_fee_zero_copy(data, metadata) }
1621    }
1622
1623    /// Borsh 解析器 - Claim Fee
1624    #[cfg(feature = "parse-borsh")]
1625    #[inline(always)]
1626    fn parse_claim_fee_borsh(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1627        // pool(32) + position(32) + owner(32) + fee_x(8) + fee_y(8) = 112 bytes
1628        const CLAIM_FEE_EVENT_SIZE: usize = 32 + 32 + 32 + 8 + 8;
1629        if data.len() < CLAIM_FEE_EVENT_SIZE { return None; }
1630
1631        let mut event = borsh::from_slice::<MeteoraDlmmClaimFeeEvent>(&data[..CLAIM_FEE_EVENT_SIZE]).ok()?;
1632        event.metadata = metadata;
1633        Some(DexEvent::MeteoraDlmmClaimFee(event))
1634    }
1635
1636    /// 零拷贝解析器 - Claim Fee
1637    #[cfg(feature = "parse-zero-copy")]
1638    #[inline(always)]
1639    fn parse_claim_fee_zero_copy(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
1640        unsafe {
1641            if !check_length(data, 32 + 32 + 32 + 8 + 8) { return None; }
1642            let pool = read_pubkey_unchecked(data, 0);
1643            let position = read_pubkey_unchecked(data, 32);
1644            let owner = read_pubkey_unchecked(data, 64);
1645            let fee_x = read_u64_unchecked(data, 96);
1646            let fee_y = read_u64_unchecked(data, 104);
1647            Some(DexEvent::MeteoraDlmmClaimFee(MeteoraDlmmClaimFeeEvent {
1648                metadata, pool, position, owner, fee_x, fee_y,
1649            }))
1650        }
1651    }
1652}