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: 8192,
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: 16384,
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: 32768,
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 Bonk,
227 RaydiumCpmm,
228 RaydiumClmm,
229 RaydiumAmmV4,
230 MeteoraDammV2,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum EventType {
235 BlockMeta,
237
238 BonkTrade,
240 BonkPoolCreate,
241 BonkMigrateAmm,
242
243 PumpFunTrade, PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn, PumpFunCreate,
249 PumpFunCreateV2, PumpFunComplete,
251 PumpFunMigrate,
252 PumpFeesCreateFeeSharingConfig,
254 PumpFeesInitializeFeeConfig,
255 PumpFeesResetFeeSharingConfig,
256 PumpFeesRevokeFeeSharingAuthority,
257 PumpFeesTransferFeeSharingAuthority,
258 PumpFeesUpdateAdmin,
259 PumpFeesUpdateFeeConfig,
260 PumpFeesUpdateFeeShares,
261 PumpFeesUpsertFeeTiers,
262 PumpFunMigrateBondingCurveCreator,
264
265 PumpSwapBuy,
267 PumpSwapSell,
268 PumpSwapCreatePool,
269 PumpSwapLiquidityAdded,
270 PumpSwapLiquidityRemoved,
271 MeteoraDammV2Swap,
313 MeteoraDammV2AddLiquidity,
314 MeteoraDammV2RemoveLiquidity,
315 MeteoraDammV2CreatePosition,
317 MeteoraDammV2ClosePosition,
318 TokenAccount,
325 NonceAccount,
326
327 AccountPumpSwapGlobalConfig,
328 AccountPumpSwapPool,
329}
330
331#[derive(Debug, Clone)]
332pub struct EventTypeFilter {
333 pub include_only: Option<Vec<EventType>>,
334 pub exclude_types: Option<Vec<EventType>>,
335}
336
337impl EventTypeFilter {
338 pub fn include_only(types: Vec<EventType>) -> Self {
339 Self { include_only: Some(types), exclude_types: None }
340 }
341
342 pub fn exclude_types(types: Vec<EventType>) -> Self {
343 Self { include_only: None, exclude_types: Some(types) }
344 }
345
346 pub fn should_include(&self, event_type: EventType) -> bool {
347 if let Some(ref include_only) = self.include_only {
348 if include_only.contains(&event_type) {
350 return true;
351 }
352 if event_type == EventType::PumpFunTrade {
356 return include_only.iter().any(|t| {
357 matches!(
358 t,
359 EventType::PumpFunBuy
360 | EventType::PumpFunSell
361 | EventType::PumpFunBuyExactSolIn
362 )
363 });
364 }
365 return false;
366 }
367
368 if let Some(ref exclude_types) = self.exclude_types {
369 return !exclude_types.contains(&event_type);
370 }
371
372 true
373 }
374
375 #[inline]
376 pub fn includes_pumpfun(&self) -> bool {
377 if let Some(ref include_only) = self.include_only {
378 return include_only.iter().any(|t| {
379 matches!(
380 t,
381 EventType::PumpFunTrade
382 | EventType::PumpFunBuy
383 | EventType::PumpFunSell
384 | EventType::PumpFunBuyExactSolIn
385 | EventType::PumpFunCreate
386 | EventType::PumpFunCreateV2
387 | EventType::PumpFunComplete
388 | EventType::PumpFunMigrate
389 | EventType::PumpFeesCreateFeeSharingConfig
390 | EventType::PumpFeesInitializeFeeConfig
391 | EventType::PumpFeesResetFeeSharingConfig
392 | EventType::PumpFeesRevokeFeeSharingAuthority
393 | EventType::PumpFeesTransferFeeSharingAuthority
394 | EventType::PumpFeesUpdateAdmin
395 | EventType::PumpFeesUpdateFeeConfig
396 | EventType::PumpFeesUpdateFeeShares
397 | EventType::PumpFeesUpsertFeeTiers
398 | EventType::PumpFunMigrateBondingCurveCreator
399 )
400 });
401 }
402
403 if let Some(ref exclude_types) = self.exclude_types {
404 return !exclude_types.iter().any(|t| {
405 matches!(
406 t,
407 EventType::PumpFunTrade
408 | EventType::PumpFunBuy
409 | EventType::PumpFunSell
410 | EventType::PumpFunBuyExactSolIn
411 | EventType::PumpFunCreate
412 | EventType::PumpFunCreateV2
413 | EventType::PumpFunComplete
414 | EventType::PumpFunMigrate
415 | EventType::PumpFeesCreateFeeSharingConfig
416 | EventType::PumpFeesInitializeFeeConfig
417 | EventType::PumpFeesResetFeeSharingConfig
418 | EventType::PumpFeesRevokeFeeSharingAuthority
419 | EventType::PumpFeesTransferFeeSharingAuthority
420 | EventType::PumpFeesUpdateAdmin
421 | EventType::PumpFeesUpdateFeeConfig
422 | EventType::PumpFeesUpdateFeeShares
423 | EventType::PumpFeesUpsertFeeTiers
424 | EventType::PumpFunMigrateBondingCurveCreator
425 )
426 });
427 }
428
429 true
430 }
431
432 #[inline]
433 pub fn includes_meteora_damm_v2(&self) -> bool {
434 if let Some(ref include_only) = self.include_only {
435 return include_only.iter().any(|t| {
436 matches!(
437 t,
438 EventType::MeteoraDammV2Swap
439 | EventType::MeteoraDammV2AddLiquidity
440 | EventType::MeteoraDammV2CreatePosition
441 | EventType::MeteoraDammV2ClosePosition
442 | EventType::MeteoraDammV2RemoveLiquidity
443 )
444 });
445 }
446 if let Some(ref exclude_types) = self.exclude_types {
447 return !exclude_types.iter().any(|t| {
448 matches!(
449 t,
450 EventType::MeteoraDammV2Swap
451 | EventType::MeteoraDammV2AddLiquidity
452 | EventType::MeteoraDammV2CreatePosition
453 | EventType::MeteoraDammV2ClosePosition
454 | EventType::MeteoraDammV2RemoveLiquidity
455 )
456 });
457 }
458 true
459 }
460
461 #[inline]
462 pub fn includes_pump_fees(&self) -> bool {
463 macro_rules! any_pfees {
464 () => {
465 EventType::PumpFeesCreateFeeSharingConfig
466 | EventType::PumpFeesInitializeFeeConfig
467 | EventType::PumpFeesResetFeeSharingConfig
468 | EventType::PumpFeesRevokeFeeSharingAuthority
469 | EventType::PumpFeesTransferFeeSharingAuthority
470 | EventType::PumpFeesUpdateAdmin
471 | EventType::PumpFeesUpdateFeeConfig
472 | EventType::PumpFeesUpdateFeeShares
473 | EventType::PumpFeesUpsertFeeTiers
474 };
475 }
476 if let Some(ref include_only) = self.include_only {
477 return include_only.iter().any(|t| matches!(t, any_pfees!()));
478 }
479 if let Some(ref exclude_types) = self.exclude_types {
480 return !exclude_types.iter().any(|t| matches!(t, any_pfees!()));
481 }
482 true
483 }
484
485 #[inline]
487 pub fn includes_pumpswap(&self) -> bool {
488 if let Some(ref include_only) = self.include_only {
489 return include_only.iter().any(|t| {
490 matches!(
491 t,
492 EventType::PumpSwapBuy
493 | EventType::PumpSwapSell
494 | EventType::PumpSwapCreatePool
495 | EventType::PumpSwapLiquidityAdded
496 | EventType::PumpSwapLiquidityRemoved
497 )
498 });
499 }
500 if let Some(ref exclude_types) = self.exclude_types {
501 return !exclude_types.iter().any(|t| {
502 matches!(
503 t,
504 EventType::PumpSwapBuy
505 | EventType::PumpSwapSell
506 | EventType::PumpSwapCreatePool
507 | EventType::PumpSwapLiquidityAdded
508 | EventType::PumpSwapLiquidityRemoved
509 )
510 });
511 }
512 true
513 }
514
515 #[inline]
517 pub fn includes_raydium_launchpad(&self) -> bool {
518 if let Some(ref include_only) = self.include_only {
519 return include_only.iter().any(|t| {
520 matches!(
521 t,
522 EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
523 )
524 });
525 }
526 if let Some(ref exclude_types) = self.exclude_types {
527 return !exclude_types.iter().any(|t| {
528 matches!(
529 t,
530 EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
531 )
532 });
533 }
534 true
535 }
536}
537
538#[derive(Debug, Clone)]
539pub struct SlotFilter {
540 pub min_slot: Option<u64>,
541 pub max_slot: Option<u64>,
542}
543
544impl SlotFilter {
545 pub fn new() -> Self {
546 Self { min_slot: None, max_slot: None }
547 }
548
549 pub fn min_slot(mut self, slot: u64) -> Self {
550 self.min_slot = Some(slot);
551 self
552 }
553
554 pub fn max_slot(mut self, slot: u64) -> Self {
555 self.max_slot = Some(slot);
556 self
557 }
558}
559
560impl Default for SlotFilter {
561 fn default() -> Self {
562 Self::new()
563 }
564}