1use serde::{Deserialize, Serialize};
2use yellowstone_grpc_proto::geyser::SubscribeRequestFilterAccountsFilter;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
6pub enum OrderMode {
7 #[default]
9 Unordered,
10 Ordered,
14 StreamingOrdered,
18 MicroBatch,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClientConfig {
26 pub enable_metrics: bool,
28 pub connection_timeout_ms: u64,
30 pub request_timeout_ms: u64,
32 pub enable_tls: bool,
34 pub max_retries: u32,
35 pub retry_delay_ms: u64,
36 pub max_concurrent_streams: u32,
37 pub keep_alive_interval_ms: u64,
38 pub keep_alive_timeout_ms: u64,
39 pub buffer_size: usize,
40 pub order_mode: OrderMode,
42 pub order_timeout_ms: u64,
45 pub micro_batch_us: u64,
48}
49
50impl Default for ClientConfig {
51 fn default() -> Self {
52 Self {
53 enable_metrics: false,
54 connection_timeout_ms: 8000,
55 request_timeout_ms: 15000,
56 enable_tls: true,
57 max_retries: 3,
58 retry_delay_ms: 1000,
59 max_concurrent_streams: 100,
60 keep_alive_interval_ms: 30000,
61 keep_alive_timeout_ms: 5000,
62 buffer_size: 8192,
63 order_mode: OrderMode::Unordered,
64 order_timeout_ms: 100,
65 micro_batch_us: 100, }
67 }
68}
69
70impl ClientConfig {
71 pub fn low_latency() -> Self {
72 Self {
73 enable_metrics: false,
74 connection_timeout_ms: 5000,
75 request_timeout_ms: 10000,
76 enable_tls: true,
77 max_retries: 1,
78 retry_delay_ms: 100,
79 max_concurrent_streams: 200,
80 keep_alive_interval_ms: 10000,
81 keep_alive_timeout_ms: 2000,
82 buffer_size: 16384,
83 order_mode: OrderMode::Unordered,
84 order_timeout_ms: 50,
85 micro_batch_us: 50, }
87 }
88
89 pub fn high_throughput() -> Self {
90 Self {
91 enable_metrics: true,
92 connection_timeout_ms: 10000,
93 request_timeout_ms: 30000,
94 enable_tls: true,
95 max_retries: 5,
96 retry_delay_ms: 2000,
97 max_concurrent_streams: 500,
98 keep_alive_interval_ms: 60000,
99 keep_alive_timeout_ms: 10000,
100 buffer_size: 32768,
101 order_mode: OrderMode::Unordered,
102 order_timeout_ms: 200,
103 micro_batch_us: 200, }
105 }
106}
107
108#[derive(Debug, Clone)]
109pub struct TransactionFilter {
110 pub account_include: Vec<String>,
111 pub account_exclude: Vec<String>,
112 pub account_required: Vec<String>,
113}
114
115impl TransactionFilter {
116 pub fn new() -> Self {
117 Self {
118 account_include: Vec::new(),
119 account_exclude: Vec::new(),
120 account_required: Vec::new(),
121 }
122 }
123
124 pub fn include_account(mut self, account: impl Into<String>) -> Self {
125 self.account_include.push(account.into());
126 self
127 }
128
129 pub fn exclude_account(mut self, account: impl Into<String>) -> Self {
130 self.account_exclude.push(account.into());
131 self
132 }
133
134 pub fn require_account(mut self, account: impl Into<String>) -> Self {
135 self.account_required.push(account.into());
136 self
137 }
138
139 pub fn from_program_ids(program_ids: Vec<String>) -> Self {
141 Self {
142 account_include: program_ids,
143 account_exclude: Vec::new(),
144 account_required: Vec::new(),
145 }
146 }
147}
148
149impl Default for TransactionFilter {
150 fn default() -> Self {
151 Self::new()
152 }
153}
154
155#[derive(Debug, Clone)]
156pub struct AccountFilter {
157 pub account: Vec<String>,
158 pub owner: Vec<String>,
159 pub filters: Vec<SubscribeRequestFilterAccountsFilter>,
160}
161
162impl AccountFilter {
163 pub fn new() -> Self {
164 Self { account: Vec::new(), owner: Vec::new(), filters: Vec::new() }
165 }
166
167 pub fn add_account(mut self, account: impl Into<String>) -> Self {
168 self.account.push(account.into());
169 self
170 }
171
172 pub fn add_owner(mut self, owner: impl Into<String>) -> Self {
173 self.owner.push(owner.into());
174 self
175 }
176
177 pub fn add_filter(mut self, filter: SubscribeRequestFilterAccountsFilter) -> Self {
178 self.filters.push(filter);
179 self
180 }
181
182 pub fn from_program_owners(program_ids: Vec<String>) -> Self {
184 Self { account: Vec::new(), owner: program_ids, filters: Vec::new() }
185 }
186}
187
188impl Default for AccountFilter {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194#[derive(Debug, Clone)]
195pub struct AccountFilterData {
196 pub memcmp: Option<AccountFilterMemcmp>,
197 pub datasize: Option<u64>,
198}
199
200#[derive(Debug, Clone)]
201pub struct AccountFilterMemcmp {
202 pub offset: u64,
203 pub bytes: Vec<u8>,
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
207pub enum Protocol {
208 PumpFun,
209 PumpSwap,
210 Bonk,
211 RaydiumCpmm,
212 RaydiumClmm,
213 RaydiumAmmV4,
214 MeteoraDammV2,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum EventType {
219 BlockMeta,
221
222 BonkTrade,
224 BonkPoolCreate,
225 BonkMigrateAmm,
226
227 PumpFunTrade, PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn, PumpFunCreate,
233 PumpFunComplete,
234 PumpFunMigrate,
235
236 PumpSwapBuy,
238 PumpSwapSell,
239 PumpSwapCreatePool,
240 PumpSwapLiquidityAdded,
241 PumpSwapLiquidityRemoved,
242 MeteoraDammV2Swap,
284 MeteoraDammV2AddLiquidity,
285 MeteoraDammV2RemoveLiquidity,
286 MeteoraDammV2CreatePosition,
288 MeteoraDammV2ClosePosition,
289 TokenAccount,
296 NonceAccount,
297
298 AccountPumpSwapGlobalConfig,
299 AccountPumpSwapPool,
300}
301
302#[derive(Debug, Clone)]
303pub struct EventTypeFilter {
304 pub include_only: Option<Vec<EventType>>,
305 pub exclude_types: Option<Vec<EventType>>,
306}
307
308impl EventTypeFilter {
309 pub fn include_only(types: Vec<EventType>) -> Self {
310 Self { include_only: Some(types), exclude_types: None }
311 }
312
313 pub fn exclude_types(types: Vec<EventType>) -> Self {
314 Self { include_only: None, exclude_types: Some(types) }
315 }
316
317 pub fn should_include(&self, event_type: EventType) -> bool {
318 if let Some(ref include_only) = self.include_only {
319 if include_only.contains(&event_type) {
321 return true;
322 }
323 if event_type == EventType::PumpFunTrade {
327 return include_only.iter().any(|t| matches!(t,
328 EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
329 ));
330 }
331 return false;
332 }
333
334 if let Some(ref exclude_types) = self.exclude_types {
335 return !exclude_types.contains(&event_type);
336 }
337
338 true
339 }
340
341 #[inline]
342 pub fn includes_pumpfun(&self) -> bool {
343 if let Some(ref include_only) = self.include_only {
344 return include_only.iter().any(|t| {
345 matches!(
346 t,
347 EventType::PumpFunTrade
348 | EventType::PumpFunBuy
349 | EventType::PumpFunSell
350 | EventType::PumpFunBuyExactSolIn
351 | EventType::PumpFunCreate
352 | EventType::PumpFunComplete
353 | EventType::PumpFunMigrate
354 )
355 });
356 }
357
358 if let Some(ref exclude_types) = self.exclude_types {
359 return !exclude_types.iter().any(|t| {
360 matches!(
361 t,
362 EventType::PumpFunTrade
363 | EventType::PumpFunBuy
364 | EventType::PumpFunSell
365 | EventType::PumpFunBuyExactSolIn
366 | EventType::PumpFunCreate
367 | EventType::PumpFunComplete
368 | EventType::PumpFunMigrate
369 )
370 });
371 }
372
373 true
374 }
375
376 #[inline]
377 pub fn includes_meteora_damm_v2(&self) -> bool {
378 if let Some(ref include_only) = self.include_only {
379 return include_only.iter().any(|t| {
380 matches!(
381 t,
382 EventType::MeteoraDammV2Swap
383 | EventType::MeteoraDammV2AddLiquidity
384 | EventType::MeteoraDammV2CreatePosition
385 | EventType::MeteoraDammV2ClosePosition
386 | EventType::MeteoraDammV2RemoveLiquidity
387 )
388 });
389 }
390 if let Some(ref exclude_types) = self.exclude_types {
391 return !exclude_types.iter().any(|t| {
392 matches!(
393 t,
394 EventType::MeteoraDammV2Swap
395 | EventType::MeteoraDammV2AddLiquidity
396 | EventType::MeteoraDammV2CreatePosition
397 | EventType::MeteoraDammV2ClosePosition
398 | EventType::MeteoraDammV2RemoveLiquidity
399 )
400 });
401 }
402 true
403 }
404
405 #[inline]
407 pub fn includes_pumpswap(&self) -> bool {
408 if let Some(ref include_only) = self.include_only {
409 return include_only.iter().any(|t| {
410 matches!(
411 t,
412 EventType::PumpSwapBuy
413 | EventType::PumpSwapSell
414 | EventType::PumpSwapCreatePool
415 | EventType::PumpSwapLiquidityAdded
416 | EventType::PumpSwapLiquidityRemoved
417 )
418 });
419 }
420 if let Some(ref exclude_types) = self.exclude_types {
421 return !exclude_types.iter().any(|t| {
422 matches!(
423 t,
424 EventType::PumpSwapBuy
425 | EventType::PumpSwapSell
426 | EventType::PumpSwapCreatePool
427 | EventType::PumpSwapLiquidityAdded
428 | EventType::PumpSwapLiquidityRemoved
429 )
430 });
431 }
432 true
433 }
434
435 #[inline]
437 pub fn includes_raydium_launchpad(&self) -> bool {
438 if let Some(ref include_only) = self.include_only {
439 return include_only.iter().any(|t| {
440 matches!(
441 t,
442 EventType::BonkTrade
443 | EventType::BonkPoolCreate
444 | EventType::BonkMigrateAmm
445 )
446 });
447 }
448 if let Some(ref exclude_types) = self.exclude_types {
449 return !exclude_types.iter().any(|t| {
450 matches!(
451 t,
452 EventType::BonkTrade
453 | EventType::BonkPoolCreate
454 | EventType::BonkMigrateAmm
455 )
456 });
457 }
458 true
459 }
460}
461
462#[derive(Debug, Clone)]
463pub struct SlotFilter {
464 pub min_slot: Option<u64>,
465 pub max_slot: Option<u64>,
466}
467
468impl SlotFilter {
469 pub fn new() -> Self {
470 Self { min_slot: None, max_slot: None }
471 }
472
473 pub fn min_slot(mut self, slot: u64) -> Self {
474 self.min_slot = Some(slot);
475 self
476 }
477
478 pub fn max_slot(mut self, slot: u64) -> Self {
479 self.max_slot = Some(slot);
480 self
481 }
482}
483
484impl Default for SlotFilter {
485 fn default() -> Self {
486 Self::new()
487 }
488}