Skip to content
Merged
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 153 additions & 19 deletions crates/rpc/rpc-convert/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,78 @@ impl TryIntoSimTx<EthereumTxEnvelope<TxEip4844>> for TransactionRequest {
}
}

/// Converts `TxReq` into `TxEnv`.
///
/// Where:
/// * `TxReq` is a transaction request received from an RPC API
/// * `TxEnv` is the corresponding transaction environment for execution
///
/// The `TxEnvConverter` has two blanket implementations:
/// * `()` assuming `TxReq` implements [`TryIntoTxEnv`] and is used as default for [`RpcConverter`].
/// * `Fn(TxReq, &CfgEnv<()>, &BlockEnv) -> Result<TxEnv, E>` and can be applied using
/// [`RpcConverter::with_tx_env_converter`].
///
/// One should prefer to implement [`TryIntoTxEnv`] for `TxReq` to get the `TxEnvConverter`
/// implementation for free, thanks to the blanket implementation, unless the conversion requires
/// more context. For example, some configuration parameters or access handles to database, network,
/// etc.
pub trait TxEnvConverter<TxReq, TxEnv>: Debug + Send + Sync + Unpin + Clone + 'static {

@klkvr klkvr Aug 26, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a blanket implementation for closures in scope of this PR?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this works because this requires Spec generic,

should we make this an AT as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah right

yeah we can make Spec an AT on RpcConvert, and do TxEnvConverter<TxReq, TxEnv, Spec>. we should know the spec type from the Evm

/// An associated error that can occur during conversion.
type Error;

/// Converts a rpc transaction request into a transaction environment.
///
/// See [`TxEnvConverter`] for more information.
fn convert_tx_env<Spec>(
&self,
tx_req: TxReq,
cfg_env: &CfgEnv<Spec>,
block_env: &BlockEnv,
) -> Result<TxEnv, Self::Error>;
}

impl<TxReq, TxEnv> TxEnvConverter<TxReq, TxEnv> for ()
where
TxReq: TryIntoTxEnv<TxEnv>,
{
type Error = TxReq::Err;

fn convert_tx_env<Spec>(
&self,
tx_req: TxReq,
cfg_env: &CfgEnv<Spec>,
block_env: &BlockEnv,
) -> Result<TxEnv, Self::Error> {
tx_req.try_into_tx_env(cfg_env, block_env)
}
}

/// Converts rpc transaction requests into transaction environment using a closure.
impl<F, TxReq, TxEnv, E> TxEnvConverter<TxReq, TxEnv> for F
where
F: Fn(TxReq, &CfgEnv<()>, &BlockEnv) -> Result<TxEnv, E>
Comment thread
lean-apple marked this conversation as resolved.
Outdated
+ Debug
+ Send
+ Sync
+ Unpin
+ Clone
+ 'static,
TxReq: Clone,
E: std::error::Error + Send + Sync + 'static,
{
type Error = E;

fn convert_tx_env<Spec>(
&self,
tx_req: TxReq,
cfg_env: &CfgEnv<Spec>,
block_env: &BlockEnv,
) -> Result<TxEnv, Self::Error> {
let cfg_env_with_chain_id = CfgEnv::<()>::new_with_spec(()).with_chain_id(cfg_env.chain_id);
self(tx_req, &cfg_env_with_chain_id, block_env)
}
}

/// Converts `self` into `T`.
///
/// Should create an executable transaction environment using [`TransactionRequest`].
Expand Down Expand Up @@ -499,18 +571,29 @@ pub struct TransactionConversionError(String);
/// network and EVM associated primitives:
/// * [`FromConsensusTx`]: from signed transaction into RPC response object.
/// * [`TryIntoSimTx`]: from RPC transaction request into a simulated transaction.
/// * [`TryIntoTxEnv`]: from RPC transaction request into an executable transaction.
/// * [`TryIntoTxEnv`] or [`TxEnvConverter`]: from RPC transaction request into an executable
/// transaction.
/// * [`TxInfoMapper`]: from [`TransactionInfo`] into [`FromConsensusTx::TxInfo`]. Should be
/// implemented for a dedicated struct that is assigned to `Map`. If [`FromConsensusTx::TxInfo`]
/// is [`TransactionInfo`] then `()` can be used as `Map` which trivially passes over the input
/// object.
#[derive(Debug)]
pub struct RpcConverter<Network, Evm, Receipt, Header = (), Map = (), SimTx = (), RpcTx = ()> {
pub struct RpcConverter<
Network,
Evm,
Receipt,
Header = (),
Map = (),
SimTx = (),
RpcTx = (),
TxEnv = (),
> {
network: PhantomData<Network>,
evm: PhantomData<Evm>,
receipt_converter: Receipt,
header_converter: Header,
mapper: Map,
tx_env_converter: TxEnv,
sim_tx_converter: SimTx,
rpc_tx_converter: RpcTx,
}
Expand All @@ -524,24 +607,28 @@ impl<Network, Evm, Receipt> RpcConverter<Network, Evm, Receipt> {
receipt_converter,
header_converter: (),
mapper: (),
tx_env_converter: (),
sim_tx_converter: (),
rpc_tx_converter: (),
}
}
}

impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
{
/// Converts the network type
pub fn with_network<N>(self) -> RpcConverter<N, Evm, Receipt, Header, Map, SimTx, RpcTx> {
pub fn with_network<N>(
self,
) -> RpcConverter<N, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> {
let Self {
receipt_converter,
header_converter,
mapper,
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
..
} = self;
RpcConverter {
Expand All @@ -552,14 +639,43 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}

/// Converts the transaction environment type.
pub fn with_tx_env_converter<TxEnvNew>(
self,
tx_env_converter: TxEnvNew,
) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnvNew> {
let Self {
receipt_converter,
header_converter,
mapper,
network,
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter: _,
..
} = self;
RpcConverter {
receipt_converter,
header_converter,
mapper,
network,
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}

/// Configures the header converter.
pub fn with_header_converter<HeaderNew>(
self,
header_converter: HeaderNew,
) -> RpcConverter<Network, Evm, Receipt, HeaderNew, Map, SimTx, RpcTx> {
) -> RpcConverter<Network, Evm, Receipt, HeaderNew, Map, SimTx, RpcTx, TxEnv> {
let Self {
receipt_converter,
header_converter: _,
Expand All @@ -568,6 +684,7 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
} = self;
RpcConverter {
receipt_converter,
Expand All @@ -577,14 +694,15 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}

/// Configures the mapper.
pub fn with_mapper<MapNew>(
self,
mapper: MapNew,
) -> RpcConverter<Network, Evm, Receipt, Header, MapNew, SimTx, RpcTx> {
) -> RpcConverter<Network, Evm, Receipt, Header, MapNew, SimTx, RpcTx, TxEnv> {
let Self {
receipt_converter,
header_converter,
Expand All @@ -593,6 +711,7 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
} = self;
RpcConverter {
receipt_converter,
Expand All @@ -602,21 +721,23 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}

/// Swaps the simulate transaction converter with `sim_tx_converter`.
pub fn with_sim_tx_converter<SimTxNew>(
self,
sim_tx_converter: SimTxNew,
) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTxNew, RpcTx> {
) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTxNew, RpcTx, TxEnv> {
let Self {
receipt_converter,
header_converter,
mapper,
network,
evm,
rpc_tx_converter,
tx_env_converter,
..
} = self;
RpcConverter {
Expand All @@ -627,21 +748,23 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}

/// Swaps the RPC transaction converter with `rpc_tx_converter`.
pub fn with_rpc_tx_converter<RpcTxNew>(
self,
rpc_tx_converter: RpcTxNew,
) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTxNew> {
) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTxNew, TxEnv> {
let Self {
receipt_converter,
header_converter,
mapper,
network,
evm,
sim_tx_converter,
tx_env_converter,
..
} = self;
RpcConverter {
Expand All @@ -652,18 +775,20 @@ impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
evm,
sim_tx_converter,
rpc_tx_converter,
tx_env_converter,
}
}
}

impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx> Default
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> Default
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
where
Receipt: Default,
Header: Default,
Map: Default,
SimTx: Default,
RpcTx: Default,
TxEnv: Default,
{
fn default() -> Self {
Self {
Expand All @@ -674,12 +799,21 @@ where
mapper: Default::default(),
sim_tx_converter: Default::default(),
rpc_tx_converter: Default::default(),
tx_env_converter: Default::default(),
}
}
}

impl<Network, Evm, Receipt: Clone, Header: Clone, Map: Clone, SimTx: Clone, RpcTx: Clone> Clone
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
impl<
Network,
Evm,
Receipt: Clone,
Header: Clone,
Map: Clone,
SimTx: Clone,
RpcTx: Clone,
TxEnv: Clone,
> Clone for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
{
fn clone(&self) -> Self {
Self {
Expand All @@ -690,23 +824,22 @@ impl<Network, Evm, Receipt: Clone, Header: Clone, Map: Clone, SimTx: Clone, RpcT
mapper: self.mapper.clone(),
sim_tx_converter: self.sim_tx_converter.clone(),
rpc_tx_converter: self.rpc_tx_converter.clone(),
tx_env_converter: self.tx_env_converter.clone(),
}
}
}

impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx> RpcConvert
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx>
impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> RpcConvert
for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
where
N: NodePrimitives,
Network: RpcTypes + Send + Sync + Unpin + Clone + Debug,
Evm: ConfigureEvm<Primitives = N> + 'static,
TxTy<N>: Clone + Debug,
RpcTxReq<Network>: TryIntoTxEnv<TxEnvFor<Evm>>,
Receipt: ReceiptConverter<
N,
RpcReceipt = RpcReceipt<Network>,
Error: From<TransactionConversionError>
+ From<<RpcTxReq<Network> as TryIntoTxEnv<TxEnvFor<Evm>>>::Err>
+ From<TxEnv::Error>
+ From<<Map as TxInfoMapper<TxTy<N>>>::Err>
+ From<RpcTx::Err>
+ Error
Expand All @@ -724,6 +857,7 @@ where
SimTx: SimTxConverter<RpcTxReq<Network>, TxTy<N>>,
RpcTx:
RpcTxConverter<TxTy<N>, Network::TransactionResponse, <Map as TxInfoMapper<TxTy<N>>>::Out>,
TxEnv: TxEnvConverter<RpcTxReq<Network>, TxEnvFor<Evm>>,
{
type Primitives = N;
type Network = Network;
Expand Down Expand Up @@ -757,7 +891,7 @@ where
cfg_env: &CfgEnv<Spec>,
block_env: &BlockEnv,
) -> Result<Self::TxEnv, Self::Error> {
Ok(request.try_into_tx_env(cfg_env, block_env)?)
self.tx_env_converter.convert_tx_env(request, cfg_env, block_env).map_err(Into::into)
}

fn convert_receipts(
Expand Down
Loading