sui_gql_client/queries/
latest_version_at_checkpoint_v2.rs1use af_sui_types::{Address, Version};
2use cynic::{QueryFragment, QueryVariables};
3
4use crate::{GraphQlClient, GraphQlErrors, GraphQlResponseExt as _, schema};
5
6#[derive(thiserror::Error, Clone, Debug, PartialEq)]
7pub enum Error<C: std::error::Error> {
8 #[error(
9 "The latest checkpoint number ({0}) known to the server is smaller than the queried \
10 checkpoint"
11 )]
12 WatermarkTooLow(u64),
13 #[error("Missing latest checkpoint number")]
14 NoLatestCheckpoint,
15 #[error("No data in GraphQL response")]
16 NoData,
17 #[error("No transaction blocks found for object {id}")]
18 NoTransactionBlocks { id: Address },
19 #[error("Missing transaction effects")]
20 MissingTxEffects,
21 #[error("In client: {0}")]
22 Client(C),
23 #[error("From server: {0}")]
24 Server(#[from] GraphQlErrors),
25}
26
27pub async fn query<C: GraphQlClient>(
38 client: &C,
39 id: Address,
40 ckpt_num: u64,
41) -> Result<u64, Error<C::Error>> {
42 let Some(mut data): Option<Query> = client
43 .query(Variables {
44 object_id: Some(id),
45 checkpoint_num: Some(ckpt_num + 1),
46 })
47 .await
48 .map_err(Error::Client)?
49 .try_into_data()?
50 else {
51 return Err(Error::NoData);
52 };
53
54 let watermark = data
55 .checkpoint
56 .ok_or(Error::NoLatestCheckpoint)?
57 .sequence_number;
58 if watermark < ckpt_num {
59 return Err(Error::WatermarkTooLow(watermark));
60 }
61
62 Ok(data
63 .transaction_blocks
64 .nodes
65 .pop()
66 .ok_or_else(|| Error::NoTransactionBlocks { id })?
67 .effects
68 .ok_or(Error::MissingTxEffects)?
69 .lamport_version)
70}
71
72#[derive(QueryVariables, Debug)]
73struct Variables {
74 checkpoint_num: Option<Version>,
75 object_id: Option<Address>,
76}
77
78#[derive(QueryFragment, Debug)]
79#[cynic(graphql_type = "Query", variables = "Variables")]
80struct Query {
81 checkpoint: Option<CheckpointNumber>,
82
83 #[arguments(last: 1, filter: { beforeCheckpoint: $checkpoint_num, changedObject: $object_id })]
84 transaction_blocks: TransactionBlockConnection,
85}
86
87#[derive(QueryFragment, Debug)]
88#[cynic(graphql_type = "Checkpoint")]
89struct CheckpointNumber {
90 sequence_number: Version,
91}
92
93#[derive(QueryFragment, Debug)]
94struct TransactionBlockConnection {
95 nodes: Vec<TransactionBlock>,
96}
97
98#[derive(QueryFragment, Debug)]
99struct TransactionBlock {
100 effects: Option<TransactionBlockEffects>,
101}
102
103#[derive(QueryFragment, Debug)]
104struct TransactionBlockEffects {
105 lamport_version: Version,
106}
107
108#[cfg(test)]
109#[allow(clippy::unwrap_used)]
110#[test]
111fn init_gql_output() {
112 use cynic::QueryBuilder as _;
113 let vars = Variables {
114 object_id: Some(
115 "0x4264c07a42f9d002c1244e43a1f0fa21c49e4a25c7202c597b8476ef6bb57113"
116 .parse()
117 .unwrap(),
118 ),
119 checkpoint_num: Some(54773328),
120 };
121 let operation = Query::build(vars);
122 insta::assert_snapshot!(operation.query, @r###"
123 query Query($checkpointNum: UInt53, $objectId: SuiAddress) {
124 checkpoint {
125 sequenceNumber
126 }
127 transactionBlocks(last: 1, filter: {beforeCheckpoint: $checkpointNum, changedObject: $objectId}) {
128 nodes {
129 effects {
130 lamportVersion
131 }
132 }
133 }
134 }
135 "###);
136}