Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/anvil/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ alloy-consensus = { workspace = true, features = ["k256", "kzg"] }
alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] }
op-alloy-consensus = { workspace = true, features = ["serde"] }
alloy-network.workspace = true
alloy-evm.workspace = true
serde.workspace = true
serde_json.workspace = true
bytes.workspace = true
Expand Down
262 changes: 68 additions & 194 deletions crates/anvil/core/src/eth/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@
use alloy_consensus::{
Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, Transaction, TxEip1559, TxEip2930,
TxEnvelope, TxLegacy, TxReceipt, Typed2718,
constants::{
EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
LEGACY_TX_TYPE_ID,
},
transaction::{
Recovered, TxEip7702,
eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar},
},
};

use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718};
use alloy_evm::FromRecoveredTx;
use alloy_network::{AnyReceiptEnvelope, AnyRpcTransaction, AnyTransactionReceipt, AnyTxEnvelope};
use alloy_primitives::{Address, B256, Bloom, Bytes, Signature, TxHash, TxKind, U64, U256};
use alloy_rlp::{Decodable, Encodable, Header};
Expand Down Expand Up @@ -388,196 +385,6 @@ impl PendingTransaction {
pub fn sender(&self) -> &Address {
&self.sender
}

/// Converts the [PendingTransaction] into the [TxEnv] context that [`revm`](foundry_evm)
/// expects.
///
/// Base [`TxEnv`] is encapsulated in the [`op_revm::OpTransaction`]
pub fn to_revm_tx_env(&self) -> OpTransaction<TxEnv> {
fn transact_to(kind: &TxKind) -> TxKind {
match kind {
TxKind::Call(c) => TxKind::Call(*c),
TxKind::Create => TxKind::Create,
}
}

let caller = *self.sender();
match &self.transaction.transaction {
TypedTransaction::Legacy(tx) => {
let chain_id = tx.tx().chain_id;
let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx();
OpTransaction::new(TxEnv {
caller,
kind: transact_to(to),
data: input.clone(),
chain_id,
nonce: *nonce,
value: (*value),
gas_price: *gas_price,
gas_priority_fee: None,
gas_limit: *gas_limit,
access_list: vec![].into(),
tx_type: LEGACY_TX_TYPE_ID,
..Default::default()
})
}
TypedTransaction::EIP2930(tx) => {
let TxEip2930 {
chain_id,
nonce,
gas_price,
gas_limit,
to,
value,
input,
access_list,
..
} = tx.tx();
OpTransaction::new(TxEnv {
caller,
kind: transact_to(to),
data: input.clone(),
chain_id: Some(*chain_id),
nonce: *nonce,
value: *value,
gas_price: *gas_price,
gas_priority_fee: None,
gas_limit: *gas_limit,
access_list: access_list.clone(),
tx_type: EIP2930_TX_TYPE_ID,
..Default::default()
})
}
TypedTransaction::EIP1559(tx) => {
let TxEip1559 {
chain_id,
nonce,
max_priority_fee_per_gas,
max_fee_per_gas,
gas_limit,
to,
value,
input,
access_list,
..
} = tx.tx();
OpTransaction::new(TxEnv {
caller,
kind: transact_to(to),
data: input.clone(),
chain_id: Some(*chain_id),
nonce: *nonce,
value: *value,
gas_price: *max_fee_per_gas,
gas_priority_fee: Some(*max_priority_fee_per_gas),
gas_limit: *gas_limit,
access_list: access_list.clone(),
tx_type: EIP1559_TX_TYPE_ID,
..Default::default()
})
}
TypedTransaction::EIP4844(tx) => {
let TxEip4844 {
chain_id,
nonce,
max_fee_per_blob_gas,
max_fee_per_gas,
max_priority_fee_per_gas,
gas_limit,
to,
value,
input,
access_list,
blob_versioned_hashes,
..
} = tx.tx().tx();
OpTransaction::new(TxEnv {
caller,
kind: TxKind::Call(*to),
data: input.clone(),
chain_id: Some(*chain_id),
nonce: *nonce,
value: *value,
gas_price: *max_fee_per_gas,
gas_priority_fee: Some(*max_priority_fee_per_gas),
max_fee_per_blob_gas: *max_fee_per_blob_gas,
blob_hashes: blob_versioned_hashes.clone(),
gas_limit: *gas_limit,
access_list: access_list.clone(),
tx_type: EIP4844_TX_TYPE_ID,
..Default::default()
})
}
TypedTransaction::EIP7702(tx) => {
let TxEip7702 {
chain_id,
nonce,
gas_limit,
max_fee_per_gas,
max_priority_fee_per_gas,
to,
value,
access_list,
authorization_list,
input,
} = tx.tx();

let mut tx = TxEnv {
caller,
kind: TxKind::Call(*to),
data: input.clone(),
chain_id: Some(*chain_id),
nonce: *nonce,
value: *value,
gas_price: *max_fee_per_gas,
gas_priority_fee: Some(*max_priority_fee_per_gas),
gas_limit: *gas_limit,
access_list: access_list.clone(),
tx_type: EIP7702_TX_TYPE_ID,
..Default::default()
};
tx.set_signed_authorization(authorization_list.clone());

OpTransaction::new(tx)
}
TypedTransaction::Deposit(tx) => {
let chain_id = tx.chain_id();
let TxDeposit {
source_hash,
to,
mint,
value,
gas_limit,
is_system_transaction,
input,
..
} = tx;

let base = TxEnv {
caller,
kind: transact_to(to),
data: input.clone(),
chain_id,
nonce: 0,
value: *value,
gas_price: 0,
gas_priority_fee: None,
gas_limit: { *gas_limit },
access_list: vec![].into(),
tx_type: DEPOSIT_TX_TYPE_ID,
..Default::default()
};

let deposit = DepositTransactionParts {
source_hash: *source_hash,
mint: Some(*mint),
is_system_transaction: *is_system_transaction,
};

OpTransaction { base, deposit, enveloped_tx: None }
}
}
}
}

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

impl FromRecoveredTx<TypedTransaction> for TxEnv {
fn from_recovered_tx(tx: &TypedTransaction, caller: Address) -> Self {
match tx {
TypedTransaction::Legacy(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
TypedTransaction::EIP2930(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
TypedTransaction::EIP1559(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
TypedTransaction::EIP4844(signed_tx) => {
Self::from_recovered_tx(signed_tx.tx().tx(), caller)
}
TypedTransaction::EIP7702(signed_tx) => Self::from_recovered_tx(signed_tx.tx(), caller),
TypedTransaction::Deposit(tx) => Self::from_recovered_tx(tx, caller),
}
}
}

impl FromRecoveredTx<TypedTransaction> for OpTransaction<TxEnv> {
fn from_recovered_tx(tx: &TypedTransaction, caller: Address) -> Self {
let base = TxEnv::from_recovered_tx(tx, caller);

let deposit = if let TypedTransaction::Deposit(deposit_tx) = tx {
DepositTransactionParts {
source_hash: deposit_tx.source_hash,
mint: Some(deposit_tx.mint),
is_system_transaction: deposit_tx.is_system_transaction,
}
} else {
Default::default()
};

Self { base, deposit, enveloped_tx: None }
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1785,4 +1625,38 @@ mod tests {

let _typed_tx: TypedTransaction = serde_json::from_str(tx).unwrap();
}

#[test]
fn test_from_recovered_tx_legacy() {
let tx = r#"
{
"Legacy": {
"chainId": "0x1",
"nonce": "0x0",
"gas": "0x5208",
"gasPrice": "0x1",
"to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"value": "0x1",
"input": "0x",
"r": "0x85c2794a580da137e24ccc823b45ae5cea99371ae23ee13860fcc6935f8305b0",
"s": "0x41de7fa4121dab284af4453d30928241208bafa90cdb701fe9bc7054759fe3cd",
"v": "0x1b",
"hash": "0x8c9b68e8947ace33028dba167354fde369ed7bbe34911b772d09b3c64b861515"
}
}"#;

let typed_tx: TypedTransaction = serde_json::from_str(tx).unwrap();
let sender = typed_tx.recover().unwrap();

// Test TxEnv conversion via FromRecoveredTx trait
let tx_env = TxEnv::from_recovered_tx(&typed_tx, sender);
assert_eq!(tx_env.caller, sender);
assert_eq!(tx_env.gas_limit, 0x5208);
assert_eq!(tx_env.gas_price, 1);

// Test OpTransaction<TxEnv> conversion via FromRecoveredTx trait
let op_tx = OpTransaction::<TxEnv>::from_recovered_tx(&typed_tx, sender);
assert_eq!(op_tx.base.caller, sender);
assert_eq!(op_tx.base.gas_limit, 0x5208);
}
}
11 changes: 7 additions & 4 deletions crates/anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use alloy_consensus::{
};
use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams};
use alloy_evm::{
EthEvm, Evm,
EthEvm, Evm, FromRecoveredTx,
eth::EthEvmContext,
precompiles::{DynPrecompile, Precompile, PrecompilesMap},
};
Expand All @@ -34,10 +34,12 @@ use foundry_evm::{
traces::{CallTraceDecoder, CallTraceNode},
};
use foundry_evm_networks::NetworkConfigs;
use op_revm::{L1BlockInfo, OpContext, precompiles::OpPrecompiles};
use op_revm::{L1BlockInfo, OpContext, OpTransaction, precompiles::OpPrecompiles};
use revm::{
Database, DatabaseRef, Inspector, Journal,
context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext},
context::{
Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv,
},
context_interface::result::{EVMError, ExecutionResult, Output},
database::WrapDatabaseRef,
handler::{EthPrecompiles, instructions::EthInstructions},
Expand Down Expand Up @@ -269,7 +271,8 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
}

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

if self.networks.is_optimism() {
tx_env.enveloped_tx = Some(alloy_rlp::encode(&tx.transaction.transaction).into());
Expand Down
12 changes: 9 additions & 3 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use alloy_eips::{
eip7910::SystemContract,
};
use alloy_evm::{
Database, Evm,
Database, Evm, FromRecoveredTx,
eth::EthEvmContext,
overrides::{OverrideBlockHashes, apply_state_overrides},
precompiles::{DynPrecompile, Precompile, PrecompilesMap},
Expand Down Expand Up @@ -1205,7 +1205,10 @@ impl Backend {
BlockchainError,
> {
let mut env = self.next_env();
env.tx = tx.pending_transaction.to_revm_tx_env();
env.tx = FromRecoveredTx::from_recovered_tx(
&tx.pending_transaction.transaction.transaction,
*tx.pending_transaction.sender(),
);

if env.networks.is_optimism() {
env.tx.enveloped_tx =
Expand Down Expand Up @@ -2768,7 +2771,10 @@ impl Backend {

let target_tx = block.transactions[index].clone();
let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
let mut tx_env = target_tx.to_revm_tx_env();
let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
&target_tx.transaction.transaction,
*target_tx.sender(),
);
if env.networks.is_optimism() {
tx_env.enveloped_tx = Some(target_tx.transaction.transaction.encoded_2718().into());
}
Expand Down
Loading