openzeppelin_monitor/models/blockchain/evm/
receipt.rs

1//! EVM receipt data structures.
2
3use std::ops::Deref;
4
5use serde::{Deserialize, Serialize};
6
7use alloy::{
8	consensus::{Eip658Value, ReceiptEnvelope},
9	primitives::{aliases::B2048, Address, Bytes, Log as AlloyLog, B256, U256, U64},
10	rpc::types::{Index, TransactionReceipt as AlloyTransactionReceipt},
11};
12
13/// Base Receipt struct
14/// Copied from web3 crate (now deprecated) and slightly modified for alloy compatibility
15#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
16pub struct BaseReceipt {
17	/// Transaction hash.
18	#[serde(rename = "transactionHash")]
19	pub transaction_hash: B256,
20	/// Index within the block.
21	#[serde(rename = "transactionIndex")]
22	pub transaction_index: Index,
23	/// Hash of the block this transaction was included within.
24	#[serde(rename = "blockHash")]
25	pub block_hash: Option<B256>,
26	/// Number of the block this transaction was included within.
27	#[serde(rename = "blockNumber")]
28	pub block_number: Option<U64>,
29	/// Sender
30	/// Note: default address if the client did not return this value
31	/// (maintains backwards compatibility for <= 0.7.0 when this field was missing)
32	#[serde(default)]
33	pub from: Address,
34	/// Recipient (None when contract creation)
35	/// Note: Also `None` if the client did not return this value
36	/// (maintains backwards compatibility for <= 0.7.0 when this field was missing)
37	#[serde(default)]
38	pub to: Option<Address>,
39	/// Cumulative gas used within the block after this was executed.
40	#[serde(rename = "cumulativeGasUsed")]
41	pub cumulative_gas_used: U256,
42	/// Gas used by this transaction alone.
43	///
44	/// Gas used is `None` if the the client is running in light client mode.
45	#[serde(rename = "gasUsed")]
46	pub gas_used: Option<U256>,
47	/// Contract address created, or `None` if not a deployment.
48	#[serde(rename = "contractAddress")]
49	pub contract_address: Option<Address>,
50	/// Logs generated within this transaction.
51	pub logs: Vec<BaseLog>,
52	/// Status: either 1 (success) or 0 (failure).
53	pub status: Option<U64>,
54	/// State root.
55	pub root: Option<B256>,
56	/// Logs bloom
57	#[serde(rename = "logsBloom")]
58	pub logs_bloom: B2048,
59	/// Transaction type, Some(1) for AccessList transaction, None for Legacy
60	#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
61	pub transaction_type: Option<U64>,
62	/// Effective gas price
63	#[serde(rename = "effectiveGasPrice")]
64	pub effective_gas_price: Option<U256>,
65}
66
67/// Base Log struct
68/// Copied from web3 crate (now deprecated) and slightly modified for alloy compatibility
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct BaseLog {
71	/// H160
72	pub address: Address,
73	/// Topics
74	pub topics: Vec<B256>,
75	/// Data
76	pub data: Bytes,
77	/// Block Hash
78	#[serde(rename = "blockHash")]
79	pub block_hash: Option<B256>,
80	/// Block Number
81	#[serde(rename = "blockNumber")]
82	pub block_number: Option<U64>,
83	/// Transaction Hash
84	#[serde(rename = "transactionHash")]
85	pub transaction_hash: Option<B256>,
86	/// Transaction Index
87	#[serde(rename = "transactionIndex")]
88	pub transaction_index: Option<Index>,
89	/// Log Index in Block
90	#[serde(rename = "logIndex")]
91	pub log_index: Option<U256>,
92	/// Log Index in Transaction
93	#[serde(rename = "transactionLogIndex")]
94	pub transaction_log_index: Option<U256>,
95	/// Log Type
96	#[serde(rename = "logType")]
97	pub log_type: Option<String>,
98	/// Removed
99	pub removed: Option<bool>,
100}
101
102impl From<AlloyLog> for BaseLog {
103	fn from(log: AlloyLog) -> Self {
104		Self {
105			address: log.address,
106			topics: log.topics().to_vec(),
107			data: log.data.data,
108			block_hash: None,
109			block_number: None,
110			transaction_hash: None,
111			transaction_index: None,
112			log_index: None,
113			transaction_log_index: None,
114			log_type: None,
115			removed: None,
116		}
117	}
118}
119
120/// Wrapper around Base Receipt that implements additional functionality
121///
122/// This type provides a convenient interface for working with EVM receipts
123/// while maintaining compatibility with the alloy types.
124#[derive(Debug, Clone, Serialize, Deserialize, Default)]
125pub struct TransactionReceipt(pub BaseReceipt);
126
127impl From<BaseReceipt> for TransactionReceipt {
128	fn from(tx: BaseReceipt) -> Self {
129		Self(tx)
130	}
131}
132
133impl From<AlloyTransactionReceipt> for TransactionReceipt {
134	fn from(receipt: AlloyTransactionReceipt) -> Self {
135		let inner_receipt = match &receipt.inner {
136			ReceiptEnvelope::Legacy(r) => &r.receipt,
137			ReceiptEnvelope::Eip2930(r) => &r.receipt,
138			ReceiptEnvelope::Eip1559(r) => &r.receipt,
139			ReceiptEnvelope::Eip4844(r) => &r.receipt,
140			ReceiptEnvelope::Eip7702(r) => &r.receipt,
141		};
142
143		let tx = BaseReceipt {
144			transaction_hash: receipt.transaction_hash,
145			transaction_index: Index::from(receipt.transaction_index.unwrap_or(0) as usize),
146			block_hash: receipt.block_hash,
147			block_number: receipt.block_number.map(U64::from),
148			from: receipt.from,
149			to: receipt.to,
150			cumulative_gas_used: U256::from(inner_receipt.cumulative_gas_used),
151			gas_used: Some(U256::from(receipt.gas_used)),
152			contract_address: receipt.contract_address,
153			logs: inner_receipt
154				.logs
155				.iter()
156				.cloned()
157				.map(|l| BaseLog::from(alloy::primitives::Log::from(l)))
158				.collect(),
159			status: match inner_receipt.status {
160				Eip658Value::Eip658(status) => Some(U64::from(if status { 1u64 } else { 0u64 })),
161				Eip658Value::PostState(_) => Some(U64::from(1u64)),
162			},
163			root: None,
164			logs_bloom: B2048::from_slice(match &receipt.inner {
165				ReceiptEnvelope::Legacy(r) => r.logs_bloom.as_slice(),
166				ReceiptEnvelope::Eip2930(r) => r.logs_bloom.as_slice(),
167				ReceiptEnvelope::Eip1559(r) => r.logs_bloom.as_slice(),
168				ReceiptEnvelope::Eip4844(r) => r.logs_bloom.as_slice(),
169				ReceiptEnvelope::Eip7702(r) => r.logs_bloom.as_slice(),
170			}),
171			transaction_type: Some(U64::from(match receipt.inner {
172				ReceiptEnvelope::Legacy(_) => 0,
173				ReceiptEnvelope::Eip2930(_) => 1,
174				ReceiptEnvelope::Eip1559(_) => 2,
175				ReceiptEnvelope::Eip4844(_) => 3,
176				ReceiptEnvelope::Eip7702(_) => 4,
177			})),
178			effective_gas_price: Some(U256::from(receipt.effective_gas_price)),
179		};
180		Self(tx)
181	}
182}
183
184impl Deref for TransactionReceipt {
185	type Target = BaseReceipt;
186
187	fn deref(&self) -> &Self::Target {
188		&self.0
189	}
190}