use reqwest::Client;
use crate::error::{Result, StakewizError};
use crate::models::*;
use crate::params::QueryParams;
const BASE_URL: &str = "https://api.stakewiz.com";
#[derive(Debug, Clone)]
pub struct StakewizClient {
http: Client,
base_url: String,
}
impl Default for StakewizClient {
fn default() -> Self {
Self::new()
}
}
impl StakewizClient {
pub fn new() -> Self {
Self {
http: Client::new(),
base_url: BASE_URL.to_string(),
}
}
pub fn with_http_client(http: Client) -> Self {
Self {
http,
base_url: BASE_URL.to_string(),
}
}
pub fn with_base_url(mut self, url: &str) -> Self {
self.base_url = url.trim_end_matches('/').to_string();
self
}
async fn get_json<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
let url = format!("{}{}", self.base_url, path);
let resp = self.http.get(&url).send().await?;
let status = resp.status();
if !status.is_success() {
let body = resp.text().await.unwrap_or_default();
return Err(StakewizError::Api(format!("HTTP {status}: {body}")));
}
Ok(resp.json::<T>().await?)
}
async fn get_json_with_params<T: serde::de::DeserializeOwned>(
&self,
path: &str,
params: &QueryParams,
) -> Result<T> {
let url = format!("{}{}", self.base_url, path);
let pairs = params.to_query_pairs();
let resp = self.http.get(&url).query(&pairs).send().await?;
let status = resp.status();
if !status.is_success() {
let body = resp.text().await.unwrap_or_default();
return Err(StakewizError::Api(format!("HTTP {status}: {body}")));
}
Ok(resp.json::<T>().await?)
}
pub async fn get_validators(&self, params: Option<&QueryParams>) -> Result<Vec<Validator>> {
match params {
Some(p) => self.get_json_with_params("/validators", p).await,
None => self.get_json("/validators").await,
}
}
pub async fn get_validator(&self, vote_identity: &str) -> Result<Validator> {
self.get_json(&format!("/validator/{vote_identity}")).await
}
pub async fn get_validator_delinquencies(
&self,
vote_identity: &str,
params: Option<&QueryParams>,
) -> Result<Vec<Delinquency>> {
let path = format!("/validator_delinquencies/{vote_identity}");
match params {
Some(p) => self.get_json_with_params(&path, p).await,
None => self.get_json(&path).await,
}
}
pub async fn get_validator_total_stakes(
&self,
vote_identity: &str,
params: Option<&QueryParams>,
) -> Result<Vec<EpochStake>> {
let path = format!("/validator_total_stakes/{vote_identity}");
match params {
Some(p) => self.get_json_with_params(&path, p).await,
None => self.get_json(&path).await,
}
}
pub async fn get_validator_epoch_stakes_summary(
&self,
vote_identity: &str,
) -> Result<Vec<StakeChanges>> {
self.get_json(&format!("/validator_epoch_stakes/{vote_identity}"))
.await
}
pub async fn get_validator_stakes(
&self,
vote_identity: &str,
) -> Result<Vec<StakeAccount>> {
self.get_json(&format!("/validator_stakes/{vote_identity}"))
.await
}
pub async fn get_validator_wiz_scores(
&self,
vote_identity: &str,
) -> Result<Vec<HistoricWizScore>> {
self.get_json(&format!("/validator_wiz_scores/{vote_identity}"))
.await
}
pub async fn get_validator_vote_success(
&self,
vote_identity: &str,
params: Option<&QueryParams>,
) -> Result<Vec<VoteSuccess>> {
let path = format!("/validator_vote_success/{vote_identity}");
match params {
Some(p) => self.get_json_with_params(&path, p).await,
None => self.get_json(&path).await,
}
}
pub async fn get_validator_skip_rate(
&self,
vote_identity: &str,
params: Option<&QueryParams>,
) -> Result<Vec<SkipRate>> {
let path = format!("/validator_skip_rate/{vote_identity}");
match params {
Some(p) => self.get_json_with_params(&path, p).await,
None => self.get_json(&path).await,
}
}
pub async fn get_cluster_stats(&self) -> Result<ClusterStats> {
self.get_json("/cluster_stats").await
}
pub async fn get_wiz_score_weightings(&self) -> Result<WizScoreWeightings> {
self.get_json("/wiz_score").await
}
pub async fn get_epoch_info(&self) -> Result<EpochInfo> {
self.get_json("/epoch_info").await
}
pub async fn get_epoch_history(&self, epoch: u64) -> Result<Option<EpochHistory>> {
let url = format!("{}/epoch_history/{epoch}", self.base_url);
let resp = self.http.get(&url).send().await?;
let text = resp.text().await?;
if text.trim() == "false" {
return Ok(None);
}
Ok(Some(serde_json::from_str(&text)?))
}
pub async fn get_all_epochs_history(&self) -> Result<Vec<EpochHistoryEntry>> {
self.get_json("/all_epochs_history").await
}
pub async fn get_stake_validators_by_withdraw_authority(
&self,
pubkey: &str,
) -> Result<Vec<String>> {
self.get_json(&format!(
"/stake_validators_by_withdraw_authority/{pubkey}"
))
.await
}
}