sui_gql_client/queries/
mod.rs

1use std::collections::HashMap;
2
3use af_sui_types::{
4    Address as SuiAddress,
5    Object,
6    ObjectArg,
7    ObjectId,
8    ObjectRef,
9    StructTag,
10    TransactionData,
11    Version,
12};
13// For `object_args!` macro only
14#[doc(hidden)]
15pub use bimap::BiMap;
16use futures::Stream;
17use outputs::{DynamicField, ObjectKey, RawMoveStruct, RawMoveValue};
18
19use crate::{GraphQlClient, GraphQlErrors};
20
21mod current_epoch_id;
22mod epoch_final_checkpoint_num;
23mod events_backward;
24mod filtered_full_objects;
25pub mod fragments;
26mod full_object;
27mod full_objects;
28mod gas_payment;
29mod genesis_tx;
30mod latest_checkpoint;
31mod latest_object_version;
32mod latest_objects_version;
33mod latest_package;
34mod latest_version_at_checkpoint_v2;
35mod max_page_size;
36mod multi_get_objects;
37mod object_arg;
38mod object_args;
39mod object_args_and_content;
40mod object_content;
41mod object_type;
42mod objects_content;
43mod objects_flat;
44pub mod outputs;
45mod owner_df_content;
46mod owner_df_contents;
47mod owner_df_contents_stream;
48mod owner_dof_content;
49mod package_at_version;
50mod packages_from_original;
51mod packages_published_epoch;
52mod reference_gas_price;
53pub(crate) mod stream;
54mod transaction_blocks_status;
55
56pub use self::events_backward::{EventEdge, EventFilter};
57pub use self::gas_payment::Error as GasPaymentError;
58pub use self::latest_version_at_checkpoint_v2::Error as LatestVersionAtCheckpointError;
59pub use self::object_args::Error as ObjectArgsError;
60pub use self::object_args_and_content::Error as ObjectArgsAndContentError;
61
62/// Standard query result type to aid in adding new queries.
63type Result<T, C> = std::result::Result<T, Error<<C as GraphQlClient>::Error>>;
64
65/// Extension trait to [`GraphQlClient`] collecting all defined queries in one place.
66#[trait_variant::make(Send)]
67pub trait GraphQlClientExt: GraphQlClient + Sized {
68    // NOTE: `.await` is not used in the implementations below because `trait_variant` de-sugars the
69    // method definitions removing their `async` prefixes
70
71    /// The latest epoch id.
72    async fn current_epoch_id(&self) -> Result<u64, Self> {
73        current_epoch_id::query(self)
74    }
75
76    /// The last checkpoint number of an epoch.
77    async fn epoch_final_checkpoint_num(&self, epoch_id: u64) -> Result<u64, Self> {
78        epoch_final_checkpoint_num::query(self, epoch_id)
79    }
80
81    /// Return a single page of events + cursors and a flag indicating if there's a previous page.
82    ///
83    /// If `page_size` is left `None`, the server decides the size of the page.
84    ///
85    /// The edges are returned in reverse order of which they where returned by the server
86    async fn events_backward(
87        &self,
88        filter: Option<EventFilter>,
89        cursor: Option<String>,
90        page_size: Option<u32>,
91    ) -> Result<(Vec<EventEdge>, bool), Self> {
92        events_backward::query(self, filter, cursor, page_size)
93    }
94
95    /// The latest full [`Object`] contents with the possibility to filter by owner or object type.
96    fn filtered_full_objects(
97        &self,
98        owner: Option<SuiAddress>,
99        type_: Option<String>,
100        page_size: Option<u32>,
101    ) -> impl Stream<Item = Result<Object, Self>> + '_ {
102        filtered_full_objects::query(self, owner, type_, page_size)
103    }
104
105    /// The full [`Object`] contents at a certain version or the latest if not specified.
106    async fn full_object(&self, object_id: ObjectId, version: Option<u64>) -> Result<Object, Self> {
107        full_object::query(self, object_id, version)
108    }
109
110    /// The full [`Object`] contents at their latest versions.
111    ///
112    /// Fails if any requested object id is not in the final map.
113    ///
114    /// # Note
115    ///
116    /// The check for returned object ids is just so that the caller can safely do `map[object_id]`
117    /// on the returned map. Keep in mind that the result if an object id is repeated in `objects`
118    /// is undefined. Avoid doing so.
119    async fn latest_full_objects(
120        &self,
121        objects: impl IntoIterator<Item = ObjectId> + Send,
122        page_size: Option<u32>,
123    ) -> Result<HashMap<ObjectId, Object>, Self> {
124        full_objects::query(self, objects, page_size)
125    }
126
127    /// The full [`Object`] contents at specific versions.
128    ///
129    /// Duplicate object keys are automatically discarded.
130    async fn multi_get_objects(
131        &self,
132        keys: impl IntoIterator<Item = (ObjectId, Version)> + Send,
133    ) -> Result<Vec<Object>, Self> {
134        self::multi_get_objects::query(self, keys)
135    }
136
137    /// Genesis transaction of the Sui network instance.
138    async fn genesis_tx(&self) -> Result<TransactionData, Self> {
139        genesis_tx::query(self)
140    }
141
142    /// Latest checkpoint number.
143    async fn latest_checkpoint(&self) -> Result<u64, Self> {
144        latest_checkpoint::query(self)
145    }
146
147    /// The latest checkpoint number and object version of an object.
148    async fn latest_object_version(&self, object_id: ObjectId) -> Result<(u64, u64), Self> {
149        latest_object_version::query(self, object_id)
150    }
151
152    /// The latest checkpoint number and the map of object ids to the their version at that
153    /// checkpoint.
154    ///
155    /// Fails if the server doesn't return the version for any of the requested objects.
156    async fn latest_objects_version(
157        &self,
158        object_ids: &[ObjectId],
159    ) -> Result<(u64, HashMap<ObjectId, u64>), Self> {
160        latest_objects_version::query(self, object_ids)
161    }
162
163    /// Version of the object at this checkpoint.
164    async fn latest_version_at_checkpoint(
165        &self,
166        id: ObjectId,
167        ckpt_num: u64,
168    ) -> std::result::Result<u64, LatestVersionAtCheckpointError<Self::Error>> {
169        latest_version_at_checkpoint_v2::query(self, id, ckpt_num)
170    }
171
172    /// Get the object argument for a programmable transaction.
173    async fn object_arg(&self, id: ObjectId) -> Result<ObjectArg, Self> {
174        object_arg::query(self, id)
175    }
176
177    /// Turn a bijective map of names and object ids into one of names and object args.
178    ///
179    /// Fails if the query response does not have the necessary data for the input map.
180    async fn object_args(
181        &self,
182        names: BiMap<String, ObjectId>,
183        page_size: Option<u32>,
184    ) -> std::result::Result<BiMap<String, ObjectArg>, ObjectArgsError<Self::Error>> {
185        object_args::query(self, names, page_size)
186    }
187
188    /// Get a sequence of object args and contents corresponding to `object_ids`, but not
189    /// necessarily in the same order.
190    ///
191    /// **NOTE**: prefer [`GraphQlClientExt::latest_full_objects`] instead and call `Object::object_arg`
192    /// on each returned object.
193    ///
194    /// The `mutable` argument controls whether we want to create mutable [`ObjectArg`]s, if they
195    /// are of the [`ObjectArg::SharedObject`] variant.
196    ///
197    /// Fails if any object in the response is missing data.
198    async fn object_args_and_content(
199        &self,
200        object_ids: impl IntoIterator<Item = ObjectId> + Send,
201        mutable: bool,
202        page_size: Option<u32>,
203    ) -> std::result::Result<Vec<(ObjectArg, RawMoveStruct)>, ObjectArgsAndContentError<Self::Error>>
204    {
205        object_args_and_content::query(self, object_ids, mutable, page_size)
206    }
207
208    /// Get the raw Move struct of an object's content.
209    async fn object_content(
210        &self,
211        object_id: ObjectId,
212        version: Option<u64>,
213    ) -> Result<RawMoveStruct, Self> {
214        object_content::query(self, object_id, version)
215    }
216
217    async fn objects_content(
218        &self,
219        object_ids: Vec<ObjectId>,
220    ) -> Result<HashMap<ObjectId, RawMoveStruct>, Self> {
221        objects_content::query(self, object_ids)
222    }
223
224    /// Get the raw Move value of a dynamic field's value.
225    async fn owner_df_content(
226        &self,
227        address: SuiAddress,
228        raw_move_value: RawMoveValue,
229        root_version: Option<u64>,
230    ) -> Result<RawMoveValue, Self> {
231        owner_df_content::query(self, address, raw_move_value, root_version)
232    }
233
234    /// Map of all keys to dynamic field values: [`RawMoveValue`] -> [`DynamicField`].
235    async fn owner_df_contents(
236        &self,
237        address: SuiAddress,
238        root_version: Option<u64>,
239        first: Option<i32>,
240        after: Option<String>,
241    ) -> Result<(HashMap<RawMoveValue, DynamicField>, Option<String>), Self> {
242        owner_df_contents::query(self, address, root_version, first, after)
243    }
244
245    /// **Streamed** map of all keys to dynamic field values: [`RawMoveValue`] -> [`DynamicField`].
246    async fn owner_df_contents_stream(
247        &self,
248        address: SuiAddress,
249        root_version: Option<u64>,
250        page_size: Option<i32>,
251    ) -> impl Stream<Item = Result<(RawMoveValue, DynamicField), Self>> + '_ {
252        owner_df_contents_stream::query(self, address, root_version, page_size)
253    }
254
255    /// Get the raw Move struct of a dynamic object field's value.
256    async fn owner_dof_content(
257        &self,
258        address: SuiAddress,
259        raw_move_value: RawMoveValue,
260        root_version: Option<u64>,
261    ) -> Result<(ObjectKey, RawMoveStruct), Self> {
262        owner_dof_content::query(self, address, raw_move_value, root_version)
263    }
264
265    /// Get the latest version of a package.
266    ///
267    /// Returns both its ID and version number.
268    async fn latest_package(&self, package_id: ObjectId) -> Result<(ObjectId, Version), Self> {
269        latest_package::query(self, package_id)
270    }
271
272    /// Get the address of a package at a specific version.
273    async fn package_at_version(
274        &self,
275        package_id: ObjectId,
276        version: Version,
277    ) -> Result<ObjectId, Self> {
278        package_at_version::query(self, package_id, version)
279    }
280
281    /// Get all the package ids and versions given the original package id.
282    async fn packages_from_original(
283        &self,
284        package_id: ObjectId,
285    ) -> Result<impl Iterator<Item = (ObjectId, u64)>, Self> {
286        packages_from_original::query(self, package_id)
287    }
288
289    /// The epoch and checkpoint number (in this order) for each package id.
290    async fn packages_published_epoch(
291        &self,
292        package_ids: Vec<ObjectId>,
293    ) -> Result<impl Iterator<Item = (ObjectId, u64, u64)>, Self> {
294        packages_published_epoch::query(self, package_ids)
295    }
296
297    /// The reference gas price for the latest epoch.
298    async fn reference_gas_price(&self) -> Result<u64, Self> {
299        reference_gas_price::query(self)
300    }
301
302    /// Get execution status for the input transaction digests
303    #[expect(deprecated, reason = "Internal module deprecation")]
304    async fn transaction_blocks_status(
305        &self,
306        transaction_digests: Vec<String>,
307    ) -> Result<impl Iterator<Item = crate::extract::Result<(String, bool)>>, Self> {
308        transaction_blocks_status::query(self, transaction_digests)
309    }
310
311    /// Gas coins to satisfy the budget, excluding some object ids.
312    ///
313    /// The `exclude` is here because it can be useful if a SUI coin is already being used in the
314    /// PTB itself. However in such a scenario one can use [`Argument::Gas`] instead.
315    ///
316    /// [`Argument::Gas`]: af_sui_types::Argument::Gas
317    async fn gas_payment(
318        &self,
319        sponsor: SuiAddress,
320        budget: u64,
321        exclude: Vec<ObjectId>,
322    ) -> std::result::Result<Vec<ObjectRef>, GasPaymentError<Self::Error>> {
323        gas_payment::query(self, sponsor, budget, exclude)
324    }
325
326    /// The maximum size for pagination allowed by the server.
327    async fn max_page_size(&self) -> Result<i32, Self> {
328        max_page_size::query(self)
329    }
330
331    /// Struct type of an object given its ID.
332    async fn object_type(&self, id: ObjectId) -> Result<StructTag, Self> {
333        object_type::query(self, id)
334    }
335}
336
337impl<T: GraphQlClient> GraphQlClientExt for T {}
338
339/// Generic error type for queries.
340#[derive(thiserror::Error, Clone, Debug)]
341pub enum Error<C: std::error::Error> {
342    #[error("Client error: {0:?}")]
343    Client(C),
344    #[error("In server response: {0}")]
345    Server(#[from] GraphQlErrors),
346    #[error("Missing data in response: {0}")]
347    MissingData(String),
348}
349
350#[expect(deprecated, reason = "Internal module deprecation")]
351impl<C: std::error::Error> From<crate::extract::Error> for Error<C> {
352    fn from(value: crate::extract::Error) -> Self {
353        Self::MissingData(value.0)
354    }
355}
356
357impl<C: std::error::Error> From<&'static str> for Error<C> {
358    fn from(value: &'static str) -> Self {
359        Self::MissingData(value.into())
360    }
361}
362
363/// Helper to generate [`Error::MissingData`].
364///
365/// Works very much like an `anyhow!`/`eyre!` macro, but intended for the case when trying to
366/// extract some data from the query.
367#[macro_export]
368macro_rules! missing_data {
369    ($($msg:tt)*) => {
370        $crate::queries::Error::MissingData(format!($($msg)*))
371    };
372}