alloy_network_primitives/
traits.rs

1use crate::{BlockTransactions, InclusionInfo};
2use alloy_consensus::{BlockHeader, Transaction};
3use alloy_eips::BlockNumHash;
4use alloy_primitives::{Address, BlockHash, TxHash, B256};
5use alloy_serde::WithOtherFields;
6
7/// Error returned when a transaction failed.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct TransactionFailedError {
10    /// Hash of the failed transaction.
11    pub transaction_hash: TxHash,
12}
13
14impl core::fmt::Display for TransactionFailedError {
15    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16        write!(f, "Transaction {} failed", self.transaction_hash)
17    }
18}
19
20impl core::error::Error for TransactionFailedError {}
21
22/// Receipt JSON-RPC response.
23pub trait ReceiptResponse {
24    /// Address of the created contract, or `None` if the transaction was not a deployment.
25    fn contract_address(&self) -> Option<Address>;
26
27    /// Status of the transaction.
28    ///
29    /// ## Note
30    ///
31    /// Caution must be taken when using this method for deep-historical
32    /// receipts, as it may not accurately reflect the status of the
33    /// transaction. The transaction status is not knowable from the receipt
34    /// for transactions before [EIP-658].
35    fn status(&self) -> bool;
36
37    /// Hash of the block this transaction was included within.
38    fn block_hash(&self) -> Option<BlockHash>;
39
40    /// Number of the block this transaction was included within.
41    fn block_number(&self) -> Option<u64>;
42
43    /// Returns the [`BlockNumHash`] of the block this transaction was mined in.
44    ///
45    /// Returns `None` if this transaction is still pending.
46    fn block_hash_num(&self) -> Option<BlockNumHash> {
47        Some(BlockNumHash::new(self.block_number()?, self.block_hash()?))
48    }
49
50    /// Transaction Hash.
51    fn transaction_hash(&self) -> TxHash;
52
53    /// Index within the block.
54    fn transaction_index(&self) -> Option<u64>;
55
56    /// Gas used by this transaction alone.
57    fn gas_used(&self) -> u64;
58
59    /// Effective gas price.
60    fn effective_gas_price(&self) -> u128;
61
62    /// Total cost of this transaction = gas_used * effective_gas_price.
63    fn cost(&self) -> u128 {
64        self.gas_used() as u128 * self.effective_gas_price()
65    }
66
67    /// Blob gas used by the eip-4844 transaction.
68    fn blob_gas_used(&self) -> Option<u64>;
69
70    /// Blob gas price paid by the eip-4844 transaction.
71    fn blob_gas_price(&self) -> Option<u128>;
72
73    /// Address of the sender.
74    fn from(&self) -> Address;
75
76    /// Address of the receiver.
77    fn to(&self) -> Option<Address>;
78
79    /// Returns the cumulative gas used at this receipt.
80    fn cumulative_gas_used(&self) -> u64;
81
82    /// The post-transaction state root (pre Byzantium)
83    ///
84    /// EIP98 makes this field optional.
85    fn state_root(&self) -> Option<B256>;
86
87    /// Ensures the transaction was successful, returning an error if it failed.
88    fn ensure_success(&self) -> Result<(), TransactionFailedError> {
89        if self.status() {
90            Ok(())
91        } else {
92            Err(TransactionFailedError { transaction_hash: self.transaction_hash() })
93        }
94    }
95}
96
97/// Transaction JSON-RPC response. Aggregates transaction data with its block and signer context.
98pub trait TransactionResponse: Transaction {
99    /// Hash of the transaction
100    #[doc(alias = "transaction_hash")]
101    fn tx_hash(&self) -> TxHash;
102
103    /// Returns the hash of the block this transaction was mined in.
104    ///
105    /// Returns `None` if this transaction is still pending.
106    fn block_hash(&self) -> Option<BlockHash>;
107
108    /// Returns the number of the block this transaction was mined in.
109    ///
110    /// Returns `None` if this transaction is still pending.
111    fn block_number(&self) -> Option<u64>;
112
113    /// Returns the [`BlockNumHash`] of the block this transaction was mined in.
114    ///
115    /// Returns `None` if this transaction is still pending.
116    fn block_hash_num(&self) -> Option<BlockNumHash> {
117        Some(BlockNumHash::new(self.block_number()?, self.block_hash()?))
118    }
119
120    /// Transaction Index
121    fn transaction_index(&self) -> Option<u64>;
122
123    /// Sender of the transaction
124    fn from(&self) -> Address;
125
126    /// Gas Price, this is the RPC format for `max_fee_per_gas`, pre-eip-1559.
127    fn gas_price(&self) -> Option<u128> {
128        if self.ty() < 2 {
129            return Some(Transaction::max_fee_per_gas(self));
130        }
131        None
132    }
133
134    /// Max BaseFeePerGas the user is willing to pay. For pre-eip-1559 transactions, the field
135    /// label `gas_price` is used instead.
136    fn max_fee_per_gas(&self) -> Option<u128> {
137        if self.ty() < 2 {
138            return None;
139        }
140        Some(Transaction::max_fee_per_gas(self))
141    }
142
143    /// Transaction type format for RPC. This field is included since eip-2930.
144    fn transaction_type(&self) -> Option<u8> {
145        match self.ty() {
146            0 => None,
147            ty => Some(ty),
148        }
149    }
150
151    /// Returns the [`InclusionInfo`] if the transaction has been included.
152    ///
153    /// Returns `None` if this transaction is still pending (missing block number, hash, or index).
154    fn inclusion_info(&self) -> Option<InclusionInfo> {
155        Some(InclusionInfo {
156            block_hash: self.block_hash()?,
157            block_number: self.block_number()?,
158            transaction_index: self.transaction_index()?,
159        })
160    }
161}
162
163/// Header JSON-RPC response.
164pub trait HeaderResponse: BlockHeader {
165    /// Block hash
166    fn hash(&self) -> BlockHash;
167}
168
169/// Block JSON-RPC response.
170pub trait BlockResponse {
171    /// Header type
172    type Header;
173    /// Transaction type
174    type Transaction: TransactionResponse;
175
176    /// Block header
177    fn header(&self) -> &Self::Header;
178
179    /// Block transactions
180    fn transactions(&self) -> &BlockTransactions<Self::Transaction>;
181
182    /// Mutable reference to block transactions
183    fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction>;
184
185    /// Returns the `other` field from `WithOtherFields` type.
186    fn other_fields(&self) -> Option<&alloy_serde::OtherFields> {
187        None
188    }
189}
190
191impl<T: TransactionResponse> TransactionResponse for WithOtherFields<T> {
192    fn tx_hash(&self) -> TxHash {
193        self.inner.tx_hash()
194    }
195
196    fn block_hash(&self) -> Option<BlockHash> {
197        self.inner.block_hash()
198    }
199
200    fn block_number(&self) -> Option<u64> {
201        self.inner.block_number()
202    }
203
204    fn transaction_index(&self) -> Option<u64> {
205        self.inner.transaction_index()
206    }
207
208    fn from(&self) -> Address {
209        self.inner.from()
210    }
211}
212
213impl<T: ReceiptResponse> ReceiptResponse for WithOtherFields<T> {
214    fn contract_address(&self) -> Option<Address> {
215        self.inner.contract_address()
216    }
217
218    fn status(&self) -> bool {
219        self.inner.status()
220    }
221
222    fn block_hash(&self) -> Option<BlockHash> {
223        self.inner.block_hash()
224    }
225
226    fn block_number(&self) -> Option<u64> {
227        self.inner.block_number()
228    }
229
230    fn transaction_hash(&self) -> TxHash {
231        self.inner.transaction_hash()
232    }
233
234    fn transaction_index(&self) -> Option<u64> {
235        self.inner.transaction_index()
236    }
237
238    fn gas_used(&self) -> u64 {
239        self.inner.gas_used()
240    }
241
242    fn effective_gas_price(&self) -> u128 {
243        self.inner.effective_gas_price()
244    }
245
246    fn blob_gas_used(&self) -> Option<u64> {
247        self.inner.blob_gas_used()
248    }
249
250    fn blob_gas_price(&self) -> Option<u128> {
251        self.inner.blob_gas_price()
252    }
253
254    fn from(&self) -> Address {
255        self.inner.from()
256    }
257
258    fn to(&self) -> Option<Address> {
259        self.inner.to()
260    }
261
262    fn cumulative_gas_used(&self) -> u64 {
263        self.inner.cumulative_gas_used()
264    }
265
266    fn state_root(&self) -> Option<B256> {
267        self.inner.state_root()
268    }
269}
270
271impl<T: BlockResponse> BlockResponse for WithOtherFields<T> {
272    type Header = T::Header;
273    type Transaction = T::Transaction;
274
275    fn header(&self) -> &Self::Header {
276        self.inner.header()
277    }
278
279    fn transactions(&self) -> &BlockTransactions<Self::Transaction> {
280        self.inner.transactions()
281    }
282
283    fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
284        self.inner.transactions_mut()
285    }
286
287    fn other_fields(&self) -> Option<&alloy_serde::OtherFields> {
288        Some(&self.other)
289    }
290}
291
292impl<T: HeaderResponse> HeaderResponse for WithOtherFields<T> {
293    fn hash(&self) -> BlockHash {
294        self.inner.hash()
295    }
296}