Skip to main content

af_iperps/graphql/
ch_positions.rs

1use 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}