openzeppelin_monitor/models/blockchain/evm/
block.rs

1//! EVM block data structures.
2
3use alloy::{
4	primitives::{aliases::B2048, Address, Bytes, B256, B64, U256, U64},
5	rpc::types::{Block as AlloyBlock, BlockTransactions, Transaction as AlloyTransaction},
6};
7use serde::{Deserialize, Serialize};
8use std::ops::Deref;
9
10use super::EVMTransaction;
11
12/// Base Block struct
13/// Copied from web3 crate (now deprecated) and slightly modified for alloy compatibility
14#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
15pub struct BaseBlock<TX> {
16	/// Hash of the block
17	pub hash: Option<B256>,
18	/// Hash of the parent
19	#[serde(rename = "parentHash")]
20	pub parent_hash: B256,
21	/// Hash of the uncles
22	#[serde(rename = "sha3Uncles")]
23	#[serde(default)]
24	pub uncles_hash: B256,
25	/// Miner/author's address.
26	#[serde(rename = "miner", default)]
27	pub author: Address,
28	/// State root hash
29	#[serde(rename = "stateRoot")]
30	pub state_root: B256,
31	/// Transactions root hash
32	#[serde(rename = "transactionsRoot")]
33	pub transactions_root: B256,
34	/// Transactions receipts root hash
35	#[serde(rename = "receiptsRoot")]
36	pub receipts_root: B256,
37	/// Block number. None if pending.
38	pub number: Option<U64>,
39	/// Gas Used
40	#[serde(rename = "gasUsed")]
41	pub gas_used: U256,
42	/// Gas Limit
43	#[serde(rename = "gasLimit")]
44	#[serde(default)]
45	pub gas_limit: U256,
46	/// Base fee per unit of gas (if past London)
47	#[serde(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")]
48	pub base_fee_per_gas: Option<U256>,
49	/// Extra data
50	#[serde(rename = "extraData")]
51	pub extra_data: Bytes,
52	/// Logs bloom
53	#[serde(rename = "logsBloom")]
54	pub logs_bloom: Option<B2048>,
55	/// Timestamp
56	pub timestamp: U256,
57	/// Difficulty
58	#[serde(default)]
59	pub difficulty: U256,
60	/// Total difficulty
61	#[serde(rename = "totalDifficulty")]
62	pub total_difficulty: Option<U256>,
63	/// Seal fields
64	#[serde(default, rename = "sealFields")]
65	pub seal_fields: Vec<Bytes>,
66	/// Uncles' hashes
67	#[serde(default)]
68	pub uncles: Vec<B256>,
69	/// Transactions
70	pub transactions: Vec<TX>,
71	/// Size in bytes
72	pub size: Option<U256>,
73	/// Mix Hash
74	#[serde(rename = "mixHash")]
75	pub mix_hash: Option<B256>,
76	/// Nonce
77	pub nonce: Option<B64>,
78}
79
80/// Wrapper around Base Block that implements additional functionality
81///
82/// This type provides a convenient interface for working with EVM blocks
83/// while maintaining compatibility with the alloy types.
84#[derive(Debug, Serialize, Deserialize, Clone, Default)]
85pub struct Block(pub BaseBlock<EVMTransaction>);
86
87impl Block {
88	/// Get the block number
89	///
90	/// Returns the block number as an `Option<u64>`.
91	pub fn number(&self) -> Option<u64> {
92		self.0.number.map(|n| n.to())
93	}
94}
95
96impl From<BaseBlock<EVMTransaction>> for Block {
97	fn from(block: BaseBlock<EVMTransaction>) -> Self {
98		Self(block)
99	}
100}
101
102impl From<AlloyBlock<AlloyTransaction>> for Block {
103	fn from(block: AlloyBlock<AlloyTransaction>) -> Self {
104		let block = BaseBlock {
105			hash: Some(block.header.hash),
106			parent_hash: block.header.inner.parent_hash,
107			uncles_hash: block.header.inner.ommers_hash,
108			author: block.header.inner.beneficiary,
109			state_root: block.header.inner.state_root,
110			transactions_root: block.header.inner.transactions_root,
111			receipts_root: block.header.inner.receipts_root,
112			number: Some(U64::from(block.header.inner.number)),
113			gas_used: U256::from(block.header.inner.gas_used),
114			gas_limit: U256::from(block.header.inner.gas_limit),
115			base_fee_per_gas: block
116				.header
117				.inner
118				.base_fee_per_gas
119				.map(|fee| U256::from(fee)),
120			extra_data: block.header.inner.extra_data,
121			logs_bloom: Some(block.header.inner.logs_bloom.into()),
122			timestamp: U256::from(block.header.inner.timestamp),
123			difficulty: block.header.inner.difficulty,
124			total_difficulty: block.header.total_difficulty,
125			seal_fields: vec![], // Alloy doesn't have seal fields
126			uncles: block.uncles,
127			transactions: match block.transactions {
128				BlockTransactions::Full(txs) => txs.into_iter().map(EVMTransaction::from).collect(),
129				_ => vec![],
130			},
131			size: block.header.size.map(|s| U256::from(s)),
132			mix_hash: Some(block.header.inner.mix_hash),
133			nonce: Some(block.header.inner.nonce),
134		};
135
136		Self(block)
137	}
138}
139
140impl Deref for Block {
141	type Target = BaseBlock<EVMTransaction>;
142
143	fn deref(&self) -> &Self::Target {
144		&self.0
145	}
146}
147
148#[cfg(test)]
149mod tests {
150	use super::*;
151	use alloy::primitives::{Address, B256, U256, U64};
152
153	fn create_test_block(block_number: u64) -> BaseBlock<EVMTransaction> {
154		BaseBlock {
155			number: Some(U64::from(block_number)),
156			hash: Some(B256::ZERO),
157			parent_hash: B256::ZERO,
158			uncles_hash: B256::ZERO,
159			author: Address::ZERO,
160			state_root: B256::ZERO,
161			transactions_root: B256::ZERO,
162			receipts_root: B256::ZERO,
163			gas_used: U256::ZERO,
164			gas_limit: U256::ZERO,
165			extra_data: vec![].into(),
166			logs_bloom: None,
167			timestamp: U256::ZERO,
168			difficulty: U256::ZERO,
169			total_difficulty: None,
170			seal_fields: vec![],
171			uncles: vec![],
172			transactions: vec![],
173			size: None,
174			mix_hash: None,
175			nonce: None,
176			base_fee_per_gas: None,
177		}
178	}
179
180	#[test]
181	fn test_block_number() {
182		// Create a test block with number
183		let base_block = create_test_block(12345);
184		let block = Block(base_block.clone());
185		assert_eq!(block.number(), Some(12345));
186
187		// Test with None value
188		let base_block_no_number = BaseBlock {
189			number: None,
190			..base_block
191		};
192		let block_no_number = Block(base_block_no_number);
193		assert_eq!(block_no_number.number(), None);
194	}
195
196	#[test]
197	fn test_from_base_block() {
198		let base_block = create_test_block(12345);
199		let block: Block = base_block.clone().into();
200		assert_eq!(block.0.number, base_block.number);
201	}
202
203	#[test]
204	fn test_deref() {
205		let base_block = create_test_block(12345);
206
207		let block = Block(base_block.clone());
208		// Test that we can access BaseBlock fields through deref
209		assert_eq!(block.number, base_block.number);
210		assert_eq!(block.hash, base_block.hash);
211	}
212}