1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9pub mod discriminators {
11 pub const SWAP_EVENT: [u8; 8] = [64, 198, 205, 232, 38, 8, 113, 226];
12 pub const SWAP_BASE_IN: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
13 pub const SWAP_BASE_OUT: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173];
14 pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
15 pub const DEPOSIT: [u8; 8] = [242, 35, 198, 137, 82, 225, 242, 182];
16 pub const WITHDRAW: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
17}
18
19pub const PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
21
22pub fn is_raydium_cpmm_log(log: &str) -> bool {
24 log.contains(&format!("Program {} invoke", PROGRAM_ID))
25 || log.contains(&format!("Program {} success", PROGRAM_ID))
26 || (log.contains("raydium") && log.contains("cpmm"))
27}
28
29#[inline(always)] pub fn parse_log(
32 log: &str,
33 signature: Signature,
34 slot: u64,
35 tx_index: u64,
36 block_time_us: Option<i64>,
37 grpc_recv_us: i64,
38) -> Option<DexEvent> {
39 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
40}
41
42fn parse_structured_log(
44 log: &str,
45 signature: Signature,
46 slot: u64,
47 tx_index: u64,
48 block_time_us: Option<i64>,
49 grpc_recv_us: i64,
50) -> Option<DexEvent> {
51 let program_data = extract_program_data(log)?;
52 if program_data.len() < 8 {
53 return None;
54 }
55
56 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
57 let data = &program_data[8..];
58
59 match discriminator {
60 discriminators::SWAP_EVENT => parse_swap_event_from_data(
61 data,
62 create_metadata_simple(
63 signature,
64 slot,
65 tx_index,
66 block_time_us,
67 Pubkey::default(),
68 grpc_recv_us,
69 ),
70 ),
71 discriminators::SWAP_BASE_IN => {
72 parse_swap_base_in_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
73 }
74 discriminators::SWAP_BASE_OUT => {
75 parse_swap_base_out_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
76 }
77 discriminators::CREATE_POOL => {
78 parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
79 }
80 discriminators::DEPOSIT => {
81 parse_deposit_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
82 }
83 discriminators::WITHDRAW => {
84 parse_withdraw_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
85 }
86 _ => None,
87 }
88}
89
90#[inline(always)]
96pub fn parse_swap_event_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
97 if data.len() < 32 + (6 * 8) + 1 {
98 return None;
99 }
100 let mut offset = 0;
101 let pool_id = read_pubkey(data, offset)?;
102 offset += 32;
103 let input_vault_before = read_u64_le(data, offset)?;
104 offset += 8;
105 let output_vault_before = read_u64_le(data, offset)?;
106 offset += 8;
107 let input_amount = read_u64_le(data, offset)?;
108 offset += 8;
109 let output_amount = read_u64_le(data, offset)?;
110 offset += 8;
111 let input_transfer_fee = read_u64_le(data, offset)?;
112 offset += 8;
113 let output_transfer_fee = read_u64_le(data, offset)?;
114 offset += 8;
115 let base_input = read_bool(data, offset)?;
116
117 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
118 metadata,
119 pool_id,
120 input_vault_before,
121 output_vault_before,
122 input_amount,
123 output_amount,
124 input_transfer_fee,
125 output_transfer_fee,
126 base_input,
127 }))
128}
129
130#[inline(always)]
132pub fn parse_swap_base_in_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
133 let mut offset = 0;
134
135 let pool_state = read_pubkey(data, offset)?;
136 offset += 32;
137
138 let _user = read_pubkey(data, offset)?;
139 offset += 32;
140
141 let amount_in = read_u64_le(data, offset)?;
142 offset += 8;
143
144 let _minimum_amount_out = read_u64_le(data, offset)?;
145 offset += 8;
146
147 let amount_out = read_u64_le(data, offset)?;
148 offset += 8;
149
150 let is_base_input = read_bool(data, offset)?;
151
152 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
153 metadata,
154 pool_id: pool_state,
155 input_vault_before: 0,
156 output_vault_before: 0,
157 input_amount: amount_in,
158 output_amount: amount_out,
159 input_transfer_fee: 0,
160 output_transfer_fee: 0,
161 base_input: is_base_input,
162 }))
163}
164
165#[inline(always)]
167pub fn parse_swap_base_out_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
168 let mut offset = 0;
169
170 let pool_state = read_pubkey(data, offset)?;
171 offset += 32;
172
173 let _user = read_pubkey(data, offset)?;
174 offset += 32;
175
176 let _maximum_amount_in = read_u64_le(data, offset)?;
177 offset += 8;
178
179 let amount_out = read_u64_le(data, offset)?;
180 offset += 8;
181
182 let amount_in = read_u64_le(data, offset)?;
183 offset += 8;
184
185 let is_base_output = read_bool(data, offset)?;
186
187 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
188 metadata,
189 pool_id: pool_state,
190 input_vault_before: 0,
191 output_vault_before: 0,
192 input_amount: amount_in,
193 output_amount: amount_out,
194 input_transfer_fee: 0,
195 output_transfer_fee: 0,
196 base_input: !is_base_output,
197 }))
198}
199
200#[inline(always)]
202pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
203 let mut offset = 0;
204
205 let pool_state = read_pubkey(data, offset)?;
206 offset += 32;
207
208 let _token_0_mint = read_pubkey(data, offset)?;
209 offset += 32;
210
211 let _token_1_mint = read_pubkey(data, offset)?;
212 offset += 32;
213
214 let creator = read_pubkey(data, offset)?;
215 offset += 32;
216
217 let initial_amount_0 = read_u64_le(data, offset)?;
218 offset += 8;
219
220 let initial_amount_1 = read_u64_le(data, offset)?;
221
222 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
223 metadata,
224 pool: pool_state,
225 creator,
226 init_amount0: initial_amount_0,
227 init_amount1: initial_amount_1,
228 }))
229}
230
231#[inline(always)]
233pub fn parse_deposit_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
234 let mut offset = 0;
235
236 let pool_state = read_pubkey(data, offset)?;
237 offset += 32;
238
239 let user = read_pubkey(data, offset)?;
240 offset += 32;
241
242 let lp_token_amount = read_u64_le(data, offset)?;
243 offset += 8;
244
245 let token_0_amount = read_u64_le(data, offset)?;
246 offset += 8;
247
248 let token_1_amount = read_u64_le(data, offset)?;
249
250 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
251 metadata,
252 pool: pool_state,
253 user,
254 lp_token_amount,
255 token0_amount: token_0_amount,
256 token1_amount: token_1_amount,
257 }))
258}
259
260#[inline(always)]
262pub fn parse_withdraw_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
263 let mut offset = 0;
264
265 let pool_state = read_pubkey(data, offset)?;
266 offset += 32;
267
268 let user = read_pubkey(data, offset)?;
269 offset += 32;
270
271 let lp_token_amount = read_u64_le(data, offset)?;
272 offset += 8;
273
274 let token_0_amount = read_u64_le(data, offset)?;
275 offset += 8;
276
277 let token_1_amount = read_u64_le(data, offset)?;
278
279 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
280 metadata,
281 pool: pool_state,
282 user,
283 lp_token_amount,
284 token0_amount: token_0_amount,
285 token1_amount: token_1_amount,
286 }))
287}
288
289fn parse_swap_base_in_event(
291 data: &[u8],
292 signature: Signature,
293 slot: u64,
294 tx_index: u64,
295 block_time_us: Option<i64>,
296 grpc_recv_us: i64,
297) -> Option<DexEvent> {
298 let mut offset = 0;
299
300 let pool_state = read_pubkey(data, offset)?;
301 offset += 32;
302
303 let user = read_pubkey(data, offset)?;
304 offset += 32;
305
306 let amount_in = read_u64_le(data, offset)?;
307 offset += 8;
308
309 let minimum_amount_out = read_u64_le(data, offset)?;
310 offset += 8;
311
312 let amount_out = read_u64_le(data, offset)?;
313 offset += 8;
314
315 let is_base_input = read_bool(data, offset)?;
316
317 let metadata =
318 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
319
320 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
321 metadata,
322
323 pool_id: pool_state,
325 input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
328 output_amount: amount_out,
329 input_transfer_fee: 0, output_transfer_fee: 0, base_input: is_base_input,
332 }))
349}
350
351fn parse_swap_base_out_event(
353 data: &[u8],
354 signature: Signature,
355 slot: u64,
356 tx_index: u64,
357 block_time_us: Option<i64>,
358 grpc_recv_us: i64,
359) -> Option<DexEvent> {
360 let mut offset = 0;
361
362 let pool_state = read_pubkey(data, offset)?;
363 offset += 32;
364
365 let user = read_pubkey(data, offset)?;
366 offset += 32;
367
368 let maximum_amount_in = read_u64_le(data, offset)?;
369 offset += 8;
370
371 let amount_out = read_u64_le(data, offset)?;
372 offset += 8;
373
374 let amount_in = read_u64_le(data, offset)?;
375 offset += 8;
376
377 let is_base_output = read_bool(data, offset)?;
378
379 let metadata =
380 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
381
382 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
383 metadata,
384
385 pool_id: pool_state,
387 input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
390 output_amount: amount_out,
391 input_transfer_fee: 0, output_transfer_fee: 0, base_input: !is_base_output,
394 }))
411}
412
413fn parse_create_pool_event(
415 data: &[u8],
416 signature: Signature,
417 slot: u64,
418 tx_index: u64,
419 block_time_us: Option<i64>,
420 grpc_recv_us: i64,
421) -> Option<DexEvent> {
422 let mut offset = 0;
423
424 let pool_state = read_pubkey(data, offset)?;
425 offset += 32;
426
427 let token_0_mint = read_pubkey(data, offset)?;
428 offset += 32;
429
430 let token_1_mint = read_pubkey(data, offset)?;
431 offset += 32;
432
433 let creator = read_pubkey(data, offset)?;
434 offset += 32;
435
436 let initial_amount_0 = read_u64_le(data, offset)?;
437 offset += 8;
438
439 let initial_amount_1 = read_u64_le(data, offset)?;
440
441 let metadata =
442 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
443
444 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
445 metadata,
446 pool: pool_state,
447 creator,
448 init_amount0: initial_amount_0,
449 init_amount1: initial_amount_1,
450 }))
451}
452
453fn parse_deposit_event(
455 data: &[u8],
456 signature: Signature,
457 slot: u64,
458 tx_index: u64,
459 block_time_us: Option<i64>,
460 grpc_recv_us: i64,
461) -> Option<DexEvent> {
462 let mut offset = 0;
463
464 let pool_state = read_pubkey(data, offset)?;
465 offset += 32;
466
467 let user = read_pubkey(data, offset)?;
468 offset += 32;
469
470 let lp_token_amount = read_u64_le(data, offset)?;
471 offset += 8;
472
473 let token_0_amount = read_u64_le(data, offset)?;
474 offset += 8;
475
476 let token_1_amount = read_u64_le(data, offset)?;
477
478 let metadata =
479 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
480
481 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
482 metadata,
483 pool: pool_state,
484 user,
485 lp_token_amount,
486 token0_amount: token_0_amount,
487 token1_amount: token_1_amount,
488 }))
489}
490
491fn parse_withdraw_event(
493 data: &[u8],
494 signature: Signature,
495 slot: u64,
496 tx_index: u64,
497 block_time_us: Option<i64>,
498 grpc_recv_us: i64,
499) -> Option<DexEvent> {
500 let mut offset = 0;
501
502 let pool_state = read_pubkey(data, offset)?;
503 offset += 32;
504
505 let user = read_pubkey(data, offset)?;
506 offset += 32;
507
508 let lp_token_amount = read_u64_le(data, offset)?;
509 offset += 8;
510
511 let token_0_amount = read_u64_le(data, offset)?;
512 offset += 8;
513
514 let token_1_amount = read_u64_le(data, offset)?;
515
516 let metadata =
517 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
518
519 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
520 metadata,
521 pool: pool_state,
522 user,
523 lp_token_amount,
524 token0_amount: token_0_amount,
525 token1_amount: token_1_amount,
526 }))
527}
528
529fn parse_text_log(
531 log: &str,
532 signature: Signature,
533 slot: u64,
534 tx_index: u64,
535 block_time_us: Option<i64>,
536 grpc_recv_us: i64,
537) -> Option<DexEvent> {
538 use super::utils::text_parser::*;
539
540 if log.contains("swap") || log.contains("Swap") {
541 if log.contains("base_in") {
542 return parse_swap_base_in_from_text(
543 log,
544 signature,
545 slot,
546 tx_index,
547 block_time_us,
548 grpc_recv_us,
549 );
550 } else if log.contains("base_out") {
551 return parse_swap_base_out_from_text(
552 log,
553 signature,
554 slot,
555 tx_index,
556 block_time_us,
557 grpc_recv_us,
558 );
559 } else {
560 return parse_swap_base_in_from_text(
561 log,
562 signature,
563 slot,
564 tx_index,
565 block_time_us,
566 grpc_recv_us,
567 );
568 }
569 }
570
571 if log.contains("deposit") || log.contains("Deposit") {
572 return parse_deposit_from_text(
573 log,
574 signature,
575 slot,
576 tx_index,
577 block_time_us,
578 grpc_recv_us,
579 );
580 }
581
582 if log.contains("withdraw") || log.contains("Withdraw") {
583 return parse_withdraw_from_text(
584 log,
585 signature,
586 slot,
587 tx_index,
588 block_time_us,
589 grpc_recv_us,
590 );
591 }
592
593 if log.contains("create") && log.contains("pool") {
594 return parse_create_pool_from_text(
595 log,
596 signature,
597 slot,
598 tx_index,
599 block_time_us,
600 grpc_recv_us,
601 );
602 }
603
604 None
605}
606
607fn parse_swap_base_in_from_text(
609 log: &str,
610 signature: Signature,
611 slot: u64,
612 tx_index: u64,
613 block_time_us: Option<i64>,
614 grpc_recv_us: i64,
615) -> Option<DexEvent> {
616 use super::utils::text_parser::*;
617
618 let metadata = create_metadata_simple(
619 signature,
620 slot,
621 tx_index,
622 block_time_us,
623 Pubkey::default(),
624 grpc_recv_us,
625 );
626
627 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
628 metadata,
629
630 pool_id: Pubkey::default(),
632 input_vault_before: 0,
633 output_vault_before: 0,
634 input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
635 output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
636 input_transfer_fee: 0,
637 output_transfer_fee: 0,
638 base_input: true,
639 }))
656}
657
658fn parse_swap_base_out_from_text(
660 log: &str,
661 signature: Signature,
662 slot: u64,
663 tx_index: u64,
664 block_time_us: Option<i64>,
665 grpc_recv_us: i64,
666) -> Option<DexEvent> {
667 use super::utils::text_parser::*;
668
669 let metadata = create_metadata_simple(
670 signature,
671 slot,
672 tx_index,
673 block_time_us,
674 Pubkey::default(),
675 grpc_recv_us,
676 );
677
678 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
679 metadata,
680
681 pool_id: Pubkey::default(),
683 input_vault_before: 0,
684 output_vault_before: 0,
685 input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
686 output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
687 input_transfer_fee: 0,
688 output_transfer_fee: 0,
689 base_input: false,
690 }))
707}
708
709fn parse_create_pool_from_text(
711 log: &str,
712 signature: Signature,
713 slot: u64,
714 tx_index: u64,
715 block_time_us: Option<i64>,
716 grpc_recv_us: i64,
717) -> Option<DexEvent> {
718 use super::utils::text_parser::*;
719
720 let metadata = create_metadata_simple(
721 signature,
722 slot,
723 tx_index,
724 block_time_us,
725 Pubkey::default(),
726 grpc_recv_us,
727 );
728
729 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
730 metadata,
731 pool: Pubkey::default(),
732 creator: Pubkey::default(),
733 init_amount0: extract_number_from_text(log, "amount_0").unwrap_or(1_000_000_000),
734 init_amount1: extract_number_from_text(log, "amount_1").unwrap_or(1_000_000_000),
735 }))
736}
737
738fn parse_deposit_from_text(
740 log: &str,
741 signature: Signature,
742 slot: u64,
743 tx_index: u64,
744 block_time_us: Option<i64>,
745 grpc_recv_us: i64,
746) -> Option<DexEvent> {
747 use super::utils::text_parser::*;
748
749 let metadata = create_metadata_simple(
750 signature,
751 slot,
752 tx_index,
753 block_time_us,
754 Pubkey::default(),
755 grpc_recv_us,
756 );
757
758 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
759 metadata,
760 pool: Pubkey::default(),
761 user: Pubkey::default(),
762 lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
763 token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
764 token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
765 }))
766}
767
768fn parse_withdraw_from_text(
770 log: &str,
771 signature: Signature,
772 slot: u64,
773 tx_index: u64,
774 block_time_us: Option<i64>,
775 grpc_recv_us: i64,
776) -> Option<DexEvent> {
777 use super::utils::text_parser::*;
778
779 let metadata = create_metadata_simple(
780 signature,
781 slot,
782 tx_index,
783 block_time_us,
784 Pubkey::default(),
785 grpc_recv_us,
786 );
787
788 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
789 metadata,
790 pool: Pubkey::default(),
791 user: Pubkey::default(),
792 lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
793 token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
794 token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
795 }))
796}