1use super::perf_hints::{likely, unlikely};
11use crate::core::events::{DexEvent, EventMetadata};
12use crate::grpc::types::{EventType, EventTypeFilter};
13use crate::instr::program_ids;
14use memchr::memmem;
15use once_cell::sync::Lazy;
16use solana_sdk::pubkey::Pubkey;
17use solana_sdk::signature::Signature;
18
19static PUMPFUN_FINDER: Lazy<memmem::Finder> =
21 Lazy::new(|| memmem::Finder::new(b"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"));
22static RAYDIUM_AMM_FINDER: Lazy<memmem::Finder> =
23 Lazy::new(|| memmem::Finder::new(b"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"));
24static RAYDIUM_CLMM_FINDER: Lazy<memmem::Finder> =
25 Lazy::new(|| memmem::Finder::new(b"CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"));
26static RAYDIUM_CPMM_FINDER: Lazy<memmem::Finder> =
27 Lazy::new(|| memmem::Finder::new(b"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"));
28static RAYDIUM_LAUNCHLAB_FINDER: Lazy<memmem::Finder> =
29 Lazy::new(|| memmem::Finder::new(b"LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"));
30static PROGRAM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program"));
31static PROGRAM_DATA_FINDER: Lazy<memmem::Finder> =
32 Lazy::new(|| memmem::Finder::new(b"Program data: "));
33const PUMPFUN_CREATE_PREFIX: &[u8] = b"Program data: G3KpTd7rY3";
36static WHIRL_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"whirL"));
37static METEORA_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"meteora"));
38static METEORA_LB_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"LB"));
39static METEORA_DLMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"DLMM"));
40static PUMPSWAP_LOWER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"pumpswap"));
41static PUMPSWAP_UPPER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"PumpSwap"));
42
43pub mod program_id_strings {
45 pub const PUMPFUN_INVOKE: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke";
46 pub const PUMPFUN_SUCCESS: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success";
47 pub const PUMPFUN_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
48
49 pub const RAYDIUM_LAUNCHLAB_INVOKE: &str =
50 "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj invoke";
51 pub const RAYDIUM_LAUNCHLAB_SUCCESS: &str =
52 "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj success";
53 pub const RAYDIUM_LAUNCHLAB_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
54
55 pub const RAYDIUM_CLMM_INVOKE: &str =
56 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK invoke";
57 pub const RAYDIUM_CLMM_SUCCESS: &str =
58 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK success";
59 pub const RAYDIUM_CLMM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK";
60
61 pub const RAYDIUM_CPMM_INVOKE: &str =
62 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C invoke";
63 pub const RAYDIUM_CPMM_SUCCESS: &str =
64 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C success";
65 pub const RAYDIUM_CPMM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
66
67 pub const RAYDIUM_AMM_V4_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
68
69 pub const PROGRAM_DATA: &str = "Program data: ";
71 pub const PROGRAM_LOG: &str = "Program log: ";
72
73 pub const PUMPFUN_CREATE_DISCRIMINATOR: &str = "GB7IKAUcB3c"; }
76
77#[derive(Debug, Copy, Clone, PartialEq)]
79pub enum LogType {
80 PumpFun,
81 RaydiumLaunchlab,
82 PumpAmm,
83 RaydiumClmm,
84 RaydiumCpmm,
85 RaydiumAmm,
86 OrcaWhirlpool,
87 MeteoraAmm,
88 MeteoraDamm,
89 MeteoraDlmm,
90 Unknown,
91}
92
93#[inline(always)]
95pub fn detect_log_type(log: &str) -> LogType {
96 let log_bytes = log.as_bytes();
97
98 if log_bytes.len() < 20 {
100 return LogType::Unknown;
101 }
102
103 let has_program_data = PROGRAM_DATA_FINDER.find(log_bytes).is_some();
105
106 if unlikely(!has_program_data) {
108 return LogType::Unknown;
109 }
110
111 if likely(RAYDIUM_AMM_FINDER.find(log_bytes).is_some()) {
114 return LogType::RaydiumAmm;
115 }
116
117 if RAYDIUM_CLMM_FINDER.find(log_bytes).is_some() {
119 return LogType::RaydiumClmm;
120 }
121
122 if RAYDIUM_CPMM_FINDER.find(log_bytes).is_some() {
124 return LogType::RaydiumCpmm;
125 }
126
127 if RAYDIUM_LAUNCHLAB_FINDER.find(log_bytes).is_some() {
129 return LogType::RaydiumLaunchlab;
130 }
131
132 if WHIRL_FINDER.find(log_bytes).is_some() {
134 return LogType::OrcaWhirlpool;
135 }
136
137 if let Some(pos) = METEORA_FINDER.find(log_bytes) {
139 let rest = &log_bytes[pos..];
140 if METEORA_LB_FINDER.find(rest).is_some() {
141 return LogType::MeteoraDamm;
142 } else if METEORA_DLMM_FINDER.find(rest).is_some() {
143 return LogType::MeteoraDlmm;
144 } else {
145 return LogType::MeteoraAmm;
146 }
147 }
148
149 if PUMPSWAP_LOWER_FINDER.find(log_bytes).is_some()
151 || PUMPSWAP_UPPER_FINDER.find(log_bytes).is_some()
152 {
153 return LogType::PumpAmm;
154 }
155
156 if likely(PUMPFUN_FINDER.find(log_bytes).is_some()) {
159 return LogType::PumpFun;
160 }
161
162 if log.len() > 30 {
166 return LogType::PumpFun;
167 }
168
169 LogType::Unknown
170}
171
172mod discriminators {
176 pub const PUMPFUN_CREATE: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
178 pub const PUMPFUN_TRADE: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
179 pub const PUMPFUN_MIGRATE: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
180 pub const PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR: u64 =
181 u64::from_le_bytes([155, 167, 104, 220, 213, 108, 243, 3]);
182 pub const RAYDIUM_LAUNCHLAB_POOL_CREATE: u64 =
186 u64::from_le_bytes([151, 215, 226, 9, 118, 161, 115, 174]);
187 pub const RAYDIUM_LAUNCHLAB_TRADE: u64 =
188 u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
189 pub const PUMP_FEES_CREATE_FEE_SHARING_CONFIG: u64 =
191 u64::from_le_bytes([133, 105, 170, 200, 184, 116, 251, 88]);
192 pub const PUMP_FEES_INITIALIZE_FEE_CONFIG: u64 =
193 u64::from_le_bytes([89, 138, 244, 230, 10, 56, 226, 126]);
194 pub const PUMP_FEES_RESET_FEE_SHARING_CONFIG: u64 =
195 u64::from_le_bytes([203, 204, 151, 226, 120, 55, 214, 243]);
196 pub const PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY: u64 =
197 u64::from_le_bytes([114, 23, 101, 60, 14, 190, 153, 62]);
198 pub const PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY: u64 =
199 u64::from_le_bytes([124, 143, 198, 245, 77, 184, 8, 236]);
200 pub const PUMP_FEES_UPDATE_ADMIN: u64 =
201 u64::from_le_bytes([225, 152, 171, 87, 246, 63, 66, 234]);
202 pub const PUMP_FEES_UPDATE_FEE_CONFIG: u64 =
203 u64::from_le_bytes([90, 23, 65, 35, 62, 244, 188, 208]);
204 pub const PUMP_FEES_UPDATE_FEE_SHARES: u64 =
205 u64::from_le_bytes([21, 186, 196, 184, 91, 228, 225, 203]);
206 pub const PUMP_FEES_UPSERT_FEE_TIERS: u64 =
207 u64::from_le_bytes([171, 89, 169, 187, 122, 186, 33, 204]);
208
209 pub const PUMPSWAP_BUY: u64 = u64::from_le_bytes([103, 244, 82, 31, 44, 245, 119, 119]);
211 pub const PUMPSWAP_SELL: u64 = u64::from_le_bytes([62, 47, 55, 10, 165, 3, 220, 42]);
212 pub const PUMPSWAP_CREATE_POOL: u64 =
213 u64::from_le_bytes([177, 49, 12, 210, 160, 118, 167, 116]);
214 pub const PUMPSWAP_ADD_LIQUIDITY: u64 =
215 u64::from_le_bytes([120, 248, 61, 83, 31, 142, 107, 144]);
216 pub const PUMPSWAP_REMOVE_LIQUIDITY: u64 =
217 u64::from_le_bytes([22, 9, 133, 26, 160, 44, 71, 192]);
218
219 pub const RAYDIUM_CLMM_SWAP: u64 = u64::from_le_bytes([64, 198, 205, 232, 38, 8, 113, 226]);
221 pub const RAYDIUM_CLMM_INCREASE_LIQUIDITY: u64 =
222 u64::from_le_bytes([49, 79, 105, 212, 32, 34, 30, 84]);
223 pub const RAYDIUM_CLMM_DECREASE_LIQUIDITY: u64 =
224 u64::from_le_bytes([58, 222, 86, 58, 68, 50, 85, 56]);
225 pub const RAYDIUM_CLMM_LIQUIDITY_CHANGE: u64 =
226 u64::from_le_bytes([126, 240, 175, 206, 158, 88, 153, 107]);
227 pub const RAYDIUM_CLMM_CONFIG_CHANGE: u64 =
228 u64::from_le_bytes([247, 189, 7, 119, 106, 112, 95, 151]);
229 pub const RAYDIUM_CLMM_CREATE_PERSONAL_POSITION: u64 =
230 u64::from_le_bytes([100, 30, 87, 249, 196, 223, 154, 206]);
231 pub const RAYDIUM_CLMM_LIQUIDITY_CALCULATE: u64 =
232 u64::from_le_bytes([237, 112, 148, 230, 57, 84, 180, 162]);
233 pub const RAYDIUM_CLMM_OPEN_LIMIT_ORDER: u64 =
234 u64::from_le_bytes([106, 24, 71, 85, 57, 169, 158, 216]);
235 pub const RAYDIUM_CLMM_INCREASE_LIMIT_ORDER: u64 =
236 u64::from_le_bytes([11, 120, 13, 204, 199, 87, 19, 200]);
237 pub const RAYDIUM_CLMM_DECREASE_LIMIT_ORDER: u64 =
238 u64::from_le_bytes([70, 48, 40, 221, 219, 237, 212, 163]);
239 pub const RAYDIUM_CLMM_SETTLE_LIMIT_ORDER: u64 =
240 u64::from_le_bytes([88, 119, 77, 164, 125, 124, 10, 194]);
241 pub const RAYDIUM_CLMM_UPDATE_REWARD_INFOS: u64 =
242 u64::from_le_bytes([109, 127, 186, 78, 114, 65, 37, 236]);
243 pub const RAYDIUM_CLMM_CREATE_POOL: u64 = u64::from_le_bytes([25, 94, 75, 47, 112, 99, 53, 63]);
244 pub const RAYDIUM_CLMM_COLLECT_PERSONAL_FEE: u64 =
245 u64::from_le_bytes([166, 174, 105, 192, 81, 161, 83, 105]);
246 pub const RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE: u64 =
247 u64::from_le_bytes([206, 87, 17, 79, 45, 41, 213, 61]);
248
249 pub const RAYDIUM_CPMM_SWAP_BASE_IN: u64 =
251 u64::from_le_bytes([143, 190, 90, 218, 196, 30, 51, 222]);
252 pub const RAYDIUM_CPMM_SWAP_EVENT: u64 =
253 u64::from_le_bytes([64, 198, 205, 232, 38, 8, 113, 226]);
254 pub const RAYDIUM_CPMM_SWAP_BASE_OUT: u64 =
255 u64::from_le_bytes([55, 217, 98, 86, 163, 74, 180, 173]);
256 pub const RAYDIUM_CPMM_CREATE_POOL: u64 =
257 u64::from_le_bytes([233, 146, 209, 142, 207, 104, 64, 188]);
258 pub const RAYDIUM_CPMM_DEPOSIT: u64 =
259 u64::from_le_bytes([242, 35, 198, 137, 82, 225, 242, 182]);
260 pub const RAYDIUM_CPMM_WITHDRAW: u64 =
261 u64::from_le_bytes([183, 18, 70, 156, 148, 109, 161, 34]);
262
263 pub const RAYDIUM_AMM_SWAP_BASE_IN: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 9]);
265 pub const RAYDIUM_AMM_SWAP_BASE_OUT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 11]);
266 pub const RAYDIUM_AMM_DEPOSIT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 3]);
267 pub const RAYDIUM_AMM_WITHDRAW: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 4]);
268 pub const RAYDIUM_AMM_INITIALIZE2: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 1]);
269 pub const RAYDIUM_AMM_WITHDRAW_PNL: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 7]);
270
271 pub const ORCA_TRADED: u64 = u64::from_le_bytes([225, 202, 73, 175, 147, 43, 160, 150]);
273 pub const ORCA_LIQUIDITY_INCREASED: u64 =
274 u64::from_le_bytes([30, 7, 144, 181, 102, 254, 155, 161]);
275 pub const ORCA_LIQUIDITY_DECREASED: u64 =
276 u64::from_le_bytes([166, 1, 36, 71, 112, 202, 181, 171]);
277 pub const ORCA_POOL_INITIALIZED: u64 =
278 u64::from_le_bytes([100, 118, 173, 87, 12, 198, 254, 229]);
279
280 pub const METEORA_AMM_SWAP: u64 = u64::from_le_bytes([81, 108, 227, 190, 205, 208, 10, 196]);
282 pub const METEORA_AMM_ADD_LIQUIDITY: u64 =
283 u64::from_le_bytes([31, 94, 125, 90, 227, 52, 61, 186]);
284 pub const METEORA_AMM_REMOVE_LIQUIDITY: u64 =
285 u64::from_le_bytes([116, 244, 97, 232, 103, 31, 152, 58]);
286 pub const METEORA_AMM_BOOTSTRAP_LIQUIDITY: u64 =
287 u64::from_le_bytes([121, 127, 38, 136, 92, 55, 14, 247]);
288 pub const METEORA_AMM_POOL_CREATED: u64 =
289 u64::from_le_bytes([202, 44, 41, 88, 104, 220, 157, 82]);
290 pub const METEORA_AMM_SET_POOL_FEES: u64 =
291 u64::from_le_bytes([245, 26, 198, 164, 88, 18, 75, 9]);
292
293 pub const METEORA_DAMM_SWAP: u64 = u64::from_le_bytes([27, 60, 21, 213, 138, 170, 187, 147]);
295 pub const METEORA_DAMM_SWAP2: u64 = u64::from_le_bytes([189, 66, 51, 168, 38, 80, 117, 153]);
296 pub const METEORA_DAMM_ADD_LIQUIDITY: u64 =
297 u64::from_le_bytes([175, 242, 8, 157, 30, 247, 185, 169]);
298 pub const METEORA_DAMM_REMOVE_LIQUIDITY: u64 =
299 u64::from_le_bytes([87, 46, 88, 98, 175, 96, 34, 91]);
300 pub const METEORA_DAMM_LIQUIDITY_CHANGE: u64 =
301 u64::from_le_bytes([197, 171, 78, 127, 224, 211, 87, 13]);
302 pub const METEORA_DAMM_INITIALIZE_POOL: u64 =
303 u64::from_le_bytes([228, 50, 246, 85, 203, 66, 134, 37]);
304 pub const METEORA_DAMM_CREATE_POSITION: u64 =
305 u64::from_le_bytes([156, 15, 119, 198, 29, 181, 221, 55]);
306 pub const METEORA_DAMM_CLAIM_POSITION_FEE: u64 =
307 u64::from_le_bytes([198, 182, 183, 52, 97, 12, 49, 56]);
308 pub const METEORA_DAMM_CLAIM_REWARD: u64 =
309 u64::from_le_bytes([218, 86, 147, 200, 235, 188, 215, 231]);
310 pub const METEORA_DAMM_CLOSE_POSITION: u64 =
311 u64::from_le_bytes([20, 145, 144, 68, 143, 142, 214, 178]);
312 pub const METEORA_DAMM_UPDATE_DELEGATE_PERMISSION: u64 =
313 u64::from_le_bytes([66, 188, 75, 151, 150, 232, 87, 93]);
314 pub const METEORA_DAMM_WITHDRAW_DEAD_LIQUIDITY_REWARD: u64 =
315 u64::from_le_bytes([228, 66, 150, 195, 42, 62, 163, 13]);
316 pub const METEORA_DAMM_WITHDRAW_INELIGIBLE_REWARD: u64 =
317 u64::from_le_bytes([248, 215, 184, 78, 31, 180, 179, 168]);
318 pub const METEORA_DAMM_UPDATE_REWARD_FUNDER: u64 =
319 u64::from_le_bytes([76, 154, 208, 13, 40, 115, 246, 146]);
320 pub const METEORA_DAMM_UPDATE_REWARD_DURATION: u64 =
321 u64::from_le_bytes([149, 135, 65, 231, 129, 153, 65, 57]);
322 pub const METEORA_DAMM_INITIALIZE_REWARD: u64 =
323 u64::from_le_bytes([129, 91, 188, 3, 246, 52, 185, 249]);
324 pub const METEORA_DAMM_FUND_REWARD: u64 =
325 u64::from_le_bytes([104, 233, 237, 122, 199, 191, 121, 85]);
326 pub const METEORA_DAMM_CREATE_CONFIG: u64 =
327 u64::from_le_bytes([131, 207, 180, 174, 180, 73, 165, 54]);
328 pub const METEORA_DAMM_CREATE_DYNAMIC_CONFIG: u64 =
329 u64::from_le_bytes([231, 197, 13, 164, 248, 213, 133, 152]);
330
331 pub const METEORA_DBC_SWAP2: u64 = u64::from_le_bytes([189, 66, 51, 168, 38, 80, 117, 153]);
334 pub const METEORA_DBC_SWAP2_TRANSFER_HOOK: u64 =
335 u64::from_le_bytes([134, 59, 168, 120, 94, 51, 114, 231]);
336 pub const METEORA_DBC_SWAP: u64 = u64::from_le_bytes([27, 60, 21, 213, 138, 170, 187, 147]);
337 pub const METEORA_DBC_INITIALIZE_POOL: u64 =
338 u64::from_le_bytes([228, 50, 246, 85, 203, 66, 134, 37]);
339 pub const METEORA_DBC_CURVE_COMPLETE: u64 =
340 u64::from_le_bytes([229, 231, 86, 84, 156, 134, 75, 24]);
341
342 pub const METEORA_DLMM_SWAP: u64 = u64::from_le_bytes([81, 108, 227, 190, 205, 208, 10, 196]);
345 pub const METEORA_DLMM_SWAP2: u64 = u64::from_le_bytes([46, 116, 82, 215, 148, 27, 84, 77]);
346 pub const METEORA_DLMM_ADD_LIQUIDITY: u64 =
347 u64::from_le_bytes([31, 94, 125, 90, 227, 52, 61, 186]);
348 pub const METEORA_DLMM_REMOVE_LIQUIDITY: u64 =
349 u64::from_le_bytes([116, 244, 97, 232, 103, 31, 152, 58]);
350 pub const METEORA_DLMM_INITIALIZE_POOL: u64 =
351 u64::from_le_bytes([185, 74, 252, 125, 27, 215, 188, 111]);
352 pub const METEORA_DLMM_INITIALIZE_BIN_ARRAY: u64 =
353 u64::from_le_bytes([11, 18, 155, 194, 33, 115, 238, 119]);
354 pub const METEORA_DLMM_CREATE_POSITION: u64 =
355 u64::from_le_bytes([144, 142, 252, 84, 157, 53, 37, 121]);
356 pub const METEORA_DLMM_CLOSE_POSITION: u64 =
357 u64::from_le_bytes([255, 196, 16, 107, 28, 202, 53, 128]);
358 pub const METEORA_DLMM_CLAIM_FEE: u64 =
359 u64::from_le_bytes([75, 122, 154, 48, 140, 74, 123, 163]);
360 pub const METEORA_DLMM_CLAIM_FEE2: u64 =
361 u64::from_le_bytes([232, 171, 242, 97, 58, 77, 35, 45]);
362 pub const METEORA_DLMM_CLAIM_REWARD2: u64 =
363 u64::from_le_bytes([27, 143, 244, 33, 80, 43, 110, 146]);
364}
365
366#[inline(always)]
378pub fn parse_log_optimized(
380 log: &str,
381 signature: Signature,
382 slot: u64,
383 tx_index: u64,
384 block_time_us: Option<i64>,
385 grpc_recv_us: i64,
386 event_type_filter: Option<&EventTypeFilter>,
387 is_created_buy: bool,
388 recent_blockhash: Option<&[u8]>,
389) -> Option<DexEvent> {
390 parse_log_optimized_inner(
391 log,
392 signature,
393 slot,
394 tx_index,
395 block_time_us,
396 grpc_recv_us,
397 event_type_filter,
398 is_created_buy,
399 recent_blockhash,
400 None,
401 )
402}
403
404#[inline(always)]
410pub fn parse_log_optimized_with_program_id(
411 log: &str,
412 signature: Signature,
413 slot: u64,
414 tx_index: u64,
415 block_time_us: Option<i64>,
416 grpc_recv_us: i64,
417 event_type_filter: Option<&EventTypeFilter>,
418 is_created_buy: bool,
419 recent_blockhash: Option<&[u8]>,
420 program_id: Option<&Pubkey>,
421) -> Option<DexEvent> {
422 parse_log_optimized_inner(
423 log,
424 signature,
425 slot,
426 tx_index,
427 block_time_us,
428 grpc_recv_us,
429 event_type_filter,
430 is_created_buy,
431 recent_blockhash,
432 program_id,
433 )
434}
435
436#[inline(always)]
437fn decode_base64_discriminator(trimmed: &str) -> Option<u64> {
438 let bytes = trimmed.as_bytes();
439 if bytes.len() < 12 {
440 return None;
441 }
442
443 let mut discriminator_buf = [0u8; 9];
444 let decoded_len = {
445 use base64_simd::AsOut;
446 let decoded =
447 base64_simd::STANDARD.decode(&bytes[..12], discriminator_buf.as_mut().as_out()).ok()?;
448 decoded.len()
449 };
450 if decoded_len < 8 {
451 return None;
452 }
453
454 Some(unsafe { (discriminator_buf.as_ptr() as *const u64).read_unaligned() })
455}
456
457#[inline(always)]
458fn filter_includes_known_program(program_id: &Pubkey, filter: &EventTypeFilter) -> bool {
459 match *program_id {
460 program_ids::PUMPFUN_PROGRAM_ID => filter.includes_pumpfun(),
461 program_ids::PUMP_FEES_PROGRAM_ID => filter.includes_pump_fees(),
462 program_ids::PUMPSWAP_PROGRAM_ID => filter.includes_pumpswap(),
463 program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID => filter.includes_raydium_launchlab(),
464 program_ids::RAYDIUM_CLMM_PROGRAM_ID => filter.includes_raydium_clmm(),
465 program_ids::RAYDIUM_CPMM_PROGRAM_ID => filter.includes_raydium_cpmm(),
466 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => filter.includes_raydium_amm_v4(),
467 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => filter.includes_orca_whirlpool(),
468 program_ids::METEORA_POOLS_PROGRAM_ID => filter.includes_meteora_pools(),
469 program_ids::METEORA_DAMM_V2_PROGRAM_ID => filter.includes_meteora_damm_v2(),
470 program_ids::METEORA_DLMM_PROGRAM_ID => filter.includes_meteora_dlmm(),
471 program_ids::METEORA_DBC_PROGRAM_ID => filter.includes_meteora_dbc(),
472 _ => true,
473 }
474}
475
476#[inline(always)]
477fn filter_wants_supported_logs(filter: &EventTypeFilter) -> bool {
478 filter.includes_pumpfun()
479 || filter.includes_pump_fees()
480 || filter.includes_pumpswap()
481 || filter.includes_raydium_launchlab()
482 || filter.includes_raydium_clmm()
483 || filter.includes_raydium_cpmm()
484 || filter.includes_raydium_amm_v4()
485 || filter.includes_orca_whirlpool()
486 || filter.includes_meteora_pools()
487 || filter.includes_meteora_damm_v2()
488 || filter.includes_meteora_dlmm()
489 || filter.includes_meteora_dbc()
490}
491
492#[inline(always)]
493fn filter_wants_pumpfun_trade_event(filter: &EventTypeFilter) -> bool {
494 filter.should_include(EventType::PumpFunTrade)
495 || filter.should_include(EventType::PumpFunBuy)
496 || filter.should_include(EventType::PumpFunSell)
497 || filter.should_include(EventType::PumpFunBuyExactSolIn)
498}
499
500#[inline(always)]
501fn filter_wants_launchlab_trade_event(filter: &EventTypeFilter) -> bool {
502 filter.should_include(EventType::RaydiumLaunchlabTrade)
503}
504
505#[inline(always)]
506fn unscoped_filter_allows_discriminator(discriminator: u64, filter: &EventTypeFilter) -> bool {
507 match discriminator {
508 discriminators::PUMPFUN_TRADE => {
510 filter_wants_pumpfun_trade_event(filter)
511 || filter.should_include(EventType::RaydiumLaunchlabTrade)
512 }
513 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
516 filter.should_include(EventType::RaydiumCpmmSwap)
517 || filter.should_include(EventType::MeteoraDlmmSwap)
518 }
519 _ => discriminator_to_event_type(discriminator)
520 .map(|event_type| filter.should_include(event_type))
521 .unwrap_or_else(|| filter_wants_supported_logs(filter)),
522 }
523}
524
525#[inline(always)]
526fn filter_allows_discriminator(
527 program_id: Option<&Pubkey>,
528 discriminator: u64,
529 event_type_filter: Option<&EventTypeFilter>,
530) -> bool {
531 let Some(filter) = event_type_filter else {
532 return true;
533 };
534
535 if let Some(program_id) = program_id {
536 if *program_id == program_ids::PUMPFUN_PROGRAM_ID
537 && discriminator == discriminators::PUMPFUN_TRADE
538 {
539 return filter_wants_pumpfun_trade_event(filter);
540 }
541 if let Some(event_type) =
542 program_scoped_discriminator_to_event_type(program_id, discriminator)
543 {
544 return filter.should_include(event_type);
545 }
546 return filter_includes_known_program(program_id, filter);
547 }
548
549 unscoped_filter_allows_discriminator(discriminator, filter)
550}
551
552#[inline(always)]
553fn apply_event_type_filter(
554 event: DexEvent,
555 event_type_filter: Option<&EventTypeFilter>,
556) -> Option<DexEvent> {
557 if let Some(filter) = event_type_filter {
558 if !filter.should_include_dex_event(&event) {
559 return None;
560 }
561 }
562 Some(event)
563}
564
565#[inline(always)]
566fn parse_unscoped_pumpfun_launchlab_trade(
567 data: &[u8],
568 metadata: EventMetadata,
569 event_type_filter: Option<&EventTypeFilter>,
570 is_created_buy: bool,
571) -> Option<DexEvent> {
572 let wants_pumpfun = event_type_filter.map(filter_wants_pumpfun_trade_event).unwrap_or(true);
573 let wants_launchlab = event_type_filter.map(filter_wants_launchlab_trade_event).unwrap_or(true);
574
575 if wants_pumpfun {
576 if let Some(event) =
577 crate::logs::pump::parse_trade_from_data(data, metadata.clone(), is_created_buy)
578 .and_then(|event| apply_event_type_filter(event, event_type_filter))
579 {
580 return Some(event);
581 }
582 }
583
584 if wants_launchlab {
585 return crate::logs::raydium_launchlab::parse_trade_from_data(data, metadata)
586 .and_then(|event| apply_event_type_filter(event, event_type_filter));
587 }
588
589 None
590}
591
592#[inline(always)]
593fn parse_log_optimized_inner(
594 log: &str,
595 signature: Signature,
596 slot: u64,
597 tx_index: u64,
598 block_time_us: Option<i64>,
599 grpc_recv_us: i64,
600 event_type_filter: Option<&EventTypeFilter>,
601 is_created_buy: bool,
602 recent_blockhash: Option<&[u8]>,
603 program_id: Option<&Pubkey>,
604) -> Option<DexEvent> {
605 if program_id == Some(&program_ids::RAYDIUM_AMM_V4_PROGRAM_ID) && log.contains("ray_log: ") {
606 if event_type_filter
607 .map(|filter| !filter.should_include(EventType::RaydiumAmmV4Swap))
608 .unwrap_or(false)
609 {
610 return None;
611 }
612 let metadata = EventMetadata {
613 signature,
614 slot,
615 tx_index,
616 block_time_us: block_time_us.unwrap_or(0),
617 grpc_recv_us,
618 recent_blockhash: recent_blockhash.map(|s| bs58::encode(s).into_string()),
619 };
620 return crate::logs::raydium_amm::parse_ray_log_swap(log, metadata);
621 }
622 let log_bytes = log.as_bytes();
625 let data_start = if log_bytes.starts_with(program_id_strings::PROGRAM_DATA.as_bytes()) {
626 program_id_strings::PROGRAM_DATA.len()
627 } else {
628 PROGRAM_DATA_FINDER.find(log_bytes)? + program_id_strings::PROGRAM_DATA.len()
629 };
630
631 if log_bytes.len() <= data_start {
632 return None;
633 }
634
635 const STACK_DECODE_CAP: usize = 512;
638 let data_part = &log[data_start..];
639 let trimmed = data_part.trim();
640
641 let discriminator = decode_base64_discriminator(trimmed)?;
644 if !filter_allows_discriminator(program_id, discriminator, event_type_filter) {
645 return None;
646 }
647
648 use base64_simd::AsOut;
650 let max_decoded_len = (trimmed.len() / 4).saturating_mul(3).saturating_add(3);
651 let mut stack_buf = [0u8; STACK_DECODE_CAP];
652 let heap_buf: Vec<u8>;
653 let program_data: &[u8] = if max_decoded_len <= STACK_DECODE_CAP {
654 let decoded_len = {
655 let decoded_slice = base64_simd::STANDARD
656 .decode(trimmed.as_bytes(), stack_buf.as_mut().as_out())
657 .ok()?;
658 decoded_slice.len()
659 };
660 &stack_buf[..decoded_len]
661 } else {
662 heap_buf = base64_simd::STANDARD.decode_to_vec(trimmed.as_bytes()).ok()?;
663 heap_buf.as_slice()
664 };
665
666 if program_data.len() < 8 {
667 return None;
668 }
669
670 debug_assert_eq!(discriminator, unsafe {
671 (program_data.as_ptr() as *const u64).read_unaligned()
672 });
673
674 let data = &program_data[8..]; use crate::core::events::*;
678
679 let metadata = EventMetadata {
680 signature,
681 slot,
682 tx_index,
683 block_time_us: block_time_us.unwrap_or(0),
684 grpc_recv_us,
685 recent_blockhash: recent_blockhash.map(|s| bs58::encode(s).into_string()),
686 };
687
688 if crate::logs::pump_upgrade::event_type(discriminator, program_id).is_some() {
689 return apply_event_type_filter(
690 crate::logs::pump_upgrade::parse(discriminator, data, metadata, program_id)?,
691 event_type_filter,
692 );
693 }
694
695 if let Some(program_id) = program_id {
696 return parse_program_scoped_event(
697 program_id,
698 discriminator,
699 data,
700 metadata,
701 log,
702 signature,
703 slot,
704 tx_index,
705 block_time_us,
706 grpc_recv_us,
707 event_type_filter,
708 is_created_buy,
709 );
710 }
711
712 if likely(discriminator == discriminators::PUMPFUN_TRADE) {
720 return parse_unscoped_pumpfun_launchlab_trade(
723 data,
724 metadata,
725 event_type_filter,
726 is_created_buy,
727 );
728 }
729
730 if likely(discriminator == discriminators::RAYDIUM_CLMM_SWAP) {
731 return apply_event_type_filter(
733 crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)?,
734 event_type_filter,
735 );
736 }
737
738 if likely(discriminator == discriminators::RAYDIUM_AMM_SWAP_BASE_IN) {
739 return apply_event_type_filter(
741 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)?,
742 event_type_filter,
743 );
744 }
745
746 if likely(discriminator == discriminators::PUMPSWAP_BUY) {
747 return apply_event_type_filter(
749 crate::logs::pump_amm::parse_buy_from_data(data, metadata)?,
750 event_type_filter,
751 );
752 }
753
754 if discriminator == discriminators::PUMPSWAP_SELL {
755 return apply_event_type_filter(
757 crate::logs::pump_amm::parse_sell_from_data(data, metadata)?,
758 event_type_filter,
759 );
760 }
761
762 let event = match discriminator {
767 discriminators::PUMPFUN_CREATE => crate::logs::pump::parse_create_from_data(data, metadata),
772 discriminators::PUMPFUN_MIGRATE => {
773 crate::logs::pump::parse_migrate_from_data(data, metadata)
774 }
775 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
776 crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
777 }
778 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
779 crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
780 }
781 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
782 crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
783 }
784 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
785 crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(data, metadata)
786 }
787 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
788 crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(data, metadata)
789 }
790 discriminators::PUMP_FEES_UPDATE_ADMIN => {
791 crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
792 }
793 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => {
794 crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
795 }
796 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => {
797 crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
798 }
799 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => {
800 crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
801 }
802 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
803 crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(data, metadata)
804 }
805 discriminators::PUMPSWAP_CREATE_POOL => {
806 crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
807 }
808 discriminators::PUMPSWAP_ADD_LIQUIDITY => {
809 crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
810 }
811 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => {
812 crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
813 }
814
815 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
818 crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
819 }
820 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
821 crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
822 }
823 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
824 crate::logs::raydium_clmm::parse_liquidity_change_from_data(data, metadata)
825 }
826 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => {
827 crate::logs::raydium_clmm::parse_config_change_from_data(data, metadata)
828 }
829 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
830 crate::logs::raydium_clmm::parse_create_personal_position_from_data(data, metadata)
831 }
832 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
833 crate::logs::raydium_clmm::parse_liquidity_calculate_from_data(data, metadata)
834 }
835 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
836 crate::logs::raydium_clmm::parse_open_limit_order_from_data(data, metadata)
837 }
838 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
839 crate::logs::raydium_clmm::parse_increase_limit_order_from_data(data, metadata)
840 }
841 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
842 crate::logs::raydium_clmm::parse_decrease_limit_order_from_data(data, metadata)
843 }
844 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
845 crate::logs::raydium_clmm::parse_settle_limit_order_from_data(data, metadata)
846 }
847 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
848 crate::logs::raydium_clmm::parse_update_reward_infos_from_data(data, metadata)
849 }
850 discriminators::RAYDIUM_CLMM_CREATE_POOL => {
851 crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
852 }
853 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE => {
854 crate::logs::raydium_clmm::parse_collect_personal_fee_from_data(data, metadata)
855 }
856 discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
857 crate::logs::raydium_clmm::parse_collect_protocol_fee_from_data(data, metadata)
858 }
859
860 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
862 crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
863 }
864 discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
865 crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
866 }
867 discriminators::RAYDIUM_CPMM_DEPOSIT => {
870 crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
871 }
872 discriminators::RAYDIUM_CPMM_WITHDRAW => {
873 crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
874 }
875
876 discriminators::RAYDIUM_AMM_SWAP_BASE_IN => {
878 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
879 }
880 discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
881 crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
882 }
883 discriminators::RAYDIUM_AMM_DEPOSIT => {
884 crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
885 }
886 discriminators::RAYDIUM_AMM_WITHDRAW => {
887 crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
888 }
889 discriminators::RAYDIUM_AMM_INITIALIZE2 => {
890 crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
891 }
892 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => {
893 crate::logs::raydium_amm::parse_withdraw_pnl_from_data(data, metadata)
894 }
895
896 discriminators::ORCA_TRADED => {
898 crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
899 }
900 discriminators::ORCA_LIQUIDITY_INCREASED => {
901 crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
902 }
903 discriminators::ORCA_LIQUIDITY_DECREASED => {
904 crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
905 }
906 discriminators::ORCA_POOL_INITIALIZED => {
907 crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
908 }
909
910 discriminators::METEORA_AMM_SWAP => {
912 crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
913 }
914 discriminators::METEORA_AMM_ADD_LIQUIDITY => {
915 crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
916 }
917 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
918 crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
919 }
920 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
921 crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
922 }
923 discriminators::METEORA_AMM_POOL_CREATED => {
924 crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
925 }
926 discriminators::METEORA_AMM_SET_POOL_FEES => {
927 crate::logs::meteora_amm::parse_set_pool_fees_from_data(data, metadata)
928 }
929
930 discriminators::METEORA_DAMM_SWAP => {
932 crate::logs::meteora_damm::parse_swap_from_data(data, metadata)
933 }
934 discriminators::METEORA_DAMM_SWAP2 => {
935 crate::logs::meteora_damm::parse_swap2_from_data(data, metadata)
936 }
937 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
938 crate::logs::meteora_damm::parse_add_liquidity_from_data(data, metadata)
939 }
940 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
941 crate::logs::meteora_damm::parse_remove_liquidity_from_data(data, metadata)
942 }
943 discriminators::METEORA_DAMM_LIQUIDITY_CHANGE => {
944 crate::logs::meteora_damm::parse_liquidity_change_from_data(data, metadata)
945 }
946 discriminators::METEORA_DAMM_INITIALIZE_POOL => {
947 crate::logs::meteora_damm::parse_initialize_pool_from_data(data, metadata)
948 }
949 discriminators::METEORA_DAMM_CREATE_POSITION => {
950 crate::logs::meteora_damm::parse_create_position_from_data(data, metadata)
951 }
952 discriminators::METEORA_DAMM_CLAIM_POSITION_FEE => {
953 crate::logs::meteora_damm::parse_claim_position_fee_from_data(data, metadata)
954 }
955 discriminators::METEORA_DAMM_CLAIM_REWARD => {
956 crate::logs::meteora_damm::parse_claim_reward_from_data(data, metadata)
957 }
958 discriminators::METEORA_DAMM_CLOSE_POSITION => {
959 crate::logs::meteora_damm::parse_close_position_from_data(data, metadata)
960 }
961 discriminators::METEORA_DAMM_UPDATE_DELEGATE_PERMISSION => {
962 crate::logs::meteora_damm::parse_update_delegate_permission_from_data(data, metadata)
963 }
964 discriminators::METEORA_DAMM_WITHDRAW_DEAD_LIQUIDITY_REWARD => {
965 crate::logs::meteora_damm::parse_withdraw_dead_liquidity_reward_from_data(
966 data, metadata,
967 )
968 }
969 discriminators::METEORA_DAMM_WITHDRAW_INELIGIBLE_REWARD => {
970 crate::logs::meteora_damm::parse_withdraw_ineligible_reward_from_data(data, metadata)
971 }
972 discriminators::METEORA_DAMM_UPDATE_REWARD_FUNDER => {
973 crate::logs::meteora_damm::parse_update_reward_funder_from_data(data, metadata)
974 }
975 discriminators::METEORA_DAMM_UPDATE_REWARD_DURATION => {
976 crate::logs::meteora_damm::parse_update_reward_duration_from_data(data, metadata)
977 }
978 discriminators::METEORA_DAMM_INITIALIZE_REWARD => {
979 crate::logs::meteora_damm::parse_initialize_reward_from_data(data, metadata)
980 }
981 discriminators::METEORA_DAMM_FUND_REWARD => {
982 crate::logs::meteora_damm::parse_fund_reward_from_data(data, metadata)
983 }
984 discriminators::METEORA_DAMM_CREATE_CONFIG => {
985 crate::logs::meteora_damm::parse_create_config_from_data(data, metadata)
986 }
987 discriminators::METEORA_DAMM_CREATE_DYNAMIC_CONFIG => {
988 crate::logs::meteora_damm::parse_create_dynamic_config_from_data(data, metadata)
989 }
990
991 _ => {
996 if let Some(event) = crate::logs::parse_meteora_dlmm_log(
998 log,
999 signature,
1000 slot,
1001 tx_index,
1002 block_time_us,
1003 grpc_recv_us,
1004 ) {
1005 return apply_event_type_filter(event, event_type_filter);
1006 }
1007 None
1008 }
1009 }?;
1010 apply_event_type_filter(event, event_type_filter)
1011}
1012
1013#[inline(always)]
1014fn program_scoped_discriminator_to_event_type(
1015 program_id: &Pubkey,
1016 discriminator: u64,
1017) -> Option<EventType> {
1018 if let Some(kind) = crate::logs::pump_upgrade::event_type(discriminator, Some(program_id)) {
1019 return Some(kind);
1020 }
1021 match *program_id {
1022 program_ids::PUMPFUN_PROGRAM_ID => match discriminator {
1023 discriminators::PUMPFUN_CREATE => Some(EventType::PumpFunCreate),
1024 discriminators::PUMPFUN_TRADE => Some(EventType::PumpFunTrade),
1025 discriminators::PUMPFUN_MIGRATE => Some(EventType::PumpFunMigrate),
1026 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
1027 Some(EventType::PumpFunMigrateBondingCurveCreator)
1028 }
1029 _ => None,
1030 },
1031 program_ids::PUMP_FEES_PROGRAM_ID => match discriminator {
1032 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
1033 Some(EventType::PumpFeesCreateFeeSharingConfig)
1034 }
1035 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
1036 Some(EventType::PumpFeesInitializeFeeConfig)
1037 }
1038 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
1039 Some(EventType::PumpFeesResetFeeSharingConfig)
1040 }
1041 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
1042 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
1043 }
1044 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
1045 Some(EventType::PumpFeesTransferFeeSharingAuthority)
1046 }
1047 discriminators::PUMP_FEES_UPDATE_ADMIN => Some(EventType::PumpFeesUpdateAdmin),
1048 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => Some(EventType::PumpFeesUpdateFeeConfig),
1049 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => Some(EventType::PumpFeesUpdateFeeShares),
1050 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => Some(EventType::PumpFeesUpsertFeeTiers),
1051 _ => None,
1052 },
1053 program_ids::PUMPSWAP_PROGRAM_ID => match discriminator {
1054 discriminators::PUMPSWAP_BUY => Some(EventType::PumpSwapBuy),
1055 discriminators::PUMPSWAP_SELL => Some(EventType::PumpSwapSell),
1056 discriminators::PUMPSWAP_CREATE_POOL => Some(EventType::PumpSwapCreatePool),
1057 discriminators::PUMPSWAP_ADD_LIQUIDITY => Some(EventType::PumpSwapLiquidityAdded),
1058 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => Some(EventType::PumpSwapLiquidityRemoved),
1059 _ => None,
1060 },
1061 program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID => match discriminator {
1062 discriminators::RAYDIUM_LAUNCHLAB_TRADE => Some(EventType::RaydiumLaunchlabTrade),
1063 discriminators::RAYDIUM_LAUNCHLAB_POOL_CREATE => {
1064 Some(EventType::RaydiumLaunchlabPoolCreate)
1065 }
1066 _ => None,
1067 },
1068 program_ids::RAYDIUM_CLMM_PROGRAM_ID => match discriminator {
1069 discriminators::RAYDIUM_CLMM_SWAP => Some(EventType::RaydiumClmmSwap),
1070 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
1071 Some(EventType::RaydiumClmmIncreaseLiquidity)
1072 }
1073 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
1074 Some(EventType::RaydiumClmmDecreaseLiquidity)
1075 }
1076 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
1077 Some(EventType::RaydiumClmmLiquidityChange)
1078 }
1079 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => Some(EventType::RaydiumClmmConfigChange),
1080 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
1081 Some(EventType::RaydiumClmmCreatePersonalPosition)
1082 }
1083 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
1084 Some(EventType::RaydiumClmmLiquidityCalculate)
1085 }
1086 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
1087 Some(EventType::RaydiumClmmOpenLimitOrder)
1088 }
1089 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
1090 Some(EventType::RaydiumClmmIncreaseLimitOrder)
1091 }
1092 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
1093 Some(EventType::RaydiumClmmDecreaseLimitOrder)
1094 }
1095 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
1096 Some(EventType::RaydiumClmmSettleLimitOrder)
1097 }
1098 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
1099 Some(EventType::RaydiumClmmUpdateRewardInfos)
1100 }
1101 discriminators::RAYDIUM_CLMM_CREATE_POOL => Some(EventType::RaydiumClmmCreatePool),
1102 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE
1103 | discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
1104 Some(EventType::RaydiumClmmCollectFee)
1105 }
1106 _ => None,
1107 },
1108 program_ids::RAYDIUM_CPMM_PROGRAM_ID => match discriminator {
1109 discriminators::RAYDIUM_CPMM_SWAP_EVENT
1110 | discriminators::RAYDIUM_CPMM_SWAP_BASE_IN
1111 | discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => Some(EventType::RaydiumCpmmSwap),
1112 discriminators::RAYDIUM_CPMM_CREATE_POOL => Some(EventType::RaydiumCpmmInitialize),
1113 discriminators::RAYDIUM_CPMM_DEPOSIT => Some(EventType::RaydiumCpmmDeposit),
1114 discriminators::RAYDIUM_CPMM_WITHDRAW => Some(EventType::RaydiumCpmmWithdraw),
1115 _ => None,
1116 },
1117 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => match discriminator {
1118 discriminators::RAYDIUM_AMM_SWAP_BASE_IN
1119 | discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => Some(EventType::RaydiumAmmV4Swap),
1120 discriminators::RAYDIUM_AMM_DEPOSIT => Some(EventType::RaydiumAmmV4Deposit),
1121 discriminators::RAYDIUM_AMM_WITHDRAW => Some(EventType::RaydiumAmmV4Withdraw),
1122 discriminators::RAYDIUM_AMM_INITIALIZE2 => Some(EventType::RaydiumAmmV4Initialize2),
1123 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => Some(EventType::RaydiumAmmV4WithdrawPnl),
1124 _ => None,
1125 },
1126 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => match discriminator {
1127 discriminators::ORCA_TRADED => Some(EventType::OrcaWhirlpoolSwap),
1128 discriminators::ORCA_LIQUIDITY_INCREASED => {
1129 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
1130 }
1131 discriminators::ORCA_LIQUIDITY_DECREASED => {
1132 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
1133 }
1134 discriminators::ORCA_POOL_INITIALIZED => Some(EventType::OrcaWhirlpoolPoolInitialized),
1135 _ => None,
1136 },
1137 program_ids::METEORA_POOLS_PROGRAM_ID => match discriminator {
1138 discriminators::METEORA_AMM_SWAP => Some(EventType::MeteoraPoolsSwap),
1139 discriminators::METEORA_AMM_ADD_LIQUIDITY => Some(EventType::MeteoraPoolsAddLiquidity),
1140 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
1141 Some(EventType::MeteoraPoolsRemoveLiquidity)
1142 }
1143 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
1144 Some(EventType::MeteoraPoolsBootstrapLiquidity)
1145 }
1146 discriminators::METEORA_AMM_POOL_CREATED => Some(EventType::MeteoraPoolsPoolCreated),
1147 discriminators::METEORA_AMM_SET_POOL_FEES => Some(EventType::MeteoraPoolsSetPoolFees),
1148 _ => None,
1149 },
1150 program_ids::METEORA_DAMM_V2_PROGRAM_ID => match discriminator {
1151 discriminators::METEORA_DAMM_SWAP | discriminators::METEORA_DAMM_SWAP2 => {
1152 Some(EventType::MeteoraDammV2Swap)
1153 }
1154 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
1155 Some(EventType::MeteoraDammV2AddLiquidity)
1156 }
1157 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
1158 Some(EventType::MeteoraDammV2RemoveLiquidity)
1159 }
1160 discriminators::METEORA_DAMM_LIQUIDITY_CHANGE => None,
1163 discriminators::METEORA_DAMM_INITIALIZE_POOL => {
1164 Some(EventType::MeteoraDammV2InitializePool)
1165 }
1166 discriminators::METEORA_DAMM_CREATE_POSITION => {
1167 Some(EventType::MeteoraDammV2CreatePosition)
1168 }
1169 discriminators::METEORA_DAMM_CLAIM_POSITION_FEE => {
1170 Some(EventType::MeteoraDammV2ClaimPositionFee)
1171 }
1172 discriminators::METEORA_DAMM_CLAIM_REWARD => Some(EventType::MeteoraDammV2ClaimReward),
1173 discriminators::METEORA_DAMM_CLOSE_POSITION => {
1174 Some(EventType::MeteoraDammV2ClosePosition)
1175 }
1176 discriminators::METEORA_DAMM_UPDATE_DELEGATE_PERMISSION => {
1177 Some(EventType::MeteoraDammV2UpdateDelegatePermission)
1178 }
1179 discriminators::METEORA_DAMM_WITHDRAW_DEAD_LIQUIDITY_REWARD => {
1180 Some(EventType::MeteoraDammV2WithdrawDeadLiquidityReward)
1181 }
1182 discriminators::METEORA_DAMM_WITHDRAW_INELIGIBLE_REWARD => {
1183 Some(EventType::MeteoraDammV2WithdrawIneligibleReward)
1184 }
1185 discriminators::METEORA_DAMM_UPDATE_REWARD_FUNDER => {
1186 Some(EventType::MeteoraDammV2UpdateRewardFunder)
1187 }
1188 discriminators::METEORA_DAMM_UPDATE_REWARD_DURATION => {
1189 Some(EventType::MeteoraDammV2UpdateRewardDuration)
1190 }
1191 discriminators::METEORA_DAMM_INITIALIZE_REWARD => {
1192 Some(EventType::MeteoraDammV2InitializeReward)
1193 }
1194 discriminators::METEORA_DAMM_FUND_REWARD => Some(EventType::MeteoraDammV2FundReward),
1195 discriminators::METEORA_DAMM_CREATE_CONFIG => {
1196 Some(EventType::MeteoraDammV2CreateConfig)
1197 }
1198 discriminators::METEORA_DAMM_CREATE_DYNAMIC_CONFIG => {
1199 Some(EventType::MeteoraDammV2CreateDynamicConfig)
1200 }
1201 _ => None,
1202 },
1203 program_ids::METEORA_DBC_PROGRAM_ID => match discriminator {
1204 discriminators::METEORA_DBC_SWAP
1205 | discriminators::METEORA_DBC_SWAP2
1206 | discriminators::METEORA_DBC_SWAP2_TRANSFER_HOOK => Some(EventType::MeteoraDbcSwap),
1207 discriminators::METEORA_DBC_INITIALIZE_POOL => {
1208 Some(EventType::MeteoraDbcInitializePool)
1209 }
1210 discriminators::METEORA_DBC_CURVE_COMPLETE => Some(EventType::MeteoraDbcCurveComplete),
1211 _ => None,
1212 },
1213 program_ids::METEORA_DLMM_PROGRAM_ID => match discriminator {
1214 discriminators::METEORA_DLMM_SWAP | discriminators::METEORA_DLMM_SWAP2 => {
1215 Some(EventType::MeteoraDlmmSwap)
1216 }
1217 discriminators::METEORA_DLMM_ADD_LIQUIDITY => Some(EventType::MeteoraDlmmAddLiquidity),
1218 discriminators::METEORA_DLMM_REMOVE_LIQUIDITY => {
1219 Some(EventType::MeteoraDlmmRemoveLiquidity)
1220 }
1221 discriminators::METEORA_DLMM_INITIALIZE_POOL => {
1222 Some(EventType::MeteoraDlmmInitializePool)
1223 }
1224 discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY => {
1225 Some(EventType::MeteoraDlmmInitializeBinArray)
1226 }
1227 discriminators::METEORA_DLMM_CREATE_POSITION => {
1228 Some(EventType::MeteoraDlmmCreatePosition)
1229 }
1230 discriminators::METEORA_DLMM_CLOSE_POSITION => {
1231 Some(EventType::MeteoraDlmmClosePosition)
1232 }
1233 discriminators::METEORA_DLMM_CLAIM_FEE | discriminators::METEORA_DLMM_CLAIM_FEE2 => {
1234 Some(EventType::MeteoraDlmmClaimFee)
1235 }
1236 discriminators::METEORA_DLMM_CLAIM_REWARD2 => Some(EventType::MeteoraDlmmClaimReward),
1237 _ => None,
1238 },
1239 _ => None,
1240 }
1241}
1242
1243#[inline(always)]
1244fn parse_program_scoped_event(
1245 program_id: &Pubkey,
1246 discriminator: u64,
1247 data: &[u8],
1248 metadata: EventMetadata,
1249 log: &str,
1250 signature: Signature,
1251 slot: u64,
1252 tx_index: u64,
1253 block_time_us: Option<i64>,
1254 grpc_recv_us: i64,
1255 event_type_filter: Option<&EventTypeFilter>,
1256 is_created_buy: bool,
1257) -> Option<DexEvent> {
1258 if let Some(filter) = event_type_filter {
1259 if *program_id == program_ids::PUMPFUN_PROGRAM_ID
1260 && discriminator == discriminators::PUMPFUN_TRADE
1261 {
1262 if !filter_wants_pumpfun_trade_event(filter) {
1263 return None;
1264 }
1265 } else if let Some(event_type) =
1266 program_scoped_discriminator_to_event_type(program_id, discriminator)
1267 {
1268 if !filter.should_include(event_type) {
1269 return None;
1270 }
1271 }
1272 }
1273
1274 match *program_id {
1275 program_ids::PUMPFUN_PROGRAM_ID => {
1276 if let Some(filter) = event_type_filter {
1277 if !filter.includes_pumpfun() {
1278 return None;
1279 }
1280 }
1281 match discriminator {
1282 discriminators::PUMPFUN_TRADE => {
1283 let event =
1284 crate::logs::pump::parse_trade_from_data(data, metadata, is_created_buy)?;
1285 filter_pumpfun_trade_variant(event, event_type_filter)
1286 }
1287 discriminators::PUMPFUN_CREATE => {
1288 crate::logs::pump::parse_create_from_data(data, metadata)
1289 }
1290 discriminators::PUMPFUN_MIGRATE => {
1291 crate::logs::pump::parse_migrate_from_data(data, metadata)
1292 }
1293 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
1294 crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(data, metadata)
1295 }
1296 _ => None,
1297 }
1298 }
1299 program_ids::PUMP_FEES_PROGRAM_ID => {
1300 if let Some(filter) = event_type_filter {
1301 if !filter.includes_pump_fees() {
1302 return None;
1303 }
1304 }
1305 match discriminator {
1306 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
1307 crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(
1308 data, metadata,
1309 )
1310 }
1311 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
1312 crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
1313 }
1314 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
1315 crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
1316 }
1317 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
1318 crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(
1319 data, metadata,
1320 )
1321 }
1322 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
1323 crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(
1324 data, metadata,
1325 )
1326 }
1327 discriminators::PUMP_FEES_UPDATE_ADMIN => {
1328 crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
1329 }
1330 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => {
1331 crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
1332 }
1333 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => {
1334 crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
1335 }
1336 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => {
1337 crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
1338 }
1339 _ => None,
1340 }
1341 }
1342 program_ids::PUMPSWAP_PROGRAM_ID => {
1343 if let Some(filter) = event_type_filter {
1344 if !filter.includes_pumpswap() {
1345 return None;
1346 }
1347 }
1348 match discriminator {
1349 discriminators::PUMPSWAP_BUY => {
1350 crate::logs::pump_amm::parse_buy_from_data(data, metadata)
1351 }
1352 discriminators::PUMPSWAP_SELL => {
1353 crate::logs::pump_amm::parse_sell_from_data(data, metadata)
1354 }
1355 discriminators::PUMPSWAP_CREATE_POOL => {
1356 crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
1357 }
1358 discriminators::PUMPSWAP_ADD_LIQUIDITY => {
1359 crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
1360 }
1361 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => {
1362 crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
1363 }
1364 _ => None,
1365 }
1366 }
1367 program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID => {
1368 if let Some(filter) = event_type_filter {
1369 if !filter.includes_raydium_launchlab() {
1370 return None;
1371 }
1372 }
1373 match discriminator {
1374 discriminators::RAYDIUM_LAUNCHLAB_TRADE => {
1375 crate::logs::raydium_launchlab::parse_trade_from_data(data, metadata)
1376 }
1377 discriminators::RAYDIUM_LAUNCHLAB_POOL_CREATE => {
1378 crate::logs::raydium_launchlab::parse_pool_create_from_data(data, metadata)
1379 }
1380 _ => None,
1381 }
1382 }
1383 program_ids::RAYDIUM_CLMM_PROGRAM_ID => {
1384 if let Some(filter) = event_type_filter {
1385 if !filter.includes_raydium_clmm() {
1386 return None;
1387 }
1388 }
1389 match discriminator {
1390 discriminators::RAYDIUM_CLMM_SWAP => {
1391 crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)
1392 }
1393 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
1394 crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
1395 }
1396 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
1397 crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
1398 }
1399 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
1400 crate::logs::raydium_clmm::parse_liquidity_change_from_data(data, metadata)
1401 }
1402 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => {
1403 crate::logs::raydium_clmm::parse_config_change_from_data(data, metadata)
1404 }
1405 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
1406 crate::logs::raydium_clmm::parse_create_personal_position_from_data(
1407 data, metadata,
1408 )
1409 }
1410 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
1411 crate::logs::raydium_clmm::parse_liquidity_calculate_from_data(data, metadata)
1412 }
1413 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
1414 crate::logs::raydium_clmm::parse_open_limit_order_from_data(data, metadata)
1415 }
1416 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
1417 crate::logs::raydium_clmm::parse_increase_limit_order_from_data(data, metadata)
1418 }
1419 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
1420 crate::logs::raydium_clmm::parse_decrease_limit_order_from_data(data, metadata)
1421 }
1422 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
1423 crate::logs::raydium_clmm::parse_settle_limit_order_from_data(data, metadata)
1424 }
1425 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
1426 crate::logs::raydium_clmm::parse_update_reward_infos_from_data(data, metadata)
1427 }
1428 discriminators::RAYDIUM_CLMM_CREATE_POOL => {
1429 crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
1430 }
1431 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE => {
1432 crate::logs::raydium_clmm::parse_collect_personal_fee_from_data(data, metadata)
1433 }
1434 discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
1435 crate::logs::raydium_clmm::parse_collect_protocol_fee_from_data(data, metadata)
1436 }
1437 _ => None,
1438 }
1439 }
1440 program_ids::RAYDIUM_CPMM_PROGRAM_ID => {
1441 if let Some(filter) = event_type_filter {
1442 if !filter.includes_raydium_cpmm() {
1443 return None;
1444 }
1445 }
1446 match discriminator {
1447 discriminators::RAYDIUM_CPMM_SWAP_EVENT => {
1448 crate::logs::raydium_cpmm::parse_swap_event_from_data(data, metadata)
1449 }
1450 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
1451 crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
1452 }
1453 discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
1454 crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
1455 }
1456 discriminators::RAYDIUM_CPMM_CREATE_POOL => {
1457 crate::logs::raydium_cpmm::parse_create_pool_from_data(data, metadata)
1458 }
1459 discriminators::RAYDIUM_CPMM_DEPOSIT => {
1460 crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
1461 }
1462 discriminators::RAYDIUM_CPMM_WITHDRAW => {
1463 crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
1464 }
1465 _ => None,
1466 }
1467 }
1468 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => {
1469 if let Some(filter) = event_type_filter {
1470 if !filter.includes_raydium_amm_v4() {
1471 return None;
1472 }
1473 }
1474 match discriminator {
1475 discriminators::RAYDIUM_AMM_SWAP_BASE_IN => {
1476 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
1477 }
1478 discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
1479 crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
1480 }
1481 discriminators::RAYDIUM_AMM_DEPOSIT => {
1482 crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
1483 }
1484 discriminators::RAYDIUM_AMM_WITHDRAW => {
1485 crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
1486 }
1487 discriminators::RAYDIUM_AMM_INITIALIZE2 => {
1488 crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
1489 }
1490 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => {
1491 crate::logs::raydium_amm::parse_withdraw_pnl_from_data(data, metadata)
1492 }
1493 _ => None,
1494 }
1495 }
1496 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => {
1497 if let Some(filter) = event_type_filter {
1498 if !filter.includes_orca_whirlpool() {
1499 return None;
1500 }
1501 }
1502 match discriminator {
1503 discriminators::ORCA_TRADED => {
1504 crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
1505 }
1506 discriminators::ORCA_LIQUIDITY_INCREASED => {
1507 crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
1508 }
1509 discriminators::ORCA_LIQUIDITY_DECREASED => {
1510 crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
1511 }
1512 discriminators::ORCA_POOL_INITIALIZED => {
1513 crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
1514 }
1515 _ => None,
1516 }
1517 }
1518 program_ids::METEORA_POOLS_PROGRAM_ID => {
1519 if let Some(filter) = event_type_filter {
1520 if !filter.includes_meteora_pools() {
1521 return None;
1522 }
1523 }
1524 match discriminator {
1525 discriminators::METEORA_AMM_SWAP => {
1526 crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
1527 }
1528 discriminators::METEORA_AMM_ADD_LIQUIDITY => {
1529 crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
1530 }
1531 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
1532 crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
1533 }
1534 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
1535 crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
1536 }
1537 discriminators::METEORA_AMM_POOL_CREATED => {
1538 crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
1539 }
1540 discriminators::METEORA_AMM_SET_POOL_FEES => {
1541 crate::logs::meteora_amm::parse_set_pool_fees_from_data(data, metadata)
1542 }
1543 _ => None,
1544 }
1545 }
1546 program_ids::METEORA_DAMM_V2_PROGRAM_ID => {
1547 if let Some(filter) = event_type_filter {
1548 if !filter.includes_meteora_damm_v2() {
1549 return None;
1550 }
1551 }
1552 match discriminator {
1553 discriminators::METEORA_DAMM_SWAP => {
1554 crate::logs::meteora_damm::parse_swap_from_data(data, metadata)
1555 }
1556 discriminators::METEORA_DAMM_SWAP2 => {
1557 crate::logs::meteora_damm::parse_swap2_from_data(data, metadata)
1558 }
1559 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
1560 crate::logs::meteora_damm::parse_add_liquidity_from_data(data, metadata)
1561 }
1562 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
1563 crate::logs::meteora_damm::parse_remove_liquidity_from_data(data, metadata)
1564 }
1565 discriminators::METEORA_DAMM_LIQUIDITY_CHANGE => {
1566 let event = crate::logs::meteora_damm::parse_liquidity_change_from_data(
1567 data, metadata,
1568 )?;
1569 apply_event_type_filter(event, event_type_filter)
1570 }
1571 discriminators::METEORA_DAMM_INITIALIZE_POOL => {
1572 crate::logs::meteora_damm::parse_initialize_pool_from_data(data, metadata)
1573 }
1574 discriminators::METEORA_DAMM_CREATE_POSITION => {
1575 crate::logs::meteora_damm::parse_create_position_from_data(data, metadata)
1576 }
1577 discriminators::METEORA_DAMM_CLAIM_POSITION_FEE => {
1578 crate::logs::meteora_damm::parse_claim_position_fee_from_data(data, metadata)
1579 }
1580 discriminators::METEORA_DAMM_CLAIM_REWARD => {
1581 crate::logs::meteora_damm::parse_claim_reward_from_data(data, metadata)
1582 }
1583 discriminators::METEORA_DAMM_CLOSE_POSITION => {
1584 crate::logs::meteora_damm::parse_close_position_from_data(data, metadata)
1585 }
1586 discriminators::METEORA_DAMM_UPDATE_DELEGATE_PERMISSION => {
1587 crate::logs::meteora_damm::parse_update_delegate_permission_from_data(
1588 data, metadata,
1589 )
1590 }
1591 discriminators::METEORA_DAMM_WITHDRAW_DEAD_LIQUIDITY_REWARD => {
1592 crate::logs::meteora_damm::parse_withdraw_dead_liquidity_reward_from_data(
1593 data, metadata,
1594 )
1595 }
1596 discriminators::METEORA_DAMM_WITHDRAW_INELIGIBLE_REWARD => {
1597 crate::logs::meteora_damm::parse_withdraw_ineligible_reward_from_data(
1598 data, metadata,
1599 )
1600 }
1601 discriminators::METEORA_DAMM_UPDATE_REWARD_FUNDER => {
1602 crate::logs::meteora_damm::parse_update_reward_funder_from_data(data, metadata)
1603 }
1604 discriminators::METEORA_DAMM_UPDATE_REWARD_DURATION => {
1605 crate::logs::meteora_damm::parse_update_reward_duration_from_data(
1606 data, metadata,
1607 )
1608 }
1609 discriminators::METEORA_DAMM_INITIALIZE_REWARD => {
1610 crate::logs::meteora_damm::parse_initialize_reward_from_data(data, metadata)
1611 }
1612 discriminators::METEORA_DAMM_FUND_REWARD => {
1613 crate::logs::meteora_damm::parse_fund_reward_from_data(data, metadata)
1614 }
1615 discriminators::METEORA_DAMM_CREATE_CONFIG => {
1616 crate::logs::meteora_damm::parse_create_config_from_data(data, metadata)
1617 }
1618 discriminators::METEORA_DAMM_CREATE_DYNAMIC_CONFIG => {
1619 crate::logs::meteora_damm::parse_create_dynamic_config_from_data(data, metadata)
1620 }
1621 _ => None,
1622 }
1623 }
1624 program_ids::METEORA_DBC_PROGRAM_ID => {
1625 if let Some(filter) = event_type_filter {
1626 if !filter.includes_meteora_dbc() {
1627 return None;
1628 }
1629 }
1630 match discriminator {
1631 discriminators::METEORA_DBC_SWAP => {
1632 crate::logs::meteora_dbc::parse_swap_from_data(data, metadata)
1633 }
1634 discriminators::METEORA_DBC_SWAP2
1635 | discriminators::METEORA_DBC_SWAP2_TRANSFER_HOOK => {
1636 crate::logs::meteora_dbc::parse_swap2_from_data(
1637 data,
1638 metadata,
1639 discriminator == discriminators::METEORA_DBC_SWAP2_TRANSFER_HOOK,
1640 )
1641 }
1642 discriminators::METEORA_DBC_INITIALIZE_POOL => {
1643 crate::logs::meteora_dbc::parse_initialize_pool_from_data(data, metadata)
1644 }
1645 discriminators::METEORA_DBC_CURVE_COMPLETE => {
1646 crate::logs::meteora_dbc::parse_curve_complete_from_data(data, metadata)
1647 }
1648 _ => None,
1649 }
1650 }
1651 program_ids::METEORA_DLMM_PROGRAM_ID => {
1652 if let Some(filter) = event_type_filter {
1653 if !filter.includes_meteora_dlmm() {
1654 return None;
1655 }
1656 }
1657 match discriminator {
1658 discriminators::METEORA_DLMM_SWAP => {
1659 crate::logs::meteora_dlmm::parse_swap_from_data(data, metadata)
1660 }
1661 discriminators::METEORA_DLMM_SWAP2 => {
1662 crate::logs::meteora_dlmm::parse_swap2_from_data(data, metadata)
1663 }
1664 discriminators::METEORA_DLMM_ADD_LIQUIDITY => {
1665 crate::logs::meteora_dlmm::parse_add_liquidity_from_data(data, metadata)
1666 }
1667 discriminators::METEORA_DLMM_REMOVE_LIQUIDITY => {
1668 crate::logs::meteora_dlmm::parse_remove_liquidity_from_data(data, metadata)
1669 }
1670 discriminators::METEORA_DLMM_INITIALIZE_POOL => {
1671 crate::logs::meteora_dlmm::parse_lb_pair_create_from_data(data, metadata)
1672 }
1673 discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY => {
1674 crate::logs::meteora_dlmm::parse_initialize_bin_array_from_data(data, metadata)
1675 }
1676 discriminators::METEORA_DLMM_CREATE_POSITION => {
1677 crate::logs::meteora_dlmm::parse_position_create_from_data(data, metadata)
1678 }
1679 discriminators::METEORA_DLMM_CLOSE_POSITION => {
1680 crate::logs::meteora_dlmm::parse_position_close_from_data(data, metadata)
1681 }
1682 discriminators::METEORA_DLMM_CLAIM_FEE => {
1683 crate::logs::meteora_dlmm::parse_claim_fee_from_data(data, metadata)
1684 }
1685 discriminators::METEORA_DLMM_CLAIM_FEE2 => {
1686 crate::logs::meteora_dlmm::parse_claim_fee2_from_data(data, metadata)
1687 }
1688 discriminators::METEORA_DLMM_CLAIM_REWARD2 => {
1689 crate::logs::meteora_dlmm::parse_claim_reward2_from_data(data, metadata)
1690 }
1691 _ => None,
1692 }
1693 }
1694 _ => None,
1695 }
1696}
1697
1698#[inline(always)]
1699fn filter_pumpfun_trade_variant(
1700 event: DexEvent,
1701 event_type_filter: Option<&EventTypeFilter>,
1702) -> Option<DexEvent> {
1703 if let Some(filter) = event_type_filter {
1704 if let Some(ref include_only) = filter.include_only {
1705 let has_specific_filter = !include_only.contains(&EventType::PumpFunTrade)
1706 && include_only.iter().any(|t| {
1707 matches!(
1708 t,
1709 EventType::PumpFunBuy
1710 | EventType::PumpFunSell
1711 | EventType::PumpFunBuyExactSolIn
1712 | EventType::PumpFunCreate
1713 | EventType::PumpFunCreateV2
1714 )
1715 });
1716 if has_specific_filter {
1717 let event_type_matches = match &event {
1718 DexEvent::PumpFunBuy(_) => include_only.iter().any(|t| {
1719 matches!(t, EventType::PumpFunBuy | EventType::PumpFunBuyExactSolIn)
1720 }),
1721 DexEvent::PumpFunSell(_) => include_only.contains(&EventType::PumpFunSell),
1722 DexEvent::PumpFunBuyExactSolIn(_) => include_only.iter().any(|t| {
1723 matches!(t, EventType::PumpFunBuy | EventType::PumpFunBuyExactSolIn)
1724 }),
1725 DexEvent::PumpFunTrade(_) => include_only.contains(&EventType::PumpFunTrade),
1726 DexEvent::PumpFunCreate(_) | DexEvent::PumpFunCreateV2(_) => {
1727 include_only.iter().any(|t| {
1728 matches!(t, EventType::PumpFunCreate | EventType::PumpFunCreateV2)
1729 })
1730 }
1731 _ => false,
1732 };
1733 if !event_type_matches {
1734 return None;
1735 }
1736 }
1737 }
1738 if filter.exclude_types.is_some() && !filter.should_include_dex_event(&event) {
1739 return None;
1740 }
1741 }
1742 Some(event)
1743}
1744
1745#[inline(always)]
1747fn discriminator_to_event_type(discriminator: u64) -> Option<EventType> {
1748 if discriminator == discriminators::METEORA_DLMM_CLAIM_REWARD2 {
1749 return Some(EventType::MeteoraDlmmClaimReward);
1750 }
1751 if let Some(kind) = crate::logs::pump_upgrade::event_type(discriminator, None) {
1752 return Some(kind);
1753 }
1754
1755 match discriminator {
1756 discriminators::PUMPFUN_CREATE => Some(EventType::PumpFunCreate),
1757 discriminators::PUMPFUN_TRADE => Some(EventType::PumpFunTrade),
1758 discriminators::PUMPFUN_MIGRATE => Some(EventType::PumpFunMigrate),
1759 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
1760 Some(EventType::PumpFeesCreateFeeSharingConfig)
1761 }
1762 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
1763 Some(EventType::PumpFeesInitializeFeeConfig)
1764 }
1765 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
1766 Some(EventType::PumpFeesResetFeeSharingConfig)
1767 }
1768 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
1769 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
1770 }
1771 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
1772 Some(EventType::PumpFeesTransferFeeSharingAuthority)
1773 }
1774 discriminators::PUMP_FEES_UPDATE_ADMIN => Some(EventType::PumpFeesUpdateAdmin),
1775 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => Some(EventType::PumpFeesUpdateFeeConfig),
1776 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => Some(EventType::PumpFeesUpdateFeeShares),
1777 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => Some(EventType::PumpFeesUpsertFeeTiers),
1778 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
1779 Some(EventType::PumpFunMigrateBondingCurveCreator)
1780 }
1781 discriminators::PUMPSWAP_BUY => Some(EventType::PumpSwapBuy),
1782 discriminators::PUMPSWAP_SELL => Some(EventType::PumpSwapSell),
1783 discriminators::PUMPSWAP_CREATE_POOL => Some(EventType::PumpSwapCreatePool),
1784 discriminators::PUMPSWAP_ADD_LIQUIDITY => Some(EventType::PumpSwapLiquidityAdded),
1785 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => Some(EventType::PumpSwapLiquidityRemoved),
1786 discriminators::RAYDIUM_LAUNCHLAB_POOL_CREATE => {
1787 Some(EventType::RaydiumLaunchlabPoolCreate)
1788 }
1789 discriminators::RAYDIUM_CLMM_SWAP => Some(EventType::RaydiumClmmSwap),
1790 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
1791 Some(EventType::RaydiumClmmIncreaseLiquidity)
1792 }
1793 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
1794 Some(EventType::RaydiumClmmDecreaseLiquidity)
1795 }
1796 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
1797 Some(EventType::RaydiumClmmLiquidityChange)
1798 }
1799 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => Some(EventType::RaydiumClmmConfigChange),
1800 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
1801 Some(EventType::RaydiumClmmCreatePersonalPosition)
1802 }
1803 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
1804 Some(EventType::RaydiumClmmLiquidityCalculate)
1805 }
1806 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => Some(EventType::RaydiumClmmOpenLimitOrder),
1807 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
1808 Some(EventType::RaydiumClmmIncreaseLimitOrder)
1809 }
1810 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
1811 Some(EventType::RaydiumClmmDecreaseLimitOrder)
1812 }
1813 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
1814 Some(EventType::RaydiumClmmSettleLimitOrder)
1815 }
1816 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
1817 Some(EventType::RaydiumClmmUpdateRewardInfos)
1818 }
1819 discriminators::RAYDIUM_CLMM_CREATE_POOL => Some(EventType::RaydiumClmmCreatePool),
1820 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE
1821 | discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
1822 Some(EventType::RaydiumClmmCollectFee)
1823 }
1824 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN | discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
1825 Some(EventType::RaydiumCpmmSwap)
1826 }
1827 discriminators::RAYDIUM_CPMM_DEPOSIT => Some(EventType::RaydiumCpmmDeposit),
1828 discriminators::RAYDIUM_CPMM_WITHDRAW => Some(EventType::RaydiumCpmmWithdraw),
1829 discriminators::RAYDIUM_AMM_SWAP_BASE_IN | discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
1830 Some(EventType::RaydiumAmmV4Swap)
1831 }
1832 discriminators::RAYDIUM_AMM_DEPOSIT => Some(EventType::RaydiumAmmV4Deposit),
1833 discriminators::RAYDIUM_AMM_WITHDRAW => Some(EventType::RaydiumAmmV4Withdraw),
1834 discriminators::RAYDIUM_AMM_INITIALIZE2 => Some(EventType::RaydiumAmmV4Initialize2),
1835 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => Some(EventType::RaydiumAmmV4WithdrawPnl),
1836 discriminators::ORCA_TRADED => Some(EventType::OrcaWhirlpoolSwap),
1837 discriminators::ORCA_LIQUIDITY_INCREASED => {
1838 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
1839 }
1840 discriminators::ORCA_LIQUIDITY_DECREASED => {
1841 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
1842 }
1843 discriminators::ORCA_POOL_INITIALIZED => Some(EventType::OrcaWhirlpoolPoolInitialized),
1844 discriminators::METEORA_AMM_SWAP => Some(EventType::MeteoraPoolsSwap),
1845 discriminators::METEORA_AMM_ADD_LIQUIDITY => Some(EventType::MeteoraPoolsAddLiquidity),
1846 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
1847 Some(EventType::MeteoraPoolsRemoveLiquidity)
1848 }
1849 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
1850 Some(EventType::MeteoraPoolsBootstrapLiquidity)
1851 }
1852 discriminators::METEORA_AMM_POOL_CREATED => Some(EventType::MeteoraPoolsPoolCreated),
1853 discriminators::METEORA_AMM_SET_POOL_FEES => Some(EventType::MeteoraPoolsSetPoolFees),
1854 discriminators::METEORA_DAMM_SWAP | discriminators::METEORA_DAMM_SWAP2 => {
1855 Some(EventType::MeteoraDammV2Swap)
1856 }
1857 discriminators::METEORA_DAMM_ADD_LIQUIDITY => Some(EventType::MeteoraDammV2AddLiquidity),
1858 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
1859 Some(EventType::MeteoraDammV2RemoveLiquidity)
1860 }
1861 discriminators::METEORA_DAMM_INITIALIZE_POOL => {
1862 Some(EventType::MeteoraDammV2InitializePool)
1863 }
1864 discriminators::METEORA_DAMM_CREATE_POSITION => {
1865 Some(EventType::MeteoraDammV2CreatePosition)
1866 }
1867 discriminators::METEORA_DAMM_CLAIM_POSITION_FEE => {
1868 Some(EventType::MeteoraDammV2ClaimPositionFee)
1869 }
1870 discriminators::METEORA_DAMM_CLAIM_REWARD => Some(EventType::MeteoraDammV2ClaimReward),
1871 discriminators::METEORA_DAMM_CLOSE_POSITION => Some(EventType::MeteoraDammV2ClosePosition),
1872 discriminators::METEORA_DAMM_UPDATE_DELEGATE_PERMISSION => {
1873 Some(EventType::MeteoraDammV2UpdateDelegatePermission)
1874 }
1875 discriminators::METEORA_DAMM_WITHDRAW_DEAD_LIQUIDITY_REWARD => {
1876 Some(EventType::MeteoraDammV2WithdrawDeadLiquidityReward)
1877 }
1878 discriminators::METEORA_DAMM_WITHDRAW_INELIGIBLE_REWARD => {
1879 Some(EventType::MeteoraDammV2WithdrawIneligibleReward)
1880 }
1881 discriminators::METEORA_DAMM_UPDATE_REWARD_FUNDER => {
1882 Some(EventType::MeteoraDammV2UpdateRewardFunder)
1883 }
1884 discriminators::METEORA_DAMM_UPDATE_REWARD_DURATION => {
1885 Some(EventType::MeteoraDammV2UpdateRewardDuration)
1886 }
1887 discriminators::METEORA_DAMM_INITIALIZE_REWARD => {
1888 Some(EventType::MeteoraDammV2InitializeReward)
1889 }
1890 discriminators::METEORA_DAMM_FUND_REWARD => Some(EventType::MeteoraDammV2FundReward),
1891 discriminators::METEORA_DAMM_CREATE_CONFIG => Some(EventType::MeteoraDammV2CreateConfig),
1892 discriminators::METEORA_DAMM_CREATE_DYNAMIC_CONFIG => {
1893 Some(EventType::MeteoraDammV2CreateDynamicConfig)
1894 }
1895 _ => None,
1896 }
1897}
1898
1899#[inline]
1903pub fn detect_pumpfun_create(logs: &[String]) -> bool {
1904 logs.iter().any(|log| {
1905 let bytes = log.as_bytes();
1906 bytes.starts_with(PUMPFUN_CREATE_PREFIX)
1907 && matches!(bytes.get(PUMPFUN_CREATE_PREFIX.len()), Some(b'Y' | b'Z' | b'a' | b'b'))
1908 })
1909}
1910
1911static INVOKE_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"invoke ["));
1912
1913#[inline]
1916pub fn parse_invoke_info(log: &str) -> Option<(&str, usize)> {
1917 let bytes = log.as_bytes();
1918 if !bytes.starts_with(b"Program ") || !bytes.ends_with(b"]") {
1919 return None;
1920 }
1921 let invoke_start = INVOKE_FINDER.find(bytes)?;
1922 if invoke_start <= 8 || bytes.get(invoke_start - 1) != Some(&b' ') {
1923 return None;
1924 }
1925 let depth_start = invoke_start + b"invoke [".len();
1926 let depth_bytes = bytes.get(depth_start..bytes.len() - 1)?;
1927 if depth_bytes.is_empty() {
1928 return None;
1929 }
1930
1931 let mut depth = 0usize;
1932 for &byte in depth_bytes {
1933 if !byte.is_ascii_digit() {
1934 return None;
1935 }
1936 let digit = (byte - b'0') as usize;
1937 depth = depth.checked_mul(10)?.checked_add(digit)?;
1938 }
1939 let program_id = log.get(8..invoke_start - 1)?;
1940 (depth > 0).then_some((program_id, depth))
1941}
1942
1943#[inline]
1945pub fn parse_program_complete_info(log: &str) -> Option<&str> {
1946 let rest = log.strip_prefix("Program ")?;
1947 if let Some(program_id) = rest.strip_suffix(" success") {
1948 return (!program_id.is_empty()).then_some(program_id);
1949 }
1950 if let Some(pos) = rest.find(" failed:") {
1951 return Some(&rest[..pos]);
1952 }
1953 None
1954}
1955
1956#[cfg(test)]
1957mod tests {
1958 use super::*;
1959 use crate::core::events::PumpFunTradeEvent;
1960 use base64::{engine::general_purpose::STANDARD, Engine as _};
1961 use solana_sdk::{pubkey::Pubkey, signature::Signature};
1962
1963 #[test]
1964 fn pumpfun_create_detection_requires_canonical_program_data_prefix() {
1965 for first_payload_byte in [0, 64, 128, 192] {
1966 let mut raw = discriminators::PUMPFUN_CREATE.to_le_bytes().to_vec();
1967 raw.push(first_payload_byte);
1968 let create = vec![format!("Program data: {}", STANDARD.encode(raw))];
1969 assert!(detect_pumpfun_create(&create));
1970 }
1971
1972 let unrelated = vec!["Program log: Program data: G3KpTd7rY3Yrest".to_owned()];
1973 assert!(!detect_pumpfun_create(&unrelated));
1974 }
1975
1976 #[test]
1977 fn program_scoped_launchlab_trade_is_not_parsed_as_pumpfun() {
1978 let pool = Pubkey::new_unique();
1979 let mut raw = Vec::new();
1980 raw.extend_from_slice(&discriminators::RAYDIUM_LAUNCHLAB_TRADE.to_le_bytes());
1981 raw.extend_from_slice(pool.as_ref());
1982 for value in 0u64..13 {
1983 raw.extend_from_slice(&(100 + value).to_le_bytes());
1984 }
1985 raw.push(1); raw.push(2); raw.push(1); let log = format!("Program data: {}", STANDARD.encode(raw));
1990 let filter = EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade]);
1991 let event = parse_log_optimized_with_program_id(
1992 &log,
1993 Signature::default(),
1994 1,
1995 2,
1996 Some(3),
1997 4,
1998 Some(&filter),
1999 false,
2000 None,
2001 Some(&program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID),
2002 )
2003 .expect("launchlab trade should parse");
2004
2005 match event {
2006 DexEvent::RaydiumLaunchlabTrade(trade) => {
2007 assert_eq!(trade.pool_state, pool);
2008 assert_eq!(trade.amount_in, 107);
2009 assert_eq!(trade.amount_out, 108);
2010 assert!(!trade.is_buy);
2011 assert!(trade.exact_in);
2012 }
2013 other => panic!("expected RaydiumLaunchlabTrade, got {other:?}"),
2014 }
2015 }
2016
2017 fn launchlab_trade_log() -> (String, Pubkey) {
2018 let pool = Pubkey::new_unique();
2019 let mut raw = Vec::new();
2020 raw.extend_from_slice(&discriminators::RAYDIUM_LAUNCHLAB_TRADE.to_le_bytes());
2021 raw.extend_from_slice(pool.as_ref());
2022 for value in 0u64..13 {
2023 raw.extend_from_slice(&(100 + value).to_le_bytes());
2024 }
2025 raw.push(1); raw.push(2); raw.push(1); (format!("Program data: {}", STANDARD.encode(raw)), pool)
2030 }
2031
2032 #[test]
2033 fn unscoped_launchlab_trade_filter_parses_shared_discriminator() {
2034 let (log, pool) = launchlab_trade_log();
2035 let filter = EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade]);
2036 let event = parse_log_optimized(
2037 &log,
2038 Signature::default(),
2039 1,
2040 2,
2041 Some(3),
2042 4,
2043 Some(&filter),
2044 false,
2045 None,
2046 )
2047 .expect("unscoped LaunchLab trade should parse when requested");
2048
2049 match event {
2050 DexEvent::RaydiumLaunchlabTrade(trade) => {
2051 assert_eq!(trade.pool_state, pool);
2052 assert_eq!(trade.amount_in, 107);
2053 assert_eq!(trade.amount_out, 108);
2054 assert!(!trade.is_buy);
2055 assert!(trade.exact_in);
2056 }
2057 other => panic!("expected RaydiumLaunchlabTrade, got {other:?}"),
2058 }
2059 }
2060
2061 #[test]
2062 fn unscoped_pumpfun_only_filter_does_not_parse_launchlab_trade() {
2063 let (log, _pool) = launchlab_trade_log();
2064 let filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2065 assert!(parse_log_optimized(
2066 &log,
2067 Signature::default(),
2068 1,
2069 2,
2070 Some(3),
2071 4,
2072 Some(&filter),
2073 false,
2074 None,
2075 )
2076 .is_none());
2077 }
2078
2079 #[test]
2080 fn program_scoped_dlmm_initialize_bin_array_parses_and_filters() {
2081 let pool = Pubkey::new_unique();
2082 let bin_array = Pubkey::new_unique();
2083 let mut raw = Vec::new();
2084 raw.extend_from_slice(&discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY.to_le_bytes());
2085 raw.extend_from_slice(pool.as_ref());
2086 raw.extend_from_slice(bin_array.as_ref());
2087 raw.extend_from_slice(&(-12i64).to_le_bytes());
2088
2089 let log = format!("Program data: {}", STANDARD.encode(raw));
2090 let matching_filter =
2091 EventTypeFilter::include_only(vec![EventType::MeteoraDlmmInitializeBinArray]);
2092 let event = parse_log_optimized_with_program_id(
2093 &log,
2094 Signature::default(),
2095 1,
2096 2,
2097 Some(3),
2098 4,
2099 Some(&matching_filter),
2100 false,
2101 None,
2102 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
2103 )
2104 .expect("DLMM initialize bin array should parse");
2105
2106 match event {
2107 DexEvent::MeteoraDlmmInitializeBinArray(event) => {
2108 assert_eq!(event.pool, pool);
2109 assert_eq!(event.bin_array, bin_array);
2110 assert_eq!(event.index, -12);
2111 }
2112 other => panic!("expected MeteoraDlmmInitializeBinArray, got {other:?}"),
2113 }
2114
2115 let non_matching_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
2116 assert!(parse_log_optimized_with_program_id(
2117 &log,
2118 Signature::default(),
2119 1,
2120 2,
2121 Some(3),
2122 4,
2123 Some(&non_matching_filter),
2124 false,
2125 None,
2126 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
2127 )
2128 .is_none());
2129 }
2130
2131 #[test]
2132 fn pumpfun_trade_filter_remains_generic_when_combined_with_specific_type() {
2133 let filter =
2134 EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
2135 let event = DexEvent::PumpFunSell(PumpFunTradeEvent {
2136 metadata: EventMetadata::default(),
2137 is_buy: false,
2138 ix_name: "sell".to_string(),
2139 ..Default::default()
2140 });
2141
2142 assert!(filter_pumpfun_trade_variant(event, Some(&filter)).is_some());
2143 }
2144
2145 #[test]
2146 fn pumpfun_buy_family_filter_matches_both_buy_variants() {
2147 let buy = DexEvent::PumpFunBuy(PumpFunTradeEvent {
2148 metadata: EventMetadata::default(),
2149 is_buy: true,
2150 ix_name: "buy_exact_quote_in_v2".to_string(),
2151 ..Default::default()
2152 });
2153 let exact_sol = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
2154 metadata: EventMetadata::default(),
2155 is_buy: true,
2156 ix_name: "buy_exact_sol_in".to_string(),
2157 ..Default::default()
2158 });
2159
2160 let buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2161 assert!(filter_pumpfun_trade_variant(buy.clone(), Some(&buy_filter)).is_some());
2162 assert!(filter_pumpfun_trade_variant(exact_sol.clone(), Some(&buy_filter)).is_some());
2163
2164 let exact_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
2165 assert!(filter_pumpfun_trade_variant(buy, Some(&exact_filter)).is_some());
2166 assert!(filter_pumpfun_trade_variant(exact_sol, Some(&exact_filter)).is_some());
2167 }
2168
2169 #[test]
2170 fn discriminator_prefix_filter_handles_program_scoped_collisions() {
2171 let dlmm_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
2172 assert!(filter_allows_discriminator(
2173 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
2174 discriminators::METEORA_DLMM_SWAP,
2175 Some(&dlmm_filter),
2176 ));
2177 assert!(!filter_allows_discriminator(
2178 Some(&program_ids::RAYDIUM_CPMM_PROGRAM_ID),
2179 discriminators::METEORA_DLMM_SWAP,
2180 Some(&dlmm_filter),
2181 ));
2182
2183 let raydium_launchlab_filter =
2184 EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade]);
2185 assert!(filter_allows_discriminator(
2186 Some(&program_ids::RAYDIUM_LAUNCHLAB_PROGRAM_ID),
2187 discriminators::RAYDIUM_LAUNCHLAB_TRADE,
2188 Some(&raydium_launchlab_filter),
2189 ));
2190 assert!(!filter_allows_discriminator(
2191 Some(&program_ids::PUMPFUN_PROGRAM_ID),
2192 discriminators::PUMPFUN_TRADE,
2193 Some(&raydium_launchlab_filter),
2194 ));
2195
2196 let pumpfun_buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2197 assert!(filter_allows_discriminator(
2198 Some(&program_ids::PUMPFUN_PROGRAM_ID),
2199 discriminators::PUMPFUN_TRADE,
2200 Some(&pumpfun_buy_filter),
2201 ));
2202
2203 let dbc_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]);
2204 assert!(filter_allows_discriminator(
2205 Some(&program_ids::METEORA_DBC_PROGRAM_ID),
2206 discriminators::METEORA_DBC_SWAP,
2207 Some(&dbc_filter),
2208 ));
2209 assert!(!filter_allows_discriminator(
2210 Some(&program_ids::METEORA_DAMM_V2_PROGRAM_ID),
2211 discriminators::METEORA_DAMM_SWAP,
2212 Some(&dbc_filter),
2213 ));
2214 assert!(!filter_allows_discriminator(
2215 Some(&program_ids::METEORA_DBC_PROGRAM_ID),
2216 discriminators::METEORA_DLMM_CLOSE_POSITION,
2217 Some(&raydium_launchlab_filter),
2218 ));
2219 }
2220
2221 #[test]
2222 fn program_scoped_pumpfun_buy_filter_parses_trade_log_variant() {
2223 let log = "Program data: vdt/007mYe5StuUGXKtQJzSLsEK5h79gIdGUQz7vyn59ApMQyeYlr3cK4wUAAAAA7dnMPhkDAAAB5uPeR/hOJigYiGhz2PiTzeNML3vtbrwijyhrJHoTgitivC1qAAAAALjcux8HAAAAt2E0T9e8AwC4MJgjAAAAALfJIQNGvgIA4ATIfOuY+lzkf4A4Bv0seUXSlSSVmuwA3tl4FPOPeEZfAAAAAAAAACBRDgAAAAAAbf5L76S20PsQ+d4EfYrWKDprZOVyf9lJPbA04mYiiiweAAAAAAAAAGmFBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAGJ1eQAAAAAAAAAAAAAAAAAAAAAAiBMAAAAAAACQKAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHcK4wUAAAAAuNy7HwcAAAC4MJgjAAAAAA==";
2224 let filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2225 let event = parse_log_optimized_with_program_id(
2226 log,
2227 Signature::default(),
2228 426270756,
2229 268,
2230 Some(1781382243600841),
2231 1781382243601307,
2232 Some(&filter),
2233 false,
2234 None,
2235 Some(&program_ids::PUMPFUN_PROGRAM_ID),
2236 )
2237 .expect("PumpFun TradeEvent log should parse under PumpFunBuy filter");
2238
2239 match event {
2240 DexEvent::PumpFunBuy(trade) => {
2241 assert_eq!(trade.ix_name, "buy");
2242 assert_eq!(trade.sol_amount, 98_765_431);
2243 assert_eq!(trade.token_amount, 3_406_962_678_253);
2244 assert_eq!(trade.virtual_sol_reserves, 30_597_176_504);
2245 assert_eq!(trade.virtual_token_reserves, 1_052_057_862_955_447);
2246 assert_eq!(trade.real_sol_reserves, 597_176_504);
2247 assert_eq!(trade.real_token_reserves, 772_157_862_955_447);
2248 }
2249 other => panic!("expected PumpFunBuy, got {other:?}"),
2250 }
2251 }
2252
2253 #[test]
2254 fn unscoped_pumpfun_buy_filter_parses_trade_log_variant() {
2255 let log = "Program data: vdt/007mYe5StuUGXKtQJzSLsEK5h79gIdGUQz7vyn59ApMQyeYlr3cK4wUAAAAA7dnMPhkDAAAB5uPeR/hOJigYiGhz2PiTzeNML3vtbrwijyhrJHoTgitivC1qAAAAALjcux8HAAAAt2E0T9e8AwC4MJgjAAAAALfJIQNGvgIA4ATIfOuY+lzkf4A4Bv0seUXSlSSVmuwA3tl4FPOPeEZfAAAAAAAAACBRDgAAAAAAbf5L76S20PsQ+d4EfYrWKDprZOVyf9lJPbA04mYiiiweAAAAAAAAAGmFBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAGJ1eQAAAAAAAAAAAAAAAAAAAAAAiBMAAAAAAACQKAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHcK4wUAAAAAuNy7HwcAAAC4MJgjAAAAAA==";
2256 let filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2257 let event = parse_log_optimized(
2258 log,
2259 Signature::default(),
2260 426270756,
2261 268,
2262 Some(1781382243600841),
2263 1781382243601307,
2264 Some(&filter),
2265 false,
2266 None,
2267 )
2268 .expect("Unscoped PumpFun TradeEvent log should parse under PumpFunBuy filter");
2269
2270 match event {
2271 DexEvent::PumpFunBuy(trade) => {
2272 assert_eq!(trade.ix_name, "buy");
2273 assert_eq!(trade.sol_amount, 98_765_431);
2274 assert_eq!(trade.token_amount, 3_406_962_678_253);
2275 assert_eq!(trade.virtual_sol_reserves, 30_597_176_504);
2276 assert_eq!(trade.virtual_token_reserves, 1_052_057_862_955_447);
2277 assert_eq!(trade.real_sol_reserves, 597_176_504);
2278 assert_eq!(trade.real_token_reserves, 772_157_862_955_447);
2279 }
2280 other => panic!("expected PumpFunBuy, got {other:?}"),
2281 }
2282 }
2283
2284 #[test]
2285 fn discriminator_prefix_filter_keeps_unscoped_collision_candidates() {
2286 let dlmm_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
2287 assert!(filter_allows_discriminator(
2288 None,
2289 discriminators::METEORA_DLMM_SWAP2,
2290 Some(&dlmm_filter),
2291 ));
2292
2293 let cpmm_filter = EventTypeFilter::include_only(vec![EventType::RaydiumCpmmInitialize]);
2294 assert!(filter_allows_discriminator(
2295 None,
2296 discriminators::RAYDIUM_CPMM_CREATE_POOL,
2297 Some(&cpmm_filter),
2298 ));
2299
2300 let pumpfun_buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
2301 assert!(filter_allows_discriminator(
2302 None,
2303 discriminators::PUMPFUN_TRADE,
2304 Some(&pumpfun_buy_filter),
2305 ));
2306 }
2307
2308 #[test]
2309 fn unscoped_collision_does_not_emit_wrong_protocol_event_after_filter() {
2310 let mut raw = Vec::new();
2311 raw.extend_from_slice(&discriminators::METEORA_DLMM_SWAP.to_le_bytes());
2312 raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&1u64.to_le_bytes());
2315 raw.extend_from_slice(&2u64.to_le_bytes());
2316 raw.extend_from_slice(&3u64.to_le_bytes());
2317 raw.push(1);
2318
2319 let log = format!("Program data: {}", STANDARD.encode(raw));
2320 let filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
2321 assert!(parse_log_optimized(
2322 &log,
2323 Signature::default(),
2324 1,
2325 2,
2326 Some(3),
2327 4,
2328 Some(&filter),
2329 false,
2330 None,
2331 )
2332 .is_none());
2333 }
2334
2335 #[test]
2336 fn unscoped_pumpfun_launchlab_collision_does_not_emit_wrong_protocol_event() {
2337 let mut raw = Vec::new();
2338 raw.extend_from_slice(&discriminators::PUMPFUN_TRADE.to_le_bytes());
2339 raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&1u64.to_le_bytes()); raw.extend_from_slice(&2u64.to_le_bytes()); raw.push(1); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&3i64.to_le_bytes()); for value in 4u64..=9 {
2346 raw.extend_from_slice(&value.to_le_bytes());
2347 }
2348 raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&10u64.to_le_bytes()); raw.extend_from_slice(&11u64.to_le_bytes()); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&12u64.to_le_bytes()); raw.extend_from_slice(&13u64.to_le_bytes()); let log = format!("Program data: {}", STANDARD.encode(raw));
2356 let filter = EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade]);
2357 assert!(parse_log_optimized(
2358 &log,
2359 Signature::default(),
2360 1,
2361 2,
2362 Some(3),
2363 4,
2364 Some(&filter),
2365 false,
2366 None,
2367 )
2368 .is_none());
2369 }
2370
2371 #[test]
2372 fn discriminator_prefix_decode_reads_first_event_bytes() {
2373 let mut raw = Vec::new();
2374 raw.extend_from_slice(&discriminators::PUMP_FEES_UPDATE_ADMIN.to_le_bytes());
2375 raw.extend_from_slice(Pubkey::new_unique().as_ref());
2376 raw.extend_from_slice(Pubkey::new_unique().as_ref());
2377
2378 let encoded = STANDARD.encode(raw);
2379 assert_eq!(
2380 decode_base64_discriminator(&encoded),
2381 Some(discriminators::PUMP_FEES_UPDATE_ADMIN)
2382 );
2383 }
2384
2385 #[test]
2386 fn program_scoped_damm_add_liquidity_parses_from_decoded_data() {
2387 let pool = Pubkey::new_unique();
2388 let position = Pubkey::new_unique();
2389 let owner = Pubkey::new_unique();
2390 let mut raw = Vec::new();
2391 raw.extend_from_slice(&discriminators::METEORA_DAMM_ADD_LIQUIDITY.to_le_bytes());
2392 raw.extend_from_slice(pool.as_ref());
2393 raw.extend_from_slice(position.as_ref());
2394 raw.extend_from_slice(owner.as_ref());
2395 raw.extend_from_slice(&123u128.to_le_bytes());
2396 raw.extend_from_slice(&10u64.to_le_bytes());
2397 raw.extend_from_slice(&20u64.to_le_bytes());
2398 raw.extend_from_slice(&30u64.to_le_bytes());
2399 raw.extend_from_slice(&40u64.to_le_bytes());
2400 raw.extend_from_slice(&50u64.to_le_bytes());
2401 raw.extend_from_slice(&60u64.to_le_bytes());
2402
2403 let log = format!("Program data: {}", STANDARD.encode(raw));
2404 let filter = EventTypeFilter::include_only(vec![EventType::MeteoraDammV2AddLiquidity]);
2405 let event = parse_log_optimized_with_program_id(
2406 &log,
2407 Signature::default(),
2408 1,
2409 2,
2410 Some(3),
2411 4,
2412 Some(&filter),
2413 false,
2414 None,
2415 Some(&program_ids::METEORA_DAMM_V2_PROGRAM_ID),
2416 )
2417 .expect("DAMM V2 add-liquidity should parse");
2418
2419 match event {
2420 DexEvent::MeteoraDammV2AddLiquidity(event) => {
2421 assert_eq!(event.pool, pool);
2422 assert_eq!(event.position, position);
2423 assert_eq!(event.owner, owner);
2424 assert_eq!(event.liquidity_delta, 123);
2425 assert_eq!(event.token_a_amount, 30);
2426 assert_eq!(event.token_b_amount, 40);
2427 assert_eq!(event.total_amount_a, 50);
2428 assert_eq!(event.total_amount_b, 60);
2429 }
2430 other => panic!("expected MeteoraDammV2AddLiquidity, got {other:?}"),
2431 }
2432 }
2433
2434 #[test]
2435 fn program_scoped_dbc_swap_parses_and_does_not_emit_as_damm() {
2436 let pool = Pubkey::new_unique();
2437 let config = Pubkey::new_unique();
2438 let mut raw = Vec::new();
2439 raw.extend_from_slice(&discriminators::METEORA_DBC_SWAP.to_le_bytes());
2440 raw.extend_from_slice(pool.as_ref());
2441 raw.extend_from_slice(config.as_ref());
2442 raw.push(1);
2443 raw.push(0);
2444 raw.extend_from_slice(&100u64.to_le_bytes());
2445 raw.extend_from_slice(&90u64.to_le_bytes());
2446 raw.extend_from_slice(&100u64.to_le_bytes());
2447 raw.extend_from_slice(&95u64.to_le_bytes());
2448 raw.extend_from_slice(&(1u128 << 64).to_le_bytes());
2449 raw.extend_from_slice(&2u64.to_le_bytes());
2450 raw.extend_from_slice(&3u64.to_le_bytes());
2451 raw.extend_from_slice(&0u64.to_le_bytes());
2452 raw.extend_from_slice(&100u64.to_le_bytes());
2453 raw.extend_from_slice(&1_777_920_719u64.to_le_bytes());
2454
2455 let log = format!("Program data: {}", STANDARD.encode(raw));
2456 let dbc_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]);
2457 let event = parse_log_optimized_with_program_id(
2458 &log,
2459 Signature::default(),
2460 1,
2461 2,
2462 Some(3),
2463 4,
2464 Some(&dbc_filter),
2465 false,
2466 None,
2467 Some(&program_ids::METEORA_DBC_PROGRAM_ID),
2468 )
2469 .expect("DBC swap should parse with DBC program context");
2470
2471 match event {
2472 DexEvent::MeteoraDbcSwap(event) => {
2473 assert_eq!(event.pool, pool);
2474 assert_eq!(event.config, config);
2475 assert_eq!(event.amount_in, 100);
2476 assert_eq!(event.output_amount, 95);
2477 }
2478 other => panic!("expected MeteoraDbcSwap, got {other:?}"),
2479 }
2480
2481 let damm_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap]);
2482 assert!(parse_log_optimized_with_program_id(
2483 &log,
2484 Signature::default(),
2485 1,
2486 2,
2487 Some(3),
2488 4,
2489 Some(&damm_filter),
2490 false,
2491 None,
2492 Some(&program_ids::METEORA_DBC_PROGRAM_ID),
2493 )
2494 .is_none());
2495 }
2496
2497 #[test]
2498 fn large_program_data_uses_heap_fallback_without_dropping_event() {
2499 let mut raw = Vec::new();
2500 raw.extend_from_slice(&discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG.to_le_bytes());
2501 raw.extend_from_slice(&1_777_920_719i64.to_le_bytes());
2502 raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.push(0); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(Pubkey::new_unique().as_ref()); raw.extend_from_slice(&64u32.to_le_bytes());
2508 for i in 0..64u16 {
2509 raw.extend_from_slice(Pubkey::new_unique().as_ref());
2510 raw.extend_from_slice(&i.to_le_bytes());
2511 }
2512 raw.push(1); let encoded = STANDARD.encode(&raw);
2515 assert!(encoded.len() > 2700, "test must exceed the old fixed stack buffer limit");
2516 let log = format!("Program data: {encoded}");
2517
2518 let event = parse_log_optimized_with_program_id(
2519 &log,
2520 Signature::default(),
2521 1,
2522 2,
2523 Some(3),
2524 4,
2525 None,
2526 false,
2527 None,
2528 Some(&program_ids::PUMP_FEES_PROGRAM_ID),
2529 )
2530 .expect("large pump-fees event should parse via heap fallback");
2531
2532 match event {
2533 DexEvent::PumpFeesCreateFeeSharingConfig(event) => {
2534 assert_eq!(event.initial_shareholders.len(), 64);
2535 assert_eq!(event.status, crate::core::events::PumpFeesConfigStatus::Active);
2536 }
2537 other => panic!("expected PumpFeesCreateFeeSharingConfig, got {other:?}"),
2538 }
2539 }
2540
2541 #[test]
2542 fn completion_parser_extracts_program_id() {
2543 assert_eq!(
2544 parse_program_complete_info(
2545 "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj success"
2546 ),
2547 Some("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj")
2548 );
2549 assert_eq!(
2550 parse_program_complete_info(
2551 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C failed: custom program error: 0x1"
2552 ),
2553 Some("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C")
2554 );
2555 }
2556
2557 #[test]
2558 fn invoke_parser_accepts_only_complete_positive_depth_lines() {
2559 assert_eq!(
2560 parse_invoke_info("Program 11111111111111111111111111111111 invoke [12]"),
2561 Some(("11111111111111111111111111111111", 12))
2562 );
2563 for malformed in [
2564 "prefix Program 11111111111111111111111111111111 invoke [1]",
2565 "Program 11111111111111111111111111111111 invoke [0]",
2566 "Program 11111111111111111111111111111111 invoke [1",
2567 "Program 11111111111111111111111111111111 invoke []",
2568 "Program 11111111111111111111111111111111 invoke [1] trailing",
2569 ] {
2570 assert_eq!(parse_invoke_info(malformed), None, "accepted {malformed}");
2571 }
2572 }
2573}