pink_web3/types/
transaction.rs

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