1use super::utils::*;
6use crate::core::events::*;
7use solana_sdk::{pubkey::Pubkey, signature::Signature};
8
9pub mod discriminators {
11 pub const TRADE: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9];
12 pub const POOL_CREATE: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
13 pub const MIGRATE_AMM: [u8; 8] = [3, 4, 5, 6, 7, 8, 9, 10];
14}
15
16pub const PROGRAM_ID: &str = "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1";
18
19pub fn is_raydium_launchpad_log(log: &str) -> bool {
21 log.contains(&format!("Program {} invoke", PROGRAM_ID))
22 || log.contains(&format!("Program {} success", PROGRAM_ID))
23 || log.contains("bonk")
24 || log.contains("Bonk")
25}
26
27pub fn parse_log(
29 log: &str,
30 signature: Signature,
31 slot: u64,
32 tx_index: u64,
33 block_time_us: Option<i64>,
34 grpc_recv_us: i64,
35) -> Option<DexEvent> {
36 parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
37}
38
39fn parse_structured_log(
41 log: &str,
42 signature: Signature,
43 slot: u64,
44 tx_index: u64,
45 block_time_us: Option<i64>,
46 grpc_recv_us: i64,
47) -> Option<DexEvent> {
48 let program_data = extract_program_data(log)?;
49 if program_data.len() < 8 {
50 return None;
51 }
52
53 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
54 let data = &program_data[8..];
55
56 match discriminator {
57 discriminators::TRADE => {
58 parse_trade_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
59 }
60 discriminators::POOL_CREATE => {
61 parse_pool_create_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
62 }
63 discriminators::MIGRATE_AMM => {
64 parse_migrate_amm_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
65 }
66 _ => None,
67 }
68}
69
70fn parse_trade_event(
72 data: &[u8],
73 signature: Signature,
74 slot: u64,
75 tx_index: u64,
76 block_time_us: Option<i64>,
77 grpc_recv_us: i64,
78) -> Option<DexEvent> {
79 let mut offset = 0;
80
81 let pool_state = read_pubkey(data, offset)?;
82 offset += 32;
83
84 let user = read_pubkey(data, offset)?;
85 offset += 32;
86
87 let amount_in = read_u64_le(data, offset)?;
88 offset += 8;
89
90 let amount_out = read_u64_le(data, offset)?;
91 offset += 8;
92
93 let is_buy = read_bool(data, offset)?;
94 offset += 1;
95
96 let exact_in = read_bool(data, offset)?;
97
98 let metadata =
99 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
100
101 Some(DexEvent::BonkTrade(BonkTradeEvent {
102 metadata,
103 pool_state,
104 user,
105 amount_in,
106 amount_out,
107 is_buy,
108 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
109 exact_in,
110 }))
111}
112
113fn parse_pool_create_event(
115 data: &[u8],
116 signature: Signature,
117 slot: u64,
118 tx_index: u64,
119 block_time_us: Option<i64>,
120 grpc_recv_us: i64,
121) -> Option<DexEvent> {
122 let mut offset = 0;
123
124 let pool_state = read_pubkey(data, offset)?;
125 offset += 32;
126
127 let _token_a_mint = read_pubkey(data, offset)?;
128 offset += 32;
129
130 let _token_b_mint = read_pubkey(data, offset)?;
131 offset += 32;
132
133 let creator = read_pubkey(data, offset)?;
134 offset += 32;
135
136 let _initial_liquidity_a = read_u64_le(data, offset)?;
137 offset += 8;
138
139 let _initial_liquidity_b = read_u64_le(data, offset)?;
140
141 let metadata =
142 create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
143
144 Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
145 metadata,
146 base_mint_param: BaseMintParam {
147 symbol: "BONK".to_string(),
148 name: "Bonk Pool".to_string(),
149 uri: "https://bonk.com".to_string(),
150 decimals: 5,
151 },
152 pool_state,
153 creator,
154 }))
155}
156
157fn parse_migrate_amm_event(
159 data: &[u8],
160 signature: Signature,
161 slot: u64,
162 tx_index: u64,
163 block_time_us: Option<i64>,
164 grpc_recv_us: i64,
165) -> Option<DexEvent> {
166 let mut offset = 0;
167
168 let old_pool = read_pubkey(data, offset)?;
169 offset += 32;
170
171 let new_pool = read_pubkey(data, offset)?;
172 offset += 32;
173
174 let user = read_pubkey(data, offset)?;
175 offset += 32;
176
177 let liquidity_amount = read_u64_le(data, offset)?;
178
179 let metadata =
180 create_metadata_simple(signature, slot, tx_index, block_time_us, old_pool, grpc_recv_us);
181
182 Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
183 metadata,
184 old_pool,
185 new_pool,
186 user,
187 liquidity_amount,
188 }))
189}
190
191fn parse_text_log(
193 tx_index: u64,
194 log: &str,
195 signature: Signature,
196 slot: u64,
197 block_time_us: Option<i64>,
198 grpc_recv_us: i64,
199) -> Option<DexEvent> {
200 use super::utils::text_parser::*;
201
202 if log.contains("trade") || log.contains("swap") {
203 return parse_trade_from_text(tx_index, log, signature, slot, block_time_us, grpc_recv_us);
204 }
205
206 if log.contains("pool") && log.contains("create") {
207 return parse_pool_create_from_text(
208 tx_index,
209 log,
210 signature,
211 slot,
212 block_time_us,
213 grpc_recv_us,
214 );
215 }
216
217 if log.contains("migrate") {
218 return parse_migrate_from_text(
219 tx_index,
220 log,
221 signature,
222 slot,
223 block_time_us,
224 grpc_recv_us,
225 );
226 }
227
228 None
229}
230
231fn parse_trade_from_text(
233 tx_index: u64,
234 log: &str,
235 signature: Signature,
236 slot: u64,
237 block_time_us: Option<i64>,
238 grpc_recv_us: i64,
239) -> Option<DexEvent> {
240 use super::utils::text_parser::*;
241
242 let metadata = create_metadata_simple(
243 signature,
244 slot,
245 tx_index,
246 block_time_us,
247 Pubkey::default(),
248 grpc_recv_us,
249 );
250 let is_buy = detect_trade_type(log).unwrap_or(true);
251
252 Some(DexEvent::BonkTrade(BonkTradeEvent {
253 metadata,
254 pool_state: Pubkey::default(),
255 user: Pubkey::default(),
256 amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1000000),
257 amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950000),
258 is_buy,
259 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
260 exact_in: true,
261 }))
262}
263
264fn parse_pool_create_from_text(
266 tx_index: u64,
267 log: &str,
268 signature: Signature,
269 slot: u64,
270 block_time_us: Option<i64>,
271 grpc_recv_us: i64,
272) -> Option<DexEvent> {
273 let metadata = create_metadata_simple(
274 signature,
275 slot,
276 tx_index,
277 block_time_us,
278 Pubkey::default(),
279 grpc_recv_us,
280 );
281
282 Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
283 metadata,
284 base_mint_param: BaseMintParam {
285 symbol: "BONK".to_string(),
286 name: "Bonk Pool".to_string(),
287 uri: "https://bonk.com".to_string(),
288 decimals: 5,
289 },
290 pool_state: Pubkey::default(),
291 creator: Pubkey::default(),
292 }))
293}
294
295fn parse_migrate_from_text(
297 tx_index: u64,
298 log: &str,
299 signature: Signature,
300 slot: u64,
301 block_time_us: Option<i64>,
302 grpc_recv_us: i64,
303) -> Option<DexEvent> {
304 use super::utils::text_parser::*;
305
306 let metadata = create_metadata_simple(
307 signature,
308 slot,
309 tx_index,
310 block_time_us,
311 Pubkey::default(),
312 grpc_recv_us,
313 );
314
315 Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
316 metadata,
317 old_pool: Pubkey::default(),
318 new_pool: Pubkey::default(),
319 user: Pubkey::default(),
320 liquidity_amount: extract_number_from_text(log, "liquidity").unwrap_or(0),
321 }))
322}