use bytes::Bytes;
use num_bigint::BigInt;
use reqwest::Method;
use serde::{Deserialize, Deserializer};
use crate::client::{Inner, request};
use crate::swarm::Error;
use super::DebugApi;
fn de_bigint<'de, D: Deserializer<'de>>(d: D) -> Result<BigInt, D::Error> {
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 de_opt_bigint<'de, D: Deserializer<'de>>(d: D) -> Result<Option<BigInt>, D::Error> {
let s: Option<String> = Deserialize::deserialize(d)?;
match s {
None => Ok(None),
Some(s) if s.is_empty() => Ok(None),
Some(s) => s
.parse::<BigInt>()
.map(Some)
.map_err(serde::de::Error::custom),
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Wallet {
#[serde(default)]
pub bzz_address: Option<String>,
#[serde(default)]
pub native_address: Option<String>,
#[serde(default, alias = "chequebook")]
pub chequebook_contract_address: Option<String>,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub bzz_balance: Option<BigInt>,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub native_token_balance: Option<BigInt>,
#[serde(rename = "chainID")]
pub chain_id: i64,
pub wallet_address: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChequebookBalance {
#[serde(deserialize_with = "de_bigint")]
pub total_balance: BigInt,
#[serde(deserialize_with = "de_bigint")]
pub available_balance: BigInt,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Settlement {
pub peer: String,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub received: Option<BigInt>,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub sent: Option<BigInt>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Settlements {
#[serde(default, deserialize_with = "de_opt_bigint")]
pub total_received: Option<BigInt>,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub total_sent: Option<BigInt>,
#[serde(default)]
pub settlements: Vec<Settlement>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Cheque {
pub beneficiary: String,
pub chequebook: String,
#[serde(default, deserialize_with = "de_opt_bigint")]
pub payout: Option<BigInt>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct LastCheque {
pub peer: String,
#[serde(default, rename = "lastreceived")]
pub last_received: Option<Cheque>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct PeerCheques {
pub peer: String,
#[serde(default, rename = "lastreceived")]
pub last_received: Option<Cheque>,
#[serde(default, rename = "lastsent")]
pub last_sent: Option<Cheque>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct CashoutResult {
pub recipient: String,
#[serde(default, rename = "lastPayout", deserialize_with = "de_opt_bigint")]
pub last_payout: Option<BigInt>,
#[serde(default)]
pub bounced: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct LastCashoutAction {
pub peer: String,
#[serde(default, rename = "uncashedAmount", deserialize_with = "de_opt_bigint")]
pub uncashed_amount: Option<BigInt>,
#[serde(default, rename = "transactionHash")]
pub transaction_hash: Option<String>,
#[serde(default, rename = "lastCashedCheque")]
pub last_cashed_cheque: Option<Cheque>,
#[serde(default)]
pub result: Option<CashoutResult>,
}
impl DebugApi {
pub async fn wallet(&self) -> Result<Wallet, Error> {
let builder = request(&self.inner, Method::GET, "wallet")?;
self.inner.send_json(builder).await
}
pub async fn withdraw_bzz(&self, amount: &BigInt, address: &str) -> Result<String, Error> {
let builder = request(&self.inner, Method::POST, "wallet/withdraw/bzz")?.query(&[
("amount", amount.to_string()),
("address", address.to_string()),
]);
tx_hash(&self.inner, builder).await
}
pub async fn withdraw_native_token(
&self,
amount: &BigInt,
address: &str,
) -> Result<String, Error> {
let builder = request(&self.inner, Method::POST, "wallet/withdraw/nativetoken")?.query(&[
("amount", amount.to_string()),
("address", address.to_string()),
]);
tx_hash(&self.inner, builder).await
}
pub async fn chequebook_balance(&self) -> Result<ChequebookBalance, Error> {
let builder = request(&self.inner, Method::GET, "chequebook/balance")?;
self.inner.send_json(builder).await
}
pub async fn chequebook_deposit(&self, amount: &BigInt) -> Result<String, Error> {
let builder = request(&self.inner, Method::POST, "chequebook/deposit")?
.query(&[("amount", amount.to_string())]);
tx_hash(&self.inner, builder).await
}
pub async fn chequebook_withdraw(&self, amount: &BigInt) -> Result<String, Error> {
let builder = request(&self.inner, Method::POST, "chequebook/withdraw")?
.query(&[("amount", amount.to_string())]);
tx_hash(&self.inner, builder).await
}
pub async fn last_cheques(&self) -> Result<Vec<LastCheque>, Error> {
let builder = request(&self.inner, Method::GET, "chequebook/cheque")?;
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "lastcheques")]
last_cheques: Vec<LastCheque>,
}
let r: Resp = self.inner.send_json(builder).await?;
Ok(r.last_cheques)
}
pub async fn peer_cheques(&self, peer: &str) -> Result<PeerCheques, Error> {
let path = format!("chequebook/cheque/{peer}");
let builder = request(&self.inner, Method::GET, &path)?;
self.inner.send_json(builder).await
}
pub async fn last_cashout_action(&self, peer: &str) -> Result<LastCashoutAction, Error> {
let path = format!("chequebook/cashout/{peer}");
let builder = request(&self.inner, Method::GET, &path)?;
self.inner.send_json(builder).await
}
pub async fn cashout_last_cheque(
&self,
peer: &str,
gas_price: Option<&BigInt>,
) -> Result<String, Error> {
let path = format!("chequebook/cashout/{peer}");
let mut builder = request(&self.inner, Method::POST, &path)?;
if let Some(gp) = gas_price {
builder = builder.header("gas-price", gp.to_string());
}
tx_hash(&self.inner, builder).await
}
pub async fn settlements(&self) -> Result<Settlements, Error> {
let builder = request(&self.inner, Method::GET, "settlements")?;
self.inner.send_json(builder).await
}
pub async fn peer_settlement(&self, peer: &str) -> Result<Settlement, Error> {
let path = format!("settlements/{peer}");
let builder = request(&self.inner, Method::GET, &path)?;
self.inner.send_json(builder).await
}
pub async fn time_settlements(&self) -> Result<Settlements, Error> {
let builder = request(&self.inner, Method::GET, "timesettlements")?;
self.inner.send_json(builder).await
}
}
async fn tx_hash(inner: &Inner, builder: reqwest::RequestBuilder) -> Result<String, Error> {
#[derive(Deserialize)]
struct Resp {
#[serde(rename = "transactionHash")]
transaction_hash: String,
}
let resp = inner.send(builder).await?;
let bytes: Bytes = resp.bytes().await?;
let r: Resp = serde_json::from_slice(&bytes)?;
Ok(r.transaction_hash)
}