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: Option<i64>, grpc_recv_us: i64) -> Option<DexEvent> {
31 parse_structured_log(log, signature, slot, tx_index, block_time, 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: 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, grpc_recv_us)
54 },
55 discriminators::INCREASE_LIQUIDITY => {
56 parse_increase_liquidity_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
57 },
58 discriminators::DECREASE_LIQUIDITY => {
59 parse_decrease_liquidity_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
60 },
61 discriminators::CREATE_POOL => {
62 parse_create_pool_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
63 },
64 discriminators::COLLECT_FEE => {
65 parse_collect_fee_event(data, signature, slot, tx_index, block_time, 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: 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, 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: 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, pool_state, grpc_recv_us);
153
154 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
155 metadata,
156 pool: pool_state,
157 user: position_nft_mint, liquidity,
159 amount0_max,
160 amount1_max,
161 }))
162}
163
164fn parse_decrease_liquidity_event(
166 data: &[u8],
167 signature: Signature,
168 slot: u64,
169 tx_index: u64,
170 block_time: Option<i64>,
171 grpc_recv_us: i64,
172) -> Option<DexEvent> {
173 let mut offset = 0;
174
175 let pool_state = read_pubkey(data, offset)?;
176 offset += 32;
177
178 let position_nft_mint = read_pubkey(data, offset)?;
179 offset += 32;
180
181 let liquidity = read_u128_le(data, offset)?;
182 offset += 16;
183
184 let amount0_min = read_u64_le(data, offset)?;
185 offset += 8;
186
187 let amount1_min = read_u64_le(data, offset)?;
188
189 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool_state, grpc_recv_us);
190
191 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
192 metadata,
193 pool: pool_state,
194 user: position_nft_mint, liquidity,
196 amount0_min,
197 amount1_min,
198 }))
199}
200
201fn parse_create_pool_event(
203 data: &[u8],
204 signature: Signature,
205 slot: u64,
206 tx_index: u64,
207 block_time: Option<i64>,
208 grpc_recv_us: i64,
209) -> Option<DexEvent> {
210 let mut offset = 0;
211
212 let pool_state = read_pubkey(data, offset)?;
213 offset += 32;
214
215 let creator = read_pubkey(data, offset)?;
216 offset += 32;
217
218 let sqrt_price_x64 = read_u128_le(data, offset)?;
219 offset += 16;
220
221 let open_time = read_u64_le(data, offset)?;
222
223 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool_state, grpc_recv_us);
224
225 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
226 metadata,
227 pool: pool_state,
228 creator,
229 sqrt_price_x64,
230 open_time,
231 }))
232}
233
234fn parse_collect_fee_event(
236 data: &[u8],
237 signature: Signature,
238 slot: u64,
239 tx_index: u64,
240 block_time: Option<i64>,
241 grpc_recv_us: i64,
242) -> Option<DexEvent> {
243 let mut offset = 0;
244
245 let pool_state = read_pubkey(data, offset)?;
246 offset += 32;
247
248 let position_nft_mint = read_pubkey(data, offset)?;
249 offset += 32;
250
251 let amount_0 = read_u64_le(data, offset)?;
252 offset += 8;
253
254 let amount_1 = read_u64_le(data, offset)?;
255
256 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool_state, grpc_recv_us);
257
258 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
259 metadata,
260 pool_state,
261 position_nft_mint,
262 amount_0,
263 amount_1,
264 }))
265}
266
267fn parse_text_log(
269 log: &str,
270 signature: Signature,
271 slot: u64,
272 tx_index: u64,
273 block_time: Option<i64>,
274 grpc_recv_us: i64,
275) -> Option<DexEvent> {
276 use super::utils::text_parser::*;
277
278 if log.contains("swap") || log.contains("Swap") {
279 return parse_swap_from_text(log, signature, slot, tx_index, block_time, grpc_recv_us);
280 }
281
282 if log.contains("increase") && log.contains("liquidity") {
283 return parse_increase_liquidity_from_text(log, signature, slot, tx_index, block_time, grpc_recv_us);
284 }
285
286 if log.contains("decrease") && log.contains("liquidity") {
287 return parse_decrease_liquidity_from_text(log, signature, slot, tx_index, block_time, grpc_recv_us);
288 }
289
290 if log.contains("create") && log.contains("pool") {
291 return parse_create_pool_from_text(log, signature, slot, tx_index, block_time, grpc_recv_us);
292 }
293
294 if log.contains("collect") && log.contains("fee") {
295 return parse_collect_fee_from_text(log, signature, slot, tx_index, block_time, grpc_recv_us);
296 }
297
298 None
299}
300
301fn parse_swap_from_text(
303 log: &str,
304 signature: Signature,
305 slot: u64,
306 tx_index: u64,
307 block_time: Option<i64>,
308 grpc_recv_us: i64,
309) -> Option<DexEvent> {
310 use super::utils::text_parser::*;
311
312 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
313 let is_base_input = detect_trade_type(log).unwrap_or(true);
314
315 Some(DexEvent::RaydiumClmmSwap(RaydiumClmmSwapEvent {
316 metadata,
317
318 pool_state: Pubkey::default(),
320 sender: Pubkey::default(),
321 token_account_0: Pubkey::default(),
322 token_account_1: Pubkey::default(),
323 amount_0: 0,
324 transfer_fee_0: 0,
325 amount_1: 0,
326 transfer_fee_1: 0,
327 zero_for_one: is_base_input,
328 sqrt_price_x64: 0,
329 liquidity: 0,
331 tick: 0,
332
333 }))
339}
340
341fn parse_increase_liquidity_from_text(
343 log: &str,
344 signature: Signature,
345 slot: u64,
346 tx_index: u64,
347 block_time: Option<i64>,
348 grpc_recv_us: i64,
349) -> Option<DexEvent> {
350 use super::utils::text_parser::*;
351
352 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
353
354 Some(DexEvent::RaydiumClmmIncreaseLiquidity(RaydiumClmmIncreaseLiquidityEvent {
355 metadata,
356 pool: Pubkey::default(),
357 user: Pubkey::default(),
358 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
359 amount0_max: extract_number_from_text(log, "amount0_max").unwrap_or(1_000_000),
360 amount1_max: extract_number_from_text(log, "amount1_max").unwrap_or(1_000_000),
361 }))
362}
363
364fn parse_decrease_liquidity_from_text(
366 log: &str,
367 signature: Signature,
368 slot: u64,
369 tx_index: u64,
370 block_time: Option<i64>,
371 grpc_recv_us: i64,
372) -> Option<DexEvent> {
373 use super::utils::text_parser::*;
374
375 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
376
377 Some(DexEvent::RaydiumClmmDecreaseLiquidity(RaydiumClmmDecreaseLiquidityEvent {
378 metadata,
379 pool: Pubkey::default(),
380 user: Pubkey::default(),
381 liquidity: extract_number_from_text(log, "liquidity").unwrap_or(1_000_000) as u128,
382 amount0_min: extract_number_from_text(log, "amount0_min").unwrap_or(1_000_000),
383 amount1_min: extract_number_from_text(log, "amount1_min").unwrap_or(1_000_000),
384 }))
385}
386
387fn parse_create_pool_from_text(
389 log: &str,
390 signature: Signature,
391 slot: u64,
392 tx_index: u64,
393 block_time: Option<i64>,
394 grpc_recv_us: i64,
395) -> Option<DexEvent> {
396 use super::utils::text_parser::*;
397
398 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
399
400 Some(DexEvent::RaydiumClmmCreatePool(RaydiumClmmCreatePoolEvent {
401 metadata,
402 pool: Pubkey::default(),
403 creator: Pubkey::default(),
404 sqrt_price_x64: 0,
405 open_time: block_time.unwrap_or(0) as u64,
406 }))
407}
408
409fn parse_collect_fee_from_text(
411 log: &str,
412 signature: Signature,
413 slot: u64,
414 tx_index: u64,
415 block_time: Option<i64>,
416 grpc_recv_us: i64,
417) -> Option<DexEvent> {
418 use super::utils::text_parser::*;
419
420 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
421
422 Some(DexEvent::RaydiumClmmCollectFee(RaydiumClmmCollectFeeEvent {
423 metadata,
424 pool_state: Pubkey::default(),
425 position_nft_mint: Pubkey::default(),
426 amount_0: extract_number_from_text(log, "amount_0").unwrap_or(10_000),
427 amount_1: extract_number_from_text(log, "amount_1").unwrap_or(10_000),
428 }))
429}