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, 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: f64,
116 pub block: u64,
118 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
120 pub reward: Option<BigInt>,
121 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
123 pub fees: Option<BigInt>,
124 pub is_healthy: bool,
126}
127
128impl DebugApi {
129 pub async fn balances(&self) -> Result<Vec<Balance>, Error> {
131 let builder = request(&self.inner, Method::GET, "balances")?;
132 #[derive(Deserialize)]
133 struct Resp {
134 balances: Vec<Balance>,
135 }
136 let r: Resp = self.inner.send_json(builder).await?;
137 Ok(r.balances)
138 }
139
140 pub async fn peer_balance(&self, address: &str) -> Result<Balance, Error> {
142 let path = format!("balances/{address}");
143 let builder = request(&self.inner, Method::GET, &path)?;
144 self.inner.send_json(builder).await
145 }
146
147 pub async fn consumed_balances(&self) -> Result<Vec<Balance>, Error> {
149 let builder = request(&self.inner, Method::GET, "consumed")?;
150 #[derive(Deserialize)]
151 struct Resp {
152 balances: Vec<Balance>,
153 }
154 let r: Resp = self.inner.send_json(builder).await?;
155 Ok(r.balances)
156 }
157
158 pub async fn peer_consumed_balance(&self, address: &str) -> Result<Balance, Error> {
161 let path = format!("consumed/{address}");
162 let builder = request(&self.inner, Method::GET, &path)?;
163 self.inner.send_json(builder).await
164 }
165
166 pub async fn accounting(&self) -> Result<HashMap<String, PeerAccounting>, Error> {
169 let builder = request(&self.inner, Method::GET, "accounting")?;
170 #[derive(Deserialize)]
171 struct Resp {
172 #[serde(rename = "peerData")]
173 peer_data: HashMap<String, PeerAccounting>,
174 }
175 let r: Resp = self.inner.send_json(builder).await?;
176 Ok(r.peer_data)
177 }
178
179 pub async fn stake(&self) -> Result<BigInt, Error> {
183 let builder = request(&self.inner, Method::GET, "stake")?;
184 #[derive(Deserialize)]
185 struct Resp {
186 #[serde(rename = "stakedAmount")]
187 staked_amount: String,
188 }
189 let r: Resp = self.inner.send_json(builder).await?;
190 r.staked_amount.parse::<BigInt>().map_err(|e| {
191 Error::argument(format!("invalid stakedAmount {:?}: {e}", r.staked_amount))
192 })
193 }
194
195 pub async fn deposit_stake(&self, amount: &BigInt) -> Result<String, Error> {
198 let path = format!("stake/{amount}");
199 let builder = request(&self.inner, Method::POST, &path)?;
200 tx_hash_response(&self.inner, builder).await
201 }
202
203 pub async fn withdrawable_stake(&self) -> Result<BigInt, Error> {
205 let builder = request(&self.inner, Method::GET, "stake/withdrawable")?;
206 #[derive(Deserialize)]
207 struct Resp {
208 #[serde(rename = "withdrawableAmount")]
209 withdrawable_amount: String,
210 }
211 let r: Resp = self.inner.send_json(builder).await?;
212 r.withdrawable_amount.parse::<BigInt>().map_err(|e| {
213 Error::argument(format!(
214 "invalid withdrawableAmount {:?}: {e}",
215 r.withdrawable_amount
216 ))
217 })
218 }
219
220 pub async fn withdraw_surplus_stake(&self) -> Result<String, Error> {
222 let builder = request(&self.inner, Method::DELETE, "stake/withdrawable")?;
223 tx_hash_response(&self.inner, builder).await
224 }
225
226 pub async fn migrate_stake(&self) -> Result<String, Error> {
229 let builder = request(&self.inner, Method::DELETE, "stake")?;
230 tx_hash_response(&self.inner, builder).await
231 }
232
233 pub async fn redistribution_state(&self) -> Result<RedistributionState, Error> {
235 let builder = request(&self.inner, Method::GET, "redistributionstate")?;
236 self.inner.send_json(builder).await
237 }
238}
239
240async fn tx_hash_response(
241 inner: &crate::client::Inner,
242 builder: reqwest::RequestBuilder,
243) -> Result<String, Error> {
244 #[derive(Deserialize)]
245 struct Resp {
246 #[serde(rename = "txHash")]
247 tx_hash: String,
248 }
249 let resp = inner.send(builder).await?;
250 let bytes: Bytes = resp.bytes().await?;
251 let r: Resp = serde_json::from_slice(&bytes)?;
252 Ok(r.tx_hash)
253}