Skip to main content

af_iperps/graphql/
ch_vault.rs

1use af_move_type::{FromRawStructError, MoveInstance};
2use af_sui_types::Address;
3use enum_as_inner::EnumAsInner;
4use sui_gql_client::queries::Error as QueryError;
5use sui_gql_client::queries::model::fragments::{DynamicFieldName, MoveValueGql};
6use sui_gql_client::queries::model::outputs::RawMoveStruct;
7use sui_gql_client::{GraphQlClient, GraphQlResponseExt, schema};
8
9use crate::keys;
10
11#[derive(thiserror::Error, Debug)]
12pub enum Error<C: std::error::Error> {
13    #[error(transparent)]
14    Query(#[from] QueryError<C>),
15    #[error("Deserializing Vault instance: {0}")]
16    ToVault(#[from] FromRawStructError),
17}
18
19type Vault = MoveInstance<crate::Vault>;
20
21pub(super) async fn query<C: GraphQlClient>(
22    client: &C,
23    package: Address,
24    ch: Address,
25) -> Result<Vault, Error<C::Error>> {
26    let raw = request(client, package, ch).await?;
27    Ok(raw.try_into()?)
28}
29
30async fn request<C: GraphQlClient>(
31    client: &C,
32    package: Address,
33    ch: Address,
34) -> Result<RawMoveStruct, QueryError<C::Error>> {
35    let vars = Variables {
36        ch,
37        vault: keys::MarketVault::new()
38            .move_instance(package)
39            .try_into()
40            .expect("BCS-serializable"),
41    };
42    let data = client
43        .query::<Query, _>(vars)
44        .await
45        .map_err(QueryError::Client)?
46        .try_into_data()?;
47    Ok(extract(data)?)
48}
49
50fn extract(data: Option<Query>) -> Result<RawMoveStruct, &'static str> {
51    graphql_extract::extract!(data => {
52        ch? {
53            vault? {
54                value? {
55                    ... on VaultDfValue::MoveValue {
56                        type_
57                        bcs
58                    }
59                }
60            }
61        }
62    });
63    let move_value = MoveValueGql { type_, bcs };
64    Ok(move_value.try_into().expect("Vault is a struct"))
65}
66
67#[cfg(test)]
68#[allow(clippy::unwrap_used)]
69#[test]
70fn gql_output() {
71    use cynic::QueryBuilder as _;
72
73    let package = Address::ZERO;
74    let vars = Variables {
75        ch: Address::ZERO,
76        vault: keys::MarketVault::new()
77            .move_instance(package)
78            .try_into()
79            .unwrap(),
80    };
81    let operation = Query::build(vars);
82    insta::assert_snapshot!(operation.query, @r###"
83    query Query($ch: SuiAddress!, $vault: DynamicFieldName!) {
84      ch: object(address: $ch) {
85        vault: dynamicField(name: $vault) {
86          value {
87            __typename
88            ... on MoveValue {
89              type {
90                repr
91              }
92              bcs
93            }
94          }
95        }
96      }
97    }
98    "###);
99}
100
101#[derive(cynic::QueryVariables, Debug)]
102struct Variables {
103    ch: Address,
104    vault: DynamicFieldName,
105}
106
107#[derive(cynic::QueryFragment, Debug)]
108#[cynic(graphql_type = "Query", variables = "Variables")]
109struct Query {
110    #[arguments(address: $ch)]
111    #[cynic(alias, rename = "object")]
112    ch: Option<ClearingHouseObject>,
113}
114
115#[derive(cynic::QueryFragment, Debug)]
116#[cynic(graphql_type = "Object", variables = "Variables")]
117struct ClearingHouseObject {
118    #[arguments(name: $vault)]
119    #[cynic(alias, rename = "dynamicField")]
120    vault: Option<VaultDf>,
121}
122
123#[derive(cynic::QueryFragment, Debug)]
124#[cynic(graphql_type = "DynamicField")]
125struct VaultDf {
126    value: Option<VaultDfValue>,
127}
128
129#[derive(cynic::InlineFragments, Debug, EnumAsInner)]
130#[cynic(graphql_type = "DynamicFieldValue")]
131enum VaultDfValue {
132    MoveValue(MoveValueGql),
133    #[cynic(fallback)]
134    Unknown,
135}