1use af_move_type::MoveInstance;
2use af_sui_types::Address;
3use enum_as_inner::EnumAsInner;
4use futures::Stream;
5use graphql_extract::extract;
6use sui_gql_client::queries::Error;
7use sui_gql_client::queries::model::fragments::{DynamicFieldName, MoveValueGql, PageInfoForward};
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 at_checkpoint: Option<u64>,
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 at_checkpoint,
40 orderbook,
41 map_name,
42 first: Some(32),
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 at_checkpoint: 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!, $atCheckpoint: UInt53, $orderbook: DynamicFieldName!, $mapName: DynamicFieldName!, $first: Int, $after: String) {
127 clearing_house: object(address: $ch, atCheckpoint: $atCheckpoint) {
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 at_checkpoint: Option<u64>,
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, atCheckpoint: $at_checkpoint)]
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: Option<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 .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(MoveValueGql),
271 #[cynic(fallback)]
272 Unknown,
273}