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_deposit(&self, amount: &BigInt) -> Result<String, Error> {
219 let builder = request(&self.inner, Method::POST, "chequebook/deposit")?
220 .query(&[("amount", amount.to_string())]);
221 tx_hash(&self.inner, builder).await
222 }
223
224 pub async fn chequebook_withdraw(&self, amount: &BigInt) -> Result<String, Error> {
227 let builder = request(&self.inner, Method::POST, "chequebook/withdraw")?
228 .query(&[("amount", amount.to_string())]);
229 tx_hash(&self.inner, builder).await
230 }
231
232 pub async fn last_cheques(&self) -> Result<Vec<LastCheque>, Error> {
234 let builder = request(&self.inner, Method::GET, "chequebook/cheque")?;
235 #[derive(Deserialize)]
236 struct Resp {
237 #[serde(rename = "lastcheques")]
238 last_cheques: Vec<LastCheque>,
239 }
240 let r: Resp = self.inner.send_json(builder).await?;
241 Ok(r.last_cheques)
242 }
243
244 pub async fn peer_cheques(&self, peer: &str) -> Result<PeerCheques, Error> {
247 let path = format!("chequebook/cheque/{peer}");
248 let builder = request(&self.inner, Method::GET, &path)?;
249 self.inner.send_json(builder).await
250 }
251
252 pub async fn last_cashout_action(&self, peer: &str) -> Result<LastCashoutAction, Error> {
255 let path = format!("chequebook/cashout/{peer}");
256 let builder = request(&self.inner, Method::GET, &path)?;
257 self.inner.send_json(builder).await
258 }
259
260 pub async fn cashout_last_cheque(
265 &self,
266 peer: &str,
267 gas_price: Option<&BigInt>,
268 ) -> Result<String, Error> {
269 let path = format!("chequebook/cashout/{peer}");
270 let mut builder = request(&self.inner, Method::POST, &path)?;
271 if let Some(gp) = gas_price {
272 builder = builder.header("gas-price", gp.to_string());
273 }
274 tx_hash(&self.inner, builder).await
275 }
276
277 pub async fn settlements(&self) -> Result<Settlements, Error> {
281 let builder = request(&self.inner, Method::GET, "settlements")?;
282 self.inner.send_json(builder).await
283 }
284
285 pub async fn peer_settlement(&self, peer: &str) -> Result<Settlement, Error> {
287 let path = format!("settlements/{peer}");
288 let builder = request(&self.inner, Method::GET, &path)?;
289 self.inner.send_json(builder).await
290 }
291
292 pub async fn time_settlements(&self) -> Result<Settlements, Error> {
297 let builder = request(&self.inner, Method::GET, "timesettlements")?;
298 self.inner.send_json(builder).await
299 }
300}
301
302async fn tx_hash(inner: &Inner, builder: reqwest::RequestBuilder) -> Result<String, Error> {
303 #[derive(Deserialize)]
304 struct Resp {
305 #[serde(rename = "transactionHash")]
306 transaction_hash: String,
307 }
308 let resp = inner.send(builder).await?;
309 let bytes: Bytes = resp.bytes().await?;
310 let r: Resp = serde_json::from_slice(&bytes)?;
311 Ok(r.transaction_hash)
312}