Skip to main content

sol_parser_sdk/logs/
meteora_dlmm.rs

1//! Meteora DLMM log parser
2//!
3//! Parse Meteora DLMM program log events
4
5use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::signature::Signature;
8
9/// Meteora DLMM 事件 discriminator 常量
10pub mod discriminators {
11    pub const SWAP_EVENT: [u8; 8] = [81, 108, 227, 190, 205, 208, 10, 196];
12    pub const SWAP2_EVENT: [u8; 8] = [46, 116, 82, 215, 148, 27, 84, 77];
13    pub const ADD_LIQUIDITY_EVENT: [u8; 8] = [31, 94, 125, 90, 227, 52, 61, 186];
14    pub const REMOVE_LIQUIDITY_EVENT: [u8; 8] = [116, 244, 97, 232, 103, 31, 152, 58];
15    pub const INITIALIZE_POOL_EVENT: [u8; 8] = [185, 74, 252, 125, 27, 215, 188, 111];
16    pub const CREATE_POSITION_EVENT: [u8; 8] = [144, 142, 252, 84, 157, 53, 37, 121];
17    pub const CLOSE_POSITION_EVENT: [u8; 8] = [255, 196, 16, 107, 28, 202, 53, 128];
18    pub const CLAIM_FEE_EVENT: [u8; 8] = [75, 122, 154, 48, 140, 74, 123, 163];
19    pub const CLAIM_FEE2_EVENT: [u8; 8] = [232, 171, 242, 97, 58, 77, 35, 45];
20
21    pub const LEGACY_SWAP_EVENT: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
22    pub const LEGACY_ADD_LIQUIDITY_EVENT: [u8; 8] = [181, 157, 89, 67, 143, 182, 52, 72];
23    pub const LEGACY_REMOVE_LIQUIDITY_EVENT: [u8; 8] = [80, 85, 209, 72, 24, 206, 35, 178];
24    pub const LEGACY_INITIALIZE_BIN_ARRAY_EVENT: [u8; 8] = [11, 18, 155, 194, 33, 115, 238, 119];
25    pub const LEGACY_INITIALIZE_POOL_EVENT: [u8; 8] = [95, 180, 10, 172, 84, 174, 232, 40];
26    pub const LEGACY_CREATE_POSITION_EVENT: [u8; 8] = [123, 233, 11, 43, 146, 180, 97, 119];
27    pub const LEGACY_CLOSE_POSITION_EVENT: [u8; 8] = [94, 168, 102, 45, 59, 122, 137, 54];
28    pub const LEGACY_CLAIM_FEE_EVENT: [u8; 8] = [152, 70, 208, 111, 104, 91, 44, 1];
29}
30
31/// 主要的 Meteora DLMM 日志解析函数
32pub fn parse_log(
33    log: &str,
34    signature: Signature,
35    slot: u64,
36    tx_index: u64,
37    block_time_us: Option<i64>,
38    grpc_recv_us: i64,
39) -> Option<DexEvent> {
40    parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
41}
42
43/// 解析结构化日志(基于 discriminator)
44fn parse_structured_log(
45    log: &str,
46    signature: Signature,
47    slot: u64,
48    tx_index: u64,
49    block_time_us: Option<i64>,
50    grpc_recv_us: i64,
51) -> Option<DexEvent> {
52    let program_data = extract_program_data(log)?;
53    if program_data.len() < 8 {
54        return None;
55    }
56
57    let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
58    let data = &program_data[8..];
59
60    match discriminator {
61        discriminators::SWAP_EVENT | discriminators::LEGACY_SWAP_EVENT => {
62            parse_swap_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
63        }
64        discriminators::SWAP2_EVENT => {
65            parse_swap2_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
66        }
67        discriminators::ADD_LIQUIDITY_EVENT | discriminators::LEGACY_ADD_LIQUIDITY_EVENT => {
68            parse_add_liquidity_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
69        }
70        discriminators::REMOVE_LIQUIDITY_EVENT | discriminators::LEGACY_REMOVE_LIQUIDITY_EVENT => {
71            parse_remove_liquidity_event(
72                data,
73                signature,
74                slot,
75                tx_index,
76                block_time_us,
77                grpc_recv_us,
78            )
79        }
80        discriminators::LEGACY_INITIALIZE_BIN_ARRAY_EVENT => parse_initialize_bin_array_event(
81            data,
82            signature,
83            slot,
84            tx_index,
85            block_time_us,
86            grpc_recv_us,
87        ),
88        discriminators::INITIALIZE_POOL_EVENT => {
89            parse_lb_pair_create_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
90        }
91        discriminators::LEGACY_INITIALIZE_POOL_EVENT => parse_initialize_pool_event(
92            data,
93            signature,
94            slot,
95            tx_index,
96            block_time_us,
97            grpc_recv_us,
98        ),
99        discriminators::CREATE_POSITION_EVENT => parse_position_create_event(
100            data,
101            signature,
102            slot,
103            tx_index,
104            block_time_us,
105            grpc_recv_us,
106        ),
107        discriminators::LEGACY_CREATE_POSITION_EVENT => parse_create_position_event(
108            data,
109            signature,
110            slot,
111            tx_index,
112            block_time_us,
113            grpc_recv_us,
114        ),
115        discriminators::CLOSE_POSITION_EVENT => {
116            parse_position_close_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
117        }
118        discriminators::LEGACY_CLOSE_POSITION_EVENT => {
119            parse_close_position_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
120        }
121        discriminators::CLAIM_FEE_EVENT | discriminators::LEGACY_CLAIM_FEE_EVENT => {
122            parse_claim_fee_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
123        }
124        discriminators::CLAIM_FEE2_EVENT => {
125            parse_claim_fee2_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
126        }
127        _ => None,
128    }
129}
130
131// =============================================================================
132// Public from_data parsers - Accept pre-decoded data, eliminate double decode
133// =============================================================================
134
135#[inline(always)]
136pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
137    let mut offset = 0;
138
139    let pool = read_pubkey(data, offset)?;
140    offset += 32;
141
142    let from = read_pubkey(data, offset)?;
143    offset += 32;
144
145    let start_bin_id = read_i32_le(data, offset)?;
146    offset += 4;
147
148    let end_bin_id = read_i32_le(data, offset)?;
149    offset += 4;
150
151    let amount_in = read_u64_le(data, offset)?;
152    offset += 8;
153
154    let amount_out = read_u64_le(data, offset)?;
155    offset += 8;
156
157    let swap_for_y = read_bool(data, offset)?;
158    offset += 1;
159
160    let fee = read_u64_le(data, offset)?;
161    offset += 8;
162
163    let protocol_fee = read_u64_le(data, offset)?;
164    offset += 8;
165
166    let fee_bps = read_u128_le(data, offset)?;
167    offset += 16;
168
169    let host_fee = read_u64_le(data, offset)?;
170
171    Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
172        metadata,
173        token_x_mint: solana_sdk::pubkey::Pubkey::default(),
174        token_y_mint: solana_sdk::pubkey::Pubkey::default(),
175        pool,
176        from,
177        start_bin_id,
178        end_bin_id,
179        amount_in,
180        amount_out,
181        swap_for_y,
182        fee,
183        protocol_fee,
184        fee_bps,
185        host_fee,
186    }))
187}
188
189#[inline(always)]
190pub fn parse_swap2_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
191    if data.len() < 147 {
192        return None;
193    }
194    let mut offset = 0;
195
196    let pool = read_pubkey(data, offset)?;
197    offset += 32;
198
199    let from = read_pubkey(data, offset)?;
200    offset += 32;
201
202    let start_bin_id = read_i32_le(data, offset)?;
203    offset += 4;
204
205    let end_bin_id = read_i32_le(data, offset)?;
206    offset += 4;
207
208    let swap_for_y = read_bool(data, offset)?;
209    offset += 1;
210
211    let fee_bps = read_u128_le(data, offset)?;
212    offset += 16;
213
214    let amount_in = read_u64_le(data, offset)?;
215    offset += 8;
216
217    let _amount_left = read_u64_le(data, offset)?;
218    offset += 8;
219
220    let amount_out = read_u64_le(data, offset)?;
221    offset += 8;
222
223    let fee = read_u64_le(data, offset)?;
224    offset += 8;
225
226    let protocol_fee = read_u64_le(data, offset)?;
227    offset += 8;
228
229    let _limit_order_fee = read_u64_le(data, offset)?;
230    offset += 8;
231
232    let host_fee = read_u64_le(data, offset)?;
233
234    Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
235        metadata,
236        token_x_mint: solana_sdk::pubkey::Pubkey::default(),
237        token_y_mint: solana_sdk::pubkey::Pubkey::default(),
238        pool,
239        from,
240        start_bin_id,
241        end_bin_id,
242        amount_in,
243        amount_out,
244        swap_for_y,
245        fee,
246        protocol_fee,
247        fee_bps,
248        host_fee,
249    }))
250}
251
252#[inline(always)]
253pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
254    let mut offset = 0;
255
256    let pool = read_pubkey(data, offset)?;
257    offset += 32;
258
259    let from = read_pubkey(data, offset)?;
260    offset += 32;
261
262    let position = read_pubkey(data, offset)?;
263    offset += 32;
264
265    let amount_0 = read_u64_le(data, offset)?;
266    offset += 8;
267
268    let amount_1 = read_u64_le(data, offset)?;
269    offset += 8;
270
271    let active_bin_id = read_i32_le(data, offset)?;
272
273    Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
274        metadata,
275        pool,
276        from,
277        position,
278        amounts: [amount_0, amount_1],
279        active_bin_id,
280    }))
281}
282
283#[inline(always)]
284pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
285    let mut offset = 0;
286
287    let pool = read_pubkey(data, offset)?;
288    offset += 32;
289
290    let from = read_pubkey(data, offset)?;
291    offset += 32;
292
293    let position = read_pubkey(data, offset)?;
294    offset += 32;
295
296    let amount_0 = read_u64_le(data, offset)?;
297    offset += 8;
298
299    let amount_1 = read_u64_le(data, offset)?;
300    offset += 8;
301
302    let active_bin_id = read_i32_le(data, offset)?;
303
304    Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
305        metadata,
306        pool,
307        from,
308        position,
309        amounts: [amount_0, amount_1],
310        active_bin_id,
311    }))
312}
313
314#[inline(always)]
315pub fn parse_initialize_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
316    let mut offset = 0;
317
318    let pool = read_pubkey(data, offset)?;
319    offset += 32;
320
321    let creator = read_pubkey(data, offset)?;
322    offset += 32;
323
324    let active_bin_id = read_i32_le(data, offset)?;
325    offset += 4;
326
327    let bin_step = read_u16_le(data, offset)?;
328
329    Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
330        metadata,
331        pool,
332        creator,
333        active_bin_id,
334        bin_step,
335    }))
336}
337
338#[inline(always)]
339pub fn parse_initialize_bin_array_from_data(
340    data: &[u8],
341    metadata: EventMetadata,
342) -> Option<DexEvent> {
343    let mut offset = 0;
344
345    let pool = read_pubkey(data, offset)?;
346    offset += 32;
347
348    let bin_array = read_pubkey(data, offset)?;
349    offset += 32;
350
351    let index = read_i64_le(data, offset)?;
352
353    Some(DexEvent::MeteoraDlmmInitializeBinArray(MeteoraDlmmInitializeBinArrayEvent {
354        metadata,
355        pool,
356        bin_array,
357        index,
358    }))
359}
360
361#[inline(always)]
362pub fn parse_create_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
363    let mut offset = 0;
364
365    let pool = read_pubkey(data, offset)?;
366    offset += 32;
367
368    let position = read_pubkey(data, offset)?;
369    offset += 32;
370
371    let owner = read_pubkey(data, offset)?;
372    offset += 32;
373
374    let lower_bin_id = read_i32_le(data, offset)?;
375    offset += 4;
376
377    let width = read_u32_le(data, offset)?;
378
379    Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
380        metadata,
381        pool,
382        position,
383        owner,
384        lower_bin_id,
385        width,
386    }))
387}
388
389#[inline(always)]
390pub fn parse_close_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
391    let mut offset = 0;
392
393    let pool = read_pubkey(data, offset)?;
394    offset += 32;
395
396    let position = read_pubkey(data, offset)?;
397    offset += 32;
398
399    let owner = read_pubkey(data, offset)?;
400
401    Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
402        metadata,
403        pool,
404        position,
405        owner,
406    }))
407}
408
409#[inline(always)]
410pub fn parse_claim_fee_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
411    let mut offset = 0;
412
413    let pool = read_pubkey(data, offset)?;
414    offset += 32;
415
416    let position = read_pubkey(data, offset)?;
417    offset += 32;
418
419    let owner = read_pubkey(data, offset)?;
420    offset += 32;
421
422    let fee_x = read_u64_le(data, offset)?;
423    offset += 8;
424
425    let fee_y = read_u64_le(data, offset)?;
426
427    Some(DexEvent::MeteoraDlmmClaimFee(MeteoraDlmmClaimFeeEvent {
428        metadata,
429        pool,
430        position,
431        owner,
432        fee_x,
433        fee_y,
434    }))
435}
436
437#[inline(always)]
438pub fn parse_claim_fee2_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
439    if data.len() < 116 {
440        return None;
441    }
442    parse_claim_fee_from_data(data, metadata)
443}
444
445#[inline(always)]
446pub fn parse_lb_pair_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
447    let mut offset = 0;
448
449    let pool = read_pubkey(data, offset)?;
450    offset += 32;
451
452    let bin_step = read_u16_le(data, offset)?;
453
454    Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
455        metadata,
456        pool,
457        creator: Default::default(),
458        active_bin_id: 0,
459        bin_step,
460    }))
461}
462
463#[inline(always)]
464pub fn parse_position_create_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
465    let mut offset = 0;
466
467    let pool = read_pubkey(data, offset)?;
468    offset += 32;
469
470    let position = read_pubkey(data, offset)?;
471    offset += 32;
472
473    let owner = read_pubkey(data, offset)?;
474
475    Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
476        metadata,
477        pool,
478        position,
479        owner,
480        lower_bin_id: 0,
481        width: 0,
482    }))
483}
484
485#[inline(always)]
486pub fn parse_position_close_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
487    let mut offset = 0;
488
489    let position = read_pubkey(data, offset)?;
490    offset += 32;
491
492    let owner = read_pubkey(data, offset)?;
493
494    Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
495        metadata,
496        pool: Default::default(),
497        position,
498        owner,
499    }))
500}
501
502/// 解析交换事件
503fn parse_swap_event(
504    data: &[u8],
505    signature: Signature,
506    slot: u64,
507    tx_index: u64,
508    block_time_us: Option<i64>,
509    grpc_recv_us: i64,
510) -> Option<DexEvent> {
511    let mut offset = 0;
512
513    let pool = read_pubkey(data, offset)?;
514    offset += 32;
515
516    let from = read_pubkey(data, offset)?;
517    offset += 32;
518
519    let start_bin_id = read_i32_le(data, offset)?;
520    offset += 4;
521
522    let end_bin_id = read_i32_le(data, offset)?;
523    offset += 4;
524
525    let amount_in = read_u64_le(data, offset)?;
526    offset += 8;
527
528    let amount_out = read_u64_le(data, offset)?;
529    offset += 8;
530
531    let swap_for_y = read_bool(data, offset)?;
532    offset += 1;
533
534    let fee = read_u64_le(data, offset)?;
535    offset += 8;
536
537    let protocol_fee = read_u64_le(data, offset)?;
538    offset += 8;
539
540    let fee_bps = read_u128_le(data, offset)?;
541    offset += 16;
542
543    let host_fee = read_u64_le(data, offset)?;
544
545    let metadata =
546        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
547
548    Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
549        metadata,
550        token_x_mint: solana_sdk::pubkey::Pubkey::default(),
551        token_y_mint: solana_sdk::pubkey::Pubkey::default(),
552        pool,
553        from,
554        start_bin_id,
555        end_bin_id,
556        amount_in,
557        amount_out,
558        swap_for_y,
559        fee,
560        protocol_fee,
561        fee_bps,
562        host_fee,
563    }))
564}
565
566fn parse_swap2_event(
567    data: &[u8],
568    signature: Signature,
569    slot: u64,
570    tx_index: u64,
571    block_time_us: Option<i64>,
572    grpc_recv_us: i64,
573) -> Option<DexEvent> {
574    let metadata = create_metadata_simple(
575        signature,
576        slot,
577        tx_index,
578        block_time_us,
579        Default::default(),
580        grpc_recv_us,
581    );
582    parse_swap2_from_data(data, metadata)
583}
584
585/// 解析添加流动性事件
586fn parse_add_liquidity_event(
587    data: &[u8],
588    signature: Signature,
589    slot: u64,
590    tx_index: u64,
591    block_time_us: Option<i64>,
592    grpc_recv_us: i64,
593) -> Option<DexEvent> {
594    let mut offset = 0;
595
596    let pool = read_pubkey(data, offset)?;
597    offset += 32;
598
599    let from = read_pubkey(data, offset)?;
600    offset += 32;
601
602    let position = read_pubkey(data, offset)?;
603    offset += 32;
604
605    let amount_0 = read_u64_le(data, offset)?;
606    offset += 8;
607
608    let amount_1 = read_u64_le(data, offset)?;
609    offset += 8;
610
611    let active_bin_id = read_i32_le(data, offset)?;
612
613    let metadata =
614        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
615
616    Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
617        metadata,
618        pool,
619        from,
620        position,
621        amounts: [amount_0, amount_1],
622        active_bin_id,
623    }))
624}
625
626/// 解析移除流动性事件
627fn parse_remove_liquidity_event(
628    data: &[u8],
629    signature: Signature,
630    slot: u64,
631    tx_index: u64,
632    block_time_us: Option<i64>,
633    grpc_recv_us: i64,
634) -> Option<DexEvent> {
635    let mut offset = 0;
636
637    let pool = read_pubkey(data, offset)?;
638    offset += 32;
639
640    let from = read_pubkey(data, offset)?;
641    offset += 32;
642
643    let position = read_pubkey(data, offset)?;
644    offset += 32;
645
646    let amount_0 = read_u64_le(data, offset)?;
647    offset += 8;
648
649    let amount_1 = read_u64_le(data, offset)?;
650    offset += 8;
651
652    let active_bin_id = read_i32_le(data, offset)?;
653
654    let metadata =
655        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
656
657    Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
658        metadata,
659        pool,
660        from,
661        position,
662        amounts: [amount_0, amount_1],
663        active_bin_id,
664    }))
665}
666
667/// 解析池初始化事件
668fn parse_initialize_pool_event(
669    data: &[u8],
670    signature: Signature,
671    slot: u64,
672    tx_index: u64,
673    block_time_us: Option<i64>,
674    grpc_recv_us: i64,
675) -> Option<DexEvent> {
676    let mut offset = 0;
677
678    let pool = read_pubkey(data, offset)?;
679    offset += 32;
680
681    let creator = read_pubkey(data, offset)?;
682    offset += 32;
683
684    let active_bin_id = read_i32_le(data, offset)?;
685    offset += 4;
686
687    let bin_step = read_u16_le(data, offset)?;
688
689    let metadata =
690        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
691
692    Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
693        metadata,
694        pool,
695        creator,
696        active_bin_id,
697        bin_step,
698    }))
699}
700
701/// 解析 Bin Array 初始化事件
702fn parse_initialize_bin_array_event(
703    data: &[u8],
704    signature: Signature,
705    slot: u64,
706    tx_index: u64,
707    block_time_us: Option<i64>,
708    grpc_recv_us: i64,
709) -> Option<DexEvent> {
710    let mut offset = 0;
711
712    let pool = read_pubkey(data, offset)?;
713    offset += 32;
714
715    let bin_array = read_pubkey(data, offset)?;
716    offset += 32;
717
718    let index = read_i64_le(data, offset)?;
719
720    let metadata =
721        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
722
723    Some(DexEvent::MeteoraDlmmInitializeBinArray(MeteoraDlmmInitializeBinArrayEvent {
724        metadata,
725        pool,
726        bin_array,
727        index,
728    }))
729}
730
731/// 解析创建头寸事件
732fn parse_create_position_event(
733    data: &[u8],
734    signature: Signature,
735    slot: u64,
736    tx_index: u64,
737    block_time_us: Option<i64>,
738    grpc_recv_us: i64,
739) -> Option<DexEvent> {
740    let mut offset = 0;
741
742    let pool = read_pubkey(data, offset)?;
743    offset += 32;
744
745    let position = read_pubkey(data, offset)?;
746    offset += 32;
747
748    let owner = read_pubkey(data, offset)?;
749    offset += 32;
750
751    let lower_bin_id = read_i32_le(data, offset)?;
752    offset += 4;
753
754    let width = read_u32_le(data, offset)?;
755
756    let metadata =
757        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
758
759    Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
760        metadata,
761        pool,
762        position,
763        owner,
764        lower_bin_id,
765        width,
766    }))
767}
768
769/// 解析关闭头寸事件
770fn parse_close_position_event(
771    data: &[u8],
772    signature: Signature,
773    slot: u64,
774    tx_index: u64,
775    block_time_us: Option<i64>,
776    grpc_recv_us: i64,
777) -> Option<DexEvent> {
778    let mut offset = 0;
779
780    let pool = read_pubkey(data, offset)?;
781    offset += 32;
782
783    let position = read_pubkey(data, offset)?;
784    offset += 32;
785
786    let owner = read_pubkey(data, offset)?;
787
788    let metadata =
789        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
790
791    Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
792        metadata,
793        pool,
794        position,
795        owner,
796    }))
797}
798
799/// 解析费用领取事件
800fn parse_claim_fee_event(
801    data: &[u8],
802    signature: Signature,
803    slot: u64,
804    tx_index: u64,
805    block_time_us: Option<i64>,
806    grpc_recv_us: i64,
807) -> Option<DexEvent> {
808    let mut offset = 0;
809
810    let pool = read_pubkey(data, offset)?;
811    offset += 32;
812
813    let position = read_pubkey(data, offset)?;
814    offset += 32;
815
816    let owner = read_pubkey(data, offset)?;
817    offset += 32;
818
819    let fee_x = read_u64_le(data, offset)?;
820    offset += 8;
821
822    let fee_y = read_u64_le(data, offset)?;
823
824    let metadata =
825        create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
826
827    Some(DexEvent::MeteoraDlmmClaimFee(MeteoraDlmmClaimFeeEvent {
828        metadata,
829        pool,
830        position,
831        owner,
832        fee_x,
833        fee_y,
834    }))
835}
836
837fn parse_claim_fee2_event(
838    data: &[u8],
839    signature: Signature,
840    slot: u64,
841    tx_index: u64,
842    block_time_us: Option<i64>,
843    grpc_recv_us: i64,
844) -> Option<DexEvent> {
845    let metadata = create_metadata_simple(
846        signature,
847        slot,
848        tx_index,
849        block_time_us,
850        Default::default(),
851        grpc_recv_us,
852    );
853    parse_claim_fee2_from_data(data, metadata)
854}
855
856fn parse_lb_pair_create_event(
857    data: &[u8],
858    signature: Signature,
859    slot: u64,
860    tx_index: u64,
861    block_time_us: Option<i64>,
862    grpc_recv_us: i64,
863) -> Option<DexEvent> {
864    let metadata = create_metadata_simple(
865        signature,
866        slot,
867        tx_index,
868        block_time_us,
869        Default::default(),
870        grpc_recv_us,
871    );
872    parse_lb_pair_create_from_data(data, metadata)
873}
874
875fn parse_position_create_event(
876    data: &[u8],
877    signature: Signature,
878    slot: u64,
879    tx_index: u64,
880    block_time_us: Option<i64>,
881    grpc_recv_us: i64,
882) -> Option<DexEvent> {
883    let metadata = create_metadata_simple(
884        signature,
885        slot,
886        tx_index,
887        block_time_us,
888        Default::default(),
889        grpc_recv_us,
890    );
891    parse_position_create_from_data(data, metadata)
892}
893
894fn parse_position_close_event(
895    data: &[u8],
896    signature: Signature,
897    slot: u64,
898    tx_index: u64,
899    block_time_us: Option<i64>,
900    grpc_recv_us: i64,
901) -> Option<DexEvent> {
902    let metadata = create_metadata_simple(
903        signature,
904        slot,
905        tx_index,
906        block_time_us,
907        Default::default(),
908        grpc_recv_us,
909    );
910    parse_position_close_from_data(data, metadata)
911}
912
913/// 文本回退解析
914fn parse_text_log(
915    log: &str,
916    signature: Signature,
917    slot: u64,
918    tx_index: u64,
919    block_time_us: Option<i64>,
920    grpc_recv_us: i64,
921) -> Option<DexEvent> {
922    use super::utils::text_parser::*;
923
924    if log.contains("swap") || log.contains("Swap") {
925        return parse_swap_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
926    }
927
928    if log.contains("add") && log.contains("liquidity") {
929        return parse_add_liquidity_from_text(
930            log,
931            signature,
932            slot,
933            tx_index,
934            block_time_us,
935            grpc_recv_us,
936        );
937    }
938
939    if log.contains("remove") && log.contains("liquidity") {
940        return parse_remove_liquidity_from_text(
941            log,
942            signature,
943            slot,
944            tx_index,
945            block_time_us,
946            grpc_recv_us,
947        );
948    }
949
950    if log.contains("initialize") && log.contains("pool") {
951        return parse_initialize_pool_from_text(
952            log,
953            signature,
954            slot,
955            tx_index,
956            block_time_us,
957            grpc_recv_us,
958        );
959    }
960
961    None
962}
963
964/// 从文本解析交换事件
965fn parse_swap_from_text(
966    log: &str,
967    signature: Signature,
968    slot: u64,
969    tx_index: u64,
970    block_time_us: Option<i64>,
971    grpc_recv_us: i64,
972) -> Option<DexEvent> {
973    use super::utils::text_parser::*;
974
975    let metadata = create_metadata_simple(
976        signature,
977        slot,
978        tx_index,
979        block_time_us,
980        solana_sdk::pubkey::Pubkey::default(),
981        grpc_recv_us,
982    );
983
984    Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
985        metadata,
986        token_x_mint: solana_sdk::pubkey::Pubkey::default(),
987        token_y_mint: solana_sdk::pubkey::Pubkey::default(),
988        pool: solana_sdk::pubkey::Pubkey::default(),
989        from: solana_sdk::pubkey::Pubkey::default(),
990        start_bin_id: 0,
991        end_bin_id: 0,
992        amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000),
993        amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950_000),
994        swap_for_y: detect_trade_type(log).unwrap_or(true),
995        fee: extract_number_from_text(log, "fee").unwrap_or(3000),
996        protocol_fee: 0,
997        fee_bps: 0,
998        host_fee: 0,
999    }))
1000}
1001
1002/// 从文本解析添加流动性事件
1003fn parse_add_liquidity_from_text(
1004    log: &str,
1005    signature: Signature,
1006    slot: u64,
1007    tx_index: u64,
1008    block_time_us: Option<i64>,
1009    grpc_recv_us: i64,
1010) -> Option<DexEvent> {
1011    use super::utils::text_parser::*;
1012
1013    let metadata = create_metadata_simple(
1014        signature,
1015        slot,
1016        tx_index,
1017        block_time_us,
1018        solana_sdk::pubkey::Pubkey::default(),
1019        grpc_recv_us,
1020    );
1021
1022    Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
1023        metadata,
1024        pool: solana_sdk::pubkey::Pubkey::default(),
1025        from: solana_sdk::pubkey::Pubkey::default(),
1026        position: solana_sdk::pubkey::Pubkey::default(),
1027        amounts: [
1028            extract_number_from_text(log, "amount_x").unwrap_or(500_000),
1029            extract_number_from_text(log, "amount_y").unwrap_or(500_000),
1030        ],
1031        active_bin_id: 0,
1032    }))
1033}
1034
1035/// 从文本解析移除流动性事件
1036fn parse_remove_liquidity_from_text(
1037    log: &str,
1038    signature: Signature,
1039    slot: u64,
1040    tx_index: u64,
1041    block_time_us: Option<i64>,
1042    grpc_recv_us: i64,
1043) -> Option<DexEvent> {
1044    use super::utils::text_parser::*;
1045
1046    let metadata = create_metadata_simple(
1047        signature,
1048        slot,
1049        tx_index,
1050        block_time_us,
1051        solana_sdk::pubkey::Pubkey::default(),
1052        grpc_recv_us,
1053    );
1054
1055    Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
1056        metadata,
1057        pool: solana_sdk::pubkey::Pubkey::default(),
1058        from: solana_sdk::pubkey::Pubkey::default(),
1059        position: solana_sdk::pubkey::Pubkey::default(),
1060        amounts: [
1061            extract_number_from_text(log, "amount_x").unwrap_or(500_000),
1062            extract_number_from_text(log, "amount_y").unwrap_or(500_000),
1063        ],
1064        active_bin_id: 0,
1065    }))
1066}
1067
1068/// 从文本解析池初始化事件
1069fn parse_initialize_pool_from_text(
1070    log: &str,
1071    signature: Signature,
1072    slot: u64,
1073    tx_index: u64,
1074    block_time_us: Option<i64>,
1075    grpc_recv_us: i64,
1076) -> Option<DexEvent> {
1077    use super::utils::text_parser::*;
1078
1079    let metadata = create_metadata_simple(
1080        signature,
1081        slot,
1082        tx_index,
1083        block_time_us,
1084        solana_sdk::pubkey::Pubkey::default(),
1085        grpc_recv_us,
1086    );
1087
1088    Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
1089        metadata,
1090        pool: solana_sdk::pubkey::Pubkey::default(),
1091        creator: solana_sdk::pubkey::Pubkey::default(),
1092        active_bin_id: extract_number_from_text(log, "bin_id").unwrap_or(0) as i32,
1093        bin_step: extract_number_from_text(log, "bin_step").unwrap_or(1) as u16,
1094    }))
1095}