1use bytes::Bytes;
5use num_bigint::BigInt;
6use reqwest::Method;
7use serde::{Deserialize, Deserializer};
8
9use crate::client::{Inner, request};
10use crate::swarm::Error;
11
12use super::DebugApi;
13
14fn de_bigint<'de, D: Deserializer<'de>>(d: D) -> Result<BigInt, D::Error> {
15 let s: String = Deserialize::deserialize(d)?;
16 if s.is_empty() {
17 return Ok(BigInt::from(0));
18 }
19 s.parse::<BigInt>().map_err(serde::de::Error::custom)
20}
21
22fn de_opt_bigint<'de, D: Deserializer<'de>>(d: D) -> Result<Option<BigInt>, D::Error> {
23 let s: Option<String> = Deserialize::deserialize(d)?;
24 match s {
25 None => Ok(None),
26 Some(s) if s.is_empty() => Ok(None),
27 Some(s) => s
28 .parse::<BigInt>()
29 .map(Some)
30 .map_err(serde::de::Error::custom),
31 }
32}
33
34#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct Wallet {
44 #[serde(default)]
46 pub bzz_address: Option<String>,
47 #[serde(default)]
49 pub native_address: Option<String>,
50 #[serde(default, alias = "chequebook")]
54 pub chequebook_contract_address: Option<String>,
55 #[serde(default, deserialize_with = "de_opt_bigint")]
57 pub bzz_balance: Option<BigInt>,
58 #[serde(default, deserialize_with = "de_opt_bigint")]
60 pub native_token_balance: Option<BigInt>,
61 #[serde(rename = "chainID")]
63 pub chain_id: i64,
64 pub wallet_address: String,
66}
67
68#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub struct ChequebookBalance {
72 #[serde(deserialize_with = "de_bigint")]
74 pub total_balance: BigInt,
75 #[serde(deserialize_with = "de_bigint")]
77 pub available_balance: BigInt,
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
82pub struct Settlement {
83 pub peer: String,
85 #[serde(default, deserialize_with = "de_opt_bigint")]
87 pub received: Option<BigInt>,
88 #[serde(default, deserialize_with = "de_opt_bigint")]
90 pub sent: Option<BigInt>,
91}
92
93#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct Settlements {
97 #[serde(default, deserialize_with = "de_opt_bigint")]
99 pub total_received: Option<BigInt>,
100 #[serde(default, deserialize_with = "de_opt_bigint")]
102 pub total_sent: Option<BigInt>,
103 #[serde(default)]
105 pub settlements: Vec<Settlement>,
106}
107
108#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
110pub struct Cheque {
111 pub beneficiary: String,
113 pub chequebook: String,
115 #[serde(default, deserialize_with = "de_opt_bigint")]
117 pub payout: Option<BigInt>,
118}
119
120#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
122pub struct LastCheque {
123 pub peer: String,
125 #[serde(default, rename = "lastreceived")]
127 pub last_received: Option<Cheque>,
128}
129
130#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
132pub struct PeerCheques {
133 pub peer: String,
135 #[serde(default, rename = "lastreceived")]
137 pub last_received: Option<Cheque>,
138 #[serde(default, rename = "lastsent")]
140 pub last_sent: Option<Cheque>,
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
145pub struct CashoutResult {
146 pub recipient: String,
148 #[serde(default, rename = "lastPayout", deserialize_with = "de_opt_bigint")]
150 pub last_payout: Option<BigInt>,
151 #[serde(default)]
153 pub bounced: bool,
154}
155
156#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
158pub struct LastCashoutAction {
159 pub peer: String,
161 #[serde(default, rename = "uncashedAmount", deserialize_with = "de_opt_bigint")]
163 pub uncashed_amount: Option<BigInt>,
164 #[serde(default, rename = "transactionHash")]
166 pub transaction_hash: Option<String>,
167 #[serde(default, rename = "lastCashedCheque")]
169 pub last_cashed_cheque: Option<Cheque>,
170 #[serde(default)]
172 pub result: Option<CashoutResult>,
173}
174
175impl DebugApi {
176 pub async fn wallet(&self) -> Result<Wallet, Error> {
180 let builder = request(&self.inner, Method::GET, "wallet")?;
181 self.inner.send_json(builder).await
182 }
183
184 pub async fn withdraw_bzz(&self, amount: &BigInt, address: &str) -> Result<String, Error> {
187 let builder = request(&self.inner, Method::POST, "wallet/withdraw/bzz")?.query(&[
188 ("amount", amount.to_string()),
189 ("address", address.to_string()),
190 ]);
191 tx_hash(&self.inner, builder).await
192 }
193
194 pub async fn withdraw_native_token(
197 &self,
198 amount: &BigInt,
199 address: &str,
200 ) -> Result<String, Error> {
201 let builder = request(&self.inner, Method::POST, "wallet/withdraw/nativetoken")?.query(&[
202 ("amount", amount.to_string()),
203 ("address", address.to_string()),
204 ]);
205 tx_hash(&self.inner, builder).await
206 }
207
208 pub async fn chequebook_balance(&self) -> Result<ChequebookBalance, Error> {
212 let builder = request(&self.inner, Method::GET, "chequebook/balance")?;
213 self.inner.send_json(builder).await
214 }
215
216 pub async fn chequebook_address(&self) -> Result<String, Error> {
221 let builder = request(&self.inner, Method::GET, "chequebook/address")?;
222 #[derive(Deserialize)]
223 struct Resp {
224 #[serde(rename = "chequebookAddress")]
225 chequebook_address: String,
226 }
227 let r: Resp = self.inner.send_json(builder).await?;
228 Ok(r.chequebook_address)
229 }
230
231 pub async fn chequebook_deposit(&self, amount: &BigInt) -> Result<String, Error> {
234 let builder = request(&self.inner, Method::POST, "chequebook/deposit")?
235 .query(&[("amount", amount.to_string())]);
236 tx_hash(&self.inner, builder).await
237 }
238
239 pub async fn chequebook_withdraw(&self, amount: &BigInt) -> Result<String, Error> {
242 let builder = request(&self.inner, Method::POST, "chequebook/withdraw")?
243 .query(&[("amount", amount.to_string())]);
244 tx_hash(&self.inner, builder).await
245 }
246
247 pub async fn last_cheques(&self) -> Result<Vec<LastCheque>, Error> {
249 let builder = request(&self.inner, Method::GET, "chequebook/cheque")?;
250 #[derive(Deserialize)]
251 struct Resp {
252 #[serde(rename = "lastcheques")]
253 last_cheques: Vec<LastCheque>,
254 }
255 let r: Resp = self.inner.send_json(builder).await?;
256 Ok(r.last_cheques)
257 }
258
259 pub async fn peer_cheques(&self, peer: &str) -> Result<PeerCheques, Error> {
262 let path = format!("chequebook/cheque/{peer}");
263 let builder = request(&self.inner, Method::GET, &path)?;
264 self.inner.send_json(builder).await
265 }
266
267 pub async fn last_cashout_action(&self, peer: &str) -> Result<LastCashoutAction, Error> {
270 let path = format!("chequebook/cashout/{peer}");
271 let builder = request(&self.inner, Method::GET, &path)?;
272 self.inner.send_json(builder).await
273 }
274
275 pub async fn cashout_last_cheque(
280 &self,
281 peer: &str,
282 gas_price: Option<&BigInt>,
283 ) -> Result<String, Error> {
284 let path = format!("chequebook/cashout/{peer}");
285 let mut builder = request(&self.inner, Method::POST, &path)?;
286 if let Some(gp) = gas_price {
287 builder = builder.header("gas-price", gp.to_string());
288 }
289 tx_hash(&self.inner, builder).await
290 }
291
292 pub async fn settlements(&self) -> Result<Settlements, Error> {
296 let builder = request(&self.inner, Method::GET, "settlements")?;
297 self.inner.send_json(builder).await
298 }
299
300 pub async fn peer_settlement(&self, peer: &str) -> Result<Settlement, Error> {
302 let path = format!("settlements/{peer}");
303 let builder = request(&self.inner, Method::GET, &path)?;
304 self.inner.send_json(builder).await
305 }
306
307 pub async fn time_settlements(&self) -> Result<Settlements, Error> {
312 let builder = request(&self.inner, Method::GET, "timesettlements")?;
313 self.inner.send_json(builder).await
314 }
315}
316
317async fn tx_hash(inner: &Inner, builder: reqwest::RequestBuilder) -> Result<String, Error> {
318 #[derive(Deserialize)]
319 struct Resp {
320 #[serde(rename = "transactionHash")]
321 transaction_hash: String,
322 }
323 let resp = inner.send(builder).await?;
324 let bytes: Bytes = resp.bytes().await?;
325 let r: Resp = serde_json::from_slice(&bytes)?;
326 Ok(r.transaction_hash)
327}