Skip to main content

kobe_client/
types.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4pub mod bam_epoch_metrics;
5pub mod bam_validators;
6pub mod coinbase_balance;
7
8/// Staker rewards response from the API
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct StakerRewardsResponse {
11    pub rewards: Vec<StakerReward>,
12    pub total: Option<u64>,
13}
14
15/// Individual staker reward entry
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct StakerReward {
18    /// The public key of the stake account
19    pub stake_account: String,
20
21    /// The stake authority
22    pub stake_authority: String,
23
24    /// The withdraw authority
25    pub withdraw_authority: String,
26
27    /// Epoch when the reward was earned
28    pub epoch: u64,
29
30    /// MEV rewards in lamports
31    pub mev_rewards: u64,
32
33    /// Priority fee rewards in lamports
34    pub priority_fee_rewards: Option<u64>,
35
36    /// Whether MEV rewards have been claimed
37    pub mev_claimed: bool,
38
39    /// Whether priority fee rewards have been claimed
40    pub priority_fee_claimed: Option<bool>,
41
42    /// Validator vote account
43    pub vote_account: String,
44}
45
46/// Validator rewards response
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct ValidatorRewardsResponse {
49    pub validators: Vec<ValidatorReward>,
50}
51
52/// Validator reward entry
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ValidatorReward {
55    /// Validator vote account public key
56    pub vote_account: String,
57
58    /// Epoch
59    pub epoch: u64,
60
61    /// MEV commission in basis points (10000 = 100%)
62    pub mev_commission_bps: u16,
63
64    /// Total MEV rewards in lamports
65    pub mev_rewards: u64,
66
67    /// Priority fee commission in basis points
68    pub priority_fee_commission_bps: Option<u16>,
69
70    /// Total priority fee rewards in lamports
71    pub priority_fee_rewards: Option<u64>,
72
73    /// Number of stakers
74    pub num_stakers: Option<u64>,
75
76    /// Total active stake
77    pub active_stake: Option<u64>,
78}
79
80/// Response for validators endpoint
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ValidatorsResponse {
83    pub validators: Vec<ValidatorInfo>,
84}
85
86/// Validator information
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct ValidatorInfo {
89    /// Identity account
90    pub identity_account: Option<String>,
91
92    /// Validator vote account
93    pub vote_account: String,
94
95    /// MEV commission in basis points
96    pub mev_commission_bps: Option<u16>,
97
98    /// MEV rewards for the epoch (lamports)
99    pub mev_rewards: Option<u64>,
100
101    /// Priority fee commission in basis points
102    pub priority_fee_commission_bps: Option<u16>,
103
104    /// Priority fee rewards (lamports)
105    pub priority_fee_rewards: Option<u64>,
106
107    /// Whether the validator is running Jito
108    pub running_jito: bool,
109
110    /// Whether the validator is running BAM
111    pub running_bam: Option<bool>,
112
113    /// BAM connection rate
114    pub bam_connection_rate: Option<f64>,
115
116    /// Active stake amount (lamports)
117    pub active_stake: u64,
118}
119
120/// Validator data for a specific epoch
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct ValidatorByVoteAccount {
123    /// Epoch
124    pub epoch: u64,
125
126    /// MEV commission in basis points
127    pub mev_commission_bps: u16,
128
129    /// MEV rewards (lamports)
130    pub mev_rewards: u64,
131
132    /// Priority fee commission in basis points
133    pub priority_fee_commission_bps: u16,
134
135    /// Priority fee rewards (lamports)
136    pub priority_fee_rewards: u64,
137}
138
139/// MEV rewards network statistics
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct MevRewards {
142    /// Epoch number
143    pub epoch: u64,
144
145    /// Total network MEV in lamports
146    pub total_network_mev_lamports: u64,
147
148    /// Jito stake weight in lamports
149    pub jito_stake_weight_lamports: u64,
150
151    /// MEV reward per lamport staked
152    pub mev_reward_per_lamport: f64,
153}
154
155/// Daily MEV tips data
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct DailyMevRewards {
158    /// Date of the tips
159    pub day: DateTime<Utc>,
160
161    /// Number of MEV tips
162    pub count_mev_tips: u64,
163
164    /// Jito tips amount (SOL)
165    pub jito_tips: f64,
166
167    /// Number of unique tippers
168    pub tippers: u64,
169
170    /// Validator tips amount (SOL)
171    pub validator_tips: f64,
172}
173
174/// Jito stake over time data
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct JitoStakeOverTime {
177    /// Map of epoch to stake ratio
178    pub stake_ratio_over_time: std::collections::HashMap<String, f64>,
179}
180
181/// MEV commission average over time
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct MevCommissionAverageOverTime {
184    /// Aggregated MEV rewards
185    pub aggregated_mev_rewards: u64,
186
187    /// MEV rewards time series
188    pub mev_rewards: Vec<TimeSeriesData<u64>>,
189
190    /// Total value locked time series
191    pub tvl: Vec<TimeSeriesData<u64>>,
192
193    /// APY time series
194    pub apy: Vec<TimeSeriesData<f64>>,
195
196    /// Number of validators time series
197    pub num_validators: Vec<TimeSeriesData<u64>>,
198
199    /// JitoSOL supply time series
200    pub supply: Vec<TimeSeriesData<f64>>,
201}
202
203/// Time series data point
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct TimeSeriesData<T> {
206    /// Data value
207    pub data: T,
208
209    /// Timestamp
210    pub date: DateTime<Utc>,
211}
212
213/// JitoSOL to SOL ratio data
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct JitoSolRatio {
216    /// Time series of ratio data
217    pub ratios: Vec<TimeSeriesData<f64>>,
218}
219
220/// Stake pool statistics response
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct StakePoolStats {
223    /// Total aggregated MEV rewards across all time periods (lamports)
224    pub aggregated_mev_rewards: u64,
225
226    /// Time series data of MEV rewards
227    pub mev_rewards: Vec<TimeSeriesData<u64>>,
228
229    /// Time series data of Total Value Locked (lamports)
230    pub tvl: Vec<TimeSeriesData<u64>>,
231
232    /// Time series data of Annual Percentage Yield (decimal, e.g., 0.07 = 7%)
233    pub apy: Vec<TimeSeriesData<f64>>,
234
235    /// Time series data of validator count
236    pub num_validators: Vec<TimeSeriesData<u64>>,
237
238    /// Time series data of JitoSOL token supply
239    pub supply: Vec<TimeSeriesData<f64>>,
240}
241
242/// BAM Delegation Blacklist response
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct BamDelegationBlacklistEntry {
245    /// Vote account address
246    pub vote_account: String,
247
248    /// Added epoch
249    pub added_epoch: u64,
250}
251
252// ============================================================================
253// Request Types
254// ============================================================================
255
256/// Request parameters for epoch-based queries
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct EpochRequest {
259    /// Epoch number
260    pub epoch: u64,
261}
262
263/// Range filter for time-based queries
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct RangeFilter {
266    /// Start time (ISO 8601 format)
267    pub start: DateTime<Utc>,
268
269    /// End time (ISO 8601 format)
270    pub end: DateTime<Utc>,
271}
272
273/// Request with range filter
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct RangeRequest {
276    /// Time range filter
277    pub range_filter: RangeFilter,
278}
279
280/// Sort configuration for stake pool stats
281#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct SortBy {
283    /// Sort field (currently only "BlockTime" is supported)
284    pub field: String,
285
286    /// Sort order: "Asc" or "Desc"
287    pub order: String,
288}
289
290impl Default for SortBy {
291    fn default() -> Self {
292        Self {
293            field: "BlockTime".to_string(),
294            order: "Asc".to_string(),
295        }
296    }
297}
298
299/// Request for stake pool statistics
300#[derive(Debug, Clone, Serialize, Deserialize, Default)]
301pub struct StakePoolStatsRequest {
302    /// Time bucket aggregation type (currently only "Daily" is supported)
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub bucket_type: Option<String>,
305
306    /// Date range filter
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub range_filter: Option<RangeFilter>,
309
310    /// Sort configuration
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub sort_by: Option<SortBy>,
313}
314
315impl StakePoolStatsRequest {
316    /// Create a new request with default values
317    pub fn new() -> Self {
318        Self::default()
319    }
320
321    /// Set bucket type (currently only "Daily" is supported)
322    pub fn with_bucket_type(mut self, bucket_type: impl Into<String>) -> Self {
323        self.bucket_type = Some(bucket_type.into());
324        self
325    }
326
327    /// Set range filter
328    pub fn with_range_filter(mut self, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
329        self.range_filter = Some(RangeFilter { start, end });
330        self
331    }
332
333    /// Set sort configuration
334    pub fn with_sort_by(mut self, field: impl Into<String>, order: impl Into<String>) -> Self {
335        self.sort_by = Some(SortBy {
336            field: field.into(),
337            order: order.into(),
338        });
339        self
340    }
341
342    /// Set sort order to ascending
343    pub fn sort_asc(mut self) -> Self {
344        if let Some(ref mut sort) = self.sort_by {
345            sort.order = "Asc".to_string();
346        } else {
347            self.sort_by = Some(SortBy {
348                field: "BlockTime".to_string(),
349                order: "Asc".to_string(),
350            });
351        }
352        self
353    }
354
355    /// Set sort order to descending
356    pub fn sort_desc(mut self) -> Self {
357        if let Some(ref mut sort) = self.sort_by {
358            sort.order = "Desc".to_string();
359        } else {
360            self.sort_by = Some(SortBy {
361                field: "BlockTime".to_string(),
362                order: "Desc".to_string(),
363            });
364        }
365        self
366    }
367}
368
369/// Validator history account data
370#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct ValidatorHistoryAccount {
372    /// Validator vote account
373    pub vote_account: String,
374
375    /// Historical entries
376    pub history: Vec<ValidatorHistoryEntry>,
377}
378
379/// Single validator history entry for an epoch
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct ValidatorHistoryEntry {
382    /// Epoch
383    pub epoch: u64,
384
385    /// Vote credits earned
386    pub vote_credits: Option<u32>,
387
388    /// Validator commission
389    pub commission: Option<u8>,
390
391    /// MEV commission in basis points
392    pub mev_commission_bps: Option<u16>,
393
394    /// Validator version
395    pub version: Option<String>,
396
397    /// Client type
398    pub client_type: Option<String>,
399
400    /// Active stake
401    pub active_stake: Option<u64>,
402
403    /// Stake rank
404    pub stake_rank: Option<u32>,
405
406    /// Whether validator is in superminority
407    pub is_superminority: Option<bool>,
408
409    /// IP address
410    pub ip_address: Option<String>,
411}
412
413/// Steward configuration
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct StewardConfig {
416    /// Stake pool address
417    pub stake_pool: String,
418
419    /// Authority
420    pub authority: String,
421
422    /// Scoring parameters
423    pub scoring_params: ScoringParams,
424}
425
426/// Scoring parameters for validator selection
427#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct ScoringParams {
429    /// Minimum vote credits
430    pub min_vote_credits: u32,
431
432    /// Maximum commission
433    pub max_commission: u8,
434
435    /// Performance weight
436    pub performance_weight: f64,
437
438    /// Commission weight
439    pub commission_weight: f64,
440
441    /// Stake concentration limit
442    pub stake_concentration_limit: f64,
443}
444
445// ============================================================================
446// Common Types
447// ============================================================================
448
449/// Query parameters for paginated requests
450#[derive(Debug, Clone, Default)]
451pub struct QueryParams {
452    /// Limit number of results
453    pub limit: Option<u32>,
454
455    /// Offset for pagination
456    pub offset: Option<u32>,
457
458    /// Epoch filter
459    pub epoch: Option<u64>,
460
461    /// Sort order (asc/desc)
462    pub sort_order: Option<String>,
463}
464
465impl QueryParams {
466    /// Create new query params with limit
467    pub fn with_limit(limit: u32) -> Self {
468        Self {
469            limit: Some(limit),
470            ..Default::default()
471        }
472    }
473
474    /// Create new query params with epoch
475    pub fn with_epoch(epoch: u64) -> Self {
476        Self {
477            epoch: Some(epoch),
478            ..Default::default()
479        }
480    }
481
482    /// Set limit
483    pub fn limit(mut self, limit: u32) -> Self {
484        self.limit = Some(limit);
485        self
486    }
487
488    /// Set offset
489    pub fn offset(mut self, offset: u32) -> Self {
490        self.offset = Some(offset);
491        self
492    }
493
494    /// Set epoch
495    pub fn epoch(mut self, epoch: u64) -> Self {
496        self.epoch = Some(epoch);
497        self
498    }
499
500    /// Convert to query string
501    pub fn to_query_string(&self) -> String {
502        let mut params = Vec::new();
503
504        if let Some(limit) = self.limit {
505            params.push(format!("limit={}", limit));
506        }
507        if let Some(offset) = self.offset {
508            params.push(format!("offset={}", offset));
509        }
510        if let Some(epoch) = self.epoch {
511            params.push(format!("epoch={}", epoch));
512        }
513        if let Some(ref sort_order) = self.sort_order {
514            params.push(format!("sort_order={}", sort_order));
515        }
516
517        if params.is_empty() {
518            String::new()
519        } else {
520            format!("?{}", params.join("&"))
521        }
522    }
523}
524
525// ============================================================================
526// Error Response Types
527// ============================================================================
528
529/// API error response structure
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct ApiErrorResponse {
532    pub error: String,
533    pub message: Option<String>,
534    pub status_code: Option<u16>,
535}