use std::collections::HashMap;
use bytes::Bytes;
use num_bigint::BigInt;
use reqwest::Method;
use serde::{Deserialize, Deserializer};
use crate::client::request;
use crate::swarm::Error;
use super::DebugApi;
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Balance {
pub peer: String,
#[serde(deserialize_with = "deserialize_bigint")]
pub balance: BigInt,
}
fn deserialize_bigint<'de, D>(d: D) -> Result<BigInt, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(d)?;
if s.is_empty() {
return Ok(BigInt::from(0));
}
s.parse::<BigInt>().map_err(serde::de::Error::custom)
}
fn deserialize_opt_bigint<'de, D>(d: D) -> Result<Option<BigInt>, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(d)?;
if s.is_empty() {
return Ok(None);
}
s.parse::<BigInt>()
.map(Some)
.map_err(serde::de::Error::custom)
}
#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PeerAccounting {
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub balance: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub consumed_balance: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub threshold_received: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub threshold_given: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub current_threshold_received: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub current_threshold_given: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub surplus_balance: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub reserved_balance: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub shadow_reserved_balance: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub ghost_balance: Option<BigInt>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RedistributionState {
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub minimum_gas_funds: Option<BigInt>,
pub has_sufficient_funds: bool,
pub is_frozen: bool,
pub is_fully_synced: bool,
pub phase: String,
pub round: u64,
pub last_won_round: u64,
pub last_played_round: u64,
pub last_frozen_round: u64,
pub last_selected_round: u64,
pub last_sample_duration_seconds: u64,
pub block: u64,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub reward: Option<BigInt>,
#[serde(default, deserialize_with = "deserialize_opt_bigint")]
pub fees: Option<BigInt>,
pub is_healthy: bool,
}
impl DebugApi {
pub async fn balances(&self) -> Result<Vec<Balance>, Error> {
let builder = request(&self.inner, Method::GET, "balances")?;
#[derive(Deserialize)]
struct Resp {
balances: Vec<Balance>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.balances)
}
pub async fn peer_balance(&self, address: &str) -> Result<Balance, Error> {
let path = format!("balances/{address}");
let builder = request(&self.inner, Method::GET, &path)?;
self.inner.send_json(builder).await
}
pub async fn consumed_balances(&self) -> Result<Vec<Balance>, Error> {
let builder = request(&self.inner, Method::GET, "consumed")?;
#[derive(Deserialize)]
struct Resp {
balances: Vec<Balance>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.balances)
}
pub async fn peer_consumed_balance(&self, address: &str) -> Result<Balance, Error> {
let path = format!("consumed/{address}");
let builder = request(&self.inner, Method::GET, &path)?;
self.inner.send_json(builder).await
}
pub async fn accounting(&self) -> Result<HashMap<String, PeerAccounting>, Error> {
let builder = request(&self.inner, Method::GET, "accounting")?;
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "peerData")]
peer_data: HashMap<String, PeerAccounting>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.peer_data)
}
pub async fn stake(&self) -> Result<BigInt, Error> {
let builder = request(&self.inner, Method::GET, "stake")?;
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "stakedAmount")]
staked_amount: String,
}
let r: Resp = self.inner.send_json(builder).await?;
r.staked_amount.parse::<BigInt>().map_err(|e| {
Error::argument(format!("invalid stakedAmount {:?}: {e}", r.staked_amount))
})
}
pub async fn deposit_stake(&self, amount: &BigInt) -> Result<String, Error> {
let path = format!("stake/{amount}");
let builder = request(&self.inner, Method::POST, &path)?;
tx_hash_response(&self.inner, builder).await
}
pub async fn withdrawable_stake(&self) -> Result<BigInt, Error> {
let builder = request(&self.inner, Method::GET, "stake/withdrawable")?;
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "withdrawableAmount")]
withdrawable_amount: String,
}
let r: Resp = self.inner.send_json(builder).await?;
r.withdrawable_amount.parse::<BigInt>().map_err(|e| {
Error::argument(format!(
"invalid withdrawableAmount {:?}: {e}",
r.withdrawable_amount
))
})
}
pub async fn withdraw_surplus_stake(&self) -> Result<String, Error> {
let builder = request(&self.inner, Method::DELETE, "stake/withdrawable")?;
tx_hash_response(&self.inner, builder).await
}
pub async fn migrate_stake(&self) -> Result<String, Error> {
let builder = request(&self.inner, Method::DELETE, "stake")?;
tx_hash_response(&self.inner, builder).await
}
pub async fn redistribution_state(&self) -> Result<RedistributionState, Error> {
let builder = request(&self.inner, Method::GET, "redistributionstate")?;
self.inner.send_json(builder).await
}
}
async fn tx_hash_response(
inner: &crate::client::Inner,
builder: reqwest::RequestBuilder,
) -> Result<String, Error> {
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "txHash")]
tx_hash: String,
}
let resp = inner.send(builder).await?;
let bytes: Bytes = resp.bytes().await?;
let r: Resp = serde_json::from_slice(&bytes)?;
Ok(r.tx_hash)
}