1use crate::prelude::*;
2use crate::types::{Address, Bytes, Index, Log, H2048, H256, U256, U64};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
7pub struct Transaction {
8 pub hash: H256,
10 pub nonce: U256,
12 #[serde(rename = "blockHash")]
14 pub block_hash: Option<H256>,
15 #[serde(rename = "blockNumber")]
17 pub block_number: Option<U64>,
18 #[serde(rename = "transactionIndex")]
20 pub transaction_index: Option<Index>,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub from: Option<Address>,
24 pub to: Option<Address>,
26 pub value: U256,
28 #[serde(rename = "gasPrice")]
30 pub gas_price: Option<U256>,
31 pub gas: U256,
33 pub input: Bytes,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub v: Option<U64>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub r: Option<U256>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub s: Option<U256>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub raw: Option<Bytes>,
47 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
49 pub transaction_type: Option<U64>,
50 #[serde(rename = "accessList", default, skip_serializing_if = "Option::is_none")]
52 pub access_list: Option<AccessList>,
53 #[serde(rename = "maxFeePerGas", skip_serializing_if = "Option::is_none")]
55 pub max_fee_per_gas: Option<U256>,
56 #[serde(rename = "maxPriorityFeePerGas", skip_serializing_if = "Option::is_none")]
58 pub max_priority_fee_per_gas: Option<U256>,
59}
60
61#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
63pub struct Receipt {
64 #[serde(rename = "transactionHash")]
66 pub transaction_hash: H256,
67 #[serde(rename = "transactionIndex")]
69 pub transaction_index: Index,
70 #[serde(rename = "blockHash")]
72 pub block_hash: Option<H256>,
73 #[serde(rename = "blockNumber")]
75 pub block_number: Option<U64>,
76 #[serde(default)]
80 pub from: Address,
81 #[serde(default)]
85 pub to: Option<Address>,
86 #[serde(rename = "cumulativeGasUsed")]
88 pub cumulative_gas_used: U256,
89 #[serde(rename = "gasUsed")]
93 pub gas_used: Option<U256>,
94 #[serde(rename = "contractAddress")]
96 pub contract_address: Option<Address>,
97 pub logs: Vec<Log>,
99 pub status: Option<U64>,
101 pub root: Option<H256>,
103 #[serde(rename = "logsBloom")]
105 pub logs_bloom: H2048,
106 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
108 pub transaction_type: Option<U64>,
109 #[serde(rename = "effectiveGasPrice")]
111 pub effective_gas_price: Option<U256>,
112}
113
114#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
116pub struct RawTransaction {
117 pub raw: Bytes,
119 pub tx: Transaction,
121}
122
123pub type AccessList = Vec<AccessListItem>;
125
126#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct AccessListItem {
130 pub address: Address,
132 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 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}