Skip to main content

bee/postage/
endpoints.rs

1//! `/stamps` and `/batches` endpoint methods.
2
3use num_bigint::BigInt;
4use reqwest::Method;
5use serde::Deserialize;
6
7use crate::api::PostageBatchOptions;
8use crate::client::request;
9use crate::swarm::{BatchId, Error};
10
11use super::PostageApi;
12use super::types::{GlobalPostageBatch, PostageBatch, PostageBatchBuckets};
13
14#[derive(Deserialize)]
15struct StampsResp {
16    stamps: Vec<PostageBatch>,
17}
18
19#[derive(Deserialize)]
20struct BatchesResp {
21    batches: Vec<GlobalPostageBatch>,
22}
23
24#[derive(Deserialize)]
25struct BatchIdResp {
26    #[serde(rename = "batchID")]
27    batch_id: String,
28}
29
30impl PostageApi {
31    /// Every postage batch owned by this node — `GET /stamps`.
32    pub async fn get_postage_batches(&self) -> Result<Vec<PostageBatch>, Error> {
33        let builder = request(&self.inner, Method::GET, "stamps")?;
34        let res: StampsResp = self.inner.send_json(builder).await?;
35        Ok(res.stamps)
36    }
37
38    /// Single owned batch by id — `GET /stamps/{id}`.
39    pub async fn get_postage_batch(&self, batch_id: &BatchId) -> Result<PostageBatch, Error> {
40        let path = format!("stamps/{}", batch_id.to_hex());
41        let builder = request(&self.inner, Method::GET, &path)?;
42        self.inner.send_json(builder).await
43    }
44
45    /// Per-bucket collision stats — `GET /stamps/{id}/buckets`.
46    pub async fn get_postage_batch_buckets(
47        &self,
48        batch_id: &BatchId,
49    ) -> Result<PostageBatchBuckets, Error> {
50        let path = format!("stamps/{}/buckets", batch_id.to_hex());
51        let builder = request(&self.inner, Method::GET, &path)?;
52        self.inner.send_json(builder).await
53    }
54
55    /// Every chain-visible postage batch — `GET /batches`. Returns the
56    /// chain-wide view (no owner-only fields).
57    pub async fn get_global_postage_batches(&self) -> Result<Vec<GlobalPostageBatch>, Error> {
58        let builder = request(&self.inner, Method::GET, "batches")?;
59        let res: BatchesResp = self.inner.send_json(builder).await?;
60        Ok(res.batches)
61    }
62
63    /// Buy a new postage batch — `POST /stamps/{amount}/{depth}`.
64    /// Returns the freshly-minted [`BatchId`].
65    pub async fn create_postage_batch(
66        &self,
67        amount: &BigInt,
68        depth: u8,
69        label: Option<&str>,
70    ) -> Result<BatchId, Error> {
71        let opts = label.map(|l| PostageBatchOptions {
72            label: Some(l.to_string()),
73            ..Default::default()
74        });
75        self.create_postage_batch_with_options(amount, depth, opts.as_ref())
76            .await
77    }
78
79    /// Buy a new postage batch with full options support. Mirrors
80    /// bee-js `Bee.createPostageBatch` and bee-go's options-style
81    /// constructor — `label` and `immutable` map to query parameters,
82    /// `gas_price` / `gas_limit` map to `Gas-Price` / `Gas-Limit` headers.
83    pub async fn create_postage_batch_with_options(
84        &self,
85        amount: &BigInt,
86        depth: u8,
87        opts: Option<&PostageBatchOptions>,
88    ) -> Result<BatchId, Error> {
89        let path = format!("stamps/{amount}/{depth}");
90        let mut builder = request(&self.inner, Method::POST, &path)?;
91        if let Some(o) = opts {
92            let mut query: Vec<(&str, String)> = Vec::new();
93            if let Some(l) = &o.label {
94                query.push(("label", l.clone()));
95            }
96            if let Some(im) = o.immutable {
97                query.push(("immutable", im.to_string()));
98            }
99            if !query.is_empty() {
100                let q: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
101                builder = builder.query(&q);
102            }
103            if let Some(gp) = &o.gas_price {
104                builder = builder.header("Gas-Price", gp);
105            }
106            if let Some(gl) = &o.gas_limit {
107                builder = builder.header("Gas-Limit", gl);
108            }
109        }
110        let res: BatchIdResp = self.inner.send_json(builder).await?;
111        BatchId::from_hex(&res.batch_id)
112    }
113
114    /// Top up an existing batch — `PATCH /stamps/topup/{id}/{amount}`.
115    pub async fn top_up_batch(&self, batch_id: &BatchId, amount: &BigInt) -> Result<(), Error> {
116        let path = format!("stamps/topup/{}/{amount}", batch_id.to_hex());
117        let builder = request(&self.inner, Method::PATCH, &path)?;
118        self.inner.send(builder).await?;
119        Ok(())
120    }
121
122    /// Increase the depth of an existing batch — `PATCH /stamps/dilute/{id}/{depth}`.
123    pub async fn dilute_batch(&self, batch_id: &BatchId, depth: u8) -> Result<(), Error> {
124        let path = format!("stamps/dilute/{}/{depth}", batch_id.to_hex());
125        let builder = request(&self.inner, Method::PATCH, &path)?;
126        self.inner.send(builder).await?;
127        Ok(())
128    }
129}