reth_optimism_rpc/eth/
transaction.rs

1//! Loads and formats OP transaction RPC response.
2
3use crate::{OpEthApi, OpEthApiError, SequencerClient};
4use alloy_primitives::{Bytes, B256};
5use alloy_rpc_types_eth::TransactionInfo;
6use op_alloy_consensus::{transaction::OpTransactionInfo, OpTransaction};
7use reth_optimism_primitives::DepositReceipt;
8use reth_primitives_traits::SignedTransaction;
9use reth_rpc_eth_api::{
10    helpers::{spec::SignersForRpc, EthTransactions, LoadTransaction},
11    try_into_op_tx_info, FromEthApiError, RpcConvert, RpcNodeCore, TxInfoMapper,
12};
13use reth_rpc_eth_types::utils::recover_raw_transaction;
14use reth_storage_api::{errors::ProviderError, ReceiptProvider};
15use reth_transaction_pool::{
16    AddedTransactionOutcome, PoolTransaction, TransactionOrigin, TransactionPool,
17};
18use std::fmt::{Debug, Formatter};
19
20impl<N, Rpc> EthTransactions for OpEthApi<N, Rpc>
21where
22    N: RpcNodeCore,
23    Rpc: RpcConvert<Primitives = N::Primitives, Error = OpEthApiError>,
24{
25    fn signers(&self) -> &SignersForRpc<Self::Provider, Self::NetworkTypes> {
26        self.inner.eth_api.signers()
27    }
28
29    /// Decodes and recovers the transaction and submits it to the pool.
30    ///
31    /// Returns the hash of the transaction.
32    async fn send_raw_transaction(&self, tx: Bytes) -> Result<B256, Self::Error> {
33        let recovered = recover_raw_transaction(&tx)?;
34
35        // broadcast raw transaction to subscribers if there is any.
36        self.eth_api().broadcast_raw_transaction(tx.clone());
37
38        let pool_transaction = <Self::Pool as TransactionPool>::Transaction::from_pooled(recovered);
39
40        // On optimism, transactions are forwarded directly to the sequencer to be included in
41        // blocks that it builds.
42        if let Some(client) = self.raw_tx_forwarder().as_ref() {
43            tracing::debug!(target: "rpc::eth", hash = %pool_transaction.hash(), "forwarding raw transaction to sequencer");
44            let hash = client.forward_raw_transaction(&tx).await.inspect_err(|err| {
45                    tracing::debug!(target: "rpc::eth", %err, hash=% *pool_transaction.hash(), "failed to forward raw transaction");
46                })?;
47
48            // Retain tx in local tx pool after forwarding, for local RPC usage.
49            let _ = self.inner.eth_api.add_pool_transaction(pool_transaction).await.inspect_err(|err| {
50                tracing::warn!(target: "rpc::eth", %err, %hash, "successfully sent tx to sequencer, but failed to persist in local tx pool");
51            });
52
53            return Ok(hash)
54        }
55
56        // submit the transaction to the pool with a `Local` origin
57        let AddedTransactionOutcome { hash, .. } = self
58            .pool()
59            .add_transaction(TransactionOrigin::Local, pool_transaction)
60            .await
61            .map_err(Self::Error::from_eth_err)?;
62
63        Ok(hash)
64    }
65}
66
67impl<N, Rpc> LoadTransaction for OpEthApi<N, Rpc>
68where
69    N: RpcNodeCore,
70    Rpc: RpcConvert<Primitives = N::Primitives, Error = OpEthApiError>,
71{
72}
73
74impl<N, Rpc> OpEthApi<N, Rpc>
75where
76    N: RpcNodeCore,
77    Rpc: RpcConvert<Primitives = N::Primitives>,
78{
79    /// Returns the [`SequencerClient`] if one is set.
80    pub fn raw_tx_forwarder(&self) -> Option<SequencerClient> {
81        self.inner.sequencer_client.clone()
82    }
83}
84
85/// Optimism implementation of [`TxInfoMapper`].
86///
87/// For deposits, receipt is fetched to extract `deposit_nonce` and `deposit_receipt_version`.
88/// Otherwise, it works like regular Ethereum implementation, i.e. uses [`TransactionInfo`].
89pub struct OpTxInfoMapper<Provider> {
90    provider: Provider,
91}
92
93impl<Provider: Clone> Clone for OpTxInfoMapper<Provider> {
94    fn clone(&self) -> Self {
95        Self { provider: self.provider.clone() }
96    }
97}
98
99impl<Provider> Debug for OpTxInfoMapper<Provider> {
100    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101        f.debug_struct("OpTxInfoMapper").finish()
102    }
103}
104
105impl<Provider> OpTxInfoMapper<Provider> {
106    /// Creates [`OpTxInfoMapper`] that uses [`ReceiptProvider`] borrowed from given `eth_api`.
107    pub const fn new(provider: Provider) -> Self {
108        Self { provider }
109    }
110}
111
112impl<T, Provider> TxInfoMapper<T> for OpTxInfoMapper<Provider>
113where
114    T: OpTransaction + SignedTransaction,
115    Provider: ReceiptProvider<Receipt: DepositReceipt>,
116{
117    type Out = OpTransactionInfo;
118    type Err = ProviderError;
119
120    fn try_map(&self, tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, ProviderError> {
121        try_into_op_tx_info(&self.provider, tx, tx_info)
122    }
123}