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