ic_web3_rs/types/
transaction.rs

1use crate::types::{Address, Bytes, Index, Log, H2048, H256, U256, U64};
2use serde::{Deserialize, Serialize};
3
4/// Description of a Transaction, pending or in the chain.
5#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
6pub struct Transaction {
7    /// Hash
8    pub hash: H256,
9    /// Nonce
10    pub nonce: U256,
11    /// Block hash. None when pending.
12    #[serde(rename = "blockHash")]
13    pub block_hash: Option<H256>,
14    /// Block number. None when pending.
15    #[serde(rename = "blockNumber")]
16    pub block_number: Option<U64>,
17    /// Transaction Index. None when pending.
18    #[serde(rename = "transactionIndex")]
19    pub transaction_index: Option<Index>,
20    /// Sender
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub from: Option<Address>,
23    /// Recipient (None when contract creation)
24    pub to: Option<Address>,
25    /// Transfered value
26    pub value: U256,
27    /// Gas Price
28    #[serde(rename = "gasPrice")]
29    pub gas_price: Option<U256>,
30    /// Gas amount
31    pub gas: U256,
32    /// Input data
33    pub input: Bytes,
34    /// ECDSA recovery id
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    pub v: Option<U64>,
37    /// ECDSA signature r, 32 bytes
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub r: Option<U256>,
40    /// ECDSA signature s, 32 bytes
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub s: Option<U256>,
43    /// Raw transaction data
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub raw: Option<Bytes>,
46    /// Transaction type, Some(1) for AccessList transaction, None for Legacy
47    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
48    pub transaction_type: Option<U64>,
49    /// Access list
50    #[serde(rename = "accessList", default, skip_serializing_if = "Option::is_none")]
51    pub access_list: Option<AccessList>,
52    /// Max fee per gas
53    #[serde(rename = "maxFeePerGas", skip_serializing_if = "Option::is_none")]
54    pub max_fee_per_gas: Option<U256>,
55    /// miner bribe
56    #[serde(rename = "maxPriorityFeePerGas", skip_serializing_if = "Option::is_none")]
57    pub max_priority_fee_per_gas: Option<U256>,
58}
59
60/// "Receipt" of an executed transaction: details of its execution.
61#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
62pub struct Receipt {
63    /// Transaction hash.
64    #[serde(rename = "transactionHash")]
65    pub transaction_hash: H256,
66    /// Index within the block.
67    #[serde(rename = "transactionIndex")]
68    pub transaction_index: Index,
69    /// Hash of the block this transaction was included within.
70    #[serde(rename = "blockHash")]
71    pub block_hash: Option<H256>,
72    /// Number of the block this transaction was included within.
73    #[serde(rename = "blockNumber")]
74    pub block_number: Option<U64>,
75    /// Sender
76    /// Note: default address if the client did not return this value
77    /// (maintains backwards compatibility for <= 0.7.0 when this field was missing)
78    #[serde(default)]
79    pub from: Address,
80    /// Recipient (None when contract creation)
81    /// Note: Also `None` if the client did not return this value
82    /// (maintains backwards compatibility for <= 0.7.0 when this field was missing)
83    #[serde(default)]
84    pub to: Option<Address>,
85    /// Cumulative gas used within the block after this was executed.
86    #[serde(rename = "cumulativeGasUsed")]
87    pub cumulative_gas_used: U256,
88    /// Gas used by this transaction alone.
89    ///
90    /// Gas used is `None` if the the client is running in light client mode.
91    #[serde(rename = "gasUsed")]
92    pub gas_used: Option<U256>,
93    /// Contract address created, or `None` if not a deployment.
94    #[serde(rename = "contractAddress")]
95    pub contract_address: Option<Address>,
96    /// Logs generated within this transaction.
97    pub logs: Vec<Log>,
98    /// Status: either 1 (success) or 0 (failure).
99    pub status: Option<U64>,
100    /// State root.
101    pub root: Option<H256>,
102    /// Logs bloom
103    #[serde(rename = "logsBloom")]
104    pub logs_bloom: H2048,
105    /// Transaction type, Some(1) for AccessList transaction, None for Legacy
106    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
107    pub transaction_type: Option<U64>,
108    /// Effective gas price
109    #[serde(rename = "effectiveGasPrice")]
110    pub effective_gas_price: Option<U256>,
111}
112
113/// Raw bytes of a signed, but not yet sent transaction
114#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
115pub struct RawTransaction {
116    /// Signed transaction as raw bytes
117    pub raw: Bytes,
118    /// Transaction details
119    pub tx: Transaction,
120}
121
122/// Access list
123pub type AccessList = Vec<AccessListItem>;
124
125/// Access list item
126#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct AccessListItem {
129    /// Accessed address
130    pub address: Address,
131    /// Accessed storage keys
132    pub storage_keys: Vec<H256>,
133}
134
135#[cfg(test)]
136mod tests {
137    use super::{RawTransaction, Receipt};
138
139    #[test]
140    fn test_deserialize_receipt() {
141        let receipt_str = "{\"blockHash\":\"0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5\",\"blockNumber\":\"0x38\",\"contractAddress\":\"0x03d8c4566478a6e1bf75650248accce16a98509f\",\"from\":\"0x407d73d8a49eeb85d32cf465507dd71d507100c1\",\"to\":\"0x853f43d8a49eeb85d32cf465507dd71d507100c1\",\"cumulativeGasUsed\":\"0x927c0\",\"gasUsed\":\"0x927c0\",\"logs\":[],\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"root\":null,\"transactionHash\":\"0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26\",\"transactionIndex\":\"0x0\",\"effectiveGasPrice\": \"0x100\"}";
142
143        let _receipt: Receipt = serde_json::from_str(receipt_str).unwrap();
144    }
145
146    #[test]
147    fn should_deserialize_receipt_without_from_to() {
148        let receipt_str = r#"{
149        "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
150        "blockNumber": "0x38",
151        "contractAddress": "0x03d8c4566478a6e1bf75650248accce16a98509f",
152        "cumulativeGasUsed": "0x927c0",
153        "gasUsed": "0x927c0",
154        "logs": [],
155        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
156        "root": null,
157        "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
158        "transactionIndex": "0x0",
159        "status": "0x1",
160        "effectiveGasPrice": "0x100"
161        }"#;
162
163        let _receipt: Receipt = serde_json::from_str(receipt_str).unwrap();
164    }
165
166    #[test]
167    fn should_deserialize_receipt_with_status() {
168        let receipt_str = r#"{
169        "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
170        "blockNumber": "0x38",
171        "contractAddress": "0x03d8c4566478a6e1bf75650248accce16a98509f",
172        "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
173        "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
174        "cumulativeGasUsed": "0x927c0",
175        "gasUsed": "0x927c0",
176        "logs": [],
177        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
178        "root": null,
179        "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
180        "transactionIndex": "0x0",
181        "status": "0x1",
182        "effectiveGasPrice": "0x100"
183    }"#;
184
185        let _receipt: Receipt = serde_json::from_str(receipt_str).unwrap();
186    }
187
188    #[test]
189    fn should_deserialize_receipt_without_to() {
190        let receipt_str = r#"{
191        "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
192        "blockNumber": "0x38",
193        "contractAddress": "0x03d8c4566478a6e1bf75650248accce16a98509f",
194        "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
195        "to": null,
196        "cumulativeGasUsed": "0x927c0",
197        "gasUsed": "0x927c0",
198        "logs": [],
199        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
200        "root": null,
201        "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
202        "transactionIndex": "0x0",
203        "status": "0x1",
204        "effectiveGasPrice": "0x100"
205        }"#;
206
207        let _receipt: Receipt = serde_json::from_str(receipt_str).unwrap();
208    }
209
210    #[test]
211    fn should_deserialize_receipt_without_gas() {
212        let receipt_str = r#"{
213        "blockHash": "0x83eaba432089a0bfe99e9fc9022d1cfcb78f95f407821be81737c84ae0b439c5",
214        "blockNumber": "0x38",
215        "contractAddress": "0x03d8c4566478a6e1bf75650248accce16a98509f",
216        "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
217        "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
218        "cumulativeGasUsed": "0x927c0",
219        "gasUsed": null,
220        "logs": [],
221        "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
222        "root": null,
223        "transactionHash": "0x422fb0d5953c0c48cbb42fb58e1c30f5e150441c68374d70ca7d4f191fd56f26",
224        "transactionIndex": "0x0",
225        "status": "0x1",
226        "effectiveGasPrice": "0x100"
227    }"#;
228
229        let _receipt: Receipt = serde_json::from_str(receipt_str).unwrap();
230    }
231
232    #[test]
233    fn test_deserialize_signed_tx_parity() {
234        // taken from RPC docs.
235        let tx_str = r#"{
236        "raw": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
237        "tx": {
238          "hash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
239          "nonce": "0x0",
240          "blockHash": "0xbeab0aa2411b7ab17f30a99d3cb9c6ef2fc5426d6ad6fd9e2a26a6aed1d1055b",
241          "blockNumber": "0x15df",
242          "transactionIndex": "0x1",
243          "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
244          "to": "0x853f43d8a49eeb85d32cf465507dd71d507100c1",
245          "value": "0x7f110",
246          "gas": "0x7f110",
247          "gasPrice": "0x09184e72a000",
248          "input": "0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360",
249          "s": "0x777"
250        }
251    }"#;
252
253        let _tx: RawTransaction = serde_json::from_str(tx_str).unwrap();
254    }
255
256    #[test]
257    fn test_deserialize_signed_tx_geth() {
258        let tx_str = r#"{
259        "raw": "0xf85d01018094f3b3138e5eb1c75b43994d1bb760e2f9f735789680801ca06484d00575e961a7db35ebe5badaaca5cb7ee65d1f2f22f22da87c238b99d30da07a85d65797e4b555c1d3f64beebb2cb6f16a6fbd40c43cc48451eaf85305f66e",
260        "tx": {
261          "gas": "0x0",
262          "gasPrice": "0x1",
263          "hash": "0x0a32fb4e18bc6f7266a164579237b1b5c74271d453c04eab70444ca367d38418",
264          "input": "0x",
265          "nonce": "0x1",
266          "to": "0xf3b3138e5eb1c75b43994d1bb760e2f9f7357896",
267          "r": "0x6484d00575e961a7db35ebe5badaaca5cb7ee65d1f2f22f22da87c238b99d30d",
268          "s": "0x7a85d65797e4b555c1d3f64beebb2cb6f16a6fbd40c43cc48451eaf85305f66e",
269          "v": "0x1c",
270          "value": "0x0"
271        }
272    }"#;
273
274        let _tx: RawTransaction = serde_json::from_str(tx_str).unwrap();
275    }
276}