1use super::utils::*;
4use crate::core::events::*;
5use solana_sdk::signature::Signature;
6
7pub mod discriminators {
8 pub const SWAP_EVENT: [u8; 8] = [27, 60, 21, 213, 138, 170, 187, 147];
9 pub const INITIALIZE_POOL_EVENT: [u8; 8] = [228, 50, 246, 85, 203, 66, 134, 37];
10 pub const CURVE_COMPLETE_EVENT: [u8; 8] = [229, 231, 86, 84, 156, 134, 75, 24];
11}
12
13pub fn parse_log(
14 log: &str,
15 signature: Signature,
16 slot: u64,
17 tx_index: u64,
18 block_time_us: Option<i64>,
19 grpc_recv_us: i64,
20) -> Option<DexEvent> {
21 let program_data = extract_program_data(log)?;
22 if program_data.len() < 8 {
23 return None;
24 }
25
26 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
27 let data = &program_data[8..];
28 let pool = read_pubkey(data, 0).unwrap_or_default();
29 let metadata =
30 create_metadata_simple(signature, slot, tx_index, block_time_us, pool, grpc_recv_us);
31
32 match discriminator {
33 discriminators::SWAP_EVENT => parse_swap_from_data(data, metadata),
34 discriminators::INITIALIZE_POOL_EVENT => parse_initialize_pool_from_data(data, metadata),
35 discriminators::CURVE_COMPLETE_EVENT => parse_curve_complete_from_data(data, metadata),
36 _ => None,
37 }
38}
39
40#[inline(always)]
41pub fn parse_swap_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
42 let mut offset = 0;
43
44 let pool = read_pubkey(data, offset)?;
45 offset += 32;
46 let config = read_pubkey(data, offset)?;
47 offset += 32;
48 let trade_direction = read_u8(data, offset)?;
49 offset += 1;
50 let has_referral = read_bool(data, offset)?;
51 offset += 1;
52 let params_amount_in = read_u64_le(data, offset)?;
53 offset += 8;
54 let minimum_amount_out = read_u64_le(data, offset)?;
55 offset += 8;
56 let actual_input_amount = read_u64_le(data, offset)?;
57 offset += 8;
58 let output_amount = read_u64_le(data, offset)?;
59 offset += 8;
60 let next_sqrt_price = read_u128_le(data, offset)?;
61 offset += 16;
62 let trading_fee = read_u64_le(data, offset)?;
63 offset += 8;
64 let protocol_fee = read_u64_le(data, offset)?;
65 offset += 8;
66 let referral_fee = read_u64_le(data, offset)?;
67 offset += 8;
68 let amount_in = read_u64_le(data, offset).unwrap_or(params_amount_in);
69 offset += 8;
70 let current_timestamp = read_u64_le(data, offset)?;
71
72 Some(DexEvent::MeteoraDbcSwap(MeteoraDbcSwapEvent {
73 metadata,
74 pool,
75 config,
76 trade_direction,
77 has_referral,
78 amount_in,
79 minimum_amount_out,
80 actual_input_amount,
81 output_amount,
82 next_sqrt_price,
83 trading_fee,
84 protocol_fee,
85 referral_fee,
86 current_timestamp,
87 }))
88}
89
90#[inline(always)]
91pub fn parse_initialize_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
92 let mut offset = 0;
93
94 let pool = read_pubkey(data, offset)?;
95 offset += 32;
96 let config = read_pubkey(data, offset)?;
97 offset += 32;
98 let creator = read_pubkey(data, offset)?;
99 offset += 32;
100 let base_mint = read_pubkey(data, offset)?;
101 offset += 32;
102 let pool_type = read_u8(data, offset)?;
103 offset += 1;
104 let activation_point = read_u64_le(data, offset)?;
105
106 Some(DexEvent::MeteoraDbcInitializePool(MeteoraDbcInitializePoolEvent {
107 metadata,
108 pool,
109 config,
110 creator,
111 base_mint,
112 pool_type,
113 activation_point,
114 }))
115}
116
117#[inline(always)]
118pub fn parse_curve_complete_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
119 let mut offset = 0;
120
121 let pool = read_pubkey(data, offset)?;
122 offset += 32;
123 let config = read_pubkey(data, offset)?;
124 offset += 32;
125 let base_reserve = read_u64_le(data, offset)?;
126 offset += 8;
127 let quote_reserve = read_u64_le(data, offset)?;
128
129 Some(DexEvent::MeteoraDbcCurveComplete(MeteoraDbcCurveCompleteEvent {
130 metadata,
131 pool,
132 config,
133 base_reserve,
134 quote_reserve,
135 }))
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use solana_sdk::pubkey::Pubkey;
142
143 fn push_pubkey(buf: &mut Vec<u8>, byte: u8) -> Pubkey {
144 let key = Pubkey::new_from_array([byte; 32]);
145 buf.extend_from_slice(key.as_ref());
146 key
147 }
148
149 #[test]
150 fn parses_dbc_swap_layout() {
151 let mut data = Vec::new();
152 let pool = push_pubkey(&mut data, 1);
153 let config = push_pubkey(&mut data, 2);
154 data.push(1);
155 data.push(1);
156 data.extend_from_slice(&10_u64.to_le_bytes());
157 data.extend_from_slice(&9_u64.to_le_bytes());
158 data.extend_from_slice(&10_u64.to_le_bytes());
159 data.extend_from_slice(&8_u64.to_le_bytes());
160 data.extend_from_slice(&(1_u128 << 64).to_le_bytes());
161 data.extend_from_slice(&1_u64.to_le_bytes());
162 data.extend_from_slice(&2_u64.to_le_bytes());
163 data.extend_from_slice(&3_u64.to_le_bytes());
164 data.extend_from_slice(&10_u64.to_le_bytes());
165 data.extend_from_slice(&123_u64.to_le_bytes());
166
167 let event = parse_swap_from_data(&data, EventMetadata::default()).unwrap();
168 match event {
169 DexEvent::MeteoraDbcSwap(event) => {
170 assert_eq!(event.pool, pool);
171 assert_eq!(event.config, config);
172 assert_eq!(event.trade_direction, 1);
173 assert!(event.has_referral);
174 assert_eq!(event.minimum_amount_out, 9);
175 assert_eq!(event.output_amount, 8);
176 assert_eq!(event.protocol_fee, 2);
177 assert_eq!(event.current_timestamp, 123);
178 }
179 other => panic!("expected MeteoraDbcSwap, got {other:?}"),
180 }
181 }
182
183 #[test]
184 fn parses_dbc_initialize_pool_layout() {
185 let mut data = Vec::new();
186 let pool = push_pubkey(&mut data, 1);
187 let config = push_pubkey(&mut data, 2);
188 let creator = push_pubkey(&mut data, 3);
189 let base_mint = push_pubkey(&mut data, 4);
190 data.push(2);
191 data.extend_from_slice(&456_u64.to_le_bytes());
192
193 let event = parse_initialize_pool_from_data(&data, EventMetadata::default()).unwrap();
194 match event {
195 DexEvent::MeteoraDbcInitializePool(event) => {
196 assert_eq!(event.pool, pool);
197 assert_eq!(event.config, config);
198 assert_eq!(event.creator, creator);
199 assert_eq!(event.base_mint, base_mint);
200 assert_eq!(event.pool_type, 2);
201 assert_eq!(event.activation_point, 456);
202 }
203 other => panic!("expected MeteoraDbcInitializePool, got {other:?}"),
204 }
205 }
206}