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}