1use serde::{Deserialize, Serialize};
2use yellowstone_grpc_proto::geyser::{
3 subscribe_request_filter_accounts_filter::Filter as AccountsFilterOneof,
4 subscribe_request_filter_accounts_filter_memcmp::Data as MemcmpDataOneof,
5 SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterMemcmp,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10pub enum OrderMode {
11 #[default]
13 Unordered,
14 Ordered,
19 StreamingOrdered,
23 MicroBatch,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ClientConfig {
31 pub enable_metrics: bool,
33 pub connection_timeout_ms: u64,
35 pub request_timeout_ms: u64,
37 pub enable_tls: bool,
39 pub max_retries: u32,
40 pub retry_delay_ms: u64,
43 pub max_concurrent_streams: u32,
44 pub keep_alive_interval_ms: u64,
45 pub keep_alive_timeout_ms: u64,
46 pub buffer_size: usize,
47 pub order_mode: OrderMode,
49 pub order_timeout_ms: u64,
52 pub micro_batch_us: u64,
55}
56
57impl Default for ClientConfig {
58 fn default() -> Self {
59 Self {
60 enable_metrics: false,
61 connection_timeout_ms: 8000,
62 request_timeout_ms: 15000,
63 enable_tls: true,
64 max_retries: 3,
65 retry_delay_ms: 1000,
66 max_concurrent_streams: 100,
67 keep_alive_interval_ms: 30000,
68 keep_alive_timeout_ms: 5000,
69 buffer_size: 100_000,
70 order_mode: OrderMode::Unordered,
71 order_timeout_ms: 100,
72 micro_batch_us: 100, }
74 }
75}
76
77impl ClientConfig {
78 pub fn low_latency() -> Self {
79 Self {
80 enable_metrics: false,
81 connection_timeout_ms: 5000,
82 request_timeout_ms: 10000,
83 enable_tls: true,
84 max_retries: 1,
85 retry_delay_ms: 100,
86 max_concurrent_streams: 200,
87 keep_alive_interval_ms: 10000,
88 keep_alive_timeout_ms: 2000,
89 buffer_size: 100_000,
90 order_mode: OrderMode::Unordered,
91 order_timeout_ms: 50,
92 micro_batch_us: 50, }
94 }
95
96 pub fn high_throughput() -> Self {
97 Self {
98 enable_metrics: true,
99 connection_timeout_ms: 10000,
100 request_timeout_ms: 30000,
101 enable_tls: true,
102 max_retries: 5,
103 retry_delay_ms: 2000,
104 max_concurrent_streams: 500,
105 keep_alive_interval_ms: 60000,
106 keep_alive_timeout_ms: 10000,
107 buffer_size: 200_000,
108 order_mode: OrderMode::Unordered,
109 order_timeout_ms: 200,
110 micro_batch_us: 200, }
112 }
113}
114
115#[derive(Debug, Clone)]
116pub struct TransactionFilter {
117 pub account_include: Vec<String>,
118 pub account_exclude: Vec<String>,
119 pub account_required: Vec<String>,
120}
121
122impl TransactionFilter {
123 pub fn new() -> Self {
124 Self {
125 account_include: Vec::new(),
126 account_exclude: Vec::new(),
127 account_required: Vec::new(),
128 }
129 }
130
131 pub fn include_account(mut self, account: impl Into<String>) -> Self {
132 self.account_include.push(account.into());
133 self
134 }
135
136 pub fn exclude_account(mut self, account: impl Into<String>) -> Self {
137 self.account_exclude.push(account.into());
138 self
139 }
140
141 pub fn require_account(mut self, account: impl Into<String>) -> Self {
142 self.account_required.push(account.into());
143 self
144 }
145
146 pub fn from_program_ids(program_ids: Vec<String>) -> Self {
148 Self {
149 account_include: program_ids,
150 account_exclude: Vec::new(),
151 account_required: Vec::new(),
152 }
153 }
154}
155
156impl Default for TransactionFilter {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162#[derive(Debug, Clone)]
163pub struct AccountFilter {
164 pub account: Vec<String>,
165 pub owner: Vec<String>,
166 pub filters: Vec<SubscribeRequestFilterAccountsFilter>,
167}
168
169impl AccountFilter {
170 pub fn new() -> Self {
171 Self { account: Vec::new(), owner: Vec::new(), filters: Vec::new() }
172 }
173
174 pub fn add_account(mut self, account: impl Into<String>) -> Self {
175 self.account.push(account.into());
176 self
177 }
178
179 pub fn add_owner(mut self, owner: impl Into<String>) -> Self {
180 self.owner.push(owner.into());
181 self
182 }
183
184 pub fn add_filter(mut self, filter: SubscribeRequestFilterAccountsFilter) -> Self {
185 self.filters.push(filter);
186 self
187 }
188
189 pub fn from_program_owners(program_ids: Vec<String>) -> Self {
191 Self { account: Vec::new(), owner: program_ids, filters: Vec::new() }
192 }
193}
194
195impl Default for AccountFilter {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201#[inline]
204pub fn account_filter_memcmp(offset: u64, bytes: Vec<u8>) -> SubscribeRequestFilterAccountsFilter {
205 SubscribeRequestFilterAccountsFilter {
206 filter: Some(AccountsFilterOneof::Memcmp(SubscribeRequestFilterAccountsFilterMemcmp {
207 offset,
208 data: Some(MemcmpDataOneof::Bytes(bytes)),
209 })),
210 }
211}
212
213#[derive(Debug, Clone)]
214pub struct AccountFilterData {
215 pub memcmp: Option<AccountFilterMemcmp>,
216 pub datasize: Option<u64>,
217}
218
219#[derive(Debug, Clone)]
220pub struct AccountFilterMemcmp {
221 pub offset: u64,
222 pub bytes: Vec<u8>,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
226pub enum Protocol {
227 PumpFun,
228 PumpSwap,
229 PumpFees,
230 LaunchLab,
232 StonkFun,
234 RaydiumLaunchlab,
236 RaydiumCpmm,
237 RaydiumClmm,
238 RaydiumAmmV4,
239 OrcaWhirlpool,
240 MeteoraPools,
241 MeteoraDammV2,
242 MeteoraDlmm,
243 MeteoraDbc,
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247#[non_exhaustive]
248pub enum EventType {
249 BlockMeta,
251
252 RaydiumLaunchlabTrade,
254 RaydiumLaunchlabPoolCreate,
255 RaydiumLaunchlabMigrateAmm,
256
257 PumpFunTrade, PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn, PumpFunCreate,
263 PumpFunCreateV2, PumpFunComplete,
265 PumpFunMigrate,
266 PumpFeesCreateFeeSharingConfig,
268 PumpFeesInitializeFeeConfig,
269 PumpFeesResetFeeSharingConfig,
270 PumpFeesRevokeFeeSharingAuthority,
271 PumpFeesTransferFeeSharingAuthority,
272 PumpFeesUpdateAdmin,
273 PumpFeesUpdateFeeConfig,
274 PumpFeesUpdateFeeShares,
275 PumpFeesUpsertFeeTiers,
276 PumpFunMigrateBondingCurveCreator,
278
279 PumpSwapTrade,
281 PumpSwapBuy,
282 PumpSwapSell,
283 PumpSwapCreatePool,
284 PumpSwapLiquidityAdded,
285 PumpSwapLiquidityRemoved,
286 RaydiumCpmmSwap,
291 RaydiumCpmmDeposit,
292 RaydiumCpmmWithdraw,
293 RaydiumCpmmInitialize,
294
295 RaydiumClmmSwap,
297 RaydiumClmmCreatePool,
298 RaydiumClmmOpenPosition,
299 RaydiumClmmClosePosition,
300 RaydiumClmmIncreaseLiquidity,
301 RaydiumClmmDecreaseLiquidity,
302 RaydiumClmmLiquidityChange,
303 RaydiumClmmConfigChange,
304 RaydiumClmmCreatePersonalPosition,
305 RaydiumClmmLiquidityCalculate,
306 RaydiumClmmOpenLimitOrder,
307 RaydiumClmmIncreaseLimitOrder,
308 RaydiumClmmDecreaseLimitOrder,
309 RaydiumClmmSettleLimitOrder,
310 RaydiumClmmUpdateRewardInfos,
311 RaydiumClmmOpenPositionWithTokenExtNft,
312 RaydiumClmmCollectFee,
313
314 RaydiumAmmV4Swap,
316 RaydiumAmmV4Deposit,
317 RaydiumAmmV4Withdraw,
318 RaydiumAmmV4Initialize2,
319 RaydiumAmmV4WithdrawPnl,
320
321 OrcaWhirlpoolSwap,
323 OrcaWhirlpoolLiquidityIncreased,
324 OrcaWhirlpoolLiquidityDecreased,
325 OrcaWhirlpoolPoolInitialized,
326
327 MeteoraPoolsSwap,
329 MeteoraPoolsAddLiquidity,
330 MeteoraPoolsRemoveLiquidity,
331 MeteoraPoolsBootstrapLiquidity,
332 MeteoraPoolsPoolCreated,
333 MeteoraPoolsSetPoolFees,
334
335 MeteoraDammV2Swap,
337 MeteoraDammV2AddLiquidity,
338 MeteoraDammV2RemoveLiquidity,
339 MeteoraDammV2InitializePool,
340 MeteoraDammV2CreatePosition,
341 MeteoraDammV2ClaimPositionFee,
342 MeteoraDammV2ClaimReward,
343 MeteoraDlmmClaimReward,
344 MeteoraDammV2ClosePosition,
345 MeteoraDammV2UpdateDelegatePermission,
346 MeteoraDammV2WithdrawDeadLiquidityReward,
347 MeteoraDammV2WithdrawIneligibleReward,
348 MeteoraDammV2UpdateRewardFunder,
349 MeteoraDammV2UpdateRewardDuration,
350 MeteoraDammV2InitializeReward,
351 MeteoraDammV2FundReward,
352 MeteoraDammV2CreateConfig,
353 MeteoraDammV2CreateDynamicConfig,
354 MeteoraDbcSwap,
361 MeteoraDbcInitializePool,
362 MeteoraDbcCurveComplete,
363
364 MeteoraDlmmSwap,
366 MeteoraDlmmAddLiquidity,
367 MeteoraDlmmRemoveLiquidity,
368 MeteoraDlmmInitializePool,
369 MeteoraDlmmInitializeBinArray,
370 MeteoraDlmmCreatePosition,
371 MeteoraDlmmClosePosition,
372 MeteoraDlmmClaimFee,
373
374 TokenAccount,
376 TokenInfo,
377 NonceAccount,
378 AccountPumpFunGlobal,
379 AccountPumpFunBondingCurve,
380 AccountPumpFunFeeConfig,
381 AccountPumpFunSharingConfig,
382 AccountPumpFunGlobalVolumeAccumulator,
383 AccountPumpFunUserVolumeAccumulator,
384
385 AccountPumpSwapGlobalConfig,
386 AccountPumpSwapPool,
387 AccountRaydiumClmmAmmConfig,
388 AccountRaydiumClmmPoolState,
389 AccountRaydiumClmmTickArrayState,
390 AccountRaydiumCpmmAmmConfig,
391 AccountRaydiumCpmmPoolState,
392 RaydiumCpmmCollectCreatorFee,
393 AccountRaydiumCpmmCreatorFeeShare,
394 AccountOrcaWhirlpool,
395 AccountOrcaPosition,
396 AccountOrcaTickArray,
397 AccountLiquiditySnapshot,
398 AccountRawSnapshot,
400 AccountOrcaFeeTier,
401 AccountOrcaWhirlpoolsConfig,
402 PumpFunPostCompleteBuy,
403 PumpFunSweepBondingCurveFee,
404 PumpSwapSweepPoolFee,
405 PumpRouteIntent,
407}
408
409#[derive(Debug, Clone)]
410pub struct EventTypeFilter {
411 pub include_only: Option<Vec<EventType>>,
412 pub exclude_types: Option<Vec<EventType>>,
413}
414
415impl EventTypeFilter {
416 pub fn include_only(types: Vec<EventType>) -> Self {
417 Self { include_only: Some(types), exclude_types: None }
418 }
419
420 pub fn exclude_types(types: Vec<EventType>) -> Self {
421 Self { include_only: None, exclude_types: Some(types) }
422 }
423
424 #[inline]
425 fn includes_any(&self, event_types: &[EventType]) -> bool {
426 event_types.iter().any(|event_type| self.should_include(*event_type))
427 }
428
429 pub fn should_include(&self, event_type: EventType) -> bool {
430 if let Some(ref include_only) = self.include_only {
431 if include_only.contains(&event_type) {
433 return true;
434 }
435 if matches!(
436 event_type,
437 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
438 ) {
439 if pumpfun_trade_filter_is_generic(include_only) {
440 return true;
441 }
442 if event_type == EventType::PumpFunBuyExactSolIn
443 && pumpfun_buy_filter_is_generic(include_only)
444 {
445 return true;
446 }
447 return false;
448 }
449 if is_pumpfun_create_family(event_type) {
450 return include_only.iter().any(|t| is_pumpfun_create_family(*t));
451 }
452 if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell) {
453 return include_only.contains(&EventType::PumpSwapTrade);
454 }
455 return false;
456 }
457
458 if let Some(ref exclude_types) = self.exclude_types {
459 if exclude_types.contains(&event_type) {
460 return false;
461 }
462 if matches!(
463 event_type,
464 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
465 ) && exclude_types.contains(&EventType::PumpFunTrade)
466 {
467 return false;
468 }
469 if event_type == EventType::PumpFunBuyExactSolIn
470 && exclude_types.contains(&EventType::PumpFunBuy)
471 {
472 return false;
473 }
474 if is_pumpfun_create_family(event_type)
475 && exclude_types.iter().any(|t| is_pumpfun_create_family(*t))
476 {
477 return false;
478 }
479 if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell)
480 && exclude_types.contains(&EventType::PumpSwapTrade)
481 {
482 return false;
483 }
484 return true;
485 }
486
487 true
488 }
489
490 pub(crate) fn includes_pump_route(&self, kind: crate::core::events::PumpRouteKind) -> bool {
494 if self
495 .exclude_types
496 .as_ref()
497 .is_some_and(|types| types.contains(&EventType::PumpRouteIntent))
498 {
499 return false;
500 }
501 self.include_only.as_ref().is_some_and(|types| types.contains(&EventType::PumpRouteIntent))
502 || self.should_include(EventType::PumpFunTrade)
503 || (kind == crate::core::events::PumpRouteKind::MultiHopSwap
504 && self.should_include(EventType::PumpSwapTrade))
505 }
506
507 pub fn should_include_dex_event(&self, event: &crate::core::events::DexEvent) -> bool {
508 if let crate::core::events::DexEvent::PumpRouteIntent(intent) = event {
509 return self.includes_pump_route(intent.kind);
510 }
511 let Some(event_type) = event_type_from_dex_event(event) else { return true };
512 self.should_include(event_type)
513 }
514
515 #[inline]
516 pub fn includes_block_meta(&self) -> bool {
517 if let Some(ref include_only) = self.include_only {
518 return include_only.contains(&EventType::BlockMeta);
519 }
520 false
521 }
522
523 #[inline]
524 pub fn normalize_dex_event(
525 &self,
526 event: crate::core::events::DexEvent,
527 ) -> crate::core::events::DexEvent {
528 use crate::core::events::DexEvent;
529
530 let Some(ref include_only) = self.include_only else { return event };
531 if pumpfun_trade_filter_is_generic(include_only) {
532 return match event {
533 DexEvent::PumpFunBuy(t)
534 | DexEvent::PumpFunSell(t)
535 | DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunTrade(t),
536 other => other,
537 };
538 }
539 if pumpfun_buy_filter_is_generic(include_only) {
540 return match event {
541 DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunBuy(t),
542 other => other,
543 };
544 }
545
546 event
547 }
548
549 #[inline]
550 pub fn includes_pumpfun(&self) -> bool {
551 self.includes_any(&[
552 EventType::PumpFunTrade,
553 EventType::PumpFunBuy,
554 EventType::PumpFunSell,
555 EventType::PumpFunBuyExactSolIn,
556 EventType::PumpFunCreate,
557 EventType::PumpFunCreateV2,
558 EventType::PumpFunComplete,
559 EventType::PumpFunMigrate,
560 EventType::PumpFunMigrateBondingCurveCreator,
561 EventType::PumpFunPostCompleteBuy,
562 EventType::PumpFunSweepBondingCurveFee,
563 ])
564 }
565
566 #[inline]
567 pub fn includes_meteora_damm_v2(&self) -> bool {
568 self.includes_any(&[
569 EventType::MeteoraDammV2Swap,
570 EventType::MeteoraDammV2AddLiquidity,
571 EventType::MeteoraDammV2CreatePosition,
572 EventType::MeteoraDammV2ClaimPositionFee,
573 EventType::MeteoraDammV2ClaimReward,
574 EventType::MeteoraDammV2ClosePosition,
575 EventType::MeteoraDammV2InitializePool,
576 EventType::MeteoraDammV2RemoveLiquidity,
577 EventType::MeteoraDammV2UpdateDelegatePermission,
578 EventType::MeteoraDammV2WithdrawDeadLiquidityReward,
579 EventType::MeteoraDammV2WithdrawIneligibleReward,
580 EventType::MeteoraDammV2UpdateRewardFunder,
581 EventType::MeteoraDammV2UpdateRewardDuration,
582 EventType::MeteoraDammV2InitializeReward,
583 EventType::MeteoraDammV2FundReward,
584 EventType::MeteoraDammV2CreateConfig,
585 EventType::MeteoraDammV2CreateDynamicConfig,
586 ])
587 }
588
589 #[inline]
590 pub fn includes_pump_fees(&self) -> bool {
591 self.includes_any(&[
592 EventType::PumpFeesCreateFeeSharingConfig,
593 EventType::PumpFeesInitializeFeeConfig,
594 EventType::PumpFeesResetFeeSharingConfig,
595 EventType::PumpFeesRevokeFeeSharingAuthority,
596 EventType::PumpFeesTransferFeeSharingAuthority,
597 EventType::PumpFeesUpdateAdmin,
598 EventType::PumpFeesUpdateFeeConfig,
599 EventType::PumpFeesUpdateFeeShares,
600 EventType::PumpFeesUpsertFeeTiers,
601 ])
602 }
603
604 #[inline]
606 pub fn includes_pumpswap(&self) -> bool {
607 self.includes_any(&[
608 EventType::PumpSwapTrade,
609 EventType::PumpSwapBuy,
610 EventType::PumpSwapSell,
611 EventType::PumpSwapCreatePool,
612 EventType::PumpSwapSweepPoolFee,
613 EventType::PumpSwapLiquidityAdded,
614 EventType::PumpSwapLiquidityRemoved,
615 ])
616 }
617
618 #[inline]
620 pub fn includes_raydium_launchlab(&self) -> bool {
621 self.includes_any(&[
622 EventType::RaydiumLaunchlabTrade,
623 EventType::RaydiumLaunchlabPoolCreate,
624 EventType::RaydiumLaunchlabMigrateAmm,
625 ])
626 }
627
628 #[inline]
629 pub fn includes_raydium_cpmm(&self) -> bool {
630 self.includes_any(&[
631 EventType::RaydiumCpmmSwap,
632 EventType::RaydiumCpmmDeposit,
633 EventType::RaydiumCpmmWithdraw,
634 EventType::RaydiumCpmmInitialize,
635 EventType::RaydiumCpmmCollectCreatorFee,
636 ])
637 }
638
639 #[inline]
640 pub fn includes_raydium_clmm(&self) -> bool {
641 self.includes_any(&[
642 EventType::RaydiumClmmSwap,
643 EventType::RaydiumClmmCreatePool,
644 EventType::RaydiumClmmOpenPosition,
645 EventType::RaydiumClmmClosePosition,
646 EventType::RaydiumClmmIncreaseLiquidity,
647 EventType::RaydiumClmmDecreaseLiquidity,
648 EventType::RaydiumClmmLiquidityChange,
649 EventType::RaydiumClmmConfigChange,
650 EventType::RaydiumClmmCreatePersonalPosition,
651 EventType::RaydiumClmmLiquidityCalculate,
652 EventType::RaydiumClmmOpenLimitOrder,
653 EventType::RaydiumClmmIncreaseLimitOrder,
654 EventType::RaydiumClmmDecreaseLimitOrder,
655 EventType::RaydiumClmmSettleLimitOrder,
656 EventType::RaydiumClmmUpdateRewardInfos,
657 EventType::RaydiumClmmOpenPositionWithTokenExtNft,
658 EventType::RaydiumClmmCollectFee,
659 ])
660 }
661
662 #[inline]
663 pub fn includes_raydium_amm_v4(&self) -> bool {
664 self.includes_any(&[
665 EventType::RaydiumAmmV4Swap,
666 EventType::RaydiumAmmV4Deposit,
667 EventType::RaydiumAmmV4Withdraw,
668 EventType::RaydiumAmmV4Initialize2,
669 EventType::RaydiumAmmV4WithdrawPnl,
670 ])
671 }
672
673 #[inline]
674 pub fn includes_orca_whirlpool(&self) -> bool {
675 self.includes_any(&[
676 EventType::OrcaWhirlpoolSwap,
677 EventType::OrcaWhirlpoolLiquidityIncreased,
678 EventType::OrcaWhirlpoolLiquidityDecreased,
679 EventType::OrcaWhirlpoolPoolInitialized,
680 ])
681 }
682
683 #[inline]
684 pub fn includes_meteora_pools(&self) -> bool {
685 self.includes_any(&[
686 EventType::MeteoraPoolsSwap,
687 EventType::MeteoraPoolsAddLiquidity,
688 EventType::MeteoraPoolsRemoveLiquidity,
689 EventType::MeteoraPoolsBootstrapLiquidity,
690 EventType::MeteoraPoolsPoolCreated,
691 EventType::MeteoraPoolsSetPoolFees,
692 ])
693 }
694
695 #[inline]
696 pub fn includes_meteora_dlmm(&self) -> bool {
697 self.includes_any(&[
698 EventType::MeteoraDlmmSwap,
699 EventType::MeteoraDlmmAddLiquidity,
700 EventType::MeteoraDlmmRemoveLiquidity,
701 EventType::MeteoraDlmmInitializePool,
702 EventType::MeteoraDlmmInitializeBinArray,
703 EventType::MeteoraDlmmCreatePosition,
704 EventType::MeteoraDlmmClosePosition,
705 EventType::MeteoraDlmmClaimFee,
706 EventType::MeteoraDlmmClaimReward,
707 ])
708 }
709
710 #[inline]
711 pub fn includes_meteora_dbc(&self) -> bool {
712 self.includes_any(&[
713 EventType::MeteoraDbcSwap,
714 EventType::MeteoraDbcInitializePool,
715 EventType::MeteoraDbcCurveComplete,
716 ])
717 }
718}
719
720#[inline]
721fn pumpfun_trade_filter_is_generic(include_only: &[EventType]) -> bool {
722 include_only.contains(&EventType::PumpFunTrade)
723 && !include_only.iter().any(|t| {
724 matches!(
725 t,
726 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
727 )
728 })
729}
730
731#[inline]
732fn pumpfun_buy_filter_is_generic(include_only: &[EventType]) -> bool {
733 include_only.contains(&EventType::PumpFunBuy)
734 && !include_only.contains(&EventType::PumpFunBuyExactSolIn)
735}
736
737#[inline]
738fn is_pumpfun_create_family(event_type: EventType) -> bool {
739 matches!(event_type, EventType::PumpFunCreate | EventType::PumpFunCreateV2)
740}
741
742#[inline]
743pub fn event_type_from_dex_event(event: &crate::core::events::DexEvent) -> Option<EventType> {
744 use crate::core::events::DexEvent;
745 match event {
746 DexEvent::PumpRouteIntent(_) => Some(EventType::PumpRouteIntent),
747 DexEvent::PumpFunCreate(_) => Some(EventType::PumpFunCreate),
748 DexEvent::PumpFunCreateV2(_) => Some(EventType::PumpFunCreateV2),
749 DexEvent::PumpFunTrade(_) => Some(EventType::PumpFunTrade),
750 DexEvent::PumpFunBuy(_) => Some(EventType::PumpFunBuy),
751 DexEvent::PumpFunSell(_) => Some(EventType::PumpFunSell),
752 DexEvent::PumpFunBuyExactSolIn(_) => Some(EventType::PumpFunBuyExactSolIn),
753 DexEvent::PumpFunMigrate(_) => Some(EventType::PumpFunMigrate),
754 DexEvent::PumpFeesCreateFeeSharingConfig(_) => {
755 Some(EventType::PumpFeesCreateFeeSharingConfig)
756 }
757 DexEvent::PumpFeesInitializeFeeConfig(_) => Some(EventType::PumpFeesInitializeFeeConfig),
758 DexEvent::PumpFeesResetFeeSharingConfig(_) => {
759 Some(EventType::PumpFeesResetFeeSharingConfig)
760 }
761 DexEvent::PumpFeesRevokeFeeSharingAuthority(_) => {
762 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
763 }
764 DexEvent::PumpFeesTransferFeeSharingAuthority(_) => {
765 Some(EventType::PumpFeesTransferFeeSharingAuthority)
766 }
767 DexEvent::PumpFeesUpdateAdmin(_) => Some(EventType::PumpFeesUpdateAdmin),
768 DexEvent::PumpFeesUpdateFeeConfig(_) => Some(EventType::PumpFeesUpdateFeeConfig),
769 DexEvent::PumpFeesUpdateFeeShares(_) => Some(EventType::PumpFeesUpdateFeeShares),
770 DexEvent::PumpFeesUpsertFeeTiers(_) => Some(EventType::PumpFeesUpsertFeeTiers),
771 DexEvent::PumpFunPostCompleteBuy(_) => Some(EventType::PumpFunPostCompleteBuy),
772 DexEvent::PumpFunSweepBondingCurveFee(_) => Some(EventType::PumpFunSweepBondingCurveFee),
773 DexEvent::PumpFunComplete(_) => Some(EventType::PumpFunComplete),
774 DexEvent::PumpSwapSweepPoolFee(_) => Some(EventType::PumpSwapSweepPoolFee),
775 DexEvent::PumpFunMigrateBondingCurveCreator(_) => {
776 Some(EventType::PumpFunMigrateBondingCurveCreator)
777 }
778 DexEvent::PumpFunGlobalAccount(_) => Some(EventType::AccountPumpFunGlobal),
779 DexEvent::PumpFunBondingCurveAccount(_) => Some(EventType::AccountPumpFunBondingCurve),
780 DexEvent::PumpFunFeeConfigAccount(_) => Some(EventType::AccountPumpFunFeeConfig),
781 DexEvent::PumpFunSharingConfigAccount(_) => Some(EventType::AccountPumpFunSharingConfig),
782 DexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) => {
783 Some(EventType::AccountPumpFunGlobalVolumeAccumulator)
784 }
785 DexEvent::PumpFunUserVolumeAccumulatorAccount(_) => {
786 Some(EventType::AccountPumpFunUserVolumeAccumulator)
787 }
788 DexEvent::PumpSwapTrade(_) => Some(EventType::PumpSwapTrade),
789 DexEvent::PumpSwapBuy(_) => Some(EventType::PumpSwapBuy),
790 DexEvent::PumpSwapSell(_) => Some(EventType::PumpSwapSell),
791 DexEvent::PumpSwapCreatePool(_) => Some(EventType::PumpSwapCreatePool),
792 DexEvent::PumpSwapLiquidityAdded(_) => Some(EventType::PumpSwapLiquidityAdded),
793 DexEvent::PumpSwapLiquidityRemoved(_) => Some(EventType::PumpSwapLiquidityRemoved),
794 DexEvent::MeteoraDammV2Swap(_) => Some(EventType::MeteoraDammV2Swap),
795 DexEvent::MeteoraDammV2ClaimPositionFee(_) => {
796 Some(EventType::MeteoraDammV2ClaimPositionFee)
797 }
798 DexEvent::MeteoraDammV2ClaimReward(_) => Some(EventType::MeteoraDammV2ClaimReward),
799 DexEvent::MeteoraDlmmClaimReward(_) => Some(EventType::MeteoraDlmmClaimReward),
800 DexEvent::MeteoraDammV2CreatePosition(_) => Some(EventType::MeteoraDammV2CreatePosition),
801 DexEvent::MeteoraDammV2ClosePosition(_) => Some(EventType::MeteoraDammV2ClosePosition),
802 DexEvent::MeteoraDammV2AddLiquidity(_) => Some(EventType::MeteoraDammV2AddLiquidity),
803 DexEvent::MeteoraDammV2RemoveLiquidity(_) => Some(EventType::MeteoraDammV2RemoveLiquidity),
804 DexEvent::MeteoraDammV2InitializePool(_) => Some(EventType::MeteoraDammV2InitializePool),
805 DexEvent::MeteoraDammV2UpdateDelegatePermission(_) => {
806 Some(EventType::MeteoraDammV2UpdateDelegatePermission)
807 }
808 DexEvent::MeteoraDammV2WithdrawDeadLiquidityReward(_) => {
809 Some(EventType::MeteoraDammV2WithdrawDeadLiquidityReward)
810 }
811 DexEvent::MeteoraDammV2WithdrawIneligibleReward(_) => {
812 Some(EventType::MeteoraDammV2WithdrawIneligibleReward)
813 }
814 DexEvent::MeteoraDammV2UpdateRewardFunder(_) => {
815 Some(EventType::MeteoraDammV2UpdateRewardFunder)
816 }
817 DexEvent::MeteoraDammV2UpdateRewardDuration(_) => {
818 Some(EventType::MeteoraDammV2UpdateRewardDuration)
819 }
820 DexEvent::MeteoraDammV2InitializeReward(_) => {
821 Some(EventType::MeteoraDammV2InitializeReward)
822 }
823 DexEvent::MeteoraDammV2FundReward(_) => Some(EventType::MeteoraDammV2FundReward),
824 DexEvent::MeteoraDammV2CreateConfig(_) => Some(EventType::MeteoraDammV2CreateConfig),
825 DexEvent::MeteoraDammV2CreateDynamicConfig(_) => {
826 Some(EventType::MeteoraDammV2CreateDynamicConfig)
827 }
828 DexEvent::MeteoraDbcSwap(_) => Some(EventType::MeteoraDbcSwap),
829 DexEvent::MeteoraDbcInitializePool(_) => Some(EventType::MeteoraDbcInitializePool),
830 DexEvent::MeteoraDbcCurveComplete(_) => Some(EventType::MeteoraDbcCurveComplete),
831 DexEvent::RaydiumLaunchlabTrade(_) => Some(EventType::RaydiumLaunchlabTrade),
832 DexEvent::RaydiumLaunchlabPoolCreate(_) => Some(EventType::RaydiumLaunchlabPoolCreate),
833 DexEvent::RaydiumLaunchlabMigrateAmm(_) => Some(EventType::RaydiumLaunchlabMigrateAmm),
834 DexEvent::RaydiumClmmSwap(_) => Some(EventType::RaydiumClmmSwap),
835 DexEvent::RaydiumClmmCreatePool(_) => Some(EventType::RaydiumClmmCreatePool),
836 DexEvent::RaydiumClmmOpenPosition(_) => Some(EventType::RaydiumClmmOpenPosition),
837 DexEvent::RaydiumClmmOpenPositionWithTokenExtNft(_) => {
838 Some(EventType::RaydiumClmmOpenPositionWithTokenExtNft)
839 }
840 DexEvent::RaydiumClmmClosePosition(_) => Some(EventType::RaydiumClmmClosePosition),
841 DexEvent::RaydiumClmmIncreaseLiquidity(_) => Some(EventType::RaydiumClmmIncreaseLiquidity),
842 DexEvent::RaydiumClmmDecreaseLiquidity(_) => Some(EventType::RaydiumClmmDecreaseLiquidity),
843 DexEvent::RaydiumClmmLiquidityChange(_) => Some(EventType::RaydiumClmmLiquidityChange),
844 DexEvent::RaydiumClmmConfigChange(_) => Some(EventType::RaydiumClmmConfigChange),
845 DexEvent::RaydiumClmmCreatePersonalPosition(_) => {
846 Some(EventType::RaydiumClmmCreatePersonalPosition)
847 }
848 DexEvent::RaydiumClmmLiquidityCalculate(_) => {
849 Some(EventType::RaydiumClmmLiquidityCalculate)
850 }
851 DexEvent::RaydiumClmmOpenLimitOrder(_) => Some(EventType::RaydiumClmmOpenLimitOrder),
852 DexEvent::RaydiumClmmIncreaseLimitOrder(_) => {
853 Some(EventType::RaydiumClmmIncreaseLimitOrder)
854 }
855 DexEvent::RaydiumClmmDecreaseLimitOrder(_) => {
856 Some(EventType::RaydiumClmmDecreaseLimitOrder)
857 }
858 DexEvent::RaydiumClmmSettleLimitOrder(_) => Some(EventType::RaydiumClmmSettleLimitOrder),
859 DexEvent::RaydiumClmmUpdateRewardInfos(_) => Some(EventType::RaydiumClmmUpdateRewardInfos),
860 DexEvent::RaydiumClmmCollectFee(_) => Some(EventType::RaydiumClmmCollectFee),
861 DexEvent::RaydiumClmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumClmmAmmConfig),
862 DexEvent::RaydiumClmmPoolStateAccount(_) => Some(EventType::AccountRaydiumClmmPoolState),
863 DexEvent::RaydiumClmmTickArrayStateAccount(_) => {
864 Some(EventType::AccountRaydiumClmmTickArrayState)
865 }
866 DexEvent::RaydiumCpmmSwap(_) => Some(EventType::RaydiumCpmmSwap),
867 DexEvent::RaydiumCpmmDeposit(_) => Some(EventType::RaydiumCpmmDeposit),
868 DexEvent::RaydiumCpmmWithdraw(_) => Some(EventType::RaydiumCpmmWithdraw),
869 DexEvent::RaydiumCpmmInitialize(_) => Some(EventType::RaydiumCpmmInitialize),
870 DexEvent::RaydiumCpmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumCpmmAmmConfig),
871 DexEvent::RaydiumCpmmPoolStateAccount(_) => Some(EventType::AccountRaydiumCpmmPoolState),
872 DexEvent::RaydiumCpmmCollectCreatorFee(_) => Some(EventType::RaydiumCpmmCollectCreatorFee),
873 DexEvent::RaydiumCpmmCreatorFeeShareAccount(_) => {
874 Some(EventType::AccountRaydiumCpmmCreatorFeeShare)
875 }
876 DexEvent::RaydiumAmmV4Swap(_) => Some(EventType::RaydiumAmmV4Swap),
877 DexEvent::RaydiumAmmV4Deposit(_) => Some(EventType::RaydiumAmmV4Deposit),
878 DexEvent::RaydiumAmmV4Initialize2(_) => Some(EventType::RaydiumAmmV4Initialize2),
879 DexEvent::RaydiumAmmV4Withdraw(_) => Some(EventType::RaydiumAmmV4Withdraw),
880 DexEvent::RaydiumAmmV4WithdrawPnl(_) => Some(EventType::RaydiumAmmV4WithdrawPnl),
881 DexEvent::OrcaWhirlpoolSwap(_) => Some(EventType::OrcaWhirlpoolSwap),
882 DexEvent::OrcaWhirlpoolLiquidityIncreased(_) => {
883 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
884 }
885 DexEvent::OrcaWhirlpoolLiquidityDecreased(_) => {
886 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
887 }
888 DexEvent::OrcaWhirlpoolPoolInitialized(_) => Some(EventType::OrcaWhirlpoolPoolInitialized),
889 DexEvent::OrcaWhirlpoolAccount(_) => Some(EventType::AccountOrcaWhirlpool),
890 DexEvent::OrcaPositionAccount(_) => Some(EventType::AccountOrcaPosition),
891 DexEvent::OrcaTickArrayAccount(_) => Some(EventType::AccountOrcaTickArray),
892 DexEvent::LiquidityAccountSnapshot(_) => Some(EventType::AccountLiquiditySnapshot),
893 DexEvent::RawAccountSnapshot(_) => Some(EventType::AccountRawSnapshot),
894 DexEvent::OrcaFeeTierAccount(_) => Some(EventType::AccountOrcaFeeTier),
895 DexEvent::OrcaWhirlpoolsConfigAccount(_) => Some(EventType::AccountOrcaWhirlpoolsConfig),
896 DexEvent::MeteoraPoolsSwap(_) => Some(EventType::MeteoraPoolsSwap),
897 DexEvent::MeteoraPoolsAddLiquidity(_) => Some(EventType::MeteoraPoolsAddLiquidity),
898 DexEvent::MeteoraPoolsRemoveLiquidity(_) => Some(EventType::MeteoraPoolsRemoveLiquidity),
899 DexEvent::MeteoraPoolsBootstrapLiquidity(_) => {
900 Some(EventType::MeteoraPoolsBootstrapLiquidity)
901 }
902 DexEvent::MeteoraPoolsPoolCreated(_) => Some(EventType::MeteoraPoolsPoolCreated),
903 DexEvent::MeteoraPoolsSetPoolFees(_) => Some(EventType::MeteoraPoolsSetPoolFees),
904 DexEvent::MeteoraDlmmSwap(_) => Some(EventType::MeteoraDlmmSwap),
905 DexEvent::MeteoraDlmmAddLiquidity(_) => Some(EventType::MeteoraDlmmAddLiquidity),
906 DexEvent::MeteoraDlmmRemoveLiquidity(_) => Some(EventType::MeteoraDlmmRemoveLiquidity),
907 DexEvent::MeteoraDlmmInitializePool(_) => Some(EventType::MeteoraDlmmInitializePool),
908 DexEvent::MeteoraDlmmInitializeBinArray(_) => {
909 Some(EventType::MeteoraDlmmInitializeBinArray)
910 }
911 DexEvent::MeteoraDlmmCreatePosition(_) => Some(EventType::MeteoraDlmmCreatePosition),
912 DexEvent::MeteoraDlmmClosePosition(_) => Some(EventType::MeteoraDlmmClosePosition),
913 DexEvent::MeteoraDlmmClaimFee(_) => Some(EventType::MeteoraDlmmClaimFee),
914 DexEvent::TokenAccount(_) => Some(EventType::TokenAccount),
915 DexEvent::TokenInfo(_) => Some(EventType::TokenInfo),
916 DexEvent::NonceAccount(_) => Some(EventType::NonceAccount),
917 DexEvent::PumpSwapGlobalConfigAccount(_) => Some(EventType::AccountPumpSwapGlobalConfig),
918 DexEvent::PumpSwapPoolAccount(_) => Some(EventType::AccountPumpSwapPool),
919 DexEvent::BlockMeta(_) => Some(EventType::BlockMeta),
920 DexEvent::Error(_) => None,
921 }
922}
923
924#[cfg(test)]
925mod event_type_filter_tests {
926 use super::*;
927
928 #[test]
929 fn generic_trade_filters_cover_specific_trade_variants() {
930 let pump = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
931 assert!(pump.should_include(EventType::PumpFunTrade));
932 assert!(pump.should_include(EventType::PumpFunBuy));
933 assert!(pump.should_include(EventType::PumpFunSell));
934 assert!(pump.should_include(EventType::PumpFunBuyExactSolIn));
935
936 let pump_specific = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
937 assert!(!pump_specific.should_include(EventType::PumpFunTrade));
938 assert!(pump_specific.should_include(EventType::PumpFunBuy));
939 assert!(!pump_specific.should_include(EventType::PumpFunSell));
940 assert!(pump_specific.should_include(EventType::PumpFunBuyExactSolIn));
941
942 let pump_exact_buy = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
943 assert!(!pump_exact_buy.should_include(EventType::PumpFunTrade));
944 assert!(!pump_exact_buy.should_include(EventType::PumpFunBuy));
945 assert!(!pump_exact_buy.should_include(EventType::PumpFunSell));
946 assert!(pump_exact_buy.should_include(EventType::PumpFunBuyExactSolIn));
947
948 let pumpswap = EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]);
949 assert!(pumpswap.should_include(EventType::PumpSwapBuy));
950 assert!(pumpswap.should_include(EventType::PumpSwapSell));
951
952 let exclude_pumpswap = EventTypeFilter::exclude_types(vec![EventType::PumpSwapTrade]);
953 assert!(!exclude_pumpswap.should_include(EventType::PumpSwapBuy));
954 assert!(!exclude_pumpswap.should_include(EventType::PumpSwapSell));
955 }
956
957 #[test]
958 fn generic_pumpfun_trade_filter_normalizes_specific_variants() {
959 use crate::core::events::{DexEvent, PumpFunTradeEvent};
960
961 let filter = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
962 let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
963 assert!(matches!(filter.normalize_dex_event(event), DexEvent::PumpFunTrade(_)));
964
965 let specific_filter =
966 EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
967 let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
968 assert!(matches!(specific_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
969
970 let buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
971 let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
972 is_buy: true,
973 ..Default::default()
974 });
975 assert!(matches!(buy_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
976
977 let exact_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
978 let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
979 is_buy: true,
980 ..Default::default()
981 });
982 assert!(matches!(
983 exact_filter.normalize_dex_event(event),
984 DexEvent::PumpFunBuyExactSolIn(_)
985 ));
986
987 let create_and_trade_filter =
988 EventTypeFilter::include_only(vec![EventType::PumpFunCreate, EventType::PumpFunTrade]);
989 let event =
990 DexEvent::PumpFunSell(PumpFunTradeEvent { is_buy: false, ..Default::default() });
991 assert!(matches!(
992 create_and_trade_filter.normalize_dex_event(event),
993 DexEvent::PumpFunTrade(_)
994 ));
995 }
996
997 #[test]
998 fn all_protocol_groups_are_filterable() {
999 assert!(EventTypeFilter::include_only(vec![EventType::PumpFunTrade]).includes_pumpfun());
1000 assert!(!EventTypeFilter::include_only(vec![EventType::AccountPumpFunGlobal])
1001 .includes_pumpfun());
1002 assert!(
1003 !EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateAdmin]).includes_pumpfun()
1004 );
1005 assert!(EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]).includes_pumpswap());
1006 assert!(EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateFeeShares])
1007 .includes_pump_fees());
1008 assert!(EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade])
1009 .includes_raydium_launchlab());
1010 assert!(
1011 EventTypeFilter::include_only(vec![EventType::RaydiumCpmmSwap]).includes_raydium_cpmm()
1012 );
1013 assert!(
1014 EventTypeFilter::include_only(vec![EventType::RaydiumClmmSwap]).includes_raydium_clmm()
1015 );
1016 assert!(!EventTypeFilter::include_only(vec![EventType::AccountRaydiumClmmPoolState])
1017 .includes_raydium_clmm());
1018 assert!(EventTypeFilter::include_only(vec![EventType::RaydiumAmmV4Swap])
1019 .includes_raydium_amm_v4());
1020 assert!(EventTypeFilter::include_only(vec![EventType::OrcaWhirlpoolSwap])
1021 .includes_orca_whirlpool());
1022 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraPoolsSwap])
1023 .includes_meteora_pools());
1024 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap])
1025 .includes_meteora_damm_v2());
1026 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2InitializePool])
1027 .includes_meteora_damm_v2());
1028 assert!(
1029 EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]).includes_meteora_dlmm()
1030 );
1031 assert!(
1032 EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]).includes_meteora_dbc()
1033 );
1034 }
1035
1036 #[test]
1037 fn exclude_filters_do_not_skip_whole_protocol_groups() {
1038 let raydium = EventTypeFilter::exclude_types(vec![EventType::RaydiumCpmmSwap]);
1039 assert!(raydium.includes_raydium_cpmm());
1040 assert!(!raydium.should_include(EventType::RaydiumCpmmSwap));
1041 assert!(raydium.should_include(EventType::RaydiumCpmmDeposit));
1042
1043 let all_cpmm = EventTypeFilter::exclude_types(vec![
1044 EventType::RaydiumCpmmSwap,
1045 EventType::RaydiumCpmmDeposit,
1046 EventType::RaydiumCpmmWithdraw,
1047 EventType::RaydiumCpmmInitialize,
1048 EventType::RaydiumCpmmCollectCreatorFee,
1049 ]);
1050 assert!(!all_cpmm.includes_raydium_cpmm());
1051
1052 let all_launchlab = EventTypeFilter::exclude_types(vec![
1053 EventType::RaydiumLaunchlabTrade,
1054 EventType::RaydiumLaunchlabPoolCreate,
1055 EventType::RaydiumLaunchlabMigrateAmm,
1056 ]);
1057 assert!(!all_launchlab.includes_raydium_launchlab());
1058
1059 let pump = EventTypeFilter::exclude_types(vec![EventType::PumpFunBuy]);
1060 assert!(pump.includes_pumpfun());
1061 assert!(!pump.should_include(EventType::PumpFunBuy));
1062 assert!(!pump.should_include(EventType::PumpFunBuyExactSolIn));
1063 assert!(pump.should_include(EventType::PumpFunSell));
1064 }
1065}
1066
1067#[derive(Debug, Clone)]
1068pub struct SlotFilter {
1069 pub min_slot: Option<u64>,
1070 pub max_slot: Option<u64>,
1071}
1072
1073impl SlotFilter {
1074 pub fn new() -> Self {
1075 Self { min_slot: None, max_slot: None }
1076 }
1077
1078 pub fn min_slot(mut self, slot: u64) -> Self {
1079 self.min_slot = Some(slot);
1080 self
1081 }
1082
1083 pub fn max_slot(mut self, slot: u64) -> Self {
1084 self.max_slot = Some(slot);
1085 self
1086 }
1087}
1088
1089impl Default for SlotFilter {
1090 fn default() -> Self {
1091 Self::new()
1092 }
1093}