1use arrayvec::ArrayVec;
2use serde::{Deserialize, Deserializer, Serialize};
3
4mod bloom_filter_wrapper;
5mod data;
6mod fixed_size_data;
7mod hex;
8mod quantity;
9mod transaction_status;
10mod transaction_type;
11pub mod uint;
12mod util;
13mod withdrawal;
14
15pub use bloom_filter_wrapper::FilterWrapper;
16pub use data::Data;
17pub use fixed_size_data::FixedSizeData;
18pub use hex::Hex;
19pub use quantity::Quantity;
20pub use transaction_status::TransactionStatus;
21pub use transaction_type::TransactionType;
22pub use withdrawal::Withdrawal;
23
24#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[serde(rename_all = "camelCase")]
30pub struct BlockHeader {
31 pub number: BlockNumber,
32 pub hash: Hash,
33 pub parent_hash: Hash,
34 pub nonce: Option<Nonce>,
35 #[serde(default)]
36 pub sha3_uncles: Hash,
37 pub logs_bloom: BloomFilter,
38 pub transactions_root: Hash,
39 pub state_root: Hash,
40 pub receipts_root: Hash,
41 pub miner: Address,
42 pub difficulty: Option<Quantity>,
43 pub total_difficulty: Option<Quantity>,
44 pub extra_data: Data,
45 pub size: Quantity,
46 pub gas_limit: Quantity,
47 pub gas_used: Quantity,
48 pub timestamp: Quantity,
49 pub uncles: Option<Vec<Hash>>,
50 pub base_fee_per_gas: Option<Quantity>,
51 pub blob_gas_used: Option<Quantity>,
52 pub excess_blob_gas: Option<Quantity>,
53 pub parent_beacon_block_root: Option<Hash>,
54 pub withdrawals_root: Option<Hash>,
55 pub withdrawals: Option<Vec<Withdrawal>>,
56 pub l1_block_number: Option<BlockNumber>,
57 pub send_count: Option<Quantity>,
58 pub send_root: Option<Hash>,
59 pub mix_hash: Option<Hash>,
60}
61
62#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
67#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
68#[serde(rename_all = "camelCase")]
69pub struct Block<Tx> {
70 #[serde(flatten)]
71 pub header: BlockHeader,
72 pub transactions: Vec<Tx>,
73}
74
75#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
79#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
80#[serde(rename_all = "camelCase")]
81pub struct Transaction {
82 pub block_hash: Hash,
83 pub block_number: BlockNumber,
84 pub from: Option<Address>,
85 pub gas: Quantity,
86 pub gas_price: Option<Quantity>,
87 pub hash: Hash,
88 pub input: Data,
89 pub nonce: Quantity,
90 pub to: Option<Address>,
91 pub transaction_index: TransactionIndex,
92 pub value: Quantity,
93 #[serde(rename = "type")]
94 pub type_: Option<TransactionType>,
95 pub v: Option<Quantity>,
96 pub r: Option<Quantity>,
97 pub s: Option<Quantity>,
98 pub y_parity: Option<Quantity>,
99 pub max_priority_fee_per_gas: Option<Quantity>,
100 pub max_fee_per_gas: Option<Quantity>,
101 pub chain_id: Option<Quantity>,
102 pub access_list: Option<Vec<AccessList>>,
103 pub authorization_list: Option<Vec<Authorization>>,
104 pub max_fee_per_blob_gas: Option<Quantity>,
105 pub blob_versioned_hashes: Option<Vec<Hash>>,
106 pub deposit_receipt_version: Option<Quantity>,
108 pub mint: Option<Quantity>,
109 pub source_hash: Option<Hash>,
110}
111
112#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117#[serde(rename_all = "camelCase")]
118pub struct AccessList {
119 pub address: Option<Address>,
120 pub storage_keys: Option<Vec<Hash>>,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
128#[serde(rename_all = "camelCase")]
129pub struct Authorization {
130 pub chain_id: Quantity,
131 pub address: Address,
132 pub nonce: Quantity,
133 pub y_parity: Quantity,
134 pub r: Quantity,
135 pub s: Quantity,
136}
137
138#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
142#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
143#[serde(rename_all = "camelCase")]
144pub struct TransactionReceipt {
145 pub transaction_hash: Hash,
146 pub transaction_index: TransactionIndex,
147 pub block_hash: Hash,
148 pub block_number: BlockNumber,
149 pub from: Address,
150 pub to: Option<Address>,
151 pub cumulative_gas_used: Quantity,
152 #[serde(default, deserialize_with = "nullable_default")]
154 pub effective_gas_price: Quantity,
155 pub gas_used: Quantity,
156 pub contract_address: Option<Address>,
157 pub logs: Vec<Log>,
158 pub logs_bloom: BloomFilter,
159 #[serde(rename = "type")]
160 pub type_: Option<TransactionType>,
161 pub root: Option<Hash>,
162 pub status: Option<TransactionStatus>,
163 pub l1_fee: Option<Quantity>,
164 pub l1_gas_price: Option<Quantity>,
165 pub l1_gas_used: Option<Quantity>,
166 pub l1_fee_scalar: Option<String>,
168 pub gas_used_for_l1: Option<Quantity>,
169 pub blob_gas_price: Option<Quantity>,
170 pub deposit_nonce: Option<Quantity>,
172 pub deposit_receipt_version: Option<Quantity>,
173 pub blob_gas_used: Option<Quantity>,
174
175 pub l1_base_fee_scalar: Option<Quantity>,
177 pub l1_blob_base_fee: Option<Quantity>,
178 pub l1_blob_base_fee_scalar: Option<Quantity>,
179
180 pub l1_block_number: Option<Quantity>,
182}
183
184fn nullable_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
186where
187 D: Deserializer<'de>,
188 T: Default + Deserialize<'de>,
189{
190 Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
191}
192
193#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct Log {
199 pub removed: Option<bool>,
200 pub log_index: LogIndex,
201 pub transaction_index: TransactionIndex,
202 pub transaction_hash: Hash,
203 pub block_hash: Hash,
204 pub block_number: BlockNumber,
205 pub address: Address,
206 pub data: Data,
207 pub topics: ArrayVec<LogArgument, 4>,
208 pub block_timestamp: Option<Quantity>,
211}
212
213#[cfg(feature = "arbitrary")]
214impl<'input> arbitrary::Arbitrary<'input> for Log {
215 fn arbitrary(u: &mut arbitrary::Unstructured<'input>) -> arbitrary::Result<Self> {
216 let num_topics = u.arbitrary::<u8>()? % 4 + 1;
217 let mut topics = ArrayVec::<LogArgument, 4>::new();
218 for _ in 0..num_topics {
219 topics.push(u.arbitrary()?);
220 }
221
222 Ok(Self {
223 removed: u.arbitrary()?,
224 log_index: u.arbitrary()?,
225 transaction_index: u.arbitrary()?,
226 transaction_hash: u.arbitrary()?,
227 block_hash: u.arbitrary()?,
228 block_number: u.arbitrary()?,
229 address: u.arbitrary()?,
230 data: u.arbitrary()?,
231 topics,
232 block_timestamp: u.arbitrary()?,
233 })
234 }
235}
236
237#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
241#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
242#[serde(rename_all = "camelCase")]
243pub struct Trace {
244 pub action: TraceAction,
245 pub block_hash: Hash,
246 pub block_number: u64,
247 pub result: Option<TraceResult>,
248 pub subtraces: Option<u64>,
249 pub trace_address: Option<Vec<u64>>,
250 pub transaction_hash: Option<Hash>,
251 pub transaction_position: Option<u64>,
252 #[serde(rename = "type")]
253 pub type_: Option<String>,
254 pub error: Option<String>,
255}
256
257#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
261#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
262#[serde(rename_all = "camelCase")]
263pub struct TraceAction {
264 pub from: Option<Address>,
265 pub to: Option<Address>,
266 pub call_type: Option<String>,
267 pub gas: Option<Quantity>,
268 pub input: Option<Data>,
269 pub init: Option<Data>,
270 pub value: Option<Quantity>,
271 pub author: Option<Address>,
272 pub reward_type: Option<String>,
273 pub address: Option<Address>,
275 pub refund_address: Option<Address>,
276 pub balance: Option<Quantity>,
277}
278
279#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
283#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
284#[serde(rename_all = "camelCase")]
285pub struct TraceResult {
286 pub address: Option<Address>,
287 pub code: Option<Data>,
288 pub gas_used: Option<Quantity>,
289 pub output: Option<Data>,
290}
291
292#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
293#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
294#[serde(rename_all = "camelCase")]
295pub struct DebugBlockTrace {
296 pub result: DebugTxTrace,
297 pub tx_hash: Hash,
298}
299
300#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
301#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
302#[serde(rename_all = "camelCase")]
303pub struct DebugTxTrace {
304 #[serde(rename = "type")]
305 pub type_: Option<String>,
306 pub from: Option<Address>,
307 pub to: Option<Address>,
308 pub value: Option<Quantity>,
309 pub gas: Option<Quantity>,
310 pub gas_used: Option<Quantity>,
311 pub input: Option<Data>,
312 pub output: Option<Data>,
313 pub error: Option<String>,
314 pub revert_reason: Option<String>,
315 #[serde(default)]
316 pub calls: Vec<DebugTxTrace>,
317}
318
319pub type Hash = FixedSizeData<32>;
321
322pub type LogArgument = FixedSizeData<32>;
324
325pub type Address = FixedSizeData<20>;
327
328pub type Nonce = FixedSizeData<8>;
330
331pub type BloomFilter = Data;
332pub type BlockNumber = uint::UInt;
333pub type TransactionIndex = uint::UInt;
334pub type LogIndex = uint::UInt;
335
336#[cfg(test)]
337mod tests {
338 use serde_json::{json, Value};
339
340 use super::*;
341
342 #[test]
343 fn handle_zeta_null_effective_gas_price() {
344 let json = json!({
346 "transactionHash": "0xf19809f330bb78aa882976053ab40a7606797efcb6111f2e7112600e958a6e4c",
347 "transactionIndex": "0x22b8",
348 "blockHash": "0xaae719b56f61cb66cdc61ece1852cda22c936baff9a1dc6b0903be11073476b7",
349 "blockNumber": "0xa377b2",
350 "from": "0x735b14bb79463307aacbed86daf3322b1e6226ab",
351 "to": "0x91d18e54daf4f677cb28167158d6dd21f6ab3921",
352 "cumulativeGasUsed": "0x211ec2",
353 "effectiveGasPrice": null,
354 "contractAddress": null,
355 "gasUsed": "0x186a0",
356 "logs": [
357 {
358 "address": "0x91d18e54daf4f677cb28167158d6dd21f6ab3921",
359 "blockHash": "0xaae719b56f61cb66cdc61ece1852cda22c936baff9a1dc6b0903be11073476b7",
360 "blockNumber": "0xa377b2",
361 "data": "0x000000000000000000000000000000000000000000000000000000000000006900000000000000000000000000000000000000000000000000000000000001f4",
362 "logIndex": "0x0",
363 "removed": false,
364 "topics": [
365 "0x49f492222906ac486c3c1401fa545626df1f0c0e5a77a05597ea2ed66af9850d"
366 ],
367 "transactionHash": "0xf19809f330bb78aa882976053ab40a7606797efcb6111f2e7112600e958a6e4c",
368 "transactionIndex": "0x0"
369 }
370 ],
371 "logsBloom": "0x00000000000000000000000000000008000000000000000000000000000000000000000008000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000002000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
372 "status": "0x1",
373 "type": "0x58"
374 });
375
376 let _: TransactionReceipt =
377 serde_json::from_value(json.clone()).expect("should handle null effective gas price");
378
379 let mut obj = json.as_object().unwrap().to_owned();
381 let _ = obj.remove("effectiveGasPrice");
382 let json = Value::Object(obj);
383 let _: TransactionReceipt =
384 serde_json::from_value(json).expect("should handle undefined effective gas price");
385 }
386}