1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::signature::Signature;
8
9pub mod discriminators {
11 pub const SWAP_EVENT: [u8; 8] = [27, 60, 21, 213, 138, 170, 187, 147];
12 pub const SWAP2_EVENT: [u8; 8] = [189, 66, 51, 168, 38, 80, 117, 153];
13 pub const ADD_LIQUIDITY_EVENT: [u8; 8] = [175, 242, 8, 157, 30, 247, 185, 169];
14 pub const REMOVE_LIQUIDITY_EVENT: [u8; 8] = [87, 46, 88, 98, 175, 96, 34, 91];
15 pub const INITIALIZE_POOL_EVENT: [u8; 8] = [228, 50, 246, 85, 203, 66, 134, 37];
16 pub const CREATE_POSITION_EVENT: [u8; 8] = [156, 15, 119, 198, 29, 181, 221, 55];
17 pub const CLOSE_POSITION_EVENT: [u8; 8] = [20, 145, 144, 68, 143, 142, 214, 178];
18 pub const CLAIM_POSITION_FEE_EVENT: [u8; 8] = [198, 182, 183, 52, 97, 12, 49, 56];
19 pub const INITIALIZE_REWARD_EVENT: [u8; 8] = [129, 91, 188, 3, 246, 52, 185, 249];
20 pub const FUND_REWARD_EVENT: [u8; 8] = [104, 233, 237, 122, 199, 191, 121, 85];
21 pub const CLAIM_REWARD_EVENT: [u8; 8] = [218, 86, 147, 200, 235, 188, 215, 231];
22}
23
24pub fn parse_log(
26 log: &str,
27 signature: Signature,
28 slot: u64,
29 tx_index: u64,
30 block_time_us: Option<i64>,
31 grpc_recv_us: i64,
32) -> Option<DexEvent> {
33 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
34}
35
36fn parse_structured_log(
38 log: &str,
39 signature: Signature,
40 slot: u64,
41 tx_index: u64,
42 block_time_us: Option<i64>,
43 grpc_recv_us: i64,
44) -> Option<DexEvent> {
45 let program_data = extract_program_data(log)?;
46
47 if program_data.len() < 8 {
48 return None;
49 }
50
51 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
52 let data = &program_data[8..];
53
54 match discriminator {
55 discriminators::SWAP_EVENT => {
56 parse_swap_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
57 }
58 discriminators::SWAP2_EVENT => {
59 parse_swap2_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
60 }
61 discriminators::ADD_LIQUIDITY_EVENT => {
62 parse_add_liquidity_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
63 }
64 discriminators::REMOVE_LIQUIDITY_EVENT => parse_remove_liquidity_event(
65 data,
66 signature,
67 slot,
68 tx_index,
69 block_time_us,
70 grpc_recv_us,
71 ),
72 discriminators::INITIALIZE_POOL_EVENT => parse_initialize_pool_event(
73 data,
74 signature,
75 slot,
76 tx_index,
77 block_time_us,
78 grpc_recv_us,
79 ),
80 discriminators::CREATE_POSITION_EVENT => parse_create_position_event(
81 data,
82 signature,
83 slot,
84 tx_index,
85 block_time_us,
86 grpc_recv_us,
87 ),
88 discriminators::CLOSE_POSITION_EVENT => {
89 parse_close_position_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
90 }
91 discriminators::CLAIM_POSITION_FEE_EVENT => parse_claim_position_fee_event(
92 data,
93 signature,
94 slot,
95 tx_index,
96 block_time_us,
97 grpc_recv_us,
98 ),
99 discriminators::INITIALIZE_REWARD_EVENT => parse_initialize_reward_event(
100 data,
101 signature,
102 slot,
103 tx_index,
104 block_time_us,
105 grpc_recv_us,
106 ),
107 discriminators::FUND_REWARD_EVENT => {
108 parse_fund_reward_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
109 }
110 discriminators::CLAIM_REWARD_EVENT => {
111 parse_claim_reward_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
112 }
113 _ => None,
114 }
115}
116
117#[inline(always)]
119pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
120 let mut offset = 0;
121
122 let pool = read_pubkey(data, offset)?;
123 offset += 32;
124
125 let _config = read_pubkey(data, offset)?;
126 offset += 32;
127
128 let trade_direction = read_u8(data, offset)?;
129 offset += 1;
130
131 let has_referral = read_bool(data, offset)?;
132 offset += 1;
133
134 let amount_in = read_u64_le(data, offset)?;
135 offset += 8;
136
137 let minimum_amount_out = read_u64_le(data, offset)?;
138 offset += 8;
139
140 let actual_input_amount = read_u64_le(data, offset)?;
141 offset += 8;
142
143 let output_amount = read_u64_le(data, offset)?;
144 offset += 8;
145
146 let next_sqrt_price = read_u128_le(data, offset)?;
147 offset += 16;
148
149 let lp_fee = read_u64_le(data, offset)?;
150 offset += 8;
151
152 let protocol_fee = read_u64_le(data, offset)?;
153 offset += 8;
154
155 let referral_fee = read_u64_le(data, offset)?;
156 offset += 8;
157
158 let _amount_in_dup = read_u64_le(data, offset)?;
159 offset += 8;
160
161 let current_timestamp = read_u64_le(data, offset)?;
162
163 Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent {
164 metadata,
165 pool,
166 trade_direction,
167 has_referral,
168 amount_in,
169 minimum_amount_out,
170 output_amount,
171 next_sqrt_price,
172 lp_fee,
173 protocol_fee,
174 partner_fee: 0,
175 referral_fee,
176 actual_amount_in: actual_input_amount,
177 current_timestamp,
178 ..Default::default()
179 }))
180}
181
182fn parse_swap_event(
183 data: &[u8],
184 signature: Signature,
185 slot: u64,
186 tx_index: u64,
187 block_time_us: Option<i64>,
188 grpc_recv_us: i64,
189) -> Option<DexEvent> {
190 let pool = read_pubkey(data, 0)?;
191 let metadata =
192 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
193 parse_swap_from_data(data, metadata)
194}
195
196#[inline(always)]
198pub fn parse_swap2_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
199 let mut offset = 0;
200
201 let pool = read_pubkey(data, offset)?;
202 offset += 32;
203
204 let trade_direction = read_u8(data, offset)?;
205 offset += 1;
206
207 let _collect_fee_mode = read_u8(data, offset)?;
208 offset += 1;
209
210 let has_referral = read_bool(data, offset)?;
211 offset += 1;
212
213 let amount_0 = read_u64_le(data, offset)?;
214 offset += 8;
215
216 let amount_1 = read_u64_le(data, offset)?;
217 offset += 8;
218
219 let swap_mode = read_u8(data, offset)?;
220 offset += 1;
221
222 let included_fee_input_amount = read_u64_le(data, offset)?;
223 offset += 8;
224
225 let _excluded_fee_input_amount = read_u64_le(data, offset)?;
226 offset += 8;
227
228 let _amount_left = read_u64_le(data, offset)?;
229 offset += 8;
230
231 let output_amount = read_u64_le(data, offset)?;
232 offset += 8;
233
234 let next_sqrt_price = read_u128_le(data, offset)?;
235 offset += 16;
236
237 let lp_fee = read_u64_le(data, offset)?;
238 offset += 8;
239
240 let protocol_fee = read_u64_le(data, offset)?;
241 offset += 8;
242
243 let referral_fee = read_u64_le(data, offset)?;
244 offset += 8;
245
246 let _quote_reserve_amount = read_u64_le(data, offset)?;
247 offset += 8;
248
249 let _migration_threshold = read_u64_le(data, offset)?;
250 offset += 8;
251
252 let current_timestamp = read_u64_le(data, offset)?;
253
254 let (amount_in, minimum_amount_out) =
255 if swap_mode == 0 { (amount_0, amount_1) } else { (amount_1, amount_0) };
256
257 Some(DexEvent::MeteoraDammV2Swap(MeteoraDammV2SwapEvent {
258 metadata,
259 pool,
260 trade_direction,
261 has_referral,
262 amount_in,
263 minimum_amount_out,
264 output_amount,
265 next_sqrt_price,
266 lp_fee,
267 protocol_fee,
268 partner_fee: 0,
269 referral_fee,
270 actual_amount_in: included_fee_input_amount,
271 current_timestamp,
272 ..Default::default()
273 }))
274}
275
276fn parse_swap2_event(
277 data: &[u8],
278 signature: Signature,
279 slot: u64,
280 tx_index: u64,
281 block_time_us: Option<i64>,
282 grpc_recv_us: i64,
283) -> Option<DexEvent> {
284 let pool = read_pubkey(data, 0)?;
285 let metadata =
286 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
287 parse_swap2_from_data(data, metadata)
288}
289
290#[inline(always)]
292pub fn parse_add_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
293 let mut offset = 0;
294
295 let pool = read_pubkey(data, offset)?;
296 offset += 32;
297
298 let position = read_pubkey(data, offset)?;
299 offset += 32;
300
301 let owner = read_pubkey(data, offset)?;
302 offset += 32;
303
304 let liquidity_delta = read_u128_le(data, offset)?;
305 offset += 16;
306
307 let token_a_amount_threshold = read_u64_le(data, offset)?;
308 offset += 8;
309
310 let token_b_amount_threshold = read_u64_le(data, offset)?;
311 offset += 8;
312
313 let token_a_amount = read_u64_le(data, offset)?;
314 offset += 8;
315
316 let token_b_amount = read_u64_le(data, offset)?;
317 offset += 8;
318
319 let total_amount_a = read_u64_le(data, offset)?;
320 offset += 8;
321
322 let total_amount_b = read_u64_le(data, offset)?;
323
324 Some(DexEvent::MeteoraDammV2AddLiquidity(MeteoraDammV2AddLiquidityEvent {
325 metadata,
326 pool,
327 position,
328 owner,
329 liquidity_delta,
330 token_a_amount_threshold,
331 token_b_amount_threshold,
332 token_a_amount,
333 token_b_amount,
334 total_amount_a,
335 total_amount_b,
336 }))
337}
338
339fn parse_add_liquidity_event(
340 data: &[u8],
341 signature: Signature,
342 slot: u64,
343 tx_index: u64,
344 block_time_us: Option<i64>,
345 grpc_recv_us: i64,
346) -> Option<DexEvent> {
347 let pool = read_pubkey(data, 0)?;
348 let metadata =
349 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
350 parse_add_liquidity_from_data(data, metadata)
351}
352
353#[inline(always)]
355pub fn parse_remove_liquidity_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
356 let mut offset = 0;
357
358 let pool = read_pubkey(data, offset)?;
359 offset += 32;
360
361 let position = read_pubkey(data, offset)?;
362 offset += 32;
363
364 let owner = read_pubkey(data, offset)?;
365 offset += 32;
366
367 let liquidity_delta = read_u128_le(data, offset)?;
368 offset += 16;
369
370 let token_a_amount_threshold = read_u64_le(data, offset)?;
371 offset += 8;
372
373 let token_b_amount_threshold = read_u64_le(data, offset)?;
374 offset += 8;
375
376 let token_a_amount = read_u64_le(data, offset)?;
377 offset += 8;
378
379 let token_b_amount = read_u64_le(data, offset)?;
380
381 Some(DexEvent::MeteoraDammV2RemoveLiquidity(MeteoraDammV2RemoveLiquidityEvent {
382 metadata,
383 pool,
384 position,
385 owner,
386 liquidity_delta,
387 token_a_amount_threshold,
388 token_b_amount_threshold,
389 token_a_amount,
390 token_b_amount,
391 }))
392}
393
394fn parse_remove_liquidity_event(
395 data: &[u8],
396 signature: Signature,
397 slot: u64,
398 tx_index: u64,
399 block_time_us: Option<i64>,
400 grpc_recv_us: i64,
401) -> Option<DexEvent> {
402 let pool = read_pubkey(data, 0)?;
403 let metadata =
404 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
405 parse_remove_liquidity_from_data(data, metadata)
406}
407
408fn parse_initialize_pool_event(
410 data: &[u8],
411 signature: Signature,
412 slot: u64,
413 tx_index: u64,
414 block_time_us: Option<i64>,
415 grpc_recv_us: i64,
416) -> Option<DexEvent> {
417 let pool = read_pubkey(data, 0)?;
418 let metadata =
419 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
420 parse_initialize_pool_from_data(data, metadata)
421}
422
423#[inline(always)]
425pub fn parse_initialize_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
426 let mut offset = 0usize;
427
428 let pool = read_pubkey(data, offset)?;
429 offset += 32;
430 let token_a_mint = read_pubkey(data, offset)?;
431 offset += 32;
432 let token_b_mint = read_pubkey(data, offset)?;
433 offset += 32;
434 let creator = read_pubkey(data, offset)?;
435 offset += 32;
436 let payer = read_pubkey(data, offset)?;
437 offset += 32;
438 let alpha_vault = read_pubkey(data, offset)?;
439 offset += 32;
440
441 offset = skip_pool_fee_parameters(data, offset)?;
442 if data.len() < offset + 109 {
443 return None;
444 }
445
446 let sqrt_min_price = read_u128_le(data, offset)?;
447 offset += 16;
448 let sqrt_max_price = read_u128_le(data, offset)?;
449 offset += 16;
450 let activation_type = read_u8(data, offset)?;
451 offset += 1;
452 let collect_fee_mode = read_u8(data, offset)?;
453 offset += 1;
454 let liquidity = read_u128_le(data, offset)?;
455 offset += 16;
456 let sqrt_price = read_u128_le(data, offset)?;
457 offset += 16;
458 let activation_point = Some(read_u64_le(data, offset)?);
459 offset += 8;
460 let token_a_flag = read_u8(data, offset)?;
461 offset += 1;
462 let token_b_flag = read_u8(data, offset)?;
463 offset += 1;
464 let token_a_amount = read_u64_le(data, offset)?;
465 offset += 8;
466 let token_b_amount = read_u64_le(data, offset)?;
467 offset += 8;
468 let total_amount_a = read_u64_le(data, offset)?;
469 offset += 8;
470 let total_amount_b = read_u64_le(data, offset)?;
471 offset += 8;
472 let pool_type = read_u8(data, offset)?;
473
474 Some(DexEvent::MeteoraDammV2InitializePool(MeteoraDammV2InitializePoolEvent {
475 metadata,
476 pool,
477 token_a_mint,
478 token_b_mint,
479 creator,
480 payer,
481 alpha_vault,
482 sqrt_min_price,
483 sqrt_max_price,
484 activation_type,
485 collect_fee_mode,
486 liquidity,
487 sqrt_price,
488 activation_point,
489 token_a_flag,
490 token_b_flag,
491 token_a_amount,
492 token_b_amount,
493 total_amount_a,
494 total_amount_b,
495 pool_type,
496 ..Default::default()
497 }))
498}
499
500#[inline(always)]
501fn skip_pool_fee_parameters(data: &[u8], offset: usize) -> Option<usize> {
502 let tag_offset = offset + 30;
503 let tag = *data.get(tag_offset)?;
504 match tag {
505 0 => Some(tag_offset + 1),
506 1 => data.get(tag_offset + 1..tag_offset + 33).map(|_| tag_offset + 33),
507 _ => None,
508 }
509}
510
511#[inline(always)]
513pub fn parse_create_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
514 let mut offset = 0;
515
516 let pool = read_pubkey(data, offset)?;
517 offset += 32;
518
519 let owner = read_pubkey(data, offset)?;
520 offset += 32;
521
522 let position = read_pubkey(data, offset)?;
523 offset += 32;
524
525 let position_nft_mint = read_pubkey(data, offset)?;
526
527 Some(DexEvent::MeteoraDammV2CreatePosition(MeteoraDammV2CreatePositionEvent {
528 metadata,
529 pool,
530 owner,
531 position,
532 position_nft_mint,
533 }))
534}
535
536fn parse_create_position_event(
537 data: &[u8],
538 signature: Signature,
539 slot: u64,
540 tx_index: u64,
541 block_time_us: Option<i64>,
542 grpc_recv_us: i64,
543) -> Option<DexEvent> {
544 let pool = read_pubkey(data, 0)?;
545 let metadata =
546 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
547 parse_create_position_from_data(data, metadata)
548}
549
550#[inline(always)]
552pub fn parse_close_position_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
553 let mut offset = 0;
554
555 let pool = read_pubkey(data, offset)?;
556 offset += 32;
557
558 let owner = read_pubkey(data, offset)?;
559 offset += 32;
560
561 let position = read_pubkey(data, offset)?;
562 offset += 32;
563
564 let position_nft_mint = read_pubkey(data, offset)?;
565
566 Some(DexEvent::MeteoraDammV2ClosePosition(MeteoraDammV2ClosePositionEvent {
567 metadata,
568 pool,
569 owner,
570 position,
571 position_nft_mint,
572 }))
573}
574
575fn parse_close_position_event(
576 data: &[u8],
577 signature: Signature,
578 slot: u64,
579 tx_index: u64,
580 block_time_us: Option<i64>,
581 grpc_recv_us: i64,
582) -> Option<DexEvent> {
583 let pool = read_pubkey(data, 0)?;
584 let metadata =
585 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
586 parse_close_position_from_data(data, metadata)
587}
588
589fn parse_claim_position_fee_event(
591 data: &[u8],
592 signature: Signature,
593 slot: u64,
594 tx_index: u64,
595 block_time_us: Option<i64>,
596 grpc_recv_us: i64,
597) -> Option<DexEvent> {
598 None
626}
627
628fn parse_initialize_reward_event(
630 data: &[u8],
631 signature: Signature,
632 slot: u64,
633 tx_index: u64,
634 block_time_us: Option<i64>,
635 grpc_recv_us: i64,
636) -> Option<DexEvent> {
637 None
665}
666
667fn parse_fund_reward_event(
669 data: &[u8],
670 signature: Signature,
671 slot: u64,
672 tx_index: u64,
673 block_time_us: Option<i64>,
674 grpc_recv_us: i64,
675) -> Option<DexEvent> {
676 None
700}
701
702fn parse_claim_reward_event(
704 data: &[u8],
705 signature: Signature,
706 slot: u64,
707 tx_index: u64,
708 block_time_us: Option<i64>,
709 grpc_recv_us: i64,
710) -> Option<DexEvent> {
711 None
739}
740
741fn parse_text_log(
743 _log: &str,
744 _signature: Signature,
745 _slot: u64,
746 tx_index: u64,
747 _block_time_us: Option<i64>,
748) -> Option<DexEvent> {
749 None
751}