Skip to content

Commit e7055dd

Browse files
committed
sui-graphql-rust-sdk: add checkpoint fetching API [10/n]
1 parent 8c0b507 commit e7055dd

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Checkpoint-related convenience methods.
2+
3+
use sui_graphql_macros::Response;
4+
use sui_sdk_types::CheckpointContents;
5+
use sui_sdk_types::CheckpointSummary;
6+
7+
use super::Client;
8+
use crate::bcs::Bcs;
9+
use crate::error::Error;
10+
11+
/// A checkpoint response containing the summary and contents.
12+
///
13+
/// This struct combines the checkpoint header (summary) with its contents.
14+
#[derive(Debug, Clone)]
15+
#[non_exhaustive]
16+
pub struct CheckpointResponse {
17+
/// The checkpoint summary (epoch, sequence number, timestamp, etc.)
18+
pub summary: CheckpointSummary,
19+
/// The checkpoint contents (transaction digests and signatures)
20+
pub contents: CheckpointContents,
21+
}
22+
23+
impl Client {
24+
/// Fetch a checkpoint by its sequence number, or the latest checkpoint if not specified.
25+
///
26+
/// Returns:
27+
/// - `Ok(Some(response))` if the checkpoint exists
28+
/// - `Ok(None)` if the checkpoint does not exist
29+
/// - `Err(Error::Request)` for network errors
30+
/// - `Err(Error::Base64)` / `Err(Error::Bcs)` for decoding errors
31+
pub async fn get_checkpoint(
32+
&self,
33+
sequence_number: Option<u64>,
34+
) -> Result<Option<CheckpointResponse>, Error> {
35+
#[derive(Response)]
36+
struct Response {
37+
#[field(path = "checkpoint.summaryBcs")]
38+
summary_bcs: Option<String>,
39+
#[field(path = "checkpoint.contentBcs")]
40+
content_bcs: Option<String>,
41+
}
42+
43+
const QUERY: &str = r#"
44+
query($sequenceNumber: UInt53) {
45+
checkpoint(sequenceNumber: $sequenceNumber) {
46+
summaryBcs
47+
contentBcs
48+
}
49+
}
50+
"#;
51+
let variables = serde_json::json!({ "sequenceNumber": sequence_number });
52+
53+
let response = self.query::<Response>(QUERY, variables).await?;
54+
55+
let Some(data) = response.into_data() else {
56+
return Ok(None);
57+
};
58+
59+
let (Some(summary_bcs), Some(content_bcs)) = (data.summary_bcs, data.content_bcs) else {
60+
return Ok(None);
61+
};
62+
63+
let summary = Bcs::<CheckpointSummary>::decode(&summary_bcs)?.into_inner();
64+
let contents = Bcs::<CheckpointContents>::decode(&content_bcs)?.into_inner();
65+
66+
Ok(Some(CheckpointResponse { summary, contents }))
67+
}
68+
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::*;
73+
use wiremock::Mock;
74+
use wiremock::MockServer;
75+
use wiremock::ResponseTemplate;
76+
use wiremock::matchers::method;
77+
use wiremock::matchers::path;
78+
79+
#[tokio::test]
80+
async fn test_get_checkpoint_not_found() {
81+
let mock_server = MockServer::start().await;
82+
83+
Mock::given(method("POST"))
84+
.and(path("/"))
85+
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
86+
"data": {
87+
"checkpoint": null
88+
}
89+
})))
90+
.mount(&mock_server)
91+
.await;
92+
93+
let client = Client::new(&mock_server.uri()).unwrap();
94+
95+
let result = client.get_checkpoint(Some(999999999)).await;
96+
assert!(result.is_ok());
97+
assert!(result.unwrap().is_none());
98+
}
99+
}

crates/sui-graphql/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! GraphQL client for Sui blockchain.
22
33
pub(crate) mod chain;
4+
pub(crate) mod checkpoints;
45
pub(crate) mod coins;
56
mod objects;
67
pub(crate) mod transactions;

crates/sui-graphql/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod scalars;
1515
pub use bcs::Bcs;
1616
pub use client::Client;
1717
pub use client::chain::Epoch;
18+
pub use client::checkpoints::CheckpointResponse;
1819
pub use client::coins::Balance;
1920
pub use client::transactions::TransactionResponse;
2021
pub use error::Error;

0 commit comments

Comments
 (0)