fuel_gql_client/client/schema/
tx.rs

1use super::block::BlockIdFragment;
2use crate::client::{
3    schema::{
4        schema,
5        Address,
6        ConnectionArgs,
7        ConversionError,
8        HexString,
9        PageInfo,
10        Tai64Timestamp,
11        TransactionId,
12    },
13    types::TransactionResponse,
14    PageDirection,
15    PaginatedResult,
16    PaginationRequest,
17};
18use fuel_vm::fuel_types::{
19    bytes::Deserializable,
20    Bytes32,
21};
22use std::convert::{
23    TryFrom,
24    TryInto,
25};
26
27pub mod transparent_receipt;
28pub mod transparent_tx;
29
30#[derive(cynic::QueryVariables, Debug)]
31pub struct TxIdArgs {
32    pub id: TransactionId,
33}
34
35/// Retrieves the transaction in opaque form
36#[derive(cynic::QueryFragment, Debug)]
37#[cynic(
38    schema_path = "./assets/schema.sdl",
39    graphql_type = "Query",
40    variables = "TxIdArgs"
41)]
42pub struct TransactionQuery {
43    #[arguments(id: $id)]
44    pub transaction: Option<OpaqueTransaction>,
45}
46
47#[derive(cynic::QueryFragment, Debug)]
48#[cynic(
49    schema_path = "./assets/schema.sdl",
50    graphql_type = "Query",
51    variables = "ConnectionArgs"
52)]
53pub struct TransactionsQuery {
54    #[arguments(after: $after, before: $before, first: $first, last: $last)]
55    pub transactions: TransactionConnection,
56}
57
58#[derive(cynic::QueryFragment, Debug)]
59#[cynic(schema_path = "./assets/schema.sdl")]
60pub struct TransactionConnection {
61    pub edges: Vec<TransactionEdge>,
62    pub page_info: PageInfo,
63}
64
65impl TryFrom<TransactionConnection> for PaginatedResult<TransactionResponse, String> {
66    type Error = ConversionError;
67
68    fn try_from(conn: TransactionConnection) -> Result<Self, Self::Error> {
69        let results: Result<Vec<TransactionResponse>, Self::Error> =
70            conn.edges.into_iter().map(|e| e.node.try_into()).collect();
71
72        Ok(PaginatedResult {
73            cursor: conn.page_info.end_cursor,
74            has_next_page: conn.page_info.has_next_page,
75            has_previous_page: conn.page_info.has_previous_page,
76            results: results?,
77        })
78    }
79}
80
81#[derive(cynic::QueryFragment, Debug)]
82#[cynic(schema_path = "./assets/schema.sdl")]
83pub struct TransactionEdge {
84    pub cursor: String,
85    pub node: OpaqueTransaction,
86}
87
88#[derive(cynic::QueryFragment, Debug)]
89#[cynic(graphql_type = "Transaction", schema_path = "./assets/schema.sdl")]
90pub struct OpaqueTransaction {
91    pub raw_payload: HexString,
92    pub receipts: Option<Vec<OpaqueReceipt>>,
93    pub status: Option<TransactionStatus>,
94}
95
96impl TryFrom<OpaqueTransaction> for ::fuel_vm::fuel_tx::Transaction {
97    type Error = ConversionError;
98
99    fn try_from(value: OpaqueTransaction) -> Result<Self, Self::Error> {
100        let bytes = value.raw_payload.0 .0;
101        ::fuel_vm::fuel_tx::Transaction::from_bytes(bytes.as_slice())
102            .map_err(ConversionError::TransactionFromBytesError)
103    }
104}
105
106#[derive(cynic::QueryFragment, Debug)]
107#[cynic(schema_path = "./assets/schema.sdl", graphql_type = "Transaction")]
108pub struct TransactionIdFragment {
109    pub id: TransactionId,
110}
111
112#[derive(cynic::QueryFragment, Debug)]
113#[cynic(graphql_type = "Receipt", schema_path = "./assets/schema.sdl")]
114pub struct OpaqueReceipt {
115    pub raw_payload: HexString,
116}
117
118impl TryFrom<OpaqueReceipt> for ::fuel_vm::fuel_tx::Receipt {
119    type Error = ConversionError;
120
121    fn try_from(value: OpaqueReceipt) -> Result<Self, Self::Error> {
122        let bytes = value.raw_payload.0 .0;
123        ::fuel_vm::fuel_tx::Receipt::from_bytes(bytes.as_slice())
124            .map_err(ConversionError::ReceiptFromBytesError)
125    }
126}
127
128#[derive(cynic::Enum, Copy, Clone, Debug)]
129#[cynic(schema_path = "./assets/schema.sdl")]
130pub enum ReturnType {
131    Return,
132    ReturnData,
133    Revert,
134}
135
136#[derive(cynic::QueryFragment, Clone, Debug)]
137#[cynic(graphql_type = "ProgramState", schema_path = "./assets/schema.sdl")]
138pub struct ProgramState {
139    pub return_type: ReturnType,
140    pub data: HexString,
141}
142
143impl TryFrom<ProgramState> for fuel_vm::prelude::ProgramState {
144    type Error = ConversionError;
145
146    fn try_from(state: ProgramState) -> Result<Self, Self::Error> {
147        Ok(match state.return_type {
148            ReturnType::Return => fuel_vm::prelude::ProgramState::Return({
149                let b = state.data.0 .0;
150                let b: [u8; 8] =
151                    b.try_into().map_err(|_| ConversionError::BytesLength)?;
152                u64::from_be_bytes(b)
153            }),
154            ReturnType::ReturnData => fuel_vm::prelude::ProgramState::ReturnData({
155                Bytes32::try_from(state.data.0 .0.as_slice())?
156            }),
157            ReturnType::Revert => fuel_vm::prelude::ProgramState::Revert({
158                let b = state.data.0 .0;
159                let b: [u8; 8] =
160                    b.try_into().map_err(|_| ConversionError::BytesLength)?;
161                u64::from_be_bytes(b)
162            }),
163        })
164    }
165}
166
167#[allow(clippy::enum_variant_names)]
168#[derive(cynic::InlineFragments, Debug)]
169#[cynic(schema_path = "./assets/schema.sdl")]
170pub enum TransactionStatus {
171    SubmittedStatus(SubmittedStatus),
172    SuccessStatus(SuccessStatus),
173    SqueezedOutStatus(SqueezedOutStatus),
174    FailureStatus(FailureStatus),
175    #[cynic(fallback)]
176    Unknown,
177}
178
179#[derive(cynic::QueryFragment, Debug)]
180#[cynic(schema_path = "./assets/schema.sdl")]
181pub struct SubmittedStatus {
182    pub time: Tai64Timestamp,
183}
184
185#[derive(cynic::QueryFragment, Debug)]
186#[cynic(schema_path = "./assets/schema.sdl")]
187pub struct SuccessStatus {
188    pub block: BlockIdFragment,
189    pub time: Tai64Timestamp,
190    pub program_state: Option<ProgramState>,
191}
192
193#[derive(cynic::QueryFragment, Debug)]
194#[cynic(schema_path = "./assets/schema.sdl")]
195pub struct FailureStatus {
196    pub block: BlockIdFragment,
197    pub time: Tai64Timestamp,
198    pub reason: String,
199    pub program_state: Option<ProgramState>,
200}
201
202#[derive(cynic::QueryFragment, Debug)]
203#[cynic(schema_path = "./assets/schema.sdl")]
204pub struct SqueezedOutStatus {
205    pub reason: String,
206}
207
208#[derive(cynic::QueryVariables, Debug)]
209pub struct TransactionsByOwnerConnectionArgs {
210    /// Select transactions based on related `owner`s
211    pub owner: Address,
212    /// Skip until cursor (forward pagination)
213    pub after: Option<String>,
214    /// Skip until cursor (backward pagination)
215    pub before: Option<String>,
216    /// Retrieve the first n transactions in order (forward pagination)
217    pub first: Option<i32>,
218    /// Retrieve the last n transactions in order (backward pagination).
219    /// Can't be used at the same time as `first`.
220    pub last: Option<i32>,
221}
222
223impl From<(Address, PaginationRequest<String>)> for TransactionsByOwnerConnectionArgs {
224    fn from(r: (Address, PaginationRequest<String>)) -> Self {
225        match r.1.direction {
226            PageDirection::Forward => TransactionsByOwnerConnectionArgs {
227                owner: r.0,
228                after: r.1.cursor,
229                before: None,
230                first: Some(r.1.results as i32),
231                last: None,
232            },
233            PageDirection::Backward => TransactionsByOwnerConnectionArgs {
234                owner: r.0,
235                after: None,
236                before: r.1.cursor,
237                first: None,
238                last: Some(r.1.results as i32),
239            },
240        }
241    }
242}
243
244#[derive(cynic::QueryFragment, Debug)]
245#[cynic(
246    schema_path = "./assets/schema.sdl",
247    graphql_type = "Query",
248    variables = "TransactionsByOwnerConnectionArgs"
249)]
250pub struct TransactionsByOwnerQuery {
251    #[arguments(owner: $owner, after: $after, before: $before, first: $first, last: $last)]
252    pub transactions_by_owner: TransactionConnection,
253}
254
255#[derive(cynic::QueryFragment, Debug)]
256#[cynic(
257    schema_path = "./assets/schema.sdl",
258    graphql_type = "Subscription",
259    variables = "TxIdArgs"
260)]
261pub struct StatusChangeSubscription {
262    #[arguments(id: $id)]
263    pub status_change: TransactionStatus,
264}
265
266// mutations
267
268#[derive(cynic::QueryVariables)]
269pub struct TxArg {
270    pub tx: HexString,
271}
272
273#[derive(cynic::QueryVariables)]
274pub struct DryRunArg {
275    pub tx: HexString,
276    pub utxo_validation: Option<bool>,
277}
278
279#[derive(cynic::QueryFragment, Debug)]
280#[cynic(
281    schema_path = "./assets/schema.sdl",
282    graphql_type = "Mutation",
283    variables = "DryRunArg"
284)]
285pub struct DryRun {
286    #[arguments(tx: $tx, utxoValidation: $utxo_validation)]
287    pub dry_run: Vec<transparent_receipt::Receipt>,
288}
289
290#[derive(cynic::QueryFragment, Debug)]
291#[cynic(
292    schema_path = "./assets/schema.sdl",
293    graphql_type = "Mutation",
294    variables = "TxArg"
295)]
296pub struct Submit {
297    #[arguments(tx: $tx)]
298    pub submit: TransactionIdFragment,
299}
300
301#[cfg(test)]
302pub mod tests {
303    use super::*;
304    use crate::client::schema::Bytes;
305    use fuel_vm::fuel_types::bytes::SerializableVec;
306
307    #[test]
308    fn transparent_transaction_by_id_query_gql_output() {
309        use cynic::QueryBuilder;
310        let operation = transparent_tx::TransactionQuery::build(TxIdArgs {
311            id: TransactionId::default(),
312        });
313        insta::assert_snapshot!(operation.query)
314    }
315
316    #[test]
317    fn opaque_transaction_by_id_query_gql_output() {
318        use cynic::QueryBuilder;
319        let operation = TransactionQuery::build(TxIdArgs {
320            id: TransactionId::default(),
321        });
322        insta::assert_snapshot!(operation.query)
323    }
324
325    #[test]
326    fn transactions_connection_query_gql_output() {
327        use cynic::QueryBuilder;
328        let operation = TransactionsQuery::build(ConnectionArgs {
329            after: None,
330            before: None,
331            first: None,
332            last: None,
333        });
334        insta::assert_snapshot!(operation.query)
335    }
336
337    #[test]
338    fn transactions_by_owner_gql_output() {
339        use cynic::QueryBuilder;
340        let operation =
341            TransactionsByOwnerQuery::build(TransactionsByOwnerConnectionArgs {
342                owner: Default::default(),
343                after: None,
344                before: None,
345                first: None,
346                last: None,
347            });
348        insta::assert_snapshot!(operation.query)
349    }
350
351    #[test]
352    fn dry_run_tx_gql_output() {
353        use cynic::MutationBuilder;
354        let mut tx = ::fuel_vm::fuel_tx::Transaction::default();
355        let query = DryRun::build(DryRunArg {
356            tx: HexString(Bytes(tx.to_bytes())),
357            utxo_validation: None,
358        });
359        insta::assert_snapshot!(query.query)
360    }
361
362    #[test]
363    fn submit_tx_gql_output() {
364        use cynic::MutationBuilder;
365        let mut tx = ::fuel_vm::fuel_tx::Transaction::default();
366        let query = Submit::build(TxArg {
367            tx: HexString(Bytes(tx.to_bytes())),
368        });
369        insta::assert_snapshot!(query.query)
370    }
371}