sui_gql_client/queries/
transactions_by_digests.rs1use af_sui_types::Transaction;
2use itertools::Itertools as _;
3use sui_gql_schema::scalars::Base64Bcs;
4
5use crate::queries::Error;
6use crate::queries::model::fragments::TransactionGql;
7use crate::{GraphQlClient, GraphQlResponseExt as _, schema};
8
9#[derive(cynic::QueryVariables, Clone, Debug)]
10struct Variables {
11 keys: Vec<String>,
12}
13
14#[derive(cynic::QueryFragment, Clone, Debug)]
15#[cynic(variables = "Variables")]
16struct Query {
17 #[arguments(keys: $keys)]
18 multi_get_transactions: Vec<Option<TransactionGql>>,
19}
20
21pub(super) async fn query<C: GraphQlClient>(
22 client: &C,
23 digests: impl IntoIterator<Item = String> + Send,
24) -> super::Result<Vec<Transaction>, C> {
25 let mut digests = digests.into_iter().sorted().dedup().collect_vec();
26
27 let vars = Variables {
28 keys: digests.clone(),
29 };
30
31 let data = client
32 .query::<Query, _>(vars)
33 .await
34 .map_err(Error::Client)?
35 .try_into_data()?;
36
37 graphql_extract::extract!(data => {
38 transactions: multi_get_transactions
39 });
40
41 let returned = transactions
42 .into_iter()
43 .flatten()
44 .filter_map(|o| o.bcs)
45 .map(Base64Bcs::into_inner)
46 .inspect(|t| {
47 digests
48 .iter()
49 .position(|k| k == &t.digest().to_string())
50 .map(|p| digests.swap_remove(p));
51 })
52 .collect_vec();
53
54 if !digests.is_empty() {
55 return Err(Error::MissingData(format!("Digests {digests:?}")));
56 }
57 Ok(returned)
58}
59
60#[cfg(test)]
61#[allow(clippy::unwrap_used)]
62#[test]
63fn gql_output() -> color_eyre::Result<()> {
64 use cynic::QueryBuilder as _;
65
66 let vars = Variables { keys: vec![] };
68
69 let operation = Query::build(vars);
70 insta::assert_snapshot!(operation.query, @r"
71 query Query($keys: [String!]!) {
72 multiGetTransactions(keys: $keys) {
73 digest
74 transactionBcs
75 effects {
76 status
77 }
78 }
79 }
80 ");
81 Ok(())
82}