Skip to main content

hypersync_client/
simple_types.rs

1//! Base object types for the Hypersync client.
2use std::{collections::HashMap, sync::Arc};
3
4use arrayvec::ArrayVec;
5use hypersync_format::{
6    AccessList, Address, Authorization, BlockNumber, BloomFilter, Data, Hash, LogArgument,
7    LogIndex, Nonce, Quantity, TransactionIndex, TransactionStatus, TransactionType, Withdrawal,
8};
9use hypersync_net_types::{
10    block::BlockField, log::LogField, transaction::TransactionField, FieldSelection,
11};
12use nohash_hasher::IntMap;
13use serde::{Deserialize, Serialize};
14use xxhash_rust::xxh3::Xxh3Builder;
15
16use crate::types::ResponseData;
17
18/// An Ethereum event object.
19#[derive(Debug, Default, Clone, PartialEq)]
20pub struct Event {
21    /// An Ethereum event transaction object.
22    pub transaction: Option<Arc<Transaction>>,
23    /// An Ethereum event block object.
24    pub block: Option<Arc<Block>>,
25    /// An Ethereum event log object.
26    pub log: Log,
27}
28
29// Field lists for implementing event based API, these fields are used for joining
30// so they should always be added to the field selection.
31const BLOCK_JOIN_FIELD: BlockField = BlockField::Number;
32const TX_JOIN_FIELD: TransactionField = TransactionField::Hash;
33const LOG_JOIN_FIELD_WITH_TX: LogField = LogField::TransactionHash;
34const LOG_JOIN_FIELD_WITH_BLOCK: LogField = LogField::BlockNumber;
35
36enum InternalJoinStrategy {
37    NotSelected,
38    OnlyLogJoinField,
39    FullJoin,
40}
41
42/// Internal event join strategy for determining how to join blocks and transactions with logs
43pub(crate) struct InternalEventJoinStrategy {
44    block: InternalJoinStrategy,
45    transaction: InternalJoinStrategy,
46}
47
48impl From<&FieldSelection> for InternalEventJoinStrategy {
49    fn from(field_selection: &FieldSelection) -> Self {
50        let block_fields_num = field_selection.block.len();
51        let transaction_fields_num = field_selection.transaction.len();
52
53        Self {
54            block: if block_fields_num == 0 {
55                InternalJoinStrategy::NotSelected
56            } else if block_fields_num == 1 && field_selection.block.contains(&BLOCK_JOIN_FIELD) {
57                InternalJoinStrategy::OnlyLogJoinField
58            } else {
59                InternalJoinStrategy::FullJoin
60            },
61            transaction: if transaction_fields_num == 0 {
62                InternalJoinStrategy::NotSelected
63            } else if transaction_fields_num == 1
64                && field_selection.transaction.contains(&TX_JOIN_FIELD)
65            {
66                InternalJoinStrategy::OnlyLogJoinField
67            } else {
68                InternalJoinStrategy::FullJoin
69            },
70        }
71    }
72}
73
74impl InternalEventJoinStrategy {
75    /// Add join fields to field selection based on the event join strategy
76    pub(crate) fn add_join_fields_to_selection(&self, field_selection: &mut FieldSelection) {
77        match self.block {
78            InternalJoinStrategy::NotSelected => (),
79            InternalJoinStrategy::OnlyLogJoinField => {
80                field_selection.log.insert(LOG_JOIN_FIELD_WITH_BLOCK);
81                field_selection.block.remove(&BLOCK_JOIN_FIELD);
82            }
83            InternalJoinStrategy::FullJoin => {
84                field_selection.log.insert(LOG_JOIN_FIELD_WITH_BLOCK);
85                field_selection.block.insert(BLOCK_JOIN_FIELD);
86            }
87        }
88
89        match self.transaction {
90            InternalJoinStrategy::NotSelected => (),
91            InternalJoinStrategy::OnlyLogJoinField => {
92                field_selection.log.insert(LOG_JOIN_FIELD_WITH_TX);
93                field_selection.transaction.remove(&TX_JOIN_FIELD);
94            }
95            InternalJoinStrategy::FullJoin => {
96                field_selection.log.insert(LOG_JOIN_FIELD_WITH_TX);
97                field_selection.transaction.insert(TX_JOIN_FIELD);
98            }
99        }
100    }
101
102    /// Join response data into events based on the event join strategy
103    pub(crate) fn join_from_response_data(&self, data: ResponseData) -> Vec<Event> {
104        let blocks = data
105            .blocks
106            .into_iter()
107            .flat_map(|blocks| {
108                blocks
109                    .into_iter()
110                    .map(|block| (block.number.unwrap(), Arc::new(block)))
111            })
112            .collect::<IntMap<u64, _>>();
113
114        let transactions = data
115            .transactions
116            .into_iter()
117            .flat_map(|txs| {
118                txs.into_iter()
119                    .map(|tx| (tx.hash.clone().unwrap(), Arc::new(tx)))
120            })
121            .collect::<HashMap<_, _, Xxh3Builder>>();
122
123        data.logs
124            .into_iter()
125            .flat_map(|logs| {
126                logs.into_iter().map(|log| {
127                    let block = match self.block {
128                        InternalJoinStrategy::NotSelected => None,
129                        InternalJoinStrategy::OnlyLogJoinField => Some(Arc::new(Block {
130                            number: Some(log.block_number.unwrap().into()),
131                            ..Block::default()
132                        })),
133                        InternalJoinStrategy::FullJoin => {
134                            blocks.get(&log.block_number.unwrap().into()).cloned()
135                        }
136                    };
137                    let transaction = match self.transaction {
138                        InternalJoinStrategy::NotSelected => None,
139                        InternalJoinStrategy::OnlyLogJoinField => Some(Arc::new(Transaction {
140                            hash: log.transaction_hash.clone(),
141                            ..Transaction::default()
142                        })),
143                        InternalJoinStrategy::FullJoin => transactions
144                            .get(log.transaction_hash.as_ref().unwrap())
145                            .cloned(),
146                    };
147
148                    Event {
149                        transaction,
150                        block,
151                        log,
152                    }
153                })
154            })
155            .collect()
156    }
157}
158
159/// Block object
160#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
161pub struct Block {
162    /// A scalar value equal to the number of ancestor blocks. The genesis block has a number of
163    /// zero; formally Hi.
164    pub number: Option<u64>,
165    /// The Keccak 256-bit hash of the block
166    pub hash: Option<Hash>,
167    /// The Keccak 256-bit hash of the parent
168    /// block’s header, in its entirety; formally Hp.
169    pub parent_hash: Option<Hash>,
170    /// A 64-bit value which, combined with the mixhash, proves that a sufficient amount of
171    /// computation has been carried out on this block; formally Hn.
172    pub nonce: Option<Nonce>,
173    /// The Keccak 256-bit hash of the ommers/uncles list portion of this block; formally Ho.
174    pub sha3_uncles: Option<Hash>,
175    /// The Bloom filter composed from indexable information (logger address and log topics)
176    /// contained in each log entry from the receipt of each transaction in the transactions list;
177    /// formally Hb.
178    pub logs_bloom: Option<BloomFilter>,
179    /// The Keccak 256-bit hash of the root node of the trie structure populated with each
180    /// transaction in the transactions list portion of the block; formally Ht.
181    pub transactions_root: Option<Hash>,
182    /// The Keccak 256-bit hash of the root node of the state trie, after all transactions are
183    /// executed and finalisations applied; formally Hr.
184    pub state_root: Option<Hash>,
185    /// The Keccak 256-bit hash of the root node of the trie structure populated with each
186    /// transaction in the transactions list portion of the block; formally Ht.
187    pub receipts_root: Option<Hash>,
188    /// The 160-bit address to which all fees collected from the successful mining of this block
189    /// be transferred; formally Hc.
190    pub miner: Option<Address>,
191    /// A scalar value corresponding to the difficulty level of this block. This can be calculated
192    /// from the previous block’s difficulty level and the timestamp; formally Hd.
193    pub difficulty: Option<Quantity>,
194    /// The cumulative sum of the difficulty of all blocks that have been mined in the Ethereum
195    /// network since the inception of the network.
196    /// It measures the overall security and integrity of the Ethereum network.
197    pub total_difficulty: Option<Quantity>,
198    /// An arbitrary byte array containing data relevant to this block. This must be 32 bytes or
199    /// fewer; formally Hx.
200    pub extra_data: Option<Data>,
201    /// The size of this block in bytes as an integer value, encoded as hexadecimal.
202    pub size: Option<Quantity>,
203    /// A scalar value equal to the current limit of gas expenditure per block; formally Hl.
204    pub gas_limit: Option<Quantity>,
205    /// A scalar value equal to the total gas used in transactions in this block; formally Hg.
206    pub gas_used: Option<Quantity>,
207    /// A scalar value equal to the reasonable output of Unix’s time() at this block’s inception;
208    /// formally Hs.
209    pub timestamp: Option<Quantity>,
210    /// Ommers/uncles header.
211    pub uncles: Option<Vec<Hash>>,
212    /// A scalar representing EIP1559 base fee which can move up or down each block according
213    /// to a formula which is a function of gas used in parent block and gas target
214    /// (block gas limit divided by elasticity multiplier) of parent block.
215    /// The algorithm results in the base fee per gas increasing when blocks are
216    /// above the gas target, and decreasing when blocks are below the gas target. The base fee per
217    /// gas is burned.
218    pub base_fee_per_gas: Option<Quantity>,
219    /// The total amount of blob gas consumed by the transactions within the block, added in
220    /// EIP-4844.
221    pub blob_gas_used: Option<Quantity>,
222    /// A running total of blob gas consumed in excess of the target, prior to the block. Blocks
223    /// with above-target blob gas consumption increase this value, blocks with below-target blob
224    /// gas consumption decrease it (bounded at 0). This was added in EIP-4844.
225    pub excess_blob_gas: Option<Quantity>,
226    /// The hash of the parent beacon block's root is included in execution blocks, as proposed by
227    /// EIP-4788.
228    ///
229    /// This enables trust-minimized access to consensus state, supporting staking pools, bridges,
230    /// and more.
231    ///
232    /// The beacon roots contract handles root storage, enhancing Ethereum's functionalities.
233    pub parent_beacon_block_root: Option<Hash>,
234    /// The Keccak 256-bit hash of the withdrawals list portion of this block.
235    ///
236    /// See [EIP-4895](https://eips.ethereum.org/EIPS/eip-4895).
237    pub withdrawals_root: Option<Hash>,
238    /// Withdrawal represents a validator withdrawal from the consensus layer.
239    pub withdrawals: Option<Vec<Withdrawal>>,
240    /// The L1 block number that would be used for block.number calls.
241    pub l1_block_number: Option<BlockNumber>,
242    /// The number of L2 to L1 messages since Nitro genesis.
243    pub send_count: Option<Quantity>,
244    /// The Merkle root of the outbox tree state.
245    pub send_root: Option<Hash>,
246    /// A 256-bit hash which, combined with the
247    /// nonce, proves that a sufficient amount of computation has been carried out on this block;
248    /// formally Hm.
249    pub mix_hash: Option<Hash>,
250}
251
252/// Transaction object
253#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
254pub struct Transaction {
255    /// The Keccak 256-bit hash of the block
256    pub block_hash: Option<Hash>,
257    /// A scalar value equal to the number of ancestor blocks. The genesis block has a number of
258    /// zero; formally Hi.
259    pub block_number: Option<BlockNumber>,
260    /// The 160-bit address of the message call’s sender
261    pub from: Option<Address>,
262    /// A scalar value equal to the maximum
263    /// amount of gas that should be used in executing
264    /// this transaction. This is paid up-front, before any
265    /// computation is done and may not be increased
266    /// later; formally Tg.
267    pub gas: Option<Quantity>,
268    /// A scalar value equal to the number of
269    /// Wei to be paid per unit of gas for all computation
270    /// costs incurred as a result of the execution of this transaction; formally Tp.
271    pub gas_price: Option<Quantity>,
272    /// A transaction hash is a keccak hash of an RLP encoded signed transaction.
273    pub hash: Option<Hash>,
274    /// Input has two uses depending if transaction is Create or Call (if `to` field is None or
275    /// Some). pub init: An unlimited size byte array specifying the
276    /// EVM-code for the account initialisation procedure CREATE,
277    /// data: An unlimited size byte array specifying the
278    /// input data of the message call, formally Td.
279    pub input: Option<Data>,
280    /// A scalar value equal to the number of transactions sent by the sender; formally Tn.
281    pub nonce: Option<Quantity>,
282    /// The 160-bit address of the message call’s recipient or, for a contract creation
283    /// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
284    pub to: Option<Address>,
285    /// Index of the transaction in the block
286    pub transaction_index: Option<TransactionIndex>,
287    /// A scalar value equal to the number of Wei to
288    /// be transferred to the message call’s recipient or,
289    /// in the case of contract creation, as an endowment
290    /// to the newly created account; formally Tv.
291    pub value: Option<Quantity>,
292    /// Replay protection value based on chain_id. See EIP-155 for more info.
293    pub v: Option<Quantity>,
294    /// The R field of the signature; the point on the curve.
295    pub r: Option<Quantity>,
296    /// The S field of the signature; the point on the curve.
297    pub s: Option<Quantity>,
298    /// yParity: Signature Y parity; formally Ty
299    pub y_parity: Option<Quantity>,
300    /// Max Priority fee that transaction is paying
301    ///
302    /// As ethereum circulation is around 120mil eth as of 2022 that is around
303    /// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
304    /// 340282366920938463463374607431768211455
305    ///
306    /// This is also known as `GasTipCap`
307    pub max_priority_fee_per_gas: Option<Quantity>,
308    /// A scalar value equal to the maximum
309    /// amount of gas that should be used in executing
310    /// this transaction. This is paid up-front, before any
311    /// computation is done and may not be increased
312    /// later; formally Tg.
313    ///
314    /// As ethereum circulation is around 120mil eth as of 2022 that is around
315    /// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
316    /// 340282366920938463463374607431768211455
317    ///
318    /// This is also known as `GasFeeCap`
319    pub max_fee_per_gas: Option<Quantity>,
320    /// Added as EIP-pub 155: Simple replay attack protection
321    pub chain_id: Option<Quantity>,
322    /// The accessList specifies a list of addresses and storage keys;
323    /// these addresses and storage keys are added into the `accessed_addresses`
324    /// and `accessed_storage_keys` global sets (introduced in EIP-2929).
325    /// A gas cost is charged, though at a discount relative to the cost of
326    /// accessing outside the list.
327    pub access_list: Option<Vec<AccessList>>,
328    /// The authorization_list specifies a list of authorizations for the transaction
329    /// (introduced in EIP-7702)
330    pub authorization_list: Option<Vec<Authorization>>,
331    /// Max fee per data gas
332    ///
333    /// aka BlobFeeCap or blobGasFeeCap
334    pub max_fee_per_blob_gas: Option<Quantity>,
335    /// It contains a vector of fixed size hash(32 bytes)
336    pub blob_versioned_hashes: Option<Vec<Hash>>,
337    /// The total amount of gas used in the block until this transaction was executed.
338    pub cumulative_gas_used: Option<Quantity>,
339    /// The sum of the base fee and tip paid per unit of gas.
340    pub effective_gas_price: Option<Quantity>,
341    /// Gas used by transaction
342    pub gas_used: Option<Quantity>,
343    /// Address of created contract if transaction was a contract creation
344    pub contract_address: Option<Address>,
345    /// Bloom filter for logs produced by this transaction
346    pub logs_bloom: Option<BloomFilter>,
347    /// Transaction type. For ethereum: Legacy, Eip2930, Eip1559, Eip4844
348    #[serde(rename = "type")]
349    pub type_: Option<TransactionType>,
350    /// The Keccak 256-bit hash of the root node of the trie structure populated with each
351    /// transaction in the transactions list portion of the block; formally Ht.
352    pub root: Option<Hash>,
353    /// If transaction is executed successfully.
354    ///
355    /// This is the `statusCode`
356    pub status: Option<TransactionStatus>,
357    /// The fee associated with a transaction on the Layer 1,
358    /// it is calculated as l1GasPrice multiplied by l1GasUsed
359    pub l1_fee: Option<Quantity>,
360    /// The gas price for transactions on the Layer 1
361    pub l1_gas_price: Option<Quantity>,
362    /// The amount of gas consumed by a transaction on the Layer 1
363    pub l1_gas_used: Option<Quantity>,
364    /// A multiplier applied to the actual gas usage on Layer 1 to calculate the dynamic costs.
365    /// If set to 1, it has no impact on the L1 gas usage
366    pub l1_fee_scalar: Option<f64>,
367    /// Amount of gas spent on L1 calldata in units of L2 gas.
368    pub gas_used_for_l1: Option<Quantity>,
369    /// Gas price for blob transactions
370    pub blob_gas_price: Option<Quantity>,
371    /// Amount of blob gas used by this transaction
372    pub blob_gas_used: Option<Quantity>,
373    /// Deposit transaction nonce for Optimism
374    pub deposit_nonce: Option<Quantity>,
375    /// Deposit receipt version for Optimism
376    pub deposit_receipt_version: Option<Quantity>,
377    /// Base fee scalar for L1 cost calculation
378    pub l1_base_fee_scalar: Option<Quantity>,
379    /// L1 blob base fee for cost calculation
380    pub l1_blob_base_fee: Option<Quantity>,
381    /// L1 blob base fee scalar for cost calculation
382    pub l1_blob_base_fee_scalar: Option<Quantity>,
383    /// L1 block number associated with transaction
384    pub l1_block_number: Option<Quantity>,
385    /// Amount of ETH minted in this transaction
386    pub mint: Option<Quantity>,
387    /// 4-byte function signature hash
388    pub sighash: Option<Data>,
389    /// Source hash for optimism transactions
390    pub source_hash: Option<Hash>,
391    /// Arbitrum Nitro: L1 request ID (ArbSubmitRetryableTx, ArbDepositTx, ArbContractTx)
392    pub request_id: Option<Hash>,
393    /// Arbitrum Nitro: retryable ticket ID (ArbRetryTx)
394    pub ticket_id: Option<Hash>,
395    /// Arbitrum Nitro: fee refund address (ArbRetryTx, ArbSubmitRetryableTx)
396    pub refund_to: Option<Address>,
397    /// Arbitrum Nitro: maximum gas refund (ArbRetryTx)
398    pub max_refund: Option<Quantity>,
399    /// Arbitrum Nitro: submission fee refund (ArbRetryTx)
400    pub submission_fee_refund: Option<Quantity>,
401    /// Arbitrum Nitro: L1 base fee at time of submission (ArbSubmitRetryableTx)
402    pub l1_base_fee: Option<Quantity>,
403    /// Arbitrum Nitro: total ETH deposited from L1 (ArbSubmitRetryableTx)
404    pub deposit_value: Option<Quantity>,
405    /// Arbitrum Nitro: destination address for the retry (ArbSubmitRetryableTx)
406    pub retry_to: Option<Address>,
407    /// Arbitrum Nitro: ETH value for the retry (ArbSubmitRetryableTx)
408    pub retry_value: Option<Quantity>,
409    /// Arbitrum Nitro: calldata for the retry (ArbSubmitRetryableTx)
410    pub retry_data: Option<Data>,
411    /// Arbitrum Nitro: beneficiary address (ArbSubmitRetryableTx)
412    pub beneficiary: Option<Address>,
413    /// Arbitrum Nitro: maximum submission fee (ArbSubmitRetryableTx)
414    pub max_submission_fee: Option<Quantity>,
415}
416
417/// Log object
418#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
419pub struct Log {
420    /// The boolean value indicating if the event was removed from the blockchain due
421    /// to a chain reorganization. True if the log was removed. False if it is a valid log.
422    pub removed: Option<bool>,
423    /// The integer identifying the index of the event within the block's list of events.
424    pub log_index: Option<LogIndex>,
425    /// The integer index of the transaction within the block's list of transactions.
426    pub transaction_index: Option<TransactionIndex>,
427    /// The hash of the transaction that triggered the event.
428    pub transaction_hash: Option<Hash>,
429    /// The hash of the block in which the event was included.
430    pub block_hash: Option<Hash>,
431    /// The block number in which the event was included.
432    pub block_number: Option<BlockNumber>,
433    /// The contract address from which the event originated.
434    pub address: Option<Address>,
435    /// The non-indexed data that was emitted along with the event.
436    pub data: Option<Data>,
437    /// An array of 32-byte data fields containing indexed event parameters.
438    pub topics: ArrayVec<Option<LogArgument>, 4>,
439}
440
441/// Trace object
442#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
443pub struct Trace {
444    /// The address of the sender who initiated the transaction.
445    pub from: Option<Address>,
446    /// The address of the recipient of the transaction if it was a transaction to an address.
447    /// For contract creation transactions, this field is None.
448    pub to: Option<Address>,
449    /// The type of trace, `call` or `delegatecall`, two ways to invoke a function in a smart contract.
450    ///
451    /// `call` creates a new environment for the function to work in, so changes made in that
452    /// function won't affect the environment where the function was called.
453    ///
454    /// `delegatecall` doesn't create a new environment. Instead, it runs the function within the
455    /// environment of the caller, so changes made in that function will affect the caller's environment.
456    pub call_type: Option<String>,
457    /// The units of gas included in the transaction by the sender.
458    pub gas: Option<Quantity>,
459    /// The optional input data sent with the transaction, usually used to interact with smart contracts.
460    pub input: Option<Data>,
461    /// The init code.
462    pub init: Option<Data>,
463    /// The value of the native token transferred along with the transaction, in Wei.
464    pub value: Option<Quantity>,
465    /// The address of the receiver for reward transaction.
466    pub author: Option<Address>,
467    /// Kind of reward. `Block` reward or `Uncle` reward.
468    pub reward_type: Option<String>,
469    /// The hash of the block in which the transaction was included.
470    pub block_hash: Option<Hash>,
471    /// The number of the block in which the transaction was included.
472    pub block_number: Option<u64>,
473    /// Destroyed address.
474    pub address: Option<Address>,
475    /// Contract code.
476    pub code: Option<Data>,
477    /// The total used gas by the call, encoded as hexadecimal.
478    pub gas_used: Option<Quantity>,
479    /// The return value of the call, encoded as a hexadecimal string.
480    pub output: Option<Data>,
481    /// The number of sub-traces created during execution. When a transaction is executed on the EVM,
482    /// it may trigger additional sub-executions, such as when a smart contract calls another smart
483    /// contract or when an external account is accessed.
484    pub subtraces: Option<u64>,
485    /// An array that indicates the position of the transaction in the trace.
486    pub trace_address: Option<Vec<u64>>,
487    /// The hash of the transaction.
488    pub transaction_hash: Option<Hash>,
489    /// The index of the transaction in the block.
490    pub transaction_position: Option<u64>,
491    /// The type of action taken by the transaction, `call`, `create`, `reward` and `suicide`.
492    ///
493    /// `call` is the most common type of trace and occurs when a smart contract invokes another contract's function.
494    ///
495    /// `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain.
496    #[serde(rename = "type")]
497    pub type_: Option<String>,
498    /// A string that indicates whether the transaction was successful or not.
499    ///
500    /// None if successful, Reverted if not.
501    pub error: Option<String>,
502    /// 4-byte function signature hash for the trace
503    pub sighash: Option<Data>,
504    /// The action address for traces that create contracts
505    pub action_address: Option<Address>,
506    /// The balance associated with the trace operation
507    pub balance: Option<Quantity>,
508    /// The refund address for refund operations
509    pub refund_address: Option<Address>,
510}
511
512#[cfg(test)]
513mod tests {
514    use hypersync_net_types::TraceField;
515
516    use super::*;
517
518    fn has_tx_field(tx: &Transaction, field: TransactionField) -> bool {
519        match field {
520            TransactionField::BlockHash => tx.block_hash.is_none(),
521            TransactionField::BlockNumber => tx.block_number.is_none(),
522            TransactionField::From => tx.from.is_none(),
523            TransactionField::Gas => tx.gas.is_none(),
524            TransactionField::Hash => tx.hash.is_none(),
525            TransactionField::Input => tx.input.is_none(),
526            TransactionField::Nonce => tx.nonce.is_none(),
527            TransactionField::TransactionIndex => tx.transaction_index.is_none(),
528            TransactionField::Value => tx.value.is_none(),
529            TransactionField::CumulativeGasUsed => tx.cumulative_gas_used.is_none(),
530            TransactionField::EffectiveGasPrice => tx.effective_gas_price.is_none(),
531            TransactionField::GasUsed => tx.gas_used.is_none(),
532            TransactionField::LogsBloom => tx.logs_bloom.is_none(),
533            TransactionField::GasPrice => tx.gas_price.is_none(),
534            TransactionField::To => tx.to.is_none(),
535            TransactionField::V => tx.v.is_none(),
536            TransactionField::R => tx.r.is_none(),
537            TransactionField::S => tx.s.is_none(),
538            TransactionField::MaxPriorityFeePerGas => tx.max_priority_fee_per_gas.is_none(),
539            TransactionField::MaxFeePerGas => tx.max_fee_per_gas.is_none(),
540            TransactionField::ChainId => tx.chain_id.is_none(),
541            TransactionField::ContractAddress => tx.contract_address.is_none(),
542            TransactionField::Type => tx.type_.is_none(),
543            TransactionField::Root => tx.root.is_none(),
544            TransactionField::Status => tx.status.is_none(),
545            TransactionField::YParity => tx.y_parity.is_none(),
546            TransactionField::AccessList => tx.access_list.is_none(),
547            TransactionField::AuthorizationList => tx.authorization_list.is_none(),
548            TransactionField::L1Fee => tx.l1_fee.is_none(),
549            TransactionField::L1GasPrice => tx.l1_gas_price.is_none(),
550            TransactionField::L1GasUsed => tx.l1_gas_used.is_none(),
551            TransactionField::L1FeeScalar => tx.l1_fee_scalar.is_none(),
552            TransactionField::GasUsedForL1 => tx.gas_used_for_l1.is_none(),
553            TransactionField::MaxFeePerBlobGas => tx.max_fee_per_blob_gas.is_none(),
554            TransactionField::BlobVersionedHashes => tx.blob_versioned_hashes.is_none(),
555            TransactionField::BlobGasPrice => tx.blob_gas_price.is_none(),
556            TransactionField::BlobGasUsed => tx.blob_gas_used.is_none(),
557            TransactionField::DepositNonce => tx.deposit_nonce.is_none(),
558            TransactionField::DepositReceiptVersion => tx.deposit_receipt_version.is_none(),
559            TransactionField::L1BaseFeeScalar => tx.l1_base_fee_scalar.is_none(),
560            TransactionField::L1BlobBaseFee => tx.l1_blob_base_fee.is_none(),
561            TransactionField::L1BlobBaseFeeScalar => tx.l1_blob_base_fee_scalar.is_none(),
562            TransactionField::L1BlockNumber => tx.l1_block_number.is_none(),
563            TransactionField::Mint => tx.mint.is_none(),
564            TransactionField::Sighash => tx.sighash.is_none(),
565            TransactionField::SourceHash => tx.source_hash.is_none(),
566            TransactionField::RequestId => tx.request_id.is_none(),
567            TransactionField::TicketId => tx.ticket_id.is_none(),
568            TransactionField::RefundTo => tx.refund_to.is_none(),
569            TransactionField::MaxRefund => tx.max_refund.is_none(),
570            TransactionField::SubmissionFeeRefund => tx.submission_fee_refund.is_none(),
571            TransactionField::L1BaseFee => tx.l1_base_fee.is_none(),
572            TransactionField::DepositValue => tx.deposit_value.is_none(),
573            TransactionField::RetryTo => tx.retry_to.is_none(),
574            TransactionField::RetryValue => tx.retry_value.is_none(),
575            TransactionField::RetryData => tx.retry_data.is_none(),
576            TransactionField::Beneficiary => tx.beneficiary.is_none(),
577            TransactionField::MaxSubmissionFee => tx.max_submission_fee.is_none(),
578        }
579    }
580
581    fn has_block_field(block: &Block, field: BlockField) -> bool {
582        match field {
583            BlockField::Number => block.number.is_none(),
584            BlockField::Hash => block.hash.is_none(),
585            BlockField::ParentHash => block.parent_hash.is_none(),
586            BlockField::Nonce => block.nonce.is_none(),
587            BlockField::Sha3Uncles => block.sha3_uncles.is_none(),
588            BlockField::LogsBloom => block.logs_bloom.is_none(),
589            BlockField::TransactionsRoot => block.transactions_root.is_none(),
590            BlockField::StateRoot => block.state_root.is_none(),
591            BlockField::ReceiptsRoot => block.receipts_root.is_none(),
592            BlockField::Miner => block.miner.is_none(),
593            BlockField::Difficulty => block.difficulty.is_none(),
594            BlockField::TotalDifficulty => block.total_difficulty.is_none(),
595            BlockField::ExtraData => block.extra_data.is_none(),
596            BlockField::Size => block.size.is_none(),
597            BlockField::GasLimit => block.gas_limit.is_none(),
598            BlockField::GasUsed => block.gas_used.is_none(),
599            BlockField::Timestamp => block.timestamp.is_none(),
600            BlockField::Uncles => block.uncles.is_none(),
601            BlockField::BaseFeePerGas => block.base_fee_per_gas.is_none(),
602            BlockField::BlobGasUsed => block.blob_gas_used.is_none(),
603            BlockField::ExcessBlobGas => block.excess_blob_gas.is_none(),
604            BlockField::ParentBeaconBlockRoot => block.parent_beacon_block_root.is_none(),
605            BlockField::WithdrawalsRoot => block.withdrawals_root.is_none(),
606            BlockField::Withdrawals => block.withdrawals.is_none(),
607            BlockField::L1BlockNumber => block.l1_block_number.is_none(),
608            BlockField::SendCount => block.send_count.is_none(),
609            BlockField::SendRoot => block.send_root.is_none(),
610            BlockField::MixHash => block.mix_hash.is_none(),
611        }
612    }
613
614    fn has_log_field(log: &Log, field: LogField) -> bool {
615        match field {
616            LogField::Removed => log.removed.is_none(),
617            LogField::LogIndex => log.log_index.is_none(),
618            LogField::TransactionIndex => log.transaction_index.is_none(),
619            LogField::TransactionHash => log.transaction_hash.is_none(),
620            LogField::BlockHash => log.block_hash.is_none(),
621            LogField::BlockNumber => log.block_number.is_none(),
622            LogField::Address => log.address.is_none(),
623            LogField::Data => log.data.is_none(),
624            #[allow(clippy::get_first)] // annoying clippy
625            LogField::Topic0 => log.topics.get(0).is_none(),
626            LogField::Topic1 => log.topics.get(1).is_none(),
627            LogField::Topic2 => log.topics.get(2).is_none(),
628            LogField::Topic3 => log.topics.get(3).is_none(),
629        }
630    }
631
632    fn has_trace_field(trace: &Trace, field: TraceField) -> bool {
633        match field {
634            TraceField::From => trace.from.is_none(),
635            TraceField::To => trace.to.is_none(),
636            TraceField::CallType => trace.call_type.is_none(),
637            TraceField::Gas => trace.gas.is_none(),
638            TraceField::Input => trace.input.is_none(),
639            TraceField::Init => trace.init.is_none(),
640            TraceField::Value => trace.value.is_none(),
641            TraceField::Author => trace.author.is_none(),
642            TraceField::RewardType => trace.reward_type.is_none(),
643            TraceField::BlockHash => trace.block_hash.is_none(),
644            TraceField::BlockNumber => trace.block_number.is_none(),
645            TraceField::Address => trace.address.is_none(),
646            TraceField::Code => trace.code.is_none(),
647            TraceField::GasUsed => trace.gas_used.is_none(),
648            TraceField::Output => trace.output.is_none(),
649            TraceField::Subtraces => trace.subtraces.is_none(),
650            TraceField::TraceAddress => trace.trace_address.is_none(),
651            TraceField::TransactionHash => trace.transaction_hash.is_none(),
652            TraceField::TransactionPosition => trace.transaction_position.is_none(),
653            TraceField::Type => trace.type_.is_none(),
654            TraceField::Error => trace.error.is_none(),
655            TraceField::Sighash => trace.sighash.is_none(),
656            TraceField::ActionAddress => trace.action_address.is_none(),
657            TraceField::Balance => trace.balance.is_none(),
658            TraceField::RefundAddress => trace.refund_address.is_none(),
659        }
660    }
661
662    #[test]
663    fn has_all_tx_fields() {
664        let tx = Transaction::default();
665        for field in TransactionField::all() {
666            assert!(has_tx_field(&tx, field));
667        }
668    }
669
670    #[test]
671    fn has_all_block_fields() {
672        let block = Block::default();
673        for field in BlockField::all() {
674            assert!(has_block_field(&block, field));
675        }
676    }
677
678    #[test]
679    fn has_all_log_fields() {
680        let log = Log::default();
681        for field in LogField::all() {
682            assert!(has_log_field(&log, field));
683        }
684    }
685
686    #[test]
687    fn has_all_trace_fields() {
688        let trace = Trace::default();
689        for field in TraceField::all() {
690            assert!(has_trace_field(&trace, field));
691        }
692    }
693}