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