kobe_client/
types.rs

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