sui_gql_client/queries/
fragments.rs

1use af_sui_types::{Address as SuiAddress, TypeTag, Version};
2use cynic::QueryFragment;
3use sui_gql_schema::{scalars, schema};
4
5// ====================================================================================================
6//  Input objects
7// ====================================================================================================
8
9/// This is only used in `Query.multiGetObjects` currently
10#[derive(cynic::InputObject, Clone, Debug)]
11pub struct ObjectKey {
12    pub object_id: SuiAddress,
13    pub version: Version,
14}
15
16#[derive(cynic::InputObject, Clone, Debug, Default)]
17#[cynic(graphql_type = "ObjectFilter")]
18pub(crate) struct ObjectFilterV2<'a> {
19    /// Filter objects by their type's `package`, `package::module`, or their fully qualified type
20    /// name.
21    ///
22    /// Generic types can be queried by either the generic type name, e.g. `0x2::coin::Coin`, or by
23    /// the full type name, such as `0x2::coin::Coin<0x2::sui::SUI>`.
24    #[cynic(rename = "type")]
25    pub(crate) type_: Option<String>,
26    pub(crate) owner: Option<SuiAddress>,
27    pub(crate) object_ids: Option<&'a [SuiAddress]>,
28}
29
30#[derive(cynic::InputObject, Clone, Debug)]
31pub struct DynamicFieldName {
32    /// The string type of the DynamicField's 'name' field.
33    /// A string representation of a Move primitive like 'u64', or a struct type like '0x2::kiosk::Listing'
34    #[cynic(rename = "type")]
35    pub type_: scalars::TypeTag,
36    /// The Base64 encoded bcs serialization of the DynamicField's 'name' field.
37    pub bcs: scalars::Base64<Vec<u8>>,
38}
39
40#[cfg(feature = "move-type")]
41impl<T: af_move_type::MoveType> TryFrom<af_move_type::MoveInstance<T>> for DynamicFieldName {
42    type Error = bcs::Error;
43
44    fn try_from(value: af_move_type::MoveInstance<T>) -> Result<Self, Self::Error> {
45        let af_move_type::MoveInstance { type_, value } = value;
46        Ok(Self {
47            type_: scalars::TypeTag(type_.into()),
48            bcs: scalars::Base64::new(bcs::to_bytes(&value)?),
49        })
50    }
51}
52
53#[derive(cynic::InputObject, Clone, Debug, Default)]
54pub(crate) struct TransactionBlockFilter {
55    pub(crate) function: Option<String>,
56    pub(crate) kind: Option<TransactionBlockKindInput>,
57    pub(crate) after_checkpoint: Option<Version>,
58    pub(crate) at_checkpoint: Option<Version>,
59    pub(crate) before_checkpoint: Option<Version>,
60    pub(crate) affected_address: Option<SuiAddress>,
61    pub(crate) sent_address: Option<SuiAddress>,
62    pub(crate) input_object: Option<SuiAddress>,
63    pub(crate) changed_object: Option<SuiAddress>,
64    pub(crate) transaction_ids: Option<Vec<String>>,
65}
66
67#[derive(cynic::Enum, Clone, Debug)]
68pub(crate) enum TransactionBlockKindInput {
69    SystemTx,
70    ProgrammableTx,
71}
72
73// ====================================================================================================
74//  Simple fragments
75// ====================================================================================================
76
77#[derive(cynic::QueryFragment, Clone, Debug, Default)]
78pub(crate) struct PageInfo {
79    pub(crate) has_next_page: bool,
80    pub(crate) end_cursor: Option<String>,
81    #[expect(dead_code, reason = "For generality")]
82    pub(crate) has_previous_page: bool,
83    #[expect(dead_code, reason = "For generality")]
84    pub(crate) start_cursor: Option<String>,
85}
86
87impl From<PageInfoForward> for PageInfo {
88    fn from(
89        PageInfoForward {
90            has_next_page,
91            end_cursor,
92        }: PageInfoForward,
93    ) -> Self {
94        Self {
95            has_next_page,
96            end_cursor,
97            ..Default::default()
98        }
99    }
100}
101
102impl From<PageInfoBackward> for PageInfo {
103    fn from(
104        PageInfoBackward {
105            has_previous_page,
106            start_cursor,
107        }: PageInfoBackward,
108    ) -> Self {
109        Self {
110            has_previous_page,
111            start_cursor,
112            ..Default::default()
113        }
114    }
115}
116
117#[derive(cynic::QueryFragment, Clone, Debug)]
118#[cynic(graphql_type = "PageInfo")]
119pub struct PageInfoForward {
120    pub has_next_page: bool,
121    pub end_cursor: Option<String>,
122}
123
124#[derive(cynic::QueryFragment, Clone, Debug)]
125#[cynic(graphql_type = "PageInfo")]
126pub struct PageInfoBackward {
127    pub has_previous_page: bool,
128    pub start_cursor: Option<String>,
129}
130
131#[derive(cynic::QueryFragment, Clone, Debug)]
132#[cynic(graphql_type = "MoveValue")]
133pub struct MoveValueRaw {
134    #[cynic(rename = "type")]
135    pub type_: MoveTypeTag,
136    pub bcs: scalars::Base64<Vec<u8>>,
137}
138
139impl From<MoveValueRaw> for super::outputs::RawMoveValue {
140    fn from(MoveValueRaw { type_, bcs }: MoveValueRaw) -> Self {
141        Self {
142            type_: type_.into(),
143            bcs: bcs.into_inner(),
144        }
145    }
146}
147
148#[derive(thiserror::Error, Debug)]
149#[error("TypeTag is not a Struct variant")]
150pub struct NotMoveStructError;
151
152impl TryFrom<MoveValueRaw> for super::outputs::RawMoveStruct {
153    type Error = NotMoveStructError;
154    fn try_from(MoveValueRaw { type_, bcs }: MoveValueRaw) -> Result<Self, Self::Error> {
155        let tag: TypeTag = type_.into();
156        let TypeTag::Struct(stag) = tag else {
157            return Err(NotMoveStructError);
158        };
159        Ok(Self {
160            type_: *stag,
161            bcs: bcs.into_inner(),
162        })
163    }
164}
165
166#[cfg(feature = "move-type")]
167impl<T> TryFrom<MoveValueRaw> for af_move_type::MoveInstance<T>
168where
169    T: af_move_type::MoveType,
170{
171    type Error = ToMoveInstanceError;
172    fn try_from(MoveValueRaw { bcs, type_ }: MoveValueRaw) -> Result<Self, Self::Error> {
173        // Fail early if type tag is not expected
174        let type_ = TypeTag::from(type_).try_into()?;
175        let value = bcs::from_bytes(bcs.as_ref())?;
176        Ok(Self { type_, value })
177    }
178}
179
180#[cfg(feature = "move-type")]
181#[derive(thiserror::Error, Debug)]
182pub enum ToMoveInstanceError {
183    #[error("Mismatched types: {0}")]
184    TypeTag(#[from] af_move_type::TypeTagError),
185    #[error("Deserializing value: {0}")]
186    Bcs(#[from] bcs::Error),
187}
188
189/// Helper to extract a strongly typed [`TypeTag`] from the `MoveType` GQL type.
190#[derive(cynic::QueryFragment, Clone)]
191#[cynic(graphql_type = "MoveType")]
192pub struct MoveTypeTag {
193    /// Keep this private so that we can change where we get the [TypeTag] from.
194    repr: scalars::TypeTag,
195}
196
197impl std::fmt::Debug for MoveTypeTag {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        write!(f, "MoveTypeTag({})", self.repr.0)
200    }
201}
202
203impl From<MoveTypeTag> for TypeTag {
204    fn from(value: MoveTypeTag) -> Self {
205        value.repr.0
206    }
207}
208
209// ====================================================================================================
210//  Internal
211// ====================================================================================================
212
213#[derive(cynic::QueryFragment, Clone, Debug)]
214#[cynic(graphql_type = "MoveObject")]
215pub(super) struct MoveObjectContent<MoveValue = MoveValueRaw>
216where
217    MoveValue: QueryFragment<SchemaType = schema::MoveValue, VariablesFields = ()>,
218{
219    pub(super) contents: Option<MoveValue>,
220}
221
222impl<MoveValue> MoveObjectContent<MoveValue>
223where
224    MoveValue: QueryFragment<SchemaType = schema::MoveValue, VariablesFields = ()>,
225{
226    pub fn into_content(self) -> Option<MoveValue> {
227        self.contents
228    }
229}