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 kind: 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}
370
371/// Log object
372#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
373pub struct Log {
374 /// The boolean value indicating if the event was removed from the blockchain due
375 /// to a chain reorganization. True if the log was removed. False if it is a valid log.
376 pub removed: Option<bool>,
377 /// The integer identifying the index of the event within the block's list of events.
378 pub log_index: Option<LogIndex>,
379 /// The integer index of the transaction within the block's list of transactions.
380 pub transaction_index: Option<TransactionIndex>,
381 /// The hash of the transaction that triggered the event.
382 pub transaction_hash: Option<Hash>,
383 /// The hash of the block in which the event was included.
384 pub block_hash: Option<Hash>,
385 /// The block number in which the event was included.
386 pub block_number: Option<BlockNumber>,
387 /// The contract address from which the event originated.
388 pub address: Option<Address>,
389 /// The non-indexed data that was emitted along with the event.
390 pub data: Option<Data>,
391 /// An array of 32-byte data fields containing indexed event parameters.
392 pub topics: ArrayVec<Option<LogArgument>, 4>,
393}
394
395/// Trace object
396#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
397pub struct Trace {
398 /// The address of the sender who initiated the transaction.
399 pub from: Option<Address>,
400 /// The address of the recipient of the transaction if it was a transaction to an address.
401 /// For contract creation transactions, this field is None.
402 pub to: Option<Address>,
403 /// The type of trace, `call` or `delegatecall`, two ways to invoke a function in a smart contract.
404 ///
405 /// `call` creates a new environment for the function to work in, so changes made in that
406 /// function won't affect the environment where the function was called.
407 ///
408 /// `delegatecall` doesn't create a new environment. Instead, it runs the function within the
409 /// environment of the caller, so changes made in that function will affect the caller's environment.
410 pub call_type: Option<String>,
411 /// The units of gas included in the transaction by the sender.
412 pub gas: Option<Quantity>,
413 /// The optional input data sent with the transaction, usually used to interact with smart contracts.
414 pub input: Option<Data>,
415 /// The init code.
416 pub init: Option<Data>,
417 /// The value of the native token transferred along with the transaction, in Wei.
418 pub value: Option<Quantity>,
419 /// The address of the receiver for reward transaction.
420 pub author: Option<Address>,
421 /// Kind of reward. `Block` reward or `Uncle` reward.
422 pub reward_type: Option<String>,
423 /// The hash of the block in which the transaction was included.
424 pub block_hash: Option<Hash>,
425 /// The number of the block in which the transaction was included.
426 pub block_number: Option<u64>,
427 /// Destroyed address.
428 pub address: Option<Address>,
429 /// Contract code.
430 pub code: Option<Data>,
431 /// The total used gas by the call, encoded as hexadecimal.
432 pub gas_used: Option<Quantity>,
433 /// The return value of the call, encoded as a hexadecimal string.
434 pub output: Option<Data>,
435 /// The number of sub-traces created during execution. When a transaction is executed on the EVM,
436 /// it may trigger additional sub-executions, such as when a smart contract calls another smart
437 /// contract or when an external account is accessed.
438 pub subtraces: Option<u64>,
439 /// An array that indicates the position of the transaction in the trace.
440 pub trace_address: Option<Vec<u64>>,
441 /// The hash of the transaction.
442 pub transaction_hash: Option<Hash>,
443 /// The index of the transaction in the block.
444 pub transaction_position: Option<u64>,
445 /// The type of action taken by the transaction, `call`, `create`, `reward` and `suicide`.
446 ///
447 /// `call` is the most common type of trace and occurs when a smart contract invokes another contract's function.
448 ///
449 /// `create` represents the creation of a new smart contract. This type of trace occurs when a smart contract is deployed to the blockchain.
450 #[serde(rename = "type")]
451 pub kind: Option<String>,
452 /// A string that indicates whether the transaction was successful or not.
453 ///
454 /// None if successful, Reverted if not.
455 pub error: Option<String>,
456}