sui_gql_client/queries/
mod.rs

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