openzeppelin_monitor/models/blockchain/evm/
receipt.rs

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