af_iperps/graphql/
ch_orders.rs

1use af_move_type::MoveInstance;
2use af_sui_types::{Address, Version};
3use enum_as_inner::EnumAsInner;
4use futures::Stream;
5use graphql_extract::extract;
6use sui_gql_client::queries::fragments::{DynamicFieldName, MoveValueRaw, PageInfoForward};
7use sui_gql_client::queries::{Error, GraphQlClientExt as _};
8use sui_gql_client::{GraphQlClient, GraphQlResponseExt as _, schema};
9
10use crate::orderbook::Order;
11use crate::ordered_map::Leaf;
12
13pub(super) fn query<C: GraphQlClient>(
14    client: &C,
15    package: Address,
16    ch: Address,
17    version: Option<Version>,
18    asks: bool,
19) -> impl Stream<Item = Result<(u128, Order), Error<C::Error>>> + '_ {
20    let orderbook: DynamicFieldName = crate::keys::Orderbook::new()
21        .move_instance(package)
22        .try_into()
23        .expect("BCS-serializable");
24
25    let map_name: DynamicFieldName = if asks {
26        crate::keys::AsksMap::new()
27            .move_instance(package)
28            .try_into()
29            .expect("BCS-serializable")
30    } else {
31        crate::keys::BidsMap::new()
32            .move_instance(package)
33            .try_into()
34            .expect("BCS-serializable")
35    };
36    async_stream::try_stream! {
37        let mut vars = Variables {
38            ch,
39            version,
40            orderbook,
41            map_name,
42            first: Some(client.max_page_size().await?),
43            after: None,
44        };
45        let mut has_next_page = true;
46        while has_next_page {
47            let (page_info, orders) = request(client, vars.clone()).await?;
48
49            vars.after = page_info.end_cursor.clone();
50            has_next_page = page_info.has_next_page;
51
52            for value in orders {
53                yield value;
54            }
55        }
56    }
57}
58
59async fn request<C: GraphQlClient>(
60    client: &C,
61    vars: Variables,
62) -> Result<
63    (
64        PageInfoForward,
65        impl Iterator<Item = (u128, Order)> + 'static,
66    ),
67    Error<C::Error>,
68> {
69    let data = client
70        .query::<Query, _>(vars)
71        .await
72        .map_err(Error::Client)?
73        .try_into_data()?;
74    let MapDfsConnection { nodes, page_info } = extract(data)?;
75    Ok((page_info, nodes.into_iter().flat_map(MapDf::into_orders)))
76}
77
78fn extract(data: Option<Query>) -> Result<MapDfsConnection, &'static str> {
79    extract!(data => {
80        clearing_house? {
81            orderbook_dof? {
82                orderbook? {
83                    ... on OrderbookDofValue::MoveObject {
84                        map_dof? {
85                            map? {
86                                ... on MapDofValue::MoveObject {
87                                    map_dfs
88                                }
89                            }
90                        }
91                    }
92                }
93            }
94        }
95    });
96    Ok(map_dfs)
97}
98
99#[cfg(test)]
100#[allow(clippy::unwrap_used)]
101#[test]
102fn gql_output() {
103    use cynic::QueryBuilder as _;
104
105    let package = Address::ZERO;
106    let orderbook: DynamicFieldName = crate::keys::Orderbook::new()
107        .move_instance(package)
108        .try_into()
109        .unwrap();
110
111    let map_name: DynamicFieldName = crate::keys::BidsMap::new()
112        .move_instance(package)
113        .try_into()
114        .unwrap();
115
116    let vars = Variables {
117        ch: Address::ZERO,
118        version: None,
119        orderbook,
120        map_name,
121        first: Some(10),
122        after: None,
123    };
124    let operation = Query::build(vars);
125    insta::assert_snapshot!(operation.query, @r###"
126    query Query($ch: SuiAddress!, $version: UInt53, $orderbook: DynamicFieldName!, $mapName: DynamicFieldName!, $first: Int, $after: String) {
127      clearing_house: object(address: $ch, version: $version) {
128        orderbook_dof: dynamicObjectField(name: $orderbook) {
129          orderbook: value {
130            __typename
131            ... on MoveObject {
132              map_dof: dynamicObjectField(name: $mapName) {
133                map: value {
134                  __typename
135                  ... on MoveObject {
136                    map_dfs: dynamicFields(first: $first, after: $after) {
137                      nodes {
138                        map_df: value {
139                          __typename
140                          ... on MoveValue {
141                            type {
142                              repr
143                            }
144                            bcs
145                          }
146                        }
147                      }
148                      pageInfo {
149                        hasNextPage
150                        endCursor
151                      }
152                    }
153                    __typename
154                  }
155                }
156              }
157              __typename
158            }
159          }
160        }
161      }
162    }
163    "###);
164}
165
166#[derive(cynic::QueryVariables, Clone, Debug)]
167struct Variables {
168    ch: Address,
169    version: Option<Version>,
170    orderbook: DynamicFieldName,
171    map_name: DynamicFieldName,
172    first: Option<i32>,
173    after: Option<String>,
174}
175
176#[derive(cynic::QueryFragment, Debug)]
177#[cynic(variables = "Variables")]
178struct Query {
179    #[arguments(address: $ch, version: $version)]
180    #[cynic(alias, rename = "object")]
181    clearing_house: Option<Object>,
182}
183
184#[derive(cynic::QueryFragment, Debug)]
185#[cynic(graphql_type = "Object", variables = "Variables")]
186struct Object {
187    #[arguments(name: $orderbook)]
188    #[cynic(alias, rename = "dynamicObjectField")]
189    orderbook_dof: Option<OrderbookDof>,
190}
191
192#[derive(cynic::QueryFragment, Debug)]
193#[cynic(graphql_type = "DynamicField", variables = "Variables")]
194struct OrderbookDof {
195    #[cynic(alias, rename = "value")]
196    orderbook: Option<OrderbookDofValue>,
197}
198
199#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
200#[cynic(graphql_type = "DynamicFieldValue", variables = "Variables")]
201enum OrderbookDofValue {
202    MoveObject(OrderbookObject),
203    #[cynic(fallback)]
204    Unknown,
205}
206
207#[derive(cynic::QueryFragment, Debug)]
208#[cynic(graphql_type = "MoveObject", variables = "Variables")]
209struct OrderbookObject {
210    #[arguments(name: $map_name)]
211    #[cynic(alias, rename = "dynamicObjectField")]
212    map_dof: Option<MapDof>,
213    __typename: String,
214}
215
216#[derive(cynic::QueryFragment, Debug)]
217#[cynic(graphql_type = "DynamicField", variables = "Variables")]
218struct MapDof {
219    #[cynic(alias, rename = "value")]
220    map: Option<MapDofValue>,
221}
222
223#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
224#[cynic(graphql_type = "DynamicFieldValue", variables = "Variables")]
225enum MapDofValue {
226    MoveObject(MapObject),
227    #[cynic(fallback)]
228    Unknown,
229}
230
231#[derive(cynic::QueryFragment, Debug)]
232#[cynic(graphql_type = "MoveObject", variables = "Variables")]
233struct MapObject {
234    #[arguments(first: $first, after: $after)]
235    #[cynic(alias, rename = "dynamicFields")]
236    map_dfs: MapDfsConnection,
237    __typename: String,
238}
239
240#[derive(cynic::QueryFragment, Debug)]
241#[cynic(graphql_type = "DynamicFieldConnection")]
242struct MapDfsConnection {
243    nodes: Vec<MapDf>,
244    page_info: PageInfoForward,
245}
246
247#[derive(cynic::QueryFragment, Debug)]
248#[cynic(graphql_type = "DynamicField")]
249struct MapDf {
250    #[cynic(alias, rename = "value")]
251    map_df: Option<MapDfValue>,
252}
253
254impl MapDf {
255    fn into_orders(self) -> impl Iterator<Item = (u128, Order)> {
256        self.map_df
257            .into_iter()
258            .map(MapDfValue::into_move_value)
259            .filter_map(Result::ok)
260            // If deser. fails, we just assume it was a `Branch` instead of a `Leaf`
261            .filter_map(|raw| MoveInstance::<Leaf<Order>>::try_from(raw).ok())
262            .flat_map(|leaf| Vec::from(leaf.value.keys_vals).into_iter())
263            .map(|pair| (pair.key, pair.val))
264    }
265}
266
267#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
268#[cynic(graphql_type = "DynamicFieldValue")]
269enum MapDfValue {
270    MoveValue(MoveValueRaw),
271    #[cynic(fallback)]
272    Unknown,
273}