zilliqa_rs/transaction/
mod.rs

1/*!
2Send transaction to zilliqa network
3
4This module is used to send a transaction to zilliqa network. To compose a transaction,
5It's easier to use [TransactionBuilder].
6
7*/
8
9pub mod builder;
10
11use std::{cell::Cell, fmt::Display};
12
13pub use builder::*;
14use serde::{Serialize, Serializer};
15
16use crate::{
17    core::{GetTransactionResponse, TxHash},
18    middlewares::Middleware,
19    providers::{JsonRpcClient, Provider},
20    Error,
21};
22
23/// A high-level struct to manage a transaction.
24///
25/// This is the high-level representation of a transaction. It can be used to confirm a transaction.
26#[derive(Debug)]
27pub struct Transaction<'a, T: JsonRpcClient> {
28    /// Hash of the transaction.
29    pub id: TxHash,
30    /// Client to confirm transaction.
31    client: &'a Provider<T>,
32    /// Current state of the transaction.
33    status: Cell<TxStatus>,
34}
35
36#[derive(Debug, Clone, Copy)]
37/// Represent the status of a transaction.
38pub enum TxStatus {
39    /// Transaction is initialized.
40    Initialized,
41    /// Transaction is not confirmed yet.
42    Pending,
43    /// Transaction is confirmed.
44    Confirmed,
45    /// Transaction is rejected.
46    Rejected,
47}
48
49impl<'a, T: JsonRpcClient> Transaction<'a, T> {
50    /// Creates a new Transaction.
51    ///
52    /// To create a new transaction you need to pass a hash, which identifies a transaction uniquely, and a
53    /// provider, which is used to confirm the transaction.
54    pub fn new(id: TxHash, client: &'a Provider<T>) -> Self {
55        Self {
56            id,
57            client,
58            status: Cell::new(TxStatus::Initialized),
59        }
60    }
61
62    /// Tries to confirm a transaction.
63    pub async fn confirm(&self) -> Result<GetTransactionResponse, Error> {
64        self.try_confirm(tokio::time::Duration::from_secs(10), 33).await
65    }
66
67    /// The `try_confirm` function attempts to confirm a transaction by making repeated requests to a
68    /// client, with a specified interval and maximum number of attempts, and returns the result.
69    ///
70    /// Arguments:
71    ///
72    /// * `interval`: The `interval` parameter is the duration of time to wait between each attempt to
73    /// confirm the transaction. It is of type `tokio::time::Duration`, which represents a duration of time
74    /// in the Tokio runtime.
75    /// * `max_attempt`: The `max_attempt` parameter is the maximum number of attempts to confirm the
76    /// transaction.
77    pub async fn try_confirm(&self, interval: tokio::time::Duration, max_attempt: u32) -> Result<GetTransactionResponse, Error> {
78        self.status.set(TxStatus::Pending);
79        for _ in 0..max_attempt {
80            let res = match self.client.get_transaction(&self.id).await {
81                Ok(res) => res,
82                // TODO: Consider errors except tx hash not found.
83                Err(_) => {
84                    tokio::time::sleep(interval).await;
85                    continue;
86                }
87            };
88            self.status.set(if res.receipt.success {
89                TxStatus::Confirmed
90            } else {
91                TxStatus::Rejected
92            });
93
94            return Ok(res);
95        }
96
97        Err(Error::UnableToConfirmTransaction(max_attempt))
98    }
99}
100
101/// Represents transaction version for zilliqa transactions.
102#[derive(Debug, Default, PartialEq, Clone)]
103pub struct Version {
104    /// Version of the message being sent
105    msg_version: u16,
106    /// Identifier of a blockchain network
107    chain_id: u16,
108}
109
110impl Version {
111    /// Create a new version instance.
112    pub fn new(chain_id: u16) -> Self {
113        Self {
114            chain_id,
115            msg_version: 1,
116        }
117    }
118
119    /// Takes the `chain_id` and `msg_version` values and packs them into a
120    /// single `u32` value.
121    ///
122    /// # Example
123    /// ```
124    /// use zilliqa_rs::transaction::Version;
125    /// let version = Version::new(16);
126    /// assert_eq!(version.pack(), 0x0010_0001)
127    /// ```
128    pub fn pack(&self) -> u32 {
129        (self.chain_id as u32) << 16 | (self.msg_version as u32)
130    }
131
132    /// Checks if the version is valid.
133    pub fn is_valid(&self) -> bool {
134        (self.chain_id > 0) && (self.msg_version > 0)
135    }
136}
137
138impl Display for Version {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        write!(f, "chain_id: {}, msg_version: {}", self.chain_id, self.msg_version)
141    }
142}
143
144impl Serialize for Version {
145    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
146    where
147        S: Serializer,
148    {
149        let packed = self.pack();
150        serializer.serialize_u32(packed)
151    }
152}