af_iperps/graphql/
ch_positions.rs1use af_move_type::MoveInstance;
2use af_sui_types::Address;
3use enum_as_inner::EnumAsInner;
4use futures::Stream;
5pub use sui_gql_client::queries::Error;
6use sui_gql_client::queries::model::fragments::{MoveValueGql, PageInfoForward};
7use sui_gql_client::{GraphQlClient, GraphQlResponseExt as _, schema};
8
9type Position = MoveInstance<crate::position::Position>;
10
11pub(super) fn query<C: GraphQlClient>(
12 client: &C,
13 ch: Address,
14 at_checkpoint: Option<u64>,
15) -> impl Stream<Item = Result<(u64, Position), Error<C::Error>>> + '_ {
16 async_stream::try_stream! {
17 let mut vars = Variables {
18 ch,
19 at_checkpoint,
20 first: Some(32),
21 after: None,
22 };
23 let mut has_next_page = true;
24 while has_next_page {
25 let (page_info, positions) = request(client, vars.clone()).await?;
26
27 vars.after = page_info.end_cursor.clone();
28 has_next_page = page_info.has_next_page;
29
30 for value in positions {
31 yield value;
32 }
33 }
34 }
35}
36
37async fn request<C: GraphQlClient>(
38 client: &C,
39 vars: Variables,
40) -> Result<
41 (
42 PageInfoForward,
43 impl Iterator<Item = (u64, Position)> + 'static,
44 ),
45 Error<C::Error>,
46> {
47 let response = client
48 .query::<Query, _>(vars)
49 .await
50 .map_err(Error::Client)?;
51 let data = response.try_into_data()?;
52
53 let ChDfsConnection { nodes, page_info } = extract(data)?;
54 Ok((page_info, nodes.into_iter().filter_map(filter_df)))
55}
56
57fn extract(data: Option<Query>) -> Result<ChDfsConnection, &'static str> {
58 graphql_extract::extract!(data => {
59 clearing_house? {
60 dfs?
61 }
62 });
63 Ok(dfs)
64}
65
66fn filter_df(df: ChDf) -> Option<(u64, Position)> {
67 let df_name: MoveInstance<crate::keys::Position> = df.df_name?.try_into().ok()?;
68 let df_value_raw = df.df_value?.into_move_value().ok();
69 let df_value: Position = df_value_raw?.try_into().ok()?;
70
71 Some((df_name.value.account_id, df_value))
72}
73
74#[cfg(test)]
75#[allow(clippy::unwrap_used)]
76#[test]
77fn gql_output() {
78 use cynic::QueryBuilder as _;
79
80 let vars = Variables {
81 ch: Address::ZERO,
82 at_checkpoint: None,
83 first: Some(10),
84 after: None,
85 };
86 let operation = Query::build(vars);
87 insta::assert_snapshot!(operation.query, @r"
88 query Query($ch: SuiAddress!, $atCheckpoint: UInt53, $first: Int, $after: String) {
89 clearing_house: object(address: $ch, atCheckpoint: $atCheckpoint) {
90 dfs: dynamicFields(first: $first, after: $after) {
91 nodes {
92 df_name: name {
93 type {
94 repr
95 }
96 bcs
97 }
98 df_value: value {
99 __typename
100 ... on MoveValue {
101 type {
102 repr
103 }
104 bcs
105 }
106 }
107 }
108 pageInfo {
109 hasNextPage
110 endCursor
111 }
112 }
113 }
114 }
115 ");
116}
117
118#[derive(cynic::QueryVariables, Clone, Debug)]
119struct Variables {
120 ch: Address,
121 at_checkpoint: Option<u64>,
122 first: Option<i32>,
123 after: Option<String>,
124}
125
126#[derive(cynic::QueryFragment, Debug)]
127#[cynic(variables = "Variables")]
128struct Query {
129 #[arguments(address: $ch, atCheckpoint: $at_checkpoint)]
130 #[cynic(alias, rename = "object")]
131 clearing_house: Option<ClearingHouseObject>,
132}
133
134#[derive(cynic::QueryFragment, Debug)]
135#[cynic(graphql_type = "Object", variables = "Variables")]
136struct ClearingHouseObject {
137 #[arguments(first: $first, after: $after)]
138 #[cynic(alias, rename = "dynamicFields")]
139 dfs: Option<ChDfsConnection>,
140}
141
142#[derive(cynic::QueryFragment, Debug)]
143#[cynic(graphql_type = "DynamicFieldConnection")]
144struct ChDfsConnection {
145 nodes: Vec<ChDf>,
146 page_info: PageInfoForward,
147}
148
149#[derive(cynic::QueryFragment, Debug)]
150#[cynic(graphql_type = "DynamicField")]
151struct ChDf {
152 #[cynic(alias, rename = "name")]
153 df_name: Option<MoveValueGql>,
154 #[cynic(alias, rename = "value")]
155 df_value: Option<ChDfValue>,
156}
157
158#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
159#[cynic(graphql_type = "DynamicFieldValue")]
160enum ChDfValue {
161 MoveValue(MoveValueGql),
162 #[cynic(fallback)]
163 Unknown,
164}