Skip to content

Commit 65f96cf

Browse files
mattsseclaude
andauthored
refactor(anvil): simplify transaction conversion to RpcTransaction (#12391)
Added TypedTransaction::try_into_eth() to convert TypedTransaction to TxEnvelope, returning an error for Deposit transactions that are not part of standard Ethereum. Added MaybeImpersonatedTransaction::into_rpc_transaction() method that encapsulates the conversion logic, making it more explicit and easier to use. The From<MaybeImpersonatedTransaction> for RpcTransaction trait now delegates to this method. Removed the standalone to_alloy_transaction_with_hash_and_sender function since its logic is now part of the into_rpc_transaction method. Updated transaction_build to use the new into_rpc_transaction() method, simplifying the conversion flow and eliminating redundant recovery calls. Co-authored-by: Claude <[email protected]>
1 parent 6caec51 commit 65f96cf

File tree

2 files changed

+60
-86
lines changed
  • crates/anvil

2 files changed

+60
-86
lines changed

crates/anvil/core/src/eth/transaction/mod.rs

Lines changed: 56 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,46 @@ impl MaybeImpersonatedTransaction {
237237
}
238238
self.transaction.hash()
239239
}
240+
241+
/// Converts the transaction into an [`RpcTransaction`]
242+
pub fn into_rpc_transaction(self) -> RpcTransaction {
243+
let hash = self.hash();
244+
let from = self.recover().unwrap_or_default();
245+
let envelope = self.transaction.try_into_eth().expect("cant build deposit transactions");
246+
247+
// NOTE: we must update the hash because the tx can be impersonated, this requires forcing
248+
// the hash
249+
let inner_envelope = match envelope {
250+
TxEnvelope::Legacy(t) => {
251+
let (tx, sig, _) = t.into_parts();
252+
TxEnvelope::Legacy(Signed::new_unchecked(tx, sig, hash))
253+
}
254+
TxEnvelope::Eip2930(t) => {
255+
let (tx, sig, _) = t.into_parts();
256+
TxEnvelope::Eip2930(Signed::new_unchecked(tx, sig, hash))
257+
}
258+
TxEnvelope::Eip1559(t) => {
259+
let (tx, sig, _) = t.into_parts();
260+
TxEnvelope::Eip1559(Signed::new_unchecked(tx, sig, hash))
261+
}
262+
TxEnvelope::Eip4844(t) => {
263+
let (tx, sig, _) = t.into_parts();
264+
TxEnvelope::Eip4844(Signed::new_unchecked(tx, sig, hash))
265+
}
266+
TxEnvelope::Eip7702(t) => {
267+
let (tx, sig, _) = t.into_parts();
268+
TxEnvelope::Eip7702(Signed::new_unchecked(tx, sig, hash))
269+
}
270+
};
271+
272+
RpcTransaction {
273+
block_hash: None,
274+
block_number: None,
275+
transaction_index: None,
276+
effective_gas_price: None,
277+
inner: Recovered::new_unchecked(inner_envelope, from),
278+
}
279+
}
240280
}
241281

242282
impl Encodable for MaybeImpersonatedTransaction {
@@ -279,86 +319,7 @@ impl Deref for MaybeImpersonatedTransaction {
279319

280320
impl From<MaybeImpersonatedTransaction> for RpcTransaction {
281321
fn from(value: MaybeImpersonatedTransaction) -> Self {
282-
let hash = value.hash();
283-
let sender = value.recover().unwrap_or_default();
284-
to_alloy_transaction_with_hash_and_sender(value.transaction, hash, sender)
285-
}
286-
}
287-
288-
pub fn to_alloy_transaction_with_hash_and_sender(
289-
transaction: TypedTransaction,
290-
hash: B256,
291-
from: Address,
292-
) -> RpcTransaction {
293-
match transaction {
294-
TypedTransaction::Legacy(t) => {
295-
let (tx, sig, _) = t.into_parts();
296-
RpcTransaction {
297-
block_hash: None,
298-
block_number: None,
299-
transaction_index: None,
300-
effective_gas_price: None,
301-
inner: Recovered::new_unchecked(
302-
TxEnvelope::Legacy(Signed::new_unchecked(tx, sig, hash)),
303-
from,
304-
),
305-
}
306-
}
307-
TypedTransaction::EIP2930(t) => {
308-
let (tx, sig, _) = t.into_parts();
309-
RpcTransaction {
310-
block_hash: None,
311-
block_number: None,
312-
transaction_index: None,
313-
effective_gas_price: None,
314-
inner: Recovered::new_unchecked(
315-
TxEnvelope::Eip2930(Signed::new_unchecked(tx, sig, hash)),
316-
from,
317-
),
318-
}
319-
}
320-
TypedTransaction::EIP1559(t) => {
321-
let (tx, sig, _) = t.into_parts();
322-
RpcTransaction {
323-
block_hash: None,
324-
block_number: None,
325-
transaction_index: None,
326-
effective_gas_price: None,
327-
inner: Recovered::new_unchecked(
328-
TxEnvelope::Eip1559(Signed::new_unchecked(tx, sig, hash)),
329-
from,
330-
),
331-
}
332-
}
333-
TypedTransaction::EIP4844(t) => {
334-
let (tx, sig, _) = t.into_parts();
335-
RpcTransaction {
336-
block_hash: None,
337-
block_number: None,
338-
transaction_index: None,
339-
effective_gas_price: None,
340-
inner: Recovered::new_unchecked(
341-
TxEnvelope::Eip4844(Signed::new_unchecked(tx, sig, hash)),
342-
from,
343-
),
344-
}
345-
}
346-
TypedTransaction::EIP7702(t) => {
347-
let (tx, sig, _) = t.into_parts();
348-
RpcTransaction {
349-
block_hash: None,
350-
block_number: None,
351-
transaction_index: None,
352-
effective_gas_price: None,
353-
inner: Recovered::new_unchecked(
354-
TxEnvelope::Eip7702(Signed::new_unchecked(tx, sig, hash)),
355-
from,
356-
),
357-
}
358-
}
359-
TypedTransaction::Deposit(_t) => {
360-
unreachable!("cannot reach here, handled in `transaction_build` ")
361-
}
322+
value.into_rpc_transaction()
362323
}
363324
}
364325

@@ -655,6 +616,21 @@ impl TryFrom<AnyRpcTransaction> for TypedTransaction {
655616
}
656617

657618
impl TypedTransaction {
619+
/// Converts the transaction into a [`TxEnvelope`].
620+
///
621+
/// Returns an error if the transaction is a Deposit transaction, which is not part of the
622+
/// standard Ethereum transaction types.
623+
pub fn try_into_eth(self) -> Result<TxEnvelope, Self> {
624+
match self {
625+
Self::Legacy(tx) => Ok(TxEnvelope::Legacy(tx)),
626+
Self::EIP2930(tx) => Ok(TxEnvelope::Eip2930(tx)),
627+
Self::EIP1559(tx) => Ok(TxEnvelope::Eip1559(tx)),
628+
Self::EIP4844(tx) => Ok(TxEnvelope::Eip4844(tx)),
629+
Self::EIP7702(tx) => Ok(TxEnvelope::Eip7702(tx)),
630+
Self::Deposit(_) => Err(self),
631+
}
632+
}
633+
658634
/// Returns true if the transaction uses dynamic fees: EIP1559, EIP4844 or EIP7702
659635
pub fn is_dynamic_fee(&self) -> bool {
660636
matches!(self, Self::EIP1559(_) | Self::EIP4844(_) | Self::EIP7702(_))

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3851,9 +3851,9 @@ pub fn transaction_build(
38513851
}
38523852
}
38533853

3854-
let mut transaction: Transaction = eth_transaction.clone().into();
3854+
let mut transaction = eth_transaction.into_rpc_transaction();
38553855

3856-
let effective_gas_price = if !eth_transaction.is_dynamic_fee() {
3856+
let effective_gas_price = if !transaction.inner.is_dynamic_fee() {
38573857
transaction.effective_gas_price(base_fee)
38583858
} else if block.is_none() && info.is_none() {
38593859
// transaction is not mined yet, gas price is considered just `max_fee_per_gas`
@@ -3870,6 +3870,7 @@ pub fn transaction_build(
38703870
transaction.effective_gas_price = Some(effective_gas_price);
38713871

38723872
let envelope = transaction.inner;
3873+
let from = envelope.signer();
38733874

38743875
// if a specific hash was provided we update the transaction's hash
38753876
// This is important for impersonated transactions since they all use the
@@ -3907,10 +3908,7 @@ pub fn transaction_build(
39073908
};
39083909

39093910
let tx = Transaction {
3910-
inner: Recovered::new_unchecked(
3911-
envelope,
3912-
eth_transaction.recover().expect("can recover signed tx"),
3913-
),
3911+
inner: Recovered::new_unchecked(envelope, from),
39143912
block_hash: block
39153913
.as_ref()
39163914
.map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),

0 commit comments

Comments
 (0)