openzeppelin_monitor/models/blockchain/evm/
block.rs1use 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#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
15pub struct BaseBlock<TX> {
16 pub hash: Option<B256>,
18 #[serde(rename = "parentHash")]
20 pub parent_hash: B256,
21 #[serde(rename = "sha3Uncles")]
23 #[serde(default)]
24 pub uncles_hash: B256,
25 #[serde(rename = "miner", default)]
27 pub author: Address,
28 #[serde(rename = "stateRoot")]
30 pub state_root: B256,
31 #[serde(rename = "transactionsRoot")]
33 pub transactions_root: B256,
34 #[serde(rename = "receiptsRoot")]
36 pub receipts_root: B256,
37 pub number: Option<U64>,
39 #[serde(rename = "gasUsed")]
41 pub gas_used: U256,
42 #[serde(rename = "gasLimit")]
44 #[serde(default)]
45 pub gas_limit: U256,
46 #[serde(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")]
48 pub base_fee_per_gas: Option<U256>,
49 #[serde(rename = "extraData")]
51 pub extra_data: Bytes,
52 #[serde(rename = "logsBloom")]
54 pub logs_bloom: Option<B2048>,
55 pub timestamp: U256,
57 #[serde(default)]
59 pub difficulty: U256,
60 #[serde(rename = "totalDifficulty")]
62 pub total_difficulty: Option<U256>,
63 #[serde(default, rename = "sealFields")]
65 pub seal_fields: Vec<Bytes>,
66 #[serde(default)]
68 pub uncles: Vec<B256>,
69 pub transactions: Vec<TX>,
71 pub size: Option<U256>,
73 #[serde(rename = "mixHash")]
75 pub mix_hash: Option<B256>,
76 pub nonce: Option<B64>,
78}
79
80#[derive(Debug, Serialize, Deserialize, Clone, Default)]
85pub struct Block(pub BaseBlock<EVMTransaction>);
86
87impl Block {
88 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![], 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 let base_block = create_test_block(12345);
184 let block = Block(base_block.clone());
185 assert_eq!(block.number(), Some(12345));
186
187 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 assert_eq!(block.number, base_block.number);
210 assert_eq!(block.hash, base_block.hash);
211 }
212}