1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::signature::Signature;
8
9pub mod discriminators {
11 pub const SWAP_EVENT: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
12 pub const ADD_LIQUIDITY_EVENT: [u8; 8] = [181, 157, 89, 67, 143, 182, 52, 72];
13 pub const REMOVE_LIQUIDITY_EVENT: [u8; 8] = [80, 85, 209, 72, 24, 206, 35, 178];
14 pub const INITIALIZE_BIN_ARRAY_EVENT: [u8; 8] = [11, 18, 155, 194, 33, 115, 238, 119];
15 pub const INITIALIZE_POOL_EVENT: [u8; 8] = [95, 180, 10, 172, 84, 174, 232, 40];
16 pub const CREATE_POSITION_EVENT: [u8; 8] = [123, 233, 11, 43, 146, 180, 97, 119];
17 pub const CLOSE_POSITION_EVENT: [u8; 8] = [94, 168, 102, 45, 59, 122, 137, 54];
18 pub const CLAIM_FEE_EVENT: [u8; 8] = [152, 70, 208, 111, 104, 91, 44, 1];
19}
20
21pub fn parse_log(
23 log: &str,
24 signature: Signature,
25 slot: u64,
26 tx_index: u64,
27 block_time_us: Option<i64>,
28 grpc_recv_us: i64,
29) -> Option<DexEvent> {
30 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
31}
32
33fn parse_structured_log(
35 log: &str,
36 signature: Signature,
37 slot: u64,
38 tx_index: u64,
39 block_time_us: Option<i64>,
40 grpc_recv_us: i64,
41) -> Option<DexEvent> {
42 let program_data = extract_program_data(log)?;
43 if program_data.len() < 8 {
44 return None;
45 }
46
47 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
48 let data = &program_data[8..];
49
50 match discriminator {
51 discriminators::SWAP_EVENT => {
52 parse_swap_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
53 }
54 discriminators::ADD_LIQUIDITY_EVENT => {
55 parse_add_liquidity_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
56 }
57 discriminators::REMOVE_LIQUIDITY_EVENT => parse_remove_liquidity_event(
58 data,
59 signature,
60 slot,
61 tx_index,
62 block_time_us,
63 grpc_recv_us,
64 ),
65 discriminators::INITIALIZE_BIN_ARRAY_EVENT => parse_initialize_bin_array_event(
66 data,
67 signature,
68 slot,
69 tx_index,
70 block_time_us,
71 grpc_recv_us,
72 ),
73 discriminators::INITIALIZE_POOL_EVENT => parse_initialize_pool_event(
74 data,
75 signature,
76 slot,
77 tx_index,
78 block_time_us,
79 grpc_recv_us,
80 ),
81 discriminators::CREATE_POSITION_EVENT => parse_create_position_event(
82 data,
83 signature,
84 slot,
85 tx_index,
86 block_time_us,
87 grpc_recv_us,
88 ),
89 discriminators::CLOSE_POSITION_EVENT => {
90 parse_close_position_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
91 }
92 discriminators::CLAIM_FEE_EVENT => {
93 parse_claim_fee_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
94 }
95 _ => None,
96 }
97}
98
99#[inline(always)]
104pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
105 let mut offset = 0;
106
107 let pool = read_pubkey(data, offset)?;
108 offset += 32;
109
110 let from = read_pubkey(data, offset)?;
111 offset += 32;
112
113 let start_bin_id = read_i32_le(data, offset)?;
114 offset += 4;
115
116 let end_bin_id = read_i32_le(data, offset)?;
117 offset += 4;
118
119 let amount_in = read_u64_le(data, offset)?;
120 offset += 8;
121
122 let amount_out = read_u64_le(data, offset)?;
123 offset += 8;
124
125 let swap_for_y = read_bool(data, offset)?;
126 offset += 1;
127
128 let fee = read_u64_le(data, offset)?;
129 offset += 8;
130
131 let protocol_fee = read_u64_le(data, offset)?;
132 offset += 8;
133
134 let fee_bps = read_u128_le(data, offset)?;
135 offset += 16;
136
137 let host_fee = read_u64_le(data, offset)?;
138
139 Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
140 metadata,
141 pool,
142 from,
143 start_bin_id,
144 end_bin_id,
145 amount_in,
146 amount_out,
147 swap_for_y,
148 fee,
149 protocol_fee,
150 fee_bps,
151 host_fee,
152 }))
153}
154
155#[inline(always)]
156pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
157 let mut offset = 0;
158
159 let pool = read_pubkey(data, offset)?;
160 offset += 32;
161
162 let from = read_pubkey(data, offset)?;
163 offset += 32;
164
165 let position = read_pubkey(data, offset)?;
166 offset += 32;
167
168 let amount_0 = read_u64_le(data, offset)?;
169 offset += 8;
170
171 let amount_1 = read_u64_le(data, offset)?;
172 offset += 8;
173
174 let active_bin_id = read_i32_le(data, offset)?;
175
176 Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
177 metadata,
178 pool,
179 from,
180 position,
181 amounts: [amount_0, amount_1],
182 active_bin_id,
183 }))
184}
185
186#[inline(always)]
187pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
188 let mut offset = 0;
189
190 let pool = read_pubkey(data, offset)?;
191 offset += 32;
192
193 let from = read_pubkey(data, offset)?;
194 offset += 32;
195
196 let position = read_pubkey(data, offset)?;
197 offset += 32;
198
199 let amount_0 = read_u64_le(data, offset)?;
200 offset += 8;
201
202 let amount_1 = read_u64_le(data, offset)?;
203 offset += 8;
204
205 let active_bin_id = read_i32_le(data, offset)?;
206
207 Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
208 metadata,
209 pool,
210 from,
211 position,
212 amounts: [amount_0, amount_1],
213 active_bin_id,
214 }))
215}
216
217#[inline(always)]
218pub fn parse_initialize_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
219 let mut offset = 0;
220
221 let pool = read_pubkey(data, offset)?;
222 offset += 32;
223
224 let creator = read_pubkey(data, offset)?;
225 offset += 32;
226
227 let active_bin_id = read_i32_le(data, offset)?;
228 offset += 4;
229
230 let bin_step = read_u16_le(data, offset)?;
231
232 Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
233 metadata,
234 pool,
235 creator,
236 active_bin_id,
237 bin_step,
238 }))
239}
240
241#[inline(always)]
242pub fn parse_initialize_bin_array_from_data(
243 data: &[u8],
244 metadata: EventMetadata,
245) -> Option<DexEvent> {
246 let mut offset = 0;
247
248 let pool = read_pubkey(data, offset)?;
249 offset += 32;
250
251 let bin_array = read_pubkey(data, offset)?;
252 offset += 32;
253
254 let index = read_i64_le(data, offset)?;
255
256 Some(DexEvent::MeteoraDlmmInitializeBinArray(MeteoraDlmmInitializeBinArrayEvent {
257 metadata,
258 pool,
259 bin_array,
260 index,
261 }))
262}
263
264#[inline(always)]
265pub fn parse_create_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
266 let mut offset = 0;
267
268 let pool = read_pubkey(data, offset)?;
269 offset += 32;
270
271 let position = read_pubkey(data, offset)?;
272 offset += 32;
273
274 let owner = read_pubkey(data, offset)?;
275 offset += 32;
276
277 let lower_bin_id = read_i32_le(data, offset)?;
278 offset += 4;
279
280 let width = read_u32_le(data, offset)?;
281
282 Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
283 metadata,
284 pool,
285 position,
286 owner,
287 lower_bin_id,
288 width,
289 }))
290}
291
292#[inline(always)]
293pub fn parse_close_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
294 let mut offset = 0;
295
296 let pool = read_pubkey(data, offset)?;
297 offset += 32;
298
299 let position = read_pubkey(data, offset)?;
300 offset += 32;
301
302 let owner = read_pubkey(data, offset)?;
303
304 Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
305 metadata,
306 pool,
307 position,
308 owner,
309 }))
310}
311
312#[inline(always)]
313pub fn parse_claim_fee_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
314 let mut offset = 0;
315
316 let pool = read_pubkey(data, offset)?;
317 offset += 32;
318
319 let position = read_pubkey(data, offset)?;
320 offset += 32;
321
322 let owner = read_pubkey(data, offset)?;
323 offset += 32;
324
325 let fee_x = read_u64_le(data, offset)?;
326 offset += 8;
327
328 let fee_y = read_u64_le(data, offset)?;
329
330 Some(DexEvent::MeteoraDlmmClaimFee(MeteoraDlmmClaimFeeEvent {
331 metadata,
332 pool,
333 position,
334 owner,
335 fee_x,
336 fee_y,
337 }))
338}
339
340fn parse_swap_event(
342 data: &[u8],
343 signature: Signature,
344 slot: u64,
345 tx_index: u64,
346 block_time_us: Option<i64>,
347 grpc_recv_us: i64,
348) -> Option<DexEvent> {
349 let mut offset = 0;
350
351 let pool = read_pubkey(data, offset)?;
352 offset += 32;
353
354 let from = read_pubkey(data, offset)?;
355 offset += 32;
356
357 let start_bin_id = read_i32_le(data, offset)?;
358 offset += 4;
359
360 let end_bin_id = read_i32_le(data, offset)?;
361 offset += 4;
362
363 let amount_in = read_u64_le(data, offset)?;
364 offset += 8;
365
366 let amount_out = read_u64_le(data, offset)?;
367 offset += 8;
368
369 let swap_for_y = read_bool(data, offset)?;
370 offset += 1;
371
372 let fee = read_u64_le(data, offset)?;
373 offset += 8;
374
375 let protocol_fee = read_u64_le(data, offset)?;
376 offset += 8;
377
378 let fee_bps = read_u128_le(data, offset)?;
379 offset += 16;
380
381 let host_fee = read_u64_le(data, offset)?;
382
383 let metadata =
384 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
385
386 Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
387 metadata,
388 pool,
389 from,
390 start_bin_id,
391 end_bin_id,
392 amount_in,
393 amount_out,
394 swap_for_y,
395 fee,
396 protocol_fee,
397 fee_bps,
398 host_fee,
399 }))
400}
401
402fn parse_add_liquidity_event(
404 data: &[u8],
405 signature: Signature,
406 slot: u64,
407 tx_index: u64,
408 block_time_us: Option<i64>,
409 grpc_recv_us: i64,
410) -> Option<DexEvent> {
411 let mut offset = 0;
412
413 let pool = read_pubkey(data, offset)?;
414 offset += 32;
415
416 let from = read_pubkey(data, offset)?;
417 offset += 32;
418
419 let position = read_pubkey(data, offset)?;
420 offset += 32;
421
422 let amount_0 = read_u64_le(data, offset)?;
423 offset += 8;
424
425 let amount_1 = read_u64_le(data, offset)?;
426 offset += 8;
427
428 let active_bin_id = read_i32_le(data, offset)?;
429
430 let metadata =
431 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
432
433 Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
434 metadata,
435 pool,
436 from,
437 position,
438 amounts: [amount_0, amount_1],
439 active_bin_id,
440 }))
441}
442
443fn parse_remove_liquidity_event(
445 data: &[u8],
446 signature: Signature,
447 slot: u64,
448 tx_index: u64,
449 block_time_us: Option<i64>,
450 grpc_recv_us: i64,
451) -> Option<DexEvent> {
452 let mut offset = 0;
453
454 let pool = read_pubkey(data, offset)?;
455 offset += 32;
456
457 let from = read_pubkey(data, offset)?;
458 offset += 32;
459
460 let position = read_pubkey(data, offset)?;
461 offset += 32;
462
463 let amount_0 = read_u64_le(data, offset)?;
464 offset += 8;
465
466 let amount_1 = read_u64_le(data, offset)?;
467 offset += 8;
468
469 let active_bin_id = read_i32_le(data, offset)?;
470
471 let metadata =
472 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
473
474 Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
475 metadata,
476 pool,
477 from,
478 position,
479 amounts: [amount_0, amount_1],
480 active_bin_id,
481 }))
482}
483
484fn parse_initialize_pool_event(
486 data: &[u8],
487 signature: Signature,
488 slot: u64,
489 tx_index: u64,
490 block_time_us: Option<i64>,
491 grpc_recv_us: i64,
492) -> Option<DexEvent> {
493 let mut offset = 0;
494
495 let pool = read_pubkey(data, offset)?;
496 offset += 32;
497
498 let creator = read_pubkey(data, offset)?;
499 offset += 32;
500
501 let active_bin_id = read_i32_le(data, offset)?;
502 offset += 4;
503
504 let bin_step = read_u16_le(data, offset)?;
505
506 let metadata =
507 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
508
509 Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
510 metadata,
511 pool,
512 creator,
513 active_bin_id,
514 bin_step,
515 }))
516}
517
518fn parse_initialize_bin_array_event(
520 data: &[u8],
521 signature: Signature,
522 slot: u64,
523 tx_index: u64,
524 block_time_us: Option<i64>,
525 grpc_recv_us: i64,
526) -> Option<DexEvent> {
527 let mut offset = 0;
528
529 let pool = read_pubkey(data, offset)?;
530 offset += 32;
531
532 let bin_array = read_pubkey(data, offset)?;
533 offset += 32;
534
535 let index = read_i64_le(data, offset)?;
536
537 let metadata =
538 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
539
540 Some(DexEvent::MeteoraDlmmInitializeBinArray(MeteoraDlmmInitializeBinArrayEvent {
541 metadata,
542 pool,
543 bin_array,
544 index,
545 }))
546}
547
548fn parse_create_position_event(
550 data: &[u8],
551 signature: Signature,
552 slot: u64,
553 tx_index: u64,
554 block_time_us: Option<i64>,
555 grpc_recv_us: i64,
556) -> Option<DexEvent> {
557 let mut offset = 0;
558
559 let pool = read_pubkey(data, offset)?;
560 offset += 32;
561
562 let position = read_pubkey(data, offset)?;
563 offset += 32;
564
565 let owner = read_pubkey(data, offset)?;
566 offset += 32;
567
568 let lower_bin_id = read_i32_le(data, offset)?;
569 offset += 4;
570
571 let width = read_u32_le(data, offset)?;
572
573 let metadata =
574 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
575
576 Some(DexEvent::MeteoraDlmmCreatePosition(MeteoraDlmmCreatePositionEvent {
577 metadata,
578 pool,
579 position,
580 owner,
581 lower_bin_id,
582 width,
583 }))
584}
585
586fn parse_close_position_event(
588 data: &[u8],
589 signature: Signature,
590 slot: u64,
591 tx_index: u64,
592 block_time_us: Option<i64>,
593 grpc_recv_us: i64,
594) -> Option<DexEvent> {
595 let mut offset = 0;
596
597 let pool = read_pubkey(data, offset)?;
598 offset += 32;
599
600 let position = read_pubkey(data, offset)?;
601 offset += 32;
602
603 let owner = read_pubkey(data, offset)?;
604
605 let metadata =
606 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
607
608 Some(DexEvent::MeteoraDlmmClosePosition(MeteoraDlmmClosePositionEvent {
609 metadata,
610 pool,
611 position,
612 owner,
613 }))
614}
615
616fn parse_claim_fee_event(
618 data: &[u8],
619 signature: Signature,
620 slot: u64,
621 tx_index: u64,
622 block_time_us: Option<i64>,
623 grpc_recv_us: i64,
624) -> Option<DexEvent> {
625 let mut offset = 0;
626
627 let pool = read_pubkey(data, offset)?;
628 offset += 32;
629
630 let position = read_pubkey(data, offset)?;
631 offset += 32;
632
633 let owner = read_pubkey(data, offset)?;
634 offset += 32;
635
636 let fee_x = read_u64_le(data, offset)?;
637 offset += 8;
638
639 let fee_y = read_u64_le(data, offset)?;
640
641 let metadata =
642 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
643
644 Some(DexEvent::MeteoraDlmmClaimFee(MeteoraDlmmClaimFeeEvent {
645 metadata,
646 pool,
647 position,
648 owner,
649 fee_x,
650 fee_y,
651 }))
652}
653
654fn parse_text_log(
656 log: &str,
657 signature: Signature,
658 slot: u64,
659 tx_index: u64,
660 block_time_us: Option<i64>,
661 grpc_recv_us: i64,
662) -> Option<DexEvent> {
663 use super::utils::text_parser::*;
664
665 if log.contains("swap") || log.contains("Swap") {
666 return parse_swap_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
667 }
668
669 if log.contains("add") && log.contains("liquidity") {
670 return parse_add_liquidity_from_text(
671 log,
672 signature,
673 slot,
674 tx_index,
675 block_time_us,
676 grpc_recv_us,
677 );
678 }
679
680 if log.contains("remove") && log.contains("liquidity") {
681 return parse_remove_liquidity_from_text(
682 log,
683 signature,
684 slot,
685 tx_index,
686 block_time_us,
687 grpc_recv_us,
688 );
689 }
690
691 if log.contains("initialize") && log.contains("pool") {
692 return parse_initialize_pool_from_text(
693 log,
694 signature,
695 slot,
696 tx_index,
697 block_time_us,
698 grpc_recv_us,
699 );
700 }
701
702 None
703}
704
705fn parse_swap_from_text(
707 log: &str,
708 signature: Signature,
709 slot: u64,
710 tx_index: u64,
711 block_time_us: Option<i64>,
712 grpc_recv_us: i64,
713) -> Option<DexEvent> {
714 use super::utils::text_parser::*;
715
716 let metadata = create_metadata_simple(
717 signature,
718 slot,
719 tx_index,
720 block_time_us,
721 solana_sdk::pubkey::Pubkey::default(),
722 grpc_recv_us,
723 );
724
725 Some(DexEvent::MeteoraDlmmSwap(MeteoraDlmmSwapEvent {
726 metadata,
727 pool: solana_sdk::pubkey::Pubkey::default(),
728 from: solana_sdk::pubkey::Pubkey::default(),
729 start_bin_id: 0,
730 end_bin_id: 0,
731 amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000),
732 amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950_000),
733 swap_for_y: detect_trade_type(log).unwrap_or(true),
734 fee: extract_number_from_text(log, "fee").unwrap_or(3000),
735 protocol_fee: 0,
736 fee_bps: 0,
737 host_fee: 0,
738 }))
739}
740
741fn parse_add_liquidity_from_text(
743 log: &str,
744 signature: Signature,
745 slot: u64,
746 tx_index: u64,
747 block_time_us: Option<i64>,
748 grpc_recv_us: i64,
749) -> Option<DexEvent> {
750 use super::utils::text_parser::*;
751
752 let metadata = create_metadata_simple(
753 signature,
754 slot,
755 tx_index,
756 block_time_us,
757 solana_sdk::pubkey::Pubkey::default(),
758 grpc_recv_us,
759 );
760
761 Some(DexEvent::MeteoraDlmmAddLiquidity(MeteoraDlmmAddLiquidityEvent {
762 metadata,
763 pool: solana_sdk::pubkey::Pubkey::default(),
764 from: solana_sdk::pubkey::Pubkey::default(),
765 position: solana_sdk::pubkey::Pubkey::default(),
766 amounts: [
767 extract_number_from_text(log, "amount_x").unwrap_or(500_000),
768 extract_number_from_text(log, "amount_y").unwrap_or(500_000),
769 ],
770 active_bin_id: 0,
771 }))
772}
773
774fn parse_remove_liquidity_from_text(
776 log: &str,
777 signature: Signature,
778 slot: u64,
779 tx_index: u64,
780 block_time_us: Option<i64>,
781 grpc_recv_us: i64,
782) -> Option<DexEvent> {
783 use super::utils::text_parser::*;
784
785 let metadata = create_metadata_simple(
786 signature,
787 slot,
788 tx_index,
789 block_time_us,
790 solana_sdk::pubkey::Pubkey::default(),
791 grpc_recv_us,
792 );
793
794 Some(DexEvent::MeteoraDlmmRemoveLiquidity(MeteoraDlmmRemoveLiquidityEvent {
795 metadata,
796 pool: solana_sdk::pubkey::Pubkey::default(),
797 from: solana_sdk::pubkey::Pubkey::default(),
798 position: solana_sdk::pubkey::Pubkey::default(),
799 amounts: [
800 extract_number_from_text(log, "amount_x").unwrap_or(500_000),
801 extract_number_from_text(log, "amount_y").unwrap_or(500_000),
802 ],
803 active_bin_id: 0,
804 }))
805}
806
807fn parse_initialize_pool_from_text(
809 log: &str,
810 signature: Signature,
811 slot: u64,
812 tx_index: u64,
813 block_time_us: Option<i64>,
814 grpc_recv_us: i64,
815) -> Option<DexEvent> {
816 use super::utils::text_parser::*;
817
818 let metadata = create_metadata_simple(
819 signature,
820 slot,
821 tx_index,
822 block_time_us,
823 solana_sdk::pubkey::Pubkey::default(),
824 grpc_recv_us,
825 );
826
827 Some(DexEvent::MeteoraDlmmInitializePool(MeteoraDlmmInitializePoolEvent {
828 metadata,
829 pool: solana_sdk::pubkey::Pubkey::default(),
830 creator: solana_sdk::pubkey::Pubkey::default(),
831 active_bin_id: extract_number_from_text(log, "bin_id").unwrap_or(0) as i32,
832 bin_step: extract_number_from_text(log, "bin_step").unwrap_or(1) as u16,
833 }))
834}