1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9pub mod discriminators {
11 pub const SWAP: [u8; 8] = [248, 198, 158, 145, 225, 117, 135, 200];
12 pub const INCREASE_LIQUIDITY: [u8; 8] = [133, 29, 89, 223, 69, 238, 176, 10];
13 pub const DECREASE_LIQUIDITY: [u8; 8] = [160, 38, 208, 111, 104, 91, 44, 1];
14 pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
15 pub const COLLECT_FEE: [u8; 8] = [164, 152, 207, 99, 187, 104, 171, 119];
16}
17
18pub const PROGRAM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK";
20
21pub fn is_raydium_clmm_log(log: &str) -> bool {
23 log.contains(&format!("Program {} invoke", PROGRAM_ID))
24 || log.contains(&format!("Program {} success", PROGRAM_ID))
25 || log.contains("raydium")
26 || log.contains("Raydium")
27}
28
29#[inline]
31pub 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 => {
61 parse_swap_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
62 }
63 discriminators::INCREASE_LIQUIDITY => parse_increase_liquidity_event(
64 data,
65 signature,
66 slot,
67 tx_index,
68 block_time_us,
69 grpc_recv_us,
70 ),
71 discriminators::DECREASE_LIQUIDITY => parse_decrease_liquidity_event(
72 data,
73 signature,
74 slot,
75 tx_index,
76 block_time_us,
77 grpc_recv_us,
78 ),
79 discriminators::CREATE_POOL => {
80 parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
81 }
82 discriminators::COLLECT_FEE => {
83 parse_collect_fee_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
84 }
85 _ => None,
86 }
87}
88
89fn parse_swap_event(
91 data: &[u8],
92 signature: Signature,
93 slot: u64,
94 tx_index: u64,
95 block_time_us: Option<i64>,
96 grpc_recv_us: i64,
97) -> Option<DexEvent> {
98 let mut offset = 0;
99
100 let pool_state = read_pubkey(data, offset)?;
101 offset += 32;
102
103 let user = read_pubkey(data, offset)?;
104 offset += 32;
105
106 let amount = read_u64_le(data, offset)?;
107 offset += 8;
108
109 let other_amount_threshold = read_u64_le(data, offset)?;
110 offset += 8;
111
112 let sqrt_price_limit_x64 = read_u128_le(data, offset)?;
113 offset += 16;
114
115 let is_base_input = read_bool(data, offset)?;
116
117 let metadata =
118 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
119
120 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
121 metadata,
122
123 pool_state,
125 sender: user,
126 token_account_0: Pubkey::default(),
127 token_account_1: Pubkey::default(),
128 amount_0: 0, transfer_fee_0: 0, amount_1: 0, transfer_fee_1: 0, zero_for_one: is_base_input,
133 sqrt_price_x64: sqrt_price_limit_x64,
134 liquidity: 0, tick: 0, }))
144}
145
146fn parse_increase_liquidity_event(
148 data: &[u8],
149 signature: Signature,
150 slot: u64,
151 tx_index: u64,
152 block_time_us: Option<i64>,
153 grpc_recv_us: i64,
154) -> Option<DexEvent> {
155 let mut offset = 0;
156
157 let pool_state = read_pubkey(data, offset)?;
158 offset += 32;
159
160 let position_nft_mint = read_pubkey(data, offset)?;
161 offset += 32;
162
163 let liquidity = read_u128_le(data, offset)?;
164 offset += 16;
165
166 let amount0_max = read_u64_le(data, offset)?;
167 offset += 8;
168
169 let amount1_max = read_u64_le(data, offset)?;
170
171 let metadata =
172 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
173
174 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
175 metadata,
176 pool: pool_state,
177 position_nft_mint,
178 user: Pubkey::default(), liquidity,
180 amount0_max,
181 amount1_max,
182 }))
183}
184
185fn parse_decrease_liquidity_event(
187 data: &[u8],
188 signature: Signature,
189 slot: u64,
190 tx_index: u64,
191 block_time_us: Option<i64>,
192 grpc_recv_us: i64,
193) -> Option<DexEvent> {
194 let mut offset = 0;
195
196 let pool_state = read_pubkey(data, offset)?;
197 offset += 32;
198
199 let position_nft_mint = read_pubkey(data, offset)?;
200 offset += 32;
201
202 let liquidity = read_u128_le(data, offset)?;
203 offset += 16;
204
205 let amount0_min = read_u64_le(data, offset)?;
206 offset += 8;
207
208 let amount1_min = read_u64_le(data, offset)?;
209
210 let metadata =
211 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
212
213 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
214 metadata,
215 pool: pool_state,
216 position_nft_mint,
217 user: Pubkey::default(), liquidity,
219 amount0_min,
220 amount1_min,
221 }))
222}
223
224fn parse_create_pool_event(
226 data: &[u8],
227 signature: Signature,
228 slot: u64,
229 tx_index: u64,
230 block_time_us: Option<i64>,
231 grpc_recv_us: i64,
232) -> Option<DexEvent> {
233 let mut offset = 0;
234
235 let pool_state = read_pubkey(data, offset)?;
236 offset += 32;
237
238 let creator = read_pubkey(data, offset)?;
239 offset += 32;
240
241 let sqrt_price_x64 = read_u128_le(data, offset)?;
242 offset += 16;
243
244 let open_time = read_u64_le(data, offset)?;
245
246 let metadata =
247 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
248
249 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
250 metadata,
251 pool: pool_state,
252 token_0_mint: Pubkey::default(), token_1_mint: Pubkey::default(), tick_spacing: 0, fee_rate: 0, creator,
257 sqrt_price_x64,
258 open_time,
259 }))
260}
261
262fn parse_collect_fee_event(
264 data: &[u8],
265 signature: Signature,
266 slot: u64,
267 tx_index: u64,
268 block_time_us: Option<i64>,
269 grpc_recv_us: i64,
270) -> Option<DexEvent> {
271 let mut offset = 0;
272
273 let pool_state = read_pubkey(data, offset)?;
274 offset += 32;
275
276 let position_nft_mint = read_pubkey(data, offset)?;
277 offset += 32;
278
279 let amount_0 = read_u64_le(data, offset)?;
280 offset += 8;
281
282 let amount_1 = read_u64_le(data, offset)?;
283
284 let metadata =
285 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
286
287 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
288 metadata,
289 pool_state,
290 position_nft_mint,
291 amount_0,
292 amount_1,
293 }))
294}
295
296fn parse_text_log(
298 log: &str,
299 signature: Signature,
300 slot: u64,
301 tx_index: u64,
302 block_time_us: Option<i64>,
303 grpc_recv_us: i64,
304) -> Option<DexEvent> {
305 use super::utils::text_parser::*;
306
307 if log.contains("swap") || log.contains("Swap") {
308 return parse_swap_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
309 }
310
311 if log.contains("increase") && log.contains("liquidity") {
312 return parse_increase_liquidity_from_text(
313 log,
314 signature,
315 slot,
316 tx_index,
317 block_time_us,
318 grpc_recv_us,
319 );
320 }
321
322 if log.contains("decrease") && log.contains("liquidity") {
323 return parse_decrease_liquidity_from_text(
324 log,
325 signature,
326 slot,
327 tx_index,
328 block_time_us,
329 grpc_recv_us,
330 );
331 }
332
333 if log.contains("create") && log.contains("pool") {
334 return parse_create_pool_from_text(
335 log,
336 signature,
337 slot,
338 tx_index,
339 block_time_us,
340 grpc_recv_us,
341 );
342 }
343
344 if log.contains("collect") && log.contains("fee") {
345 return parse_collect_fee_from_text(
346 log,
347 signature,
348 slot,
349 tx_index,
350 block_time_us,
351 grpc_recv_us,
352 );
353 }
354
355 None
356}
357
358fn parse_swap_from_text(
360 log: &str,
361 signature: Signature,
362 slot: u64,
363 tx_index: u64,
364 block_time_us: Option<i64>,
365 grpc_recv_us: i64,
366) -> Option<DexEvent> {
367 use super::utils::text_parser::*;
368
369 let metadata = create_metadata_simple(
370 signature,
371 slot,
372 tx_index,
373 block_time_us,
374 Pubkey::default(),
375 grpc_recv_us,
376 );
377 let is_base_input = detect_trade_type(log).unwrap_or(true);
378
379 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
380 metadata,
381
382 pool_state: Pubkey::default(),
384 sender: Pubkey::default(),
385 token_account_0: Pubkey::default(),
386 token_account_1: Pubkey::default(),
387 amount_0: 0,
388 transfer_fee_0: 0,
389 amount_1: 0,
390 transfer_fee_1: 0,
391 zero_for_one: is_base_input,
392 sqrt_price_x64: 0,
393 liquidity: 0,
395 tick: 0,
396 }))
402}
403
404fn parse_increase_liquidity_from_text(
406 log: &str,
407 signature: Signature,
408 slot: u64,
409 tx_index: u64,
410 block_time_us: Option<i64>,
411 grpc_recv_us: i64,
412) -> Option<DexEvent> {
413 use super::utils::text_parser::*;
414
415 let metadata = create_metadata_simple(
416 signature,
417 slot,
418 tx_index,
419 block_time_us,
420 Pubkey::default(),
421 grpc_recv_us,
422 );
423
424 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
425 metadata,
426 pool: Pubkey::default(),
427 position_nft_mint: Pubkey::default(),
428 user: Pubkey::default(),
429 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
430 amount0_max: extract_number_from_text(log, "amount0_max").unwrap_or(1_000_000),
431 amount1_max: extract_number_from_text(log, "amount1_max").unwrap_or(1_000_000),
432 }))
433}
434
435fn parse_decrease_liquidity_from_text(
437 log: &str,
438 signature: Signature,
439 slot: u64,
440 tx_index: u64,
441 block_time_us: Option<i64>,
442 grpc_recv_us: i64,
443) -> Option<DexEvent> {
444 use super::utils::text_parser::*;
445
446 let metadata = create_metadata_simple(
447 signature,
448 slot,
449 tx_index,
450 block_time_us,
451 Pubkey::default(),
452 grpc_recv_us,
453 );
454
455 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
456 metadata,
457 pool: Pubkey::default(),
458 position_nft_mint: Pubkey::default(),
459 user: Pubkey::default(),
460 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
461 amount0_min: extract_number_from_text(log, "amount0_min").unwrap_or(1_000_000),
462 amount1_min: extract_number_from_text(log, "amount1_min").unwrap_or(1_000_000),
463 }))
464}
465
466fn parse_create_pool_from_text(
468 log: &str,
469 signature: Signature,
470 slot: u64,
471 tx_index: u64,
472 block_time_us: Option<i64>,
473 grpc_recv_us: i64,
474) -> Option<DexEvent> {
475 use super::utils::text_parser::*;
476
477 let metadata = create_metadata_simple(
478 signature,
479 slot,
480 tx_index,
481 block_time_us,
482 Pubkey::default(),
483 grpc_recv_us,
484 );
485
486 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
487 metadata,
488 pool: Pubkey::default(),
489 token_0_mint: Pubkey::default(),
490 token_1_mint: Pubkey::default(),
491 tick_spacing: 0,
492 fee_rate: 0,
493 creator: Pubkey::default(),
494 sqrt_price_x64: 0,
495 open_time: 0,
496 }))
497}
498
499fn parse_collect_fee_from_text(
501 log: &str,
502 signature: Signature,
503 slot: u64,
504 tx_index: u64,
505 block_time_us: Option<i64>,
506 grpc_recv_us: i64,
507) -> Option<DexEvent> {
508 use super::utils::text_parser::*;
509
510 let metadata = create_metadata_simple(
511 signature,
512 slot,
513 tx_index,
514 block_time_us,
515 Pubkey::default(),
516 grpc_recv_us,
517 );
518
519 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
520 metadata,
521 pool_state: Pubkey::default(),
522 position_nft_mint: Pubkey::default(),
523 amount_0: extract_number_from_text(log, "amount_0").unwrap_or(10_000),
524 amount_1: extract_number_from_text(log, "amount_1").unwrap_or(10_000),
525 }))
526}
527
528#[inline(always)]
535pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
536 let mut offset = 0;
537
538 let pool_state = read_pubkey(data, offset)?;
539 offset += 32;
540
541 let user = read_pubkey(data, offset)?;
542 offset += 32;
543
544 let _amount = read_u64_le(data, offset)?;
545 offset += 8;
546
547 let _other_amount_threshold = read_u64_le(data, offset)?;
548 offset += 8;
549
550 let sqrt_price_limit_x64 = read_u128_le(data, offset)?;
551 offset += 16;
552
553 let is_base_input = read_bool(data, offset)?;
554
555 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
556 metadata,
557 pool_state,
558 sender: user,
559 token_account_0: Pubkey::default(),
560 token_account_1: Pubkey::default(),
561 amount_0: 0,
562 transfer_fee_0: 0,
563 amount_1: 0,
564 transfer_fee_1: 0,
565 zero_for_one: is_base_input,
566 sqrt_price_x64: sqrt_price_limit_x64,
567 liquidity: 0,
568 tick: 0,
569 }))
570}
571
572#[inline(always)]
574pub fn parse_increase_liquidity_from_data(
575 data: &[u8],
576 metadata: EventMetadata,
577) -> Option<DexEvent> {
578 let mut offset = 0;
579
580 let pool = read_pubkey(data, offset)?;
581 offset += 32;
582
583 let user = read_pubkey(data, offset)?;
584 offset += 32;
585
586 let liquidity = read_u128_le(data, offset)?;
587 offset += 16;
588
589 let amount0_max = read_u64_le(data, offset)?;
590 offset += 8;
591
592 let amount1_max = read_u64_le(data, offset)?;
593
594 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
595 metadata,
596 pool,
597 position_nft_mint: Pubkey::default(), user,
599 liquidity,
600 amount0_max,
601 amount1_max,
602 }))
603}
604
605#[inline(always)]
607pub fn parse_decrease_liquidity_from_data(
608 data: &[u8],
609 metadata: EventMetadata,
610) -> Option<DexEvent> {
611 let mut offset = 0;
612
613 let pool = read_pubkey(data, offset)?;
614 offset += 32;
615
616 let user = read_pubkey(data, offset)?;
617 offset += 32;
618
619 let liquidity = read_u128_le(data, offset)?;
620 offset += 16;
621
622 let amount0_min = read_u64_le(data, offset)?;
623 offset += 8;
624
625 let amount1_min = read_u64_le(data, offset)?;
626
627 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
628 metadata,
629 pool,
630 position_nft_mint: Pubkey::default(), user,
632 liquidity,
633 amount0_min,
634 amount1_min,
635 }))
636}
637
638#[inline(always)]
640pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
641 let mut offset = 0;
642
643 let pool = read_pubkey(data, offset)?;
644 offset += 32;
645
646 let creator = read_pubkey(data, offset)?;
647 offset += 32;
648
649 let sqrt_price_x64 = read_u128_le(data, offset)?;
650 offset += 16;
651
652 let open_time = read_u64_le(data, offset)?;
653
654 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
655 metadata,
656 pool,
657 token_0_mint: Pubkey::default(), token_1_mint: Pubkey::default(), tick_spacing: 0, fee_rate: 0, creator,
662 sqrt_price_x64,
663 open_time,
664 }))
665}
666
667#[inline(always)]
669pub fn parse_collect_fee_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
670 let mut offset = 0;
671
672 let pool_state = read_pubkey(data, offset)?;
673 offset += 32;
674
675 let position_nft_mint = read_pubkey(data, offset)?;
676 offset += 32;
677
678 let amount_0 = read_u64_le(data, offset)?;
679 offset += 8;
680
681 let amount_1 = read_u64_le(data, offset)?;
682
683 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
684 metadata,
685 pool_state,
686 position_nft_mint,
687 amount_0,
688 amount_1,
689 }))
690}