feat(rpc): Add FromConsensusTx and implement IntoRpcTx for generic RPC transaction (#16784)

This commit is contained in:
Roman Hodulák
2025-06-16 18:23:09 +02:00
committed by GitHub
parent a1a1c0c6bc
commit d12a9788d9

View File

@@ -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<Self::TxEnv, Self::Error>;
}
/// 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<T> {
/// 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<BuiltIn, Other> IntoRpcTx<Transaction<Self>> for Extended<BuiltIn, Other>
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<T> {
/// 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<T: ConsensusTransaction> FromConsensusTx<T> for Transaction<T> {
type TxInfo = TransactionInfo;
fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> Transaction<Self> {
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<ConsensusTx, RpcTx> IntoRpcTx<RpcTx> for ConsensusTx
where
ConsensusTx: ConsensusTransaction,
RpcTx: FromConsensusTx<Self>,
{
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<T: ReceiptProvider<Receipt: DepositReceipt>>(
Ok(OpTransactionInfo::new(tx_info, deposit_meta))
}
impl IntoRpcTx<op_alloy_rpc_types::Transaction> for OpTxEnvelope {
impl FromConsensusTx<OpTxEnvelope> 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)
}
}