Skip to main content

fuel_core_client/client/schema/
coins.rs

1use crate::client::{
2    PageDirection,
3    PaginationRequest,
4    schema::{
5        Address,
6        AssetId,
7        Nonce,
8        PageInfo,
9        U16,
10        U32,
11        U64,
12        U128,
13        UtxoId,
14        schema,
15    },
16};
17use fuel_core_types::{
18    fuel_tx,
19    fuel_types,
20};
21
22#[derive(cynic::QueryVariables, Debug, Clone)]
23pub struct CoinByIdArgs {
24    pub utxo_id: UtxoId,
25}
26
27#[derive(cynic::QueryFragment, Clone, Debug)]
28#[cynic(
29    schema_path = "./assets/schema.sdl",
30    graphql_type = "Query",
31    variables = "CoinByIdArgs"
32)]
33pub struct CoinByIdQuery {
34    #[arguments(utxoId: $ utxo_id)]
35    pub coin: Option<Coin>,
36}
37
38#[derive(cynic::InputObject, Clone, Debug)]
39#[cynic(schema_path = "./assets/schema.sdl")]
40pub struct CoinFilterInput {
41    /// Filter coins based on the `owner` field
42    pub owner: Address,
43    /// Filter coins based on the `asset_id` field
44    pub asset_id: Option<AssetId>,
45}
46
47#[derive(cynic::QueryVariables, Debug, Clone)]
48pub struct CoinsConnectionArgs {
49    /// Filter coins based on a filter
50    filter: CoinFilterInput,
51    /// Skip until coin id (forward pagination)
52    pub after: Option<String>,
53    /// Skip until coin id (backward pagination)
54    pub before: Option<String>,
55    /// Retrieve the first n coins in order (forward pagination)
56    pub first: Option<i32>,
57    /// Retrieve the last n coins in order (backward pagination).
58    /// Can't be used at the same time as `first`.
59    pub last: Option<i32>,
60}
61
62impl From<(Address, Option<AssetId>, PaginationRequest<String>)> for CoinsConnectionArgs {
63    fn from(r: (Address, Option<AssetId>, PaginationRequest<String>)) -> Self {
64        match r.2.direction {
65            PageDirection::Forward => CoinsConnectionArgs {
66                filter: CoinFilterInput {
67                    owner: r.0,
68                    asset_id: r.1,
69                },
70                after: r.2.cursor,
71                before: None,
72                first: Some(r.2.results),
73                last: None,
74            },
75            PageDirection::Backward => CoinsConnectionArgs {
76                filter: CoinFilterInput {
77                    owner: r.0,
78                    asset_id: r.1,
79                },
80                after: None,
81                before: r.2.cursor,
82                first: None,
83                last: Some(r.2.results),
84            },
85        }
86    }
87}
88
89#[derive(cynic::QueryFragment, Clone, Debug)]
90#[cynic(
91    schema_path = "./assets/schema.sdl",
92    graphql_type = "Query",
93    variables = "CoinsConnectionArgs"
94)]
95pub struct CoinsQuery {
96    #[arguments(filter: $ filter, after: $ after, before: $ before, first: $ first, last: $ last)]
97    pub coins: CoinConnection,
98}
99
100#[derive(cynic::QueryFragment, Clone, Debug)]
101#[cynic(schema_path = "./assets/schema.sdl")]
102pub struct CoinConnection {
103    pub edges: Vec<CoinEdge>,
104    pub page_info: PageInfo,
105}
106
107#[derive(cynic::QueryFragment, Clone, Debug)]
108#[cynic(schema_path = "./assets/schema.sdl")]
109pub struct CoinEdge {
110    pub cursor: String,
111    pub node: Coin,
112}
113
114#[derive(cynic::QueryFragment, Debug, Clone)]
115#[cynic(schema_path = "./assets/schema.sdl")]
116pub struct Coin {
117    pub amount: U64,
118    pub block_created: U32,
119    pub tx_created_idx: U16,
120    pub asset_id: AssetId,
121    pub utxo_id: UtxoId,
122    pub owner: Address,
123}
124
125#[derive(cynic::QueryFragment, Clone, Debug)]
126#[cynic(schema_path = "./assets/schema.sdl", graphql_type = "Coin")]
127pub struct CoinIdFragment {
128    pub utxo_id: UtxoId,
129}
130
131#[derive(cynic::InputObject, Clone, Debug)]
132#[cynic(schema_path = "./assets/schema.sdl")]
133pub struct ExcludeInput {
134    /// Utxos to exclude from the result.
135    utxos: Vec<UtxoId>,
136    /// Messages to exclude from the result.
137    messages: Vec<Nonce>,
138}
139
140impl From<(Vec<UtxoId>, Vec<Nonce>)> for ExcludeInput {
141    fn from(value: (Vec<UtxoId>, Vec<Nonce>)) -> Self {
142        let (utxos, messages) = value;
143        Self { utxos, messages }
144    }
145}
146
147impl From<(Vec<fuel_tx::UtxoId>, Vec<fuel_types::Nonce>)> for ExcludeInput {
148    fn from(value: (Vec<fuel_tx::UtxoId>, Vec<fuel_types::Nonce>)) -> Self {
149        let vectors: (Vec<UtxoId>, Vec<Nonce>) = (
150            value.0.into_iter().map(Into::into).collect(),
151            value.1.into_iter().map(Into::into).collect(),
152        );
153
154        vectors.into()
155    }
156}
157
158#[derive(cynic::InputObject, Clone, Debug)]
159#[cynic(schema_path = "./assets/schema.sdl")]
160pub struct SpendQueryElementInput {
161    /// asset ID of the coins
162    pub asset_id: AssetId,
163    /// the amount to cover with this asset
164    pub amount: U128,
165    /// the maximum number of coins per asset from the owner to return.
166    pub max: Option<U16>,
167}
168
169#[derive(cynic::QueryFragment, Debug, Clone)]
170#[cynic(schema_path = "./assets/schema.sdl")]
171pub struct MessageCoin {
172    pub amount: U64,
173    pub sender: Address,
174    pub recipient: Address,
175    pub nonce: Nonce,
176    pub da_height: U64,
177}
178
179#[derive(cynic::InlineFragments, Debug, Clone)]
180#[cynic(schema_path = "./assets/schema.sdl")]
181pub enum CoinType {
182    Coin(Coin),
183    MessageCoin(MessageCoin),
184    #[cynic(fallback)]
185    Unknown,
186}
187
188impl CoinType {
189    pub fn amount(&self) -> u64 {
190        match self {
191            CoinType::Coin(c) => c.amount.0,
192            CoinType::MessageCoin(m) => m.amount.0,
193            CoinType::Unknown => 0,
194        }
195    }
196}
197
198#[derive(cynic::QueryVariables, Debug, Clone)]
199pub struct CoinsToSpendArgs {
200    /// The `Address` of the assets' coins owner.
201    owner: Address,
202    /// The total amount of each asset type to spend.
203    query_per_asset: Vec<SpendQueryElementInput>,
204    /// A list of ids to exclude from the selection.
205    excluded_ids: Option<ExcludeInput>,
206}
207
208pub(crate) type CoinsToSpendArgsTuple =
209    (Address, Vec<SpendQueryElementInput>, Option<ExcludeInput>);
210
211impl From<CoinsToSpendArgsTuple> for CoinsToSpendArgs {
212    fn from(r: CoinsToSpendArgsTuple) -> Self {
213        CoinsToSpendArgs {
214            owner: r.0,
215            query_per_asset: r.1,
216            excluded_ids: r.2,
217        }
218    }
219}
220
221#[derive(cynic::QueryFragment, Clone, Debug)]
222#[cynic(
223    schema_path = "./assets/schema.sdl",
224    graphql_type = "Query",
225    variables = "CoinsToSpendArgs"
226)]
227pub struct CoinsToSpendQuery {
228    #[arguments(owner: $ owner, queryPerAsset: $ query_per_asset, excludedIds: $ excluded_ids)]
229    pub coins_to_spend: Vec<Vec<CoinType>>,
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235
236    #[test]
237    fn coin_by_id_query_gql_output() {
238        use cynic::QueryBuilder;
239        let operation = CoinByIdQuery::build(CoinByIdArgs {
240            utxo_id: UtxoId::default(),
241        });
242        insta::assert_snapshot!(operation.query)
243    }
244
245    #[test]
246    fn coins_connection_query_gql_output() {
247        use cynic::QueryBuilder;
248        let operation = CoinsQuery::build(CoinsConnectionArgs {
249            filter: CoinFilterInput {
250                owner: Address::default(),
251                asset_id: Some(AssetId::default()),
252            },
253            after: None,
254            before: None,
255            first: None,
256            last: None,
257        });
258        insta::assert_snapshot!(operation.query)
259    }
260}