From d12a9788d9d58d18cb7fc9a6afa533fda9a97d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Mon, 16 Jun 2025 18:23:09 +0200 Subject: [PATCH] feat(rpc): Add `FromConsensusTx` and implement `IntoRpcTx` for generic RPC transaction (#16784) --- .../rpc/rpc-types-compat/src/transaction.rs | 75 +++++++++++++------ 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/crates/rpc/rpc-types-compat/src/transaction.rs b/crates/rpc/rpc-types-compat/src/transaction.rs index 06f50aebf8..3802e102f9 100644 --- a/crates/rpc/rpc-types-compat/src/transaction.rs +++ b/crates/rpc/rpc-types-compat/src/transaction.rs @@ -2,7 +2,7 @@ use crate::fees::{CallFees, CallFeesError}; use alloy_consensus::{ - error::ValueError, transaction::Recovered, EthereumTxEnvelope, Extended, SignableTransaction, + error::ValueError, transaction::Recovered, EthereumTxEnvelope, SignableTransaction, Transaction as ConsensusTransaction, TxEip4844, }; use alloy_network::Network; @@ -82,38 +82,63 @@ pub trait TransactionCompat: Send + Sync + Unpin + Clone + Debug { ) -> Result; } -/// Converts `self` into `T`. +/// Converts `self` into `T`. The opposite of [`FromConsensusTx`]. /// /// Should create an RPC transaction response object based on a consensus transaction, its signer -/// [`Address`] and an additional context. +/// [`Address`] and an additional context [`IntoRpcTx::TxInfo`]. +/// +/// Avoid implementing [`IntoRpcTx`] and use [`FromConsensusTx`] instead. Implementing it +/// automatically provides an implementation of [`IntoRpcTx`] thanks to the blanket implementation +/// in this crate. +/// +/// Prefer using [`IntoRpcTx`] over [`FromConsensusTx`] when specifying trait bounds on a generic +/// function to ensure that types that only implement [`IntoRpcTx`] can be used as well. pub trait IntoRpcTx { /// An additional context, usually [`TransactionInfo`] in a wrapper that carries some /// implementation specific extra information. type TxInfo; - /// Performs the conversion. + /// Performs the conversion consuming `self` with `signer` and `tx_info`. See [`IntoRpcTx`] + /// for details. fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> T; } -impl IntoRpcTx> for Extended -where - BuiltIn: ConsensusTransaction, - Other: ConsensusTransaction, -{ +/// Converts `T` into `self`. It is reciprocal of [`IntoRpcTx`]. +/// +/// Should create an RPC transaction response object based on a consensus transaction, its signer +/// [`Address`] and an additional context [`FromConsensusTx::TxInfo`]. +/// +/// Prefer implementing [`FromConsensusTx`] over [`IntoRpcTx`] because it automatically provides an +/// implementation of [`IntoRpcTx`] thanks to the blanket implementation in this crate. +/// +/// Prefer using [`IntoRpcTx`] over using [`FromConsensusTx`] when specifying trait bounds on a +/// generic function. This way, types that directly implement [`IntoRpcTx`] can be used as arguments +/// as well. +pub trait FromConsensusTx { + /// An additional context, usually [`TransactionInfo`] in a wrapper that carries some + /// implementation specific extra information. + type TxInfo; + + /// Performs the conversion consuming `tx` with `signer` and `tx_info`. See [`FromConsensusTx`] + /// for details. + fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Self; +} + +impl FromConsensusTx for Transaction { type TxInfo = TransactionInfo; - fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> Transaction { + fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Self { let TransactionInfo { block_hash, block_number, index: transaction_index, base_fee, .. } = tx_info; let effective_gas_price = base_fee .map(|base_fee| { - self.effective_tip_per_gas(base_fee).unwrap_or_default() + base_fee as u128 + tx.effective_tip_per_gas(base_fee).unwrap_or_default() + base_fee as u128 }) - .unwrap_or_else(|| self.max_fee_per_gas()); + .unwrap_or_else(|| tx.max_fee_per_gas()); - Transaction { - inner: Recovered::new_unchecked(self, signer), + Self { + inner: Recovered::new_unchecked(tx, signer), block_hash, block_number, transaction_index, @@ -122,6 +147,18 @@ where } } +impl IntoRpcTx for ConsensusTx +where + ConsensusTx: ConsensusTransaction, + RpcTx: FromConsensusTx, +{ + type TxInfo = RpcTx::TxInfo; + + fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> RpcTx { + RpcTx::from_consensus_tx(self, signer, tx_info) + } +} + /// Converts `self` into `T`. /// /// Should create a fake transaction for simulation using [`TransactionRequest`]. @@ -189,15 +226,11 @@ pub fn try_into_op_tx_info>( Ok(OpTransactionInfo::new(tx_info, deposit_meta)) } -impl IntoRpcTx for OpTxEnvelope { +impl FromConsensusTx for op_alloy_rpc_types::Transaction { type TxInfo = OpTransactionInfo; - fn into_rpc_tx( - self, - signer: Address, - tx_info: OpTransactionInfo, - ) -> op_alloy_rpc_types::Transaction { - op_alloy_rpc_types::Transaction::from_transaction(self.with_signer(signer), tx_info) + fn from_consensus_tx(tx: OpTxEnvelope, signer: Address, tx_info: Self::TxInfo) -> Self { + Self::from_transaction(tx.with_signer(signer), tx_info) } }