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 BONK_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: "));
33static PUMPFUN_CREATE_FINDER: Lazy<memmem::Finder> =
34 Lazy::new(|| memmem::Finder::new(b"Program data: G3KpTd7rY3Y"));
35static WHIRL_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"whirL"));
36static METEORA_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"meteora"));
37static METEORA_LB_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"LB"));
38static METEORA_DLMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"DLMM"));
39static PUMPSWAP_LOWER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"pumpswap"));
40static PUMPSWAP_UPPER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"PumpSwap"));
41
42pub mod program_id_strings {
44 pub const PUMPFUN_INVOKE: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke";
45 pub const PUMPFUN_SUCCESS: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success";
46 pub const PUMPFUN_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
47
48 pub const BONK_INVOKE: &str = "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj invoke";
49 pub const BONK_SUCCESS: &str = "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj success";
50 pub const BONK_ID: &str = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj";
51
52 pub const RAYDIUM_CLMM_INVOKE: &str =
53 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK invoke";
54 pub const RAYDIUM_CLMM_SUCCESS: &str =
55 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK success";
56 pub const RAYDIUM_CLMM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK";
57
58 pub const RAYDIUM_CPMM_INVOKE: &str =
59 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C invoke";
60 pub const RAYDIUM_CPMM_SUCCESS: &str =
61 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C success";
62 pub const RAYDIUM_CPMM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
63
64 pub const RAYDIUM_AMM_V4_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
65
66 pub const PROGRAM_DATA: &str = "Program data: ";
68 pub const PROGRAM_LOG: &str = "Program log: ";
69
70 pub const PUMPFUN_CREATE_DISCRIMINATOR: &str = "GB7IKAUcB3c"; }
73
74#[derive(Debug, Copy, Clone, PartialEq)]
76pub enum LogType {
77 PumpFun,
78 RaydiumLaunchpad,
79 PumpAmm,
80 RaydiumClmm,
81 RaydiumCpmm,
82 RaydiumAmm,
83 OrcaWhirlpool,
84 MeteoraAmm,
85 MeteoraDamm,
86 MeteoraDlmm,
87 Unknown,
88}
89
90#[inline(always)]
92pub fn detect_log_type(log: &str) -> LogType {
93 let log_bytes = log.as_bytes();
94
95 if log_bytes.len() < 20 {
97 return LogType::Unknown;
98 }
99
100 let has_program_data = PROGRAM_DATA_FINDER.find(log_bytes).is_some();
102
103 if unlikely(!has_program_data) {
105 return LogType::Unknown;
106 }
107
108 if likely(RAYDIUM_AMM_FINDER.find(log_bytes).is_some()) {
111 return LogType::RaydiumAmm;
112 }
113
114 if RAYDIUM_CLMM_FINDER.find(log_bytes).is_some() {
116 return LogType::RaydiumClmm;
117 }
118
119 if RAYDIUM_CPMM_FINDER.find(log_bytes).is_some() {
121 return LogType::RaydiumCpmm;
122 }
123
124 if BONK_FINDER.find(log_bytes).is_some() {
126 return LogType::RaydiumLaunchpad;
127 }
128
129 if WHIRL_FINDER.find(log_bytes).is_some() {
131 return LogType::OrcaWhirlpool;
132 }
133
134 if let Some(pos) = METEORA_FINDER.find(log_bytes) {
136 let rest = &log_bytes[pos..];
137 if METEORA_LB_FINDER.find(rest).is_some() {
138 return LogType::MeteoraDamm;
139 } else if METEORA_DLMM_FINDER.find(rest).is_some() {
140 return LogType::MeteoraDlmm;
141 } else {
142 return LogType::MeteoraAmm;
143 }
144 }
145
146 if PUMPSWAP_LOWER_FINDER.find(log_bytes).is_some()
148 || PUMPSWAP_UPPER_FINDER.find(log_bytes).is_some()
149 {
150 return LogType::PumpAmm;
151 }
152
153 if likely(PUMPFUN_FINDER.find(log_bytes).is_some()) {
156 return LogType::PumpFun;
157 }
158
159 if log.len() > 30 {
163 return LogType::PumpFun;
164 }
165
166 LogType::Unknown
167}
168
169mod discriminators {
173 pub const PUMPFUN_CREATE: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
175 pub const PUMPFUN_TRADE: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
176 pub const PUMPFUN_MIGRATE: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
177 pub const PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR: u64 =
178 u64::from_le_bytes([155, 167, 104, 220, 213, 108, 243, 3]);
179 pub const RAYDIUM_LAUNCHPAD_POOL_CREATE: u64 =
183 u64::from_le_bytes([151, 215, 226, 9, 118, 161, 115, 174]);
184 pub const RAYDIUM_LAUNCHPAD_TRADE: u64 =
185 u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
186 pub const PUMP_FEES_CREATE_FEE_SHARING_CONFIG: u64 =
188 u64::from_le_bytes([133, 105, 170, 200, 184, 116, 251, 88]);
189 pub const PUMP_FEES_INITIALIZE_FEE_CONFIG: u64 =
190 u64::from_le_bytes([89, 138, 244, 230, 10, 56, 226, 126]);
191 pub const PUMP_FEES_RESET_FEE_SHARING_CONFIG: u64 =
192 u64::from_le_bytes([203, 204, 151, 226, 120, 55, 214, 243]);
193 pub const PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY: u64 =
194 u64::from_le_bytes([114, 23, 101, 60, 14, 190, 153, 62]);
195 pub const PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY: u64 =
196 u64::from_le_bytes([124, 143, 198, 245, 77, 184, 8, 236]);
197 pub const PUMP_FEES_UPDATE_ADMIN: u64 =
198 u64::from_le_bytes([225, 152, 171, 87, 246, 63, 66, 234]);
199 pub const PUMP_FEES_UPDATE_FEE_CONFIG: u64 =
200 u64::from_le_bytes([90, 23, 65, 35, 62, 244, 188, 208]);
201 pub const PUMP_FEES_UPDATE_FEE_SHARES: u64 =
202 u64::from_le_bytes([21, 186, 196, 184, 91, 228, 225, 203]);
203 pub const PUMP_FEES_UPSERT_FEE_TIERS: u64 =
204 u64::from_le_bytes([171, 89, 169, 187, 122, 186, 33, 204]);
205
206 pub const PUMPSWAP_BUY: u64 = u64::from_le_bytes([103, 244, 82, 31, 44, 245, 119, 119]);
208 pub const PUMPSWAP_SELL: u64 = u64::from_le_bytes([62, 47, 55, 10, 165, 3, 220, 42]);
209 pub const PUMPSWAP_CREATE_POOL: u64 =
210 u64::from_le_bytes([177, 49, 12, 210, 160, 118, 167, 116]);
211 pub const PUMPSWAP_ADD_LIQUIDITY: u64 =
212 u64::from_le_bytes([120, 248, 61, 83, 31, 142, 107, 144]);
213 pub const PUMPSWAP_REMOVE_LIQUIDITY: u64 =
214 u64::from_le_bytes([22, 9, 133, 26, 160, 44, 71, 192]);
215
216 pub const RAYDIUM_CLMM_SWAP: u64 = u64::from_le_bytes([64, 198, 205, 232, 38, 8, 113, 226]);
218 pub const RAYDIUM_CLMM_INCREASE_LIQUIDITY: u64 =
219 u64::from_le_bytes([49, 79, 105, 212, 32, 34, 30, 84]);
220 pub const RAYDIUM_CLMM_DECREASE_LIQUIDITY: u64 =
221 u64::from_le_bytes([58, 222, 86, 58, 68, 50, 85, 56]);
222 pub const RAYDIUM_CLMM_LIQUIDITY_CHANGE: u64 =
223 u64::from_le_bytes([126, 240, 175, 206, 158, 88, 153, 107]);
224 pub const RAYDIUM_CLMM_CONFIG_CHANGE: u64 =
225 u64::from_le_bytes([247, 189, 7, 119, 106, 112, 95, 151]);
226 pub const RAYDIUM_CLMM_CREATE_PERSONAL_POSITION: u64 =
227 u64::from_le_bytes([100, 30, 87, 249, 196, 223, 154, 206]);
228 pub const RAYDIUM_CLMM_LIQUIDITY_CALCULATE: u64 =
229 u64::from_le_bytes([237, 112, 148, 230, 57, 84, 180, 162]);
230 pub const RAYDIUM_CLMM_OPEN_LIMIT_ORDER: u64 =
231 u64::from_le_bytes([106, 24, 71, 85, 57, 169, 158, 216]);
232 pub const RAYDIUM_CLMM_INCREASE_LIMIT_ORDER: u64 =
233 u64::from_le_bytes([11, 120, 13, 204, 199, 87, 19, 200]);
234 pub const RAYDIUM_CLMM_DECREASE_LIMIT_ORDER: u64 =
235 u64::from_le_bytes([70, 48, 40, 221, 219, 237, 212, 163]);
236 pub const RAYDIUM_CLMM_SETTLE_LIMIT_ORDER: u64 =
237 u64::from_le_bytes([88, 119, 77, 164, 125, 124, 10, 194]);
238 pub const RAYDIUM_CLMM_UPDATE_REWARD_INFOS: u64 =
239 u64::from_le_bytes([109, 127, 186, 78, 114, 65, 37, 236]);
240 pub const RAYDIUM_CLMM_CREATE_POOL: u64 = u64::from_le_bytes([25, 94, 75, 47, 112, 99, 53, 63]);
241 pub const RAYDIUM_CLMM_COLLECT_PERSONAL_FEE: u64 =
242 u64::from_le_bytes([166, 174, 105, 192, 81, 161, 83, 105]);
243 pub const RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE: u64 =
244 u64::from_le_bytes([206, 87, 17, 79, 45, 41, 213, 61]);
245
246 pub const RAYDIUM_CPMM_SWAP_BASE_IN: u64 =
248 u64::from_le_bytes([143, 190, 90, 218, 196, 30, 51, 222]);
249 pub const RAYDIUM_CPMM_SWAP_BASE_OUT: u64 =
250 u64::from_le_bytes([55, 217, 98, 86, 163, 74, 180, 173]);
251 pub const RAYDIUM_CPMM_CREATE_POOL: u64 =
252 u64::from_le_bytes([233, 146, 209, 142, 207, 104, 64, 188]);
253 pub const RAYDIUM_CPMM_DEPOSIT: u64 =
254 u64::from_le_bytes([242, 35, 198, 137, 82, 225, 242, 182]);
255 pub const RAYDIUM_CPMM_WITHDRAW: u64 =
256 u64::from_le_bytes([183, 18, 70, 156, 148, 109, 161, 34]);
257
258 pub const RAYDIUM_AMM_SWAP_BASE_IN: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 9]);
260 pub const RAYDIUM_AMM_SWAP_BASE_OUT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 11]);
261 pub const RAYDIUM_AMM_DEPOSIT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 3]);
262 pub const RAYDIUM_AMM_WITHDRAW: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 4]);
263 pub const RAYDIUM_AMM_INITIALIZE2: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 1]);
264 pub const RAYDIUM_AMM_WITHDRAW_PNL: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 7]);
265
266 pub const ORCA_TRADED: u64 = u64::from_le_bytes([225, 202, 73, 175, 147, 43, 160, 150]);
268 pub const ORCA_LIQUIDITY_INCREASED: u64 =
269 u64::from_le_bytes([30, 7, 144, 181, 102, 254, 155, 161]);
270 pub const ORCA_LIQUIDITY_DECREASED: u64 =
271 u64::from_le_bytes([166, 1, 36, 71, 112, 202, 181, 171]);
272 pub const ORCA_POOL_INITIALIZED: u64 =
273 u64::from_le_bytes([100, 118, 173, 87, 12, 198, 254, 229]);
274
275 pub const METEORA_AMM_SWAP: u64 = u64::from_le_bytes([81, 108, 227, 190, 205, 208, 10, 196]);
277 pub const METEORA_AMM_ADD_LIQUIDITY: u64 =
278 u64::from_le_bytes([31, 94, 125, 90, 227, 52, 61, 186]);
279 pub const METEORA_AMM_REMOVE_LIQUIDITY: u64 =
280 u64::from_le_bytes([116, 244, 97, 232, 103, 31, 152, 58]);
281 pub const METEORA_AMM_BOOTSTRAP_LIQUIDITY: u64 =
282 u64::from_le_bytes([121, 127, 38, 136, 92, 55, 14, 247]);
283 pub const METEORA_AMM_POOL_CREATED: u64 =
284 u64::from_le_bytes([202, 44, 41, 88, 104, 220, 157, 82]);
285 pub const METEORA_AMM_SET_POOL_FEES: u64 =
286 u64::from_le_bytes([245, 26, 198, 164, 88, 18, 75, 9]);
287
288 pub const METEORA_DAMM_SWAP: u64 = u64::from_le_bytes([27, 60, 21, 213, 138, 170, 187, 147]);
290 pub const METEORA_DAMM_SWAP2: u64 = u64::from_le_bytes([189, 66, 51, 168, 38, 80, 117, 153]);
291 pub const METEORA_DAMM_ADD_LIQUIDITY: u64 =
292 u64::from_le_bytes([175, 242, 8, 157, 30, 247, 185, 169]);
293 pub const METEORA_DAMM_REMOVE_LIQUIDITY: u64 =
294 u64::from_le_bytes([87, 46, 88, 98, 175, 96, 34, 91]);
295 pub const METEORA_DAMM_INITIALIZE_POOL: u64 =
296 u64::from_le_bytes([228, 50, 246, 85, 203, 66, 134, 37]);
297 pub const METEORA_DAMM_CREATE_POSITION: u64 =
298 u64::from_le_bytes([156, 15, 119, 198, 29, 181, 221, 55]);
299 pub const METEORA_DAMM_CLOSE_POSITION: u64 =
300 u64::from_le_bytes([20, 145, 144, 68, 143, 142, 214, 178]);
301
302 pub const METEORA_DLMM_SWAP: u64 = u64::from_le_bytes([143, 190, 90, 218, 196, 30, 51, 222]);
304 pub const METEORA_DLMM_ADD_LIQUIDITY: u64 =
305 u64::from_le_bytes([181, 157, 89, 67, 143, 182, 52, 72]);
306 pub const METEORA_DLMM_REMOVE_LIQUIDITY: u64 =
307 u64::from_le_bytes([80, 85, 209, 72, 24, 206, 35, 178]);
308 pub const METEORA_DLMM_INITIALIZE_POOL: u64 =
309 u64::from_le_bytes([95, 180, 10, 172, 84, 174, 232, 40]);
310 pub const METEORA_DLMM_INITIALIZE_BIN_ARRAY: u64 =
311 u64::from_le_bytes([11, 18, 155, 194, 33, 115, 238, 119]);
312 pub const METEORA_DLMM_CREATE_POSITION: u64 =
313 u64::from_le_bytes([123, 233, 11, 43, 146, 180, 97, 119]);
314 pub const METEORA_DLMM_CLOSE_POSITION: u64 =
315 u64::from_le_bytes([94, 168, 102, 45, 59, 122, 137, 54]);
316 pub const METEORA_DLMM_CLAIM_FEE: u64 = u64::from_le_bytes([152, 70, 208, 111, 104, 91, 44, 1]);
317}
318
319#[inline(always)]
331pub fn parse_log_optimized(
333 log: &str,
334 signature: Signature,
335 slot: u64,
336 tx_index: u64,
337 block_time_us: Option<i64>,
338 grpc_recv_us: i64,
339 event_type_filter: Option<&EventTypeFilter>,
340 is_created_buy: bool,
341 recent_blockhash: Option<&[u8]>,
342) -> Option<DexEvent> {
343 parse_log_optimized_inner(
344 log,
345 signature,
346 slot,
347 tx_index,
348 block_time_us,
349 grpc_recv_us,
350 event_type_filter,
351 is_created_buy,
352 recent_blockhash,
353 None,
354 )
355}
356
357#[inline(always)]
363pub fn parse_log_optimized_with_program_id(
364 log: &str,
365 signature: Signature,
366 slot: u64,
367 tx_index: u64,
368 block_time_us: Option<i64>,
369 grpc_recv_us: i64,
370 event_type_filter: Option<&EventTypeFilter>,
371 is_created_buy: bool,
372 recent_blockhash: Option<&[u8]>,
373 program_id: Option<&Pubkey>,
374) -> Option<DexEvent> {
375 parse_log_optimized_inner(
376 log,
377 signature,
378 slot,
379 tx_index,
380 block_time_us,
381 grpc_recv_us,
382 event_type_filter,
383 is_created_buy,
384 recent_blockhash,
385 program_id,
386 )
387}
388
389#[inline(always)]
390fn decode_base64_discriminator(trimmed: &str) -> Option<u64> {
391 let bytes = trimmed.as_bytes();
392 if bytes.len() < 12 {
393 return None;
394 }
395
396 let mut discriminator_buf = [0u8; 9];
397 let decoded_len = {
398 use base64_simd::AsOut;
399 let decoded =
400 base64_simd::STANDARD.decode(&bytes[..12], discriminator_buf.as_mut().as_out()).ok()?;
401 decoded.len()
402 };
403 if decoded_len < 8 {
404 return None;
405 }
406
407 Some(unsafe { (discriminator_buf.as_ptr() as *const u64).read_unaligned() })
408}
409
410#[inline(always)]
411fn filter_includes_known_program(program_id: &Pubkey, filter: &EventTypeFilter) -> bool {
412 match *program_id {
413 program_ids::PUMPFUN_PROGRAM_ID => filter.includes_pumpfun(),
414 program_ids::PUMP_FEES_PROGRAM_ID => filter.includes_pump_fees(),
415 program_ids::PUMPSWAP_PROGRAM_ID => filter.includes_pumpswap(),
416 program_ids::BONK_PROGRAM_ID => filter.includes_raydium_launchpad(),
417 program_ids::RAYDIUM_CLMM_PROGRAM_ID => filter.includes_raydium_clmm(),
418 program_ids::RAYDIUM_CPMM_PROGRAM_ID => filter.includes_raydium_cpmm(),
419 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => filter.includes_raydium_amm_v4(),
420 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => filter.includes_orca_whirlpool(),
421 program_ids::METEORA_POOLS_PROGRAM_ID => filter.includes_meteora_pools(),
422 program_ids::METEORA_DAMM_V2_PROGRAM_ID => filter.includes_meteora_damm_v2(),
423 program_ids::METEORA_DLMM_PROGRAM_ID => filter.includes_meteora_dlmm(),
424 _ => true,
425 }
426}
427
428#[inline(always)]
429fn filter_wants_supported_logs(filter: &EventTypeFilter) -> bool {
430 filter.includes_pumpfun()
431 || filter.includes_pump_fees()
432 || filter.includes_pumpswap()
433 || filter.includes_raydium_launchpad()
434 || filter.includes_raydium_clmm()
435 || filter.includes_raydium_cpmm()
436 || filter.includes_raydium_amm_v4()
437 || filter.includes_orca_whirlpool()
438 || filter.includes_meteora_pools()
439 || filter.includes_meteora_damm_v2()
440 || filter.includes_meteora_dlmm()
441}
442
443#[inline(always)]
444fn unscoped_filter_allows_discriminator(discriminator: u64, filter: &EventTypeFilter) -> bool {
445 match discriminator {
446 discriminators::PUMPFUN_TRADE => {
448 filter.should_include(EventType::PumpFunTrade)
449 || filter.should_include(EventType::BonkTrade)
450 }
451 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
453 filter.should_include(EventType::RaydiumCpmmSwap)
454 || filter.should_include(EventType::MeteoraDlmmSwap)
455 }
456 _ => discriminator_to_event_type(discriminator)
457 .map(|event_type| filter.should_include(event_type))
458 .unwrap_or_else(|| filter_wants_supported_logs(filter)),
459 }
460}
461
462#[inline(always)]
463fn filter_allows_discriminator(
464 program_id: Option<&Pubkey>,
465 discriminator: u64,
466 event_type_filter: Option<&EventTypeFilter>,
467) -> bool {
468 let Some(filter) = event_type_filter else {
469 return true;
470 };
471
472 if let Some(program_id) = program_id {
473 if let Some(event_type) =
474 program_scoped_discriminator_to_event_type(program_id, discriminator)
475 {
476 return filter.should_include(event_type);
477 }
478 return filter_includes_known_program(program_id, filter);
479 }
480
481 unscoped_filter_allows_discriminator(discriminator, filter)
482}
483
484#[inline(always)]
485fn apply_event_type_filter(
486 event: DexEvent,
487 event_type_filter: Option<&EventTypeFilter>,
488) -> Option<DexEvent> {
489 if let Some(filter) = event_type_filter {
490 if !filter.should_include_dex_event(&event) {
491 return None;
492 }
493 }
494 Some(event)
495}
496
497#[inline(always)]
498fn parse_log_optimized_inner(
499 log: &str,
500 signature: Signature,
501 slot: u64,
502 tx_index: u64,
503 block_time_us: Option<i64>,
504 grpc_recv_us: i64,
505 event_type_filter: Option<&EventTypeFilter>,
506 is_created_buy: bool,
507 recent_blockhash: Option<&[u8]>,
508 program_id: Option<&Pubkey>,
509) -> Option<DexEvent> {
510 let log_bytes = log.as_bytes();
512 let pos = PROGRAM_DATA_FINDER.find(log_bytes)?;
513 let data_start = pos + 14; if log_bytes.len() <= data_start {
516 return None;
517 }
518
519 const STACK_DECODE_CAP: usize = 2048;
522 let data_part = &log[data_start..];
523 let trimmed = data_part.trim();
524
525 let discriminator = decode_base64_discriminator(trimmed)?;
528 if !filter_allows_discriminator(program_id, discriminator, event_type_filter) {
529 return None;
530 }
531
532 use base64_simd::AsOut;
534 let max_decoded_len = (trimmed.len() / 4).saturating_mul(3).saturating_add(3);
535 let mut stack_buf = [0u8; STACK_DECODE_CAP];
536 let heap_buf: Vec<u8>;
537 let program_data: &[u8] = if max_decoded_len <= STACK_DECODE_CAP {
538 let decoded_len = {
539 let decoded_slice = base64_simd::STANDARD
540 .decode(trimmed.as_bytes(), stack_buf.as_mut().as_out())
541 .ok()?;
542 decoded_slice.len()
543 };
544 &stack_buf[..decoded_len]
545 } else {
546 heap_buf = base64_simd::STANDARD.decode_to_vec(trimmed.as_bytes()).ok()?;
547 heap_buf.as_slice()
548 };
549
550 if program_data.len() < 8 {
551 return None;
552 }
553
554 debug_assert_eq!(discriminator, unsafe {
555 (program_data.as_ptr() as *const u64).read_unaligned()
556 });
557
558 let data = &program_data[8..]; use crate::core::events::*;
562
563 let metadata = EventMetadata {
564 signature,
565 slot,
566 tx_index,
567 block_time_us: block_time_us.unwrap_or(0),
568 grpc_recv_us,
569 recent_blockhash: recent_blockhash.map(|s| bs58::encode(s).into_string()),
570 };
571
572 if let Some(program_id) = program_id {
573 return parse_program_scoped_event(
574 program_id,
575 discriminator,
576 data,
577 metadata,
578 log,
579 signature,
580 slot,
581 tx_index,
582 block_time_us,
583 grpc_recv_us,
584 event_type_filter,
585 is_created_buy,
586 );
587 }
588
589 if likely(discriminator == discriminators::PUMPFUN_TRADE) {
597 let event = crate::logs::pump::parse_trade_from_data(data, metadata, is_created_buy)?;
599 return apply_event_type_filter(event, event_type_filter);
600 }
601
602 if likely(discriminator == discriminators::RAYDIUM_CLMM_SWAP) {
603 return apply_event_type_filter(
605 crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)?,
606 event_type_filter,
607 );
608 }
609
610 if likely(discriminator == discriminators::RAYDIUM_AMM_SWAP_BASE_IN) {
611 return apply_event_type_filter(
613 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)?,
614 event_type_filter,
615 );
616 }
617
618 if likely(discriminator == discriminators::PUMPSWAP_BUY) {
619 return apply_event_type_filter(
621 crate::logs::pump_amm::parse_buy_from_data(data, metadata)?,
622 event_type_filter,
623 );
624 }
625
626 if discriminator == discriminators::PUMPSWAP_SELL {
627 return apply_event_type_filter(
629 crate::logs::pump_amm::parse_sell_from_data(data, metadata)?,
630 event_type_filter,
631 );
632 }
633
634 let event = match discriminator {
639 discriminators::PUMPFUN_CREATE => crate::logs::pump::parse_create_from_data(data, metadata),
644 discriminators::PUMPFUN_MIGRATE => {
645 crate::logs::pump::parse_migrate_from_data(data, metadata)
646 }
647 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
648 crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
649 }
650 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
651 crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
652 }
653 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
654 crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
655 }
656 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
657 crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(data, metadata)
658 }
659 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
660 crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(data, metadata)
661 }
662 discriminators::PUMP_FEES_UPDATE_ADMIN => {
663 crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
664 }
665 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => {
666 crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
667 }
668 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => {
669 crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
670 }
671 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => {
672 crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
673 }
674 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
675 crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(data, metadata)
676 }
677 discriminators::PUMPSWAP_CREATE_POOL => {
678 crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
679 }
680 discriminators::PUMPSWAP_ADD_LIQUIDITY => {
681 crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
682 }
683 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => {
684 crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
685 }
686
687 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
690 crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
691 }
692 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
693 crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
694 }
695 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
696 crate::logs::raydium_clmm::parse_liquidity_change_from_data(data, metadata)
697 }
698 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => {
699 crate::logs::raydium_clmm::parse_config_change_from_data(data, metadata)
700 }
701 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
702 crate::logs::raydium_clmm::parse_create_personal_position_from_data(data, metadata)
703 }
704 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
705 crate::logs::raydium_clmm::parse_liquidity_calculate_from_data(data, metadata)
706 }
707 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
708 crate::logs::raydium_clmm::parse_open_limit_order_from_data(data, metadata)
709 }
710 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
711 crate::logs::raydium_clmm::parse_increase_limit_order_from_data(data, metadata)
712 }
713 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
714 crate::logs::raydium_clmm::parse_decrease_limit_order_from_data(data, metadata)
715 }
716 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
717 crate::logs::raydium_clmm::parse_settle_limit_order_from_data(data, metadata)
718 }
719 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
720 crate::logs::raydium_clmm::parse_update_reward_infos_from_data(data, metadata)
721 }
722 discriminators::RAYDIUM_CLMM_CREATE_POOL => {
723 crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
724 }
725 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE => {
726 crate::logs::raydium_clmm::parse_collect_personal_fee_from_data(data, metadata)
727 }
728 discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
729 crate::logs::raydium_clmm::parse_collect_protocol_fee_from_data(data, metadata)
730 }
731
732 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
734 crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
735 }
736 discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
737 crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
738 }
739 discriminators::RAYDIUM_CPMM_DEPOSIT => {
742 crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
743 }
744 discriminators::RAYDIUM_CPMM_WITHDRAW => {
745 crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
746 }
747
748 discriminators::RAYDIUM_AMM_SWAP_BASE_IN => {
750 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
751 }
752 discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
753 crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
754 }
755 discriminators::RAYDIUM_AMM_DEPOSIT => {
756 crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
757 }
758 discriminators::RAYDIUM_AMM_WITHDRAW => {
759 crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
760 }
761 discriminators::RAYDIUM_AMM_INITIALIZE2 => {
762 crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
763 }
764 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => {
765 crate::logs::raydium_amm::parse_withdraw_pnl_from_data(data, metadata)
766 }
767
768 discriminators::ORCA_TRADED => {
770 crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
771 }
772 discriminators::ORCA_LIQUIDITY_INCREASED => {
773 crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
774 }
775 discriminators::ORCA_LIQUIDITY_DECREASED => {
776 crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
777 }
778 discriminators::ORCA_POOL_INITIALIZED => {
779 crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
780 }
781
782 discriminators::METEORA_AMM_SWAP => {
784 crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
785 }
786 discriminators::METEORA_AMM_ADD_LIQUIDITY => {
787 crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
788 }
789 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
790 crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
791 }
792 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
793 crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
794 }
795 discriminators::METEORA_AMM_POOL_CREATED => {
796 crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
797 }
798 discriminators::METEORA_AMM_SET_POOL_FEES => {
799 crate::logs::meteora_amm::parse_set_pool_fees_from_data(data, metadata)
800 }
801
802 discriminators::METEORA_DAMM_SWAP => {
804 crate::logs::meteora_damm::parse_swap_from_data(data, metadata)
805 }
806 discriminators::METEORA_DAMM_SWAP2 => {
807 crate::logs::meteora_damm::parse_swap2_from_data(data, metadata)
808 }
809 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
810 crate::logs::meteora_damm::parse_add_liquidity_from_data(data, metadata)
811 }
812 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
813 crate::logs::meteora_damm::parse_remove_liquidity_from_data(data, metadata)
814 }
815 discriminators::METEORA_DAMM_CREATE_POSITION => {
816 crate::logs::meteora_damm::parse_create_position_from_data(data, metadata)
817 }
818 discriminators::METEORA_DAMM_CLOSE_POSITION => {
819 crate::logs::meteora_damm::parse_close_position_from_data(data, metadata)
820 }
821
822 _ => {
828 if let Some(event) = crate::logs::parse_meteora_dlmm_log(
830 log,
831 signature,
832 slot,
833 tx_index,
834 block_time_us,
835 grpc_recv_us,
836 ) {
837 return apply_event_type_filter(event, event_type_filter);
838 }
839 None
840 }
841 }?;
842 apply_event_type_filter(event, event_type_filter)
843}
844
845#[inline(always)]
846fn program_scoped_discriminator_to_event_type(
847 program_id: &Pubkey,
848 discriminator: u64,
849) -> Option<EventType> {
850 match *program_id {
851 program_ids::PUMPFUN_PROGRAM_ID => match discriminator {
852 discriminators::PUMPFUN_CREATE => Some(EventType::PumpFunCreate),
853 discriminators::PUMPFUN_TRADE => Some(EventType::PumpFunTrade),
854 discriminators::PUMPFUN_MIGRATE => Some(EventType::PumpFunMigrate),
855 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
856 Some(EventType::PumpFunMigrateBondingCurveCreator)
857 }
858 _ => None,
859 },
860 program_ids::PUMP_FEES_PROGRAM_ID => match discriminator {
861 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
862 Some(EventType::PumpFeesCreateFeeSharingConfig)
863 }
864 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
865 Some(EventType::PumpFeesInitializeFeeConfig)
866 }
867 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
868 Some(EventType::PumpFeesResetFeeSharingConfig)
869 }
870 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
871 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
872 }
873 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
874 Some(EventType::PumpFeesTransferFeeSharingAuthority)
875 }
876 discriminators::PUMP_FEES_UPDATE_ADMIN => Some(EventType::PumpFeesUpdateAdmin),
877 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => Some(EventType::PumpFeesUpdateFeeConfig),
878 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => Some(EventType::PumpFeesUpdateFeeShares),
879 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => Some(EventType::PumpFeesUpsertFeeTiers),
880 _ => None,
881 },
882 program_ids::PUMPSWAP_PROGRAM_ID => match discriminator {
883 discriminators::PUMPSWAP_BUY => Some(EventType::PumpSwapBuy),
884 discriminators::PUMPSWAP_SELL => Some(EventType::PumpSwapSell),
885 discriminators::PUMPSWAP_CREATE_POOL => Some(EventType::PumpSwapCreatePool),
886 discriminators::PUMPSWAP_ADD_LIQUIDITY => Some(EventType::PumpSwapLiquidityAdded),
887 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => Some(EventType::PumpSwapLiquidityRemoved),
888 _ => None,
889 },
890 program_ids::BONK_PROGRAM_ID => match discriminator {
891 discriminators::RAYDIUM_LAUNCHPAD_TRADE => Some(EventType::BonkTrade),
892 discriminators::RAYDIUM_LAUNCHPAD_POOL_CREATE => Some(EventType::BonkPoolCreate),
893 _ => None,
894 },
895 program_ids::RAYDIUM_CLMM_PROGRAM_ID => match discriminator {
896 discriminators::RAYDIUM_CLMM_SWAP => Some(EventType::RaydiumClmmSwap),
897 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
898 Some(EventType::RaydiumClmmIncreaseLiquidity)
899 }
900 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
901 Some(EventType::RaydiumClmmDecreaseLiquidity)
902 }
903 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
904 Some(EventType::RaydiumClmmLiquidityChange)
905 }
906 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => Some(EventType::RaydiumClmmConfigChange),
907 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
908 Some(EventType::RaydiumClmmCreatePersonalPosition)
909 }
910 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
911 Some(EventType::RaydiumClmmLiquidityCalculate)
912 }
913 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
914 Some(EventType::RaydiumClmmOpenLimitOrder)
915 }
916 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
917 Some(EventType::RaydiumClmmIncreaseLimitOrder)
918 }
919 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
920 Some(EventType::RaydiumClmmDecreaseLimitOrder)
921 }
922 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
923 Some(EventType::RaydiumClmmSettleLimitOrder)
924 }
925 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
926 Some(EventType::RaydiumClmmUpdateRewardInfos)
927 }
928 discriminators::RAYDIUM_CLMM_CREATE_POOL => Some(EventType::RaydiumClmmCreatePool),
929 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE
930 | discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
931 Some(EventType::RaydiumClmmCollectFee)
932 }
933 _ => None,
934 },
935 program_ids::RAYDIUM_CPMM_PROGRAM_ID => match discriminator {
936 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN
937 | discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => Some(EventType::RaydiumCpmmSwap),
938 discriminators::RAYDIUM_CPMM_CREATE_POOL => Some(EventType::RaydiumCpmmInitialize),
939 discriminators::RAYDIUM_CPMM_DEPOSIT => Some(EventType::RaydiumCpmmDeposit),
940 discriminators::RAYDIUM_CPMM_WITHDRAW => Some(EventType::RaydiumCpmmWithdraw),
941 _ => None,
942 },
943 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => match discriminator {
944 discriminators::RAYDIUM_AMM_SWAP_BASE_IN
945 | discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => Some(EventType::RaydiumAmmV4Swap),
946 discriminators::RAYDIUM_AMM_DEPOSIT => Some(EventType::RaydiumAmmV4Deposit),
947 discriminators::RAYDIUM_AMM_WITHDRAW => Some(EventType::RaydiumAmmV4Withdraw),
948 discriminators::RAYDIUM_AMM_INITIALIZE2 => Some(EventType::RaydiumAmmV4Initialize2),
949 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => Some(EventType::RaydiumAmmV4WithdrawPnl),
950 _ => None,
951 },
952 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => match discriminator {
953 discriminators::ORCA_TRADED => Some(EventType::OrcaWhirlpoolSwap),
954 discriminators::ORCA_LIQUIDITY_INCREASED => {
955 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
956 }
957 discriminators::ORCA_LIQUIDITY_DECREASED => {
958 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
959 }
960 discriminators::ORCA_POOL_INITIALIZED => Some(EventType::OrcaWhirlpoolPoolInitialized),
961 _ => None,
962 },
963 program_ids::METEORA_POOLS_PROGRAM_ID => match discriminator {
964 discriminators::METEORA_AMM_SWAP => Some(EventType::MeteoraPoolsSwap),
965 discriminators::METEORA_AMM_ADD_LIQUIDITY => Some(EventType::MeteoraPoolsAddLiquidity),
966 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
967 Some(EventType::MeteoraPoolsRemoveLiquidity)
968 }
969 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
970 Some(EventType::MeteoraPoolsBootstrapLiquidity)
971 }
972 discriminators::METEORA_AMM_POOL_CREATED => Some(EventType::MeteoraPoolsPoolCreated),
973 discriminators::METEORA_AMM_SET_POOL_FEES => Some(EventType::MeteoraPoolsSetPoolFees),
974 _ => None,
975 },
976 program_ids::METEORA_DAMM_V2_PROGRAM_ID => match discriminator {
977 discriminators::METEORA_DAMM_SWAP | discriminators::METEORA_DAMM_SWAP2 => {
978 Some(EventType::MeteoraDammV2Swap)
979 }
980 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
981 Some(EventType::MeteoraDammV2AddLiquidity)
982 }
983 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
984 Some(EventType::MeteoraDammV2RemoveLiquidity)
985 }
986 discriminators::METEORA_DAMM_CREATE_POSITION => {
987 Some(EventType::MeteoraDammV2CreatePosition)
988 }
989 discriminators::METEORA_DAMM_CLOSE_POSITION => {
990 Some(EventType::MeteoraDammV2ClosePosition)
991 }
992 _ => None,
993 },
994 program_ids::METEORA_DLMM_PROGRAM_ID => match discriminator {
995 discriminators::METEORA_DLMM_SWAP => Some(EventType::MeteoraDlmmSwap),
996 discriminators::METEORA_DLMM_ADD_LIQUIDITY => Some(EventType::MeteoraDlmmAddLiquidity),
997 discriminators::METEORA_DLMM_REMOVE_LIQUIDITY => {
998 Some(EventType::MeteoraDlmmRemoveLiquidity)
999 }
1000 discriminators::METEORA_DLMM_INITIALIZE_POOL => {
1001 Some(EventType::MeteoraDlmmInitializePool)
1002 }
1003 discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY => {
1004 Some(EventType::MeteoraDlmmInitializeBinArray)
1005 }
1006 discriminators::METEORA_DLMM_CREATE_POSITION => {
1007 Some(EventType::MeteoraDlmmCreatePosition)
1008 }
1009 discriminators::METEORA_DLMM_CLOSE_POSITION => {
1010 Some(EventType::MeteoraDlmmClosePosition)
1011 }
1012 discriminators::METEORA_DLMM_CLAIM_FEE => Some(EventType::MeteoraDlmmClaimFee),
1013 _ => None,
1014 },
1015 _ => None,
1016 }
1017}
1018
1019#[inline(always)]
1020fn parse_program_scoped_event(
1021 program_id: &Pubkey,
1022 discriminator: u64,
1023 data: &[u8],
1024 metadata: EventMetadata,
1025 log: &str,
1026 signature: Signature,
1027 slot: u64,
1028 tx_index: u64,
1029 block_time_us: Option<i64>,
1030 grpc_recv_us: i64,
1031 event_type_filter: Option<&EventTypeFilter>,
1032 is_created_buy: bool,
1033) -> Option<DexEvent> {
1034 if let Some(filter) = event_type_filter {
1035 if let Some(event_type) =
1036 program_scoped_discriminator_to_event_type(program_id, discriminator)
1037 {
1038 if !filter.should_include(event_type) {
1039 return None;
1040 }
1041 }
1042 }
1043
1044 match *program_id {
1045 program_ids::PUMPFUN_PROGRAM_ID => {
1046 if let Some(filter) = event_type_filter {
1047 if !filter.includes_pumpfun() {
1048 return None;
1049 }
1050 }
1051 match discriminator {
1052 discriminators::PUMPFUN_TRADE => {
1053 let event =
1054 crate::logs::pump::parse_trade_from_data(data, metadata, is_created_buy)?;
1055 filter_pumpfun_trade_variant(event, event_type_filter)
1056 }
1057 discriminators::PUMPFUN_CREATE => {
1058 crate::logs::pump::parse_create_from_data(data, metadata)
1059 }
1060 discriminators::PUMPFUN_MIGRATE => {
1061 crate::logs::pump::parse_migrate_from_data(data, metadata)
1062 }
1063 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
1064 crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(data, metadata)
1065 }
1066 _ => None,
1067 }
1068 }
1069 program_ids::PUMP_FEES_PROGRAM_ID => {
1070 if let Some(filter) = event_type_filter {
1071 if !filter.includes_pump_fees() {
1072 return None;
1073 }
1074 }
1075 match discriminator {
1076 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
1077 crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(
1078 data, metadata,
1079 )
1080 }
1081 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
1082 crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
1083 }
1084 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
1085 crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
1086 }
1087 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
1088 crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(
1089 data, metadata,
1090 )
1091 }
1092 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
1093 crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(
1094 data, metadata,
1095 )
1096 }
1097 discriminators::PUMP_FEES_UPDATE_ADMIN => {
1098 crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
1099 }
1100 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => {
1101 crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
1102 }
1103 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => {
1104 crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
1105 }
1106 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => {
1107 crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
1108 }
1109 _ => None,
1110 }
1111 }
1112 program_ids::PUMPSWAP_PROGRAM_ID => {
1113 if let Some(filter) = event_type_filter {
1114 if !filter.includes_pumpswap() {
1115 return None;
1116 }
1117 }
1118 match discriminator {
1119 discriminators::PUMPSWAP_BUY => {
1120 crate::logs::pump_amm::parse_buy_from_data(data, metadata)
1121 }
1122 discriminators::PUMPSWAP_SELL => {
1123 crate::logs::pump_amm::parse_sell_from_data(data, metadata)
1124 }
1125 discriminators::PUMPSWAP_CREATE_POOL => {
1126 crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
1127 }
1128 discriminators::PUMPSWAP_ADD_LIQUIDITY => {
1129 crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
1130 }
1131 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => {
1132 crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
1133 }
1134 _ => None,
1135 }
1136 }
1137 program_ids::BONK_PROGRAM_ID => {
1138 if let Some(filter) = event_type_filter {
1139 if !filter.includes_raydium_launchpad() {
1140 return None;
1141 }
1142 }
1143 match discriminator {
1144 discriminators::RAYDIUM_LAUNCHPAD_TRADE => {
1145 crate::logs::raydium_launchpad::parse_trade_from_data(data, metadata)
1146 }
1147 discriminators::RAYDIUM_LAUNCHPAD_POOL_CREATE => {
1148 crate::logs::raydium_launchpad::parse_pool_create_from_data(data, metadata)
1149 }
1150 _ => None,
1151 }
1152 }
1153 program_ids::RAYDIUM_CLMM_PROGRAM_ID => {
1154 if let Some(filter) = event_type_filter {
1155 if !filter.includes_raydium_clmm() {
1156 return None;
1157 }
1158 }
1159 match discriminator {
1160 discriminators::RAYDIUM_CLMM_SWAP => {
1161 crate::logs::raydium_clmm::parse_swap_from_data(data, metadata)
1162 }
1163 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
1164 crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
1165 }
1166 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
1167 crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
1168 }
1169 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
1170 crate::logs::raydium_clmm::parse_liquidity_change_from_data(data, metadata)
1171 }
1172 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => {
1173 crate::logs::raydium_clmm::parse_config_change_from_data(data, metadata)
1174 }
1175 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
1176 crate::logs::raydium_clmm::parse_create_personal_position_from_data(
1177 data, metadata,
1178 )
1179 }
1180 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
1181 crate::logs::raydium_clmm::parse_liquidity_calculate_from_data(data, metadata)
1182 }
1183 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => {
1184 crate::logs::raydium_clmm::parse_open_limit_order_from_data(data, metadata)
1185 }
1186 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
1187 crate::logs::raydium_clmm::parse_increase_limit_order_from_data(data, metadata)
1188 }
1189 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
1190 crate::logs::raydium_clmm::parse_decrease_limit_order_from_data(data, metadata)
1191 }
1192 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
1193 crate::logs::raydium_clmm::parse_settle_limit_order_from_data(data, metadata)
1194 }
1195 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
1196 crate::logs::raydium_clmm::parse_update_reward_infos_from_data(data, metadata)
1197 }
1198 discriminators::RAYDIUM_CLMM_CREATE_POOL => {
1199 crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
1200 }
1201 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE => {
1202 crate::logs::raydium_clmm::parse_collect_personal_fee_from_data(data, metadata)
1203 }
1204 discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
1205 crate::logs::raydium_clmm::parse_collect_protocol_fee_from_data(data, metadata)
1206 }
1207 _ => None,
1208 }
1209 }
1210 program_ids::RAYDIUM_CPMM_PROGRAM_ID => {
1211 if let Some(filter) = event_type_filter {
1212 if !filter.includes_raydium_cpmm() {
1213 return None;
1214 }
1215 }
1216 match discriminator {
1217 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
1218 crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
1219 }
1220 discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
1221 crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
1222 }
1223 discriminators::RAYDIUM_CPMM_CREATE_POOL => {
1224 crate::logs::raydium_cpmm::parse_create_pool_from_data(data, metadata)
1225 }
1226 discriminators::RAYDIUM_CPMM_DEPOSIT => {
1227 crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
1228 }
1229 discriminators::RAYDIUM_CPMM_WITHDRAW => {
1230 crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
1231 }
1232 _ => None,
1233 }
1234 }
1235 program_ids::RAYDIUM_AMM_V4_PROGRAM_ID => {
1236 if let Some(filter) = event_type_filter {
1237 if !filter.includes_raydium_amm_v4() {
1238 return None;
1239 }
1240 }
1241 match discriminator {
1242 discriminators::RAYDIUM_AMM_SWAP_BASE_IN => {
1243 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
1244 }
1245 discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
1246 crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
1247 }
1248 discriminators::RAYDIUM_AMM_DEPOSIT => {
1249 crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
1250 }
1251 discriminators::RAYDIUM_AMM_WITHDRAW => {
1252 crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
1253 }
1254 discriminators::RAYDIUM_AMM_INITIALIZE2 => {
1255 crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
1256 }
1257 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => {
1258 crate::logs::raydium_amm::parse_withdraw_pnl_from_data(data, metadata)
1259 }
1260 _ => None,
1261 }
1262 }
1263 program_ids::ORCA_WHIRLPOOL_PROGRAM_ID => {
1264 if let Some(filter) = event_type_filter {
1265 if !filter.includes_orca_whirlpool() {
1266 return None;
1267 }
1268 }
1269 match discriminator {
1270 discriminators::ORCA_TRADED => {
1271 crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
1272 }
1273 discriminators::ORCA_LIQUIDITY_INCREASED => {
1274 crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
1275 }
1276 discriminators::ORCA_LIQUIDITY_DECREASED => {
1277 crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
1278 }
1279 discriminators::ORCA_POOL_INITIALIZED => {
1280 crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
1281 }
1282 _ => None,
1283 }
1284 }
1285 program_ids::METEORA_POOLS_PROGRAM_ID => {
1286 if let Some(filter) = event_type_filter {
1287 if !filter.includes_meteora_pools() {
1288 return None;
1289 }
1290 }
1291 match discriminator {
1292 discriminators::METEORA_AMM_SWAP => {
1293 crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
1294 }
1295 discriminators::METEORA_AMM_ADD_LIQUIDITY => {
1296 crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
1297 }
1298 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
1299 crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
1300 }
1301 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
1302 crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
1303 }
1304 discriminators::METEORA_AMM_POOL_CREATED => {
1305 crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
1306 }
1307 discriminators::METEORA_AMM_SET_POOL_FEES => {
1308 crate::logs::meteora_amm::parse_set_pool_fees_from_data(data, metadata)
1309 }
1310 _ => None,
1311 }
1312 }
1313 program_ids::METEORA_DAMM_V2_PROGRAM_ID => {
1314 if let Some(filter) = event_type_filter {
1315 if !filter.includes_meteora_damm_v2() {
1316 return None;
1317 }
1318 }
1319 match discriminator {
1320 discriminators::METEORA_DAMM_SWAP => {
1321 crate::logs::meteora_damm::parse_swap_from_data(data, metadata)
1322 }
1323 discriminators::METEORA_DAMM_SWAP2 => {
1324 crate::logs::meteora_damm::parse_swap2_from_data(data, metadata)
1325 }
1326 discriminators::METEORA_DAMM_ADD_LIQUIDITY => {
1327 crate::logs::meteora_damm::parse_add_liquidity_from_data(data, metadata)
1328 }
1329 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
1330 crate::logs::meteora_damm::parse_remove_liquidity_from_data(data, metadata)
1331 }
1332 discriminators::METEORA_DAMM_CREATE_POSITION => {
1333 crate::logs::meteora_damm::parse_create_position_from_data(data, metadata)
1334 }
1335 discriminators::METEORA_DAMM_CLOSE_POSITION => {
1336 crate::logs::meteora_damm::parse_close_position_from_data(data, metadata)
1337 }
1338 _ => None,
1339 }
1340 }
1341 program_ids::METEORA_DLMM_PROGRAM_ID => {
1342 if let Some(filter) = event_type_filter {
1343 if !filter.includes_meteora_dlmm() {
1344 return None;
1345 }
1346 }
1347 match discriminator {
1348 discriminators::METEORA_DLMM_SWAP => {
1349 crate::logs::meteora_dlmm::parse_swap_from_data(data, metadata)
1350 }
1351 discriminators::METEORA_DLMM_ADD_LIQUIDITY => {
1352 crate::logs::meteora_dlmm::parse_add_liquidity_from_data(data, metadata)
1353 }
1354 discriminators::METEORA_DLMM_REMOVE_LIQUIDITY => {
1355 crate::logs::meteora_dlmm::parse_remove_liquidity_from_data(data, metadata)
1356 }
1357 discriminators::METEORA_DLMM_INITIALIZE_POOL => {
1358 crate::logs::meteora_dlmm::parse_initialize_pool_from_data(data, metadata)
1359 }
1360 discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY => {
1361 crate::logs::meteora_dlmm::parse_initialize_bin_array_from_data(data, metadata)
1362 }
1363 discriminators::METEORA_DLMM_CREATE_POSITION => {
1364 crate::logs::meteora_dlmm::parse_create_position_from_data(data, metadata)
1365 }
1366 discriminators::METEORA_DLMM_CLOSE_POSITION => {
1367 crate::logs::meteora_dlmm::parse_close_position_from_data(data, metadata)
1368 }
1369 discriminators::METEORA_DLMM_CLAIM_FEE => {
1370 crate::logs::meteora_dlmm::parse_claim_fee_from_data(data, metadata)
1371 }
1372 _ => None,
1373 }
1374 }
1375 _ => None,
1376 }
1377}
1378
1379#[inline(always)]
1380fn filter_pumpfun_trade_variant(
1381 event: DexEvent,
1382 event_type_filter: Option<&EventTypeFilter>,
1383) -> Option<DexEvent> {
1384 if let Some(filter) = event_type_filter {
1385 if let Some(ref include_only) = filter.include_only {
1386 let has_specific_filter = !include_only.contains(&EventType::PumpFunTrade)
1387 && include_only.iter().any(|t| {
1388 matches!(
1389 t,
1390 EventType::PumpFunBuy
1391 | EventType::PumpFunSell
1392 | EventType::PumpFunBuyExactSolIn
1393 | EventType::PumpFunCreate
1394 | EventType::PumpFunCreateV2
1395 )
1396 });
1397 if has_specific_filter {
1398 let event_type_matches = match &event {
1399 DexEvent::PumpFunBuy(_) => include_only.contains(&EventType::PumpFunBuy),
1400 DexEvent::PumpFunSell(_) => include_only.contains(&EventType::PumpFunSell),
1401 DexEvent::PumpFunBuyExactSolIn(_) => {
1402 include_only.contains(&EventType::PumpFunBuyExactSolIn)
1403 }
1404 DexEvent::PumpFunTrade(_) => include_only.contains(&EventType::PumpFunTrade),
1405 DexEvent::PumpFunCreate(_) => include_only.contains(&EventType::PumpFunCreate),
1406 DexEvent::PumpFunCreateV2(_) => {
1407 include_only.contains(&EventType::PumpFunCreateV2)
1408 }
1409 _ => false,
1410 };
1411 if !event_type_matches {
1412 return None;
1413 }
1414 }
1415 }
1416 if filter.exclude_types.is_some() && !filter.should_include_dex_event(&event) {
1417 return None;
1418 }
1419 }
1420 Some(event)
1421}
1422
1423#[inline(always)]
1425fn discriminator_to_event_type(discriminator: u64) -> Option<EventType> {
1426 match discriminator {
1427 discriminators::PUMPFUN_CREATE => Some(EventType::PumpFunCreate),
1428 discriminators::PUMPFUN_TRADE => Some(EventType::PumpFunTrade),
1429 discriminators::PUMPFUN_MIGRATE => Some(EventType::PumpFunMigrate),
1430 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
1431 Some(EventType::PumpFeesCreateFeeSharingConfig)
1432 }
1433 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
1434 Some(EventType::PumpFeesInitializeFeeConfig)
1435 }
1436 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
1437 Some(EventType::PumpFeesResetFeeSharingConfig)
1438 }
1439 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
1440 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
1441 }
1442 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
1443 Some(EventType::PumpFeesTransferFeeSharingAuthority)
1444 }
1445 discriminators::PUMP_FEES_UPDATE_ADMIN => Some(EventType::PumpFeesUpdateAdmin),
1446 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => Some(EventType::PumpFeesUpdateFeeConfig),
1447 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => Some(EventType::PumpFeesUpdateFeeShares),
1448 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => Some(EventType::PumpFeesUpsertFeeTiers),
1449 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
1450 Some(EventType::PumpFunMigrateBondingCurveCreator)
1451 }
1452 discriminators::PUMPSWAP_BUY => Some(EventType::PumpSwapBuy),
1453 discriminators::PUMPSWAP_SELL => Some(EventType::PumpSwapSell),
1454 discriminators::PUMPSWAP_CREATE_POOL => Some(EventType::PumpSwapCreatePool),
1455 discriminators::PUMPSWAP_ADD_LIQUIDITY => Some(EventType::PumpSwapLiquidityAdded),
1456 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => Some(EventType::PumpSwapLiquidityRemoved),
1457 discriminators::RAYDIUM_LAUNCHPAD_POOL_CREATE => Some(EventType::BonkPoolCreate),
1458 discriminators::RAYDIUM_CLMM_SWAP => Some(EventType::RaydiumClmmSwap),
1459 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
1460 Some(EventType::RaydiumClmmIncreaseLiquidity)
1461 }
1462 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
1463 Some(EventType::RaydiumClmmDecreaseLiquidity)
1464 }
1465 discriminators::RAYDIUM_CLMM_LIQUIDITY_CHANGE => {
1466 Some(EventType::RaydiumClmmLiquidityChange)
1467 }
1468 discriminators::RAYDIUM_CLMM_CONFIG_CHANGE => Some(EventType::RaydiumClmmConfigChange),
1469 discriminators::RAYDIUM_CLMM_CREATE_PERSONAL_POSITION => {
1470 Some(EventType::RaydiumClmmCreatePersonalPosition)
1471 }
1472 discriminators::RAYDIUM_CLMM_LIQUIDITY_CALCULATE => {
1473 Some(EventType::RaydiumClmmLiquidityCalculate)
1474 }
1475 discriminators::RAYDIUM_CLMM_OPEN_LIMIT_ORDER => Some(EventType::RaydiumClmmOpenLimitOrder),
1476 discriminators::RAYDIUM_CLMM_INCREASE_LIMIT_ORDER => {
1477 Some(EventType::RaydiumClmmIncreaseLimitOrder)
1478 }
1479 discriminators::RAYDIUM_CLMM_DECREASE_LIMIT_ORDER => {
1480 Some(EventType::RaydiumClmmDecreaseLimitOrder)
1481 }
1482 discriminators::RAYDIUM_CLMM_SETTLE_LIMIT_ORDER => {
1483 Some(EventType::RaydiumClmmSettleLimitOrder)
1484 }
1485 discriminators::RAYDIUM_CLMM_UPDATE_REWARD_INFOS => {
1486 Some(EventType::RaydiumClmmUpdateRewardInfos)
1487 }
1488 discriminators::RAYDIUM_CLMM_CREATE_POOL => Some(EventType::RaydiumClmmCreatePool),
1489 discriminators::RAYDIUM_CLMM_COLLECT_PERSONAL_FEE
1490 | discriminators::RAYDIUM_CLMM_COLLECT_PROTOCOL_FEE => {
1491 Some(EventType::RaydiumClmmCollectFee)
1492 }
1493 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN | discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
1494 Some(EventType::RaydiumCpmmSwap)
1495 }
1496 discriminators::RAYDIUM_CPMM_DEPOSIT => Some(EventType::RaydiumCpmmDeposit),
1497 discriminators::RAYDIUM_CPMM_WITHDRAW => Some(EventType::RaydiumCpmmWithdraw),
1498 discriminators::RAYDIUM_AMM_SWAP_BASE_IN | discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
1499 Some(EventType::RaydiumAmmV4Swap)
1500 }
1501 discriminators::RAYDIUM_AMM_DEPOSIT => Some(EventType::RaydiumAmmV4Deposit),
1502 discriminators::RAYDIUM_AMM_WITHDRAW => Some(EventType::RaydiumAmmV4Withdraw),
1503 discriminators::RAYDIUM_AMM_INITIALIZE2 => Some(EventType::RaydiumAmmV4Initialize2),
1504 discriminators::RAYDIUM_AMM_WITHDRAW_PNL => Some(EventType::RaydiumAmmV4WithdrawPnl),
1505 discriminators::ORCA_TRADED => Some(EventType::OrcaWhirlpoolSwap),
1506 discriminators::ORCA_LIQUIDITY_INCREASED => {
1507 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
1508 }
1509 discriminators::ORCA_LIQUIDITY_DECREASED => {
1510 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
1511 }
1512 discriminators::ORCA_POOL_INITIALIZED => Some(EventType::OrcaWhirlpoolPoolInitialized),
1513 discriminators::METEORA_AMM_SWAP => Some(EventType::MeteoraPoolsSwap),
1514 discriminators::METEORA_AMM_ADD_LIQUIDITY => Some(EventType::MeteoraPoolsAddLiquidity),
1515 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
1516 Some(EventType::MeteoraPoolsRemoveLiquidity)
1517 }
1518 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
1519 Some(EventType::MeteoraPoolsBootstrapLiquidity)
1520 }
1521 discriminators::METEORA_AMM_POOL_CREATED => Some(EventType::MeteoraPoolsPoolCreated),
1522 discriminators::METEORA_AMM_SET_POOL_FEES => Some(EventType::MeteoraPoolsSetPoolFees),
1523 discriminators::METEORA_DAMM_SWAP | discriminators::METEORA_DAMM_SWAP2 => {
1524 Some(EventType::MeteoraDammV2Swap)
1525 }
1526 discriminators::METEORA_DAMM_ADD_LIQUIDITY => Some(EventType::MeteoraDammV2AddLiquidity),
1527 discriminators::METEORA_DAMM_REMOVE_LIQUIDITY => {
1528 Some(EventType::MeteoraDammV2RemoveLiquidity)
1529 }
1530 discriminators::METEORA_DAMM_CREATE_POSITION => {
1531 Some(EventType::MeteoraDammV2CreatePosition)
1532 }
1533 discriminators::METEORA_DAMM_CLOSE_POSITION => Some(EventType::MeteoraDammV2ClosePosition),
1534 _ => None,
1535 }
1536}
1537
1538#[inline]
1542pub fn detect_pumpfun_create(logs: &[String]) -> bool {
1543 logs.iter().any(|log| PUMPFUN_CREATE_FINDER.find(log.as_bytes()).is_some())
1544}
1545
1546static INVOKE_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"invoke ["));
1548
1549#[inline]
1552pub fn parse_invoke_info(log: &str) -> Option<(&str, usize)> {
1553 let log_bytes = log.as_bytes();
1554
1555 let invoke_start = INVOKE_FINDER.find(log_bytes)?;
1557 let bracket_start = invoke_start + 8; if bracket_start >= log_bytes.len() {
1561 return None;
1562 }
1563
1564 let mut depth = 0usize;
1566 for &byte in &log_bytes[bracket_start..] {
1567 match byte {
1568 b'0'..=b'9' => {
1569 depth = depth * 10 + (byte - b'0') as usize;
1570 }
1571 b']' => break,
1572 _ => return None, }
1574 }
1575
1576 if invoke_start < 8 {
1578 return None; }
1580
1581 let program_start = 8; let program_end = invoke_start - 1; if program_end <= program_start {
1585 return None;
1586 }
1587
1588 let program_id = std::str::from_utf8(&log_bytes[program_start..program_end]).ok()?;
1589
1590 Some((program_id, depth))
1591}
1592
1593#[inline]
1595pub fn parse_program_complete_info(log: &str) -> Option<&str> {
1596 let rest = log.strip_prefix("Program ")?;
1597 if let Some(pos) = rest.find(" success") {
1598 return Some(&rest[..pos]);
1599 }
1600 if let Some(pos) = rest.find(" failed:") {
1601 return Some(&rest[..pos]);
1602 }
1603 None
1604}
1605
1606#[cfg(test)]
1607mod tests {
1608 use super::*;
1609 use crate::core::events::PumpFunTradeEvent;
1610 use base64::{engine::general_purpose::STANDARD, Engine as _};
1611 use solana_sdk::{pubkey::Pubkey, signature::Signature};
1612
1613 #[test]
1614 fn program_scoped_launchpad_trade_is_not_parsed_as_pumpfun() {
1615 let pool = Pubkey::new_unique();
1616 let mut raw = Vec::new();
1617 raw.extend_from_slice(&discriminators::RAYDIUM_LAUNCHPAD_TRADE.to_le_bytes());
1618 raw.extend_from_slice(pool.as_ref());
1619 for value in 0u64..13 {
1620 raw.extend_from_slice(&(100 + value).to_le_bytes());
1621 }
1622 raw.push(1); raw.push(2); raw.push(1); let log = format!("Program data: {}", STANDARD.encode(raw));
1627 let filter = EventTypeFilter::include_only(vec![EventType::BonkTrade]);
1628 let event = parse_log_optimized_with_program_id(
1629 &log,
1630 Signature::default(),
1631 1,
1632 2,
1633 Some(3),
1634 4,
1635 Some(&filter),
1636 false,
1637 None,
1638 Some(&program_ids::BONK_PROGRAM_ID),
1639 )
1640 .expect("launchpad trade should parse");
1641
1642 match event {
1643 DexEvent::BonkTrade(trade) => {
1644 assert_eq!(trade.pool_state, pool);
1645 assert_eq!(trade.amount_in, 107);
1646 assert_eq!(trade.amount_out, 108);
1647 assert!(!trade.is_buy);
1648 assert!(trade.exact_in);
1649 }
1650 other => panic!("expected BonkTrade, got {other:?}"),
1651 }
1652 }
1653
1654 #[test]
1655 fn program_scoped_dlmm_initialize_bin_array_parses_and_filters() {
1656 let pool = Pubkey::new_unique();
1657 let bin_array = Pubkey::new_unique();
1658 let mut raw = Vec::new();
1659 raw.extend_from_slice(&discriminators::METEORA_DLMM_INITIALIZE_BIN_ARRAY.to_le_bytes());
1660 raw.extend_from_slice(pool.as_ref());
1661 raw.extend_from_slice(bin_array.as_ref());
1662 raw.extend_from_slice(&(-12i64).to_le_bytes());
1663
1664 let log = format!("Program data: {}", STANDARD.encode(raw));
1665 let matching_filter =
1666 EventTypeFilter::include_only(vec![EventType::MeteoraDlmmInitializeBinArray]);
1667 let event = parse_log_optimized_with_program_id(
1668 &log,
1669 Signature::default(),
1670 1,
1671 2,
1672 Some(3),
1673 4,
1674 Some(&matching_filter),
1675 false,
1676 None,
1677 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
1678 )
1679 .expect("DLMM initialize bin array should parse");
1680
1681 match event {
1682 DexEvent::MeteoraDlmmInitializeBinArray(event) => {
1683 assert_eq!(event.pool, pool);
1684 assert_eq!(event.bin_array, bin_array);
1685 assert_eq!(event.index, -12);
1686 }
1687 other => panic!("expected MeteoraDlmmInitializeBinArray, got {other:?}"),
1688 }
1689
1690 let non_matching_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
1691 assert!(parse_log_optimized_with_program_id(
1692 &log,
1693 Signature::default(),
1694 1,
1695 2,
1696 Some(3),
1697 4,
1698 Some(&non_matching_filter),
1699 false,
1700 None,
1701 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
1702 )
1703 .is_none());
1704 }
1705
1706 #[test]
1707 fn pumpfun_trade_filter_remains_generic_when_combined_with_specific_type() {
1708 let filter =
1709 EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
1710 let event = DexEvent::PumpFunSell(PumpFunTradeEvent {
1711 metadata: EventMetadata::default(),
1712 is_buy: false,
1713 ix_name: "sell".to_string(),
1714 ..Default::default()
1715 });
1716
1717 assert!(filter_pumpfun_trade_variant(event, Some(&filter)).is_some());
1718 }
1719
1720 #[test]
1721 fn discriminator_prefix_filter_handles_program_scoped_collisions() {
1722 let dlmm_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
1723 assert!(filter_allows_discriminator(
1724 Some(&program_ids::METEORA_DLMM_PROGRAM_ID),
1725 discriminators::METEORA_DLMM_SWAP,
1726 Some(&dlmm_filter),
1727 ));
1728 assert!(!filter_allows_discriminator(
1729 Some(&program_ids::RAYDIUM_CPMM_PROGRAM_ID),
1730 discriminators::METEORA_DLMM_SWAP,
1731 Some(&dlmm_filter),
1732 ));
1733
1734 let bonk_filter = EventTypeFilter::include_only(vec![EventType::BonkTrade]);
1735 assert!(filter_allows_discriminator(
1736 Some(&program_ids::BONK_PROGRAM_ID),
1737 discriminators::RAYDIUM_LAUNCHPAD_TRADE,
1738 Some(&bonk_filter),
1739 ));
1740 assert!(!filter_allows_discriminator(
1741 Some(&program_ids::PUMPFUN_PROGRAM_ID),
1742 discriminators::PUMPFUN_TRADE,
1743 Some(&bonk_filter),
1744 ));
1745 }
1746
1747 #[test]
1748 fn discriminator_prefix_filter_keeps_unscoped_collision_candidates() {
1749 let dlmm_filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
1750 assert!(filter_allows_discriminator(
1751 None,
1752 discriminators::METEORA_DLMM_SWAP,
1753 Some(&dlmm_filter),
1754 ));
1755
1756 let cpmm_filter = EventTypeFilter::include_only(vec![EventType::RaydiumCpmmInitialize]);
1757 assert!(filter_allows_discriminator(
1758 None,
1759 discriminators::RAYDIUM_CPMM_CREATE_POOL,
1760 Some(&cpmm_filter),
1761 ));
1762 }
1763
1764 #[test]
1765 fn unscoped_collision_does_not_emit_wrong_protocol_event_after_filter() {
1766 let mut raw = Vec::new();
1767 raw.extend_from_slice(&discriminators::METEORA_DLMM_SWAP.to_le_bytes());
1768 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());
1771 raw.extend_from_slice(&2u64.to_le_bytes());
1772 raw.extend_from_slice(&3u64.to_le_bytes());
1773 raw.push(1);
1774
1775 let log = format!("Program data: {}", STANDARD.encode(raw));
1776 let filter = EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]);
1777 assert!(parse_log_optimized(
1778 &log,
1779 Signature::default(),
1780 1,
1781 2,
1782 Some(3),
1783 4,
1784 Some(&filter),
1785 false,
1786 None,
1787 )
1788 .is_none());
1789 }
1790
1791 #[test]
1792 fn unscoped_pumpfun_launchpad_collision_does_not_emit_wrong_protocol_event() {
1793 let mut raw = Vec::new();
1794 raw.extend_from_slice(&discriminators::PUMPFUN_TRADE.to_le_bytes());
1795 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 {
1802 raw.extend_from_slice(&value.to_le_bytes());
1803 }
1804 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));
1812 let filter = EventTypeFilter::include_only(vec![EventType::BonkTrade]);
1813 assert!(parse_log_optimized(
1814 &log,
1815 Signature::default(),
1816 1,
1817 2,
1818 Some(3),
1819 4,
1820 Some(&filter),
1821 false,
1822 None,
1823 )
1824 .is_none());
1825 }
1826
1827 #[test]
1828 fn discriminator_prefix_decode_reads_first_event_bytes() {
1829 let mut raw = Vec::new();
1830 raw.extend_from_slice(&discriminators::PUMP_FEES_UPDATE_ADMIN.to_le_bytes());
1831 raw.extend_from_slice(Pubkey::new_unique().as_ref());
1832 raw.extend_from_slice(Pubkey::new_unique().as_ref());
1833
1834 let encoded = STANDARD.encode(raw);
1835 assert_eq!(
1836 decode_base64_discriminator(&encoded),
1837 Some(discriminators::PUMP_FEES_UPDATE_ADMIN)
1838 );
1839 }
1840
1841 #[test]
1842 fn program_scoped_damm_add_liquidity_parses_from_decoded_data() {
1843 let pool = Pubkey::new_unique();
1844 let position = Pubkey::new_unique();
1845 let owner = Pubkey::new_unique();
1846 let mut raw = Vec::new();
1847 raw.extend_from_slice(&discriminators::METEORA_DAMM_ADD_LIQUIDITY.to_le_bytes());
1848 raw.extend_from_slice(pool.as_ref());
1849 raw.extend_from_slice(position.as_ref());
1850 raw.extend_from_slice(owner.as_ref());
1851 raw.extend_from_slice(&123u128.to_le_bytes());
1852 raw.extend_from_slice(&10u64.to_le_bytes());
1853 raw.extend_from_slice(&20u64.to_le_bytes());
1854 raw.extend_from_slice(&30u64.to_le_bytes());
1855 raw.extend_from_slice(&40u64.to_le_bytes());
1856 raw.extend_from_slice(&50u64.to_le_bytes());
1857 raw.extend_from_slice(&60u64.to_le_bytes());
1858
1859 let log = format!("Program data: {}", STANDARD.encode(raw));
1860 let filter = EventTypeFilter::include_only(vec![EventType::MeteoraDammV2AddLiquidity]);
1861 let event = parse_log_optimized_with_program_id(
1862 &log,
1863 Signature::default(),
1864 1,
1865 2,
1866 Some(3),
1867 4,
1868 Some(&filter),
1869 false,
1870 None,
1871 Some(&program_ids::METEORA_DAMM_V2_PROGRAM_ID),
1872 )
1873 .expect("DAMM V2 add-liquidity should parse");
1874
1875 match event {
1876 DexEvent::MeteoraDammV2AddLiquidity(event) => {
1877 assert_eq!(event.pool, pool);
1878 assert_eq!(event.position, position);
1879 assert_eq!(event.owner, owner);
1880 assert_eq!(event.liquidity_delta, 123);
1881 assert_eq!(event.token_a_amount, 30);
1882 assert_eq!(event.token_b_amount, 40);
1883 assert_eq!(event.total_amount_a, 50);
1884 assert_eq!(event.total_amount_b, 60);
1885 }
1886 other => panic!("expected MeteoraDammV2AddLiquidity, got {other:?}"),
1887 }
1888 }
1889
1890 #[test]
1891 fn large_program_data_uses_heap_fallback_without_dropping_event() {
1892 let mut raw = Vec::new();
1893 raw.extend_from_slice(&discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG.to_le_bytes());
1894 raw.extend_from_slice(&1_777_920_719i64.to_le_bytes());
1895 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());
1901 for i in 0..64u16 {
1902 raw.extend_from_slice(Pubkey::new_unique().as_ref());
1903 raw.extend_from_slice(&i.to_le_bytes());
1904 }
1905 raw.push(1); let encoded = STANDARD.encode(&raw);
1908 assert!(encoded.len() > 2700, "test must exceed the old fixed stack buffer limit");
1909 let log = format!("Program data: {encoded}");
1910
1911 let event = parse_log_optimized_with_program_id(
1912 &log,
1913 Signature::default(),
1914 1,
1915 2,
1916 Some(3),
1917 4,
1918 None,
1919 false,
1920 None,
1921 Some(&program_ids::PUMP_FEES_PROGRAM_ID),
1922 )
1923 .expect("large pump-fees event should parse via heap fallback");
1924
1925 match event {
1926 DexEvent::PumpFeesCreateFeeSharingConfig(event) => {
1927 assert_eq!(event.initial_shareholders.len(), 64);
1928 assert_eq!(event.status, crate::core::events::PumpFeesConfigStatus::Active);
1929 }
1930 other => panic!("expected PumpFeesCreateFeeSharingConfig, got {other:?}"),
1931 }
1932 }
1933
1934 #[test]
1935 fn completion_parser_extracts_program_id() {
1936 assert_eq!(
1937 parse_program_complete_info(
1938 "Program LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj success"
1939 ),
1940 Some("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj")
1941 );
1942 assert_eq!(
1943 parse_program_complete_info(
1944 "Program CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C failed: custom program error: 0x1"
1945 ),
1946 Some("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C")
1947 );
1948 }
1949}