Skip to main content

iota_sdk_graphql_client/api/
epochs.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2026 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Epoch API implementation.
6
7use cynic::QueryBuilder;
8
9use crate::{
10    GraphQLClient,
11    api::define_query,
12    error::GraphQLResult,
13    query_types::{Epoch, EpochArgs, EpochQueryFragment, EpochSummaryQueryFragment},
14};
15
16define_query! {
17    /// Query for [`GraphQLClient::epoch`]. Await it to send the request.
18    pub struct GetEpochQuery {
19        client: GraphQLClient,
20        epoch: Option<u64>,
21    }
22    output: GraphQLResult<Option<Epoch>>;
23}
24
25impl GetEpochQuery {
26    /// Set the epoch number. Defaults to the last known epoch.
27    pub fn epoch_number(mut self, epoch_number: u64) -> Self {
28        self.epoch = Some(epoch_number);
29        self
30    }
31
32    async fn send(self) -> GraphQLResult<Option<Epoch>> {
33        let operation = EpochQueryFragment::build(EpochArgs { id: self.epoch });
34        let response = self.client.run_query(&operation).await?;
35
36        Ok(response.epoch)
37    }
38}
39
40define_query! {
41    /// Query for [`GraphQLClient::epoch_total_checkpoints`]. Await it to send
42    /// the request.
43    pub struct GetEpochTotalCheckpointsQuery {
44        client: GraphQLClient,
45        epoch: Option<u64>,
46    }
47    output: GraphQLResult<Option<u64>>;
48}
49
50impl GetEpochTotalCheckpointsQuery {
51    /// Set the epoch number. Defaults to the last known epoch.
52    pub fn epoch_number(mut self, epoch_number: u64) -> Self {
53        self.epoch = Some(epoch_number);
54        self
55    }
56
57    async fn send(self) -> GraphQLResult<Option<u64>> {
58        let response = self.client.epoch_summary(self.epoch).await?;
59
60        Ok(response.epoch.and_then(|e| e.total_checkpoints))
61    }
62}
63
64define_query! {
65    /// Query for [`GraphQLClient::epoch_total_transaction_blocks`]. Await it to
66    /// send the request.
67    pub struct GetEpochTotalTransactionBlocksQuery {
68        client: GraphQLClient,
69        epoch: Option<u64>,
70    }
71    output: GraphQLResult<Option<u64>>;
72}
73
74impl GetEpochTotalTransactionBlocksQuery {
75    /// Set the epoch number. Defaults to the last known epoch.
76    pub fn epoch_number(mut self, epoch_number: u64) -> Self {
77        self.epoch = Some(epoch_number);
78        self
79    }
80
81    async fn send(self) -> GraphQLResult<Option<u64>> {
82        let response = self.client.epoch_summary(self.epoch).await?;
83
84        Ok(response.epoch.and_then(|e| e.total_transactions))
85    }
86}
87
88impl GraphQLClient {
89    /// Internal method for getting the epoch summary that is called in a few
90    /// other APIs for convenience.
91    pub(crate) async fn epoch_summary(
92        &self,
93        epoch: Option<u64>,
94    ) -> GraphQLResult<EpochSummaryQueryFragment> {
95        let operation = EpochSummaryQueryFragment::build(EpochArgs { id: epoch });
96        self.run_query(&operation).await
97    }
98
99    /// Return the epoch information. Defaults to the last known epoch.
100    pub fn epoch(&self) -> GetEpochQuery {
101        GetEpochQuery {
102            client: self.clone(),
103            epoch: None,
104        }
105    }
106
107    /// Return the number of checkpoints in an epoch. Resolves to `None` if the
108    /// epoch requested is not available in the GraphQL service (e.g., due to
109    /// pruning).
110    pub fn epoch_total_checkpoints(&self) -> GetEpochTotalCheckpointsQuery {
111        GetEpochTotalCheckpointsQuery {
112            client: self.clone(),
113            epoch: None,
114        }
115    }
116
117    /// Return the number of transaction blocks in an epoch. Resolves to `None`
118    /// if the epoch requested is not available in the GraphQL service (e.g.,
119    /// due to pruning).
120    pub fn epoch_total_transaction_blocks(&self) -> GetEpochTotalTransactionBlocksQuery {
121        GetEpochTotalTransactionBlocksQuery {
122            client: self.clone(),
123            epoch: None,
124        }
125    }
126}
127
128#[cfg(all(test, not(target_arch = "wasm32")))]
129mod tests {
130    use crate::test_utils::{sent_variables, test_client};
131
132    #[tokio::test]
133    async fn epoch_queries_send_the_epoch() {
134        let vars = sent_variables("EpochQueryFragment", |client| async move {
135            let _ = client.epoch().epoch_number(3).await;
136        })
137        .await;
138        assert_eq!(vars["id"], 3);
139
140        let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
141            let _ = client.epoch_total_checkpoints().epoch_number(4).await;
142        })
143        .await;
144        assert_eq!(vars["id"], 4);
145
146        let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
147            let _ = client
148                .epoch_total_transaction_blocks()
149                .epoch_number(5)
150                .await;
151        })
152        .await;
153        assert_eq!(vars["id"], 5);
154
155        let vars = sent_variables("EpochQueryFragment", |client| async move {
156            let _ = client.epoch().await;
157        })
158        .await;
159        assert!(vars["id"].is_null());
160
161        let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
162            let _ = client.epoch_total_checkpoints().await;
163        })
164        .await;
165        assert!(vars["id"].is_null());
166
167        let vars = sent_variables("EpochSummaryQueryFragment", |client| async move {
168            let _ = client.epoch_total_transaction_blocks().await;
169        })
170        .await;
171        assert!(vars["id"].is_null());
172    }
173
174    #[tokio::test]
175    async fn test_epoch_query() {
176        let client = test_client();
177        client
178            .epoch()
179            .await
180            .map_err(|e| {
181                format!(
182                    "Epoch query failed for {} network: Error {e}",
183                    client.rpc_server()
184                )
185            })
186            .unwrap()
187            .unwrap();
188    }
189
190    #[tokio::test]
191    async fn test_epoch_total_checkpoints_query() {
192        let client = test_client();
193        client
194            .epoch_total_checkpoints()
195            .await
196            .map_err(|e| {
197                format!(
198                    "Epoch total checkpoints query failed for {} network: Error {e}",
199                    client.rpc_server()
200                )
201            })
202            .unwrap()
203            .unwrap();
204    }
205
206    #[tokio::test]
207    async fn test_epoch_total_transaction_blocks_query() {
208        let client = test_client();
209        client
210            .epoch_total_transaction_blocks()
211            .await
212            .map_err(|e| {
213                format!(
214                    "Epoch total transaction blocks query failed for {} network: Error {e}",
215                    client.rpc_server()
216                )
217            })
218            .unwrap();
219    }
220
221    #[tokio::test]
222    async fn test_epoch_summary_query() {
223        let client = test_client();
224        client
225            .epoch_summary(None)
226            .await
227            .map_err(|e| {
228                format!(
229                    "Epoch summary query failed for {} network: Error {e}",
230                    client.rpc_server()
231                )
232            })
233            .unwrap();
234    }
235}