1use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
8
9pub mod discriminators {
11 pub const SWAP_BASE_IN_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 9];
13 pub const SWAP_BASE_OUT_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 11];
14 pub const DEPOSIT_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 3];
15 pub const WITHDRAW_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 4];
16 pub const INITIALIZE2_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 1];
17 pub const WITHDRAW_PNL_EVENT: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 7];
18}
19
20pub const PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
22
23#[inline]
25pub fn parse_log(log: &str, signature: Signature, slot: u64, tx_index: u64, block_time: Option<i64>, grpc_recv_us: i64) -> Option<DexEvent> {
26 parse_structured_log(log, signature, slot, tx_index, block_time, grpc_recv_us)
27}
28
29fn parse_structured_log(
31 log: &str,
32 signature: Signature,
33 slot: u64,
34 tx_index: u64,
35 block_time: Option<i64>,
36 grpc_recv_us: i64,
37) -> Option<DexEvent> {
38 let program_data = extract_program_data(log)?;
39 if program_data.len() < 8 {
40 return None;
41 }
42
43 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
44 let data = &program_data[8..];
45
46 match discriminator {
47 discriminators::SWAP_BASE_IN_EVENT => {
48 parse_swap_base_in_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
49 },
50 discriminators::SWAP_BASE_OUT_EVENT => {
51 parse_swap_base_out_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
52 },
53 discriminators::DEPOSIT_EVENT => {
54 parse_deposit_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
55 },
56 discriminators::WITHDRAW_EVENT => {
57 parse_withdraw_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
58 },
59 discriminators::INITIALIZE2_EVENT => {
60 parse_initialize2_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
61 },
62 discriminators::WITHDRAW_PNL_EVENT => {
63 parse_withdraw_pnl_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
64 },
65 _ => None,
66 }
67}
68
69fn parse_swap_base_in_event(
71 data: &[u8],
72 signature: Signature,
73 slot: u64,
74 tx_index: u64,
75 block_time: Option<i64>,
76 grpc_recv_us: i64,
77) -> Option<DexEvent> {
78 let mut offset = 0;
79
80 let amm = read_pubkey(data, offset)?;
81 offset += 32;
82
83 let user = read_pubkey(data, offset)?;
84 offset += 32;
85
86 let amount_in = read_u64_le(data, offset)?;
87 offset += 8;
88
89 let minimum_amount_out = read_u64_le(data, offset)?;
90
91 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
92
93 Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
94 metadata,
95 amount_in,
96 minimum_amount_out,
97 max_amount_in: 0,
98 amount_out: 0,
99 token_program: Pubkey::default(),
100 amm,
101 amm_authority: Pubkey::default(),
102 amm_open_orders: Pubkey::default(),
103 amm_target_orders: None,
104 pool_coin_token_account: Pubkey::default(),
105 pool_pc_token_account: Pubkey::default(),
106 serum_program: Pubkey::default(),
107 serum_market: Pubkey::default(),
108 serum_bids: Pubkey::default(),
109 serum_asks: Pubkey::default(),
110 serum_event_queue: Pubkey::default(),
111 serum_coin_vault_account: Pubkey::default(),
112 serum_pc_vault_account: Pubkey::default(),
113 serum_vault_signer: Pubkey::default(),
114 user_source_token_account: Pubkey::default(),
115 user_destination_token_account: Pubkey::default(),
116 user_source_owner: user,
117 }))
118}
119
120fn parse_swap_base_out_event(
122 data: &[u8],
123 signature: Signature,
124 slot: u64,
125 tx_index: u64,
126 block_time: Option<i64>,
127 grpc_recv_us: i64,
128) -> Option<DexEvent> {
129 let mut offset = 0;
130
131 let amm = read_pubkey(data, offset)?;
132 offset += 32;
133
134 let user = read_pubkey(data, offset)?;
135 offset += 32;
136
137 let max_amount_in = read_u64_le(data, offset)?;
138 offset += 8;
139
140 let amount_out = read_u64_le(data, offset)?;
141
142 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
143
144 Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
145 metadata,
146 amount_in: 0,
147 minimum_amount_out: 0,
148 max_amount_in,
149 amount_out,
150 token_program: Pubkey::default(),
151 amm,
152 amm_authority: Pubkey::default(),
153 amm_open_orders: Pubkey::default(),
154 amm_target_orders: None,
155 pool_coin_token_account: Pubkey::default(),
156 pool_pc_token_account: Pubkey::default(),
157 serum_program: Pubkey::default(),
158 serum_market: Pubkey::default(),
159 serum_bids: Pubkey::default(),
160 serum_asks: Pubkey::default(),
161 serum_event_queue: Pubkey::default(),
162 serum_coin_vault_account: Pubkey::default(),
163 serum_pc_vault_account: Pubkey::default(),
164 serum_vault_signer: Pubkey::default(),
165 user_source_token_account: Pubkey::default(),
166 user_destination_token_account: Pubkey::default(),
167 user_source_owner: user,
168 }))
169}
170
171fn parse_deposit_event(
173 data: &[u8],
174 signature: Signature,
175 slot: u64,
176 tx_index: u64,
177 block_time: Option<i64>,
178 grpc_recv_us: i64,
179) -> Option<DexEvent> {
180 let mut offset = 0;
181
182 let amm = read_pubkey(data, offset)?;
183 offset += 32;
184
185 let user = read_pubkey(data, offset)?;
186 offset += 32;
187
188 let max_coin_amount = read_u64_le(data, offset)?;
189 offset += 8;
190
191 let max_pc_amount = read_u64_le(data, offset)?;
192 offset += 8;
193
194 let base_side = read_u64_le(data, offset)?;
195
196 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
197
198 Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
199 metadata,
200 max_coin_amount,
201 max_pc_amount,
202 base_side,
203 token_program: Pubkey::default(),
204 amm,
205 amm_authority: Pubkey::default(),
206 amm_open_orders: Pubkey::default(),
207 amm_target_orders: Pubkey::default(),
208 lp_mint_address: Pubkey::default(),
209 pool_coin_token_account: Pubkey::default(),
210 pool_pc_token_account: Pubkey::default(),
211 serum_market: Pubkey::default(),
212 user_coin_token_account: Pubkey::default(),
213 user_pc_token_account: Pubkey::default(),
214 user_lp_token_account: Pubkey::default(),
215 user_owner: user,
216 serum_event_queue: Pubkey::default(),
217 }))
218}
219
220fn parse_withdraw_event(
222 data: &[u8],
223 signature: Signature,
224 slot: u64,
225 tx_index: u64,
226 block_time: Option<i64>,
227 grpc_recv_us: i64,
228) -> Option<DexEvent> {
229 let mut offset = 0;
230
231 let amm = read_pubkey(data, offset)?;
232 offset += 32;
233
234 let user = read_pubkey(data, offset)?;
235 offset += 32;
236
237 let amount = read_u64_le(data, offset)?;
238
239 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
240
241 Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
242 metadata,
243 amount,
244 token_program: Pubkey::default(),
245 amm,
246 amm_authority: Pubkey::default(),
247 amm_open_orders: Pubkey::default(),
248 amm_target_orders: Pubkey::default(),
249 lp_mint_address: Pubkey::default(),
250 pool_coin_token_account: Pubkey::default(),
251 pool_pc_token_account: Pubkey::default(),
252 pool_withdraw_queue: Pubkey::default(),
253 pool_temp_lp_token_account: Pubkey::default(),
254 serum_program: Pubkey::default(),
255 serum_market: Pubkey::default(),
256 serum_coin_vault_account: Pubkey::default(),
257 serum_pc_vault_account: Pubkey::default(),
258 serum_vault_signer: Pubkey::default(),
259 user_lp_token_account: Pubkey::default(),
260 user_coin_token_account: Pubkey::default(),
261 user_pc_token_account: Pubkey::default(),
262 user_owner: user,
263 serum_event_queue: Pubkey::default(),
264 serum_bids: Pubkey::default(),
265 serum_asks: Pubkey::default(),
266 }))
267}
268
269fn parse_initialize2_event(
271 data: &[u8],
272 signature: Signature,
273 slot: u64,
274 tx_index: u64,
275 block_time: Option<i64>,
276 grpc_recv_us: i64,
277) -> Option<DexEvent> {
278 let mut offset = 0;
279
280 let amm = read_pubkey(data, offset)?;
281 offset += 32;
282
283 let user = read_pubkey(data, offset)?;
284 offset += 32;
285
286 let nonce = data.get(offset)?.clone();
287 offset += 1;
288
289 let open_time = read_u64_le(data, offset)?;
290 offset += 8;
291
292 let init_pc_amount = read_u64_le(data, offset)?;
293 offset += 8;
294
295 let init_coin_amount = read_u64_le(data, offset)?;
296
297 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
298
299 Some(DexEvent::RaydiumAmmV4Initialize2(RaydiumAmmV4Initialize2Event {
300 metadata,
301 nonce,
302 open_time,
303 init_pc_amount,
304 init_coin_amount,
305 token_program: Pubkey::default(),
306 spl_associated_token_account: Pubkey::default(),
307 system_program: Pubkey::default(),
308 rent: Pubkey::default(),
309 amm,
310 amm_authority: Pubkey::default(),
311 amm_open_orders: Pubkey::default(),
312 lp_mint: Pubkey::default(),
313 coin_mint: Pubkey::default(),
314 pc_mint: Pubkey::default(),
315 pool_coin_token_account: Pubkey::default(),
316 pool_pc_token_account: Pubkey::default(),
317 pool_withdraw_queue: Pubkey::default(),
318 amm_target_orders: Pubkey::default(),
319 pool_temp_lp: Pubkey::default(),
320 serum_program: Pubkey::default(),
321 serum_market: Pubkey::default(),
322 user_wallet: user,
323 user_token_coin: Pubkey::default(),
324 user_token_pc: Pubkey::default(),
325 user_lp_token_account: Pubkey::default(),
326 }))
327}
328
329fn parse_withdraw_pnl_event(
331 data: &[u8],
332 signature: Signature,
333 slot: u64,
334 tx_index: u64,
335 block_time: Option<i64>,
336 grpc_recv_us: i64,
337) -> Option<DexEvent> {
338 let mut offset = 0;
339
340 let amm = read_pubkey(data, offset)?;
341 offset += 32;
342
343 let pnl_owner = read_pubkey(data, offset)?;
344
345 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, amm, grpc_recv_us);
346
347 Some(DexEvent::RaydiumAmmV4WithdrawPnl(RaydiumAmmV4WithdrawPnlEvent {
348 metadata,
349 token_program: Pubkey::default(),
350 amm,
351 amm_config: Pubkey::default(),
352 amm_authority: Pubkey::default(),
353 amm_open_orders: Pubkey::default(),
354 pool_coin_token_account: Pubkey::default(),
355 pool_pc_token_account: Pubkey::default(),
356 coin_pnl_token_account: Pubkey::default(),
357 pc_pnl_token_account: Pubkey::default(),
358 pnl_owner,
359 amm_target_orders: Pubkey::default(),
360 serum_program: Pubkey::default(),
361 serum_market: Pubkey::default(),
362 serum_event_queue: Pubkey::default(),
363 serum_coin_vault_account: Pubkey::default(),
364 serum_pc_vault_account: Pubkey::default(),
365 serum_vault_signer: Pubkey::default(),
366 }))
367}
368
369fn parse_text_log(
371 log: &str,
372 signature: Signature,
373 slot: u64,
374 tx_index: u64,
375 block_time: Option<i64>,
376 grpc_recv_us: i64,
377) -> Option<DexEvent> {
378 if log.contains("swap") || log.contains("Swap") {
380 return parse_swap_log_fallback(log, signature, slot, tx_index, block_time, grpc_recv_us);
381 }
382
383 if log.contains("deposit") || log.contains("Deposit") {
385 return parse_deposit_log_fallback(log, signature, slot, tx_index, block_time, grpc_recv_us);
386 }
387
388 if log.contains("withdraw") || log.contains("Withdraw") {
390 return parse_withdraw_log_fallback(log, signature, slot, tx_index, block_time, grpc_recv_us);
391 }
392
393 None
394}
395
396fn parse_swap_log_fallback(
398 log: &str,
399 signature: Signature,
400 slot: u64,
401 tx_index: u64,
402 block_time: Option<i64>,
403 grpc_recv_us: i64,
404) -> Option<DexEvent> {
405 let amount_in = super::utils::text_parser::extract_number_from_text(log, "amount_in")
407 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountIn"))
408 .unwrap_or(0);
409
410 let amount_out = super::utils::text_parser::extract_number_from_text(log, "amount_out")
411 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "amountOut"))
412 .unwrap_or(0);
413
414 let minimum_amount_out = super::utils::text_parser::extract_number_from_text(log, "minimum_amount_out")
415 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "minimumAmountOut"))
416 .unwrap_or(0);
417
418 let max_amount_in = super::utils::text_parser::extract_number_from_text(log, "max_amount_in")
419 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxAmountIn"))
420 .unwrap_or(0);
421
422 let default_pubkey = Pubkey::default();
423 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, default_pubkey, grpc_recv_us);
424
425 Some(DexEvent::RaydiumAmmV4Swap(RaydiumAmmV4SwapEvent {
426 metadata,
427 amount_in,
428 minimum_amount_out,
429 max_amount_in,
430 amount_out,
431 token_program: default_pubkey,
432 amm: default_pubkey,
433 amm_authority: default_pubkey,
434 amm_open_orders: default_pubkey,
435 amm_target_orders: None,
436 pool_coin_token_account: default_pubkey,
437 pool_pc_token_account: default_pubkey,
438 serum_program: default_pubkey,
439 serum_market: default_pubkey,
440 serum_bids: default_pubkey,
441 serum_asks: default_pubkey,
442 serum_event_queue: default_pubkey,
443 serum_coin_vault_account: default_pubkey,
444 serum_pc_vault_account: default_pubkey,
445 serum_vault_signer: default_pubkey,
446 user_source_token_account: default_pubkey,
447 user_destination_token_account: default_pubkey,
448 user_source_owner: default_pubkey,
449 }))
450}
451
452fn parse_deposit_log_fallback(
454 log: &str,
455 signature: Signature,
456 slot: u64,
457 tx_index: u64,
458 block_time: Option<i64>,
459 grpc_recv_us: i64,
460) -> Option<DexEvent> {
461 let max_coin_amount = super::utils::text_parser::extract_number_from_text(log, "max_coin_amount")
462 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxCoinAmount"))
463 .unwrap_or(0);
464
465 let max_pc_amount = super::utils::text_parser::extract_number_from_text(log, "max_pc_amount")
466 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "maxPcAmount"))
467 .unwrap_or(0);
468
469 let base_side = super::utils::text_parser::extract_number_from_text(log, "base_side")
470 .or_else(|| super::utils::text_parser::extract_number_from_text(log, "baseSide"))
471 .unwrap_or(0);
472
473 let default_pubkey = Pubkey::default();
474 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, default_pubkey, grpc_recv_us);
475
476 Some(DexEvent::RaydiumAmmV4Deposit(RaydiumAmmV4DepositEvent {
477 metadata,
478 max_coin_amount,
479 max_pc_amount,
480 base_side,
481 token_program: default_pubkey,
482 amm: default_pubkey,
483 amm_authority: default_pubkey,
484 amm_open_orders: default_pubkey,
485 amm_target_orders: default_pubkey,
486 lp_mint_address: default_pubkey,
487 pool_coin_token_account: default_pubkey,
488 pool_pc_token_account: default_pubkey,
489 serum_market: default_pubkey,
490 user_coin_token_account: default_pubkey,
491 user_pc_token_account: default_pubkey,
492 user_lp_token_account: default_pubkey,
493 user_owner: default_pubkey,
494 serum_event_queue: default_pubkey,
495 }))
496}
497
498fn parse_withdraw_log_fallback(
500 log: &str,
501 signature: Signature,
502 slot: u64,
503 tx_index: u64,
504 block_time: Option<i64>,
505 grpc_recv_us: i64,
506) -> Option<DexEvent> {
507 let amount = super::utils::text_parser::extract_number_from_text(log, "amount")
508 .unwrap_or(0);
509
510 let default_pubkey = Pubkey::default();
511 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, default_pubkey, grpc_recv_us);
512
513 Some(DexEvent::RaydiumAmmV4Withdraw(RaydiumAmmV4WithdrawEvent {
514 metadata,
515 amount,
516 token_program: default_pubkey,
517 amm: default_pubkey,
518 amm_authority: default_pubkey,
519 amm_open_orders: default_pubkey,
520 amm_target_orders: default_pubkey,
521 lp_mint_address: default_pubkey,
522 pool_coin_token_account: default_pubkey,
523 pool_pc_token_account: default_pubkey,
524 pool_withdraw_queue: default_pubkey,
525 pool_temp_lp_token_account: default_pubkey,
526 serum_program: default_pubkey,
527 serum_market: default_pubkey,
528 serum_coin_vault_account: default_pubkey,
529 serum_pc_vault_account: default_pubkey,
530 serum_vault_signer: default_pubkey,
531 user_lp_token_account: default_pubkey,
532 user_coin_token_account: default_pubkey,
533 user_pc_token_account: default_pubkey,
534 user_owner: default_pubkey,
535 serum_event_queue: default_pubkey,
536 serum_bids: default_pubkey,
537 serum_asks: default_pubkey,
538 }))
539}