sui_gql_client/queries/
mod.rs

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