1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9pub mod discriminators {
11 pub const SWAP_BASE_IN: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
12 pub const SWAP_BASE_OUT: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173];
13 pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
14 pub const DEPOSIT: [u8; 8] = [242, 35, 198, 137, 82, 225, 242, 182];
15 pub const WITHDRAW: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
16}
17
18pub const PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
20
21pub fn is_raydium_cpmm_log(log: &str) -> bool {
23 log.contains(&format!("Program {} invoke", PROGRAM_ID))
24 || log.contains(&format!("Program {} success", PROGRAM_ID))
25 || (log.contains("raydium") && log.contains("cpmm"))
26}
27
28#[inline(always)] pub fn parse_log(
31 log: &str,
32 signature: Signature,
33 slot: u64,
34 tx_index: u64,
35 block_time_us: Option<i64>,
36 grpc_recv_us: i64,
37) -> Option<DexEvent> {
38 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
39}
40
41fn parse_structured_log(
43 log: &str,
44 signature: Signature,
45 slot: u64,
46 tx_index: u64,
47 block_time_us: Option<i64>,
48 grpc_recv_us: i64,
49) -> Option<DexEvent> {
50 let program_data = extract_program_data(log)?;
51 if program_data.len() < 8 {
52 return None;
53 }
54
55 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
56 let data = &program_data[8..];
57
58 match discriminator {
59 discriminators::SWAP_BASE_IN => {
60 parse_swap_base_in_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
61 }
62 discriminators::SWAP_BASE_OUT => {
63 parse_swap_base_out_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
64 }
65 discriminators::CREATE_POOL => {
66 parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
67 }
68 discriminators::DEPOSIT => {
69 parse_deposit_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
70 }
71 discriminators::WITHDRAW => {
72 parse_withdraw_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
73 }
74 _ => None,
75 }
76}
77
78#[inline(always)]
84pub fn parse_swap_base_in_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
85 let mut offset = 0;
86
87 let pool_state = read_pubkey(data, offset)?;
88 offset += 32;
89
90 let _user = read_pubkey(data, offset)?;
91 offset += 32;
92
93 let amount_in = read_u64_le(data, offset)?;
94 offset += 8;
95
96 let _minimum_amount_out = read_u64_le(data, offset)?;
97 offset += 8;
98
99 let amount_out = read_u64_le(data, offset)?;
100 offset += 8;
101
102 let is_base_input = read_bool(data, offset)?;
103
104 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
105 metadata,
106 pool_id: pool_state,
107 input_vault_before: 0,
108 output_vault_before: 0,
109 input_amount: amount_in,
110 output_amount: amount_out,
111 input_transfer_fee: 0,
112 output_transfer_fee: 0,
113 base_input: is_base_input,
114 }))
115}
116
117#[inline(always)]
119pub fn parse_swap_base_out_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
120 let mut offset = 0;
121
122 let pool_state = read_pubkey(data, offset)?;
123 offset += 32;
124
125 let _user = read_pubkey(data, offset)?;
126 offset += 32;
127
128 let _maximum_amount_in = read_u64_le(data, offset)?;
129 offset += 8;
130
131 let amount_out = read_u64_le(data, offset)?;
132 offset += 8;
133
134 let amount_in = read_u64_le(data, offset)?;
135 offset += 8;
136
137 let is_base_output = read_bool(data, offset)?;
138
139 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
140 metadata,
141 pool_id: pool_state,
142 input_vault_before: 0,
143 output_vault_before: 0,
144 input_amount: amount_in,
145 output_amount: amount_out,
146 input_transfer_fee: 0,
147 output_transfer_fee: 0,
148 base_input: !is_base_output,
149 }))
150}
151
152#[inline(always)]
154pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
155 let mut offset = 0;
156
157 let pool_state = read_pubkey(data, offset)?;
158 offset += 32;
159
160 let _token_0_mint = read_pubkey(data, offset)?;
161 offset += 32;
162
163 let _token_1_mint = read_pubkey(data, offset)?;
164 offset += 32;
165
166 let creator = read_pubkey(data, offset)?;
167 offset += 32;
168
169 let initial_amount_0 = read_u64_le(data, offset)?;
170 offset += 8;
171
172 let initial_amount_1 = read_u64_le(data, offset)?;
173
174 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
175 metadata,
176 pool: pool_state,
177 creator,
178 init_amount0: initial_amount_0,
179 init_amount1: initial_amount_1,
180 }))
181}
182
183#[inline(always)]
185pub fn parse_deposit_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
186 let mut offset = 0;
187
188 let pool_state = read_pubkey(data, offset)?;
189 offset += 32;
190
191 let user = read_pubkey(data, offset)?;
192 offset += 32;
193
194 let lp_token_amount = read_u64_le(data, offset)?;
195 offset += 8;
196
197 let token_0_amount = read_u64_le(data, offset)?;
198 offset += 8;
199
200 let token_1_amount = read_u64_le(data, offset)?;
201
202 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
203 metadata,
204 pool: pool_state,
205 user,
206 lp_token_amount,
207 token0_amount: token_0_amount,
208 token1_amount: token_1_amount,
209 }))
210}
211
212#[inline(always)]
214pub fn parse_withdraw_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
215 let mut offset = 0;
216
217 let pool_state = read_pubkey(data, offset)?;
218 offset += 32;
219
220 let user = read_pubkey(data, offset)?;
221 offset += 32;
222
223 let lp_token_amount = read_u64_le(data, offset)?;
224 offset += 8;
225
226 let token_0_amount = read_u64_le(data, offset)?;
227 offset += 8;
228
229 let token_1_amount = read_u64_le(data, offset)?;
230
231 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
232 metadata,
233 pool: pool_state,
234 user,
235 lp_token_amount,
236 token0_amount: token_0_amount,
237 token1_amount: token_1_amount,
238 }))
239}
240
241fn parse_swap_base_in_event(
243 data: &[u8],
244 signature: Signature,
245 slot: u64,
246 tx_index: u64,
247 block_time_us: Option<i64>,
248 grpc_recv_us: i64,
249) -> Option<DexEvent> {
250 let mut offset = 0;
251
252 let pool_state = read_pubkey(data, offset)?;
253 offset += 32;
254
255 let user = read_pubkey(data, offset)?;
256 offset += 32;
257
258 let amount_in = read_u64_le(data, offset)?;
259 offset += 8;
260
261 let minimum_amount_out = read_u64_le(data, offset)?;
262 offset += 8;
263
264 let amount_out = read_u64_le(data, offset)?;
265 offset += 8;
266
267 let is_base_input = read_bool(data, offset)?;
268
269 let metadata =
270 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
271
272 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
273 metadata,
274
275 pool_id: pool_state,
277 input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
280 output_amount: amount_out,
281 input_transfer_fee: 0, output_transfer_fee: 0, base_input: is_base_input,
284 }))
301}
302
303fn parse_swap_base_out_event(
305 data: &[u8],
306 signature: Signature,
307 slot: u64,
308 tx_index: u64,
309 block_time_us: Option<i64>,
310 grpc_recv_us: i64,
311) -> Option<DexEvent> {
312 let mut offset = 0;
313
314 let pool_state = read_pubkey(data, offset)?;
315 offset += 32;
316
317 let user = read_pubkey(data, offset)?;
318 offset += 32;
319
320 let maximum_amount_in = read_u64_le(data, offset)?;
321 offset += 8;
322
323 let amount_out = read_u64_le(data, offset)?;
324 offset += 8;
325
326 let amount_in = read_u64_le(data, offset)?;
327 offset += 8;
328
329 let is_base_output = read_bool(data, offset)?;
330
331 let metadata =
332 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
333
334 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
335 metadata,
336
337 pool_id: pool_state,
339 input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
342 output_amount: amount_out,
343 input_transfer_fee: 0, output_transfer_fee: 0, base_input: !is_base_output,
346 }))
363}
364
365fn parse_create_pool_event(
367 data: &[u8],
368 signature: Signature,
369 slot: u64,
370 tx_index: u64,
371 block_time_us: Option<i64>,
372 grpc_recv_us: i64,
373) -> Option<DexEvent> {
374 let mut offset = 0;
375
376 let pool_state = read_pubkey(data, offset)?;
377 offset += 32;
378
379 let token_0_mint = read_pubkey(data, offset)?;
380 offset += 32;
381
382 let token_1_mint = read_pubkey(data, offset)?;
383 offset += 32;
384
385 let creator = read_pubkey(data, offset)?;
386 offset += 32;
387
388 let initial_amount_0 = read_u64_le(data, offset)?;
389 offset += 8;
390
391 let initial_amount_1 = read_u64_le(data, offset)?;
392
393 let metadata =
394 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
395
396 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
397 metadata,
398 pool: pool_state,
399 creator,
400 init_amount0: initial_amount_0,
401 init_amount1: initial_amount_1,
402 }))
403}
404
405fn parse_deposit_event(
407 data: &[u8],
408 signature: Signature,
409 slot: u64,
410 tx_index: u64,
411 block_time_us: Option<i64>,
412 grpc_recv_us: i64,
413) -> Option<DexEvent> {
414 let mut offset = 0;
415
416 let pool_state = read_pubkey(data, offset)?;
417 offset += 32;
418
419 let user = read_pubkey(data, offset)?;
420 offset += 32;
421
422 let lp_token_amount = read_u64_le(data, offset)?;
423 offset += 8;
424
425 let token_0_amount = read_u64_le(data, offset)?;
426 offset += 8;
427
428 let token_1_amount = read_u64_le(data, offset)?;
429
430 let metadata =
431 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
432
433 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
434 metadata,
435 pool: pool_state,
436 user,
437 lp_token_amount,
438 token0_amount: token_0_amount,
439 token1_amount: token_1_amount,
440 }))
441}
442
443fn parse_withdraw_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_state = read_pubkey(data, offset)?;
455 offset += 32;
456
457 let user = read_pubkey(data, offset)?;
458 offset += 32;
459
460 let lp_token_amount = read_u64_le(data, offset)?;
461 offset += 8;
462
463 let token_0_amount = read_u64_le(data, offset)?;
464 offset += 8;
465
466 let token_1_amount = read_u64_le(data, offset)?;
467
468 let metadata =
469 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
470
471 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
472 metadata,
473 pool: pool_state,
474 user,
475 lp_token_amount,
476 token0_amount: token_0_amount,
477 token1_amount: token_1_amount,
478 }))
479}
480
481fn parse_text_log(
483 log: &str,
484 signature: Signature,
485 slot: u64,
486 tx_index: u64,
487 block_time_us: Option<i64>,
488 grpc_recv_us: i64,
489) -> Option<DexEvent> {
490 use super::utils::text_parser::*;
491
492 if log.contains("swap") || log.contains("Swap") {
493 if log.contains("base_in") {
494 return parse_swap_base_in_from_text(
495 log,
496 signature,
497 slot,
498 tx_index,
499 block_time_us,
500 grpc_recv_us,
501 );
502 } else if log.contains("base_out") {
503 return parse_swap_base_out_from_text(
504 log,
505 signature,
506 slot,
507 tx_index,
508 block_time_us,
509 grpc_recv_us,
510 );
511 } else {
512 return parse_swap_base_in_from_text(
513 log,
514 signature,
515 slot,
516 tx_index,
517 block_time_us,
518 grpc_recv_us,
519 );
520 }
521 }
522
523 if log.contains("deposit") || log.contains("Deposit") {
524 return parse_deposit_from_text(
525 log,
526 signature,
527 slot,
528 tx_index,
529 block_time_us,
530 grpc_recv_us,
531 );
532 }
533
534 if log.contains("withdraw") || log.contains("Withdraw") {
535 return parse_withdraw_from_text(
536 log,
537 signature,
538 slot,
539 tx_index,
540 block_time_us,
541 grpc_recv_us,
542 );
543 }
544
545 if log.contains("create") && log.contains("pool") {
546 return parse_create_pool_from_text(
547 log,
548 signature,
549 slot,
550 tx_index,
551 block_time_us,
552 grpc_recv_us,
553 );
554 }
555
556 None
557}
558
559fn parse_swap_base_in_from_text(
561 log: &str,
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 use super::utils::text_parser::*;
569
570 let metadata = create_metadata_simple(
571 signature,
572 slot,
573 tx_index,
574 block_time_us,
575 Pubkey::default(),
576 grpc_recv_us,
577 );
578
579 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
580 metadata,
581
582 pool_id: Pubkey::default(),
584 input_vault_before: 0,
585 output_vault_before: 0,
586 input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
587 output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
588 input_transfer_fee: 0,
589 output_transfer_fee: 0,
590 base_input: true,
591 }))
608}
609
610fn parse_swap_base_out_from_text(
612 log: &str,
613 signature: Signature,
614 slot: u64,
615 tx_index: u64,
616 block_time_us: Option<i64>,
617 grpc_recv_us: i64,
618) -> Option<DexEvent> {
619 use super::utils::text_parser::*;
620
621 let metadata = create_metadata_simple(
622 signature,
623 slot,
624 tx_index,
625 block_time_us,
626 Pubkey::default(),
627 grpc_recv_us,
628 );
629
630 Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
631 metadata,
632
633 pool_id: Pubkey::default(),
635 input_vault_before: 0,
636 output_vault_before: 0,
637 input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
638 output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
639 input_transfer_fee: 0,
640 output_transfer_fee: 0,
641 base_input: false,
642 }))
659}
660
661fn parse_create_pool_from_text(
663 log: &str,
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 use super::utils::text_parser::*;
671
672 let metadata = create_metadata_simple(
673 signature,
674 slot,
675 tx_index,
676 block_time_us,
677 Pubkey::default(),
678 grpc_recv_us,
679 );
680
681 Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
682 metadata,
683 pool: Pubkey::default(),
684 creator: Pubkey::default(),
685 init_amount0: extract_number_from_text(log, "amount_0").unwrap_or(1_000_000_000),
686 init_amount1: extract_number_from_text(log, "amount_1").unwrap_or(1_000_000_000),
687 }))
688}
689
690fn parse_deposit_from_text(
692 log: &str,
693 signature: Signature,
694 slot: u64,
695 tx_index: u64,
696 block_time_us: Option<i64>,
697 grpc_recv_us: i64,
698) -> Option<DexEvent> {
699 use super::utils::text_parser::*;
700
701 let metadata = create_metadata_simple(
702 signature,
703 slot,
704 tx_index,
705 block_time_us,
706 Pubkey::default(),
707 grpc_recv_us,
708 );
709
710 Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
711 metadata,
712 pool: Pubkey::default(),
713 user: Pubkey::default(),
714 lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
715 token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
716 token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
717 }))
718}
719
720fn parse_withdraw_from_text(
722 log: &str,
723 signature: Signature,
724 slot: u64,
725 tx_index: u64,
726 block_time_us: Option<i64>,
727 grpc_recv_us: i64,
728) -> Option<DexEvent> {
729 use super::utils::text_parser::*;
730
731 let metadata = create_metadata_simple(
732 signature,
733 slot,
734 tx_index,
735 block_time_us,
736 Pubkey::default(),
737 grpc_recv_us,
738 );
739
740 Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
741 metadata,
742 pool: Pubkey::default(),
743 user: Pubkey::default(),
744 lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
745 token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
746 token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
747 }))
748}