reth_rpc/eth/helpers/
transaction.rs1use crate::EthApi;
4use alloy_primitives::{hex, Bytes, B256};
5use reth_rpc_convert::RpcConvert;
6use reth_rpc_eth_api::{
7 helpers::{spec::SignersForRpc, EthTransactions, LoadTransaction},
8 FromEvmError, RpcNodeCore,
9};
10use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError};
11use reth_transaction_pool::{AddedTransactionOutcome, PoolTransaction, TransactionPool};
12
13impl<N, Rpc> EthTransactions for EthApi<N, Rpc>
14where
15 N: RpcNodeCore,
16 EthApiError: FromEvmError<N::Evm>,
17 Rpc: RpcConvert<Primitives = N::Primitives, Error = EthApiError>,
18{
19 #[inline]
20 fn signers(&self) -> &SignersForRpc<Self::Provider, Self::NetworkTypes> {
21 self.inner.signers()
22 }
23
24 async fn send_raw_transaction(&self, tx: Bytes) -> Result<B256, Self::Error> {
28 let recovered = recover_raw_transaction(&tx)?;
29
30 self.broadcast_raw_transaction(tx.clone());
32
33 let pool_transaction = <Self::Pool as TransactionPool>::Transaction::from_pooled(recovered);
34
35 if let Some(client) = self.raw_tx_forwarder() {
37 tracing::debug!(target: "rpc::eth", hash = %pool_transaction.hash(), "forwarding raw transaction to forwarder");
38 let rlp_hex = hex::encode_prefixed(tx);
39
40 let hash =
41 client.request("eth_sendRawTransaction", (rlp_hex,)).await.inspect_err(|err| {
42 tracing::debug!(target: "rpc::eth", %err, hash=% *pool_transaction.hash(), "failed to forward raw transaction");
43 }).map_err(EthApiError::other)?;
44
45 let _ = self.inner.add_pool_transaction(pool_transaction).await;
47
48 return Ok(hash);
49 }
50
51 let AddedTransactionOutcome { hash, .. } =
53 self.inner.add_pool_transaction(pool_transaction).await?;
54
55 Ok(hash)
56 }
57}
58
59impl<N, Rpc> LoadTransaction for EthApi<N, Rpc>
60where
61 N: RpcNodeCore,
62 EthApiError: FromEvmError<N::Evm>,
63 Rpc: RpcConvert<Primitives = N::Primitives, Error = EthApiError>,
64{
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use alloy_primitives::{hex_literal::hex, Bytes};
71 use reth_chainspec::ChainSpecProvider;
72 use reth_evm_ethereum::EthEvmConfig;
73 use reth_network_api::noop::NoopNetwork;
74 use reth_provider::test_utils::NoopProvider;
75 use reth_rpc_eth_api::helpers::EthTransactions;
76 use reth_transaction_pool::{test_utils::testing_pool, TransactionPool};
77
78 #[tokio::test]
79 async fn send_raw_transaction() {
80 let noop_provider = NoopProvider::default();
81 let noop_network_provider = NoopNetwork::default();
82
83 let pool = testing_pool();
84
85 let evm_config = EthEvmConfig::new(noop_provider.chain_spec());
86 let eth_api =
87 EthApi::builder(noop_provider.clone(), pool.clone(), noop_network_provider, evm_config)
88 .build();
89
90 let tx_1 = Bytes::from(hex!(
92 "02f871018303579880850555633d1b82520894eee27662c2b8eba3cd936a23f039f3189633e4c887ad591c62bdaeb180c080a07ea72c68abfb8fca1bd964f0f99132ed9280261bdca3e549546c0205e800f7d0a05b4ef3039e9c9b9babc179a1878fb825b5aaf5aed2fa8744854150157b08d6f3"
93 ));
94
95 let tx_1_result = eth_api.send_raw_transaction(tx_1).await.unwrap();
96 assert_eq!(
97 pool.len(),
98 1,
99 "expect 1 transaction in the pool, but pool size is {}",
100 pool.len()
101 );
102
103 let tx_2 = Bytes::from(hex!(
105 "02f9043c018202b7843b9aca00850c807d37a08304d21d94ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b881bc16d674ec80000b903c43593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000063e2d99f00000000000000000000000000000000000000000000000000000000000000030b000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000065717fe021ea67801d1088cc80099004b05b64600000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e95fd5965fd1f1a6f0d4600000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000428dca9537116148616a5a3e44035af17238fe9dc080a0c6ec1e41f5c0b9511c49b171ad4e04c6bb419c74d99fe9891d74126ec6e4e879a032069a753d7a2cfa158df95421724d24c0e9501593c09905abf3699b4a4405ce"
106 ));
107
108 let tx_2_result = eth_api.send_raw_transaction(tx_2).await.unwrap();
109 assert_eq!(
110 pool.len(),
111 2,
112 "expect 2 transactions in the pool, but pool size is {}",
113 pool.len()
114 );
115
116 assert!(pool.get(&tx_1_result).is_some(), "tx1 not found in the pool");
117 assert!(pool.get(&tx_2_result).is_some(), "tx2 not found in the pool");
118 }
119}