1use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
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") || log.contains("Raydium")
26}
27
28#[inline]
30pub fn parse_log(log: &str, signature: Signature, slot: u64, tx_index: u64, block_time_us: Option<i64>, grpc_recv_us: i64) -> Option<DexEvent> {
31 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
32}
33
34fn parse_structured_log(
36 log: &str,
37 signature: Signature,
38 slot: u64,
39 tx_index: u64,
40 block_time_us: Option<i64>,
41 grpc_recv_us: i64,
42) -> Option<DexEvent> {
43 let program_data = extract_program_data(log)?;
44 if program_data.len() < 8 {
45 return None;
46 }
47
48 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
49 let data = &program_data[8..];
50
51 match discriminator {
52 discriminators::SWAP => {
53 parse_swap_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
54 },
55 discriminators::INCREASE_LIQUIDITY => {
56 parse_increase_liquidity_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
57 },
58 discriminators::DECREASE_LIQUIDITY => {
59 parse_decrease_liquidity_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
60 },
61 discriminators::CREATE_POOL => {
62 parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
63 },
64 discriminators::COLLECT_FEE => {
65 parse_collect_fee_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
66 },
67 _ => None,
68 }
69}
70
71fn parse_swap_event(
73 data: &[u8],
74 signature: Signature,
75 slot: u64,
76 tx_index: u64,
77 block_time_us: Option<i64>,
78 grpc_recv_us: i64,
79) -> Option<DexEvent> {
80 let mut offset = 0;
81
82 let pool_state = read_pubkey(data, offset)?;
83 offset += 32;
84
85 let user = read_pubkey(data, offset)?;
86 offset += 32;
87
88 let amount = read_u64_le(data, offset)?;
89 offset += 8;
90
91 let other_amount_threshold = read_u64_le(data, offset)?;
92 offset += 8;
93
94 let sqrt_price_limit_x64 = read_u128_le(data, offset)?;
95 offset += 16;
96
97 let is_base_input = read_bool(data, offset)?;
98
99 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
100
101 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
102 metadata,
103
104 pool_state,
106 sender: user,
107 token_account_0: Pubkey::default(),
108 token_account_1: Pubkey::default(),
109 amount_0: 0, transfer_fee_0: 0, amount_1: 0, transfer_fee_1: 0, zero_for_one: is_base_input,
114 sqrt_price_x64: sqrt_price_limit_x64,
115 liquidity: 0, tick: 0, }))
125}
126
127fn parse_increase_liquidity_event(
129 data: &[u8],
130 signature: Signature,
131 slot: u64,
132 tx_index: u64,
133 block_time_us: Option<i64>,
134 grpc_recv_us: i64,
135) -> Option<DexEvent> {
136 let mut offset = 0;
137
138 let pool_state = read_pubkey(data, offset)?;
139 offset += 32;
140
141 let position_nft_mint = read_pubkey(data, offset)?;
142 offset += 32;
143
144 let liquidity = read_u128_le(data, offset)?;
145 offset += 16;
146
147 let amount0_max = read_u64_le(data, offset)?;
148 offset += 8;
149
150 let amount1_max = read_u64_le(data, offset)?;
151
152 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
153
154 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
155 metadata,
156 pool: pool_state,
157 position_nft_mint,
158 user: Pubkey::default(), liquidity,
160 amount0_max,
161 amount1_max,
162 }))
163}
164
165fn parse_decrease_liquidity_event(
167 data: &[u8],
168 signature: Signature,
169 slot: u64,
170 tx_index: u64,
171 block_time_us: Option<i64>,
172 grpc_recv_us: i64,
173) -> Option<DexEvent> {
174 let mut offset = 0;
175
176 let pool_state = read_pubkey(data, offset)?;
177 offset += 32;
178
179 let position_nft_mint = read_pubkey(data, offset)?;
180 offset += 32;
181
182 let liquidity = read_u128_le(data, offset)?;
183 offset += 16;
184
185 let amount0_min = read_u64_le(data, offset)?;
186 offset += 8;
187
188 let amount1_min = read_u64_le(data, offset)?;
189
190 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
191
192 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
193 metadata,
194 pool: pool_state,
195 position_nft_mint,
196 user: Pubkey::default(), liquidity,
198 amount0_min,
199 amount1_min,
200 }))
201}
202
203fn parse_create_pool_event(
205 data: &[u8],
206 signature: Signature,
207 slot: u64,
208 tx_index: u64,
209 block_time_us: Option<i64>,
210 grpc_recv_us: i64,
211) -> Option<DexEvent> {
212 let mut offset = 0;
213
214 let pool_state = read_pubkey(data, offset)?;
215 offset += 32;
216
217 let creator = read_pubkey(data, offset)?;
218 offset += 32;
219
220 let sqrt_price_x64 = read_u128_le(data, offset)?;
221 offset += 16;
222
223 let open_time = read_u64_le(data, offset)?;
224
225 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
226
227 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
228 metadata,
229 pool: pool_state,
230 token_0_mint: Pubkey::default(), token_1_mint: Pubkey::default(), tick_spacing: 0, fee_rate: 0, creator,
235 sqrt_price_x64,
236 open_time,
237 }))
238}
239
240fn parse_collect_fee_event(
242 data: &[u8],
243 signature: Signature,
244 slot: u64,
245 tx_index: u64,
246 block_time_us: Option<i64>,
247 grpc_recv_us: i64,
248) -> Option<DexEvent> {
249 let mut offset = 0;
250
251 let pool_state = read_pubkey(data, offset)?;
252 offset += 32;
253
254 let position_nft_mint = read_pubkey(data, offset)?;
255 offset += 32;
256
257 let amount_0 = read_u64_le(data, offset)?;
258 offset += 8;
259
260 let amount_1 = read_u64_le(data, offset)?;
261
262 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
263
264 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
265 metadata,
266 pool_state,
267 position_nft_mint,
268 amount_0,
269 amount_1,
270 }))
271}
272
273fn parse_text_log(
275 log: &str,
276 signature: Signature,
277 slot: u64,
278 tx_index: u64,
279 block_time_us: Option<i64>,
280 grpc_recv_us: i64,
281) -> Option<DexEvent> {
282 use super::utils::text_parser::*;
283
284 if log.contains("swap") || log.contains("Swap") {
285 return parse_swap_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
286 }
287
288 if log.contains("increase") && log.contains("liquidity") {
289 return parse_increase_liquidity_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
290 }
291
292 if log.contains("decrease") && log.contains("liquidity") {
293 return parse_decrease_liquidity_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
294 }
295
296 if log.contains("create") && log.contains("pool") {
297 return parse_create_pool_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
298 }
299
300 if log.contains("collect") && log.contains("fee") {
301 return parse_collect_fee_from_text(log, signature, slot, tx_index, block_time_us, grpc_recv_us);
302 }
303
304 None
305}
306
307fn parse_swap_from_text(
309 log: &str,
310 signature: Signature,
311 slot: u64,
312 tx_index: u64,
313 block_time_us: Option<i64>,
314 grpc_recv_us: i64,
315) -> Option<DexEvent> {
316 use super::utils::text_parser::*;
317
318 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, Pubkey::default(), grpc_recv_us);
319 let is_base_input = detect_trade_type(log).unwrap_or(true);
320
321 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
322 metadata,
323
324 pool_state: Pubkey::default(),
326 sender: Pubkey::default(),
327 token_account_0: Pubkey::default(),
328 token_account_1: Pubkey::default(),
329 amount_0: 0,
330 transfer_fee_0: 0,
331 amount_1: 0,
332 transfer_fee_1: 0,
333 zero_for_one: is_base_input,
334 sqrt_price_x64: 0,
335 liquidity: 0,
337 tick: 0,
338
339 }))
345}
346
347fn parse_increase_liquidity_from_text(
349 log: &str,
350 signature: Signature,
351 slot: u64,
352 tx_index: u64,
353 block_time_us: Option<i64>,
354 grpc_recv_us: i64,
355) -> Option<DexEvent> {
356 use super::utils::text_parser::*;
357
358 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, Pubkey::default(), grpc_recv_us);
359
360 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
361 metadata,
362 pool: Pubkey::default(),
363 position_nft_mint: Pubkey::default(),
364 user: Pubkey::default(),
365 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
366 amount0_max: extract_number_from_text(log, "amount0_max").unwrap_or(1_000_000),
367 amount1_max: extract_number_from_text(log, "amount1_max").unwrap_or(1_000_000),
368 }))
369}
370
371fn parse_decrease_liquidity_from_text(
373 log: &str,
374 signature: Signature,
375 slot: u64,
376 tx_index: u64,
377 block_time_us: Option<i64>,
378 grpc_recv_us: i64,
379) -> Option<DexEvent> {
380 use super::utils::text_parser::*;
381
382 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, Pubkey::default(), grpc_recv_us);
383
384 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
385 metadata,
386 pool: Pubkey::default(),
387 position_nft_mint: Pubkey::default(),
388 user: Pubkey::default(),
389 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
390 amount0_min: extract_number_from_text(log, "amount0_min").unwrap_or(1_000_000),
391 amount1_min: extract_number_from_text(log, "amount1_min").unwrap_or(1_000_000),
392 }))
393}
394
395fn parse_create_pool_from_text(
397 log: &str,
398 signature: Signature,
399 slot: u64,
400 tx_index: u64,
401 block_time_us: Option<i64>,
402 grpc_recv_us: i64,
403) -> Option<DexEvent> {
404 use super::utils::text_parser::*;
405
406 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, Pubkey::default(), grpc_recv_us);
407
408 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
409 metadata,
410 pool: Pubkey::default(),
411 token_0_mint: Pubkey::default(),
412 token_1_mint: Pubkey::default(),
413 tick_spacing: 0,
414 fee_rate: 0,
415 creator: Pubkey::default(),
416 sqrt_price_x64: 0,
417 open_time: 0,
418 }))
419}
420
421fn parse_collect_fee_from_text(
423 log: &str,
424 signature: Signature,
425 slot: u64,
426 tx_index: u64,
427 block_time_us: Option<i64>,
428 grpc_recv_us: i64,
429) -> Option<DexEvent> {
430 use super::utils::text_parser::*;
431
432 let metadata = create_metadata_simple(signature, slot, tx_index, block_time_us, Pubkey::default(), grpc_recv_us);
433
434 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
435 metadata,
436 pool_state: Pubkey::default(),
437 position_nft_mint: Pubkey::default(),
438 amount_0: extract_number_from_text(log, "amount_0").unwrap_or(10_000),
439 amount_1: extract_number_from_text(log, "amount_1").unwrap_or(10_000),
440 }))
441}
442
443#[inline(always)]
450pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
451 let mut offset = 0;
452
453 let pool_state = read_pubkey(data, offset)?;
454 offset += 32;
455
456 let user = read_pubkey(data, offset)?;
457 offset += 32;
458
459 let _amount = read_u64_le(data, offset)?;
460 offset += 8;
461
462 let _other_amount_threshold = read_u64_le(data, offset)?;
463 offset += 8;
464
465 let sqrt_price_limit_x64 = read_u128_le(data, offset)?;
466 offset += 16;
467
468 let is_base_input = read_bool(data, offset)?;
469
470 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
471 metadata,
472 pool_state,
473 sender: user,
474 token_account_0: Pubkey::default(),
475 token_account_1: Pubkey::default(),
476 amount_0: 0,
477 transfer_fee_0: 0,
478 amount_1: 0,
479 transfer_fee_1: 0,
480 zero_for_one: is_base_input,
481 sqrt_price_x64: sqrt_price_limit_x64,
482 liquidity: 0,
483 tick: 0,
484 }))
485}
486
487#[inline(always)]
489pub fn parse_increase_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
490 let mut offset = 0;
491
492 let pool = read_pubkey(data, offset)?;
493 offset += 32;
494
495 let user = read_pubkey(data, offset)?;
496 offset += 32;
497
498 let liquidity = read_u128_le(data, offset)?;
499 offset += 16;
500
501 let amount0_max = read_u64_le(data, offset)?;
502 offset += 8;
503
504 let amount1_max = read_u64_le(data, offset)?;
505
506 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
507 metadata,
508 pool,
509 position_nft_mint: Pubkey::default(), user,
511 liquidity,
512 amount0_max,
513 amount1_max,
514 }))
515}
516
517#[inline(always)]
519pub fn parse_decrease_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
520 let mut offset = 0;
521
522 let pool = read_pubkey(data, offset)?;
523 offset += 32;
524
525 let user = read_pubkey(data, offset)?;
526 offset += 32;
527
528 let liquidity = read_u128_le(data, offset)?;
529 offset += 16;
530
531 let amount0_min = read_u64_le(data, offset)?;
532 offset += 8;
533
534 let amount1_min = read_u64_le(data, offset)?;
535
536 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
537 metadata,
538 pool,
539 position_nft_mint: Pubkey::default(), user,
541 liquidity,
542 amount0_min,
543 amount1_min,
544 }))
545}
546
547#[inline(always)]
549pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
550 let mut offset = 0;
551
552 let pool = read_pubkey(data, offset)?;
553 offset += 32;
554
555 let creator = read_pubkey(data, offset)?;
556 offset += 32;
557
558 let sqrt_price_x64 = read_u128_le(data, offset)?;
559 offset += 16;
560
561 let open_time = read_u64_le(data, offset)?;
562
563 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
564 metadata,
565 pool,
566 token_0_mint: Pubkey::default(), token_1_mint: Pubkey::default(), tick_spacing: 0, fee_rate: 0, creator,
571 sqrt_price_x64,
572 open_time,
573 }))
574}
575
576#[inline(always)]
578pub fn parse_collect_fee_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
579 let mut offset = 0;
580
581 let pool_state = read_pubkey(data, offset)?;
582 offset += 32;
583
584 let position_nft_mint = read_pubkey(data, offset)?;
585 offset += 32;
586
587 let amount_0 = read_u64_le(data, offset)?;
588 offset += 8;
589
590 let amount_1 = read_u64_le(data, offset)?;
591
592 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
593 metadata,
594 pool_state,
595 position_nft_mint,
596 amount_0,
597 amount_1,
598 }))
599}