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