1use std::collections::HashMap;
5
6use bytes::Bytes;
7use num_bigint::BigInt;
8use reqwest::Method;
9use serde::{Deserialize, Deserializer};
10
11use crate::client::request;
12use crate::swarm::Error;
13
14use super::DebugApi;
15
16#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
18pub struct Balance {
19 pub peer: String,
21 #[serde(deserialize_with = "deserialize_bigint")]
23 pub balance: BigInt,
24}
25
26fn deserialize_bigint<'de, D>(d: D) -> Result<BigInt, D::Error>
27where
28 D: Deserializer<'de>,
29{
30 let s: String = Deserialize::deserialize(d)?;
31 if s.is_empty() {
32 return Ok(BigInt::from(0));
33 }
34 s.parse::<BigInt>().map_err(serde::de::Error::custom)
35}
36
37fn deserialize_opt_bigint<'de, D>(d: D) -> Result<Option<BigInt>, D::Error>
38where
39 D: Deserializer<'de>,
40{
41 let s: String = Deserialize::deserialize(d)?;
42 if s.is_empty() {
43 return Ok(None);
44 }
45 s.parse::<BigInt>()
46 .map(Some)
47 .map_err(serde::de::Error::custom)
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct PeerAccounting {
55 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
57 pub balance: Option<BigInt>,
58 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
60 pub consumed_balance: Option<BigInt>,
61 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
63 pub threshold_received: Option<BigInt>,
64 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
66 pub threshold_given: Option<BigInt>,
67 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
69 pub current_threshold_received: Option<BigInt>,
70 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
72 pub current_threshold_given: Option<BigInt>,
73 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
75 pub surplus_balance: Option<BigInt>,
76 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
78 pub reserved_balance: Option<BigInt>,
79 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
81 pub shadow_reserved_balance: Option<BigInt>,
82 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
84 pub ghost_balance: Option<BigInt>,
85}
86
87#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct RedistributionState {
92 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
94 pub minimum_gas_funds: Option<BigInt>,
95 pub has_sufficient_funds: bool,
97 pub is_frozen: bool,
99 pub is_fully_synced: bool,
101 pub phase: String,
103 pub round: u64,
105 pub last_won_round: u64,
107 pub last_played_round: u64,
109 pub last_frozen_round: u64,
111 pub last_selected_round: u64,
113 pub last_sample_duration_seconds: u64,
115 pub block: u64,
117 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
119 pub reward: Option<BigInt>,
120 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
122 pub fees: Option<BigInt>,
123 pub is_healthy: bool,
125}
126
127impl DebugApi {
128 pub async fn balances(&self) -> Result<Vec<Balance>, Error> {
130 let builder = request(&self.inner, Method::GET, "balances")?;
131 #[derive(Deserialize)]
132 struct Resp {
133 balances: Vec<Balance>,
134 }
135 let r: Resp = self.inner.send_json(builder).await?;
136 Ok(r.balances)
137 }
138
139 pub async fn peer_balance(&self, address: &str) -> Result<Balance, Error> {
141 let path = format!("balances/{address}");
142 let builder = request(&self.inner, Method::GET, &path)?;
143 self.inner.send_json(builder).await
144 }
145
146 pub async fn consumed_balances(&self) -> Result<Vec<Balance>, Error> {
148 let builder = request(&self.inner, Method::GET, "consumed")?;
149 #[derive(Deserialize)]
150 struct Resp {
151 balances: Vec<Balance>,
152 }
153 let r: Resp = self.inner.send_json(builder).await?;
154 Ok(r.balances)
155 }
156
157 pub async fn peer_consumed_balance(&self, address: &str) -> Result<Balance, Error> {
160 let path = format!("consumed/{address}");
161 let builder = request(&self.inner, Method::GET, &path)?;
162 self.inner.send_json(builder).await
163 }
164
165 pub async fn accounting(&self) -> Result<HashMap<String, PeerAccounting>, Error> {
168 let builder = request(&self.inner, Method::GET, "accounting")?;
169 #[derive(Deserialize)]
170 struct Resp {
171 #[serde(rename = "peerData")]
172 peer_data: HashMap<String, PeerAccounting>,
173 }
174 let r: Resp = self.inner.send_json(builder).await?;
175 Ok(r.peer_data)
176 }
177
178 pub async fn stake(&self) -> Result<BigInt, Error> {
182 let builder = request(&self.inner, Method::GET, "stake")?;
183 #[derive(Deserialize)]
184 struct Resp {
185 #[serde(rename = "stakedAmount")]
186 staked_amount: String,
187 }
188 let r: Resp = self.inner.send_json(builder).await?;
189 r.staked_amount.parse::<BigInt>().map_err(|e| {
190 Error::argument(format!("invalid stakedAmount {:?}: {e}", r.staked_amount))
191 })
192 }
193
194 pub async fn deposit_stake(&self, amount: &BigInt) -> Result<String, Error> {
197 let path = format!("stake/{amount}");
198 let builder = request(&self.inner, Method::POST, &path)?;
199 tx_hash_response(&self.inner, builder).await
200 }
201
202 pub async fn withdrawable_stake(&self) -> Result<BigInt, Error> {
204 let builder = request(&self.inner, Method::GET, "stake/withdrawable")?;
205 #[derive(Deserialize)]
206 struct Resp {
207 #[serde(rename = "withdrawableAmount")]
208 withdrawable_amount: String,
209 }
210 let r: Resp = self.inner.send_json(builder).await?;
211 r.withdrawable_amount.parse::<BigInt>().map_err(|e| {
212 Error::argument(format!(
213 "invalid withdrawableAmount {:?}: {e}",
214 r.withdrawable_amount
215 ))
216 })
217 }
218
219 pub async fn withdraw_surplus_stake(&self) -> Result<String, Error> {
221 let builder = request(&self.inner, Method::DELETE, "stake/withdrawable")?;
222 tx_hash_response(&self.inner, builder).await
223 }
224
225 pub async fn migrate_stake(&self) -> Result<String, Error> {
228 let builder = request(&self.inner, Method::DELETE, "stake")?;
229 tx_hash_response(&self.inner, builder).await
230 }
231
232 pub async fn redistribution_state(&self) -> Result<RedistributionState, Error> {
234 let builder = request(&self.inner, Method::GET, "redistributionstate")?;
235 self.inner.send_json(builder).await
236 }
237}
238
239async fn tx_hash_response(
240 inner: &crate::client::Inner,
241 builder: reqwest::RequestBuilder,
242) -> Result<String, Error> {
243 #[derive(Deserialize)]
244 struct Resp {
245 #[serde(rename = "txHash")]
246 tx_hash: String,
247 }
248 let resp = inner.send(builder).await?;
249 let bytes: Bytes = resp.bytes().await?;
250 let r: Resp = serde_json::from_slice(&bytes)?;
251 Ok(r.tx_hash)
252}