1use solana_sdk::{pubkey::Pubkey, signature::Signature};
6use crate::core::events::*;
7use super::utils::*;
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") || log.contains("Bonk")
24}
25
26pub fn parse_log(log: &str, signature: Signature, slot: u64, tx_index: u64, block_time: Option<i64>, grpc_recv_us: i64) -> Option<DexEvent> {
28 parse_structured_log(log, signature, slot, tx_index, block_time, grpc_recv_us)
29}
30
31
32fn parse_structured_log(
34 log: &str,
35 signature: Signature,
36 slot: u64,
37 tx_index: u64,
38 block_time: Option<i64>,
39 grpc_recv_us: i64,
40) -> Option<DexEvent> {
41 let program_data = extract_program_data(log)?;
42 if program_data.len() < 8 {
43 return None;
44 }
45
46 let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
47 let data = &program_data[8..];
48
49 match discriminator {
50 discriminators::TRADE => {
51 parse_trade_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
52 },
53 discriminators::POOL_CREATE => {
54 parse_pool_create_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
55 },
56 discriminators::MIGRATE_AMM => {
57 parse_migrate_amm_event(data, signature, slot, tx_index, block_time, grpc_recv_us)
58 },
59 _ => None,
60 }
61}
62
63fn parse_trade_event(
65 data: &[u8],
66 signature: Signature,
67 slot: u64,
68 tx_index: u64,
69 block_time: Option<i64>,
70 grpc_recv_us: i64,
71) -> Option<DexEvent> {
72 let mut offset = 0;
73
74 let pool_state = read_pubkey(data, offset)?;
75 offset += 32;
76
77 let user = read_pubkey(data, offset)?;
78 offset += 32;
79
80 let amount_in = read_u64_le(data, offset)?;
81 offset += 8;
82
83 let amount_out = read_u64_le(data, offset)?;
84 offset += 8;
85
86 let is_buy = read_bool(data, offset)?;
87 offset += 1;
88
89 let exact_in = read_bool(data, offset)?;
90
91 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool_state, grpc_recv_us);
92
93 Some(DexEvent::BonkTrade(BonkTradeEvent {
94 metadata,
95 pool_state,
96 user,
97 amount_in,
98 amount_out,
99 is_buy,
100 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
101 exact_in,
102 }))
103}
104
105fn parse_pool_create_event(
107 data: &[u8],
108 signature: Signature,
109 slot: u64,
110 tx_index: u64,
111 block_time: Option<i64>,
112 grpc_recv_us: i64,
113) -> Option<DexEvent> {
114 let mut offset = 0;
115
116 let pool_state = read_pubkey(data, offset)?;
117 offset += 32;
118
119 let _token_a_mint = read_pubkey(data, offset)?;
120 offset += 32;
121
122 let _token_b_mint = read_pubkey(data, offset)?;
123 offset += 32;
124
125 let creator = read_pubkey(data, offset)?;
126 offset += 32;
127
128 let _initial_liquidity_a = read_u64_le(data, offset)?;
129 offset += 8;
130
131 let _initial_liquidity_b = read_u64_le(data, offset)?;
132
133 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, pool_state, grpc_recv_us);
134
135 Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
136 metadata,
137 base_mint_param: BaseMintParam {
138 symbol: "BONK".to_string(),
139 name: "Bonk Pool".to_string(),
140 uri: "https://bonk.com".to_string(),
141 decimals: 5,
142 },
143 pool_state,
144 creator,
145 }))
146}
147
148fn parse_migrate_amm_event(
150 data: &[u8],
151 signature: Signature,
152 slot: u64,
153 tx_index: u64,
154 block_time: Option<i64>,
155 grpc_recv_us: i64,
156) -> Option<DexEvent> {
157 let mut offset = 0;
158
159 let old_pool = read_pubkey(data, offset)?;
160 offset += 32;
161
162 let new_pool = read_pubkey(data, offset)?;
163 offset += 32;
164
165 let user = read_pubkey(data, offset)?;
166 offset += 32;
167
168 let liquidity_amount = read_u64_le(data, offset)?;
169
170 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, old_pool, grpc_recv_us);
171
172 Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
173 metadata,
174 old_pool,
175 new_pool,
176 user,
177 liquidity_amount,
178 }))
179}
180
181fn parse_text_log(
183 tx_index: u64,
184 log: &str,
185 signature: Signature,
186 slot: u64,
187 block_time: Option<i64>,
188 grpc_recv_us: i64,
189) -> Option<DexEvent> {
190 use super::utils::text_parser::*;
191
192 if log.contains("trade") || log.contains("swap") {
193 return parse_trade_from_text(tx_index, log, signature, slot, block_time, grpc_recv_us);
194 }
195
196 if log.contains("pool") && log.contains("create") {
197 return parse_pool_create_from_text(tx_index, log, signature, slot, block_time, grpc_recv_us);
198 }
199
200 if log.contains("migrate") {
201 return parse_migrate_from_text(tx_index, log, signature, slot, block_time, grpc_recv_us);
202 }
203
204 None
205}
206
207fn parse_trade_from_text(tx_index: u64,
209 log: &str,
210 signature: Signature,
211 slot: u64,
212 block_time: Option<i64>,
213 grpc_recv_us: i64,
214) -> Option<DexEvent> {
215 use super::utils::text_parser::*;
216
217 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
218 let is_buy = detect_trade_type(log).unwrap_or(true);
219
220 Some(DexEvent::BonkTrade(BonkTradeEvent {
221 metadata,
222 pool_state: Pubkey::default(),
223 user: Pubkey::default(),
224 amount_in: extract_number_from_text(log, "amount_in").unwrap_or(1000000),
225 amount_out: extract_number_from_text(log, "amount_out").unwrap_or(950000),
226 is_buy,
227 trade_direction: if is_buy { TradeDirection::Buy } else { TradeDirection::Sell },
228 exact_in: true,
229 }))
230}
231
232fn parse_pool_create_from_text(tx_index: u64,
234 log: &str,
235 signature: Signature,
236 slot: u64,
237 block_time: Option<i64>,
238 grpc_recv_us: i64,
239) -> Option<DexEvent> {
240 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
241
242 Some(DexEvent::BonkPoolCreate(BonkPoolCreateEvent {
243 metadata,
244 base_mint_param: BaseMintParam {
245 symbol: "BONK".to_string(),
246 name: "Bonk Pool".to_string(),
247 uri: "https://bonk.com".to_string(),
248 decimals: 5,
249 },
250 pool_state: Pubkey::default(),
251 creator: Pubkey::default(),
252 }))
253}
254
255fn parse_migrate_from_text(tx_index: u64,
257 log: &str,
258 signature: Signature,
259 slot: u64,
260 block_time: Option<i64>,
261 grpc_recv_us: i64,
262) -> Option<DexEvent> {
263 use super::utils::text_parser::*;
264
265 let metadata = create_metadata_simple(signature, slot, tx_index, block_time, Pubkey::default(), grpc_recv_us);
266
267 Some(DexEvent::BonkMigrateAmm(BonkMigrateAmmEvent {
268 metadata,
269 old_pool: Pubkey::default(),
270 new_pool: Pubkey::default(),
271 user: Pubkey::default(),
272 liquidity_amount: extract_number_from_text(log, "liquidity").unwrap_or(0),
273 }))
274}