1use super::perf_hints::{likely, unlikely};
11use crate::core::events::{DexEvent, EventMetadata};
12use crate::grpc::types::{EventType, EventTypeFilter};
13use memchr::memmem;
14use once_cell::sync::Lazy;
15use solana_sdk::signature::Signature;
16
17static PUMPFUN_FINDER: Lazy<memmem::Finder> =
19 Lazy::new(|| memmem::Finder::new(b"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"));
20static RAYDIUM_AMM_FINDER: Lazy<memmem::Finder> =
21 Lazy::new(|| memmem::Finder::new(b"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"));
22static RAYDIUM_CLMM_FINDER: Lazy<memmem::Finder> =
23 Lazy::new(|| memmem::Finder::new(b"CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6"));
24static RAYDIUM_CPMM_FINDER: Lazy<memmem::Finder> =
25 Lazy::new(|| memmem::Finder::new(b"CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD"));
26static BONK_FINDER: Lazy<memmem::Finder> =
27 Lazy::new(|| memmem::Finder::new(b"Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b"));
28static PROGRAM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program"));
29static PROGRAM_DATA_FINDER: Lazy<memmem::Finder> =
30 Lazy::new(|| memmem::Finder::new(b"Program data: "));
31static PUMPFUN_CREATE_FINDER: Lazy<memmem::Finder> =
32 Lazy::new(|| memmem::Finder::new(b"Program data: G3KpTd7rY3Y"));
33static WHIRL_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"whirL"));
34static METEORA_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"meteora"));
35static METEORA_LB_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"LB"));
36static METEORA_DLMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"DLMM"));
37static PUMPSWAP_LOWER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"pumpswap"));
38static PUMPSWAP_UPPER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"PumpSwap"));
39
40pub mod program_id_strings {
42 pub const PUMPFUN_INVOKE: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke";
43 pub const PUMPFUN_SUCCESS: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success";
44 pub const PUMPFUN_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
45
46 pub const BONK_INVOKE: &str = "Program Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b invoke";
47 pub const BONK_SUCCESS: &str = "Program Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b success";
48 pub const BONK_ID: &str = "Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b";
49
50 pub const RAYDIUM_CLMM_INVOKE: &str =
51 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6 invoke";
52 pub const RAYDIUM_CLMM_SUCCESS: &str =
53 "Program CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6 success";
54 pub const RAYDIUM_CLMM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6";
55
56 pub const RAYDIUM_CPMM_INVOKE: &str =
57 "Program CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD invoke";
58 pub const RAYDIUM_CPMM_SUCCESS: &str =
59 "Program CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD success";
60 pub const RAYDIUM_CPMM_ID: &str = "CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD";
61
62 pub const RAYDIUM_AMM_V4_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
63
64 pub const PROGRAM_DATA: &str = "Program data: ";
66 pub const PROGRAM_LOG: &str = "Program log: ";
67
68 pub const PUMPFUN_CREATE_DISCRIMINATOR: &str = "GB7IKAUcB3c"; }
71
72#[derive(Debug, Copy, Clone, PartialEq)]
74pub enum LogType {
75 PumpFun,
76 RaydiumLaunchpad,
77 PumpAmm,
78 RaydiumClmm,
79 RaydiumCpmm,
80 RaydiumAmm,
81 OrcaWhirlpool,
82 MeteoraAmm,
83 MeteoraDamm,
84 MeteoraDlmm,
85 Unknown,
86}
87
88#[inline(always)]
90pub fn detect_log_type(log: &str) -> LogType {
91 let log_bytes = log.as_bytes();
92
93 if log_bytes.len() < 20 {
95 return LogType::Unknown;
96 }
97
98 let has_program_data = PROGRAM_DATA_FINDER.find(log_bytes).is_some();
100
101 if unlikely(!has_program_data) {
103 return LogType::Unknown;
104 }
105
106 if likely(RAYDIUM_AMM_FINDER.find(log_bytes).is_some()) {
109 return LogType::RaydiumAmm;
110 }
111
112 if RAYDIUM_CLMM_FINDER.find(log_bytes).is_some() {
114 return LogType::RaydiumClmm;
115 }
116
117 if RAYDIUM_CPMM_FINDER.find(log_bytes).is_some() {
119 return LogType::RaydiumCpmm;
120 }
121
122 if BONK_FINDER.find(log_bytes).is_some() {
124 return LogType::RaydiumLaunchpad;
125 }
126
127 if WHIRL_FINDER.find(log_bytes).is_some() {
129 return LogType::OrcaWhirlpool;
130 }
131
132 if let Some(pos) = METEORA_FINDER.find(log_bytes) {
134 let rest = &log_bytes[pos..];
135 if METEORA_LB_FINDER.find(rest).is_some() {
136 return LogType::MeteoraDamm;
137 } else if METEORA_DLMM_FINDER.find(rest).is_some() {
138 return LogType::MeteoraDlmm;
139 } else {
140 return LogType::MeteoraAmm;
141 }
142 }
143
144 if PUMPSWAP_LOWER_FINDER.find(log_bytes).is_some()
146 || PUMPSWAP_UPPER_FINDER.find(log_bytes).is_some()
147 {
148 return LogType::PumpAmm;
149 }
150
151 if likely(PUMPFUN_FINDER.find(log_bytes).is_some()) {
154 return LogType::PumpFun;
155 }
156
157 if log.len() > 30 {
161 return LogType::PumpFun;
162 }
163
164 LogType::Unknown
165}
166
167mod discriminators {
171 pub const PUMPFUN_CREATE: u64 = u64::from_le_bytes([27, 114, 169, 77, 222, 235, 99, 118]);
173 pub const PUMPFUN_TRADE: u64 = u64::from_le_bytes([189, 219, 127, 211, 78, 230, 97, 238]);
174 pub const PUMPFUN_MIGRATE: u64 = u64::from_le_bytes([189, 233, 93, 185, 92, 148, 234, 148]);
175 pub const PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR: u64 =
176 u64::from_le_bytes([155, 167, 104, 220, 213, 108, 243, 3]);
177 pub const PUMP_FEES_CREATE_FEE_SHARING_CONFIG: u64 =
179 u64::from_le_bytes([133, 105, 170, 200, 184, 116, 251, 88]);
180 pub const PUMP_FEES_INITIALIZE_FEE_CONFIG: u64 =
181 u64::from_le_bytes([89, 138, 244, 230, 10, 56, 226, 126]);
182 pub const PUMP_FEES_RESET_FEE_SHARING_CONFIG: u64 =
183 u64::from_le_bytes([203, 204, 151, 226, 120, 55, 214, 243]);
184 pub const PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY: u64 =
185 u64::from_le_bytes([114, 23, 101, 60, 14, 190, 153, 62]);
186 pub const PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY: u64 =
187 u64::from_le_bytes([124, 143, 198, 245, 77, 184, 8, 236]);
188 pub const PUMP_FEES_UPDATE_ADMIN: u64 =
189 u64::from_le_bytes([225, 152, 171, 87, 246, 63, 66, 234]);
190 pub const PUMP_FEES_UPDATE_FEE_CONFIG: u64 =
191 u64::from_le_bytes([90, 23, 65, 35, 62, 244, 188, 208]);
192 pub const PUMP_FEES_UPDATE_FEE_SHARES: u64 =
193 u64::from_le_bytes([21, 186, 196, 184, 91, 228, 225, 203]);
194 pub const PUMP_FEES_UPSERT_FEE_TIERS: u64 =
195 u64::from_le_bytes([171, 89, 169, 187, 122, 186, 33, 204]);
196
197 pub const PUMPSWAP_BUY: u64 = u64::from_le_bytes([103, 244, 82, 31, 44, 245, 119, 119]);
199 pub const PUMPSWAP_SELL: u64 = u64::from_le_bytes([62, 47, 55, 10, 165, 3, 220, 42]);
200 pub const PUMPSWAP_CREATE_POOL: u64 =
201 u64::from_le_bytes([177, 49, 12, 210, 160, 118, 167, 116]);
202 pub const PUMPSWAP_ADD_LIQUIDITY: u64 =
203 u64::from_le_bytes([120, 248, 61, 83, 31, 142, 107, 144]);
204 pub const PUMPSWAP_REMOVE_LIQUIDITY: u64 =
205 u64::from_le_bytes([22, 9, 133, 26, 160, 44, 71, 192]);
206
207 pub const RAYDIUM_CLMM_SWAP: u64 = u64::from_le_bytes([248, 198, 158, 145, 225, 117, 135, 200]);
209 pub const RAYDIUM_CLMM_INCREASE_LIQUIDITY: u64 =
210 u64::from_le_bytes([133, 29, 89, 223, 69, 238, 176, 10]);
211 pub const RAYDIUM_CLMM_DECREASE_LIQUIDITY: u64 =
212 u64::from_le_bytes([160, 38, 208, 111, 104, 91, 44, 1]);
213 pub const RAYDIUM_CLMM_CREATE_POOL: u64 =
214 u64::from_le_bytes([233, 146, 209, 142, 207, 104, 64, 188]);
215 pub const RAYDIUM_CLMM_COLLECT_FEE: u64 =
216 u64::from_le_bytes([164, 152, 207, 99, 187, 104, 171, 119]);
217
218 pub const RAYDIUM_CPMM_SWAP_BASE_IN: u64 =
220 u64::from_le_bytes([143, 190, 90, 218, 196, 30, 51, 222]);
221 pub const RAYDIUM_CPMM_SWAP_BASE_OUT: u64 =
222 u64::from_le_bytes([55, 217, 98, 86, 163, 74, 180, 173]);
223 pub const RAYDIUM_CPMM_CREATE_POOL: u64 =
224 u64::from_le_bytes([233, 146, 209, 142, 207, 104, 64, 188]);
225 pub const RAYDIUM_CPMM_DEPOSIT: u64 =
226 u64::from_le_bytes([242, 35, 198, 137, 82, 225, 242, 182]);
227 pub const RAYDIUM_CPMM_WITHDRAW: u64 =
228 u64::from_le_bytes([183, 18, 70, 156, 148, 109, 161, 34]);
229
230 pub const RAYDIUM_AMM_SWAP_BASE_IN: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 9]);
232 pub const RAYDIUM_AMM_SWAP_BASE_OUT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 11]);
233 pub const RAYDIUM_AMM_DEPOSIT: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 3]);
234 pub const RAYDIUM_AMM_WITHDRAW: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 4]);
235 pub const RAYDIUM_AMM_INITIALIZE2: u64 = u64::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 1]);
236
237 pub const ORCA_TRADED: u64 = u64::from_le_bytes([225, 202, 73, 175, 147, 43, 160, 150]);
239 pub const ORCA_LIQUIDITY_INCREASED: u64 =
240 u64::from_le_bytes([30, 7, 144, 181, 102, 254, 155, 161]);
241 pub const ORCA_LIQUIDITY_DECREASED: u64 =
242 u64::from_le_bytes([166, 1, 36, 71, 112, 202, 181, 171]);
243 pub const ORCA_POOL_INITIALIZED: u64 =
244 u64::from_le_bytes([100, 118, 173, 87, 12, 198, 254, 229]);
245
246 pub const METEORA_AMM_SWAP: u64 = u64::from_le_bytes([81, 108, 227, 190, 205, 208, 10, 196]);
248 pub const METEORA_AMM_ADD_LIQUIDITY: u64 =
249 u64::from_le_bytes([31, 94, 125, 90, 227, 52, 61, 186]);
250 pub const METEORA_AMM_REMOVE_LIQUIDITY: u64 =
251 u64::from_le_bytes([116, 244, 97, 232, 103, 31, 152, 58]);
252 pub const METEORA_AMM_BOOTSTRAP_LIQUIDITY: u64 =
253 u64::from_le_bytes([121, 127, 38, 136, 92, 55, 14, 247]);
254 pub const METEORA_AMM_POOL_CREATED: u64 =
255 u64::from_le_bytes([202, 44, 41, 88, 104, 220, 157, 82]);
256
257 pub const METEORA_DAMM_SWAP: u64 = u64::from_le_bytes([27, 60, 21, 213, 138, 170, 187, 147]);
259 pub const METEORA_DAMM_SWAP2: u64 = u64::from_le_bytes([189, 66, 51, 168, 38, 80, 117, 153]);
260 pub const METEORA_DAMM_ADD_LIQUIDITY: u64 =
261 u64::from_le_bytes([175, 242, 8, 157, 30, 247, 185, 169]);
262 pub const METEORA_DAMM_REMOVE_LIQUIDITY: u64 =
263 u64::from_le_bytes([87, 46, 88, 98, 175, 96, 34, 91]);
264 pub const METEORA_DAMM_INITIALIZE_POOL: u64 =
265 u64::from_le_bytes([228, 50, 246, 85, 203, 66, 134, 37]);
266 pub const METEORA_DAMM_CREATE_POSITION: u64 =
267 u64::from_le_bytes([156, 15, 119, 198, 29, 181, 221, 55]);
268 pub const METEORA_DAMM_CLOSE_POSITION: u64 =
269 u64::from_le_bytes([20, 145, 144, 68, 143, 142, 214, 178]);
270
271 pub const METEORA_DLMM_SWAP: u64 = u64::from_le_bytes([143, 190, 90, 218, 196, 30, 51, 222]);
273 pub const METEORA_DLMM_ADD_LIQUIDITY: u64 =
274 u64::from_le_bytes([181, 157, 89, 67, 143, 182, 52, 72]);
275 pub const METEORA_DLMM_REMOVE_LIQUIDITY: u64 =
276 u64::from_le_bytes([80, 85, 209, 72, 24, 206, 35, 178]);
277 pub const METEORA_DLMM_INITIALIZE_POOL: u64 =
278 u64::from_le_bytes([95, 180, 10, 172, 84, 174, 232, 40]);
279 pub const METEORA_DLMM_CREATE_POSITION: u64 =
280 u64::from_le_bytes([123, 233, 11, 43, 146, 180, 97, 119]);
281 pub const METEORA_DLMM_CLOSE_POSITION: u64 =
282 u64::from_le_bytes([94, 168, 102, 45, 59, 122, 137, 54]);
283}
284
285#[inline(always)]
297pub fn parse_log_optimized(
299 log: &str,
300 signature: Signature,
301 slot: u64,
302 tx_index: u64,
303 block_time_us: Option<i64>,
304 grpc_recv_us: i64,
305 event_type_filter: Option<&EventTypeFilter>,
306 is_created_buy: bool,
307 recent_blockhash: Option<&[u8]>,
308) -> Option<DexEvent> {
309 let log_bytes = log.as_bytes();
311 let pos = PROGRAM_DATA_FINDER.find(log_bytes)?;
312 let data_start = pos + 14; if log_bytes.len() <= data_start {
315 return None;
316 }
317
318 let mut buf = [0u8; 2048]; let data_part = &log[data_start..];
321 let trimmed = data_part.trim();
322
323 if trimmed.len() > 2700 {
326 return None;
327 }
328
329 use base64_simd::AsOut;
331 let decoded_slice =
332 base64_simd::STANDARD.decode(trimmed.as_bytes(), buf.as_mut().as_out()).ok()?;
333 let decoded_len = decoded_slice.len();
334
335 if decoded_len < 8 {
336 return None;
337 }
338
339 let program_data = &buf[..decoded_len];
340
341 let discriminator = unsafe {
343 let ptr = program_data.as_ptr() as *const u64;
344 ptr.read_unaligned()
345 };
346
347 let event_type = discriminator_to_event_type(discriminator);
349
350 if let Some(filter) = event_type_filter {
352 if let Some(et) = event_type {
353 if !filter.should_include(et) {
354 return None; }
356 } else {
357 if let Some(ref include_only) = filter.include_only {
359 let wants_supported = include_only.iter().any(|t| {
360 matches!(
361 t,
362 EventType::PumpFunTrade
363 | EventType::PumpFunCreate
364 | EventType::PumpFunMigrate
365 | EventType::PumpFunBuy
366 | EventType::PumpFunSell
367 | EventType::PumpFunBuyExactSolIn
368 | EventType::PumpFunMigrateBondingCurveCreator
369 | EventType::PumpFeesCreateFeeSharingConfig
370 | EventType::PumpFeesInitializeFeeConfig
371 | EventType::PumpFeesResetFeeSharingConfig
372 | EventType::PumpFeesRevokeFeeSharingAuthority
373 | EventType::PumpFeesTransferFeeSharingAuthority
374 | EventType::PumpFeesUpdateAdmin
375 | EventType::PumpFeesUpdateFeeConfig
376 | EventType::PumpFeesUpdateFeeShares
377 | EventType::PumpFeesUpsertFeeTiers
378 | EventType::PumpSwapBuy
379 | EventType::PumpSwapSell
380 | EventType::PumpSwapCreatePool
381 | EventType::PumpSwapLiquidityAdded
382 | EventType::PumpSwapLiquidityRemoved
383 )
384 });
385 if !wants_supported {
386 return None;
387 }
388 }
389 }
390 }
391
392 let data = &program_data[8..]; use crate::core::events::*;
396
397 let metadata = EventMetadata {
398 signature,
399 slot,
400 tx_index,
401 block_time_us: block_time_us.unwrap_or(0),
402 grpc_recv_us,
403 recent_blockhash: recent_blockhash.map(|s| bs58::encode(s).into_string()),
404 };
405
406 if likely(discriminator == discriminators::PUMPFUN_TRADE) {
414 let event = crate::logs::pump::parse_trade_from_data(data, metadata, is_created_buy)?;
416 if let Some(filter) = event_type_filter {
418 if let Some(ref include_only) = filter.include_only {
419 let has_specific_filter = include_only.iter().any(|t| {
420 matches!(
421 t,
422 EventType::PumpFunBuy
423 | EventType::PumpFunSell
424 | EventType::PumpFunBuyExactSolIn
425 | EventType::PumpFunCreate
426 | EventType::PumpFunCreateV2
427 )
428 });
429 if has_specific_filter {
430 let event_type_matches = match &event {
431 DexEvent::PumpFunBuy(_) => include_only.contains(&EventType::PumpFunBuy),
432 DexEvent::PumpFunSell(_) => include_only.contains(&EventType::PumpFunSell),
433 DexEvent::PumpFunBuyExactSolIn(_) => {
434 include_only.contains(&EventType::PumpFunBuyExactSolIn)
435 }
436 DexEvent::PumpFunTrade(_) => {
437 include_only.contains(&EventType::PumpFunTrade)
438 }
439 DexEvent::PumpFunCreate(_) => {
440 include_only.contains(&EventType::PumpFunCreate)
441 }
442 DexEvent::PumpFunCreateV2(_) => {
443 include_only.contains(&EventType::PumpFunCreateV2)
444 }
445 _ => false,
446 };
447 if !event_type_matches {
448 return None;
449 }
450 }
451 }
452 }
453 return Some(event);
454 }
455
456 if likely(discriminator == discriminators::RAYDIUM_CLMM_SWAP) {
457 return crate::logs::raydium_clmm::parse_swap_from_data(data, metadata);
459 }
460
461 if likely(discriminator == discriminators::RAYDIUM_AMM_SWAP_BASE_IN) {
462 return crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata);
464 }
465
466 if likely(discriminator == discriminators::PUMPSWAP_BUY) {
467 return crate::logs::pump_amm::parse_buy_from_data(data, metadata);
469 }
470
471 if discriminator == discriminators::PUMPSWAP_SELL {
472 return crate::logs::pump_amm::parse_sell_from_data(data, metadata);
474 }
475
476 match discriminator {
481 discriminators::PUMPFUN_CREATE => crate::logs::pump::parse_create_from_data(data, metadata),
486 discriminators::PUMPFUN_MIGRATE => {
487 crate::logs::pump::parse_migrate_from_data(data, metadata)
488 },
489 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
490 crate::logs::pump_fees::parse_create_fee_sharing_config_from_data(data, metadata)
491 }
492 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
493 crate::logs::pump_fees::parse_initialize_fee_config_from_data(data, metadata)
494 }
495 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
496 crate::logs::pump_fees::parse_reset_fee_sharing_config_from_data(data, metadata)
497 }
498 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
499 crate::logs::pump_fees::parse_revoke_fee_sharing_authority_from_data(data, metadata)
500 }
501 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
502 crate::logs::pump_fees::parse_transfer_fee_sharing_authority_from_data(data, metadata)
503 }
504 discriminators::PUMP_FEES_UPDATE_ADMIN => {
505 crate::logs::pump_fees::parse_update_admin_from_data(data, metadata)
506 }
507 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => {
508 crate::logs::pump_fees::parse_update_fee_config_from_data(data, metadata)
509 }
510 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => {
511 crate::logs::pump_fees::parse_update_fee_shares_from_data(data, metadata)
512 }
513 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => {
514 crate::logs::pump_fees::parse_upsert_fee_tiers_from_data(data, metadata)
515 },
516 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => crate::logs::pump::parse_migrate_bonding_curve_creator_from_data(
517 data,
518 metadata,
519 ),
520 discriminators::PUMPSWAP_CREATE_POOL => {
521 crate::logs::pump_amm::parse_create_pool_from_data(data, metadata)
522 }
523 discriminators::PUMPSWAP_ADD_LIQUIDITY => {
524 crate::logs::pump_amm::parse_add_liquidity_from_data(data, metadata)
525 }
526 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => {
527 crate::logs::pump_amm::parse_remove_liquidity_from_data(data, metadata)
528 }
529
530 discriminators::RAYDIUM_CLMM_INCREASE_LIQUIDITY => {
533 crate::logs::raydium_clmm::parse_increase_liquidity_from_data(data, metadata)
534 }
535 discriminators::RAYDIUM_CLMM_DECREASE_LIQUIDITY => {
536 crate::logs::raydium_clmm::parse_decrease_liquidity_from_data(data, metadata)
537 }
538 discriminators::RAYDIUM_CLMM_CREATE_POOL => {
539 crate::logs::raydium_clmm::parse_create_pool_from_data(data, metadata)
540 }
541 discriminators::RAYDIUM_CLMM_COLLECT_FEE => {
542 crate::logs::raydium_clmm::parse_collect_fee_from_data(data, metadata)
543 }
544
545 discriminators::RAYDIUM_CPMM_SWAP_BASE_IN => {
547 crate::logs::raydium_cpmm::parse_swap_base_in_from_data(data, metadata)
548 }
549 discriminators::RAYDIUM_CPMM_SWAP_BASE_OUT => {
550 crate::logs::raydium_cpmm::parse_swap_base_out_from_data(data, metadata)
551 }
552 discriminators::RAYDIUM_CPMM_DEPOSIT => {
555 crate::logs::raydium_cpmm::parse_deposit_from_data(data, metadata)
556 }
557 discriminators::RAYDIUM_CPMM_WITHDRAW => {
558 crate::logs::raydium_cpmm::parse_withdraw_from_data(data, metadata)
559 }
560
561 discriminators::RAYDIUM_AMM_SWAP_BASE_IN => {
563 crate::logs::raydium_amm::parse_swap_base_in_from_data(data, metadata)
564 }
565 discriminators::RAYDIUM_AMM_SWAP_BASE_OUT => {
566 crate::logs::raydium_amm::parse_swap_base_out_from_data(data, metadata)
567 }
568 discriminators::RAYDIUM_AMM_DEPOSIT => {
569 crate::logs::raydium_amm::parse_deposit_from_data(data, metadata)
570 }
571 discriminators::RAYDIUM_AMM_WITHDRAW => {
572 crate::logs::raydium_amm::parse_withdraw_from_data(data, metadata)
573 }
574 discriminators::RAYDIUM_AMM_INITIALIZE2 => {
575 crate::logs::raydium_amm::parse_initialize2_from_data(data, metadata)
576 }
577
578 discriminators::ORCA_TRADED => {
580 crate::logs::orca_whirlpool::parse_traded_from_data(data, metadata)
581 }
582 discriminators::ORCA_LIQUIDITY_INCREASED => {
583 crate::logs::orca_whirlpool::parse_liquidity_increased_from_data(data, metadata)
584 }
585 discriminators::ORCA_LIQUIDITY_DECREASED => {
586 crate::logs::orca_whirlpool::parse_liquidity_decreased_from_data(data, metadata)
587 }
588 discriminators::ORCA_POOL_INITIALIZED => {
589 crate::logs::orca_whirlpool::parse_pool_initialized_from_data(data, metadata)
590 }
591
592 discriminators::METEORA_AMM_SWAP => {
594 crate::logs::meteora_amm::parse_swap_from_data(data, metadata)
595 }
596 discriminators::METEORA_AMM_ADD_LIQUIDITY => {
597 crate::logs::meteora_amm::parse_add_liquidity_from_data(data, metadata)
598 }
599 discriminators::METEORA_AMM_REMOVE_LIQUIDITY => {
600 crate::logs::meteora_amm::parse_remove_liquidity_from_data(data, metadata)
601 }
602 discriminators::METEORA_AMM_BOOTSTRAP_LIQUIDITY => {
603 crate::logs::meteora_amm::parse_bootstrap_liquidity_from_data(data, metadata)
604 }
605 discriminators::METEORA_AMM_POOL_CREATED => {
606 crate::logs::meteora_amm::parse_pool_created_from_data(data, metadata)
607 }
608
609 discriminators::METEORA_DAMM_SWAP
611 | discriminators::METEORA_DAMM_SWAP2
612 | discriminators::METEORA_DAMM_ADD_LIQUIDITY
613 | discriminators::METEORA_DAMM_REMOVE_LIQUIDITY
614 | discriminators::METEORA_DAMM_INITIALIZE_POOL
615 | discriminators::METEORA_DAMM_CREATE_POSITION
616 | discriminators::METEORA_DAMM_CLOSE_POSITION => crate::logs::parse_meteora_damm_log(
617 log,
618 signature,
619 slot,
620 tx_index,
621 block_time_us,
622 grpc_recv_us,
623 ),
624
625 _ => {
631 if let Some(event) = crate::logs::parse_meteora_dlmm_log(
633 log,
634 signature,
635 slot,
636 tx_index,
637 block_time_us,
638 grpc_recv_us,
639 ) {
640 return Some(event);
641 }
642 None
643 }
644 }
645}
646
647#[inline(always)]
649fn discriminator_to_event_type(discriminator: u64) -> Option<EventType> {
650 match discriminator {
651 discriminators::PUMPFUN_CREATE => Some(EventType::PumpFunCreate),
652 discriminators::PUMPFUN_TRADE => Some(EventType::PumpFunTrade),
653 discriminators::PUMPFUN_MIGRATE => Some(EventType::PumpFunMigrate),
654 discriminators::PUMP_FEES_CREATE_FEE_SHARING_CONFIG => {
655 Some(EventType::PumpFeesCreateFeeSharingConfig)
656 }
657 discriminators::PUMP_FEES_INITIALIZE_FEE_CONFIG => {
658 Some(EventType::PumpFeesInitializeFeeConfig)
659 }
660 discriminators::PUMP_FEES_RESET_FEE_SHARING_CONFIG => {
661 Some(EventType::PumpFeesResetFeeSharingConfig)
662 }
663 discriminators::PUMP_FEES_REVOKE_FEE_SHARING_AUTHORITY => {
664 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
665 }
666 discriminators::PUMP_FEES_TRANSFER_FEE_SHARING_AUTHORITY => {
667 Some(EventType::PumpFeesTransferFeeSharingAuthority)
668 }
669 discriminators::PUMP_FEES_UPDATE_ADMIN => Some(EventType::PumpFeesUpdateAdmin),
670 discriminators::PUMP_FEES_UPDATE_FEE_CONFIG => Some(EventType::PumpFeesUpdateFeeConfig),
671 discriminators::PUMP_FEES_UPDATE_FEE_SHARES => Some(EventType::PumpFeesUpdateFeeShares),
672 discriminators::PUMP_FEES_UPSERT_FEE_TIERS => Some(EventType::PumpFeesUpsertFeeTiers),
673 discriminators::PUMPFUN_MIGRATE_BONDING_CURVE_CREATOR => {
674 Some(EventType::PumpFunMigrateBondingCurveCreator)
675 },
676 discriminators::PUMPSWAP_BUY => Some(EventType::PumpSwapBuy),
677 discriminators::PUMPSWAP_SELL => Some(EventType::PumpSwapSell),
678 discriminators::PUMPSWAP_CREATE_POOL => Some(EventType::PumpSwapCreatePool),
679 discriminators::PUMPSWAP_ADD_LIQUIDITY => Some(EventType::PumpSwapLiquidityAdded),
680 discriminators::PUMPSWAP_REMOVE_LIQUIDITY => Some(EventType::PumpSwapLiquidityRemoved),
681 _ => None,
682 }
683}
684
685#[inline]
689pub fn detect_pumpfun_create(logs: &[String]) -> bool {
690 logs.iter().any(|log| PUMPFUN_CREATE_FINDER.find(log.as_bytes()).is_some())
691}
692
693static INVOKE_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"invoke ["));
695
696#[inline]
699pub fn parse_invoke_info(log: &str) -> Option<(&str, usize)> {
700 let log_bytes = log.as_bytes();
701
702 let invoke_start = INVOKE_FINDER.find(log_bytes)?;
704 let bracket_start = invoke_start + 8; if bracket_start >= log_bytes.len() {
708 return None;
709 }
710
711 let mut depth = 0usize;
713 for &byte in &log_bytes[bracket_start..] {
714 match byte {
715 b'0'..=b'9' => {
716 depth = depth * 10 + (byte - b'0') as usize;
717 }
718 b']' => break,
719 _ => return None, }
721 }
722
723 if invoke_start < 8 {
725 return None; }
727
728 let program_start = 8; let program_end = invoke_start - 1; if program_end <= program_start {
732 return None;
733 }
734
735 let program_id = std::str::from_utf8(&log_bytes[program_start..program_end]).ok()?;
736
737 Some((program_id, depth))
738}