Skip to main content

sol_parser_sdk/instr/
all_inner.rs

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