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