Skip to content

Commit 7163da0

Browse files
mablrmattsse
andauthored
feat(anvil): Implement alloy_evm FromRecoveredTx trait for anvil TypedTransaction (#12424)
* feat(anvil): Implement alloy_evm `FromRecoveredTx` trait for anvil `TypedTransaction` * refactor(anvil): use`from_recovered_tx` for `TypedTransaction` tx types except deposit * refactor(anvil): use `from_recovered_tx` for deposit tx type --------- Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
1 parent b9c9731 commit 7163da0

File tree

5 files changed

+86
-201
lines changed

5 files changed

+86
-201
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/anvil/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ alloy-consensus = { workspace = true, features = ["k256", "kzg"] }
3333
alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] }
3434
op-alloy-consensus = { workspace = true, features = ["serde"] }
3535
alloy-network.workspace = true
36+
alloy-evm.workspace = true
3637
serde.workspace = true
3738
serde_json.workspace = true
3839
bytes.workspace = true

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

Lines changed: 68 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22
use alloy_consensus::{
33
Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, Transaction, TxEip1559, TxEip2930,
44
TxEnvelope, TxLegacy, TxReceipt, Typed2718,
5-
constants::{
6-
EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
7-
LEGACY_TX_TYPE_ID,
8-
},
95
transaction::{
106
Recovered, TxEip7702,
117
eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar},
128
},
139
};
1410

1511
use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718};
12+
use alloy_evm::FromRecoveredTx;
1613
use alloy_network::{AnyReceiptEnvelope, AnyRpcTransaction, AnyTransactionReceipt, AnyTxEnvelope};
1714
use alloy_primitives::{Address, B256, Bloom, Bytes, Signature, TxHash, TxKind, U64, U256};
1815
use alloy_rlp::{Decodable, Encodable, Header};
@@ -388,196 +385,6 @@ impl PendingTransaction {
388385
pub fn sender(&self) -> &Address {
389386
&self.sender
390387
}
391-
392-
/// Converts the [PendingTransaction] into the [TxEnv] context that [`revm`](foundry_evm)
393-
/// expects.
394-
///
395-
/// Base [`TxEnv`] is encapsulated in the [`op_revm::OpTransaction`]
396-
pub fn to_revm_tx_env(&self) -> OpTransaction<TxEnv> {
397-
fn transact_to(kind: &TxKind) -> TxKind {
398-
match kind {
399-
TxKind::Call(c) => TxKind::Call(*c),
400-
TxKind::Create => TxKind::Create,
401-
}
402-
}
403-
404-
let caller = *self.sender();
405-
match &self.transaction.transaction {
406-
TypedTransaction::Legacy(tx) => {
407-
let chain_id = tx.tx().chain_id;
408-
let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx();
409-
OpTransaction::new(TxEnv {
410-
caller,
411-
kind: transact_to(to),
412-
data: input.clone(),
413-
chain_id,
414-
nonce: *nonce,
415-
value: (*value),
416-
gas_price: *gas_price,
417-
gas_priority_fee: None,
418-
gas_limit: *gas_limit,
419-
access_list: vec![].into(),
420-
tx_type: LEGACY_TX_TYPE_ID,
421-
..Default::default()
422-
})
423-
}
424-
TypedTransaction::EIP2930(tx) => {
425-
let TxEip2930 {
426-
chain_id,
427-
nonce,
428-
gas_price,
429-
gas_limit,
430-
to,
431-
value,
432-
input,
433-
access_list,
434-
..
435-
} = tx.tx();
436-
OpTransaction::new(TxEnv {
437-
caller,
438-
kind: transact_to(to),
439-
data: input.clone(),
440-
chain_id: Some(*chain_id),
441-
nonce: *nonce,
442-
value: *value,
443-
gas_price: *gas_price,
444-
gas_priority_fee: None,
445-
gas_limit: *gas_limit,
446-
access_list: access_list.clone(),
447-
tx_type: EIP2930_TX_TYPE_ID,
448-
..Default::default()
449-
})
450-
}
451-
TypedTransaction::EIP1559(tx) => {
452-
let TxEip1559 {
453-
chain_id,
454-
nonce,
455-
max_priority_fee_per_gas,
456-
max_fee_per_gas,
457-
gas_limit,
458-
to,
459-
value,
460-
input,
461-
access_list,
462-
..
463-
} = tx.tx();
464-
OpTransaction::new(TxEnv {
465-
caller,
466-
kind: transact_to(to),
467-
data: input.clone(),
468-
chain_id: Some(*chain_id),
469-
nonce: *nonce,
470-
value: *value,
471-
gas_price: *max_fee_per_gas,
472-
gas_priority_fee: Some(*max_priority_fee_per_gas),
473-
gas_limit: *gas_limit,
474-
access_list: access_list.clone(),
475-
tx_type: EIP1559_TX_TYPE_ID,
476-
..Default::default()
477-
})
478-
}
479-
TypedTransaction::EIP4844(tx) => {
480-
let TxEip4844 {
481-
chain_id,
482-
nonce,
483-
max_fee_per_blob_gas,
484-
max_fee_per_gas,
485-
max_priority_fee_per_gas,
486-
gas_limit,
487-
to,
488-
value,
489-
input,
490-
access_list,
491-
blob_versioned_hashes,
492-
..
493-
} = tx.tx().tx();
494-
OpTransaction::new(TxEnv {
495-
caller,
496-
kind: TxKind::Call(*to),
497-
data: input.clone(),
498-
chain_id: Some(*chain_id),
499-
nonce: *nonce,
500-
value: *value,
501-
gas_price: *max_fee_per_gas,
502-
gas_priority_fee: Some(*max_priority_fee_per_gas),
503-
max_fee_per_blob_gas: *max_fee_per_blob_gas,
504-
blob_hashes: blob_versioned_hashes.clone(),
505-
gas_limit: *gas_limit,
506-
access_list: access_list.clone(),
507-
tx_type: EIP4844_TX_TYPE_ID,
508-
..Default::default()
509-
})
510-
}
511-
TypedTransaction::EIP7702(tx) => {
512-
let TxEip7702 {
513-
chain_id,
514-
nonce,
515-
gas_limit,
516-
max_fee_per_gas,
517-
max_priority_fee_per_gas,
518-
to,
519-
value,
520-
access_list,
521-
authorization_list,
522-
input,
523-
} = tx.tx();
524-
525-
let mut tx = TxEnv {
526-
caller,
527-
kind: TxKind::Call(*to),
528-
data: input.clone(),
529-
chain_id: Some(*chain_id),
530-
nonce: *nonce,
531-
value: *value,
532-
gas_price: *max_fee_per_gas,
533-
gas_priority_fee: Some(*max_priority_fee_per_gas),
534-
gas_limit: *gas_limit,
535-
access_list: access_list.clone(),
536-
tx_type: EIP7702_TX_TYPE_ID,
537-
..Default::default()
538-
};
539-
tx.set_signed_authorization(authorization_list.clone());
540-
541-
OpTransaction::new(tx)
542-
}
543-
TypedTransaction::Deposit(tx) => {
544-
let chain_id = tx.chain_id();
545-
let TxDeposit {
546-
source_hash,
547-
to,
548-
mint,
549-
value,
550-
gas_limit,
551-
is_system_transaction,
552-
input,
553-
..
554-
} = tx;
555-
556-
let base = TxEnv {
557-
caller,
558-
kind: transact_to(to),
559-
data: input.clone(),
560-
chain_id,
561-
nonce: 0,
562-
value: *value,
563-
gas_price: 0,
564-
gas_priority_fee: None,
565-
gas_limit: { *gas_limit },
566-
access_list: vec![].into(),
567-
tx_type: DEPOSIT_TX_TYPE_ID,
568-
..Default::default()
569-
};
570-
571-
let deposit = DepositTransactionParts {
572-
source_hash: *source_hash,
573-
mint: Some(*mint),
574-
is_system_transaction: *is_system_transaction,
575-
};
576-
577-
OpTransaction { base, deposit, enveloped_tx: None }
578-
}
579-
}
580-
}
581388
}
582389

583390
/// Container type for signed, typed transactions.
@@ -1539,6 +1346,39 @@ pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option<Receip
15391346
})
15401347
}
15411348

1349+
impl FromRecoveredTx<TypedTransaction> for TxEnv {
1350+
fn from_recovered_tx(tx: &TypedTransaction, caller: Address) -> Self {
1351+
match tx {
1352+
TypedTransaction::Legacy(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
1353+
TypedTransaction::EIP2930(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
1354+
TypedTransaction::EIP1559(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
1355+
TypedTransaction::EIP4844(signed_tx) => {
1356+
Self::from_recovered_tx(signed_tx.tx().tx(), caller)
1357+
}
1358+
TypedTransaction::EIP7702(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
1359+
TypedTransaction::Deposit(tx) => Self::from_recovered_tx(tx, caller),
1360+
}
1361+
}
1362+
}
1363+
1364+
impl FromRecoveredTx<TypedTransaction> for OpTransaction<TxEnv> {
1365+
fn from_recovered_tx(tx: &TypedTransaction, caller: Address) -> Self {
1366+
let base = TxEnv::from_recovered_tx(tx, caller);
1367+
1368+
let deposit = if let TypedTransaction::Deposit(deposit_tx) = tx {
1369+
DepositTransactionParts {
1370+
source_hash: deposit_tx.source_hash,
1371+
mint: Some(deposit_tx.mint),
1372+
is_system_transaction: deposit_tx.is_system_transaction,
1373+
}
1374+
} else {
1375+
Default::default()
1376+
};
1377+
1378+
Self { base, deposit, enveloped_tx: None }
1379+
}
1380+
}
1381+
15421382
#[cfg(test)]
15431383
mod tests {
15441384
use super::*;
@@ -1785,4 +1625,38 @@ mod tests {
17851625

17861626
let _typed_tx: TypedTransaction = serde_json::from_str(tx).unwrap();
17871627
}
1628+
1629+
#[test]
1630+
fn test_from_recovered_tx_legacy() {
1631+
let tx = r#"
1632+
{
1633+
"Legacy": {
1634+
"chainId": "0x1",
1635+
"nonce": "0x0",
1636+
"gas": "0x5208",
1637+
"gasPrice": "0x1",
1638+
"to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
1639+
"value": "0x1",
1640+
"input": "0x",
1641+
"r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
1642+
"s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
1643+
"v": "0x1b",
1644+
"hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
1645+
}
1646+
}"#;
1647+
1648+
let typed_tx: TypedTransaction = serde_json::from_str(tx).unwrap();
1649+
let sender = typed_tx.recover().unwrap();
1650+
1651+
// Test TxEnv conversion via FromRecoveredTx trait
1652+
let tx_env = TxEnv::from_recovered_tx(&typed_tx, sender);
1653+
assert_eq!(tx_env.caller, sender);
1654+
assert_eq!(tx_env.gas_limit, 0x5208);
1655+
assert_eq!(tx_env.gas_price, 1);
1656+
1657+
// Test OpTransaction<TxEnv> conversion via FromRecoveredTx trait
1658+
let op_tx = OpTransaction::<TxEnv>::from_recovered_tx(&typed_tx, sender);
1659+
assert_eq!(op_tx.base.caller, sender);
1660+
assert_eq!(op_tx.base.gas_limit, 0x5208);
1661+
}
17881662
}

crates/anvil/src/eth/backend/executor.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use alloy_consensus::{
1818
};
1919
use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams};
2020
use alloy_evm::{
21-
EthEvm, Evm,
21+
EthEvm, Evm, FromRecoveredTx,
2222
eth::EthEvmContext,
2323
precompiles::{DynPrecompile, Precompile, PrecompilesMap},
2424
};
@@ -34,10 +34,12 @@ use foundry_evm::{
3434
traces::{CallTraceDecoder, CallTraceNode},
3535
};
3636
use foundry_evm_networks::NetworkConfigs;
37-
use op_revm::{L1BlockInfo, OpContext, precompiles::OpPrecompiles};
37+
use op_revm::{L1BlockInfo, OpContext, OpTransaction, precompiles::OpPrecompiles};
3838
use revm::{
3939
Database, DatabaseRef, Inspector, Journal,
40-
context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext},
40+
context::{
41+
Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv,
42+
},
4143
context_interface::result::{EVMError, ExecutionResult, Output},
4244
database::WrapDatabaseRef,
4345
handler::{EthPrecompiles, instructions::EthInstructions},
@@ -269,7 +271,8 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
269271
}
270272

271273
fn env_for(&self, tx: &PendingTransaction) -> Env {
272-
let mut tx_env = tx.to_revm_tx_env();
274+
let mut tx_env: OpTransaction<TxEnv> =
275+
FromRecoveredTx::from_recovered_tx(&tx.transaction.transaction, *tx.sender());
273276

274277
if self.networks.is_optimism() {
275278
tx_env.enveloped_tx = Some(alloy_rlp::encode(&tx.transaction.transaction).into());

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use alloy_eips::{
4848
eip7910::SystemContract,
4949
};
5050
use alloy_evm::{
51-
Database, Evm,
51+
Database, Evm, FromRecoveredTx,
5252
eth::EthEvmContext,
5353
overrides::{OverrideBlockHashes, apply_state_overrides},
5454
precompiles::{DynPrecompile, Precompile, PrecompilesMap},
@@ -1205,7 +1205,10 @@ impl Backend {
12051205
BlockchainError,
12061206
> {
12071207
let mut env = self.next_env();
1208-
env.tx = tx.pending_transaction.to_revm_tx_env();
1208+
env.tx = FromRecoveredTx::from_recovered_tx(
1209+
&tx.pending_transaction.transaction.transaction,
1210+
*tx.pending_transaction.sender(),
1211+
);
12091212

12101213
if env.networks.is_optimism() {
12111214
env.tx.enveloped_tx =
@@ -2768,7 +2771,10 @@ impl Backend {
27682771

27692772
let target_tx = block.transactions[index].clone();
27702773
let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
2771-
let mut tx_env = target_tx.to_revm_tx_env();
2774+
let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
2775+
&target_tx.transaction.transaction,
2776+
*target_tx.sender(),
2777+
);
27722778
if env.networks.is_optimism() {
27732779
tx_env.enveloped_tx = Some(target_tx.transaction.transaction.encoded_2718().into());
27742780
}

0 commit comments

Comments
 (0)