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