Skip to main content

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    /// Returns the [`BlockNumHash`] of this header.
169    fn num_hash(&self) -> BlockNumHash {
170        BlockNumHash::new(self.number(), self.hash())
171    }
172}
173
174/// Block JSON-RPC response.
175pub trait BlockResponse {
176    /// Header type
177    type Header;
178    /// Transaction type
179    type Transaction: TransactionResponse;
180
181    /// Block header
182    fn header(&self) -> &Self::Header;
183
184    /// Block transactions
185    fn transactions(&self) -> &BlockTransactions<Self::Transaction>;
186
187    /// Mutable reference to block transactions
188    fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction>;
189
190    /// Returns the `other` field from `WithOtherFields` type.
191    fn other_fields(&self) -> Option<&alloy_serde::OtherFields> {
192        None
193    }
194}
195
196impl<T: TransactionResponse> TransactionResponse for WithOtherFields<T> {
197    fn tx_hash(&self) -> TxHash {
198        self.inner.tx_hash()
199    }
200
201    fn block_hash(&self) -> Option<BlockHash> {
202        self.inner.block_hash()
203    }
204
205    fn block_number(&self) -> Option<u64> {
206        self.inner.block_number()
207    }
208
209    fn transaction_index(&self) -> Option<u64> {
210        self.inner.transaction_index()
211    }
212
213    fn from(&self) -> Address {
214        self.inner.from()
215    }
216}
217
218impl<T: ReceiptResponse> ReceiptResponse for WithOtherFields<T> {
219    fn contract_address(&self) -> Option<Address> {
220        self.inner.contract_address()
221    }
222
223    fn status(&self) -> bool {
224        self.inner.status()
225    }
226
227    fn block_hash(&self) -> Option<BlockHash> {
228        self.inner.block_hash()
229    }
230
231    fn block_number(&self) -> Option<u64> {
232        self.inner.block_number()
233    }
234
235    fn transaction_hash(&self) -> TxHash {
236        self.inner.transaction_hash()
237    }
238
239    fn transaction_index(&self) -> Option<u64> {
240        self.inner.transaction_index()
241    }
242
243    fn gas_used(&self) -> u64 {
244        self.inner.gas_used()
245    }
246
247    fn effective_gas_price(&self) -> u128 {
248        self.inner.effective_gas_price()
249    }
250
251    fn blob_gas_used(&self) -> Option<u64> {
252        self.inner.blob_gas_used()
253    }
254
255    fn blob_gas_price(&self) -> Option<u128> {
256        self.inner.blob_gas_price()
257    }
258
259    fn from(&self) -> Address {
260        self.inner.from()
261    }
262
263    fn to(&self) -> Option<Address> {
264        self.inner.to()
265    }
266
267    fn cumulative_gas_used(&self) -> u64 {
268        self.inner.cumulative_gas_used()
269    }
270
271    fn state_root(&self) -> Option<B256> {
272        self.inner.state_root()
273    }
274}
275
276impl<T: BlockResponse> BlockResponse for WithOtherFields<T> {
277    type Header = T::Header;
278    type Transaction = T::Transaction;
279
280    fn header(&self) -> &Self::Header {
281        self.inner.header()
282    }
283
284    fn transactions(&self) -> &BlockTransactions<Self::Transaction> {
285        self.inner.transactions()
286    }
287
288    fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
289        self.inner.transactions_mut()
290    }
291
292    fn other_fields(&self) -> Option<&alloy_serde::OtherFields> {
293        Some(&self.other)
294    }
295}
296
297impl<T: HeaderResponse> HeaderResponse for WithOtherFields<T> {
298    fn hash(&self) -> BlockHash {
299        self.inner.hash()
300    }
301}