reth_rpc/eth/helpers/
types.rs1use alloy_consensus::{SignableTransaction, Transaction as _, TxEnvelope};
4use alloy_network::{Ethereum, Network};
5use alloy_primitives::Signature;
6use alloy_rpc_types::TransactionRequest;
7use alloy_rpc_types_eth::{Transaction, TransactionInfo};
8use reth_ethereum_primitives::TransactionSigned;
9use reth_primitives_traits::Recovered;
10use reth_rpc_eth_api::EthApiTypes;
11use reth_rpc_eth_types::EthApiError;
12use reth_rpc_types_compat::TransactionCompat;
13
14#[derive(Debug, Clone, Copy, Default)]
16pub struct EthereumEthApiTypes(EthTxBuilder);
17
18impl EthApiTypes for EthereumEthApiTypes {
19 type Error = EthApiError;
20 type NetworkTypes = Ethereum;
21 type TransactionCompat = EthTxBuilder;
22
23 fn tx_resp_builder(&self) -> &Self::TransactionCompat {
24 &self.0
25 }
26}
27
28#[derive(Debug, Clone, Copy, Default)]
30#[non_exhaustive]
31pub struct EthTxBuilder;
32
33impl TransactionCompat<TransactionSigned> for EthTxBuilder
34where
35 Self: Send + Sync,
36{
37 type Transaction = <Ethereum as Network>::TransactionResponse;
38
39 type Error = EthApiError;
40
41 fn fill(
42 &self,
43 tx: Recovered<TransactionSigned>,
44 tx_info: TransactionInfo,
45 ) -> Result<Self::Transaction, Self::Error> {
46 let tx = tx.convert::<TxEnvelope>();
47
48 let TransactionInfo {
49 block_hash, block_number, index: transaction_index, base_fee, ..
50 } = tx_info;
51
52 let effective_gas_price = base_fee
53 .map(|base_fee| {
54 tx.effective_tip_per_gas(base_fee).unwrap_or_default() + base_fee as u128
55 })
56 .unwrap_or_else(|| tx.max_fee_per_gas());
57
58 Ok(Transaction {
59 inner: tx,
60 block_hash,
61 block_number,
62 transaction_index,
63 effective_gas_price: Some(effective_gas_price),
64 })
65 }
66
67 fn build_simulate_v1_transaction(
68 &self,
69 request: TransactionRequest,
70 ) -> Result<TransactionSigned, Self::Error> {
71 let Ok(tx) = request.build_typed_tx() else {
72 return Err(EthApiError::TransactionConversionError)
73 };
74 let signature = Signature::new(Default::default(), Default::default(), false);
75 Ok(tx.into_signed(signature).into())
76 }
77
78 fn otterscan_api_truncate_input(tx: &mut Self::Transaction) {
79 let input = tx.inner.inner_mut().input_mut();
80 *input = input.slice(..4);
81 }
82}
83
84#[cfg(test)]
86mod tests {
87 use super::*;
88 use alloy_consensus::TxType;
89 use reth_rpc_eth_types::simulate::resolve_transaction;
90 use revm::database::CacheDB;
91
92 #[test]
93 fn test_resolve_transaction_empty_request() {
94 let builder = EthTxBuilder::default();
95 let mut db = CacheDB::<reth_revm::db::EmptyDBTyped<reth_errors::ProviderError>>::default();
96 let tx = TransactionRequest::default();
97 let result = resolve_transaction(tx, 21000, 0, 1, &mut db, &builder).unwrap();
98
99 let tx = result.into_inner();
101 assert_eq!(tx.max_fee_per_gas(), 0);
102 assert_eq!(tx.max_priority_fee_per_gas(), Some(0));
103 assert_eq!(tx.gas_price(), None);
104 }
105
106 #[test]
107 fn test_resolve_transaction_legacy() {
108 let mut db = CacheDB::<reth_revm::db::EmptyDBTyped<reth_errors::ProviderError>>::default();
109 let builder = EthTxBuilder::default();
110
111 let tx = TransactionRequest { gas_price: Some(100), ..Default::default() };
112
113 let tx = resolve_transaction(tx, 21000, 0, 1, &mut db, &builder).unwrap();
114
115 assert_eq!(tx.tx_type(), TxType::Legacy);
116
117 let tx = tx.into_inner();
118 assert_eq!(tx.gas_price(), Some(100));
119 assert_eq!(tx.max_priority_fee_per_gas(), None);
120 }
121
122 #[test]
123 fn test_resolve_transaction_partial_eip1559() {
124 let mut db = CacheDB::<reth_revm::db::EmptyDBTyped<reth_errors::ProviderError>>::default();
125 let builder = EthTxBuilder::default();
126
127 let tx = TransactionRequest {
128 max_fee_per_gas: Some(200),
129 max_priority_fee_per_gas: Some(10),
130 ..Default::default()
131 };
132
133 let result = resolve_transaction(tx, 21000, 0, 1, &mut db, &builder).unwrap();
134
135 assert_eq!(result.tx_type(), TxType::Eip1559);
136 let tx = result.into_inner();
137 assert_eq!(tx.max_fee_per_gas(), 200);
138 assert_eq!(tx.max_priority_fee_per_gas(), Some(10));
139 assert_eq!(tx.gas_price(), None);
140 }
141}