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
253 PumpSwapBuy,
255 PumpSwapSell,
256 PumpSwapCreatePool,
257 PumpSwapLiquidityAdded,
258 PumpSwapLiquidityRemoved,
259 MeteoraDammV2Swap,
301 MeteoraDammV2AddLiquidity,
302 MeteoraDammV2RemoveLiquidity,
303 MeteoraDammV2CreatePosition,
305 MeteoraDammV2ClosePosition,
306 TokenAccount,
313 NonceAccount,
314
315 AccountPumpSwapGlobalConfig,
316 AccountPumpSwapPool,
317}
318
319#[derive(Debug, Clone)]
320pub struct EventTypeFilter {
321 pub include_only: Option<Vec<EventType>>,
322 pub exclude_types: Option<Vec<EventType>>,
323}
324
325impl EventTypeFilter {
326 pub fn include_only(types: Vec<EventType>) -> Self {
327 Self { include_only: Some(types), exclude_types: None }
328 }
329
330 pub fn exclude_types(types: Vec<EventType>) -> Self {
331 Self { include_only: None, exclude_types: Some(types) }
332 }
333
334 pub fn should_include(&self, event_type: EventType) -> bool {
335 if let Some(ref include_only) = self.include_only {
336 if include_only.contains(&event_type) {
338 return true;
339 }
340 if event_type == EventType::PumpFunTrade {
344 return include_only.iter().any(|t| matches!(t,
345 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
346 ));
347 }
348 return false;
349 }
350
351 if let Some(ref exclude_types) = self.exclude_types {
352 return !exclude_types.contains(&event_type);
353 }
354
355 true
356 }
357
358 #[inline]
359 pub fn includes_pumpfun(&self) -> bool {
360 if let Some(ref include_only) = self.include_only {
361 return include_only.iter().any(|t| {
362 matches!(
363 t,
364 EventType::PumpFunTrade
365 | EventType::PumpFunBuy
366 | EventType::PumpFunSell
367 | EventType::PumpFunBuyExactSolIn
368 | EventType::PumpFunCreate
369 | EventType::PumpFunCreateV2
370 | EventType::PumpFunComplete
371 | EventType::PumpFunMigrate
372 )
373 });
374 }
375
376 if let Some(ref exclude_types) = self.exclude_types {
377 return !exclude_types.iter().any(|t| {
378 matches!(
379 t,
380 EventType::PumpFunTrade
381 | EventType::PumpFunBuy
382 | EventType::PumpFunSell
383 | EventType::PumpFunBuyExactSolIn
384 | EventType::PumpFunCreate
385 | EventType::PumpFunCreateV2
386 | EventType::PumpFunComplete
387 | EventType::PumpFunMigrate
388 )
389 });
390 }
391
392 true
393 }
394
395 #[inline]
396 pub fn includes_meteora_damm_v2(&self) -> bool {
397 if let Some(ref include_only) = self.include_only {
398 return include_only.iter().any(|t| {
399 matches!(
400 t,
401 EventType::MeteoraDammV2Swap
402 | EventType::MeteoraDammV2AddLiquidity
403 | EventType::MeteoraDammV2CreatePosition
404 | EventType::MeteoraDammV2ClosePosition
405 | EventType::MeteoraDammV2RemoveLiquidity
406 )
407 });
408 }
409 if let Some(ref exclude_types) = self.exclude_types {
410 return !exclude_types.iter().any(|t| {
411 matches!(
412 t,
413 EventType::MeteoraDammV2Swap
414 | EventType::MeteoraDammV2AddLiquidity
415 | EventType::MeteoraDammV2CreatePosition
416 | EventType::MeteoraDammV2ClosePosition
417 | EventType::MeteoraDammV2RemoveLiquidity
418 )
419 });
420 }
421 true
422 }
423
424 #[inline]
426 pub fn includes_pumpswap(&self) -> bool {
427 if let Some(ref include_only) = self.include_only {
428 return include_only.iter().any(|t| {
429 matches!(
430 t,
431 EventType::PumpSwapBuy
432 | EventType::PumpSwapSell
433 | EventType::PumpSwapCreatePool
434 | EventType::PumpSwapLiquidityAdded
435 | EventType::PumpSwapLiquidityRemoved
436 )
437 });
438 }
439 if let Some(ref exclude_types) = self.exclude_types {
440 return !exclude_types.iter().any(|t| {
441 matches!(
442 t,
443 EventType::PumpSwapBuy
444 | EventType::PumpSwapSell
445 | EventType::PumpSwapCreatePool
446 | EventType::PumpSwapLiquidityAdded
447 | EventType::PumpSwapLiquidityRemoved
448 )
449 });
450 }
451 true
452 }
453
454 #[inline]
456 pub fn includes_raydium_launchpad(&self) -> bool {
457 if let Some(ref include_only) = self.include_only {
458 return include_only.iter().any(|t| {
459 matches!(
460 t,
461 EventType::BonkTrade
462 | EventType::BonkPoolCreate
463 | EventType::BonkMigrateAmm
464 )
465 });
466 }
467 if let Some(ref exclude_types) = self.exclude_types {
468 return !exclude_types.iter().any(|t| {
469 matches!(
470 t,
471 EventType::BonkTrade
472 | EventType::BonkPoolCreate
473 | EventType::BonkMigrateAmm
474 )
475 });
476 }
477 true
478 }
479}
480
481#[derive(Debug, Clone)]
482pub struct SlotFilter {
483 pub min_slot: Option<u64>,
484 pub max_slot: Option<u64>,
485}
486
487impl SlotFilter {
488 pub fn new() -> Self {
489 Self { min_slot: None, max_slot: None }
490 }
491
492 pub fn min_slot(mut self, slot: u64) -> Self {
493 self.min_slot = Some(slot);
494 self
495 }
496
497 pub fn max_slot(mut self, slot: u64) -> Self {
498 self.max_slot = Some(slot);
499 self
500 }
501}
502
503impl Default for SlotFilter {
504 fn default() -> Self {
505 Self::new()
506 }
507}