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}
406
407#[derive(Debug, Clone)]
408pub struct EventTypeFilter {
409 pub include_only: Option<Vec<EventType>>,
410 pub exclude_types: Option<Vec<EventType>>,
411}
412
413impl EventTypeFilter {
414 pub fn include_only(types: Vec<EventType>) -> Self {
415 Self { include_only: Some(types), exclude_types: None }
416 }
417
418 pub fn exclude_types(types: Vec<EventType>) -> Self {
419 Self { include_only: None, exclude_types: Some(types) }
420 }
421
422 #[inline]
423 fn includes_any(&self, event_types: &[EventType]) -> bool {
424 event_types.iter().any(|event_type| self.should_include(*event_type))
425 }
426
427 pub fn should_include(&self, event_type: EventType) -> bool {
428 if let Some(ref include_only) = self.include_only {
429 if include_only.contains(&event_type) {
431 return true;
432 }
433 if matches!(
434 event_type,
435 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
436 ) {
437 if pumpfun_trade_filter_is_generic(include_only) {
438 return true;
439 }
440 if event_type == EventType::PumpFunBuyExactSolIn
441 && pumpfun_buy_filter_is_generic(include_only)
442 {
443 return true;
444 }
445 return false;
446 }
447 if is_pumpfun_create_family(event_type) {
448 return include_only.iter().any(|t| is_pumpfun_create_family(*t));
449 }
450 if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell) {
451 return include_only.contains(&EventType::PumpSwapTrade);
452 }
453 return false;
454 }
455
456 if let Some(ref exclude_types) = self.exclude_types {
457 if exclude_types.contains(&event_type) {
458 return false;
459 }
460 if matches!(
461 event_type,
462 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
463 ) && exclude_types.contains(&EventType::PumpFunTrade)
464 {
465 return false;
466 }
467 if event_type == EventType::PumpFunBuyExactSolIn
468 && exclude_types.contains(&EventType::PumpFunBuy)
469 {
470 return false;
471 }
472 if is_pumpfun_create_family(event_type)
473 && exclude_types.iter().any(|t| is_pumpfun_create_family(*t))
474 {
475 return false;
476 }
477 if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell)
478 && exclude_types.contains(&EventType::PumpSwapTrade)
479 {
480 return false;
481 }
482 return true;
483 }
484
485 true
486 }
487
488 pub(crate) fn includes_unresolved_pumpfun(&self) -> bool {
489 !self.exclude_types.as_ref().is_some_and(|types| types.contains(&EventType::PumpFunTrade))
490 && self.should_include(EventType::PumpFunTrade)
491 }
492
493 pub fn should_include_dex_event(&self, event: &crate::core::events::DexEvent) -> bool {
494 if let crate::core::events::DexEvent::PumpFunTrade(t) = event {
495 if t.parse_status == crate::core::events::PumpFunParseStatus::Unresolved {
496 return self.includes_unresolved_pumpfun();
499 }
500 }
501 let Some(event_type) = event_type_from_dex_event(event) else { return true };
502 self.should_include(event_type)
503 }
504
505 #[inline]
506 pub fn includes_block_meta(&self) -> bool {
507 if let Some(ref include_only) = self.include_only {
508 return include_only.contains(&EventType::BlockMeta);
509 }
510 false
511 }
512
513 #[inline]
514 pub fn normalize_dex_event(
515 &self,
516 event: crate::core::events::DexEvent,
517 ) -> crate::core::events::DexEvent {
518 use crate::core::events::DexEvent;
519
520 let Some(ref include_only) = self.include_only else { return event };
521 if pumpfun_trade_filter_is_generic(include_only) {
522 return match event {
523 DexEvent::PumpFunBuy(t)
524 | DexEvent::PumpFunSell(t)
525 | DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunTrade(t),
526 other => other,
527 };
528 }
529 if pumpfun_buy_filter_is_generic(include_only) {
530 return match event {
531 DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunBuy(t),
532 other => other,
533 };
534 }
535
536 event
537 }
538
539 #[inline]
540 pub fn includes_pumpfun(&self) -> bool {
541 self.includes_any(&[
542 EventType::PumpFunTrade,
543 EventType::PumpFunBuy,
544 EventType::PumpFunSell,
545 EventType::PumpFunBuyExactSolIn,
546 EventType::PumpFunCreate,
547 EventType::PumpFunCreateV2,
548 EventType::PumpFunComplete,
549 EventType::PumpFunMigrate,
550 EventType::PumpFunMigrateBondingCurveCreator,
551 EventType::PumpFunPostCompleteBuy,
552 EventType::PumpFunSweepBondingCurveFee,
553 ])
554 }
555
556 #[inline]
557 pub fn includes_meteora_damm_v2(&self) -> bool {
558 self.includes_any(&[
559 EventType::MeteoraDammV2Swap,
560 EventType::MeteoraDammV2AddLiquidity,
561 EventType::MeteoraDammV2CreatePosition,
562 EventType::MeteoraDammV2ClaimPositionFee,
563 EventType::MeteoraDammV2ClaimReward,
564 EventType::MeteoraDammV2ClosePosition,
565 EventType::MeteoraDammV2InitializePool,
566 EventType::MeteoraDammV2RemoveLiquidity,
567 EventType::MeteoraDammV2UpdateDelegatePermission,
568 EventType::MeteoraDammV2WithdrawDeadLiquidityReward,
569 EventType::MeteoraDammV2WithdrawIneligibleReward,
570 EventType::MeteoraDammV2UpdateRewardFunder,
571 EventType::MeteoraDammV2UpdateRewardDuration,
572 EventType::MeteoraDammV2InitializeReward,
573 EventType::MeteoraDammV2FundReward,
574 EventType::MeteoraDammV2CreateConfig,
575 EventType::MeteoraDammV2CreateDynamicConfig,
576 ])
577 }
578
579 #[inline]
580 pub fn includes_pump_fees(&self) -> bool {
581 self.includes_any(&[
582 EventType::PumpFeesCreateFeeSharingConfig,
583 EventType::PumpFeesInitializeFeeConfig,
584 EventType::PumpFeesResetFeeSharingConfig,
585 EventType::PumpFeesRevokeFeeSharingAuthority,
586 EventType::PumpFeesTransferFeeSharingAuthority,
587 EventType::PumpFeesUpdateAdmin,
588 EventType::PumpFeesUpdateFeeConfig,
589 EventType::PumpFeesUpdateFeeShares,
590 EventType::PumpFeesUpsertFeeTiers,
591 ])
592 }
593
594 #[inline]
596 pub fn includes_pumpswap(&self) -> bool {
597 self.includes_any(&[
598 EventType::PumpSwapTrade,
599 EventType::PumpSwapBuy,
600 EventType::PumpSwapSell,
601 EventType::PumpSwapCreatePool,
602 EventType::PumpSwapSweepPoolFee,
603 EventType::PumpSwapLiquidityAdded,
604 EventType::PumpSwapLiquidityRemoved,
605 ])
606 }
607
608 #[inline]
610 pub fn includes_raydium_launchlab(&self) -> bool {
611 self.includes_any(&[
612 EventType::RaydiumLaunchlabTrade,
613 EventType::RaydiumLaunchlabPoolCreate,
614 EventType::RaydiumLaunchlabMigrateAmm,
615 ])
616 }
617
618 #[inline]
619 pub fn includes_raydium_cpmm(&self) -> bool {
620 self.includes_any(&[
621 EventType::RaydiumCpmmSwap,
622 EventType::RaydiumCpmmDeposit,
623 EventType::RaydiumCpmmWithdraw,
624 EventType::RaydiumCpmmInitialize,
625 EventType::RaydiumCpmmCollectCreatorFee,
626 ])
627 }
628
629 #[inline]
630 pub fn includes_raydium_clmm(&self) -> bool {
631 self.includes_any(&[
632 EventType::RaydiumClmmSwap,
633 EventType::RaydiumClmmCreatePool,
634 EventType::RaydiumClmmOpenPosition,
635 EventType::RaydiumClmmClosePosition,
636 EventType::RaydiumClmmIncreaseLiquidity,
637 EventType::RaydiumClmmDecreaseLiquidity,
638 EventType::RaydiumClmmLiquidityChange,
639 EventType::RaydiumClmmConfigChange,
640 EventType::RaydiumClmmCreatePersonalPosition,
641 EventType::RaydiumClmmLiquidityCalculate,
642 EventType::RaydiumClmmOpenLimitOrder,
643 EventType::RaydiumClmmIncreaseLimitOrder,
644 EventType::RaydiumClmmDecreaseLimitOrder,
645 EventType::RaydiumClmmSettleLimitOrder,
646 EventType::RaydiumClmmUpdateRewardInfos,
647 EventType::RaydiumClmmOpenPositionWithTokenExtNft,
648 EventType::RaydiumClmmCollectFee,
649 ])
650 }
651
652 #[inline]
653 pub fn includes_raydium_amm_v4(&self) -> bool {
654 self.includes_any(&[
655 EventType::RaydiumAmmV4Swap,
656 EventType::RaydiumAmmV4Deposit,
657 EventType::RaydiumAmmV4Withdraw,
658 EventType::RaydiumAmmV4Initialize2,
659 EventType::RaydiumAmmV4WithdrawPnl,
660 ])
661 }
662
663 #[inline]
664 pub fn includes_orca_whirlpool(&self) -> bool {
665 self.includes_any(&[
666 EventType::OrcaWhirlpoolSwap,
667 EventType::OrcaWhirlpoolLiquidityIncreased,
668 EventType::OrcaWhirlpoolLiquidityDecreased,
669 EventType::OrcaWhirlpoolPoolInitialized,
670 ])
671 }
672
673 #[inline]
674 pub fn includes_meteora_pools(&self) -> bool {
675 self.includes_any(&[
676 EventType::MeteoraPoolsSwap,
677 EventType::MeteoraPoolsAddLiquidity,
678 EventType::MeteoraPoolsRemoveLiquidity,
679 EventType::MeteoraPoolsBootstrapLiquidity,
680 EventType::MeteoraPoolsPoolCreated,
681 EventType::MeteoraPoolsSetPoolFees,
682 ])
683 }
684
685 #[inline]
686 pub fn includes_meteora_dlmm(&self) -> bool {
687 self.includes_any(&[
688 EventType::MeteoraDlmmSwap,
689 EventType::MeteoraDlmmAddLiquidity,
690 EventType::MeteoraDlmmRemoveLiquidity,
691 EventType::MeteoraDlmmInitializePool,
692 EventType::MeteoraDlmmInitializeBinArray,
693 EventType::MeteoraDlmmCreatePosition,
694 EventType::MeteoraDlmmClosePosition,
695 EventType::MeteoraDlmmClaimFee,
696 EventType::MeteoraDlmmClaimReward,
697 ])
698 }
699
700 #[inline]
701 pub fn includes_meteora_dbc(&self) -> bool {
702 self.includes_any(&[
703 EventType::MeteoraDbcSwap,
704 EventType::MeteoraDbcInitializePool,
705 EventType::MeteoraDbcCurveComplete,
706 ])
707 }
708}
709
710#[inline]
711fn pumpfun_trade_filter_is_generic(include_only: &[EventType]) -> bool {
712 include_only.contains(&EventType::PumpFunTrade)
713 && !include_only.iter().any(|t| {
714 matches!(
715 t,
716 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
717 )
718 })
719}
720
721#[inline]
722fn pumpfun_buy_filter_is_generic(include_only: &[EventType]) -> bool {
723 include_only.contains(&EventType::PumpFunBuy)
724 && !include_only.contains(&EventType::PumpFunBuyExactSolIn)
725}
726
727#[inline]
728fn is_pumpfun_create_family(event_type: EventType) -> bool {
729 matches!(event_type, EventType::PumpFunCreate | EventType::PumpFunCreateV2)
730}
731
732#[inline]
733pub fn event_type_from_dex_event(event: &crate::core::events::DexEvent) -> Option<EventType> {
734 use crate::core::events::DexEvent;
735 match event {
736 DexEvent::PumpFunCreate(_) => Some(EventType::PumpFunCreate),
737 DexEvent::PumpFunCreateV2(_) => Some(EventType::PumpFunCreateV2),
738 DexEvent::PumpFunTrade(_) => Some(EventType::PumpFunTrade),
739 DexEvent::PumpFunBuy(_) => Some(EventType::PumpFunBuy),
740 DexEvent::PumpFunSell(_) => Some(EventType::PumpFunSell),
741 DexEvent::PumpFunBuyExactSolIn(_) => Some(EventType::PumpFunBuyExactSolIn),
742 DexEvent::PumpFunMigrate(_) => Some(EventType::PumpFunMigrate),
743 DexEvent::PumpFeesCreateFeeSharingConfig(_) => {
744 Some(EventType::PumpFeesCreateFeeSharingConfig)
745 }
746 DexEvent::PumpFeesInitializeFeeConfig(_) => Some(EventType::PumpFeesInitializeFeeConfig),
747 DexEvent::PumpFeesResetFeeSharingConfig(_) => {
748 Some(EventType::PumpFeesResetFeeSharingConfig)
749 }
750 DexEvent::PumpFeesRevokeFeeSharingAuthority(_) => {
751 Some(EventType::PumpFeesRevokeFeeSharingAuthority)
752 }
753 DexEvent::PumpFeesTransferFeeSharingAuthority(_) => {
754 Some(EventType::PumpFeesTransferFeeSharingAuthority)
755 }
756 DexEvent::PumpFeesUpdateAdmin(_) => Some(EventType::PumpFeesUpdateAdmin),
757 DexEvent::PumpFeesUpdateFeeConfig(_) => Some(EventType::PumpFeesUpdateFeeConfig),
758 DexEvent::PumpFeesUpdateFeeShares(_) => Some(EventType::PumpFeesUpdateFeeShares),
759 DexEvent::PumpFeesUpsertFeeTiers(_) => Some(EventType::PumpFeesUpsertFeeTiers),
760 DexEvent::PumpFunPostCompleteBuy(_) => Some(EventType::PumpFunPostCompleteBuy),
761 DexEvent::PumpFunSweepBondingCurveFee(_) => Some(EventType::PumpFunSweepBondingCurveFee),
762 DexEvent::PumpFunComplete(_) => Some(EventType::PumpFunComplete),
763 DexEvent::PumpSwapSweepPoolFee(_) => Some(EventType::PumpSwapSweepPoolFee),
764 DexEvent::PumpFunMigrateBondingCurveCreator(_) => {
765 Some(EventType::PumpFunMigrateBondingCurveCreator)
766 }
767 DexEvent::PumpFunGlobalAccount(_) => Some(EventType::AccountPumpFunGlobal),
768 DexEvent::PumpFunBondingCurveAccount(_) => Some(EventType::AccountPumpFunBondingCurve),
769 DexEvent::PumpFunFeeConfigAccount(_) => Some(EventType::AccountPumpFunFeeConfig),
770 DexEvent::PumpFunSharingConfigAccount(_) => Some(EventType::AccountPumpFunSharingConfig),
771 DexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) => {
772 Some(EventType::AccountPumpFunGlobalVolumeAccumulator)
773 }
774 DexEvent::PumpFunUserVolumeAccumulatorAccount(_) => {
775 Some(EventType::AccountPumpFunUserVolumeAccumulator)
776 }
777 DexEvent::PumpSwapTrade(_) => Some(EventType::PumpSwapTrade),
778 DexEvent::PumpSwapBuy(_) => Some(EventType::PumpSwapBuy),
779 DexEvent::PumpSwapSell(_) => Some(EventType::PumpSwapSell),
780 DexEvent::PumpSwapCreatePool(_) => Some(EventType::PumpSwapCreatePool),
781 DexEvent::PumpSwapLiquidityAdded(_) => Some(EventType::PumpSwapLiquidityAdded),
782 DexEvent::PumpSwapLiquidityRemoved(_) => Some(EventType::PumpSwapLiquidityRemoved),
783 DexEvent::MeteoraDammV2Swap(_) => Some(EventType::MeteoraDammV2Swap),
784 DexEvent::MeteoraDammV2ClaimPositionFee(_) => {
785 Some(EventType::MeteoraDammV2ClaimPositionFee)
786 }
787 DexEvent::MeteoraDammV2ClaimReward(_) => Some(EventType::MeteoraDammV2ClaimReward),
788 DexEvent::MeteoraDlmmClaimReward(_) => Some(EventType::MeteoraDlmmClaimReward),
789 DexEvent::MeteoraDammV2CreatePosition(_) => Some(EventType::MeteoraDammV2CreatePosition),
790 DexEvent::MeteoraDammV2ClosePosition(_) => Some(EventType::MeteoraDammV2ClosePosition),
791 DexEvent::MeteoraDammV2AddLiquidity(_) => Some(EventType::MeteoraDammV2AddLiquidity),
792 DexEvent::MeteoraDammV2RemoveLiquidity(_) => Some(EventType::MeteoraDammV2RemoveLiquidity),
793 DexEvent::MeteoraDammV2InitializePool(_) => Some(EventType::MeteoraDammV2InitializePool),
794 DexEvent::MeteoraDammV2UpdateDelegatePermission(_) => {
795 Some(EventType::MeteoraDammV2UpdateDelegatePermission)
796 }
797 DexEvent::MeteoraDammV2WithdrawDeadLiquidityReward(_) => {
798 Some(EventType::MeteoraDammV2WithdrawDeadLiquidityReward)
799 }
800 DexEvent::MeteoraDammV2WithdrawIneligibleReward(_) => {
801 Some(EventType::MeteoraDammV2WithdrawIneligibleReward)
802 }
803 DexEvent::MeteoraDammV2UpdateRewardFunder(_) => {
804 Some(EventType::MeteoraDammV2UpdateRewardFunder)
805 }
806 DexEvent::MeteoraDammV2UpdateRewardDuration(_) => {
807 Some(EventType::MeteoraDammV2UpdateRewardDuration)
808 }
809 DexEvent::MeteoraDammV2InitializeReward(_) => {
810 Some(EventType::MeteoraDammV2InitializeReward)
811 }
812 DexEvent::MeteoraDammV2FundReward(_) => Some(EventType::MeteoraDammV2FundReward),
813 DexEvent::MeteoraDammV2CreateConfig(_) => Some(EventType::MeteoraDammV2CreateConfig),
814 DexEvent::MeteoraDammV2CreateDynamicConfig(_) => {
815 Some(EventType::MeteoraDammV2CreateDynamicConfig)
816 }
817 DexEvent::MeteoraDbcSwap(_) => Some(EventType::MeteoraDbcSwap),
818 DexEvent::MeteoraDbcInitializePool(_) => Some(EventType::MeteoraDbcInitializePool),
819 DexEvent::MeteoraDbcCurveComplete(_) => Some(EventType::MeteoraDbcCurveComplete),
820 DexEvent::RaydiumLaunchlabTrade(_) => Some(EventType::RaydiumLaunchlabTrade),
821 DexEvent::RaydiumLaunchlabPoolCreate(_) => Some(EventType::RaydiumLaunchlabPoolCreate),
822 DexEvent::RaydiumLaunchlabMigrateAmm(_) => Some(EventType::RaydiumLaunchlabMigrateAmm),
823 DexEvent::RaydiumClmmSwap(_) => Some(EventType::RaydiumClmmSwap),
824 DexEvent::RaydiumClmmCreatePool(_) => Some(EventType::RaydiumClmmCreatePool),
825 DexEvent::RaydiumClmmOpenPosition(_) => Some(EventType::RaydiumClmmOpenPosition),
826 DexEvent::RaydiumClmmOpenPositionWithTokenExtNft(_) => {
827 Some(EventType::RaydiumClmmOpenPositionWithTokenExtNft)
828 }
829 DexEvent::RaydiumClmmClosePosition(_) => Some(EventType::RaydiumClmmClosePosition),
830 DexEvent::RaydiumClmmIncreaseLiquidity(_) => Some(EventType::RaydiumClmmIncreaseLiquidity),
831 DexEvent::RaydiumClmmDecreaseLiquidity(_) => Some(EventType::RaydiumClmmDecreaseLiquidity),
832 DexEvent::RaydiumClmmLiquidityChange(_) => Some(EventType::RaydiumClmmLiquidityChange),
833 DexEvent::RaydiumClmmConfigChange(_) => Some(EventType::RaydiumClmmConfigChange),
834 DexEvent::RaydiumClmmCreatePersonalPosition(_) => {
835 Some(EventType::RaydiumClmmCreatePersonalPosition)
836 }
837 DexEvent::RaydiumClmmLiquidityCalculate(_) => {
838 Some(EventType::RaydiumClmmLiquidityCalculate)
839 }
840 DexEvent::RaydiumClmmOpenLimitOrder(_) => Some(EventType::RaydiumClmmOpenLimitOrder),
841 DexEvent::RaydiumClmmIncreaseLimitOrder(_) => {
842 Some(EventType::RaydiumClmmIncreaseLimitOrder)
843 }
844 DexEvent::RaydiumClmmDecreaseLimitOrder(_) => {
845 Some(EventType::RaydiumClmmDecreaseLimitOrder)
846 }
847 DexEvent::RaydiumClmmSettleLimitOrder(_) => Some(EventType::RaydiumClmmSettleLimitOrder),
848 DexEvent::RaydiumClmmUpdateRewardInfos(_) => Some(EventType::RaydiumClmmUpdateRewardInfos),
849 DexEvent::RaydiumClmmCollectFee(_) => Some(EventType::RaydiumClmmCollectFee),
850 DexEvent::RaydiumClmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumClmmAmmConfig),
851 DexEvent::RaydiumClmmPoolStateAccount(_) => Some(EventType::AccountRaydiumClmmPoolState),
852 DexEvent::RaydiumClmmTickArrayStateAccount(_) => {
853 Some(EventType::AccountRaydiumClmmTickArrayState)
854 }
855 DexEvent::RaydiumCpmmSwap(_) => Some(EventType::RaydiumCpmmSwap),
856 DexEvent::RaydiumCpmmDeposit(_) => Some(EventType::RaydiumCpmmDeposit),
857 DexEvent::RaydiumCpmmWithdraw(_) => Some(EventType::RaydiumCpmmWithdraw),
858 DexEvent::RaydiumCpmmInitialize(_) => Some(EventType::RaydiumCpmmInitialize),
859 DexEvent::RaydiumCpmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumCpmmAmmConfig),
860 DexEvent::RaydiumCpmmPoolStateAccount(_) => Some(EventType::AccountRaydiumCpmmPoolState),
861 DexEvent::RaydiumCpmmCollectCreatorFee(_) => Some(EventType::RaydiumCpmmCollectCreatorFee),
862 DexEvent::RaydiumCpmmCreatorFeeShareAccount(_) => {
863 Some(EventType::AccountRaydiumCpmmCreatorFeeShare)
864 }
865 DexEvent::RaydiumAmmV4Swap(_) => Some(EventType::RaydiumAmmV4Swap),
866 DexEvent::RaydiumAmmV4Deposit(_) => Some(EventType::RaydiumAmmV4Deposit),
867 DexEvent::RaydiumAmmV4Initialize2(_) => Some(EventType::RaydiumAmmV4Initialize2),
868 DexEvent::RaydiumAmmV4Withdraw(_) => Some(EventType::RaydiumAmmV4Withdraw),
869 DexEvent::RaydiumAmmV4WithdrawPnl(_) => Some(EventType::RaydiumAmmV4WithdrawPnl),
870 DexEvent::OrcaWhirlpoolSwap(_) => Some(EventType::OrcaWhirlpoolSwap),
871 DexEvent::OrcaWhirlpoolLiquidityIncreased(_) => {
872 Some(EventType::OrcaWhirlpoolLiquidityIncreased)
873 }
874 DexEvent::OrcaWhirlpoolLiquidityDecreased(_) => {
875 Some(EventType::OrcaWhirlpoolLiquidityDecreased)
876 }
877 DexEvent::OrcaWhirlpoolPoolInitialized(_) => Some(EventType::OrcaWhirlpoolPoolInitialized),
878 DexEvent::OrcaWhirlpoolAccount(_) => Some(EventType::AccountOrcaWhirlpool),
879 DexEvent::OrcaPositionAccount(_) => Some(EventType::AccountOrcaPosition),
880 DexEvent::OrcaTickArrayAccount(_) => Some(EventType::AccountOrcaTickArray),
881 DexEvent::LiquidityAccountSnapshot(_) => Some(EventType::AccountLiquiditySnapshot),
882 DexEvent::RawAccountSnapshot(_) => Some(EventType::AccountRawSnapshot),
883 DexEvent::OrcaFeeTierAccount(_) => Some(EventType::AccountOrcaFeeTier),
884 DexEvent::OrcaWhirlpoolsConfigAccount(_) => Some(EventType::AccountOrcaWhirlpoolsConfig),
885 DexEvent::MeteoraPoolsSwap(_) => Some(EventType::MeteoraPoolsSwap),
886 DexEvent::MeteoraPoolsAddLiquidity(_) => Some(EventType::MeteoraPoolsAddLiquidity),
887 DexEvent::MeteoraPoolsRemoveLiquidity(_) => Some(EventType::MeteoraPoolsRemoveLiquidity),
888 DexEvent::MeteoraPoolsBootstrapLiquidity(_) => {
889 Some(EventType::MeteoraPoolsBootstrapLiquidity)
890 }
891 DexEvent::MeteoraPoolsPoolCreated(_) => Some(EventType::MeteoraPoolsPoolCreated),
892 DexEvent::MeteoraPoolsSetPoolFees(_) => Some(EventType::MeteoraPoolsSetPoolFees),
893 DexEvent::MeteoraDlmmSwap(_) => Some(EventType::MeteoraDlmmSwap),
894 DexEvent::MeteoraDlmmAddLiquidity(_) => Some(EventType::MeteoraDlmmAddLiquidity),
895 DexEvent::MeteoraDlmmRemoveLiquidity(_) => Some(EventType::MeteoraDlmmRemoveLiquidity),
896 DexEvent::MeteoraDlmmInitializePool(_) => Some(EventType::MeteoraDlmmInitializePool),
897 DexEvent::MeteoraDlmmInitializeBinArray(_) => {
898 Some(EventType::MeteoraDlmmInitializeBinArray)
899 }
900 DexEvent::MeteoraDlmmCreatePosition(_) => Some(EventType::MeteoraDlmmCreatePosition),
901 DexEvent::MeteoraDlmmClosePosition(_) => Some(EventType::MeteoraDlmmClosePosition),
902 DexEvent::MeteoraDlmmClaimFee(_) => Some(EventType::MeteoraDlmmClaimFee),
903 DexEvent::TokenAccount(_) => Some(EventType::TokenAccount),
904 DexEvent::TokenInfo(_) => Some(EventType::TokenInfo),
905 DexEvent::NonceAccount(_) => Some(EventType::NonceAccount),
906 DexEvent::PumpSwapGlobalConfigAccount(_) => Some(EventType::AccountPumpSwapGlobalConfig),
907 DexEvent::PumpSwapPoolAccount(_) => Some(EventType::AccountPumpSwapPool),
908 DexEvent::BlockMeta(_) => Some(EventType::BlockMeta),
909 DexEvent::Error(_) => None,
910 }
911}
912
913#[cfg(test)]
914mod event_type_filter_tests {
915 use super::*;
916
917 #[test]
918 fn generic_trade_filters_cover_specific_trade_variants() {
919 let pump = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
920 assert!(pump.should_include(EventType::PumpFunTrade));
921 assert!(pump.should_include(EventType::PumpFunBuy));
922 assert!(pump.should_include(EventType::PumpFunSell));
923 assert!(pump.should_include(EventType::PumpFunBuyExactSolIn));
924
925 let pump_specific = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
926 assert!(!pump_specific.should_include(EventType::PumpFunTrade));
927 assert!(pump_specific.should_include(EventType::PumpFunBuy));
928 assert!(!pump_specific.should_include(EventType::PumpFunSell));
929 assert!(pump_specific.should_include(EventType::PumpFunBuyExactSolIn));
930
931 let pump_exact_buy = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
932 assert!(!pump_exact_buy.should_include(EventType::PumpFunTrade));
933 assert!(!pump_exact_buy.should_include(EventType::PumpFunBuy));
934 assert!(!pump_exact_buy.should_include(EventType::PumpFunSell));
935 assert!(pump_exact_buy.should_include(EventType::PumpFunBuyExactSolIn));
936
937 let pumpswap = EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]);
938 assert!(pumpswap.should_include(EventType::PumpSwapBuy));
939 assert!(pumpswap.should_include(EventType::PumpSwapSell));
940
941 let exclude_pumpswap = EventTypeFilter::exclude_types(vec![EventType::PumpSwapTrade]);
942 assert!(!exclude_pumpswap.should_include(EventType::PumpSwapBuy));
943 assert!(!exclude_pumpswap.should_include(EventType::PumpSwapSell));
944 }
945
946 #[test]
947 fn generic_pumpfun_trade_filter_normalizes_specific_variants() {
948 use crate::core::events::{DexEvent, PumpFunTradeEvent};
949
950 let filter = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
951 let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
952 assert!(matches!(filter.normalize_dex_event(event), DexEvent::PumpFunTrade(_)));
953
954 let specific_filter =
955 EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
956 let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
957 assert!(matches!(specific_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
958
959 let buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
960 let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
961 is_buy: true,
962 ..Default::default()
963 });
964 assert!(matches!(buy_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
965
966 let exact_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
967 let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
968 is_buy: true,
969 ..Default::default()
970 });
971 assert!(matches!(
972 exact_filter.normalize_dex_event(event),
973 DexEvent::PumpFunBuyExactSolIn(_)
974 ));
975
976 let create_and_trade_filter =
977 EventTypeFilter::include_only(vec![EventType::PumpFunCreate, EventType::PumpFunTrade]);
978 let event =
979 DexEvent::PumpFunSell(PumpFunTradeEvent { is_buy: false, ..Default::default() });
980 assert!(matches!(
981 create_and_trade_filter.normalize_dex_event(event),
982 DexEvent::PumpFunTrade(_)
983 ));
984 }
985
986 #[test]
987 fn all_protocol_groups_are_filterable() {
988 assert!(EventTypeFilter::include_only(vec![EventType::PumpFunTrade]).includes_pumpfun());
989 assert!(!EventTypeFilter::include_only(vec![EventType::AccountPumpFunGlobal])
990 .includes_pumpfun());
991 assert!(
992 !EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateAdmin]).includes_pumpfun()
993 );
994 assert!(EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]).includes_pumpswap());
995 assert!(EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateFeeShares])
996 .includes_pump_fees());
997 assert!(EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade])
998 .includes_raydium_launchlab());
999 assert!(
1000 EventTypeFilter::include_only(vec![EventType::RaydiumCpmmSwap]).includes_raydium_cpmm()
1001 );
1002 assert!(
1003 EventTypeFilter::include_only(vec![EventType::RaydiumClmmSwap]).includes_raydium_clmm()
1004 );
1005 assert!(!EventTypeFilter::include_only(vec![EventType::AccountRaydiumClmmPoolState])
1006 .includes_raydium_clmm());
1007 assert!(EventTypeFilter::include_only(vec![EventType::RaydiumAmmV4Swap])
1008 .includes_raydium_amm_v4());
1009 assert!(EventTypeFilter::include_only(vec![EventType::OrcaWhirlpoolSwap])
1010 .includes_orca_whirlpool());
1011 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraPoolsSwap])
1012 .includes_meteora_pools());
1013 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap])
1014 .includes_meteora_damm_v2());
1015 assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2InitializePool])
1016 .includes_meteora_damm_v2());
1017 assert!(
1018 EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]).includes_meteora_dlmm()
1019 );
1020 assert!(
1021 EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]).includes_meteora_dbc()
1022 );
1023 }
1024
1025 #[test]
1026 fn exclude_filters_do_not_skip_whole_protocol_groups() {
1027 let raydium = EventTypeFilter::exclude_types(vec![EventType::RaydiumCpmmSwap]);
1028 assert!(raydium.includes_raydium_cpmm());
1029 assert!(!raydium.should_include(EventType::RaydiumCpmmSwap));
1030 assert!(raydium.should_include(EventType::RaydiumCpmmDeposit));
1031
1032 let all_cpmm = EventTypeFilter::exclude_types(vec![
1033 EventType::RaydiumCpmmSwap,
1034 EventType::RaydiumCpmmDeposit,
1035 EventType::RaydiumCpmmWithdraw,
1036 EventType::RaydiumCpmmInitialize,
1037 EventType::RaydiumCpmmCollectCreatorFee,
1038 ]);
1039 assert!(!all_cpmm.includes_raydium_cpmm());
1040
1041 let all_launchlab = EventTypeFilter::exclude_types(vec![
1042 EventType::RaydiumLaunchlabTrade,
1043 EventType::RaydiumLaunchlabPoolCreate,
1044 EventType::RaydiumLaunchlabMigrateAmm,
1045 ]);
1046 assert!(!all_launchlab.includes_raydium_launchlab());
1047
1048 let pump = EventTypeFilter::exclude_types(vec![EventType::PumpFunBuy]);
1049 assert!(pump.includes_pumpfun());
1050 assert!(!pump.should_include(EventType::PumpFunBuy));
1051 assert!(!pump.should_include(EventType::PumpFunBuyExactSolIn));
1052 assert!(pump.should_include(EventType::PumpFunSell));
1053 }
1054}
1055
1056#[derive(Debug, Clone)]
1057pub struct SlotFilter {
1058 pub min_slot: Option<u64>,
1059 pub max_slot: Option<u64>,
1060}
1061
1062impl SlotFilter {
1063 pub fn new() -> Self {
1064 Self { min_slot: None, max_slot: None }
1065 }
1066
1067 pub fn min_slot(mut self, slot: u64) -> Self {
1068 self.min_slot = Some(slot);
1069 self
1070 }
1071
1072 pub fn max_slot(mut self, slot: u64) -> Self {
1073 self.max_slot = Some(slot);
1074 self
1075 }
1076}
1077
1078impl Default for SlotFilter {
1079 fn default() -> Self {
1080 Self::new()
1081 }
1082}