From 4a1ad3658d35810a375ef0e58c45a4f67822076d Mon Sep 17 00:00:00 2001 From: Enrique Date: Thu, 25 Jan 2024 15:59:56 -0400 Subject: [PATCH] feat(`anvil`): Core types migration (#6808) * here we go again * wip: storage * chore: migrate executor, fmt * wip * chore: roughly only signers left * feat: migrate proof, bar trie stuff * chore: onto tests * chore: passing most tests * chore: fix impersonate * chore: op tests passing * txenvelope * chore: some fixes, typed data * feat(`anvil`): remove old ethers-dependent anvil core types (#6842) * feat: remove most ethers and old anvil core types * chore: replace handles for providers constructed on actual tests * finish moving test providers * chore: switch to decode_revert * chore: replace with maybe_decode_revert * u256::decode * chore: move all of anvil but tests and block subscriptions off ethers * re-enable opt * solve nits * chore: remove more println * chore: rename to gen * chore: update alloy, cleanup * chore: fix tests * chore: rename to sign * chore: fmt * chore: cleanup * docs * chore: more cleanup * clippy/fmt * chore: remove ethers from anvil-core, rm fastrlp * chore: remove fastrlp from exceptions in deny.toml * chore: rename and cleanup * directly use type to decode * address review comments * feat: onbjerg nits * chore: fix deny check * bump alloy * chore: add to_ethers ext trait for wallets * chore: update deps, revert changed typed-data tests, set chain id as none when signing typed data --------- Co-authored-by: Oliver Nordbjerg --- Cargo.lock | 85 +- Cargo.toml | 8 +- crates/anvil/Cargo.toml | 8 +- crates/anvil/core/Cargo.toml | 4 +- crates/anvil/core/src/eth/alloy_block.rs | 271 -- crates/anvil/core/src/eth/alloy_proof.rs | 25 - crates/anvil/core/src/eth/block.rs | 530 ++-- crates/anvil/core/src/eth/mod.rs | 13 +- crates/anvil/core/src/eth/proof.rs | 49 +- crates/anvil/core/src/eth/receipt.rs | 376 --- crates/anvil/core/src/eth/serde_helpers.rs | 29 + .../anvil/core/src/eth/transaction/alloy.rs | 1071 -------- .../core/src/eth/transaction/ethers_compat.rs | 501 ---- crates/anvil/core/src/eth/transaction/mod.rs | 2324 +++++++---------- .../core/src/eth/transaction/optimism.rs | 197 +- crates/anvil/core/src/eth/trie.rs | 16 +- crates/anvil/core/src/eth/utils.rs | 59 +- crates/anvil/core/src/types.rs | 6 +- crates/anvil/src/cmd.rs | 12 +- crates/anvil/src/config.rs | 46 +- crates/anvil/src/eth/api.rs | 218 +- crates/anvil/src/eth/backend/cheats.rs | 7 +- crates/anvil/src/eth/backend/executor.rs | 109 +- crates/anvil/src/eth/backend/info.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 279 +- crates/anvil/src/eth/backend/mem/state.rs | 55 +- crates/anvil/src/eth/backend/mem/storage.rs | 27 +- crates/anvil/src/eth/backend/notifications.rs | 2 +- crates/anvil/src/eth/error.rs | 41 +- crates/anvil/src/eth/fees.rs | 25 +- crates/anvil/src/eth/mod.rs | 2 +- crates/anvil/src/eth/pool/mod.rs | 3 +- crates/anvil/src/eth/pool/transactions.rs | 11 +- crates/anvil/src/eth/sign.rs | 145 +- crates/anvil/src/eth/util.rs | 10 +- crates/anvil/src/genesis.rs | 29 +- crates/anvil/src/hardfork.rs | 12 +- crates/anvil/src/lib.rs | 40 +- crates/anvil/src/pubsub.rs | 37 +- crates/anvil/tests/it/anvil.rs | 17 +- crates/anvil/tests/it/anvil_api.rs | 42 +- crates/anvil/tests/it/api.rs | 48 +- crates/anvil/tests/it/fork.rs | 186 +- crates/anvil/tests/it/gas.rs | 11 +- crates/anvil/tests/it/ipc.rs | 6 +- crates/anvil/tests/it/logs.rs | 25 +- crates/anvil/tests/it/optimism.rs | 7 +- crates/anvil/tests/it/otterscan.rs | 90 +- crates/anvil/tests/it/proof/mod.rs | 44 +- crates/anvil/tests/it/pubsub.rs | 29 +- crates/anvil/tests/it/sign.rs | 16 +- crates/anvil/tests/it/traces.rs | 43 +- crates/anvil/tests/it/transaction.rs | 169 +- crates/anvil/tests/it/txpool.rs | 3 +- crates/anvil/tests/it/utils.rs | 16 + crates/anvil/tests/it/wsapi.rs | 9 +- crates/common/Cargo.toml | 2 + crates/common/src/types.rs | 21 + deny.toml | 3 - 59 files changed, 2328 insertions(+), 5143 deletions(-) delete mode 100644 crates/anvil/core/src/eth/alloy_block.rs delete mode 100644 crates/anvil/core/src/eth/alloy_proof.rs delete mode 100644 crates/anvil/core/src/eth/receipt.rs delete mode 100644 crates/anvil/core/src/eth/transaction/alloy.rs delete mode 100644 crates/anvil/core/src/eth/transaction/ethers_compat.rs diff --git a/Cargo.lock b/Cargo.lock index f54e2b9cd317..125d824ffc16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-eips", "alloy-network", @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf1d316f6375d7cff11b526e91938b199e021da118861f9d238438938e0ac7d" +checksum = "c7265ac54c88a78604cea8444addfa9dfdad08d3098f153484cb4ee66fc202cc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b07fb0de8c985deebc60b97fc8c45fd45c101822c3241c1f389e9f9a557dd7" +checksum = "a7c5aecfe87e06da0e760840974c6e3cc19d4247be17a3172825fbbe759c8e60" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-primitives", "serde", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3729132072f369bc4e8e6e070f9cf4deb3490fc9b9eea6f71f75ec19406df811" +checksum = "f4b6fb2b432ff223d513db7f908937f63c252bee0af9b82bfd25b0a5dd1eb0d8" dependencies = [ "alloy-rlp", "arbitrary", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "alloy-providers" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-network", "alloy-primitives", @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -279,7 +279,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -290,7 +290,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -300,11 +300,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-sol-types", + "async-trait", + "auto_impl", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "k256", + "rand 0.8.5", + "thiserror", +] + [[package]] name = "alloy-sol-macro" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5531f0a16e36c547e68c73a1638bea1f26237ee8ae785527190c4e4f9fecd2c5" +checksum = "8b0b5ab0cb07c21adf9d72e988b34e8200ce648c2bba8d009183bb1c50fb1216" dependencies = [ "alloy-json-abi", "const-hex", @@ -322,18 +340,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6958f72e4aa4acc979ea70bca59204336c0686e92b26d02d48244cf25b0dabb0" +checksum = "4dd124ec0a456ec5e9dcca5b6e8b011bc723cc410d4d9a66bf032770feaeef4b" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783eb720b73d38f9d4c1fb9890e4db6bc8c708f7aa77d3071a19e06091ecd1c9" +checksum = "6c08f62ded7ce03513bfb60ef5cad4fff5d4f67eac6feb4df80426b7b9ffb06e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -345,7 +363,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-json-rpc", "base64 0.21.7", @@ -361,7 +379,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -374,7 +392,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -392,7 +410,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#d68e1103bda0a0b95e1928c56091dc48e64c95de" +source = "git+https://github.com/alloy-rs/alloy#5fa63d7259351c0c5222095006a912a610e4e1e8" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -492,10 +510,16 @@ name = "anvil" version = "0.2.0" dependencies = [ "alloy-chains", + "alloy-consensus", + "alloy-dyn-abi", + "alloy-network", "alloy-primitives", "alloy-providers", + "alloy-rlp", "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-signer", + "alloy-sol-types", "alloy-transport", "anvil-core", "anvil-rpc", @@ -548,6 +572,7 @@ name = "anvil-core" version = "0.2.0" dependencies = [ "alloy-consensus", + "alloy-dyn-abi", "alloy-eips", "alloy-network", "alloy-primitives", @@ -557,7 +582,6 @@ dependencies = [ "anvil-core", "bytes", "ethers-contract", - "ethers-core", "ethers-middleware", "ethers-providers", "foundry-common", @@ -565,7 +589,6 @@ dependencies = [ "hash-db", "hash256-std-hasher", "keccak-hasher", - "open-fastrlp", "rand 0.8.5", "reference-trie", "revm", @@ -3040,6 +3063,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", + "alloy-signer", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3053,6 +3077,7 @@ dependencies = [ "ethers-core", "ethers-middleware", "ethers-providers", + "ethers-signers", "eyre", "foundry-block-explorers", "foundry-compilers", @@ -7226,9 +7251,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cfbd642e1748fd9e47951973abfa78f825b11fbf68af9e6b9db4c983a770166" +checksum = "63bef2e2c735acbc06874eca3a8506f02a3c4700e6e748afc92cc2e4220e8a03" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 40a424679f6d..ab0714e68152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,10 +160,10 @@ alloy-transport = { git = "https://github.com/alloy-rs/alloy" } alloy-transport-http = { git = "https://github.com/alloy-rs/alloy" } alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy" } alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy" } -alloy-primitives = "0.6.0" -alloy-dyn-abi = "0.6.0" -alloy-json-abi = "0.6.0" -alloy-sol-types = "0.6.0" +alloy-primitives = "0.6.2" +alloy-dyn-abi = "0.6.2" +alloy-json-abi = "0.6.2" +alloy-sol-types = "0.6.2" syn-solidity = "0.6.0" alloy-chains = "0.1.5" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 8db3dc725053..15e6ea9921fb 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -20,7 +20,7 @@ vergen = { version = "8", default-features = false, features = ["build", "git", [dependencies] # foundry internal -anvil-core = { path = "core", features = ["fastrlp", "serde", "impersonated-tx"] } +anvil-core = { path = "core", features = ["serde", "impersonated-tx"] } anvil-rpc = { path = "rpc" } anvil-server = { path = "server" } foundry-common.workspace = true @@ -36,6 +36,12 @@ trie-db = "0.23" hash-db = "0.15" memory-db = "0.29" alloy-primitives = { workspace = true, features = ["serde"] } +alloy-consensus.workspace = true +alloy-network.workspace = true +alloy-rlp.workspace = true +alloy-signer = { workspace = true, features = ["eip712", "mnemonic"] } +alloy-sol-types = { workspace = true, features = ["std"] } +alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-rpc-types.workspace = true alloy-rpc-trace-types.workspace = true alloy-providers.workspace = true diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 9fb87785cbca..5919c0d7d417 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -21,7 +21,7 @@ alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } alloy-consensus.workspace = true -ethers-core = { workspace = true, features = ["optimism"] } +alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } # theses are not used by anvil-core, but are required by ethers, because pulled in via foundry-common ethers-contract = { workspace = true, features = ["optimism"] } ethers-providers = { workspace = true, features = ["optimism"] } @@ -30,7 +30,6 @@ ethers-middleware = { workspace = true, features = ["optimism"] } serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" -open-fastrlp = { version = "0.1.4", optional = true } # trie hash-db = { version = "0.15", default-features = false } @@ -48,5 +47,4 @@ anvil-core = { path = ".", features = ["serde"] } [features] default = ["serde"] impersonated-tx = [] -fastrlp = ["dep:open-fastrlp"] serde = ["dep:serde"] diff --git a/crates/anvil/core/src/eth/alloy_block.rs b/crates/anvil/core/src/eth/alloy_block.rs deleted file mode 100644 index 44536f886258..000000000000 --- a/crates/anvil/core/src/eth/alloy_block.rs +++ /dev/null @@ -1,271 +0,0 @@ -use super::trie; -use alloy_consensus::Header; -use alloy_primitives::{Address, Bloom, Bytes, B256, U256}; -use alloy_rlp::{RlpDecodable, RlpEncodable}; -use foundry_common::types::ToAlloy; - -// Type alias to optionally support impersonated transactions -#[cfg(not(feature = "impersonated-tx"))] -type Transaction = crate::eth::transaction::alloy::TypedTransaction; -#[cfg(feature = "impersonated-tx")] -type Transaction = crate::eth::transaction::alloy::MaybeImpersonatedTransaction; - -/// An Ethereum Block -#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)] -pub struct Block { - pub header: Header, - pub transactions: Vec, - pub ommers: Vec
, -} - -impl Block { - /// Creates a new block - /// - /// Note: if the `impersonate-tx` feature is enabled this will also accept - /// [MaybeImpersonatedTransaction] - pub fn new( - partial_header: PartialHeader, - transactions: impl IntoIterator, - ommers: Vec
, - ) -> Self - where - T: Into, - { - let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect(); - let mut encoded_ommers: Vec = Vec::new(); - alloy_rlp::encode_list(&ommers, &mut encoded_ommers); - let ommers_hash = - B256::from_slice(alloy_primitives::utils::keccak256(encoded_ommers).as_slice()); - let transactions_root = - trie::ordered_trie_root(transactions.iter().map(|r| Bytes::from(alloy_rlp::encode(r)))) - .to_alloy(); - - Self { - header: Header { - parent_hash: partial_header.parent_hash, - beneficiary: partial_header.beneficiary, - ommers_hash, - state_root: partial_header.state_root, - transactions_root, - receipts_root: partial_header.receipts_root, - logs_bloom: partial_header.logs_bloom, - difficulty: partial_header.difficulty, - number: partial_header.number, - gas_limit: partial_header.gas_limit, - gas_used: partial_header.gas_used, - timestamp: partial_header.timestamp, - extra_data: partial_header.extra_data, - mix_hash: partial_header.mix_hash, - withdrawals_root: Some(partial_header.mix_hash), - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - nonce: partial_header.nonce, - base_fee_per_gas: partial_header.base_fee, - }, - transactions, - ommers, - } - } -} - -/// Partial header definition without ommers hash and transactions root -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct PartialHeader { - pub parent_hash: B256, - pub beneficiary: Address, - pub state_root: B256, - pub receipts_root: B256, - pub logs_bloom: Bloom, - pub difficulty: U256, - pub number: u64, - pub gas_limit: u64, - pub gas_used: u64, - pub timestamp: u64, - pub extra_data: Bytes, - pub mix_hash: B256, - pub nonce: u64, - pub base_fee: Option, -} - -impl From
for PartialHeader { - fn from(value: Header) -> Self { - Self { - parent_hash: value.parent_hash, - beneficiary: value.beneficiary, - state_root: value.state_root, - receipts_root: value.receipts_root, - logs_bloom: value.logs_bloom, - difficulty: value.difficulty, - number: value.number, - gas_limit: value.gas_limit, - gas_used: value.gas_used, - timestamp: value.timestamp, - extra_data: value.extra_data, - mix_hash: value.mix_hash, - nonce: value.nonce, - base_fee: value.base_fee_per_gas, - } - } -} - -#[cfg(test)] -mod tests { - use alloy_network::Sealable; - use alloy_primitives::{ - b256, - hex::{self, FromHex}, - }; - use alloy_rlp::Decodable; - - use super::*; - use std::str::FromStr; - - #[test] - fn header_rlp_roundtrip() { - let mut header = Header { - parent_hash: Default::default(), - ommers_hash: Default::default(), - beneficiary: Default::default(), - state_root: Default::default(), - transactions_root: Default::default(), - receipts_root: Default::default(), - logs_bloom: Default::default(), - difficulty: Default::default(), - number: 124u64, - gas_limit: Default::default(), - gas_used: 1337u64, - timestamp: 0, - extra_data: Default::default(), - mix_hash: Default::default(), - nonce: 99u64, - withdrawals_root: Default::default(), - blob_gas_used: Default::default(), - excess_blob_gas: Default::default(), - parent_beacon_block_root: Default::default(), - base_fee_per_gas: None, - }; - - let encoded = alloy_rlp::encode(&header); - let decoded: Header =
::decode(&mut encoded.as_ref()).unwrap(); - assert_eq!(header, decoded); - - header.base_fee_per_gas = Some(12345u64); - - let encoded = alloy_rlp::encode(&header); - let decoded: Header =
::decode(&mut encoded.as_ref()).unwrap(); - assert_eq!(header, decoded); - } - - #[test] - fn test_encode_block_header() { - use alloy_rlp::Encodable; - - let expected = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap(); - let mut data = vec![]; - let header = Header { - parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(), - state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), - difficulty: U256::from(2222), - number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, - timestamp: 0x1a0au64, - extra_data: hex::decode("7788").unwrap().into(), - mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - nonce: 0, - base_fee_per_gas: None, - }; - - header.encode(&mut data); - assert_eq!(hex::encode(&data), hex::encode(expected)); - assert_eq!(header.length(), data.len()); - } - - #[test] - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 - fn test_decode_block_header() { - let data = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap(); - let expected = Header { - parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(), - state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), - difficulty: U256::from(2222), - number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, - timestamp: 0x1a0au64, - extra_data: hex::decode("7788").unwrap().into(), - mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, - withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - base_fee_per_gas: None, - }; - let header =
::decode(&mut data.as_slice()).unwrap(); - assert_eq!(header, expected); - } - - #[test] - // Test vector from: https://github.com/ethereum/tests/blob/f47bbef4da376a49c8fc3166f09ab8a6d182f765/BlockchainTests/ValidBlocks/bcEIP1559/baseFee.json#L15-L36 - fn test_eip1559_block_header_hash() { - let expected_hash = - b256!("6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f"); - let header = Header { - parent_hash: B256::from_str("e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a").unwrap(), - ommers_hash: B256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), - beneficiary: Address::from_str("ba5e000000000000000000000000000000000000").unwrap(), - state_root: B256::from_str("ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7").unwrap(), - transactions_root: B256::from_str("50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf").unwrap(), - receipts_root: B256::from_str("29b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9").unwrap(), - logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), - difficulty: U256::from(0x020000), - number: 1u64, - gas_limit: U256::from(0x016345785d8a0000u128).to::(), - gas_used: U256::from(0x015534).to::(), - timestamp: 0x079e, - extra_data: hex::decode("42").unwrap().into(), - mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, - base_fee_per_gas: Some(875), - withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - }; - assert_eq!(header.hash(), expected_hash); - } - - #[test] - // Test vector from network - fn block_network_roundtrip() { - use alloy_rlp::Encodable; - - let data = hex::decode("f9034df90348a0fbdbd8d2d0ac5f14bd5fa90e547fe6f1d15019c724f8e7b60972d381cd5d9cf8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c9577e7945db22e38fc060909f2278c7746b0f9ba05017cfa3b0247e35197215ae8d610265ffebc8edca8ea66d6567eb0adecda867a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018355bb7b871fffffffffffff808462bd0e1ab9014bf90148a00000000000000000000000000000000000000000000000000000000000000000f85494319fa8f1bc4e53410e92d10d918659b16540e60a945a573efb304d04c1224cd012313e827eca5dce5d94a9c831c5a268031176ebf5f3de5051e8cba0dbfe94c9577e7945db22e38fc060909f2278c7746b0f9b808400000000f8c9b841a6946f2d16f68338cbcbd8b117374ab421128ce422467088456bceba9d70c34106128e6d4564659cf6776c08a4186063c0a05f7cffd695c10cf26a6f301b67f800b8412b782100c18c35102dc0a37ece1a152544f04ad7dc1868d18a9570f744ace60870f822f53d35e89a2ea9709ccbf1f4a25ee5003944faa845d02dde0a41d5704601b841d53caebd6c8a82456e85c2806a9e08381f959a31fb94a77e58f00e38ad97b2e0355b8519ab2122662cbe022f2a4ef7ff16adc0b2d5dcd123181ec79705116db300a063746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365880000000000000000c0c0").unwrap(); - - let block = ::decode(&mut data.as_slice()).unwrap(); - - // encode and check that it matches the original data - let mut encoded = Vec::new(); - block.encode(&mut encoded); - assert_eq!(data, encoded); - - // check that length of encoding is the same as the output of `length` - assert_eq!(block.length(), encoded.len()); - } -} diff --git a/crates/anvil/core/src/eth/alloy_proof.rs b/crates/anvil/core/src/eth/alloy_proof.rs deleted file mode 100644 index adef84ae93ef..000000000000 --- a/crates/anvil/core/src/eth/alloy_proof.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Return types for `eth_getProof` - -use crate::eth::trie::KECCAK_NULL_RLP; -use alloy_primitives::{B256, U256}; -use foundry_common::types::ToAlloy; -use revm::primitives::KECCAK_EMPTY; - -#[derive(Clone, Debug, PartialEq, Eq, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)] -pub struct BasicAccount { - pub nonce: U256, - pub balance: U256, - pub storage_root: B256, - pub code_hash: B256, -} - -impl Default for BasicAccount { - fn default() -> Self { - BasicAccount { - balance: U256::ZERO, - nonce: U256::ZERO, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP.to_alloy(), - } - } -} diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 1675abc8f22c..c304eefa4a10 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -1,11 +1,16 @@ -use crate::eth::{receipt::TypedReceipt, transaction::TransactionInfo, trie}; -use ethers_core::{ - types::{Address, Bloom, Bytes, H256, H64, U256}, - utils::{ - keccak256, rlp, - rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}, - }, +use super::{ + transaction::{TransactionInfo, TypedReceipt}, + trie, }; +use alloy_consensus::Header; +use alloy_primitives::{Address, Bloom, Bytes, B256, U256}; +use alloy_rlp::{RlpDecodable, RlpEncodable}; + +// Type alias to optionally support impersonated transactions +#[cfg(not(feature = "impersonated-tx"))] +type Transaction = crate::eth::transaction::TypedTransaction; +#[cfg(feature = "impersonated-tx")] +type Transaction = crate::eth::transaction::MaybeImpersonatedTransaction; /// Container type that gathers all block data #[derive(Clone, Debug)] @@ -15,25 +20,14 @@ pub struct BlockInfo { pub receipts: Vec, } -// Type alias to optionally support impersonated transactions -#[cfg(not(feature = "impersonated-tx"))] -type Transaction = crate::eth::transaction::TypedTransaction; -#[cfg(feature = "impersonated-tx")] -type Transaction = crate::eth::transaction::MaybeImpersonatedTransaction; - -/// An Ethereum block -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +/// An Ethereum Block +#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable)] pub struct Block { pub header: Header, - /// Note: this supports impersonated transactions pub transactions: Vec, pub ommers: Vec
, } -// == impl Block == - impl Block { /// Creates a new block /// @@ -48,285 +42,92 @@ impl Block { T: Into, { let transactions: Vec<_> = transactions.into_iter().map(Into::into).collect(); - let ommers_hash = H256::from_slice(keccak256(&rlp::encode_list(&ommers)[..]).as_slice()); + let mut encoded_ommers: Vec = Vec::new(); + alloy_rlp::encode_list(&ommers, &mut encoded_ommers); + let ommers_hash = + B256::from_slice(alloy_primitives::utils::keccak256(encoded_ommers).as_slice()); let transactions_root = - trie::ordered_trie_root(transactions.iter().map(|r| rlp::encode(r).freeze())); + trie::ordered_trie_root(transactions.iter().map(|r| Bytes::from(alloy_rlp::encode(r)))); Self { - header: Header::new(partial_header, ommers_hash, transactions_root), + header: Header { + parent_hash: partial_header.parent_hash, + beneficiary: partial_header.beneficiary, + ommers_hash, + state_root: partial_header.state_root, + transactions_root, + receipts_root: partial_header.receipts_root, + logs_bloom: partial_header.logs_bloom, + difficulty: partial_header.difficulty, + number: partial_header.number, + gas_limit: partial_header.gas_limit, + gas_used: partial_header.gas_used, + timestamp: partial_header.timestamp, + extra_data: partial_header.extra_data, + mix_hash: partial_header.mix_hash, + withdrawals_root: Some(partial_header.mix_hash), + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + nonce: partial_header.nonce, + base_fee_per_gas: partial_header.base_fee, + }, transactions, ommers, } } } -impl Encodable for Block { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.header); - s.append_list(&self.transactions); - s.append_list(&self.ommers); - } -} - -impl Decodable for Block { - fn decode(rlp: &Rlp) -> Result { - Ok(Self { header: rlp.val_at(0)?, transactions: rlp.list_at(1)?, ommers: rlp.list_at(2)? }) - } -} - -/// ethereum block header -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] -pub struct Header { - pub parent_hash: H256, - pub ommers_hash: H256, - pub beneficiary: Address, - pub state_root: H256, - pub transactions_root: H256, - pub receipts_root: H256, - pub logs_bloom: Bloom, - pub difficulty: U256, - pub number: U256, - pub gas_limit: U256, - pub gas_used: U256, - pub timestamp: u64, - pub extra_data: Bytes, - pub mix_hash: H256, - pub nonce: H64, - /// BaseFee was added by EIP-1559 and is ignored in legacy headers. - pub base_fee_per_gas: Option, -} - -// == impl Header == - -impl Header { - pub fn new(partial_header: PartialHeader, ommers_hash: H256, transactions_root: H256) -> Self { - Self { - parent_hash: partial_header.parent_hash, - ommers_hash, - beneficiary: partial_header.beneficiary, - state_root: partial_header.state_root, - transactions_root, - receipts_root: partial_header.receipts_root, - logs_bloom: partial_header.logs_bloom, - difficulty: partial_header.difficulty, - number: partial_header.number, - gas_limit: partial_header.gas_limit, - gas_used: partial_header.gas_used, - timestamp: partial_header.timestamp, - extra_data: partial_header.extra_data, - mix_hash: partial_header.mix_hash, - nonce: partial_header.nonce, - base_fee_per_gas: partial_header.base_fee, - } - } - - pub fn hash(&self) -> H256 { - H256::from_slice(keccak256(&rlp::encode(self)).as_slice()) - } - - /// Returns the rlp length of the Header body, _not including_ trailing EIP155 fields or the - /// rlp list header - /// To get the length including the rlp list header, refer to the Encodable implementation. - #[cfg(feature = "fastrlp")] - pub(crate) fn header_payload_length(&self) -> usize { - use open_fastrlp::Encodable; - - let mut length = 0; - length += self.parent_hash.length(); - length += self.ommers_hash.length(); - length += self.beneficiary.length(); - length += self.state_root.length(); - length += self.transactions_root.length(); - length += self.receipts_root.length(); - length += self.logs_bloom.length(); - length += self.difficulty.length(); - length += self.number.length(); - length += self.gas_limit.length(); - length += self.gas_used.length(); - length += self.timestamp.length(); - length += self.extra_data.length(); - length += self.mix_hash.length(); - length += self.nonce.length(); - length += self.base_fee_per_gas.map(|fee| fee.length()).unwrap_or_default(); - length - } -} - -impl rlp::Encodable for Header { - fn rlp_append(&self, s: &mut rlp::RlpStream) { - if self.base_fee_per_gas.is_none() { - s.begin_list(15); - } else { - s.begin_list(16); - } - s.append(&self.parent_hash); - s.append(&self.ommers_hash); - s.append(&self.beneficiary); - s.append(&self.state_root); - s.append(&self.transactions_root); - s.append(&self.receipts_root); - s.append(&self.logs_bloom); - s.append(&self.difficulty); - s.append(&self.number); - s.append(&self.gas_limit); - s.append(&self.gas_used); - s.append(&self.timestamp); - s.append(&self.extra_data.as_ref()); - s.append(&self.mix_hash); - s.append(&self.nonce); - if let Some(ref base_fee) = self.base_fee_per_gas { - s.append(base_fee); - } - } -} - -impl rlp::Decodable for Header { - fn decode(rlp: &rlp::Rlp) -> Result { - let result = Header { - parent_hash: rlp.val_at(0)?, - ommers_hash: rlp.val_at(1)?, - beneficiary: rlp.val_at(2)?, - state_root: rlp.val_at(3)?, - transactions_root: rlp.val_at(4)?, - receipts_root: rlp.val_at(5)?, - logs_bloom: rlp.val_at(6)?, - difficulty: rlp.val_at(7)?, - number: rlp.val_at(8)?, - gas_limit: rlp.val_at(9)?, - gas_used: rlp.val_at(10)?, - timestamp: rlp.val_at(11)?, - extra_data: rlp.val_at::>(12)?.into(), - mix_hash: rlp.val_at(13)?, - nonce: rlp.val_at(14)?, - base_fee_per_gas: if let Ok(base_fee) = rlp.at(15) { - Some(::decode(&base_fee)?) - } else { - None - }, - }; - Ok(result) - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Encodable for Header { - fn length(&self) -> usize { - // add each of the fields' rlp encoded lengths - let mut length = 0; - length += self.header_payload_length(); - length += open_fastrlp::length_of_length(length); - - length - } - - fn encode(&self, out: &mut dyn open_fastrlp::BufMut) { - let list_header = - open_fastrlp::Header { list: true, payload_length: self.header_payload_length() }; - list_header.encode(out); - self.parent_hash.encode(out); - self.ommers_hash.encode(out); - self.beneficiary.encode(out); - self.state_root.encode(out); - self.transactions_root.encode(out); - self.receipts_root.encode(out); - self.logs_bloom.encode(out); - self.difficulty.encode(out); - self.number.encode(out); - self.gas_limit.encode(out); - self.gas_used.encode(out); - self.timestamp.encode(out); - self.extra_data.encode(out); - self.mix_hash.encode(out); - self.nonce.encode(out); - if let Some(base_fee_per_gas) = self.base_fee_per_gas { - base_fee_per_gas.encode(out); - } - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Decodable for Header { - fn decode(buf: &mut &[u8]) -> Result { - // slice out the rlp list header - let header = open_fastrlp::Header::decode(buf)?; - let start_len = buf.len(); - - Ok(Header { - parent_hash: ::decode(buf)?, - ommers_hash: ::decode(buf)?, - beneficiary:
::decode(buf)?, - state_root: ::decode(buf)?, - transactions_root: ::decode(buf)?, - receipts_root: ::decode(buf)?, - logs_bloom: ::decode(buf)?, - difficulty: ::decode(buf)?, - number: ::decode(buf)?, - gas_limit: ::decode(buf)?, - gas_used: ::decode(buf)?, - timestamp: ::decode(buf)?, - extra_data: ::decode(buf)?, - mix_hash: ::decode(buf)?, - nonce: ::decode(buf)?, - base_fee_per_gas: if start_len - header.payload_length < buf.len() { - // if there is leftover data in the payload, decode the base fee - Some(::decode(buf)?) - } else { - None - }, - }) - } -} - /// Partial header definition without ommers hash and transactions root #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PartialHeader { - pub parent_hash: H256, + pub parent_hash: B256, pub beneficiary: Address, - pub state_root: H256, - pub receipts_root: H256, + pub state_root: B256, + pub receipts_root: B256, pub logs_bloom: Bloom, pub difficulty: U256, - pub number: U256, - pub gas_limit: U256, - pub gas_used: U256, + pub number: u64, + pub gas_limit: u64, + pub gas_used: u64, pub timestamp: u64, pub extra_data: Bytes, - pub mix_hash: H256, - pub nonce: H64, - pub base_fee: Option, + pub mix_hash: B256, + pub nonce: u64, + pub base_fee: Option, } impl From
for PartialHeader { - fn from(header: Header) -> PartialHeader { + fn from(value: Header) -> Self { Self { - parent_hash: header.parent_hash, - beneficiary: header.beneficiary, - state_root: header.state_root, - receipts_root: header.receipts_root, - logs_bloom: header.logs_bloom, - difficulty: header.difficulty, - number: header.number, - gas_limit: header.gas_limit, - gas_used: header.gas_used, - timestamp: header.timestamp, - extra_data: header.extra_data, - mix_hash: header.mix_hash, - nonce: header.nonce, - base_fee: header.base_fee_per_gas, + parent_hash: value.parent_hash, + beneficiary: value.beneficiary, + state_root: value.state_root, + receipts_root: value.receipts_root, + logs_bloom: value.logs_bloom, + difficulty: value.difficulty, + number: value.number, + gas_limit: value.gas_limit, + gas_used: value.gas_used, + timestamp: value.timestamp, + extra_data: value.extra_data, + mix_hash: value.mix_hash, + nonce: value.nonce, + base_fee: value.base_fee_per_gas, } } } #[cfg(test)] mod tests { - use super::*; - use ethers_core::{ - types::H160, - utils::{hex, hex::FromHex}, + use alloy_network::Sealable; + use alloy_primitives::{ + b256, + hex::{self, FromHex}, }; + use alloy_rlp::Decodable; + + use super::*; use std::str::FromStr; #[test] @@ -340,158 +141,133 @@ mod tests { receipts_root: Default::default(), logs_bloom: Default::default(), difficulty: Default::default(), - number: 124u64.into(), - gas_limit: Default::default(), - gas_used: 1337u64.into(), - timestamp: 0, - extra_data: Default::default(), - mix_hash: Default::default(), - nonce: 99u64.to_be_bytes().into(), - base_fee_per_gas: None, - }; - - let encoded = rlp::encode(&header); - let decoded: Header = rlp::decode(encoded.as_ref()).unwrap(); - assert_eq!(header, decoded); - - header.base_fee_per_gas = Some(12345u64.into()); - - let encoded = rlp::encode(&header); - let decoded: Header = rlp::decode(encoded.as_ref()).unwrap(); - assert_eq!(header, decoded); - } - - #[test] - #[cfg(feature = "fastrlp")] - fn header_fastrlp_roundtrip() { - let mut header = Header { - parent_hash: Default::default(), - ommers_hash: Default::default(), - beneficiary: Default::default(), - state_root: Default::default(), - transactions_root: Default::default(), - receipts_root: Default::default(), - logs_bloom: Default::default(), - difficulty: Default::default(), - number: 124u64.into(), + number: 124u64, gas_limit: Default::default(), - gas_used: 1337u64.into(), + gas_used: 1337u64, timestamp: 0, extra_data: Default::default(), mix_hash: Default::default(), - nonce: H64::from_low_u64_be(99u64), + nonce: 99u64, + withdrawals_root: Default::default(), + blob_gas_used: Default::default(), + excess_blob_gas: Default::default(), + parent_beacon_block_root: Default::default(), base_fee_per_gas: None, }; - let mut encoded = vec![]; -
::encode(&header, &mut encoded); - let decoded: Header = -
::decode(&mut encoded.as_slice()).unwrap(); + let encoded = alloy_rlp::encode(&header); + let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); assert_eq!(header, decoded); - header.base_fee_per_gas = Some(12345u64.into()); + header.base_fee_per_gas = Some(12345u64); - encoded.clear(); -
::encode(&header, &mut encoded); - let decoded: Header = -
::decode(&mut encoded.as_slice()).unwrap(); + let encoded = alloy_rlp::encode(&header); + let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); assert_eq!(header, decoded); } #[test] - #[cfg(feature = "fastrlp")] - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 fn test_encode_block_header() { - use open_fastrlp::Encodable; + use alloy_rlp::Encodable; let expected = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap(); let mut data = vec![]; let header = Header { - parent_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - ommers_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - beneficiary: H160::from_str("0000000000000000000000000000000000000000").unwrap(), - state_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - transactions_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - receipts_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), - difficulty: 0x8aeu64.into(), - number: 0xd05u64.into(), - gas_limit: 0x115cu64.into(), - gas_used: 0x15b3u64.into(), + parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(), + state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), + difficulty: U256::from(2222), + number: 0xd05u64, + gas_limit: 0x115cu64, + gas_used: 0x15b3u64, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), - mix_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: H64::from_low_u64_be(0x0), + mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + nonce: 0, base_fee_per_gas: None, }; + header.encode(&mut data); assert_eq!(hex::encode(&data), hex::encode(expected)); assert_eq!(header.length(), data.len()); } #[test] - // Test vector from: https://github.com/ethereum/tests/blob/f47bbef4da376a49c8fc3166f09ab8a6d182f765/BlockchainTests/ValidBlocks/bcEIP1559/baseFee.json#L15-L36 - fn test_eip1559_block_header_hash() { - let expected_hash = - H256::from_str("6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f") - .unwrap(); - let header = Header { - parent_hash: H256::from_str("e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a").unwrap(), - ommers_hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), - beneficiary: H160::from_str("ba5e000000000000000000000000000000000000").unwrap(), - state_root: H256::from_str("ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7").unwrap(), - transactions_root: H256::from_str("50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf").unwrap(), - receipts_root: H256::from_str("29b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9").unwrap(), - logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), - difficulty: 0x020000.into(), - number: 0x01.into(), - gas_limit: U256::from_str("016345785d8a0000").unwrap(), - gas_used: 0x015534.into(), - timestamp: 0x079e, - extra_data: hex::decode("42").unwrap().into(), - mix_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: H64::from_low_u64_be(0x0), - base_fee_per_gas: Some(0x036b.into()), - }; - assert_eq!(header.hash(), expected_hash); - } - - #[test] - #[cfg(feature = "fastrlp")] // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 fn test_decode_block_header() { let data = hex::decode("f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000").unwrap(); let expected = Header { - parent_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - ommers_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - beneficiary: H160::from_str("0000000000000000000000000000000000000000").unwrap(), - state_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - transactions_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - receipts_root: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + parent_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + ommers_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + beneficiary: Address::from_str("0000000000000000000000000000000000000000").unwrap(), + state_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + transactions_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + receipts_root: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), - difficulty: 0x8aeu64.into(), - number: 0xd05u64.into(), - gas_limit: 0x115cu64.into(), - gas_used: 0x15b3u64.into(), + difficulty: U256::from(2222), + number: 0xd05u64, + gas_limit: 0x115cu64, + gas_used: 0x15b3u64, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), - mix_hash: H256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: H64::from_low_u64_be(0x0), + mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + nonce: 0, + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, base_fee_per_gas: None, }; - let header =
::decode(&mut data.as_slice()).unwrap(); + let header = Header::decode(&mut data.as_slice()).unwrap(); assert_eq!(header, expected); } #[test] - #[cfg(feature = "fastrlp")] + // Test vector from: https://github.com/ethereum/tests/blob/f47bbef4da376a49c8fc3166f09ab8a6d182f765/BlockchainTests/ValidBlocks/bcEIP1559/baseFee.json#L15-L36 + fn test_eip1559_block_header_hash() { + let expected_hash = + b256!("6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f"); + let header = Header { + parent_hash: B256::from_str("e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a").unwrap(), + ommers_hash: B256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), + beneficiary: Address::from_str("ba5e000000000000000000000000000000000000").unwrap(), + state_root: B256::from_str("ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7").unwrap(), + transactions_root: B256::from_str("50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf").unwrap(), + receipts_root: B256::from_str("29b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9").unwrap(), + logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), + difficulty: U256::from(0x020000), + number: 1u64, + gas_limit: U256::from(0x016345785d8a0000u128).to::(), + gas_used: U256::from(0x015534).to::(), + timestamp: 0x079e, + extra_data: hex::decode("42").unwrap().into(), + mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), + nonce: 0, + base_fee_per_gas: Some(875), + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + }; + assert_eq!(header.hash(), expected_hash); + } + + #[test] // Test vector from network - fn block_network_fastrlp_roundtrip() { - use open_fastrlp::Encodable; + fn block_network_roundtrip() { + use alloy_rlp::Encodable; let data = hex::decode("f9034df90348a0fbdbd8d2d0ac5f14bd5fa90e547fe6f1d15019c724f8e7b60972d381cd5d9cf8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c9577e7945db22e38fc060909f2278c7746b0f9ba05017cfa3b0247e35197215ae8d610265ffebc8edca8ea66d6567eb0adecda867a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018355bb7b871fffffffffffff808462bd0e1ab9014bf90148a00000000000000000000000000000000000000000000000000000000000000000f85494319fa8f1bc4e53410e92d10d918659b16540e60a945a573efb304d04c1224cd012313e827eca5dce5d94a9c831c5a268031176ebf5f3de5051e8cba0dbfe94c9577e7945db22e38fc060909f2278c7746b0f9b808400000000f8c9b841a6946f2d16f68338cbcbd8b117374ab421128ce422467088456bceba9d70c34106128e6d4564659cf6776c08a4186063c0a05f7cffd695c10cf26a6f301b67f800b8412b782100c18c35102dc0a37ece1a152544f04ad7dc1868d18a9570f744ace60870f822f53d35e89a2ea9709ccbf1f4a25ee5003944faa845d02dde0a41d5704601b841d53caebd6c8a82456e85c2806a9e08381f959a31fb94a77e58f00e38ad97b2e0355b8519ab2122662cbe022f2a4ef7ff16adc0b2d5dcd123181ec79705116db300a063746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365880000000000000000c0c0").unwrap(); - let block = ::decode(&mut data.as_slice()).unwrap(); + let block = Block::decode(&mut data.as_slice()).unwrap(); // encode and check that it matches the original data let mut encoded = Vec::new(); diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 799c6ff6b359..3fd0c6ed889c 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -1,5 +1,5 @@ use crate::{ - eth::subscription::SubscriptionId, + eth::{subscription::SubscriptionId, transaction::EthTransactionRequest}, types::{EvmMineOptions, Forking, Index}, }; use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256}; @@ -9,13 +9,9 @@ use alloy_rpc_types::{ state::StateOverride, BlockId, BlockNumberOrTag as BlockNumber, CallRequest, Filter, }; -use ethers_core::types::transaction::eip712::TypedData; -pub mod alloy_block; -pub mod alloy_proof; pub mod block; pub mod proof; -pub mod receipt; pub mod subscription; pub mod transaction; pub mod trie; @@ -26,7 +22,6 @@ pub mod serde_helpers; #[cfg(feature = "serde")] use self::serde_helpers::*; -use self::transaction::EthTransactionRequest; #[cfg(feature = "serde")] use foundry_common::serde_helpers::{ @@ -42,7 +37,7 @@ pub struct Params { } /// Represents ethereum JSON-RPC API -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "method", content = "params"))] pub enum EthRequest { @@ -159,7 +154,7 @@ pub enum EthRequest { /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md), and includes full support of arrays and recursive data structures. #[cfg_attr(feature = "serde", serde(rename = "eth_signTypedData_v4"))] - EthSignTypedDataV4(Address, TypedData), + EthSignTypedDataV4(Address, alloy_dyn_abi::TypedData), #[cfg_attr(feature = "serde", serde(rename = "eth_sendTransaction", with = "sequence"))] EthSendTransaction(Box), @@ -737,7 +732,7 @@ pub enum EthPubSub { } /// Container type for either a request or a pub sub -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "serde", serde(untagged))] pub enum EthRpcCall { diff --git a/crates/anvil/core/src/eth/proof.rs b/crates/anvil/core/src/eth/proof.rs index 1fd22a4cae5c..ba2357bffd57 100644 --- a/crates/anvil/core/src/eth/proof.rs +++ b/crates/anvil/core/src/eth/proof.rs @@ -1,59 +1,24 @@ //! Return types for `eth_getProof` use crate::eth::trie::KECCAK_NULL_RLP; -use ethers_core::{ - types::{H256, U256}, - utils::rlp, -}; -use foundry_common::types::ToEthers; +use alloy_primitives::{B256, U256}; use revm::primitives::KECCAK_EMPTY; -// reexport for convenience -pub use ethers_core::types::{EIP1186ProofResponse as AccountProof, StorageProof}; - -/// Basic account type. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] +#[derive(Clone, Debug, PartialEq, Eq, alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable)] pub struct BasicAccount { - /// Nonce of the account. pub nonce: U256, - /// Balance of the account. pub balance: U256, - /// Storage root of the account. - pub storage_root: H256, - /// Code hash of the account. - pub code_hash: H256, + pub storage_root: B256, + pub code_hash: B256, } impl Default for BasicAccount { fn default() -> Self { BasicAccount { - balance: 0.into(), - nonce: 0.into(), - code_hash: KECCAK_EMPTY.to_ethers(), + balance: U256::ZERO, + nonce: U256::ZERO, + code_hash: KECCAK_EMPTY, storage_root: KECCAK_NULL_RLP, } } } - -impl rlp::Encodable for BasicAccount { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - } -} - -impl rlp::Decodable for BasicAccount { - fn decode(rlp: &rlp::Rlp) -> Result { - let result = BasicAccount { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - }; - Ok(result) - } -} diff --git a/crates/anvil/core/src/eth/receipt.rs b/crates/anvil/core/src/eth/receipt.rs deleted file mode 100644 index c651124878a7..000000000000 --- a/crates/anvil/core/src/eth/receipt.rs +++ /dev/null @@ -1,376 +0,0 @@ -use crate::eth::utils::enveloped; -use ethers_core::{ - types::{Address, Bloom, Bytes, H256, U256}, - utils::{ - rlp, - rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}, - }, -}; -use foundry_common::types::{ToAlloy, ToEthers}; - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Log { - pub address: Address, - pub topics: Vec, - pub data: Bytes, -} - -impl From for Log { - fn from(log: revm::primitives::Log) -> Self { - let revm::primitives::Log { address, topics, data } = log; - Log { - address: address.to_ethers(), - topics: topics.into_iter().map(|h| h.to_ethers()).collect(), - data: ethers_core::types::Bytes(data.0), - } - } -} - -impl From for revm::primitives::Log { - fn from(log: Log) -> Self { - let Log { address, topics, data } = log; - revm::primitives::Log { - address: address.to_alloy(), - topics: topics.into_iter().map(|t| t.to_alloy()).collect(), - data: alloy_primitives::Bytes(data.0), - } - } -} - -impl Encodable for Log { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(3); - stream.append(&self.address); - stream.append_list(&self.topics); - stream.append(&self.data.as_ref()); - } -} - -impl Decodable for Log { - fn decode(rlp: &Rlp) -> Result { - let result = Log { - address: rlp.val_at(0)?, - topics: rlp.list_at(1)?, - data: rlp.val_at::>(2)?.into(), - }; - Ok(result) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct EIP658Receipt { - pub status_code: u8, - pub gas_used: U256, - pub logs_bloom: Bloom, - pub logs: Vec, -} - -impl Encodable for EIP658Receipt { - fn rlp_append(&self, stream: &mut RlpStream) { - stream.begin_list(4); - stream.append(&self.status_code); - stream.append(&self.gas_used); - stream.append(&self.logs_bloom); - stream.append_list(&self.logs); - } -} - -impl Decodable for EIP658Receipt { - fn decode(rlp: &Rlp) -> Result { - let result = EIP658Receipt { - status_code: rlp.val_at(0)?, - gas_used: rlp.val_at(1)?, - logs_bloom: rlp.val_at(2)?, - logs: rlp.list_at(3)?, - }; - Ok(result) - } -} - -// same underlying data structure -pub type EIP2930Receipt = EIP658Receipt; -pub type EIP1559Receipt = EIP658Receipt; -pub type DepositReceipt = EIP658Receipt; - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TypedReceipt { - /// Legacy receipt - Legacy(EIP658Receipt), - /// EIP-2930 receipt - EIP2930(EIP2930Receipt), - /// EIP-1559 receipt - EIP1559(EIP1559Receipt), - /// op-stack deposit receipt - Deposit(DepositReceipt), -} - -// == impl TypedReceipt == - -impl TypedReceipt { - /// Returns the gas used by the transactions - pub fn gas_used(&self) -> U256 { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::Deposit(r) => r.gas_used, - } - } - - /// Returns the gas used by the transactions - pub fn logs_bloom(&self) -> &Bloom { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::Deposit(r) => &r.logs_bloom, - } - } -} - -impl Encodable for TypedReceipt { - fn rlp_append(&self, s: &mut RlpStream) { - match self { - TypedReceipt::Legacy(r) => r.rlp_append(s), - TypedReceipt::EIP2930(r) => enveloped(1, r, s), - TypedReceipt::EIP1559(r) => enveloped(2, r, s), - TypedReceipt::Deposit(r) => enveloped(0x7E, r, s), - } - } -} - -impl Decodable for TypedReceipt { - fn decode(rlp: &Rlp) -> Result { - let slice = rlp.data()?; - - let first = *slice.first().ok_or(DecoderError::Custom("empty receipt"))?; - - if rlp.is_list() { - return Ok(TypedReceipt::Legacy(Decodable::decode(rlp)?)) - } - - let s = slice.get(1..).ok_or(DecoderError::Custom("no receipt content"))?; - - if first == 0x01 { - return rlp::decode(s).map(TypedReceipt::EIP2930) - } - - if first == 0x02 { - return rlp::decode(s).map(TypedReceipt::EIP1559) - } - - if first == 0x7E { - return rlp::decode(s).map(TypedReceipt::Deposit) - } - - Err(DecoderError::Custom("unknown receipt type")) - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Encodable for TypedReceipt { - fn length(&self) -> usize { - match self { - TypedReceipt::Legacy(r) => r.length(), - receipt => { - let payload_len = match receipt { - TypedReceipt::EIP2930(r) => r.length() + 1, - TypedReceipt::EIP1559(r) => r.length() + 1, - TypedReceipt::Deposit(r) => r.length() + 1, - _ => unreachable!("receipt already matched"), - }; - - // we include a string header for typed receipts, so include the length here - payload_len + open_fastrlp::length_of_length(payload_len) - } - } - } - fn encode(&self, out: &mut dyn open_fastrlp::BufMut) { - use open_fastrlp::Header; - - match self { - TypedReceipt::Legacy(r) => r.encode(out), - receipt => { - let payload_len = match receipt { - TypedReceipt::EIP2930(r) => r.length() + 1, - TypedReceipt::EIP1559(r) => r.length() + 1, - TypedReceipt::Deposit(r) => r.length() + 1, - _ => unreachable!("receipt already matched"), - }; - - match receipt { - TypedReceipt::EIP2930(r) => { - let receipt_string_header = - Header { list: false, payload_length: payload_len }; - - receipt_string_header.encode(out); - out.put_u8(0x01); - r.encode(out); - } - TypedReceipt::EIP1559(r) => { - let receipt_string_header = - Header { list: false, payload_length: payload_len }; - - receipt_string_header.encode(out); - out.put_u8(0x02); - r.encode(out); - } - TypedReceipt::Deposit(r) => { - let receipt_string_header = - Header { list: false, payload_length: payload_len }; - - receipt_string_header.encode(out); - out.put_u8(0x7E); - r.encode(out); - } - _ => unreachable!("receipt already matched"), - } - } - } - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Decodable for TypedReceipt { - fn decode(buf: &mut &[u8]) -> Result { - use bytes::Buf; - use open_fastrlp::Header; - use std::cmp::Ordering; - - // a receipt is either encoded as a string (non legacy) or a list (legacy). - // We should not consume the buffer if we are decoding a legacy receipt, so let's - // check if the first byte is between 0x80 and 0xbf. - let rlp_type = *buf - .first() - .ok_or(open_fastrlp::DecodeError::Custom("cannot decode a receipt from empty bytes"))?; - - match rlp_type.cmp(&open_fastrlp::EMPTY_LIST_CODE) { - Ordering::Less => { - // strip out the string header - let _header = Header::decode(buf)?; - let receipt_type = *buf.first().ok_or(open_fastrlp::DecodeError::Custom( - "typed receipt cannot be decoded from an empty slice", - ))?; - if receipt_type == 0x01 { - buf.advance(1); - ::decode(buf) - .map(TypedReceipt::EIP2930) - } else if receipt_type == 0x02 { - buf.advance(1); - ::decode(buf) - .map(TypedReceipt::EIP1559) - } else if receipt_type == 0x7E { - buf.advance(1); - ::decode(buf) - .map(TypedReceipt::Deposit) - } else { - Err(open_fastrlp::DecodeError::Custom("invalid receipt type")) - } - } - Ordering::Equal => Err(open_fastrlp::DecodeError::Custom( - "an empty list is not a valid receipt encoding", - )), - Ordering::Greater => { - ::decode(buf).map(TypedReceipt::Legacy) - } - } - } -} - -impl From for EIP658Receipt { - fn from(v3: TypedReceipt) -> Self { - match v3 { - TypedReceipt::Legacy(receipt) => receipt, - TypedReceipt::EIP2930(receipt) => receipt, - TypedReceipt::EIP1559(receipt) => receipt, - TypedReceipt::Deposit(receipt) => receipt, - } - } -} - -#[cfg(test)] -mod tests { - #[test] - #[cfg(feature = "fastrlp")] - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 - fn encode_legacy_receipt() { - use crate::eth::receipt::{EIP658Receipt, Log, TypedReceipt}; - use ethers_core::{ - types::{Bytes, H160, H256}, - utils::hex, - }; - use open_fastrlp::Encodable; - use std::str::FromStr; - - let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); - - let mut data = vec![]; - let receipt = TypedReceipt::Legacy(EIP658Receipt { - logs_bloom: [0; 256].into(), - gas_used: 0x1u64.into(), - logs: vec![Log { - address: H160::from_str("0000000000000000000000000000000000000011").unwrap(), - topics: vec![ - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - data: Bytes::from_str("0100ff").unwrap(), - }], - status_code: 0, - }); - receipt.encode(&mut data); - - // check that the rlp length equals the length of the expected rlp - assert_eq!(receipt.length(), expected.len()); - assert_eq!(data, expected); - } - - #[test] - #[cfg(feature = "fastrlp")] - // Test vector from: https://eips.ethereum.org/EIPS/eip-2481 - fn decode_legacy_receipt() { - use crate::eth::receipt::{EIP658Receipt, Log, TypedReceipt}; - use ethers_core::{ - types::{Bytes, H160, H256}, - utils::hex, - }; - use open_fastrlp::Decodable; - use std::str::FromStr; - - let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); - - let expected = TypedReceipt::Legacy(EIP658Receipt { - logs_bloom: [0; 256].into(), - gas_used: 0x1u64.into(), - logs: vec![Log { - address: H160::from_str("0000000000000000000000000000000000000011").unwrap(), - topics: vec![ - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - data: Bytes::from_str("0100ff").unwrap(), - }], - status_code: 0, - }); - - let receipt = TypedReceipt::decode(&mut &data[..]).unwrap(); - assert_eq!(receipt, expected); - } -} diff --git a/crates/anvil/core/src/eth/serde_helpers.rs b/crates/anvil/core/src/eth/serde_helpers.rs index f7d5bd46dba2..311b3a9f27e8 100644 --- a/crates/anvil/core/src/eth/serde_helpers.rs +++ b/crates/anvil/core/src/eth/serde_helpers.rs @@ -33,6 +33,35 @@ pub mod sequence { } } +pub mod numeric { + use alloy_primitives::U256; + use serde::{Deserialize, Deserializer}; + /// Supports parsing u64 + /// + /// See + pub fn deserialize_stringified_u64_opt<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + if let Some(num) = Option::::deserialize(deserializer)? { + num.try_into().map(Some).map_err(serde::de::Error::custom) + } else { + Ok(None) + } + } + + /// Supports parsing u64 + /// + /// See + pub fn deserialize_stringified_u64<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let num = U256::deserialize(deserializer)?; + num.try_into().map_err(serde::de::Error::custom) + } +} + /// A module that deserializes `[]` optionally pub mod empty_params { use serde::{Deserialize, Deserializer}; diff --git a/crates/anvil/core/src/eth/transaction/alloy.rs b/crates/anvil/core/src/eth/transaction/alloy.rs deleted file mode 100644 index 1c3ba1641aa3..000000000000 --- a/crates/anvil/core/src/eth/transaction/alloy.rs +++ /dev/null @@ -1,1071 +0,0 @@ -use crate::eth::{ - transaction::optimism::{DepositTransaction, DepositTransactionRequest}, - utils::eip_to_revm_access_list, -}; -use alloy_consensus::{ReceiptWithBloom, TxEip1559, TxEip2930, TxLegacy}; -use alloy_network::{Signed, Transaction, TxKind}; -use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, B256, U256}; -use alloy_rlp::{Decodable, Encodable}; -use alloy_rpc_types::{request::TransactionRequest, AccessList, CallRequest}; -use foundry_evm::traces::CallTraceNode; -use revm::{ - interpreter::InstructionResult, - primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, -}; -use std::ops::Deref; - -/// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC -#[cfg(feature = "impersonated-tx")] -pub fn impersonated_signature() -> Signature { - Signature::from_scalars_and_parity(B256::ZERO, B256::ZERO, false).unwrap() -} - -pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { - let TransactionRequest { - from, - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - gas, - value, - data, - nonce, - mut access_list, - transaction_type, - other, - .. - } = tx; - let transaction_type = transaction_type.map(|id| id.to::()); - - // Special case: OP-stack deposit tx - if transaction_type == Some(126) { - return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { - from: from.unwrap_or_default(), - source_hash: other.get_deserialized::("sourceHash")?.ok()?, - kind: TxKind::Create, - mint: other.get_deserialized::("mint")?.ok()?, - value: value.unwrap_or_default(), - gas_limit: gas.unwrap_or_default(), - is_system_tx: other.get_deserialized::("isSystemTx")?.ok()?, - input: data.unwrap_or_default(), - })) - } - - match ( - transaction_type, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - access_list.take(), - ) { - // legacy transaction - (Some(0), _, None, None, None) | (None, Some(_), None, None, None) => { - Some(TypedTransactionRequest::Legacy(TxLegacy { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: data.unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id: None, - })) - } - // EIP2930 - (Some(1), _, None, None, _) | (None, _, None, None, Some(_)) => { - Some(TypedTransactionRequest::EIP2930(TxEip2930 { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: data.unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), - })) - } - // EIP1559 - (Some(2), None, _, _, _) | - (None, None, Some(_), _, _) | - (None, None, _, Some(_), _) | - (None, None, None, None, None) => { - // Empty fields fall back to the canonical transaction schema. - Some(TypedTransactionRequest::EIP1559(TxEip1559 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: data.unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), - })) - } - _ => None, - } -} - -pub fn call_request_to_typed(tx: CallRequest) -> Option { - let CallRequest { - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - gas, - value, - input, - chain_id, - nonce, - mut access_list, - transaction_type, - .. - } = tx; - let chain_id = chain_id.map(|id| id.to::()); - let transaction_type = transaction_type.map(|id| id.to::()); - - match ( - transaction_type, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - access_list.take(), - ) { - // legacy transaction - (Some(0), _, None, None, None) | (None, Some(_), None, None, None) => { - Some(TypedTransactionRequest::Legacy(TxLegacy { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id, - })) - } - // EIP2930 - (Some(1), _, None, None, _) | (None, _, None, None, Some(_)) => { - Some(TypedTransactionRequest::EIP2930(TxEip2930 { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id: chain_id.unwrap_or_default(), - access_list: to_eip_access_list(access_list.unwrap_or_default()), - })) - } - // EIP1559 - (Some(2), None, _, _, _) | - (None, None, Some(_), _, _) | - (None, None, _, Some(_), _) | - (None, None, None, None, None) => { - // Empty fields fall back to the canonical transaction schema. - Some(TypedTransactionRequest::EIP1559(TxEip1559 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), - value: value.unwrap_or(U256::ZERO), - input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, - chain_id: chain_id.unwrap_or_default(), - access_list: to_eip_access_list(access_list.unwrap_or_default()), - })) - } - _ => None, - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedTransactionRequest { - Legacy(TxLegacy), - EIP2930(TxEip2930), - EIP1559(TxEip1559), - Deposit(DepositTransactionRequest), -} - -/// A wrapper for [TypedTransaction] that allows impersonating accounts. -/// -/// This is a helper that carries the `impersonated` sender so that the right hash -/// [TypedTransaction::impersonated_hash] can be created. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MaybeImpersonatedTransaction { - pub transaction: TypedTransaction, - pub impersonated_sender: Option
, -} - -impl MaybeImpersonatedTransaction { - /// Creates a new wrapper for the given transaction - pub fn new(transaction: TypedTransaction) -> Self { - Self { transaction, impersonated_sender: None } - } - - /// Creates a new impersonated transaction wrapper using the given sender - pub fn impersonated(transaction: TypedTransaction, impersonated_sender: Address) -> Self { - Self { transaction, impersonated_sender: Some(impersonated_sender) } - } - - /// Recovers the Ethereum address which was used to sign the transaction. - /// - /// Note: this is feature gated so it does not conflict with the `Deref`ed - /// [TypedTransaction::recover] function by default. - #[cfg(feature = "impersonated-tx")] - pub fn recover(&self) -> Result { - if let Some(sender) = self.impersonated_sender { - return Ok(sender) - } - self.transaction.recover() - } - - /// Returns the hash of the transaction - /// - /// Note: this is feature gated so it does not conflict with the `Deref`ed - /// [TypedTransaction::hash] function by default. - #[cfg(feature = "impersonated-tx")] - pub fn hash(&self) -> B256 { - if self.transaction.is_impersonated() { - if let Some(sender) = self.impersonated_sender { - return self.transaction.impersonated_hash(sender) - } - } - self.transaction.hash() - } -} - -impl Encodable for MaybeImpersonatedTransaction { - fn encode(&self, out: &mut dyn bytes::BufMut) { - self.transaction.encode(out) - } -} - -impl From for TypedTransaction { - fn from(value: MaybeImpersonatedTransaction) -> Self { - value.transaction - } -} - -impl From for MaybeImpersonatedTransaction { - fn from(value: TypedTransaction) -> Self { - MaybeImpersonatedTransaction::new(value) - } -} - -impl Decodable for MaybeImpersonatedTransaction { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - TypedTransaction::decode(buf).map(MaybeImpersonatedTransaction::new) - } -} - -impl AsRef for MaybeImpersonatedTransaction { - fn as_ref(&self) -> &TypedTransaction { - &self.transaction - } -} - -impl Deref for MaybeImpersonatedTransaction { - type Target = TypedTransaction; - - fn deref(&self) -> &Self::Target { - &self.transaction - } -} - -/// Queued transaction -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingTransaction { - /// The actual transaction - pub transaction: MaybeImpersonatedTransaction, - /// the recovered sender of this transaction - sender: Address, - /// hash of `transaction`, so it can easily be reused with encoding and hashing agan - hash: TxHash, -} - -impl PendingTransaction { - pub fn new(transaction: TypedTransaction) -> Result { - let sender = transaction.recover()?; - let hash = transaction.hash(); - Ok(Self { transaction: MaybeImpersonatedTransaction::new(transaction), sender, hash }) - } - - #[cfg(feature = "impersonated-tx")] - pub fn with_impersonated(transaction: TypedTransaction, sender: Address) -> Self { - let hash = transaction.impersonated_hash(sender); - Self { - transaction: MaybeImpersonatedTransaction::impersonated(transaction, sender), - sender, - hash, - } - } - - pub fn nonce(&self) -> U256 { - self.transaction.nonce() - } - - pub fn hash(&self) -> &TxHash { - &self.hash - } - - pub fn sender(&self) -> &Address { - &self.sender - } - - /// Converts the [PendingTransaction] into the [TxEnv] context that [`revm`](foundry_evm) - /// expects. - pub fn to_revm_tx_env(&self) -> TxEnv { - fn transact_to(kind: &TxKind) -> TransactTo { - match kind { - TxKind::Call(c) => TransactTo::Call(*c), - TxKind::Create => TransactTo::Create(CreateScheme::Create), - } - } - - let caller = *self.sender(); - match &self.transaction.transaction { - TypedTransaction::Legacy(tx) => { - let chain_id = tx.chain_id(); - let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx(); - TxEnv { - caller, - transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id, - nonce: Some(*nonce), - value: (*value), - gas_price: U256::from(*gas_price), - gas_priority_fee: None, - gas_limit: *gas_limit, - access_list: vec![], - ..Default::default() - } - } - TypedTransaction::EIP2930(tx) => { - let TxEip2930 { - chain_id, - nonce, - gas_price, - gas_limit, - to, - value, - input, - access_list, - .. - } = tx.tx(); - TxEnv { - caller, - transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id: Some(*chain_id), - nonce: Some(*nonce), - value: *value, - gas_price: U256::from(*gas_price), - gas_priority_fee: None, - gas_limit: *gas_limit, - access_list: eip_to_revm_access_list(access_list.0.clone()), - ..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(); - TxEnv { - caller, - transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id: Some(*chain_id), - nonce: Some(*nonce), - value: *value, - gas_price: U256::from(*max_fee_per_gas), - gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), - gas_limit: *gas_limit, - access_list: eip_to_revm_access_list(access_list.0.clone()), - ..Default::default() - } - } - TypedTransaction::Deposit(tx) => { - let chain_id = tx.chain_id(); - let DepositTransaction { - nonce, - source_hash, - gas_limit, - value, - kind, - mint, - input, - is_system_tx, - .. - } = tx; - TxEnv { - caller, - transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id, - nonce: Some(nonce.to::()), - value: *value, - gas_price: U256::ZERO, - gas_priority_fee: None, - gas_limit: gas_limit.to::(), - access_list: vec![], - optimism: OptimismFields { - source_hash: Some(*source_hash), - mint: Some(mint.to::()), - is_system_transaction: Some(*is_system_tx), - enveloped_tx: None, - }, - ..Default::default() - } - } - } - } -} - -/// Container type for signed, typed transactions. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedTransaction { - /// Legacy transaction type - Legacy(Signed), - /// EIP-2930 transaction - EIP2930(Signed), - /// EIP-1559 transaction - EIP1559(Signed), - /// op-stack deposit transaction - Deposit(DepositTransaction), -} - -impl TypedTransaction { - /// Returns true if the transaction uses dynamic fees: EIP1559 - pub fn is_dynamic_fee(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) - } - - pub fn gas_price(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_price, - TypedTransaction::EIP2930(tx) => tx.gas_price, - TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, - TypedTransaction::Deposit(_) => 0, - }) - } - - pub fn gas_limit(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_limit, - TypedTransaction::EIP2930(tx) => tx.gas_limit, - TypedTransaction::EIP1559(tx) => tx.gas_limit, - TypedTransaction::Deposit(tx) => tx.gas_limit.to::(), - }) - } - - pub fn value(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.value, - TypedTransaction::EIP2930(tx) => tx.value, - TypedTransaction::EIP1559(tx) => tx.value, - TypedTransaction::Deposit(tx) => tx.value, - }) - } - - pub fn data(&self) -> &Bytes { - match self { - TypedTransaction::Legacy(tx) => &tx.input, - TypedTransaction::EIP2930(tx) => &tx.input, - TypedTransaction::EIP1559(tx) => &tx.input, - TypedTransaction::Deposit(tx) => &tx.input, - } - } - - /// Returns the transaction type - pub fn r#type(&self) -> Option { - match self { - TypedTransaction::Legacy(_) => None, - TypedTransaction::EIP2930(_) => Some(1), - TypedTransaction::EIP1559(_) => Some(2), - TypedTransaction::Deposit(_) => Some(0x7E), - } - } - - /// Max cost of the transaction - pub fn max_cost(&self) -> U256 { - self.gas_limit().saturating_mul(self.gas_price()) - } - - /// Returns a helper type that contains commonly used values as fields - pub fn essentials(&self) -> TransactionEssentials { - match self { - TypedTransaction::Legacy(t) => TransactionEssentials { - kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), - gas_price: Some(U256::from(t.tx().gas_price)), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - value: t.value, - chain_id: t.tx().chain_id, - access_list: Default::default(), - }, - TypedTransaction::EIP2930(t) => TransactionEssentials { - kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), - gas_price: Some(U256::from(t.tx().gas_price)), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), - }, - TypedTransaction::EIP1559(t) => TransactionEssentials { - kind: t.to, - input: t.input.clone(), - nonce: U256::from(t.nonce), - gas_limit: U256::from(t.gas_limit), - gas_price: None, - max_fee_per_gas: Some(U256::from(t.max_fee_per_gas)), - max_priority_fee_per_gas: Some(U256::from(t.max_priority_fee_per_gas)), - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), - }, - TypedTransaction::Deposit(t) => TransactionEssentials { - kind: t.kind, - input: t.input.clone(), - nonce: t.nonce, - gas_limit: t.gas_limit, - gas_price: Some(U256::from(0)), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - value: t.value, - chain_id: t.chain_id(), - access_list: Default::default(), - }, - } - } - - pub fn nonce(&self) -> U256 { - match self { - TypedTransaction::Legacy(t) => U256::from(t.nonce), - TypedTransaction::EIP2930(t) => U256::from(t.nonce), - TypedTransaction::EIP1559(t) => U256::from(t.nonce), - TypedTransaction::Deposit(t) => U256::from(t.nonce), - } - } - - pub fn chain_id(&self) -> Option { - match self { - TypedTransaction::Legacy(t) => t.chain_id, - TypedTransaction::EIP2930(t) => Some(t.chain_id), - TypedTransaction::EIP1559(t) => Some(t.chain_id), - TypedTransaction::Deposit(t) => t.chain_id(), - } - } - - pub fn as_legacy(&self) -> Option<&Signed> { - match self { - TypedTransaction::Legacy(tx) => Some(tx), - _ => None, - } - } - - /// Returns true whether this tx is a legacy transaction - pub fn is_legacy(&self) -> bool { - matches!(self, TypedTransaction::Legacy(_)) - } - - /// Returns true whether this tx is a EIP1559 transaction - pub fn is_eip1559(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) - } - - /// Returns the hash of the transaction. - /// - /// Note: If this transaction has the Impersonated signature then this returns a modified unique - /// hash. This allows us to treat impersonated transactions as unique. - pub fn hash(&self) -> B256 { - match self { - TypedTransaction::Legacy(t) => *t.hash(), - TypedTransaction::EIP2930(t) => *t.hash(), - TypedTransaction::EIP1559(t) => *t.hash(), - TypedTransaction::Deposit(t) => t.hash(), - } - } - - /// Returns true if the transaction was impersonated (using the impersonate Signature) - #[cfg(feature = "impersonated-tx")] - pub fn is_impersonated(&self) -> bool { - self.signature() == impersonated_signature() - } - - /// Returns the hash if the transaction is impersonated (using a fake signature) - /// - /// This appends the `address` before hashing it - #[cfg(feature = "impersonated-tx")] - pub fn impersonated_hash(&self, sender: Address) -> B256 { - let mut buffer = Vec::::new(); - Encodable::encode(self, &mut buffer); - buffer.extend_from_slice(sender.as_ref()); - B256::from_slice(alloy_primitives::utils::keccak256(&buffer).as_slice()) - } - - /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { - match self { - TypedTransaction::Legacy(tx) => tx.recover_signer(), - TypedTransaction::EIP2930(tx) => tx.recover_signer(), - TypedTransaction::EIP1559(tx) => tx.recover_signer(), - TypedTransaction::Deposit(tx) => tx.recover(), - } - } - - /// Returns what kind of transaction this is - pub fn kind(&self) -> &TxKind { - match self { - TypedTransaction::Legacy(tx) => &tx.to, - TypedTransaction::EIP2930(tx) => &tx.to, - TypedTransaction::EIP1559(tx) => &tx.to, - TypedTransaction::Deposit(tx) => &tx.kind, - } - } - - /// Returns the callee if this transaction is a call - pub fn to(&self) -> Option
{ - self.kind().to() - } - - /// Returns the Signature of the transaction - pub fn signature(&self) -> Signature { - match self { - TypedTransaction::Legacy(tx) => *tx.signature(), - TypedTransaction::EIP2930(tx) => *tx.signature(), - TypedTransaction::EIP1559(tx) => *tx.signature(), - TypedTransaction::Deposit(_) => { - Signature::from_scalars_and_parity(B256::ZERO, B256::ZERO, false).unwrap() - } - } - } -} - -impl Encodable for TypedTransaction { - fn encode(&self, out: &mut dyn bytes::BufMut) { - match self { - TypedTransaction::Legacy(tx) => tx.encode(out), - TypedTransaction::EIP2930(tx) => tx.encode(out), - TypedTransaction::EIP1559(tx) => tx.encode(out), - TypedTransaction::Deposit(tx) => tx.encode(out), - } - } -} - -impl Decodable for TypedTransaction { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use bytes::Buf; - use std::cmp::Ordering; - - let first = *buf.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; - - // a signed transaction is either encoded as a string (non legacy) or a list (legacy). - // We should not consume the buffer if we are decoding a legacy transaction, so let's - // check if the first byte is between 0x80 and 0xbf. - match first.cmp(&alloy_rlp::EMPTY_LIST_CODE) { - Ordering::Less => { - // strip out the string header - // NOTE: typed transaction encodings either contain a "rlp header" which contains - // the type of the payload and its length, or they do not contain a header and - // start with the tx type byte. - // - // This line works for both types of encodings because byte slices starting with - // 0x01 and 0x02 return a Header { list: false, payload_length: 1 } when input to - // Header::decode. - // If the encoding includes a header, the header will be properly decoded and - // consumed. - // Otherwise, header decoding will succeed but nothing is consumed. - let _header = alloy_rlp::Header::decode(buf)?; - let tx_type = *buf.first().ok_or(alloy_rlp::Error::Custom( - "typed tx cannot be decoded from an empty slice", - ))?; - if tx_type == 0x01 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP2930) - } else if tx_type == 0x02 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP1559) - } else if tx_type == 0x7E { - buf.advance(1); - ::decode(buf).map(TypedTransaction::Deposit) - } else { - Err(alloy_rlp::Error::Custom("invalid tx type")) - } - } - Ordering::Equal => { - Err(alloy_rlp::Error::Custom("an empty list is not a valid transaction encoding")) - } - Ordering::Greater => { - as Decodable>::decode(buf).map(TypedTransaction::Legacy) - } - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TransactionEssentials { - pub kind: TxKind, - pub input: Bytes, - pub nonce: U256, - pub gas_limit: U256, - pub gas_price: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - pub value: U256, - pub chain_id: Option, - pub access_list: AccessList, -} - -/// Represents all relevant information of an executed transaction -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TransactionInfo { - pub transaction_hash: B256, - pub transaction_index: u32, - pub from: Address, - pub to: Option
, - pub contract_address: Option
, - pub logs: Vec, - pub logs_bloom: Bloom, - pub traces: Vec, - pub exit: InstructionResult, - pub out: Option, - pub nonce: u64, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedReceipt { - Legacy(ReceiptWithBloom), - EIP2930(ReceiptWithBloom), - EIP1559(ReceiptWithBloom), - Deposit(ReceiptWithBloom), -} - -impl TypedReceipt { - pub fn gas_used(&self) -> U256 { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::Deposit(r) => U256::from(r.receipt.cumulative_gas_used), - } - } - - pub fn logs_bloom(&self) -> &Bloom { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::Deposit(r) => &r.bloom, - } - } -} - -impl Encodable for TypedReceipt { - fn encode(&self, out: &mut dyn bytes::BufMut) { - use alloy_rlp::Header; - - match self { - TypedReceipt::Legacy(r) => r.encode(out), - receipt => { - let payload_len = match receipt { - TypedReceipt::EIP2930(r) => r.length() + 1, - TypedReceipt::EIP1559(r) => r.length() + 1, - TypedReceipt::Deposit(r) => r.length() + 1, - _ => unreachable!("receipt already matched"), - }; - - match receipt { - TypedReceipt::EIP2930(r) => { - Header { list: true, payload_length: payload_len }.encode(out); - 1u8.encode(out); - r.encode(out); - } - TypedReceipt::EIP1559(r) => { - Header { list: true, payload_length: payload_len }.encode(out); - 2u8.encode(out); - r.encode(out); - } - TypedReceipt::Deposit(r) => { - Header { list: true, payload_length: payload_len }.encode(out); - 0x7Eu8.encode(out); - r.encode(out); - } - _ => unreachable!("receipt already matched"), - } - } - } - } -} - -impl Decodable for TypedReceipt { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use alloy_rlp::Header; - use bytes::Buf; - use std::cmp::Ordering; - - // a receipt is either encoded as a string (non legacy) or a list (legacy). - // We should not consume the buffer if we are decoding a legacy receipt, so let's - // check if the first byte is between 0x80 and 0xbf. - let rlp_type = *buf - .first() - .ok_or(alloy_rlp::Error::Custom("cannot decode a receipt from empty bytes"))?; - - match rlp_type.cmp(&alloy_rlp::EMPTY_LIST_CODE) { - Ordering::Less => { - // strip out the string header - let _header = Header::decode(buf)?; - let receipt_type = *buf.first().ok_or(alloy_rlp::Error::Custom( - "typed receipt cannot be decoded from an empty slice", - ))?; - if receipt_type == 0x01 { - buf.advance(1); - ::decode(buf).map(TypedReceipt::EIP2930) - } else if receipt_type == 0x02 { - buf.advance(1); - ::decode(buf).map(TypedReceipt::EIP1559) - } else if receipt_type == 0x7E { - buf.advance(1); - ::decode(buf).map(TypedReceipt::Deposit) - } else { - Err(alloy_rlp::Error::Custom("invalid receipt type")) - } - } - Ordering::Equal => { - Err(alloy_rlp::Error::Custom("an empty list is not a valid receipt encoding")) - } - Ordering::Greater => { - ::decode(buf).map(TypedReceipt::Legacy) - } - } - } -} - -/// Translates an EIP-2930 access list to an alloy-rpc-types access list. -pub fn to_alloy_access_list( - access_list: alloy_eips::eip2930::AccessList, -) -> alloy_rpc_types::AccessList { - alloy_rpc_types::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_rpc_types::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) -} - -/// Translates an alloy-rpc-types access list to an EIP-2930 access list. -pub fn to_eip_access_list( - access_list: alloy_rpc_types::AccessList, -) -> alloy_eips::eip2930::AccessList { - alloy_eips::eip2930::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_eips::eip2930::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) -} - -#[cfg(test)] -mod tests { - use alloy_consensus::Receipt; - use alloy_primitives::{b256, hex, LogData, Signature}; - use std::str::FromStr; - - use super::*; - - #[test] - fn test_decode_call() { - let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..]; - let decoded = TypedTransaction::decode(&mut &bytes_first[..]).unwrap(); - let tx = TxLegacy { - nonce: 2u64, - gas_price: 1000000000u64.into(), - gas_limit: 100000u64, - to: TxKind::Call(Address::from_slice( - &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], - )), - value: U256::from(1000000000000000u64), - input: Bytes::default(), - chain_id: Some(4), - }; - - let signature = Signature::from_str("0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b").unwrap(); - - let tx = TypedTransaction::Legacy(Signed::new_unchecked( - tx.clone(), - signature, - b256!("a517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34"), - )); - - assert_eq!(tx, decoded); - } - - #[test] - fn test_decode_create_goerli() { - // test that an example create tx from goerli decodes properly - let tx_bytes = - hex::decode("02f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471") - .unwrap(); - let _decoded = - ::decode(&mut &tx_bytes[..]).unwrap(); - } - - #[test] - fn can_recover_sender() { - // random mainnet tx: https://etherscan.io/tx/0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f - let bytes = hex::decode("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9").unwrap(); - - let Ok(TypedTransaction::EIP1559(tx)) = TypedTransaction::decode(&mut &bytes[..]) else { - panic!("decoding TypedTransaction failed"); - }; - - assert_eq!( - tx.hash(), - &"0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f" - .parse::() - .unwrap() - ); - assert_eq!( - tx.recover_signer().unwrap(), - "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5".parse::
().unwrap() - ); - } - - #[test] - fn can_recover_sender_not_normalized() { - let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); - - let Ok(TypedTransaction::Legacy(tx)) = TypedTransaction::decode(&mut &bytes[..]) else { - panic!("decoding TypedTransaction failed"); - }; - - assert_eq!(tx.input, Bytes::from(b"")); - assert_eq!(tx.gas_price, 1); - assert_eq!(tx.gas_limit, 21000); - assert_eq!(tx.nonce, 0); - if let TxKind::Call(to) = tx.to { - assert_eq!( - to, - "0x095e7baea6a6c7c4c2dfeb977efac326af552d87".parse::
().unwrap() - ); - } else { - panic!("expected a call transaction"); - } - assert_eq!(tx.value, U256::from(0x0au64)); - assert_eq!( - tx.recover_signer().unwrap(), - "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".parse::
().unwrap() - ); - } - - #[test] - fn encode_legacy_receipt() { - let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); - - let mut data = vec![]; - let receipt = TypedReceipt::Legacy(ReceiptWithBloom { - receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, - logs: vec![Log { - address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), - data: LogData::new_unchecked( - vec![ - B256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - B256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - Bytes::from_str("0100ff").unwrap(), - ), - }], - }, - bloom: [0; 256].into(), - }); - - receipt.encode(&mut data); - - // check that the rlp length equals the length of the expected rlp - assert_eq!(receipt.length(), expected.len()); - assert_eq!(data, expected); - } - - #[test] - fn decode_legacy_receipt() { - let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); - - let expected = TypedReceipt::Legacy(ReceiptWithBloom { - receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, - logs: vec![Log { - address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), - data: LogData::new_unchecked( - vec![ - B256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - B256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - Bytes::from_str("0100ff").unwrap(), - ), - }], - }, - bloom: [0; 256].into(), - }); - - let receipt = TypedReceipt::decode(&mut &data[..]).unwrap(); - - assert_eq!(receipt, expected); - } -} diff --git a/crates/anvil/core/src/eth/transaction/ethers_compat.rs b/crates/anvil/core/src/eth/transaction/ethers_compat.rs deleted file mode 100644 index 50bb60b45981..000000000000 --- a/crates/anvil/core/src/eth/transaction/ethers_compat.rs +++ /dev/null @@ -1,501 +0,0 @@ -//! ethers compatibility, this is mainly necessary so we can use all of `ethers` signers - -use super::EthTransactionRequest; -use crate::eth::{ - proof::AccountProof, - transaction::{ - DepositTransactionRequest, EIP1559TransactionRequest, EIP2930TransactionRequest, - LegacyTransactionRequest, MaybeImpersonatedTransaction, TypedTransaction, - TypedTransactionRequest, - }, -}; -use alloy_primitives::{U128 as rU128, U256 as rU256, U64 as rU64}; -use alloy_rpc_types::{ - AccessList as AlloyAccessList, CallRequest, Signature, Transaction as AlloyTransaction, -}; -use ethers_core::types::{ - transaction::{ - eip2718::TypedTransaction as EthersTypedTransactionRequest, - eip2930::{AccessList, AccessListItem}, - optimism::DepositTransaction, - }, - Address, BigEndianHash, Eip1559TransactionRequest as EthersEip1559TransactionRequest, - Eip2930TransactionRequest as EthersEip2930TransactionRequest, NameOrAddress, StorageProof, - Transaction as EthersTransaction, TransactionRequest as EthersLegacyTransactionRequest, - TransactionRequest, H256, U256, U64, -}; -use foundry_common::types::{ToAlloy, ToEthers}; - -pub fn to_alloy_signature(signature: ethers_core::types::Signature) -> alloy_rpc_types::Signature { - alloy_rpc_types::Signature { - r: signature.r.to_alloy(), - s: signature.s.to_alloy(), - v: signature.v.to_alloy().to::(), - y_parity: None, - } -} - -pub fn to_ethers_signature(signature: alloy_rpc_types::Signature) -> ethers_core::types::Signature { - ethers_core::types::Signature { - r: signature.r.to_ethers(), - s: signature.s.to_ethers(), - v: signature.v.to::(), - } -} - -pub fn to_alloy_proof(proof: AccountProof) -> alloy_rpc_types::EIP1186AccountProofResponse { - alloy_rpc_types::EIP1186AccountProofResponse { - address: proof.address.to_alloy(), - account_proof: proof.account_proof.into_iter().map(|b| b.0.into()).collect(), - balance: proof.balance.to_alloy(), - code_hash: proof.code_hash.to_alloy(), - nonce: proof.nonce.to_alloy().to::(), - storage_hash: proof.storage_hash.to_alloy(), - storage_proof: proof.storage_proof.iter().map(to_alloy_storage_proof).collect(), - } -} - -pub fn to_alloy_storage_proof(proof: &StorageProof) -> alloy_rpc_types::EIP1186StorageProof { - alloy_rpc_types::EIP1186StorageProof { - key: rU256::from_be_bytes(proof.key.to_alloy().0).into(), - proof: proof.proof.iter().map(|b| b.clone().0.into()).collect(), - value: proof.value.to_alloy(), - } -} - -pub fn call_to_internal_tx_request(request: &CallRequest) -> EthTransactionRequest { - EthTransactionRequest { - from: request.from.map(|a| a.to_ethers()), - to: request.to.map(|a| a.to_ethers()), - gas_price: request.gas_price.map(|g| alloy_primitives::U256::from(g).to_ethers()), - max_fee_per_gas: request - .max_fee_per_gas - .map(|g| alloy_primitives::U256::from(g).to_ethers()), - max_priority_fee_per_gas: request - .max_priority_fee_per_gas - .map(|g| alloy_primitives::U256::from(g).to_ethers()), - gas: request.gas.map(|g| g.to_ethers()), - value: request.value.map(|v| v.to_ethers()), - data: request.input.unique_input().unwrap().map(|b| b.clone().0.into()), - nonce: request.nonce.map(|n| n.to::().into()), - chain_id: request.chain_id.map(|c| c.to::().into()), - access_list: request.access_list.clone().map(|a| to_ethers_access_list(a.clone()).0), - transaction_type: request.transaction_type.map(|t| t.to::().into()), - // TODO: Should this be none? - optimism_fields: None, - } -} - -pub fn to_ethers_access_list(access_list: AlloyAccessList) -> AccessList { - AccessList( - access_list - .0 - .into_iter() - .map(|item| AccessListItem { - address: item.address.to_ethers(), - storage_keys: item - .storage_keys - .into_iter() - .map(|k| { - BigEndianHash::from_uint(&U256::from_big_endian(k.to_ethers().as_bytes())) - }) - .collect(), - }) - .collect(), - ) -} - -pub fn from_ethers_access_list(access_list: AccessList) -> AlloyAccessList { - AlloyAccessList(access_list.0.into_iter().map(ToAlloy::to_alloy).collect()) -} - -impl From for EthersTypedTransactionRequest { - fn from(tx: TypedTransactionRequest) -> Self { - match tx { - TypedTransactionRequest::Legacy(tx) => { - let LegacyTransactionRequest { - nonce, - gas_price, - gas_limit, - kind, - value, - input, - chain_id, - } = tx; - EthersTypedTransactionRequest::Legacy(EthersLegacyTransactionRequest { - from: None, - to: kind.as_call().cloned().map(Into::into), - gas: Some(gas_limit), - gas_price: Some(gas_price), - value: Some(value), - data: Some(input), - nonce: Some(nonce), - chain_id: chain_id.map(Into::into), - }) - } - TypedTransactionRequest::EIP2930(tx) => { - let EIP2930TransactionRequest { - chain_id, - nonce, - gas_price, - gas_limit, - kind, - value, - input, - access_list, - } = tx; - EthersTypedTransactionRequest::Eip2930(EthersEip2930TransactionRequest { - tx: EthersLegacyTransactionRequest { - from: None, - to: kind.as_call().cloned().map(Into::into), - gas: Some(gas_limit), - gas_price: Some(gas_price), - value: Some(value), - data: Some(input), - nonce: Some(nonce), - chain_id: Some(chain_id.into()), - }, - access_list: access_list.into(), - }) - } - TypedTransactionRequest::EIP1559(tx) => { - let EIP1559TransactionRequest { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - kind, - value, - input, - access_list, - } = tx; - EthersTypedTransactionRequest::Eip1559(EthersEip1559TransactionRequest { - from: None, - to: kind.as_call().cloned().map(Into::into), - gas: Some(gas_limit), - value: Some(value), - data: Some(input), - nonce: Some(nonce), - access_list: access_list.into(), - max_priority_fee_per_gas: Some(max_priority_fee_per_gas), - max_fee_per_gas: Some(max_fee_per_gas), - chain_id: Some(chain_id.into()), - }) - } - TypedTransactionRequest::Deposit(tx) => { - let DepositTransactionRequest { - source_hash, - from, - kind, - mint, - value, - gas_limit, - is_system_tx, - input, - } = tx; - EthersTypedTransactionRequest::DepositTransaction(DepositTransaction { - tx: TransactionRequest { - from: Some(from), - to: kind.as_call().cloned().map(Into::into), - gas: Some(gas_limit), - value: Some(value), - data: Some(input), - gas_price: Some(0.into()), - nonce: Some(0.into()), - chain_id: None, - }, - source_hash, - mint: Some(mint), - is_system_tx, - }) - } - } - } -} - -fn to_ethers_transaction_with_hash_and_sender( - transaction: TypedTransaction, - hash: H256, - from: Address, -) -> EthersTransaction { - match transaction { - TypedTransaction::Legacy(t) => EthersTransaction { - hash, - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: None, - value: t.value, - gas_price: Some(t.gas_price), - max_fee_per_gas: Some(t.gas_price), - max_priority_fee_per_gas: Some(t.gas_price), - gas: t.gas_limit, - input: t.input.clone(), - chain_id: t.chain_id().map(Into::into), - v: t.signature.v.into(), - r: t.signature.r, - s: t.signature.s, - access_list: None, - transaction_type: None, - source_hash: H256::zero(), - mint: None, - is_system_tx: false, - other: Default::default(), - }, - TypedTransaction::EIP2930(t) => EthersTransaction { - hash, - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: None, - value: t.value, - gas_price: Some(t.gas_price), - max_fee_per_gas: Some(t.gas_price), - max_priority_fee_per_gas: Some(t.gas_price), - gas: t.gas_limit, - input: t.input.clone(), - chain_id: Some(t.chain_id.into()), - v: U64::from(t.odd_y_parity as u8), - r: U256::from(t.r.as_bytes()), - s: U256::from(t.s.as_bytes()), - access_list: Some(t.access_list), - transaction_type: Some(1u64.into()), - source_hash: H256::zero(), - mint: None, - is_system_tx: false, - other: Default::default(), - }, - TypedTransaction::EIP1559(t) => EthersTransaction { - hash, - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: None, - value: t.value, - gas_price: None, - max_fee_per_gas: Some(t.max_fee_per_gas), - max_priority_fee_per_gas: Some(t.max_priority_fee_per_gas), - gas: t.gas_limit, - input: t.input.clone(), - chain_id: Some(t.chain_id.into()), - v: U64::from(t.odd_y_parity as u8), - r: U256::from(t.r.as_bytes()), - s: U256::from(t.s.as_bytes()), - access_list: Some(t.access_list), - transaction_type: Some(2u64.into()), - source_hash: H256::zero(), - mint: None, - is_system_tx: false, - other: Default::default(), - }, - TypedTransaction::Deposit(t) => EthersTransaction { - hash, - nonce: t.nonce, - block_hash: None, - block_number: None, - transaction_index: None, - from, - to: None, - value: t.value, - gas_price: Some(0.into()), - max_fee_per_gas: Some(0.into()), - max_priority_fee_per_gas: Some(0.into()), - gas: t.gas_limit, - input: t.input.clone(), - chain_id: t.chain_id().map(Into::into), - v: 0.into(), - r: 0.into(), - s: 0.into(), - access_list: None, - transaction_type: Some(126u64.into()), - source_hash: t.source_hash, - mint: Some(t.mint), - is_system_tx: t.is_system_tx, - other: Default::default(), - }, - } -} - -fn to_alloy_transaction_with_hash_and_sender( - transaction: TypedTransaction, - hash: H256, - from: Address, -) -> AlloyTransaction { - match transaction { - TypedTransaction::Legacy(t) => AlloyTransaction { - hash: hash.to_alloy(), - nonce: t.nonce.to_alloy().to::(), - block_hash: None, - block_number: None, - transaction_index: None, - from: from.to_alloy(), - to: None, - value: t.value.to_alloy(), - gas_price: Some(t.gas_price.to_alloy().to::()), - max_fee_per_gas: Some(t.gas_price.to_alloy().to::()), - max_priority_fee_per_gas: Some(t.gas_price.to_alloy().to::()), - gas: t.gas_limit.to_alloy(), - input: t.input.clone().0.into(), - chain_id: t.chain_id().map(rU64::from), - signature: Some(Signature { - r: t.signature.r.to_alloy(), - s: t.signature.s.to_alloy(), - v: rU256::from(t.signature.v), - y_parity: None, - }), - access_list: None, - transaction_type: None, - max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], - other: Default::default(), - }, - TypedTransaction::EIP2930(t) => AlloyTransaction { - hash: hash.to_alloy(), - nonce: t.nonce.to_alloy().to::(), - block_hash: None, - block_number: None, - transaction_index: None, - from: from.to_alloy(), - to: None, - value: t.value.to_alloy(), - gas_price: Some(t.gas_price.to_alloy().to::()), - max_fee_per_gas: Some(t.gas_price.to_alloy().to::()), - max_priority_fee_per_gas: Some(t.gas_price.to_alloy().to::()), - gas: t.gas_limit.to_alloy(), - input: t.input.clone().0.into(), - chain_id: Some(rU64::from(t.chain_id)), - signature: Some(Signature { - r: rU256::from_be_bytes(t.r.to_alloy().0), - s: rU256::from_be_bytes(t.s.to_alloy().0), - v: rU256::from(t.odd_y_parity as u8), - y_parity: Some(t.odd_y_parity.into()), - }), - access_list: Some(from_ethers_access_list(t.access_list).0), - transaction_type: Some(rU64::from(1)), - max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], - other: Default::default(), - }, - TypedTransaction::EIP1559(t) => AlloyTransaction { - hash: hash.to_alloy(), - nonce: t.nonce.to_alloy().to::(), - block_hash: None, - block_number: None, - transaction_index: None, - from: from.to_alloy(), - to: None, - value: t.value.to_alloy(), - gas_price: None, - max_fee_per_gas: Some(t.max_fee_per_gas.to_alloy().to::()), - max_priority_fee_per_gas: Some(t.max_priority_fee_per_gas.to_alloy().to::()), - gas: t.gas_limit.to_alloy(), - input: t.input.clone().0.into(), - chain_id: Some(rU64::from(t.chain_id)), - signature: Some(Signature { - r: rU256::from_be_bytes(t.r.to_alloy().0), - s: rU256::from_be_bytes(t.s.to_alloy().0), - v: rU256::from(t.odd_y_parity as u8), - y_parity: Some(t.odd_y_parity.into()), - }), - access_list: Some(from_ethers_access_list(t.access_list).0), - transaction_type: Some(rU64::from(2)), - max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], - other: Default::default(), - }, - TypedTransaction::Deposit(t) => AlloyTransaction { - hash: hash.to_alloy(), - nonce: t.nonce.to_alloy().to::(), - block_hash: None, - block_number: None, - transaction_index: None, - from: from.to_alloy(), - to: None, - value: t.value.to_alloy(), - gas_price: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - gas: t.gas_limit.to_alloy(), - input: t.input.clone().0.into(), - chain_id: t.chain_id().map(rU64::from), - signature: None, - access_list: None, - transaction_type: None, - max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], - other: Default::default(), - }, - } -} - -impl From for EthersTransaction { - fn from(transaction: TypedTransaction) -> Self { - let hash = transaction.hash(); - let sender = transaction.recover().unwrap_or_default(); - to_ethers_transaction_with_hash_and_sender(transaction, hash, sender) - } -} - -impl From for EthersTransaction { - fn from(transaction: MaybeImpersonatedTransaction) -> Self { - let hash = transaction.hash(); - let sender = transaction.recover().unwrap_or_default(); - to_ethers_transaction_with_hash_and_sender(transaction.into(), hash, sender) - } -} - -impl From for AlloyTransaction { - fn from(transaction: MaybeImpersonatedTransaction) -> Self { - let hash = transaction.hash(); - let sender = transaction.recover().unwrap_or_default(); - - to_alloy_transaction_with_hash_and_sender(transaction.into(), hash, sender) - } -} - -impl From for EthTransactionRequest { - fn from(req: TransactionRequest) -> Self { - let TransactionRequest { from, to, gas, gas_price, value, data, nonce, chain_id, .. } = req; - EthTransactionRequest { - from, - to: to.and_then(|to| match to { - NameOrAddress::Name(_) => None, - NameOrAddress::Address(to) => Some(to), - }), - gas_price, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - gas, - value, - data, - nonce, - chain_id, - access_list: None, - transaction_type: None, - optimism_fields: None, - } - } -} - -impl From for TransactionRequest { - fn from(req: EthTransactionRequest) -> Self { - let EthTransactionRequest { from, to, gas_price, gas, value, data, nonce, .. } = req; - TransactionRequest { - from, - to: to.map(NameOrAddress::Address), - gas, - gas_price, - value, - data, - nonce, - chain_id: None, - } - } -} diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 4a878ddfbbf4..d3cf7aa7d82b 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1,20 +1,17 @@ -//! transaction related data +//! Transaction related types use crate::eth::{ - receipt::Log, - utils::{enveloped, to_revm_access_list}, + transaction::optimism::{DepositTransaction, DepositTransactionRequest}, + utils::eip_to_revm_access_list, }; -use ethers_core::{ - types::{ - transaction::eip2930::{AccessList, AccessListItem}, - Address, Bloom, Bytes, Signature, SignatureError, TxHash, H256, U256, U64, - }, - utils::{ - keccak256, rlp, - rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}, - }, +use alloy_consensus::{ReceiptWithBloom, TxEip1559, TxEip2930, TxLegacy}; +use alloy_network::{Signed, Transaction, TxKind}; +use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, B256, U128, U256, U64}; +use alloy_rlp::{Decodable, Encodable}; +use alloy_rpc_types::{ + request::TransactionRequest, AccessList, AccessListItem, CallRequest, + Signature as RpcSignature, Transaction as RpcTransaction, }; -use foundry_common::types::ToAlloy; use foundry_evm::traces::CallTraceNode; use revm::{ interpreter::InstructionResult, @@ -22,37 +19,15 @@ use revm::{ }; use std::ops::Deref; -pub mod alloy; -/// compatibility with `ethers-rs` types -mod ethers_compat; +use super::utils::from_eip_to_alloy_access_list; + pub mod optimism; -pub use ethers_compat::{ - call_to_internal_tx_request, from_ethers_access_list, to_alloy_proof, to_alloy_signature, - to_ethers_access_list, to_ethers_signature, -}; /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC #[cfg(feature = "impersonated-tx")] -pub const IMPERSONATED_SIGNATURE: alloy_rpc_types::Signature = alloy_rpc_types::Signature { - r: alloy_primitives::U256::ZERO, - s: alloy_primitives::U256::ZERO, - v: alloy_primitives::U256::ZERO, - y_parity: None, -}; - -/// Container type for various Ethereum transaction requests -/// -/// Its variants correspond to specific allowed transactions: -/// 1. Legacy (pre-EIP2718) [`LegacyTransactionRequest`] -/// 2. EIP2930 (state access lists) [`EIP2930TransactionRequest`] -/// 3. EIP1559 [`EIP1559TransactionRequest`] -/// 4. Deposit [`DepositTransactionRequest`] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedTransactionRequest { - Legacy(LegacyTransactionRequest), - EIP2930(EIP2930TransactionRequest), - EIP1559(EIP1559TransactionRequest), - Deposit(DepositTransactionRequest), +pub fn impersonated_signature() -> Signature { + Signature::from_scalars_and_parity(B256::with_last_byte(1), B256::with_last_byte(1), false) + .unwrap() } /// Represents _all_ transaction requests received from RPC @@ -103,17 +78,14 @@ pub struct EthTransactionRequest { #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct OptimismDepositRequestFields { /// op-stack deposit source hash - pub source_hash: H256, + pub source_hash: B256, /// op-stack deposit mint pub mint: U256, /// op-stack deposit system tx pub is_system_tx: bool, } -// == impl EthTransactionRequest == - impl EthTransactionRequest { - /// Converts the request into a [TypedTransactionRequest] pub fn into_typed_request(self) -> Option { let EthTransactionRequest { from, @@ -126,29 +98,29 @@ impl EthTransactionRequest { data, nonce, mut access_list, - chain_id, transaction_type, optimism_fields, .. } = self; - let chain_id = chain_id.map(|id| id.as_u64()); - let transaction_type = transaction_type.map(|id| id.as_u64()); - // op-stack deposit tx - if optimism_fields.is_some() && transaction_type == Some(126) { + let transaction_type = transaction_type.map(|id| id.to::()); + let data = data.clone().unwrap_or_default(); + // Special case: OP-stack deposit tx + if transaction_type == Some(126) { return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { - source_hash: optimism_fields.clone()?.source_hash, from: from.unwrap_or_default(), + source_hash: optimism_fields.clone()?.source_hash, kind: match to { - Some(to) => TransactionKind::Call(to), - None => TransactionKind::Create, + Some(to) => TxKind::Call(to), + None => TxKind::Create, }, mint: optimism_fields.clone()?.mint, value: value.unwrap_or_default(), gas_limit: gas.unwrap_or_default(), is_system_tx: optimism_fields.clone()?.is_system_tx, - input: data.clone().unwrap_or_default(), - })); + input: data, + })) } + match ( transaction_type, gas_price, @@ -158,33 +130,33 @@ impl EthTransactionRequest { ) { // legacy transaction (Some(0), _, None, None, None) | (None, Some(_), None, None, None) => { - Some(TypedTransactionRequest::Legacy(LegacyTransactionRequest { - nonce: nonce.unwrap_or(U256::zero()), - gas_price: gas_price.unwrap_or_default(), - gas_limit: gas.unwrap_or_default(), - value: value.unwrap_or(U256::zero()), - input: data.unwrap_or_default(), - kind: match to { - Some(to) => TransactionKind::Call(to), - None => TransactionKind::Create, + Some(TypedTransactionRequest::Legacy(TxLegacy { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data, + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, }, - chain_id, + chain_id: None, })) } // EIP2930 (Some(1), _, None, None, _) | (None, _, None, None, Some(_)) => { - Some(TypedTransactionRequest::EIP2930(EIP2930TransactionRequest { - nonce: nonce.unwrap_or(U256::zero()), - gas_price: gas_price.unwrap_or_default(), - gas_limit: gas.unwrap_or_default(), - value: value.unwrap_or(U256::zero()), - input: data.unwrap_or_default(), - kind: match to { - Some(to) => TransactionKind::Call(to), - None => TransactionKind::Create, + Some(TypedTransactionRequest::EIP2930(TxEip2930 { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data, + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, }, - chain_id: chain_id.unwrap_or_default(), - access_list: access_list.unwrap_or_default(), + chain_id: 0, + access_list: to_eip_access_list(AccessList(access_list.unwrap_or_default())), })) } // EIP1559 @@ -193,19 +165,21 @@ impl EthTransactionRequest { (None, None, _, Some(_), _) | (None, None, None, None, None) => { // Empty fields fall back to the canonical transaction schema. - Some(TypedTransactionRequest::EIP1559(EIP1559TransactionRequest { - nonce: nonce.unwrap_or(U256::zero()), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or(U256::zero()), - gas_limit: gas.unwrap_or_default(), - value: value.unwrap_or(U256::zero()), - input: data.unwrap_or_default(), - kind: match to { - Some(to) => TransactionKind::Call(to), - None => TransactionKind::Create, + Some(TypedTransactionRequest::EIP1559(TxEip1559 { + nonce: nonce.unwrap_or_default().to::(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), + max_priority_fee_per_gas: max_priority_fee_per_gas + .unwrap_or_default() + .to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data, + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, }, - chain_id: chain_id.unwrap_or_default(), - access_list: access_list.unwrap_or_default(), + chain_id: 0, + access_list: to_eip_access_list(AccessList(access_list.unwrap_or_default())), })) } _ => None, @@ -213,317 +187,200 @@ impl EthTransactionRequest { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TransactionKind { - Call(Address), - Create, -} - -// == impl TransactionKind == - -impl TransactionKind { - /// If this transaction is a call this returns the address of the callee - pub fn as_call(&self) -> Option<&Address> { - match self { - TransactionKind::Call(to) => Some(to), - TransactionKind::Create => None, - } - } -} - -impl Encodable for TransactionKind { - fn rlp_append(&self, s: &mut RlpStream) { - match self { - TransactionKind::Call(address) => { - s.encoder().encode_value(&address[..]); - } - TransactionKind::Create => s.encoder().encode_value(&[]), - } - } -} - -impl Decodable for TransactionKind { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_empty() { - if rlp.is_data() { - Ok(TransactionKind::Create) - } else { - Err(DecoderError::RlpExpectedToBeData) - } - } else { - Ok(TransactionKind::Call(rlp.as_val()?)) - } - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Encodable for TransactionKind { - fn length(&self) -> usize { - match self { - TransactionKind::Call(to) => to.length(), - TransactionKind::Create => ([]).length(), - } - } - fn encode(&self, out: &mut dyn open_fastrlp::BufMut) { - match self { - TransactionKind::Call(to) => to.encode(out), - TransactionKind::Create => ([]).encode(out), +pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + let TransactionRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + gas, + value, + data, + nonce, + mut access_list, + transaction_type, + other, + .. + } = tx; + let transaction_type = transaction_type.map(|id| id.to::()); + + // Special case: OP-stack deposit tx + if transaction_type == Some(126) { + return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { + from: from.unwrap_or_default(), + source_hash: other.get_deserialized::("sourceHash")?.ok()?, + kind: TxKind::Create, + mint: other.get_deserialized::("mint")?.ok()?, + value: value.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), + is_system_tx: other.get_deserialized::("isSystemTx")?.ok()?, + input: data.unwrap_or_default(), + })) + } + + match ( + transaction_type, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + access_list.take(), + ) { + // legacy transaction + (Some(0), _, None, None, None) | (None, Some(_), None, None, None) => { + Some(TypedTransactionRequest::Legacy(TxLegacy { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data.unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: None, + })) } - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Decodable for TransactionKind { - fn decode(buf: &mut &[u8]) -> Result { - use bytes::Buf; - - if let Some(&first) = buf.first() { - if first == 0x80 { - buf.advance(1); - Ok(TransactionKind::Create) - } else { - let addr =
::decode(buf)?; - Ok(TransactionKind::Call(addr)) - } - } else { - Err(open_fastrlp::DecodeError::InputTooShort) + // EIP2930 + (Some(1), _, None, None, _) | (None, _, None, None, Some(_)) => { + Some(TypedTransactionRequest::EIP2930(TxEip2930 { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data.unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: 0, + access_list: to_eip_access_list(access_list.unwrap_or_default()), + })) } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -pub struct EIP2930TransactionRequest { - pub chain_id: u64, - pub nonce: U256, - pub gas_price: U256, - pub gas_limit: U256, - pub kind: TransactionKind, - pub value: U256, - pub input: Bytes, - pub access_list: Vec, -} - -impl EIP2930TransactionRequest { - pub fn hash(&self) -> H256 { - let encoded = rlp::encode(self); - let mut out = vec![0; 1 + encoded.len()]; - out[0] = 1; - out[1..].copy_from_slice(&encoded); - H256::from_slice(keccak256(&out).as_slice()) - } -} - -impl From for EIP2930TransactionRequest { - fn from(tx: EIP2930Transaction) -> Self { - Self { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_price: tx.gas_price, - gas_limit: tx.gas_limit, - kind: tx.kind, - value: tx.value, - input: tx.input, - access_list: tx.access_list.0, + // EIP1559 + (Some(2), None, _, _, _) | + (None, None, Some(_), _, _) | + (None, None, _, Some(_), _) | + (None, None, None, None, None) => { + // Empty fields fall back to the canonical transaction schema. + Some(TypedTransactionRequest::EIP1559(TxEip1559 { + nonce: nonce.unwrap_or_default().to::(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: data.unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: 0, + access_list: to_eip_access_list(access_list.unwrap_or_default()), + })) } + _ => None, } } -impl Encodable for EIP2930TransactionRequest { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(8); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append_list(&self.access_list); - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LegacyTransactionRequest { - pub nonce: U256, - pub gas_price: U256, - pub gas_limit: U256, - pub kind: TransactionKind, - pub value: U256, - pub input: Bytes, - pub chain_id: Option, -} - -// == impl LegacyTransactionRequest == - -impl LegacyTransactionRequest { - pub fn hash(&self) -> H256 { - H256::from_slice(keccak256(&rlp::encode(self)).as_slice()) - } -} - -impl From for LegacyTransactionRequest { - fn from(tx: LegacyTransaction) -> Self { - let chain_id = tx.chain_id(); - Self { - nonce: tx.nonce, - gas_price: tx.gas_price, - gas_limit: tx.gas_limit, - kind: tx.kind, - value: tx.value, - input: tx.input, - chain_id, +pub fn call_request_to_typed(tx: CallRequest) -> Option { + let CallRequest { + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + gas, + value, + input, + chain_id, + nonce, + mut access_list, + transaction_type, + .. + } = tx; + let chain_id = chain_id.map(|id| id.to::()); + let transaction_type = transaction_type.map(|id| id.to::()); + + match ( + transaction_type, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + access_list.take(), + ) { + // legacy transaction + (Some(0), _, None, None, None) | (None, Some(_), None, None, None) => { + Some(TypedTransactionRequest::Legacy(TxLegacy { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id, + })) } - } -} - -impl Encodable for LegacyTransactionRequest { - fn rlp_append(&self, s: &mut RlpStream) { - if let Some(chain_id) = self.chain_id { - s.begin_list(9); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append(&chain_id); - s.append(&0u8); - s.append(&0u8); - } else { - s.begin_list(6); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); + // EIP2930 + (Some(1), _, None, None, _) | (None, _, None, None, Some(_)) => { + Some(TypedTransactionRequest::EIP2930(TxEip2930 { + nonce: nonce.unwrap_or_default().to::(), + gas_price: gas_price.unwrap_or_default().to(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: chain_id.unwrap_or_default(), + access_list: to_eip_access_list(access_list.unwrap_or_default()), + })) } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -pub struct EIP1559TransactionRequest { - pub chain_id: u64, - pub nonce: U256, - pub max_priority_fee_per_gas: U256, - pub max_fee_per_gas: U256, - pub gas_limit: U256, - pub kind: TransactionKind, - pub value: U256, - pub input: Bytes, - pub access_list: Vec, -} - -// == impl EIP1559TransactionRequest == - -impl EIP1559TransactionRequest { - pub fn hash(&self) -> H256 { - let encoded = rlp::encode(self); - let mut out = vec![0; 1 + encoded.len()]; - out[0] = 2; - out[1..].copy_from_slice(&encoded); - H256::from_slice(keccak256(&out).as_slice()) - } -} - -impl From for EIP1559TransactionRequest { - fn from(t: EIP1559Transaction) -> Self { - Self { - chain_id: t.chain_id, - nonce: t.nonce, - max_priority_fee_per_gas: t.max_priority_fee_per_gas, - max_fee_per_gas: t.max_fee_per_gas, - gas_limit: t.gas_limit, - kind: t.kind, - value: t.value, - input: t.input, - access_list: t.access_list.0, + // EIP1559 + (Some(2), None, _, _, _) | + (None, None, Some(_), _, _) | + (None, None, _, Some(_), _) | + (None, None, None, None, None) => { + // Empty fields fall back to the canonical transaction schema. + Some(TypedTransactionRequest::EIP1559(TxEip1559 { + nonce: nonce.unwrap_or_default().to::(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: input.try_into_unique_input().unwrap_or_default().unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: chain_id.unwrap_or_default(), + access_list: to_eip_access_list(access_list.unwrap_or_default()), + })) } - } -} - -impl Encodable for EIP1559TransactionRequest { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(9); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.max_priority_fee_per_gas); - s.append(&self.max_fee_per_gas); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append_list(&self.access_list); + _ => None, } } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct DepositTransactionRequest { - pub from: Address, - pub source_hash: H256, - pub kind: TransactionKind, - pub mint: U256, - pub value: U256, - pub gas_limit: U256, - pub is_system_tx: bool, - pub input: Bytes, -} - -// == impl DepositTransactionRequest == - -impl DepositTransactionRequest { - pub fn hash(&self) -> H256 { - H256::from_slice(keccak256(&rlp::encode(self)).as_slice()) - } -} - -impl From for DepositTransactionRequest { - fn from(tx: DepositTransaction) -> Self { - Self { - from: tx.from, - source_hash: tx.source_hash, - kind: tx.kind, - mint: tx.mint, - value: tx.value, - gas_limit: tx.gas_limit, - is_system_tx: tx.is_system_tx, - input: tx.input, - } - } -} - -impl Encodable for DepositTransactionRequest { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(8); - s.append(&self.from); - s.append(&self.source_hash); - s.append(&self.kind); - s.append(&self.mint); - s.append(&self.value); - s.append(&self.gas_limit); - s.append(&self.is_system_tx); - s.append(&self.input.as_ref()); - } +pub enum TypedTransactionRequest { + Legacy(TxLegacy), + EIP2930(TxEip2930), + EIP1559(TxEip1559), + Deposit(DepositTransactionRequest), } -/// A wrapper for `TypedTransaction` that allows impersonating accounts. +/// A wrapper for [TypedTransaction] that allows impersonating accounts. /// /// This is a helper that carries the `impersonated` sender so that the right hash /// [TypedTransaction::impersonated_hash] can be created. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MaybeImpersonatedTransaction { - #[cfg_attr(feature = "serde", serde(flatten))] pub transaction: TypedTransaction, - #[cfg_attr(feature = "serde", serde(skip))] pub impersonated_sender: Option
, } -// === impl MaybeImpersonatedTransaction === - impl MaybeImpersonatedTransaction { /// Creates a new wrapper for the given transaction pub fn new(transaction: TypedTransaction) -> Self { @@ -540,7 +397,7 @@ impl MaybeImpersonatedTransaction { /// Note: this is feature gated so it does not conflict with the `Deref`ed /// [TypedTransaction::recover] function by default. #[cfg(feature = "impersonated-tx")] - pub fn recover(&self) -> Result { + pub fn recover(&self) -> Result { if let Some(sender) = self.impersonated_sender { return Ok(sender) } @@ -552,7 +409,7 @@ impl MaybeImpersonatedTransaction { /// Note: this is feature gated so it does not conflict with the `Deref`ed /// [TypedTransaction::hash] function by default. #[cfg(feature = "impersonated-tx")] - pub fn hash(&self) -> H256 { + pub fn hash(&self) -> B256 { if self.transaction.is_impersonated() { if let Some(sender) = self.impersonated_sender { return self.transaction.impersonated_hash(sender) @@ -563,8 +420,8 @@ impl MaybeImpersonatedTransaction { } impl Encodable for MaybeImpersonatedTransaction { - fn rlp_append(&self, s: &mut RlpStream) { - self.transaction.rlp_append(s) + fn encode(&self, out: &mut dyn bytes::BufMut) { + self.transaction.encode(out) } } @@ -581,26 +438,8 @@ impl From for MaybeImpersonatedTransaction { } impl Decodable for MaybeImpersonatedTransaction { - fn decode(rlp: &Rlp) -> Result { - let transaction = TypedTransaction::decode(rlp)?; - Ok(Self { transaction, impersonated_sender: None }) - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Encodable for MaybeImpersonatedTransaction { - fn encode(&self, out: &mut dyn open_fastrlp::BufMut) { - self.transaction.encode(out) - } - fn length(&self) -> usize { - self.transaction.length() - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Decodable for MaybeImpersonatedTransaction { - fn decode(buf: &mut &[u8]) -> Result { - Ok(Self { transaction: open_fastrlp::Decodable::decode(buf)?, impersonated_sender: None }) + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + TypedTransaction::decode(buf).map(MaybeImpersonatedTransaction::new) } } @@ -618,123 +457,400 @@ impl Deref for MaybeImpersonatedTransaction { } } -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TypedTransaction { - /// Legacy transaction type - Legacy(LegacyTransaction), - /// EIP-2930 transaction - EIP2930(EIP2930Transaction), - /// EIP-1559 transaction - EIP1559(EIP1559Transaction), - /// op-stack deposit transaction - Deposit(DepositTransaction), +impl From for RpcTransaction { + fn from(value: MaybeImpersonatedTransaction) -> Self { + let hash = value.hash(); + let sender = value.recover().unwrap_or_default(); + to_alloy_transaction_with_hash_and_sender(value.transaction, hash, sender) + } } -// == impl TypedTransaction == - -impl TypedTransaction { - /// Returns true if the transaction uses dynamic fees: EIP1559 - pub fn is_dynamic_fee(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) +pub fn to_alloy_transaction_with_hash_and_sender( + transaction: TypedTransaction, + hash: B256, + from: Address, +) -> RpcTransaction { + match transaction { + TypedTransaction::Legacy(t) => RpcTransaction { + hash, + nonce: U64::from(t.nonce), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: None, + value: t.value, + gas_price: Some(U128::from(t.gas_price)), + max_fee_per_gas: Some(U128::from(t.gas_price)), + max_priority_fee_per_gas: Some(U128::from(t.gas_price)), + gas: U256::from(t.gas_limit), + input: t.input.clone(), + chain_id: t.chain_id.map(U64::from), + signature: Some(RpcSignature { + r: t.signature().r(), + s: t.signature().s(), + v: U256::from(t.signature().v().y_parity_byte()), + y_parity: None, + }), + access_list: None, + transaction_type: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: vec![], + other: Default::default(), + }, + TypedTransaction::EIP2930(t) => RpcTransaction { + hash, + nonce: U64::from(t.nonce), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: None, + value: t.value, + gas_price: Some(U128::from(t.gas_price)), + max_fee_per_gas: Some(U128::from(t.gas_price)), + max_priority_fee_per_gas: Some(U128::from(t.gas_price)), + gas: U256::from(t.gas_limit), + input: t.input.clone(), + chain_id: Some(U64::from(t.chain_id)), + signature: Some(RpcSignature { + r: t.signature().r(), + s: t.signature().s(), + v: U256::from(t.signature().v().y_parity_byte()), + y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), + }), + access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), + transaction_type: Some(U64::from(1)), + max_fee_per_blob_gas: None, + blob_versioned_hashes: vec![], + other: Default::default(), + }, + TypedTransaction::EIP1559(t) => RpcTransaction { + hash, + nonce: U64::from(t.nonce), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: None, + value: t.value, + gas_price: None, + max_fee_per_gas: Some(U128::from(t.max_fee_per_gas)), + max_priority_fee_per_gas: Some(U128::from(t.max_priority_fee_per_gas)), + gas: U256::from(t.gas_limit), + input: t.input.clone(), + chain_id: Some(U64::from(t.chain_id)), + signature: Some(RpcSignature { + r: t.signature().r(), + s: t.signature().s(), + v: U256::from(t.signature().v().y_parity_byte()), + y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), + }), + access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), + transaction_type: Some(U64::from(2)), + max_fee_per_blob_gas: None, + blob_versioned_hashes: vec![], + other: Default::default(), + }, + TypedTransaction::Deposit(t) => RpcTransaction { + hash, + nonce: U64::from(t.nonce), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: None, + value: t.value, + gas_price: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + gas: U256::from(t.gas_limit), + input: t.input.clone().0.into(), + chain_id: t.chain_id().map(U64::from), + signature: None, + access_list: None, + transaction_type: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: vec![], + other: Default::default(), + }, } +} - pub fn gas_price(&self) -> U256 { - match self { - TypedTransaction::Legacy(tx) => tx.gas_price, - TypedTransaction::EIP2930(tx) => tx.gas_price, - TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, - TypedTransaction::Deposit(_) => U256::from(0), - } - } +/// Queued transaction +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PendingTransaction { + /// The actual transaction + pub transaction: MaybeImpersonatedTransaction, + /// the recovered sender of this transaction + sender: Address, + /// hash of `transaction`, so it can easily be reused with encoding and hashing agan + hash: TxHash, +} - pub fn gas_limit(&self) -> U256 { - match self { - TypedTransaction::Legacy(tx) => tx.gas_limit, - TypedTransaction::EIP2930(tx) => tx.gas_limit, - TypedTransaction::EIP1559(tx) => tx.gas_limit, - TypedTransaction::Deposit(tx) => tx.gas_limit, - } +impl PendingTransaction { + pub fn new(transaction: TypedTransaction) -> Result { + let sender = transaction.recover()?; + let hash = transaction.hash(); + Ok(Self { transaction: MaybeImpersonatedTransaction::new(transaction), sender, hash }) } - pub fn value(&self) -> U256 { - match self { - TypedTransaction::Legacy(tx) => tx.value, - TypedTransaction::EIP2930(tx) => tx.value, - TypedTransaction::EIP1559(tx) => tx.value, - TypedTransaction::Deposit(tx) => tx.value, + #[cfg(feature = "impersonated-tx")] + pub fn with_impersonated(transaction: TypedTransaction, sender: Address) -> Self { + let hash = transaction.impersonated_hash(sender); + Self { + transaction: MaybeImpersonatedTransaction::impersonated(transaction, sender), + sender, + hash, } } - pub fn data(&self) -> &Bytes { - match self { - TypedTransaction::Legacy(tx) => &tx.input, - TypedTransaction::EIP2930(tx) => &tx.input, - TypedTransaction::EIP1559(tx) => &tx.input, - TypedTransaction::Deposit(tx) => &tx.input, - } + pub fn nonce(&self) -> U256 { + self.transaction.nonce() } - /// Returns the transaction type - pub fn r#type(&self) -> Option { - match self { - TypedTransaction::Legacy(_) => None, - TypedTransaction::EIP2930(_) => Some(1), - TypedTransaction::EIP1559(_) => Some(2), - TypedTransaction::Deposit(_) => Some(0x7E), - } + pub fn hash(&self) -> &TxHash { + &self.hash } - /// Max cost of the transaction - pub fn max_cost(&self) -> U256 { - self.gas_limit().saturating_mul(self.gas_price()) + pub fn sender(&self) -> &Address { + &self.sender } - /// Returns a helper type that contains commonly used values as fields - pub fn essentials(&self) -> TransactionEssentials { - match self { - TypedTransaction::Legacy(t) => TransactionEssentials { - kind: t.kind, - input: t.input.clone(), - nonce: t.nonce, - gas_limit: t.gas_limit, - gas_price: Some(t.gas_price), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - value: t.value, - chain_id: t.chain_id(), - access_list: Default::default(), - }, - TypedTransaction::EIP2930(t) => TransactionEssentials { - kind: t.kind, - input: t.input.clone(), - nonce: t.nonce, - gas_limit: t.gas_limit, - gas_price: Some(t.gas_price), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: t.access_list.clone(), - }, - TypedTransaction::EIP1559(t) => TransactionEssentials { - kind: t.kind, - input: t.input.clone(), - nonce: t.nonce, - gas_limit: t.gas_limit, - gas_price: None, - max_fee_per_gas: Some(t.max_fee_per_gas), - max_priority_fee_per_gas: Some(t.max_priority_fee_per_gas), - value: t.value, - chain_id: Some(t.chain_id), - access_list: t.access_list.clone(), - }, - TypedTransaction::Deposit(t) => TransactionEssentials { - kind: t.kind, - input: t.input.clone(), - nonce: t.nonce, - gas_limit: t.gas_limit, - gas_price: Some(U256::from(0)), + /// Converts the [PendingTransaction] into the [TxEnv] context that [`revm`](foundry_evm) + /// expects. + pub fn to_revm_tx_env(&self) -> TxEnv { + fn transact_to(kind: &TxKind) -> TransactTo { + match kind { + TxKind::Call(c) => TransactTo::Call(*c), + TxKind::Create => TransactTo::Create(CreateScheme::Create), + } + } + + let caller = *self.sender(); + match &self.transaction.transaction { + TypedTransaction::Legacy(tx) => { + let chain_id = tx.chain_id(); + let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx(); + TxEnv { + caller, + transact_to: transact_to(to), + data: alloy_primitives::Bytes(input.0.clone()), + chain_id, + nonce: Some(*nonce), + value: (*value), + gas_price: U256::from(*gas_price), + gas_priority_fee: None, + gas_limit: *gas_limit, + access_list: vec![], + ..Default::default() + } + } + TypedTransaction::EIP2930(tx) => { + let TxEip2930 { + chain_id, + nonce, + gas_price, + gas_limit, + to, + value, + input, + access_list, + .. + } = tx.tx(); + TxEnv { + caller, + transact_to: transact_to(to), + data: alloy_primitives::Bytes(input.0.clone()), + chain_id: Some(*chain_id), + nonce: Some(*nonce), + value: *value, + gas_price: U256::from(*gas_price), + gas_priority_fee: None, + gas_limit: *gas_limit, + access_list: eip_to_revm_access_list(access_list.0.clone()), + ..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(); + TxEnv { + caller, + transact_to: transact_to(to), + data: alloy_primitives::Bytes(input.0.clone()), + chain_id: Some(*chain_id), + nonce: Some(*nonce), + value: *value, + gas_price: U256::from(*max_fee_per_gas), + gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), + gas_limit: *gas_limit, + access_list: eip_to_revm_access_list(access_list.0.clone()), + ..Default::default() + } + } + TypedTransaction::Deposit(tx) => { + let chain_id = tx.chain_id(); + let DepositTransaction { + nonce, + source_hash, + gas_limit, + value, + kind, + mint, + input, + is_system_tx, + .. + } = tx; + TxEnv { + caller, + transact_to: transact_to(kind), + data: alloy_primitives::Bytes(input.0.clone()), + chain_id, + nonce: Some(nonce.to::()), + value: *value, + gas_price: U256::ZERO, + gas_priority_fee: None, + gas_limit: gas_limit.to::(), + access_list: vec![], + optimism: OptimismFields { + source_hash: Some(*source_hash), + mint: Some(mint.to::()), + is_system_transaction: Some(*is_system_tx), + enveloped_tx: None, + }, + ..Default::default() + } + } + } + } +} + +/// Container type for signed, typed transactions. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TypedTransaction { + /// Legacy transaction type + Legacy(Signed), + /// EIP-2930 transaction + EIP2930(Signed), + /// EIP-1559 transaction + EIP1559(Signed), + /// op-stack deposit transaction + Deposit(DepositTransaction), +} + +impl TypedTransaction { + /// Returns true if the transaction uses dynamic fees: EIP1559 + pub fn is_dynamic_fee(&self) -> bool { + matches!(self, TypedTransaction::EIP1559(_)) + } + + pub fn gas_price(&self) -> U256 { + U256::from(match self { + TypedTransaction::Legacy(tx) => tx.gas_price, + TypedTransaction::EIP2930(tx) => tx.gas_price, + TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, + TypedTransaction::Deposit(_) => 0, + }) + } + + pub fn gas_limit(&self) -> U256 { + U256::from(match self { + TypedTransaction::Legacy(tx) => tx.gas_limit, + TypedTransaction::EIP2930(tx) => tx.gas_limit, + TypedTransaction::EIP1559(tx) => tx.gas_limit, + TypedTransaction::Deposit(tx) => tx.gas_limit.to::(), + }) + } + + pub fn value(&self) -> U256 { + U256::from(match self { + TypedTransaction::Legacy(tx) => tx.value, + TypedTransaction::EIP2930(tx) => tx.value, + TypedTransaction::EIP1559(tx) => tx.value, + TypedTransaction::Deposit(tx) => tx.value, + }) + } + + pub fn data(&self) -> &Bytes { + match self { + TypedTransaction::Legacy(tx) => &tx.input, + TypedTransaction::EIP2930(tx) => &tx.input, + TypedTransaction::EIP1559(tx) => &tx.input, + TypedTransaction::Deposit(tx) => &tx.input, + } + } + + /// Returns the transaction type + pub fn r#type(&self) -> Option { + match self { + TypedTransaction::Legacy(_) => None, + TypedTransaction::EIP2930(_) => Some(1), + TypedTransaction::EIP1559(_) => Some(2), + TypedTransaction::Deposit(_) => Some(0x7E), + } + } + + /// Max cost of the transaction + pub fn max_cost(&self) -> U256 { + self.gas_limit().saturating_mul(self.gas_price()) + } + + /// Returns a helper type that contains commonly used values as fields + pub fn essentials(&self) -> TransactionEssentials { + match self { + TypedTransaction::Legacy(t) => TransactionEssentials { + kind: t.tx().to, + input: t.input.clone(), + nonce: U256::from(t.tx().nonce), + gas_limit: U256::from(t.tx().gas_limit), + gas_price: Some(U256::from(t.tx().gas_price)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + value: t.value, + chain_id: t.tx().chain_id, + access_list: Default::default(), + }, + TypedTransaction::EIP2930(t) => TransactionEssentials { + kind: t.tx().to, + input: t.input.clone(), + nonce: U256::from(t.tx().nonce), + gas_limit: U256::from(t.tx().gas_limit), + gas_price: Some(U256::from(t.tx().gas_price)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + value: t.value, + chain_id: Some(t.chain_id), + access_list: to_alloy_access_list(t.access_list.clone()), + }, + TypedTransaction::EIP1559(t) => TransactionEssentials { + kind: t.to, + input: t.input.clone(), + nonce: U256::from(t.nonce), + gas_limit: U256::from(t.gas_limit), + gas_price: None, + max_fee_per_gas: Some(U256::from(t.max_fee_per_gas)), + max_priority_fee_per_gas: Some(U256::from(t.max_priority_fee_per_gas)), + value: t.value, + chain_id: Some(t.chain_id), + access_list: to_alloy_access_list(t.access_list.clone()), + }, + TypedTransaction::Deposit(t) => TransactionEssentials { + kind: t.kind, + input: t.input.clone(), + nonce: t.nonce, + gas_limit: t.gas_limit, + gas_price: Some(U256::from(0)), max_fee_per_gas: None, max_priority_fee_per_gas: None, value: t.value, @@ -744,25 +860,25 @@ impl TypedTransaction { } } - pub fn nonce(&self) -> &U256 { + pub fn nonce(&self) -> U256 { match self { - TypedTransaction::Legacy(t) => t.nonce(), - TypedTransaction::EIP2930(t) => t.nonce(), - TypedTransaction::EIP1559(t) => t.nonce(), - TypedTransaction::Deposit(t) => t.nonce(), + TypedTransaction::Legacy(t) => U256::from(t.nonce), + TypedTransaction::EIP2930(t) => U256::from(t.nonce), + TypedTransaction::EIP1559(t) => U256::from(t.nonce), + TypedTransaction::Deposit(t) => U256::from(t.nonce), } } pub fn chain_id(&self) -> Option { match self { - TypedTransaction::Legacy(t) => t.chain_id(), + TypedTransaction::Legacy(t) => t.chain_id, TypedTransaction::EIP2930(t) => Some(t.chain_id), TypedTransaction::EIP1559(t) => Some(t.chain_id), TypedTransaction::Deposit(t) => t.chain_id(), } } - pub fn as_legacy(&self) -> Option<&LegacyTransaction> { + pub fn as_legacy(&self) -> Option<&Signed> { match self { TypedTransaction::Legacy(tx) => Some(tx), _ => None, @@ -783,11 +899,11 @@ impl TypedTransaction { /// /// Note: If this transaction has the Impersonated signature then this returns a modified unique /// hash. This allows us to treat impersonated transactions as unique. - pub fn hash(&self) -> H256 { + pub fn hash(&self) -> B256 { match self { - TypedTransaction::Legacy(t) => t.hash(), - TypedTransaction::EIP2930(t) => t.hash(), - TypedTransaction::EIP1559(t) => t.hash(), + TypedTransaction::Legacy(t) => *t.hash(), + TypedTransaction::EIP2930(t) => *t.hash(), + TypedTransaction::EIP1559(t) => *t.hash(), TypedTransaction::Deposit(t) => t.hash(), } } @@ -795,168 +911,83 @@ impl TypedTransaction { /// Returns true if the transaction was impersonated (using the impersonate Signature) #[cfg(feature = "impersonated-tx")] pub fn is_impersonated(&self) -> bool { - to_alloy_signature(self.signature()) == IMPERSONATED_SIGNATURE + self.signature() == impersonated_signature() } /// Returns the hash if the transaction is impersonated (using a fake signature) /// /// This appends the `address` before hashing it #[cfg(feature = "impersonated-tx")] - pub fn impersonated_hash(&self, sender: Address) -> H256 { - let mut bytes = rlp::encode(self); - bytes.extend_from_slice(sender.as_ref()); - H256::from_slice(keccak256(&bytes).as_slice()) + pub fn impersonated_hash(&self, sender: Address) -> B256 { + let mut buffer = Vec::::new(); + Encodable::encode(self, &mut buffer); + buffer.extend_from_slice(sender.as_ref()); + B256::from_slice(alloy_primitives::utils::keccak256(&buffer).as_slice()) } /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { + pub fn recover(&self) -> Result { match self { - TypedTransaction::Legacy(tx) => tx.recover(), - TypedTransaction::EIP2930(tx) => tx.recover(), - TypedTransaction::EIP1559(tx) => tx.recover(), + TypedTransaction::Legacy(tx) => tx.recover_signer(), + TypedTransaction::EIP2930(tx) => tx.recover_signer(), + TypedTransaction::EIP1559(tx) => tx.recover_signer(), TypedTransaction::Deposit(tx) => tx.recover(), } } /// Returns what kind of transaction this is - pub fn kind(&self) -> &TransactionKind { + pub fn kind(&self) -> &TxKind { match self { - TypedTransaction::Legacy(tx) => &tx.kind, - TypedTransaction::EIP2930(tx) => &tx.kind, - TypedTransaction::EIP1559(tx) => &tx.kind, + TypedTransaction::Legacy(tx) => &tx.to, + TypedTransaction::EIP2930(tx) => &tx.to, + TypedTransaction::EIP1559(tx) => &tx.to, TypedTransaction::Deposit(tx) => &tx.kind, } } /// Returns the callee if this transaction is a call - pub fn to(&self) -> Option<&Address> { - self.kind().as_call() + pub fn to(&self) -> Option
{ + self.kind().to() } /// Returns the Signature of the transaction pub fn signature(&self) -> Signature { match self { - TypedTransaction::Legacy(tx) => tx.signature, - TypedTransaction::EIP2930(tx) => { - let v = tx.odd_y_parity as u8; - let r = U256::from_big_endian(&tx.r[..]); - let s = U256::from_big_endian(&tx.s[..]); - Signature { r, s, v: v.into() } - } - TypedTransaction::EIP1559(tx) => { - let v = tx.odd_y_parity as u8; - let r = U256::from_big_endian(&tx.r[..]); - let s = U256::from_big_endian(&tx.s[..]); - Signature { r, s, v: v.into() } - } - TypedTransaction::Deposit(_) => Signature { r: U256::zero(), s: U256::zero(), v: 0 }, + TypedTransaction::Legacy(tx) => *tx.signature(), + TypedTransaction::EIP2930(tx) => *tx.signature(), + TypedTransaction::EIP1559(tx) => *tx.signature(), + TypedTransaction::Deposit(_) => Signature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + false, + ) + .unwrap(), } } } impl Encodable for TypedTransaction { - fn rlp_append(&self, s: &mut RlpStream) { - match self { - TypedTransaction::Legacy(tx) => tx.rlp_append(s), - TypedTransaction::EIP2930(tx) => enveloped(1, tx, s), - TypedTransaction::EIP1559(tx) => enveloped(2, tx, s), - TypedTransaction::Deposit(tx) => enveloped(0x7E, tx, s), - } - } -} - -impl Decodable for TypedTransaction { - fn decode(rlp: &Rlp) -> Result { - if rlp.is_list() { - return Ok(TypedTransaction::Legacy(rlp.as_val()?)) - } - let [first, s @ ..] = rlp.data()? else { - return Err(DecoderError::Custom("empty slice")); - }; - // "advance" the header, see comments in fastrlp impl below - let s = if s.is_empty() { &rlp.as_raw()[1..] } else { s }; - - match *first { - 0x01 => rlp::decode(s).map(TypedTransaction::EIP2930), - 0x02 => rlp::decode(s).map(TypedTransaction::EIP1559), - 0x7E => rlp::decode(s).map(TypedTransaction::Deposit), - _ => Err(DecoderError::Custom("invalid tx type")), - } - } -} - -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Encodable for TypedTransaction { - fn encode(&self, out: &mut dyn open_fastrlp::BufMut) { + fn encode(&self, out: &mut dyn bytes::BufMut) { match self { TypedTransaction::Legacy(tx) => tx.encode(out), - tx => { - let payload_len = match tx { - TypedTransaction::EIP2930(tx) => tx.length() + 1, - TypedTransaction::EIP1559(tx) => tx.length() + 1, - TypedTransaction::Deposit(tx) => tx.length() + 1, - _ => unreachable!("legacy tx length already matched"), - }; - - match tx { - TypedTransaction::EIP2930(tx) => { - let tx_string_header = - open_fastrlp::Header { list: false, payload_length: payload_len }; - - tx_string_header.encode(out); - out.put_u8(0x01); - tx.encode(out); - } - TypedTransaction::EIP1559(tx) => { - let tx_string_header = - open_fastrlp::Header { list: false, payload_length: payload_len }; - - tx_string_header.encode(out); - out.put_u8(0x02); - tx.encode(out); - } - TypedTransaction::Deposit(tx) => { - let tx_string_header = - open_fastrlp::Header { list: false, payload_length: payload_len }; - - tx_string_header.encode(out); - out.put_u8(0x7E); - tx.encode(out); - } - _ => unreachable!("legacy tx encode already matched"), - } - } - } - } - fn length(&self) -> usize { - match self { - TypedTransaction::Legacy(tx) => tx.length(), - tx => { - let payload_len = match tx { - TypedTransaction::EIP2930(tx) => tx.length() + 1, - TypedTransaction::EIP1559(tx) => tx.length() + 1, - TypedTransaction::Deposit(tx) => tx.length() + 1, - _ => unreachable!("legacy tx length already matched"), - }; - // we include a string header for signed types txs, so include the length here - payload_len + open_fastrlp::length_of_length(payload_len) - } + TypedTransaction::EIP2930(tx) => tx.encode(out), + TypedTransaction::EIP1559(tx) => tx.encode(out), + TypedTransaction::Deposit(tx) => tx.encode(out), } } } -#[cfg(feature = "fastrlp")] -impl open_fastrlp::Decodable for TypedTransaction { - fn decode(buf: &mut &[u8]) -> Result { +impl Decodable for TypedTransaction { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { use bytes::Buf; use std::cmp::Ordering; - let first = *buf.first().ok_or(open_fastrlp::DecodeError::Custom("empty slice"))?; + let first = *buf.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; // a signed transaction is either encoded as a string (non legacy) or a list (legacy). // We should not consume the buffer if we are decoding a legacy transaction, so let's // check if the first byte is between 0x80 and 0xbf. - match first.cmp(&open_fastrlp::EMPTY_LIST_CODE) { + match first.cmp(&alloy_rlp::EMPTY_LIST_CODE) { Ordering::Less => { // strip out the string header // NOTE: typed transaction encodings either contain a "rlp header" which contains @@ -969,581 +1000,277 @@ impl open_fastrlp::Decodable for TypedTransaction { // If the encoding includes a header, the header will be properly decoded and // consumed. // Otherwise, header decoding will succeed but nothing is consumed. - let _header = open_fastrlp::Header::decode(buf)?; - let tx_type = *buf.first().ok_or(open_fastrlp::DecodeError::Custom( + let _header = alloy_rlp::Header::decode(buf)?; + let tx_type = *buf.first().ok_or(alloy_rlp::Error::Custom( "typed tx cannot be decoded from an empty slice", ))?; if tx_type == 0x01 { buf.advance(1); - ::decode(buf) - .map(TypedTransaction::EIP2930) + as Decodable>::decode(buf).map(TypedTransaction::EIP2930) } else if tx_type == 0x02 { buf.advance(1); - ::decode(buf) - .map(TypedTransaction::EIP1559) + as Decodable>::decode(buf).map(TypedTransaction::EIP1559) } else if tx_type == 0x7E { buf.advance(1); - ::decode(buf) - .map(TypedTransaction::Deposit) + ::decode(buf).map(TypedTransaction::Deposit) } else { - Err(open_fastrlp::DecodeError::Custom("invalid tx type")) + Err(alloy_rlp::Error::Custom("invalid tx type")) } } - Ordering::Equal => Err(open_fastrlp::DecodeError::Custom( - "an empty list is not a valid transaction encoding", - )), - Ordering::Greater => ::decode(buf) - .map(TypedTransaction::Legacy), + Ordering::Equal => { + Err(alloy_rlp::Error::Custom("an empty list is not a valid transaction encoding")) + } + Ordering::Greater => { + as Decodable>::decode(buf).map(TypedTransaction::Legacy) + } } } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct LegacyTransaction { +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TransactionEssentials { + pub kind: TxKind, + pub input: Bytes, pub nonce: U256, - pub gas_price: U256, pub gas_limit: U256, - pub kind: TransactionKind, + pub gas_price: Option, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, pub value: U256, - pub input: Bytes, - pub signature: Signature, + pub chain_id: Option, + pub access_list: AccessList, } -impl LegacyTransaction { - pub fn nonce(&self) -> &U256 { - &self.nonce - } - - pub fn hash(&self) -> H256 { - H256::from_slice(keccak256(&rlp::encode(self)).as_slice()) - } +/// Represents all relevant information of an executed transaction +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TransactionInfo { + pub transaction_hash: B256, + pub transaction_index: u32, + pub from: Address, + pub to: Option
, + pub contract_address: Option
, + pub logs: Vec, + pub logs_bloom: Bloom, + pub traces: Vec, + pub exit: InstructionResult, + pub out: Option, + pub nonce: u64, +} - /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { - self.signature.recover(LegacyTransactionRequest::from(self.clone()).hash()) - } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TypedReceipt { + Legacy(ReceiptWithBloom), + EIP2930(ReceiptWithBloom), + EIP1559(ReceiptWithBloom), + Deposit(ReceiptWithBloom), +} - pub fn chain_id(&self) -> Option { - if self.signature.v > 36 { - Some((self.signature.v - 35) / 2) - } else { - None +impl TypedReceipt { + pub fn gas_used(&self) -> U256 { + match self { + TypedReceipt::Legacy(r) | + TypedReceipt::EIP1559(r) | + TypedReceipt::EIP2930(r) | + TypedReceipt::Deposit(r) => U256::from(r.receipt.cumulative_gas_used), } } - /// See - /// > If you do, then the v of the signature MUST be set to {0,1} + CHAIN_ID * 2 + 35 where - /// > {0,1} is the parity of the y value of the curve point for which r is the x-value in the - /// > secp256k1 signing process. - pub fn meets_eip155(&self, chain_id: u64) -> bool { - let double_chain_id = chain_id.saturating_mul(2); - let v = self.signature.v; - v == double_chain_id + 35 || v == double_chain_id + 36 + pub fn logs_bloom(&self) -> &Bloom { + match self { + TypedReceipt::Legacy(r) | + TypedReceipt::EIP1559(r) | + TypedReceipt::EIP2930(r) | + TypedReceipt::Deposit(r) => &r.bloom, + } } } -impl Encodable for LegacyTransaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(9); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append(&self.signature.v); - s.append(&self.signature.r); - s.append(&self.signature.s); +impl From for ReceiptWithBloom { + fn from(val: TypedReceipt) -> Self { + match val { + TypedReceipt::Legacy(r) | + TypedReceipt::EIP1559(r) | + TypedReceipt::EIP2930(r) | + TypedReceipt::Deposit(r) => r, + } } } -impl Decodable for LegacyTransaction { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? != 9 { - return Err(DecoderError::RlpIncorrectListLen) - } +impl Encodable for TypedReceipt { + fn encode(&self, out: &mut dyn bytes::BufMut) { + use alloy_rlp::Header; - let v = rlp.val_at(6)?; - let r = rlp.val_at::(7)?; - let s = rlp.val_at::(8)?; - - Ok(Self { - nonce: rlp.val_at(0)?, - gas_price: rlp.val_at(1)?, - gas_limit: rlp.val_at(2)?, - kind: rlp.val_at(3)?, - value: rlp.val_at(4)?, - input: rlp.val_at::>(5)?.into(), - signature: Signature { v, r, s }, - }) + match self { + TypedReceipt::Legacy(r) => r.encode(out), + receipt => { + let payload_len = match receipt { + TypedReceipt::EIP2930(r) => r.length() + 1, + TypedReceipt::EIP1559(r) => r.length() + 1, + TypedReceipt::Deposit(r) => r.length() + 1, + _ => unreachable!("receipt already matched"), + }; + + match receipt { + TypedReceipt::EIP2930(r) => { + Header { list: true, payload_length: payload_len }.encode(out); + 1u8.encode(out); + r.encode(out); + } + TypedReceipt::EIP1559(r) => { + Header { list: true, payload_length: payload_len }.encode(out); + 2u8.encode(out); + r.encode(out); + } + TypedReceipt::Deposit(r) => { + Header { list: true, payload_length: payload_len }.encode(out); + 0x7Eu8.encode(out); + r.encode(out); + } + _ => unreachable!("receipt already matched"), + } + } + } } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct EIP2930Transaction { - pub chain_id: u64, - pub nonce: U256, - pub gas_price: U256, - pub gas_limit: U256, - pub kind: TransactionKind, - pub value: U256, - pub input: Bytes, - pub access_list: AccessList, - pub odd_y_parity: bool, - pub r: H256, - pub s: H256, -} - -impl EIP2930Transaction { - pub fn nonce(&self) -> &U256 { - &self.nonce - } - - pub fn hash(&self) -> H256 { - let encoded = rlp::encode(self); - let mut out = vec![0; 1 + encoded.len()]; - out[0] = 1; - out[1..].copy_from_slice(&encoded); - H256::from_slice(keccak256(&out).as_slice()) - } - - /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { - let mut sig = [0u8; 65]; - sig[0..32].copy_from_slice(&self.r[..]); - sig[32..64].copy_from_slice(&self.s[..]); - sig[64] = self.odd_y_parity as u8; - let signature = Signature::try_from(&sig[..])?; - signature.recover(EIP2930TransactionRequest::from(self.clone()).hash()) - } -} - -impl Encodable for EIP2930Transaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(11); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append(&self.access_list); - s.append(&self.odd_y_parity); - s.append(&U256::from_big_endian(&self.r[..])); - s.append(&U256::from_big_endian(&self.s[..])); - } -} - -impl Decodable for EIP2930Transaction { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? != 11 { - return Err(DecoderError::RlpIncorrectListLen) - } - - Ok(Self { - chain_id: rlp.val_at(0)?, - nonce: rlp.val_at(1)?, - gas_price: rlp.val_at(2)?, - gas_limit: rlp.val_at(3)?, - kind: rlp.val_at(4)?, - value: rlp.val_at(5)?, - input: rlp.val_at::>(6)?.into(), - access_list: rlp.val_at(7)?, - odd_y_parity: rlp.val_at(8)?, - r: { - let mut rarr = [0u8; 32]; - rlp.val_at::(9)?.to_big_endian(&mut rarr); - H256::from(rarr) - }, - s: { - let mut sarr = [0u8; 32]; - rlp.val_at::(10)?.to_big_endian(&mut sarr); - H256::from(sarr) - }, - }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct EIP1559Transaction { - pub chain_id: u64, - pub nonce: U256, - pub max_priority_fee_per_gas: U256, - pub max_fee_per_gas: U256, - pub gas_limit: U256, - pub kind: TransactionKind, - pub value: U256, - pub input: Bytes, - pub access_list: AccessList, - pub odd_y_parity: bool, - pub r: H256, - pub s: H256, -} - -impl EIP1559Transaction { - pub fn nonce(&self) -> &U256 { - &self.nonce - } - - pub fn hash(&self) -> H256 { - let encoded = rlp::encode(self); - let mut out = vec![0; 1 + encoded.len()]; - out[0] = 2; - out[1..].copy_from_slice(&encoded); - H256::from_slice(keccak256(&out).as_slice()) - } - - /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { - let mut sig = [0u8; 65]; - sig[0..32].copy_from_slice(&self.r[..]); - sig[32..64].copy_from_slice(&self.s[..]); - sig[64] = self.odd_y_parity as u8; - let signature = Signature::try_from(&sig[..])?; - signature.recover(EIP1559TransactionRequest::from(self.clone()).hash()) - } -} +impl Decodable for TypedReceipt { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + use alloy_rlp::Header; + use bytes::Buf; + use std::cmp::Ordering; -impl Encodable for EIP1559Transaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(12); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.max_priority_fee_per_gas); - s.append(&self.max_fee_per_gas); - s.append(&self.gas_limit); - s.append(&self.kind); - s.append(&self.value); - s.append(&self.input.as_ref()); - s.append(&self.access_list); - s.append(&self.odd_y_parity); - s.append(&U256::from_big_endian(&self.r[..])); - s.append(&U256::from_big_endian(&self.s[..])); - } -} + // a receipt is either encoded as a string (non legacy) or a list (legacy). + // We should not consume the buffer if we are decoding a legacy receipt, so let's + // check if the first byte is between 0x80 and 0xbf. + let rlp_type = *buf + .first() + .ok_or(alloy_rlp::Error::Custom("cannot decode a receipt from empty bytes"))?; -impl Decodable for EIP1559Transaction { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? != 12 { - return Err(DecoderError::RlpIncorrectListLen) + match rlp_type.cmp(&alloy_rlp::EMPTY_LIST_CODE) { + Ordering::Less => { + // strip out the string header + let _header = Header::decode(buf)?; + let receipt_type = *buf.first().ok_or(alloy_rlp::Error::Custom( + "typed receipt cannot be decoded from an empty slice", + ))?; + if receipt_type == 0x01 { + buf.advance(1); + ::decode(buf).map(TypedReceipt::EIP2930) + } else if receipt_type == 0x02 { + buf.advance(1); + ::decode(buf).map(TypedReceipt::EIP1559) + } else if receipt_type == 0x7E { + buf.advance(1); + ::decode(buf).map(TypedReceipt::Deposit) + } else { + Err(alloy_rlp::Error::Custom("invalid receipt type")) + } + } + Ordering::Equal => { + Err(alloy_rlp::Error::Custom("an empty list is not a valid receipt encoding")) + } + Ordering::Greater => { + ::decode(buf).map(TypedReceipt::Legacy) + } } - - Ok(Self { - chain_id: rlp.val_at(0)?, - nonce: rlp.val_at(1)?, - max_priority_fee_per_gas: rlp.val_at(2)?, - max_fee_per_gas: rlp.val_at(3)?, - gas_limit: rlp.val_at(4)?, - kind: rlp.val_at(5)?, - value: rlp.val_at(6)?, - input: rlp.val_at::>(7)?.into(), - access_list: rlp.val_at(8)?, - odd_y_parity: rlp.val_at(9)?, - r: { - let mut rarr = [0u8; 32]; - rlp.val_at::(10)?.to_big_endian(&mut rarr); - H256::from(rarr) - }, - s: { - let mut sarr = [0u8; 32]; - rlp.val_at::(11)?.to_big_endian(&mut sarr); - H256::from(sarr) - }, - }) } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - -pub struct DepositTransaction { - pub nonce: U256, - pub source_hash: H256, - pub from: Address, - pub kind: TransactionKind, - pub mint: U256, - pub value: U256, - pub gas_limit: U256, - pub is_system_tx: bool, - pub input: Bytes, +/// Translates an EIP-2930 access list to an alloy-rpc-types access list. +pub fn to_alloy_access_list( + access_list: alloy_eips::eip2930::AccessList, +) -> alloy_rpc_types::AccessList { + alloy_rpc_types::AccessList( + access_list + .0 + .into_iter() + .map(|item| alloy_rpc_types::AccessListItem { + address: item.address, + storage_keys: item.storage_keys, + }) + .collect(), + ) } -impl DepositTransaction { - pub fn nonce(&self) -> &U256 { - &self.nonce - } - - pub fn hash(&self) -> H256 { - H256::from_slice(keccak256(&rlp::encode(self)).as_slice()) - } - - /// Recovers the Ethereum address which was used to sign the transaction. - pub fn recover(&self) -> Result { - Ok(self.from) - } - - pub fn chain_id(&self) -> Option { - None - } +/// Translates an alloy-rpc-types access list to an EIP-2930 access list. +pub fn to_eip_access_list( + access_list: alloy_rpc_types::AccessList, +) -> alloy_eips::eip2930::AccessList { + alloy_eips::eip2930::AccessList( + access_list + .0 + .into_iter() + .map(|item| alloy_eips::eip2930::AccessListItem { + address: item.address, + storage_keys: item.storage_keys, + }) + .collect(), + ) } -impl Encodable for DepositTransaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(9); - s.append(&self.nonce); - s.append(&self.source_hash); - s.append(&self.from); - s.append(&self.kind); - s.append(&self.mint); - s.append(&self.value); - s.append(&self.gas_limit); - s.append(&self.is_system_tx); - s.append(&self.input.as_ref()); - } -} - -impl Decodable for DepositTransaction { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? != 8 { - return Err(DecoderError::RlpIncorrectListLen) - } - - Ok(Self { - source_hash: rlp.val_at(0)?, - from: rlp.val_at(1)?, - kind: rlp.val_at(2)?, - mint: rlp.val_at(3)?, - value: rlp.val_at(4)?, - gas_limit: rlp.val_at(5)?, - is_system_tx: rlp.val_at(6)?, - input: rlp.val_at::>(7)?.into(), - nonce: U256::from(0), - }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TransactionEssentials { - pub kind: TransactionKind, - pub input: Bytes, - pub nonce: U256, - pub gas_limit: U256, - pub gas_price: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - pub value: U256, - pub chain_id: Option, - pub access_list: AccessList, -} - -/// Queued transaction -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PendingTransaction { - /// The actual transaction - pub transaction: MaybeImpersonatedTransaction, - /// the recovered sender of this transaction - sender: Address, - /// hash of `transaction`, so it can easily be reused with encoding and hashing agan - hash: TxHash, -} +#[cfg(test)] +mod tests { + use alloy_consensus::Receipt; + use alloy_primitives::{b256, hex, LogData, Signature}; + use std::str::FromStr; -// == impl PendingTransaction == + use super::*; -impl PendingTransaction { - /// Creates a new pending transaction and tries to verify transaction and recover sender. - pub fn new(transaction: TypedTransaction) -> Result { - let sender = transaction.recover()?; - Ok(Self { hash: transaction.hash(), transaction: transaction.into(), sender }) - } + #[test] + fn test_decode_call() { + let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..]; + let decoded = TypedTransaction::decode(&mut &bytes_first[..]).unwrap(); - /// Creates a new transaction with the given sender. - /// - /// In order to prevent collisions from multiple different impersonated accounts, we update the - /// transaction's hash with the address to make it unique. - /// - /// See: - #[cfg(feature = "impersonated-tx")] - pub fn with_impersonated(transaction: TypedTransaction, sender: Address) -> Self { - let hash = transaction.impersonated_hash(sender); - let transaction = MaybeImpersonatedTransaction::impersonated(transaction, sender); - Self { hash, transaction, sender } - } + let tx = TxLegacy { + nonce: 2u64, + gas_price: 1000000000u64.into(), + gas_limit: 100000u64, + to: TxKind::Call(Address::from_slice( + &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], + )), + value: U256::from(1000000000000000u64), + input: Bytes::default(), + chain_id: Some(4), + }; - pub fn nonce(&self) -> &U256 { - self.transaction.nonce() - } + let signature = Signature::from_str("0eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca182b").unwrap(); - pub fn hash(&self) -> &TxHash { - &self.hash - } + let tx = TypedTransaction::Legacy(Signed::new_unchecked( + tx.clone(), + signature, + b256!("a517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34"), + )); - pub fn sender(&self) -> &Address { - &self.sender + assert_eq!(tx, decoded); } - /// Converts the [PendingTransaction] into the [TxEnv] context that [`revm`](foundry_evm) - /// expects. - pub fn to_revm_tx_env(&self) -> TxEnv { - fn transact_to(kind: &TransactionKind) -> TransactTo { - match kind { - TransactionKind::Call(c) => TransactTo::Call((*c).to_alloy()), - TransactionKind::Create => TransactTo::Create(CreateScheme::Create), - } - } - - let caller = *self.sender(); - match &self.transaction.transaction { - TypedTransaction::Legacy(tx) => { - let chain_id = tx.chain_id(); - let LegacyTransaction { nonce, gas_price, gas_limit, value, kind, input, .. } = tx; - TxEnv { - caller: caller.to_alloy(), - transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id, - nonce: Some(nonce.as_u64()), - value: (*value).to_alloy(), - gas_price: (*gas_price).to_alloy(), - gas_priority_fee: None, - gas_limit: gas_limit.as_u64(), - access_list: vec![], - ..Default::default() - } - } - TypedTransaction::EIP2930(tx) => { - let EIP2930Transaction { - chain_id, - nonce, - gas_price, - gas_limit, - kind, - value, - input, - access_list, - .. - } = tx; - TxEnv { - caller: (caller).to_alloy(), - transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id: Some(*chain_id), - nonce: Some(nonce.as_u64()), - value: (*value).to_alloy(), - gas_price: (*gas_price).to_alloy(), - gas_priority_fee: None, - gas_limit: gas_limit.as_u64(), - access_list: to_revm_access_list(access_list.0.clone()), - ..Default::default() - } - } - TypedTransaction::EIP1559(tx) => { - let EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - kind, - value, - input, - access_list, - .. - } = tx; - TxEnv { - caller: (caller).to_alloy(), - transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id: Some(*chain_id), - nonce: Some(nonce.as_u64()), - value: (*value).to_alloy(), - gas_price: (*max_fee_per_gas).to_alloy(), - gas_priority_fee: Some((*max_priority_fee_per_gas).to_alloy()), - gas_limit: gas_limit.as_u64(), - access_list: to_revm_access_list(access_list.0.clone()), - ..Default::default() - } - } - TypedTransaction::Deposit(tx) => { - let chain_id = tx.chain_id(); - let DepositTransaction { - nonce, - source_hash, - gas_limit, - value, - kind, - mint, - input, - is_system_tx, - .. - } = tx; - TxEnv { - caller: caller.to_alloy(), - transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), - chain_id, - nonce: Some(nonce.as_u64()), - value: (*value).to_alloy(), - gas_price: 0.to_alloy(), - gas_priority_fee: None, - gas_limit: gas_limit.as_u64(), - access_list: vec![], - optimism: OptimismFields { - source_hash: Some(source_hash.to_alloy()), - mint: Some(mint.as_u128()), - is_system_transaction: Some(*is_system_tx), - enveloped_tx: None, - }, - ..Default::default() - } - } - } + #[test] + fn test_decode_create_goerli() { + // test that an example create tx from goerli decodes properly + let tx_bytes = + hex::decode("02f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471") + .unwrap(); + let _decoded = TypedTransaction::decode(&mut &tx_bytes[..]).unwrap(); } -} - -/// Represents all relevant information of an executed transaction -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TransactionInfo { - pub transaction_hash: H256, - pub transaction_index: u32, - pub from: Address, - pub to: Option
, - pub contract_address: Option
, - pub logs: Vec, - pub logs_bloom: Bloom, - pub traces: Vec, - pub exit: InstructionResult, - pub out: Option, - pub nonce: u64, -} - -#[cfg(test)] -mod tests { - use super::*; - use ethers_core::utils::hex; #[test] fn can_recover_sender() { // random mainnet tx: https://etherscan.io/tx/0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f let bytes = hex::decode("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9").unwrap(); - let Ok(TypedTransaction::EIP1559(tx)) = rlp::decode(&bytes) else { + let Ok(TypedTransaction::EIP1559(tx)) = TypedTransaction::decode(&mut &bytes[..]) else { panic!("decoding TypedTransaction failed"); }; + assert_eq!( tx.hash(), - "0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f".parse().unwrap() + &"0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f" + .parse::() + .unwrap() ); assert_eq!( - tx.recover().unwrap(), - "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5".parse().unwrap() + tx.recover_signer().unwrap(), + "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5".parse::
().unwrap() ); } @@ -1551,278 +1278,95 @@ mod tests { fn can_recover_sender_not_normalized() { let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); - let tx: TypedTransaction = rlp::decode(&bytes).expect("decoding TypedTransaction failed"); - let tx = match tx { - TypedTransaction::Legacy(tx) => tx, - _ => panic!("Invalid typed transaction"), + let Ok(TypedTransaction::Legacy(tx)) = TypedTransaction::decode(&mut &bytes[..]) else { + panic!("decoding TypedTransaction failed"); }; + assert_eq!(tx.input, Bytes::from(b"")); - assert_eq!(tx.gas_price, U256::from(0x01u64)); - assert_eq!(tx.gas_limit, U256::from(0x5208u64)); - assert_eq!(tx.nonce, U256::from(0x00u64)); - if let TransactionKind::Call(ref to) = tx.kind { - assert_eq!(*to, "095e7baea6a6c7c4c2dfeb977efac326af552d87".parse().unwrap()); + assert_eq!(tx.gas_price, 1); + assert_eq!(tx.gas_limit, 21000); + assert_eq!(tx.nonce, 0); + if let TxKind::Call(to) = tx.to { + assert_eq!( + to, + "0x095e7baea6a6c7c4c2dfeb977efac326af552d87".parse::
().unwrap() + ); } else { - panic!(); + panic!("expected a call transaction"); } assert_eq!(tx.value, U256::from(0x0au64)); assert_eq!( - tx.recover().unwrap(), - "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".parse().unwrap() + tx.recover_signer().unwrap(), + "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".parse::
().unwrap() ); } #[test] - #[cfg(feature = "fastrlp")] - fn test_decode_fastrlp_create() { - use bytes::BytesMut; - use open_fastrlp::Encodable; - - // tests that a contract creation tx encodes and decodes properly - - let tx = TypedTransaction::EIP2930(EIP2930Transaction { - chain_id: 1u64, - nonce: U256::from(0), - gas_price: U256::from(1), - gas_limit: U256::from(2), - kind: TransactionKind::Create, - value: U256::from(3), - input: Bytes::from(vec![1, 2]), - odd_y_parity: true, - r: H256::default(), - s: H256::default(), - access_list: vec![].into(), - }); - - let mut encoded = BytesMut::new(); - tx.encode(&mut encoded); - - let decoded = - ::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } - - #[test] - #[cfg(feature = "fastrlp")] - fn test_decode_fastrlp_create_goerli() { - // test that an example create tx from goerli decodes properly - let tx_bytes = - hex::decode("02f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471") - .unwrap(); - let _decoded = - ::decode(&mut &tx_bytes[..]).unwrap(); - } - - #[test] - #[cfg(feature = "fastrlp")] - fn test_decode_fastrlp_call() { - use bytes::BytesMut; - use open_fastrlp::Encodable; - - let tx = TypedTransaction::EIP2930(EIP2930Transaction { - chain_id: 1u64, - nonce: U256::from(0), - gas_price: U256::from(1), - gas_limit: U256::from(2), - kind: TransactionKind::Call(Address::default()), - value: U256::from(3), - input: Bytes::from(vec![1, 2]), - odd_y_parity: true, - r: H256::default(), - s: H256::default(), - access_list: vec![].into(), + fn encode_legacy_receipt() { + let expected = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); + + let mut data = vec![]; + let receipt = TypedReceipt::Legacy(ReceiptWithBloom { + receipt: Receipt { + success: false, + cumulative_gas_used: 0x1u64, + logs: vec![Log { + address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), + data: LogData::new_unchecked( + vec![ + B256::from_str( + "000000000000000000000000000000000000000000000000000000000000dead", + ) + .unwrap(), + B256::from_str( + "000000000000000000000000000000000000000000000000000000000000beef", + ) + .unwrap(), + ], + Bytes::from_str("0100ff").unwrap(), + ), + }], + }, + bloom: [0; 256].into(), }); - let mut encoded = BytesMut::new(); - tx.encode(&mut encoded); - - let decoded = - ::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } + receipt.encode(&mut data); - #[test] - #[cfg(feature = "fastrlp")] - fn decode_transaction_consumes_buffer() { - let bytes = &mut &hex::decode("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469").unwrap()[..]; - let _transaction_res = - ::decode(bytes).unwrap(); - assert_eq!( - bytes.len(), - 0, - "did not consume all bytes in the buffer, {:?} remaining", - bytes.len() - ); + // check that the rlp length equals the length of the expected rlp + assert_eq!(receipt.length(), expected.len()); + assert_eq!(data, expected); } #[test] - #[cfg(feature = "fastrlp")] - fn decode_multiple_network_txs() { - use std::str::FromStr; - - let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..]; - let expected = TypedTransaction::Legacy(LegacyTransaction { - nonce: 2u64.into(), - gas_price: 1000000000u64.into(), - gas_limit: 100000u64.into(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], - )), - value: 1000000000000000u64.into(), - input: Bytes::default(), - signature: Signature { - v: 43, - r: U256::from_str( - "eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae", - ) - .unwrap(), - s: U256::from_str( - "3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18", - ) - .unwrap(), + fn decode_legacy_receipt() { + let data = hex::decode("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff").unwrap(); + + let expected = TypedReceipt::Legacy(ReceiptWithBloom { + receipt: Receipt { + success: false, + cumulative_gas_used: 0x1u64, + logs: vec![Log { + address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), + data: LogData::new_unchecked( + vec![ + B256::from_str( + "000000000000000000000000000000000000000000000000000000000000dead", + ) + .unwrap(), + B256::from_str( + "000000000000000000000000000000000000000000000000000000000000beef", + ) + .unwrap(), + ], + Bytes::from_str("0100ff").unwrap(), + ), + }], }, + bloom: [0; 256].into(), }); - assert_eq!( - expected, - ::decode(bytes_first).unwrap() - ); - let bytes_second = &mut &hex::decode("f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da").unwrap()[..]; - let expected = TypedTransaction::Legacy(LegacyTransaction { - nonce: 1u64.into(), - gas_price: 1000000000u64.into(), - gas_limit: 100000u64.into(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], - )), - value: 693361000000000u64.into(), - input: Bytes::default(), - signature: Signature { - v: 43, - r: U256::from_str( - "e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a", - ) - .unwrap(), - s: U256::from_str( - "5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da", - ) - .unwrap(), - }, - }); - assert_eq!( - expected, - ::decode(bytes_second).unwrap() - ); - - let bytes_third = &mut &hex::decode("f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88").unwrap()[..]; - let expected = TypedTransaction::Legacy(LegacyTransaction { - nonce: 3u64.into(), - gas_price: 2000000000u64.into(), - gas_limit: 10000000u64.into(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], - )), - value: 1000000000000000u64.into(), - input: Bytes::default(), - signature: Signature { - v: 43, - r: U256::from_str( - "ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071", - ) - .unwrap(), - s: U256::from_str( - "3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88", - ) - .unwrap(), - }, - }); - assert_eq!( - expected, - ::decode(bytes_third).unwrap() - ); - - let bytes_fourth = &mut &hex::decode("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469").unwrap()[..]; - let expected = TypedTransaction::EIP1559(EIP1559Transaction { - chain_id: 4, - nonce: 26u64.into(), - max_priority_fee_per_gas: 1500000000u64.into(), - max_fee_per_gas: 1500000013u64.into(), - gas_limit: 21000u64.into(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("61815774383099e24810ab832a5b2a5425c154d5").unwrap()[..], - )), - value: 3000000000000000000u64.into(), - input: Bytes::default(), - access_list: AccessList::default(), - odd_y_parity: true, - r: H256::from_str("59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd") - .unwrap(), - s: H256::from_str("016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469") - .unwrap(), - }); - assert_eq!( - expected, - ::decode(bytes_fourth).unwrap() - ); - - let bytes_fifth = &mut &hex::decode("f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860").unwrap()[..]; - let expected = TypedTransaction::Legacy(LegacyTransaction { - nonce: 15u64.into(), - gas_price: 2200000000u64.into(), - gas_limit: 34811u64.into(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("cf7f9e66af820a19257a2108375b180b0ec49167").unwrap()[..], - )), - value: 1234u64.into(), - input: Bytes::default(), - signature: Signature { - v: 44, - r: U256::from_str( - "35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981", - ) - .unwrap(), - s: U256::from_str( - "612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860", - ) - .unwrap(), - }, - }); - assert_eq!( - expected, - ::decode(bytes_fifth).unwrap() - ); - - let bytes_sixth = &mut &hex::decode("b8587ef85507a0000000000000000000000000000000000000000000000000000000000000000094cf7f9e66af820a19257a2108375b180b0ec491679461815774383099e24810ab832a5b2a5425c154d5808230398287fb0180").unwrap()[..]; - let expected: TypedTransaction = TypedTransaction::Deposit(DepositTransaction { - nonce: 7u64.into(), - source_hash: H256::from_str( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - from: "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(), - kind: TransactionKind::Call(Address::from_slice( - &hex::decode("61815774383099e24810ab832a5b2a5425c154d5").unwrap()[..], - )), - mint: U256::zero(), - value: 12345u64.into(), - gas_limit: 34811u64.into(), - input: Bytes::default(), - is_system_tx: true, - }); - assert_eq!( - expected, - ::decode(bytes_sixth).unwrap() - ); - } - - // - #[test] - fn test_recover_legacy_tx() { - let raw_tx = "f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8"; + let receipt = TypedReceipt::decode(&mut &data[..]).unwrap(); - let tx: TypedTransaction = rlp::decode(&hex::decode(raw_tx).unwrap()).unwrap(); - let recovered = tx.recover().unwrap(); - let expected: Address = "0xa12e1462d0ced572f396f58b6e2d03894cd7c8a4".parse().unwrap(); - assert_eq!(expected, recovered); + assert_eq!(receipt, expected); } } diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index f6b3b47e13be..614ba15b81d6 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -1,11 +1,15 @@ -use alloy_network::TxKind; -use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_rlp::{Decodable, Encodable, Error as DecodeError, Header as RlpHeader}; +use alloy_consensus::TxType; +use alloy_network::{Transaction, TxKind}; +use alloy_primitives::{Address, Bytes, ChainId, Signature, B256, U256}; +use alloy_rlp::{ + length_of_length, Decodable, Encodable, Error as DecodeError, Header as RlpHeader, +}; +use std::mem; #[derive(Clone, Debug, PartialEq, Eq)] pub struct DepositTransactionRequest { - pub from: Address, pub source_hash: B256, + pub from: Address, pub kind: TxKind, pub mint: U256, pub value: U256, @@ -44,6 +48,187 @@ impl DepositTransactionRequest { len += self.input.length(); len } + + /// Decodes the inner [DepositTransactionRequest] fields from RLP bytes. + /// + /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following + /// RLP fields in the following order: + /// + /// - `source_hash` + /// - `from` + /// - `kind` + /// - `mint` + /// - `value` + /// - `gas_limit` + /// - `is_system_tx` + /// - `input` + pub fn decode_inner(buf: &mut &[u8]) -> Result { + Ok(Self { + source_hash: Decodable::decode(buf)?, + from: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + mint: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + is_system_tx: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + }) + } + + /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating + /// hash that for eip2718 does not require rlp header + pub(crate) fn encode_with_signature( + &self, + signature: &Signature, + out: &mut dyn alloy_rlp::BufMut, + ) { + let payload_length = self.fields_len() + signature.rlp_vrs_len(); + let header = alloy_rlp::Header { list: true, payload_length }; + header.encode(out); + self.encode_fields(out); + signature.write_rlp_vrs(out); + } + + /// Output the length of the RLP signed transaction encoding, _without_ a RLP string header. + pub fn payload_len_with_signature_without_header(&self, signature: &Signature) -> usize { + let payload_length = self.fields_len() + signature.rlp_vrs_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Output the length of the RLP signed transaction encoding. This encodes with a RLP header. + pub fn payload_len_with_signature(&self, signature: &Signature) -> usize { + let len = self.payload_len_with_signature_without_header(signature); + length_of_length(len) + len + } + + /// Get transaction type + pub(crate) const fn tx_type(&self) -> TxType { + TxType::Eip1559 + } + + /// Calculates a heuristic for the in-memory size of the [DepositTransaction] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // source_hash + mem::size_of::
() + // from + self.kind.size() + // to + mem::size_of::() + // mint + mem::size_of::() + // value + mem::size_of::() + // gas_limit + mem::size_of::() + // is_system_transaction + self.input.len() // input + } + + /// Encodes the legacy transaction in RLP for signing. + pub(crate) fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { + out.put_u8(self.tx_type() as u8); + alloy_rlp::Header { list: true, payload_length: self.fields_len() }.encode(out); + self.encode_fields(out); + } + + /// Outputs the length of the signature RLP encoding for the transaction. + pub(crate) fn payload_len_for_signature(&self) -> usize { + let payload_length = self.fields_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Outputs the signature hash of the transaction by first encoding without a signature, then + /// hashing. + pub(crate) fn signature_hash(&self) -> B256 { + let mut buf = Vec::with_capacity(self.payload_len_for_signature()); + self.encode_for_signing(&mut buf); + alloy_primitives::utils::keccak256(&buf) + } +} + +impl Transaction for DepositTransactionRequest { + type Signature = Signature; + + fn chain_id(&self) -> Option { + None + } + + fn gas_limit(&self) -> u64 { + self.gas_limit.to::() + } + + fn nonce(&self) -> u64 { + u64::MAX + } + + fn decode_signed(buf: &mut &[u8]) -> alloy_rlp::Result> + where + Self: Sized, + { + let header = alloy_rlp::Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + + let tx = Self::decode_inner(buf)?; + let signature = Signature::decode_rlp_vrs(buf)?; + + Ok(tx.into_signed(signature)) + } + + fn encode_signed(&self, signature: &Signature, out: &mut dyn bytes::BufMut) { + self.encode_with_signature(signature, out) + } + + fn gas_price(&self) -> Option { + None + } + + fn input(&self) -> &[u8] { + &self.input + } + + fn input_mut(&mut self) -> &mut Bytes { + &mut self.input + } + + fn into_signed(self, signature: Signature) -> alloy_network::Signed + where + Self: Sized, + { + alloy_network::Signed::new_unchecked(self.clone(), signature, self.signature_hash()) + } + + fn set_chain_id(&mut self, _chain_id: ChainId) {} + + fn set_gas_limit(&mut self, limit: u64) { + self.gas_limit = U256::from(limit); + } + + fn set_gas_price(&mut self, _price: U256) {} + + fn set_input(&mut self, data: Bytes) { + self.input = data; + } + + fn set_nonce(&mut self, _nonce: u64) {} + + fn set_to(&mut self, to: TxKind) { + self.kind = to; + } + + fn set_value(&mut self, value: U256) { + self.value = value; + } + + fn signature_hash(&self) -> B256 { + self.signature_hash() + } + + fn to(&self) -> TxKind { + self.kind + } + + fn value(&self) -> U256 { + self.value + } } impl From for DepositTransactionRequest { @@ -117,7 +302,6 @@ impl DepositTransaction { /// Calculates the length of the RLP-encoded transaction's fields. pub(crate) fn fields_len(&self) -> usize { let mut len = 0; - len += self.nonce.length(); len += self.source_hash.length(); len += self.from.length(); len += self.kind.length(); @@ -134,7 +318,6 @@ impl DepositTransaction { /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following /// RLP fields in the following order: /// - /// - `nonce` /// - `source_hash` /// - `from` /// - `kind` @@ -145,7 +328,7 @@ impl DepositTransaction { /// - `input` pub fn decode_inner(buf: &mut &[u8]) -> Result { Ok(Self { - nonce: Decodable::decode(buf)?, + nonce: U256::ZERO, source_hash: Decodable::decode(buf)?, from: Decodable::decode(buf)?, kind: Decodable::decode(buf)?, diff --git a/crates/anvil/core/src/eth/trie.rs b/crates/anvil/core/src/eth/trie.rs index 6187c5087d95..5d144a5db13c 100644 --- a/crates/anvil/core/src/eth/trie.rs +++ b/crates/anvil/core/src/eth/trie.rs @@ -1,5 +1,5 @@ //! Utility functions for Ethereum adapted from https://github.dev/rust-blockchain/ethereum/blob/755dffaa4903fbec1269f50cde9863cf86269a14/src/util.rs -use ethers_core::types::H256; +use alloy_primitives::B256; pub use keccak_hasher::KeccakHasher; @@ -7,36 +7,36 @@ pub use keccak_hasher::KeccakHasher; pub use reference_trie::*; /// The KECCAK of the RLP encoding of empty data. -pub const KECCAK_NULL_RLP: H256 = H256([ +pub const KECCAK_NULL_RLP: B256 = B256::new([ 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, ]); /// Generates a trie root hash for a vector of key-value tuples -pub fn trie_root(input: I) -> H256 +pub fn trie_root(input: I) -> B256 where I: IntoIterator, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, { - H256::from(triehash::trie_root::(input)) + B256::from(triehash::trie_root::(input)) } /// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. -pub fn sec_trie_root(input: I) -> H256 +pub fn sec_trie_root(input: I) -> B256 where I: IntoIterator, K: AsRef<[u8]>, V: AsRef<[u8]>, { - H256::from(triehash::sec_trie_root::(input)) + B256::from(triehash::sec_trie_root::(input)) } /// Generates a trie root hash for a vector of values -pub fn ordered_trie_root(input: I) -> H256 +pub fn ordered_trie_root(input: I) -> B256 where I: IntoIterator, V: AsRef<[u8]>, { - H256::from(triehash::ordered_trie_root::(input)) + B256::from(triehash::ordered_trie_root::(input)) } diff --git a/crates/anvil/core/src/eth/utils.rs b/crates/anvil/core/src/eth/utils.rs index d9d8c09872e5..27bb46523da4 100644 --- a/crates/anvil/core/src/eth/utils.rs +++ b/crates/anvil/core/src/eth/utils.rs @@ -1,33 +1,8 @@ -use alloy_eips::eip2930::AccessListItem as AlloyEipAccessListItem; -use alloy_primitives::{Address, U256}; -use alloy_rpc_types::AccessListItem as AlloyAccessListItem; -use ethers_core::{ - types::transaction::eip2930::AccessListItem, - utils::{ - rlp, - rlp::{Encodable, RlpStream}, - }, +use alloy_eips::eip2930::{ + AccessList as AlloyEipAccessList, AccessListItem as AlloyEipAccessListItem, }; -use foundry_common::types::ToAlloy; - -pub fn enveloped(id: u8, v: &T, s: &mut RlpStream) { - let encoded = rlp::encode(v); - let mut out = vec![0; 1 + encoded.len()]; - out[0] = id; - out[1..].copy_from_slice(&encoded); - out.rlp_append(s) -} - -pub fn to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { - list.into_iter() - .map(|item| { - ( - item.address.to_alloy(), - item.storage_keys.into_iter().map(|k| k.to_alloy().into()).collect(), - ) - }) - .collect() -} +use alloy_primitives::{Address, Parity, U256}; +use alloy_rpc_types::{AccessList as AlloyAccessList, AccessListItem as AlloyAccessListItem}; pub fn alloy_to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { list.into_iter() @@ -35,9 +10,35 @@ pub fn alloy_to_revm_access_list(list: Vec) -> Vec<(Address .collect() } +/// Translates a vec of [AlloyEipAccessListItem] to a [AlloyAccessList], translating from internal +/// type to rpc type. +pub fn from_eip_to_alloy_access_list(list: AlloyEipAccessList) -> AlloyAccessList { + AlloyAccessList( + list.0 + .into_iter() + .map(|item| AlloyAccessListItem { + address: item.address, + storage_keys: item.storage_keys.into_iter().collect(), + }) + .collect(), + ) +} + /// Translates a vec of [AlloyEipAccessListItem] to a revm style Access List. pub fn eip_to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { list.into_iter() .map(|item| (item.address, item.storage_keys.into_iter().map(|k| k.into()).collect())) .collect() } + +/// See +/// > If you do, then the v of the signature MUST be set to {0,1} + CHAIN_ID * 2 + 35 where +/// > {0,1} is the parity of the y value of the curve point for which r is the x-value in the +/// > secp256k1 signing process. +pub fn meets_eip155(chain_id: u64, v: Parity) -> bool { + let double_chain_id = chain_id.saturating_mul(2); + match v { + Parity::Eip155(v) => v == double_chain_id + 35 || v == double_chain_id + 36, + _ => false, + } +} diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index d1386854d7cd..db966d956d20 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -26,7 +26,7 @@ impl<'de> serde::Deserialize<'de> for Forking { pub json_rpc_url: Option, #[serde( default, - deserialize_with = "ethers_core::types::serde_helpers::deserialize_stringified_u64_opt" + deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" )] pub block_number: Option, } @@ -63,7 +63,7 @@ pub enum EvmMineOptions { #[cfg_attr( feature = "serde", serde( - deserialize_with = "ethers_core::types::serde_helpers::deserialize_stringified_u64_opt" + deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" ) )] timestamp: Option, @@ -75,7 +75,7 @@ pub enum EvmMineOptions { #[cfg_attr( feature = "serde", serde( - deserialize_with = "ethers_core::types::serde_helpers::deserialize_stringified_u64_opt" + deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" ) )] Timestamp(Option), diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index e1eb0eb3d27f..9ddd53434f6c 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -4,15 +4,11 @@ use crate::{ genesis::Genesis, AccountGenerator, Hardfork, NodeConfig, CHAIN_ID, }; -use alloy_primitives::U256; +use alloy_primitives::{utils::Unit, U256}; +use alloy_signer::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; -use ethers::{ - signers::coins_bip39::{English, Mnemonic}, - utils::WEI_IN_ETHER, -}; -use foundry_common::types::ToAlloy; use foundry_config::{Chain, Config}; use futures::FutureExt; use rand::{rngs::StdRng, SeedableRng}; @@ -186,7 +182,7 @@ const DEFAULT_DUMP_INTERVAL: Duration = Duration::from_secs(60); impl NodeArgs { pub fn into_node_config(self) -> NodeConfig { - let genesis_balance = WEI_IN_ETHER.saturating_mul(self.balance.into()); + let genesis_balance = Unit::ETHER.wei().saturating_mul(U256::from(self.balance)); let compute_units_per_second = if self.evm_opts.no_rate_limit { Some(u64::MAX) } else { @@ -201,7 +197,7 @@ impl NodeArgs { .with_blocktime(self.block_time.map(Duration::from_secs)) .with_no_mining(self.no_mining) .with_account_generator(self.account_generator()) - .with_genesis_balance(genesis_balance.to_alloy()) + .with_genesis_balance(genesis_balance) .with_genesis_timestamp(self.timestamp) .with_port(self.port) .with_fork_block_number( diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index b8430a74601a..3e370b576847 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -16,23 +16,18 @@ use crate::{ mem::in_memory_db::MemDb, FeeManager, Hardfork, }; -use alloy_primitives::{hex, U256}; +use alloy_primitives::{hex, utils::Unit, U256}; use alloy_providers::provider::TempProvider; use alloy_rpc_types::BlockNumberOrTag; +use alloy_signer::{ + coins_bip39::{English, Mnemonic}, + LocalWallet, MnemonicBuilder, Signer as AlloySigner, +}; use alloy_transport::TransportError; use anvil_server::ServerConfig; -use ethers::{ - core::k256::ecdsa::SigningKey, - prelude::{rand::thread_rng, Wallet}, - signers::{ - coins_bip39::{English, Mnemonic}, - MnemonicBuilder, Signer, - }, - utils::WEI_IN_ETHER, -}; use foundry_common::{ - provider::alloy::ProviderBuilder, types::ToAlloy, ALCHEMY_FREE_TIER_CUPS, - NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT, + provider::alloy::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, + REQUEST_TIMEOUT, }; use foundry_config::Config; use foundry_evm::{ @@ -43,6 +38,7 @@ use foundry_evm::{ utils::apply_chain_and_block_specific_env_changes, }; use parking_lot::RwLock; +use rand::thread_rng; use serde_json::{json, to_writer, Value}; use std::{ collections::HashMap, @@ -105,13 +101,13 @@ pub struct NodeConfig { /// The hardfork to use pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block - pub genesis_accounts: Vec>, + pub genesis_accounts: Vec, /// Native token balance of every genesis account in the genesis block pub genesis_balance: U256, /// Genesis block timestamp pub genesis_timestamp: Option, /// Signer accounts that can sign messages/transactions from the EVM node - pub signer_accounts: Vec>, + pub signer_accounts: Vec, /// Configured block time for the EVM chain. Use `None` to mine a new block for every tx pub block_time: Option, /// Disable auto, interval mining mode uns use `MiningMode::None` instead @@ -197,8 +193,7 @@ Available Accounts ); let balance = alloy_primitives::utils::format_ether(self.genesis_balance); for (idx, wallet) in self.genesis_accounts.iter().enumerate() { - write!(config_string, "\n({idx}) {} ({balance} ETH)", wallet.address().to_alloy()) - .unwrap(); + write!(config_string, "\n({idx}) {} ({balance} ETH)", wallet.address()).unwrap(); } let _ = write!( @@ -375,7 +370,7 @@ impl Default for NodeConfig { genesis_timestamp: None, genesis_accounts, // 100ETH default balance - genesis_balance: WEI_IN_ETHER.to_alloy().saturating_mul(U256::from(100u64)), + genesis_balance: Unit::ETHER.wei().saturating_mul(U256::from(100u64)), block_time: None, no_mining: false, port: NODE_PORT, @@ -471,10 +466,10 @@ impl NodeConfig { self.chain_id = chain_id.map(Into::into); let chain_id = self.get_chain_id(); self.genesis_accounts.iter_mut().for_each(|wallet| { - *wallet = wallet.clone().with_chain_id(chain_id); + *wallet = wallet.clone().with_chain_id(Some(chain_id)); }); self.signer_accounts.iter_mut().for_each(|wallet| { - *wallet = wallet.clone().with_chain_id(chain_id); + *wallet = wallet.clone().with_chain_id(Some(chain_id)); }) } @@ -559,14 +554,14 @@ impl NodeConfig { /// Sets the genesis accounts #[must_use] - pub fn with_genesis_accounts(mut self, accounts: Vec>) -> Self { + pub fn with_genesis_accounts(mut self, accounts: Vec) -> Self { self.genesis_accounts = accounts; self } /// Sets the signer accounts #[must_use] - pub fn with_signer_accounts(mut self, accounts: Vec>) -> Self { + pub fn with_signer_accounts(mut self, accounts: Vec) -> Self { self.signer_accounts = accounts; self } @@ -845,7 +840,7 @@ impl NodeConfig { let genesis = GenesisConfig { timestamp: self.get_genesis_timestamp(), balance: self.genesis_balance, - accounts: self.genesis_accounts.iter().map(|acc| acc.address().to_alloy()).collect(), + accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(), fork_genesis_account_infos: Arc::new(Default::default()), genesis_init: self.genesis.clone(), }; @@ -1160,18 +1155,17 @@ impl AccountGenerator { } impl AccountGenerator { - pub fn gen(&self) -> Vec> { + pub fn gen(&self) -> Vec { let builder = MnemonicBuilder::::default().phrase(self.phrase.as_str()); - // use the + // use the derivation path let derivation_path = self.get_derivation_path(); let mut wallets = Vec::with_capacity(self.amount); - for idx in 0..self.amount { let builder = builder.clone().derivation_path(&format!("{derivation_path}{idx}")).unwrap(); - let wallet = builder.build().unwrap().with_chain_id(self.chain_id); + let wallet = builder.build().unwrap().with_chain_id(Some(self.chain_id)); wallets.push(wallet) } wallets diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 8337ce6d3001..dffc035df634 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -9,8 +9,7 @@ use crate::{ validate::TransactionValidator, }, error::{ - decode_revert_reason, BlockchainError, FeeHistoryError, InvalidTransactionError, - Result, ToRpcResponseResult, + BlockchainError, FeeHistoryError, InvalidTransactionError, Result, ToRpcResponseResult, }, fees::{FeeDetails, FeeHistoryCache}, macros::node_info, @@ -29,7 +28,11 @@ use crate::{ revm::primitives::Output, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; +use alloy_consensus::TxLegacy; +use alloy_dyn_abi::TypedData; +use alloy_network::{Signed, TxKind}; use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256, U64}; +use alloy_rlp::Decodable; use alloy_rpc_trace_types::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, @@ -46,9 +49,8 @@ use anvil_core::{ eth::{ block::BlockInfo, transaction::{ - call_to_internal_tx_request, to_alloy_proof, to_ethers_signature, - EthTransactionRequest, LegacyTransaction, PendingTransaction, TransactionKind, - TypedTransaction, TypedTransactionRequest, + call_request_to_typed, EthTransactionRequest, PendingTransaction, TypedTransaction, + TypedTransactionRequest, }, EthRequest, }, @@ -58,13 +60,10 @@ use anvil_core::{ }, }; use anvil_rpc::{error::RpcError, response::ResponseResult}; -use ethers::{types::transaction::eip712::TypedData, utils::rlp}; -use foundry_common::{ - provider::alloy::ProviderBuilder, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::provider::alloy::ProviderBuilder; use foundry_evm::{ backend::DatabaseError, + decode::maybe_decode_revert, revm::{ db::DatabaseRef, interpreter::{return_ok, return_revert, InstructionResult}, @@ -420,18 +419,19 @@ impl EthApi { ) -> Result { match request { TypedTransactionRequest::Deposit(_) => { - const NIL_SIGNATURE: ethers::types::Signature = ethers::types::Signature { - r: ethers::types::U256::zero(), - s: ethers::types::U256::zero(), - v: 0, - }; - return build_typed_transaction(request, NIL_SIGNATURE) + let nil_signature: alloy_primitives::Signature = + alloy_primitives::Signature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + false, + ) + .unwrap(); + return build_typed_transaction(request, nil_signature) } _ => { for signer in self.signers.iter() { - if signer.accounts().contains(&from.to_ethers()) { - let signature = - signer.sign_transaction(request.clone(), &from.to_ethers())?; + if signer.accounts().contains(from) { + let signature = signer.sign_transaction(request.clone(), from)?; return build_typed_transaction(request, signature) } } @@ -567,13 +567,7 @@ impl EthApi { let mut unique = HashSet::new(); let mut accounts: Vec
= Vec::new(); for signer in self.signers.iter() { - accounts.extend( - signer - .accounts() - .into_iter() - .map(|a| a.to_alloy()) - .filter(|acc| unique.insert(*acc)), - ); + accounts.extend(signer.accounts().into_iter().filter(|acc| unique.insert(*acc))); } accounts.extend( self.backend @@ -803,7 +797,7 @@ impl EthApi { } let proof = self.backend.prove_account_at(address, keys, Some(block_request)).await?; - Ok(to_alloy_proof(proof)) + Ok(proof) } /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md). @@ -836,7 +830,8 @@ impl EthApi { pub async fn sign_typed_data_v4(&self, address: Address, data: &TypedData) -> Result { node_info!("eth_signTypedData_v4"); let signer = self.get_signer(address).ok_or(BlockchainError::NoSignerAvailable)?; - let signature = signer.sign_typed_data(address.to_ethers(), data).await?; + let signature = signer.sign_typed_data(address, data).await?; + let signature = alloy_primitives::hex::encode(signature.as_bytes()); Ok(format!("0x{signature}")) } @@ -846,7 +841,8 @@ impl EthApi { pub async fn sign(&self, address: Address, content: impl AsRef<[u8]>) -> Result { node_info!("eth_sign"); let signer = self.get_signer(address).ok_or(BlockchainError::NoSignerAvailable)?; - let signature = signer.sign(address.to_ethers(), content.as_ref()).await?; + let signature = + alloy_primitives::hex::encode(signer.sign(address, content.as_ref()).await?.as_bytes()); Ok(format!("0x{signature}")) } @@ -856,24 +852,17 @@ impl EthApi { pub async fn sign_transaction(&self, request: EthTransactionRequest) -> Result { node_info!("eth_signTransaction"); - let from = request - .from - .map(Ok) - .unwrap_or_else(|| { - self.accounts()? - .first() - .cloned() - .ok_or(BlockchainError::NoSignerAvailable) - .map(|a| a.to_ethers()) - })? - .to_alloy(); + let from = request.from.map(Ok).unwrap_or_else(|| { + self.accounts()?.first().cloned().ok_or(BlockchainError::NoSignerAvailable) + })?; let (nonce, _) = self.request_nonce(&request, from).await?; let request = self.build_typed_tx_request(request, nonce)?; let signer = self.get_signer(from).ok_or(BlockchainError::NoSignerAvailable)?; - let signature = signer.sign_transaction(request, &from.to_ethers())?; + let signature = + alloy_primitives::hex::encode(signer.sign_transaction(request, &from)?.as_bytes()); Ok(format!("0x{signature}")) } @@ -883,34 +872,23 @@ impl EthApi { pub async fn send_transaction(&self, request: EthTransactionRequest) -> Result { node_info!("eth_sendTransaction"); - let from = request - .from - .map(Ok) - .unwrap_or_else(|| { - self.accounts()? - .first() - .cloned() - .ok_or(BlockchainError::NoSignerAvailable) - .map(|a| a.to_ethers()) - })? - .to_alloy(); - + let from = request.from.map(Ok).unwrap_or_else(|| { + self.accounts()?.first().cloned().ok_or(BlockchainError::NoSignerAvailable) + })?; let (nonce, on_chain_nonce) = self.request_nonce(&request, from).await?; let request = self.build_typed_tx_request(request, nonce)?; // if the sender is currently impersonated we need to "bypass" signing let pending_transaction = if self.is_impersonated(from) { let bypass_signature = self.backend.cheats().bypass_signature(); - let transaction = - sign::build_typed_transaction(request, to_ethers_signature(bypass_signature))?; + let transaction = sign::build_typed_transaction(request, bypass_signature)?; self.ensure_typed_transaction_supported(&transaction)?; trace!(target : "node", ?from, "eth_sendTransaction: impersonating"); - PendingTransaction::with_impersonated(transaction, from.to_ethers()) + PendingTransaction::with_impersonated(transaction, from) } else { let transaction = self.sign_request(&from, request)?; self.ensure_typed_transaction_supported(&transaction)?; PendingTransaction::new(transaction)? }; - // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; @@ -926,13 +904,13 @@ impl EthApi { /// Handler for ETH RPC call: `eth_sendRawTransaction` pub async fn send_raw_transaction(&self, tx: Bytes) -> Result { node_info!("eth_sendRawTransaction"); - let data = tx.as_ref(); + let mut data = tx.as_ref(); if data.is_empty() { return Err(BlockchainError::EmptyRawTransactionData); } let transaction = if data[0] > 0x7f { // legacy transaction - match rlp::decode::(data) { + match Signed::::decode(&mut data) { Ok(transaction) => TypedTransaction::Legacy(transaction), Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), } @@ -941,32 +919,29 @@ impl EthApi { // but EIP-1559 prepends a version byte, so we need to encode the data first to get a // valid rlp and then rlp decode impl of `TypedTransaction` will remove and check the // version byte - let extend = rlp::encode(&data); - let tx = match rlp::decode::(&extend[..]) { + let extend = alloy_rlp::encode(data); + let tx = match TypedTransaction::decode(&mut &extend[..]) { Ok(transaction) => transaction, Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), }; self.ensure_typed_transaction_supported(&tx)?; - tx }; - let pending_transaction = PendingTransaction::new(transaction)?; // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let on_chain_nonce = - self.backend.current_nonce(pending_transaction.sender().to_alloy()).await?; + let on_chain_nonce = self.backend.current_nonce(*pending_transaction.sender()).await?; let from = *pending_transaction.sender(); - let nonce = *pending_transaction.transaction.nonce(); - let requires = required_marker(nonce.to_alloy(), on_chain_nonce, from.to_alloy()); + let nonce = pending_transaction.transaction.nonce(); + let requires = required_marker(nonce, on_chain_nonce, from); let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = PoolTransaction { requires, - provides: vec![to_marker(nonce.as_u64(), pending_transaction.sender().to_alloy())], + provides: vec![to_marker(nonce.to::(), *pending_transaction.sender())], pending_transaction, priority, }; @@ -1108,7 +1083,7 @@ impl EthApi { let mut tx = self.pool.get_transaction(hash).map(|pending| { let from = *pending.sender(); let mut tx = transaction_build( - Some(pending.hash().to_alloy()), + Some(*pending.hash()), pending.transaction, None, None, @@ -1116,7 +1091,7 @@ impl EthApi { ); // we set the from field here explicitly to the set sender of the pending transaction, // in case the transaction is impersonated. - tx.from = from.to_alloy(); + tx.from = from; tx }); if tx.is_none() { @@ -1916,7 +1891,7 @@ impl EthApi { if let Some(output) = receipt.out { // insert revert reason if failure if receipt.inner.status_code.unwrap_or_default().to::() == 0 { - if let Some(reason) = decode_revert_reason(&output) { + if let Some(reason) = maybe_decode_revert(&output, None, None) { tx.other.insert( "revertReason".to_string(), serde_json::to_value(reason).expect("Infallible"), @@ -1990,20 +1965,18 @@ impl EthApi { ) -> Result { node_info!("eth_sendUnsignedTransaction"); // either use the impersonated account of the request's `from` field - let from = request.from.ok_or(BlockchainError::NoSignerAvailable)?.to_alloy(); + let from = request.from.ok_or(BlockchainError::NoSignerAvailable)?; let (nonce, on_chain_nonce) = self.request_nonce(&request, from).await?; let request = self.build_typed_tx_request(request, nonce)?; let bypass_signature = self.backend.cheats().bypass_signature(); - let transaction = - sign::build_typed_transaction(request, to_ethers_signature(bypass_signature))?; + let transaction = sign::build_typed_transaction(request, bypass_signature)?; self.ensure_typed_transaction_supported(&transaction)?; - let pending_transaction = - PendingTransaction::with_impersonated(transaction, from.to_ethers()); + let pending_transaction = PendingTransaction::with_impersonated(transaction, from); // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; @@ -2036,10 +2009,10 @@ impl EthApi { fn convert(tx: Arc) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; - let to = tx.to().copied().map(ToAlloy::to_alloy); - let gas_price = tx.gas_price().to_alloy(); - let value = tx.value().to_alloy(); - let gas = tx.gas_limit().to_alloy(); + let to = tx.to(); + let gas_price = tx.gas_price(); + let value = tx.value(); + let gas = tx.gas_limit(); TxpoolInspectSummary { to, value, gas, gas_price } } @@ -2050,14 +2023,12 @@ impl EthApi { // not in sequence. The transaction nonce is an incrementing number for each transaction // with the same From address. for pending in self.pool.ready_transactions() { - let entry = - inspect.pending.entry(pending.pending_transaction.sender().to_alloy()).or_default(); + let entry = inspect.pending.entry(*pending.pending_transaction.sender()).or_default(); let key = pending.pending_transaction.nonce().to_string(); entry.insert(key, convert(pending)); } for queued in self.pool.pending_transactions() { - let entry = - inspect.pending.entry(queued.pending_transaction.sender().to_alloy()).or_default(); + let entry = inspect.pending.entry(*queued.pending_transaction.sender()).or_default(); let key = queued.pending_transaction.nonce().to_string(); entry.insert(key, convert(queued)); } @@ -2085,19 +2056,17 @@ impl EthApi { // we set the from field here explicitly to the set sender of the pending transaction, // in case the transaction is impersonated. - tx.from = from.to_alloy(); + tx.from = from; tx } for pending in self.pool.ready_transactions() { - let entry = - content.pending.entry(pending.pending_transaction.sender().to_alloy()).or_default(); + let entry = content.pending.entry(*pending.pending_transaction.sender()).or_default(); let key = pending.pending_transaction.nonce().to_string(); entry.insert(key, convert(pending)); } for queued in self.pool.pending_transactions() { - let entry = - content.pending.entry(queued.pending_transaction.sender().to_alloy()).or_default(); + let entry = content.pending.entry(*queued.pending_transaction.sender()).or_default(); let key = queued.pending_transaction.nonce().to_string(); entry.insert(key, convert(queued)); } @@ -2272,7 +2241,7 @@ impl EthApi { // succeeded } InstructionResult::OutOfGas | InstructionResult::OutOfFund => { - return Err(InvalidTransactionError::BasicOutOfGas(gas_limit.to_ethers()).into()) + return Err(InvalidTransactionError::BasicOutOfGas(gas_limit).into()) } // need to check if the revert was due to lack of gas or unrelated reason // we're also checking for InvalidFEOpcode here because this can be used to trigger an error common usage in openzeppelin @@ -2306,8 +2275,7 @@ impl EthApi { // transaction requires to succeed let gas: U256 = U256::from(gas); // Get the starting lowest gas needed depending on the transaction kind. - let mut lowest_gas_limit = - determine_base_gas_by_kind(call_to_internal_tx_request(&request)); + let mut lowest_gas_limit = determine_base_gas_by_kind(&request); // pick a point that's close to the estimated gas let mut mid_gas_limit = std::cmp::min( @@ -2413,7 +2381,7 @@ impl EthApi { /// Returns the first signer that can sign for the given address #[allow(clippy::borrowed_box)] pub fn get_signer(&self, address: Address) -> Option<&Box> { - self.signers.iter().find(|signer| signer.is_signer_for(address.to_ethers())) + self.signers.iter().find(|signer| signer.is_signer_for(address)) } /// Returns a new block event stream that yields Notifications when a new block was added @@ -2458,7 +2426,7 @@ impl EthApi { let BlockInfo { block, transactions, receipts: _ } = self.backend.pending_block(transactions).await; - let ethers_block = self.backend.convert_block(block.clone()); + let partial_block = self.backend.convert_block(block.clone()); let mut block_transactions = Vec::with_capacity(block.transactions.len()); let base_fee = self.backend.base_fee(); @@ -2467,7 +2435,7 @@ impl EthApi { let tx = block.transactions.get(info.transaction_index as usize)?.clone(); let tx = transaction_build( - Some(info.transaction_hash.to_alloy()), + Some(info.transaction_hash), tx, Some(&block), Some(info), @@ -2476,7 +2444,7 @@ impl EthApi { block_transactions.push(tx); } - Some(ethers_block.into_full_block(block_transactions)) + Some(partial_block.into_full_block(block_transactions)) } fn build_typed_tx_request( @@ -2484,46 +2452,42 @@ impl EthApi { request: EthTransactionRequest, nonce: U256, ) -> Result { - let chain_id = request.chain_id.map(|c| c.as_u64()).unwrap_or_else(|| self.chain_id()); + let chain_id = request.chain_id.map(|c| c.to::()).unwrap_or_else(|| self.chain_id()); let max_fee_per_gas = request.max_fee_per_gas; let gas_price = request.gas_price; - let gas_limit = request - .gas - .map(ToAlloy::to_alloy) - .map(Ok) - .unwrap_or_else(|| self.current_gas_limit())?; + let gas_limit = request.gas.map(Ok).unwrap_or_else(|| self.current_gas_limit())?; let request = match request.into_typed_request() { Some(TypedTransactionRequest::Legacy(mut m)) => { - m.nonce = nonce.to_ethers(); + m.nonce = nonce.to::(); m.chain_id = Some(chain_id); - m.gas_limit = gas_limit.to_ethers(); + m.gas_limit = gas_limit.to::(); if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to_ethers(); + m.gas_price = self.gas_price().unwrap_or_default().to::(); } TypedTransactionRequest::Legacy(m) } Some(TypedTransactionRequest::EIP2930(mut m)) => { - m.nonce = nonce.to_ethers(); + m.nonce = nonce.to::(); m.chain_id = chain_id; - m.gas_limit = gas_limit.to_ethers(); + m.gas_limit = gas_limit.to::(); if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to_ethers(); + m.gas_price = self.gas_price().unwrap_or_default().to::(); } TypedTransactionRequest::EIP2930(m) } Some(TypedTransactionRequest::EIP1559(mut m)) => { - m.nonce = nonce.to_ethers(); + m.nonce = nonce.to::(); m.chain_id = chain_id; - m.gas_limit = gas_limit.to_ethers(); + m.gas_limit = gas_limit.to::(); if max_fee_per_gas.is_none() { - m.max_fee_per_gas = self.gas_price().unwrap_or_default().to_ethers(); + m.max_fee_per_gas = self.gas_price().unwrap_or_default().to::(); } TypedTransactionRequest::EIP1559(m) } Some(TypedTransactionRequest::Deposit(mut m)) => { - m.gas_limit = gas_limit.to_ethers(); + m.gas_limit = gas_limit; TypedTransactionRequest::Deposit(m) } _ => return Err(BlockchainError::FailedToDecodeTransaction), @@ -2574,7 +2538,7 @@ impl EthApi { ) -> Result<(U256, U256)> { let highest_nonce = self.get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))).await?; - let nonce = request.nonce.map(ToAlloy::to_alloy).unwrap_or(highest_nonce); + let nonce = request.nonce.unwrap_or(highest_nonce); Ok((nonce, highest_nonce)) } @@ -2664,7 +2628,7 @@ where return_ok!() => { // transaction succeeded by manually increasing the gas limit to // highest, which means the caller lacks funds to pay for the tx - InvalidTransactionError::BasicOutOfGas(gas_limit.to_ethers()).into() + InvalidTransactionError::BasicOutOfGas(gas_limit).into() } return_revert!() => { // reverted again after bumping the limit @@ -2679,24 +2643,24 @@ where /// Determines the minimum gas needed for a transaction depending on the transaction kind. #[inline] -fn determine_base_gas_by_kind(request: EthTransactionRequest) -> U256 { - match request.into_typed_request() { +fn determine_base_gas_by_kind(request: &CallRequest) -> U256 { + match call_request_to_typed(request.clone()) { Some(request) => match request { - TypedTransactionRequest::Legacy(req) => match req.kind { - TransactionKind::Call(_) => MIN_TRANSACTION_GAS, - TransactionKind::Create => MIN_CREATE_GAS, + TypedTransactionRequest::Legacy(req) => match req.to { + TxKind::Call(_) => MIN_TRANSACTION_GAS, + TxKind::Create => MIN_CREATE_GAS, }, - TypedTransactionRequest::EIP1559(req) => match req.kind { - TransactionKind::Call(_) => MIN_TRANSACTION_GAS, - TransactionKind::Create => MIN_CREATE_GAS, + TypedTransactionRequest::EIP1559(req) => match req.to { + TxKind::Call(_) => MIN_TRANSACTION_GAS, + TxKind::Create => MIN_CREATE_GAS, }, - TypedTransactionRequest::EIP2930(req) => match req.kind { - TransactionKind::Call(_) => MIN_TRANSACTION_GAS, - TransactionKind::Create => MIN_CREATE_GAS, + TypedTransactionRequest::EIP2930(req) => match req.to { + TxKind::Call(_) => MIN_TRANSACTION_GAS, + TxKind::Create => MIN_CREATE_GAS, }, TypedTransactionRequest::Deposit(req) => match req.kind { - TransactionKind::Call(_) => MIN_TRANSACTION_GAS, - TransactionKind::Create => MIN_CREATE_GAS, + TxKind::Call(_) => MIN_TRANSACTION_GAS, + TxKind::Create => MIN_CREATE_GAS, }, }, // Tighten the gas limit upwards if we don't know the transaction type to avoid deployments diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index a3cde4ccbd3e..949ea10e7a33 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -1,8 +1,7 @@ //! Support for "cheat codes" / bypass functions -use alloy_primitives::Address; -use alloy_rpc_types::Signature; -use anvil_core::eth::transaction::IMPERSONATED_SIGNATURE; +use alloy_primitives::{Address, Signature}; +use anvil_core::eth::transaction::impersonated_signature; use foundry_evm::hashbrown::HashSet; use parking_lot::RwLock; use std::sync::Arc; @@ -85,7 +84,7 @@ impl Default for CheatsState { fn default() -> Self { Self { impersonated_accounts: Default::default(), - bypass_signature: IMPERSONATED_SIGNATURE, + bypass_signature: impersonated_signature(), auto_impersonate_accounts: false, } } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index a3100d5800de..7e15c67abadc 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -6,18 +6,13 @@ use crate::{ }, mem::inspector::Inspector, }; +use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; +use alloy_primitives::{Bloom, BloomInput, Log, B256, U256}; use anvil_core::eth::{ - block::{Block, BlockInfo, Header, PartialHeader}, - receipt::{DepositReceipt, EIP1559Receipt, EIP2930Receipt, EIP658Receipt, Log, TypedReceipt}, - transaction::{PendingTransaction, TransactionInfo, TypedTransaction}, + block::{Block, BlockInfo, PartialHeader}, + transaction::{PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction}, trie, }; -use ethers::{ - abi::ethereum_types::BloomInput, - types::{Bloom, H256, U256}, - utils::rlp, -}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::{ backend::DatabaseError, inspectors::{TracingInspector, TracingInspectorConfig}, @@ -47,7 +42,7 @@ pub struct ExecutedTransaction { impl ExecutedTransaction { /// Creates the receipt for the transaction fn create_receipt(&self) -> TypedReceipt { - let used_gas: U256 = self.gas_used.into(); + let used_gas = U256::from(self.gas_used); let mut bloom = Bloom::default(); logs_bloom(self.logs.clone(), &mut bloom); let logs = self.logs.clone(); @@ -55,29 +50,37 @@ impl ExecutedTransaction { // successful return see [Return] let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); match &self.transaction.pending_transaction.transaction.transaction { - TypedTransaction::Legacy(_) => TypedReceipt::Legacy(EIP658Receipt { - status_code, - gas_used: used_gas, - logs_bloom: bloom, - logs, + TypedTransaction::Legacy(_) => TypedReceipt::Legacy(ReceiptWithBloom { + receipt: Receipt { + success: status_code == 1, + cumulative_gas_used: used_gas.to::(), + logs, + }, + bloom, }), - TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(EIP2930Receipt { - status_code, - gas_used: used_gas, - logs_bloom: bloom, - logs, + TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(ReceiptWithBloom { + receipt: Receipt { + success: status_code == 1, + cumulative_gas_used: used_gas.to::(), + logs, + }, + bloom, }), - TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(EIP1559Receipt { - status_code, - gas_used: used_gas, - logs_bloom: bloom, - logs, + TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(ReceiptWithBloom { + receipt: Receipt { + success: status_code == 1, + cumulative_gas_used: used_gas.to::(), + logs, + }, + bloom, }), - TypedTransaction::Deposit(_) => TypedReceipt::Deposit(DepositReceipt { - status_code, - gas_used: used_gas, - logs_bloom: bloom, - logs, + TypedTransaction::Deposit(_) => TypedReceipt::Deposit(ReceiptWithBloom { + receipt: Receipt { + success: status_code == 1, + cumulative_gas_used: used_gas.to::(), + logs, + }, + bloom, }), } } @@ -105,7 +108,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub pending: std::vec::IntoIter>, pub block_env: BlockEnv, pub cfg_env: CfgEnv, - pub parent_hash: H256, + pub parent_hash: B256, /// Cumulative gas used by all executed transactions pub gas_used: U256, pub enable_steps_tracing: bool, @@ -118,7 +121,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let mut transaction_infos = Vec::new(); let mut receipts = Vec::new(); let mut bloom = Bloom::default(); - let mut cumulative_gas_used = U256::zero(); + let mut cumulative_gas_used = U256::ZERO; let mut invalid = Vec::new(); let mut included = Vec::new(); let gas_limit = self.block_env.gas_limit; @@ -126,7 +129,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let block_number = self.block_env.number; let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; - let timestamp = self.block_env.timestamp.to_ethers().as_u64(); + let timestamp = self.block_env.timestamp.to::(); let base_fee = if (self.cfg_env.spec_id as u8) >= (SpecId::LONDON as u8) { Some(self.block_env.basefee) } else { @@ -165,18 +168,18 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let transaction_index = transaction_infos.len() as u32; let info = TransactionInfo { - transaction_hash: transaction.hash().to_ethers(), + transaction_hash: transaction.hash(), transaction_index, from: *transaction.pending_transaction.sender(), - to: transaction.pending_transaction.transaction.to().copied(), - contract_address: contract_address.map(|c| c.to_ethers()), + to: transaction.pending_transaction.transaction.to(), + contract_address, logs, logs_bloom: *receipt.logs_bloom(), traces, exit, out: match out { - Some(Output::Call(b)) => Some(ethers::types::Bytes(b.0)), - Some(Output::Create(b, _)) => Some(ethers::types::Bytes(b.0)), + Some(Output::Call(b)) => Some(alloy_primitives::Bytes(b.0)), + Some(Output::Create(b, _)) => Some(alloy_primitives::Bytes(b.0)), _ => None, }, nonce: tx.nonce, @@ -188,23 +191,23 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' } let ommers: Vec
= Vec::new(); - let receipts_root = trie::ordered_trie_root(receipts.iter().map(rlp::encode)); + let receipts_root = trie::ordered_trie_root(receipts.iter().map(alloy_rlp::encode)); let partial_header = PartialHeader { parent_hash, - beneficiary: beneficiary.to_ethers(), - state_root: self.db.maybe_state_root().unwrap_or_default().to_ethers(), + beneficiary, + state_root: self.db.maybe_state_root().unwrap_or_default(), receipts_root, logs_bloom: bloom, - difficulty: difficulty.to_ethers(), - number: block_number.to_ethers(), - gas_limit: gas_limit.to_ethers(), - gas_used: cumulative_gas_used, + difficulty, + number: block_number.to::(), + gas_limit: gas_limit.to::(), + gas_used: cumulative_gas_used.to::(), timestamp, extra_data: Default::default(), mix_hash: Default::default(), nonce: Default::default(), - base_fee: base_fee.map(|b| b.to_ethers()), + base_fee: base_fee.map(|b| b.to::()), }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -237,14 +240,14 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator fn next(&mut self) -> Option { let transaction = self.pending.next()?; let sender = *transaction.pending_transaction.sender(); - let account = match self.db.basic(sender.to_alloy()).map(|acc| acc.unwrap_or_default()) { + let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) { Ok(account) => account, Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit let max_gas = self.gas_used.saturating_add(U256::from(env.tx.gas_limit)); - if max_gas > env.block.gas_limit.to_ethers() { + if max_gas > env.block.gas_limit { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } @@ -312,7 +315,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); - self.gas_used.saturating_add(U256::from(gas_used)); + self.gas_used = self.gas_used.saturating_add(U256::from(gas_used)); trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used); @@ -321,7 +324,11 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator exit_reason, out, gas_used, - logs: logs.unwrap_or_default().into_iter().map(Into::into).collect(), + logs: logs + .unwrap_or_default() + .into_iter() + .map(|log| Log::new_unchecked(log.address, log.topics, log.data)) + .collect(), traces: inspector .tracer .unwrap_or(TracingInspector::new(TracingInspectorConfig::all())) @@ -339,7 +346,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator fn logs_bloom(logs: Vec, bloom: &mut Bloom) { for log in logs { bloom.accrue(BloomInput::Raw(&log.address[..])); - for topic in log.topics { + for topic in log.topics() { bloom.accrue(BloomInput::Raw(&topic[..])); } } diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index de0a3d2ac54e..7b4c5d9c7e7a 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -3,7 +3,7 @@ use crate::mem::Backend; use alloy_primitives::B256; use alloy_rpc_types::Block as AlloyBlock; -use anvil_core::eth::{block::Block, receipt::TypedReceipt}; +use anvil_core::eth::{block::Block, transaction::TypedReceipt}; use std::{fmt, sync::Arc}; /// A type that can fetch data related to the ethereum storage. diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index f3ae13126de2..f95b6b96ee9a 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -29,36 +29,36 @@ use crate::{ }, NodeConfig, }; -use alloy_primitives::{Address, Bloom, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; +use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; +use alloy_network::Sealable; +use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; +use alloy_rlp::Decodable; use alloy_rpc_trace_types::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; use alloy_rpc_types::{ state::StateOverride, AccessList, Block as AlloyBlock, BlockId, - BlockNumberOrTag as BlockNumber, CallRequest, Filter, FilteredParams, Header as AlloyHeader, - Log, Transaction, TransactionReceipt, + BlockNumberOrTag as BlockNumber, CallRequest, EIP1186AccountProofResponse as AccountProof, + EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, Log, + Transaction, TransactionReceipt, }; use anvil_core::{ eth::{ - block::{Block, BlockInfo, Header}, - proof::{AccountProof, BasicAccount, StorageProof}, - receipt::{EIP658Receipt, TypedReceipt}, + block::{Block, BlockInfo}, + proof::BasicAccount, transaction::{ - MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedTransaction, + MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedReceipt, + TypedTransaction, }, trie::RefTrieDB, - utils::alloy_to_revm_access_list, + utils::{alloy_to_revm_access_list, meets_eip155}, }, types::{Forking, Index}, }; use anvil_rpc::error::RpcError; -use ethers::{ - abi::ethereum_types::BigEndianHash, - utils::{keccak256, rlp}, -}; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; -use foundry_common::types::{ToAlloy, ToEthers}; +use foundry_common::types::ToAlloy; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertSnapshotAction}, constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, @@ -78,7 +78,6 @@ use foundry_evm::{ }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use hash_db::HashDB; -use itertools::Itertools; use parking_lot::{Mutex, RwLock}; use std::{ collections::{BTreeMap, HashMap}, @@ -354,9 +353,6 @@ impl Backend { pub fn precompiles(&self) -> Vec
{ get_precompiles_for(self.env.read().cfg.spec_id) - .into_iter() - .map(ToAlloy::to_alloy) - .collect_vec() } /// Resets the fork to a fresh state @@ -417,7 +413,7 @@ impl Backend { ..env.block.clone() }; - self.time.reset(env.block.timestamp.to_ethers().as_u64()); + self.time.reset(env.block.timestamp.to::()); // this is the base fee of the current block, but we need the base fee of // the next block @@ -551,7 +547,7 @@ impl Backend { slot: U256, val: B256, ) -> DatabaseResult<()> { - self.db.write().await.set_storage_at(address, slot, val.to_ethers().into_uint().to_alloy()) + self.db.write().await.set_storage_at(address, slot, U256::from_be_bytes(val.0)) } /// Returns the configured specid @@ -677,7 +673,7 @@ impl Backend { if let Some(hash) = storage.hashes.remove(&n) { if let Some(block) = storage.blocks.remove(&hash) { for tx in block.transactions { - let _ = storage.transactions.remove(&tx.hash().to_alloy()); + let _ = storage.transactions.remove(&tx.hash()); } } } @@ -849,8 +845,8 @@ impl Backend { pending: pool_transactions.into_iter(), block_env: env.block.clone(), cfg_env: env.cfg, - parent_hash: storage.best_hash.to_ethers(), - gas_used: U256::ZERO.to_ethers(), + parent_hash: storage.best_hash, + gas_used: U256::ZERO, enable_steps_tracing: self.enable_steps_tracing, }; @@ -908,18 +904,15 @@ impl Backend { pending: pool_transactions.into_iter(), block_env: env.block.clone(), cfg_env: env.cfg.clone(), - parent_hash: best_hash.to_ethers(), - gas_used: U256::ZERO.to_ethers(), + parent_hash: best_hash, + gas_used: U256::ZERO, enable_steps_tracing: self.enable_steps_tracing, }; let executed_tx = executor.execute(); // we also need to update the new blockhash in the db itself let block_hash = executed_tx.block.block.header.hash(); - db.insert_block_hash( - executed_tx.block.block.header.number.to_alloy(), - block_hash.to_alloy(), - ); + db.insert_block_hash(U256::from(executed_tx.block.block.header.number), block_hash); (executed_tx, block_hash) }; @@ -942,16 +935,16 @@ impl Backend { let mut storage = self.blockchain.storage.write(); // update block metadata storage.best_number = block_number; - storage.best_hash = block_hash.to_alloy(); + storage.best_hash = block_hash; // Difficulty is removed and not used after Paris (aka TheMerge). Value is replaced with // prevrandao. https://github.com/bluealloy/revm/blob/1839b3fce8eaeebb85025576f2519b80615aca1e/crates/interpreter/src/instructions/host_env.rs#L27 if !self.is_eip3675() { storage.total_difficulty = - storage.total_difficulty.saturating_add(header.difficulty.to_alloy()); + storage.total_difficulty.saturating_add(header.difficulty); } - storage.blocks.insert(block_hash.to_alloy(), block); - storage.hashes.insert(block_number, block_hash.to_alloy()); + storage.blocks.insert(block_hash, block); + storage.hashes.insert(block_number, block_hash); node_info!(""); // insert all transactions @@ -964,7 +957,7 @@ impl Backend { node_info!(" Gas used: {}", receipt.gas_used()); if !info.exit.is_ok() { let r = decode_revert( - info.out.as_deref().unwrap_or_default(), + info.out.clone().unwrap_or_default().as_ref(), None, Some(info.exit), ); @@ -975,10 +968,10 @@ impl Backend { let mined_tx = MinedTransaction { info, receipt, - block_hash: block_hash.to_alloy(), + block_hash, block_number: block_number.to::(), }; - storage.transactions.insert(mined_tx.info.transaction_hash.to_alloy(), mined_tx); + storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx); } // remove old transactions that exceed the transaction block keeper @@ -1008,13 +1001,13 @@ impl Backend { (outcome, header, block_hash) }; let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - header.gas_used.to_alloy(), - header.gas_limit.to_alloy(), - header.base_fee_per_gas.unwrap_or_default().to_alloy(), + U256::from(header.gas_used), + U256::from(header.gas_limit), + U256::from(header.base_fee_per_gas.unwrap_or_default()), ); // notify all listeners - self.notify_on_new_block(header, block_hash.to_alloy()); + self.notify_on_new_block(header, block_hash); // update next base fee self.fees.set_base_fee(U256::from(next_block_base_fee)); @@ -1035,7 +1028,7 @@ impl Backend { overrides: Option, ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> { self.with_database_at(block_request, |state, block| { - let block_number = (block.number.to_ethers()).as_u64(); + let block_number = block.number.to::(); let (exit, out, gas, state) = match overrides { None => self.call_with_state(state, request, fee_details, block), Some(overrides) => { @@ -1274,9 +1267,7 @@ impl Backend { block .transactions .iter() - .filter_map(|tx| { - storage.transactions.get(&tx.hash().to_alloy()).map(|tx| tx.info.clone()) - }) + .filter_map(|tx| storage.transactions.get(&tx.hash()).map(|tx| tx.info.clone())) .collect() }; @@ -1286,9 +1277,9 @@ impl Backend { for log in logs.into_iter() { let mut log = Log { - address: log.address.to_alloy(), - topics: log.topics.into_iter().map(ToAlloy::to_alloy).collect(), - data: log.data.0.into(), + address: log.address, + topics: log.topics().to_vec(), + data: log.data.data, block_hash: None, block_number: None, transaction_hash: None, @@ -1310,9 +1301,9 @@ impl Backend { } if is_match { - log.block_hash = Some(block_hash.to_alloy()); + log.block_hash = Some(block_hash); log.block_number = Some(block.header.number.to_alloy()); - log.transaction_hash = Some(transaction_hash.to_alloy()); + log.transaction_hash = Some(transaction_hash); log.transaction_index = Some(U256::from(transaction.transaction_index)); log.log_index = Some(U256::from(block_log_index)); all_logs.push(log); @@ -1435,11 +1426,11 @@ impl Backend { let base_fee = block.header.base_fee_per_gas; let storage = self.blockchain.storage.read(); for hash in block.transactions.iter().map(|tx| tx.hash()) { - let info = storage.transactions.get(&hash.to_alloy())?.info.clone(); + let info = storage.transactions.get(&hash)?.info.clone(); let tx = block.transactions.get(info.transaction_index as usize)?.clone(); let tx = transaction_build( - Some(hash.to_alloy()), + Some(hash), tx, Some(block), Some(info), @@ -1513,12 +1504,10 @@ impl Backend { } } BlockNumber::Finalized => { - if storage.best_number.to_ethers() > (slots_in_an_epoch.to_ethers() * 2) { - *storage.hashes.get( - &(storage.best_number.to_ethers() - - (slots_in_an_epoch.to_ethers() * 2)) - .to_alloy(), - )? + if storage.best_number > (slots_in_an_epoch * U64::from(2)) { + *storage + .hashes + .get(&(storage.best_number - (slots_in_an_epoch * U64::from(2))))? } else { storage.genesis_hash } @@ -1549,7 +1538,7 @@ impl Backend { /// Takes a block as it's stored internally and returns the eth api conform block format pub fn convert_block(&self, block: Block) -> AlloyBlock { - let size = U256::from(rlp::encode(&block).len() as u32); + let size = U256::from(alloy_rlp::encode(&block).len() as u32); let Block { header, transactions, .. } = block; @@ -1571,27 +1560,31 @@ impl Backend { mix_hash, nonce, base_fee_per_gas, + withdrawals_root: _, + blob_gas_used: _, + excess_blob_gas: _, + parent_beacon_block_root: _, } = header; AlloyBlock { total_difficulty: Some(self.total_difficulty()), header: AlloyHeader { - hash: Some(hash.to_alloy()), - parent_hash: parent_hash.to_alloy(), - uncles_hash: ommers_hash.to_alloy(), - miner: beneficiary.to_alloy(), - state_root: state_root.to_alloy(), - transactions_root: transactions_root.to_alloy(), - receipts_root: receipts_root.to_alloy(), + hash: Some(hash), + parent_hash, + uncles_hash: ommers_hash, + miner: beneficiary, + state_root, + transactions_root, + receipts_root, number: Some(number.to_alloy()), gas_used: gas_used.to_alloy(), gas_limit: gas_limit.to_alloy(), extra_data: extra_data.0.into(), - logs_bloom: Bloom::from_slice(logs_bloom.as_bytes()), + logs_bloom, timestamp: U256::from(timestamp), - difficulty: difficulty.to_alloy(), - mix_hash: Some(mix_hash.to_alloy()), - nonce: Some(B64::from_slice(nonce.as_bytes())), + difficulty, + mix_hash: Some(mix_hash), + nonce: Some(B64::from(nonce)), base_fee_per_gas: base_fee_per_gas.map(|f| f.to_alloy()), withdrawals_root: None, blob_gas_used: None, @@ -1600,7 +1593,7 @@ impl Backend { }, size: Some(size), transactions: alloy_rpc_types::BlockTransactions::Hashes( - transactions.into_iter().map(|tx| tx.hash().to_alloy()).collect(), + transactions.into_iter().map(|tx| tx.hash()).collect(), ), uncles: vec![], withdrawals: None, @@ -1677,10 +1670,10 @@ impl Backend { let block = block.block; let block = BlockEnv { number: block.header.number.to_alloy(), - coinbase: block.header.beneficiary.to_alloy(), + coinbase: block.header.beneficiary, timestamp: rU256::from(block.header.timestamp), - difficulty: block.header.difficulty.to_alloy(), - prevrandao: Some(block.header.mix_hash).map(|h| h.to_alloy()), + difficulty: block.header.difficulty, + prevrandao: Some(block.header.mix_hash), basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), gas_limit: block.header.gas_limit.to_alloy(), ..Default::default() @@ -1701,14 +1694,14 @@ impl Backend { if let Some((state, block)) = self .get_block(block_number.to::()) - .and_then(|block| Some((states.get(&block.header.hash().to_alloy())?, block))) + .and_then(|block| Some((states.get(&block.header.hash())?, block))) { let block = BlockEnv { number: block.header.number.to_alloy(), - coinbase: block.header.beneficiary.to_alloy(), + coinbase: block.header.beneficiary, timestamp: rU256::from(block.header.timestamp), - difficulty: block.header.difficulty.to_alloy(), - prevrandao: Some(block.header.mix_hash).map(|h| h.to_alloy()), + difficulty: block.header.difficulty, + prevrandao: Some(block.header.mix_hash), basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), gas_limit: block.header.gas_limit.to_alloy(), ..Default::default() @@ -1738,7 +1731,7 @@ impl Backend { warn!(target: "backend", "Not historic state found for block={}", block_number); return Err(BlockchainError::BlockOutOfRange( - self.env.read().block.number.to_ethers().as_u64(), + self.env.read().block.number.to::(), block_number.to::(), )); } @@ -1829,8 +1822,7 @@ impl Backend { block_request: Option, ) -> Result { if let Some(BlockRequest::Pending(pool_transactions)) = block_request.as_ref() { - if let Some(value) = get_pool_transactions_nonce(pool_transactions, address.to_ethers()) - { + if let Some(value) = get_pool_transactions_nonce(pool_transactions, address) { return Ok(value); } } @@ -1884,7 +1876,7 @@ impl Backend { let mut traces = vec![]; let storage = self.blockchain.storage.read(); for tx in block.transactions { - traces.extend(storage.transactions.get(&tx.hash().to_alloy())?.parity_traces()); + traces.extend(storage.transactions.get(&tx.hash())?.parity_traces()); } Some(traces) } @@ -1982,7 +1974,7 @@ impl Backend { let block = self.get_block(id)?; for transaction in block.transactions { - let receipt = self.mined_transaction_receipt(transaction.hash().to_alloy())?; + let receipt = self.mined_transaction_receipt(transaction.hash())?; receipts.push(receipt.inner); } @@ -1994,18 +1986,20 @@ impl Backend { let MinedTransaction { info, receipt, block_hash, .. } = self.blockchain.get_transaction_by_hash(&hash)?; - let EIP658Receipt { status_code, gas_used, logs_bloom, logs } = receipt.into(); + let ReceiptWithBloom { receipt, bloom } = receipt.into(); + let Receipt { success, cumulative_gas_used: _, logs } = receipt; + let logs_bloom = bloom; let index = info.transaction_index as usize; let block = self.blockchain.get_block_by_hash(&block_hash)?; // TODO store cumulative gas used in receipt instead - let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash().to_alloy())); + let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash())); let mut cumulative_gas_used = U256::ZERO; for receipt in receipts.iter().take(index + 1) { - cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used().to_alloy()); + cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); } // cumulative_gas_used = cumulative_gas_used.saturating_sub(gas_used); @@ -2018,30 +2012,29 @@ impl Backend { let transaction_type = transaction.transaction.r#type(); let effective_gas_price = match transaction.transaction { - TypedTransaction::Legacy(t) => t.gas_price.to_alloy(), - TypedTransaction::EIP2930(t) => t.gas_price.to_alloy(), + TypedTransaction::Legacy(t) => t.gas_price, + TypedTransaction::EIP2930(t) => t.gas_price, TypedTransaction::EIP1559(t) => block .header .base_fee_per_gas - .map(|f| f.to_alloy()) - .unwrap_or(self.base_fee()) - .checked_add(t.max_priority_fee_per_gas.to_alloy()) - .unwrap_or(U256::MAX), - TypedTransaction::Deposit(_) => U256::from(0), + .map_or(self.base_fee().to::(), |b| b as u128) + .checked_add(t.max_priority_fee_per_gas) + .unwrap_or(u128::MAX), + TypedTransaction::Deposit(_) => 0_u128, }; let deposit_nonce = transaction_type.and_then(|x| (x == 0x7E).then_some(info.nonce)); let mut inner = TransactionReceipt { - transaction_hash: Some(info.transaction_hash.to_alloy()), + transaction_hash: Some(info.transaction_hash), transaction_index: U64::from(info.transaction_index), block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number.as_u64())), - from: info.from.to_alloy(), - to: info.to.map(|t| t.to_alloy()), + block_number: Some(U256::from(block.header.number)), + from: info.from, + to: info.to, cumulative_gas_used, - gas_used: Some(gas_used.to_alloy()), - contract_address: info.contract_address.map(|t| t.to_alloy()), + gas_used: Some(cumulative_gas_used), + contract_address: info.contract_address, logs: { let mut pre_receipts_log_index = None; if !cumulative_receipts.is_empty() { @@ -2052,12 +2045,12 @@ impl Backend { logs.iter() .enumerate() .map(|(i, log)| Log { - address: log.address.to_alloy(), - topics: log.topics.clone().into_iter().map(|t| t.to_alloy()).collect(), - data: log.data.clone().0.into(), + address: log.address, + topics: log.topics().to_vec(), + data: log.data.data.clone(), block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number.as_u64())), - transaction_hash: Some(info.transaction_hash.to_alloy()), + block_number: Some(U256::from(block.header.number)), + transaction_hash: Some(info.transaction_hash), transaction_index: Some(U256::from(info.transaction_index)), log_index: Some(U256::from( (pre_receipts_log_index.unwrap_or(0)) + i as u32, @@ -2066,11 +2059,11 @@ impl Backend { }) .collect() }, - status_code: Some(U64::from(status_code)), + status_code: Some(U64::from(success)), state_root: None, - logs_bloom: Bloom::from_slice(logs_bloom.as_bytes()), + logs_bloom, transaction_type: transaction_type.map(U8::from).unwrap_or_default(), - effective_gas_price: effective_gas_price.to::(), + effective_gas_price: U128::from(effective_gas_price), blob_gas_price: None, blob_gas_used: None, other: Default::default(), @@ -2160,12 +2153,12 @@ impl Backend { let block = storage.blocks.get(&block_hash).cloned()?; let index: usize = index.into(); let tx = block.transactions.get(index)?.clone(); - let info = storage.transactions.get(&tx.hash().to_alloy())?.info.clone(); + let info = storage.transactions.get(&tx.hash())?.info.clone(); (info, block, tx) }; Some(transaction_build( - Some(info.transaction_hash.to_alloy()), + Some(info.transaction_hash), tx, Some(&block), Some(info), @@ -2200,7 +2193,7 @@ impl Backend { let tx = block.transactions.get(info.transaction_index as usize)?.clone(); Some(transaction_build( - Some(info.transaction_hash.to_alloy()), + Some(info.transaction_hash), tx, Some(&block), Some(info), @@ -2217,7 +2210,7 @@ impl Backend { keys: Vec, block_request: Option, ) -> Result { - let account_key = B256::from(keccak256(address.to_ethers().as_bytes())); + let account_key = B256::from(alloy_primitives::utils::keccak256(address)); let block_number = block_request.as_ref().map(|r| r.block_number()); self.with_database_at(block_request, |block_db, _| { @@ -2230,13 +2223,13 @@ impl Backend { .map_err(|err| BlockchainError::TrieError(err.to_string()))?; let maybe_account: Option = { - let acc_decoder = |bytes: &[u8]| { - rlp::decode(bytes).unwrap_or_else(|_| { + let acc_decoder = |mut bytes: &[u8]| { + BasicAccount::decode(&mut bytes).unwrap_or_else(|_| { panic!("prove_account_at, could not query trie for account={:?}", &address) }) }; let query = (&mut recorder, acc_decoder); - trie.get_with(account_key.to_ethers().as_bytes(), query) + trie.get_with(account_key.as_slice(), query) .map_err(|err| BlockchainError::TrieError(err.to_string()))? }; let account = maybe_account.unwrap_or_default(); @@ -2249,7 +2242,7 @@ impl Backend { // proof is rlp encoded: // // - rlp::encode(&record).to_vec().into() + alloy_rlp::encode(record).to_vec().into() }) .collect::>(); @@ -2257,9 +2250,9 @@ impl Backend { block_db.maybe_account_db(address).ok_or(BlockchainError::DataUnavailable)?; let account_proof = AccountProof { - address: address.to_ethers(), + address, balance: account.balance, - nonce: account.nonce.as_u64().into(), + nonce: account.nonce.to::(), code_hash: account.code_hash, storage_hash: account.storage_root, account_proof: proof, @@ -2270,15 +2263,15 @@ impl Backend { let key = B256::from(keccak256(storage_key)); prove_storage(&account, &account_db.0, key).map( |(storage_proof, storage_value)| StorageProof { - key: storage_key.to_ethers(), - value: storage_value.to_ethers().into_uint(), + key: alloy_rpc_types::JsonStorageKey(storage_key), + value: U256::from_be_bytes(storage_value.0), proof: storage_proof .into_iter() .map(|proof| { // proof is rlp encoded: // // - rlp::encode(&proof).to_vec().into() + alloy_rlp::encode(proof).to_vec().into() }) .collect(), }, @@ -2317,23 +2310,21 @@ impl Backend { /// Get max nonce from transaction pool by address fn get_pool_transactions_nonce( pool_transactions: &[Arc], - address: ethers::types::H160, + address: Address, ) -> Option { let highest_nonce_tx = pool_transactions .iter() .filter(|tx| *tx.pending_transaction.sender() == address) .reduce(|accum, item| { let nonce = item.pending_transaction.nonce(); - if nonce.gt(accum.pending_transaction.nonce()) { + if nonce > accum.pending_transaction.nonce() { item } else { accum } }); if let Some(highest_nonce_tx) = highest_nonce_tx { - return Some( - highest_nonce_tx.pending_transaction.nonce().to_alloy().saturating_add(U256::from(1)), - ); + return Some(highest_nonce_tx.pending_transaction.nonce().saturating_add(U256::from(1))); } None } @@ -2345,7 +2336,7 @@ impl TransactionValidator for Backend { tx: &PendingTransaction, ) -> Result<(), BlockchainError> { let address = *tx.sender(); - let account = self.get_account(address.to_alloy()).await?; + let account = self.get_account(address).await?; let env = self.next_env(); Ok(self.validate_pool_transaction_for(tx, &account, &env)?) } @@ -2364,7 +2355,7 @@ impl TransactionValidator for Backend { if let Some(legacy) = tx.as_legacy() { // if env.cfg.spec_id >= SpecId::SPURIOUS_DRAGON && - !legacy.meets_eip155(chain_id.to::()) + !meets_eip155(chain_id.to::(), legacy.signature().v()) { warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V"); return Err(InvalidTransactionError::IncompatibleEIP155); @@ -2376,13 +2367,13 @@ impl TransactionValidator for Backend { } } - if tx.gas_limit() < MIN_TRANSACTION_GAS.to_ethers() { + if tx.gas_limit() < MIN_TRANSACTION_GAS { warn!(target: "backend", "[{:?}] gas too low", tx.hash()); return Err(InvalidTransactionError::GasTooLow); } // Check gas limit, iff block gas limit is set. - if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit.to_ethers() { + if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { detail: String::from("tx.gas_limit > env.block.gas_limit"), @@ -2393,14 +2384,14 @@ impl TransactionValidator for Backend { let is_deposit_tx = matches!(&pending.transaction.transaction, TypedTransaction::Deposit(_)); let nonce: u64 = - (*tx.nonce()).try_into().map_err(|_| InvalidTransactionError::NonceMaxValue)?; + tx.nonce().try_into().map_err(|_| InvalidTransactionError::NonceMaxValue)?; if nonce < account.nonce && !is_deposit_tx { warn!(target: "backend", "[{:?}] nonce too low", tx.hash()); return Err(InvalidTransactionError::NonceTooLow); } if (env.cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - if tx.gas_price() < env.block.basefee.to_ethers() && !is_deposit_tx { + if tx.gas_price() < env.block.basefee && !is_deposit_tx { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); return Err(InvalidTransactionError::FeeCapTooLow); } @@ -2423,8 +2414,7 @@ impl TransactionValidator for Backend { tx.hash()); InvalidTransactionError::InsufficientFunds })?; - - if account.balance < req_funds.to_alloy() { + if account.balance < req_funds { warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); return Err(InvalidTransactionError::InsufficientFunds); } @@ -2438,7 +2428,7 @@ impl TransactionValidator for Backend { env: &Env, ) -> Result<(), InvalidTransactionError> { self.validate_pool_transaction_for(tx, account, env)?; - if tx.nonce().as_u64() > account.nonce { + if tx.nonce().to::() > account.nonce { return Err(InvalidTransactionError::NonceTooHigh); } Ok(()) @@ -2479,9 +2469,9 @@ pub fn transaction_build( } transaction.block_hash = - block.as_ref().map(|block| B256::from(keccak256(&rlp::encode(&block.header)))); + block.as_ref().map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))); - transaction.block_number = block.as_ref().map(|block| U256::from(block.header.number.as_u64())); + transaction.block_number = block.as_ref().map(|block| U256::from(block.header.number)); transaction.transaction_index = info.as_ref().map(|status| U256::from(status.transaction_index)); @@ -2490,11 +2480,10 @@ pub fn transaction_build( // can't recover the sender, instead we use the sender from the executed transaction and set the // impersonated hash. if eth_transaction.is_impersonated() { - transaction.from = info.as_ref().map(|info| info.from).unwrap_or_default().to_alloy(); - transaction.hash = - eth_transaction.impersonated_hash(transaction.from.to_ethers()).to_alloy(); + transaction.from = info.as_ref().map(|info| info.from).unwrap_or_default(); + transaction.hash = eth_transaction.impersonated_hash(transaction.from); } else { - transaction.from = eth_transaction.recover().expect("can recover signed tx").to_alloy(); + transaction.from = eth_transaction.recover().expect("can recover signed tx"); } // if a specific hash was provided we update the transaction's hash @@ -2506,11 +2495,7 @@ pub fn transaction_build( transaction.hash = tx_hash; } - transaction.to = info - .as_ref() - .map_or(eth_transaction.to().cloned(), |status| status.to) - .map(|t| t.to_alloy()); - + transaction.to = info.as_ref().map_or(eth_transaction.to(), |status| status.to); transaction } @@ -2531,12 +2516,12 @@ pub fn prove_storage( .unwrap(); let item: U256 = { - let decode_value = |bytes: &[u8]| rlp::decode(bytes).expect("decoding db value failed"); + let decode_value = + |mut bytes: &[u8]| U256::decode(&mut bytes).expect("decoding db value failed"); let query = (&mut recorder, decode_value); - trie.get_with(storage_key.to_ethers().as_bytes(), query) + trie.get_with(storage_key.as_slice(), query) .map_err(|err| BlockchainError::TrieError(err.to_string()))? - .unwrap_or_else(|| U256::ZERO.to_ethers()) - .to_alloy() + .unwrap_or(U256::ZERO) }; Ok((recorder.drain().into_iter().map(|r| r.data).collect(), B256::from(item))) diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index d5a2d40a7a8b..af936578d04a 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -1,56 +1,34 @@ //! Support for generating the state root for memdb storage use crate::eth::{backend::db::AsHashDB, error::BlockchainError}; -use alloy_primitives::{Address, Bytes, B256, U256 as rU256}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rlp::Encodable; use alloy_rpc_types::state::StateOverride; use anvil_core::eth::trie::RefSecTrieDBMut; -use ethers::utils::{rlp, rlp::RlpStream}; -use foundry_common::types::ToEthers; use foundry_evm::{ backend::DatabaseError, hashbrown::HashMap as Map, revm::{ db::{CacheDB, DatabaseRef, DbAccount}, - primitives::{AccountInfo, Bytecode, Log}, + primitives::{AccountInfo, Bytecode}, }, }; use memory_db::HashKey; use trie_db::TrieMut; -/// Returns the log hash for all `logs` -/// -/// The log hash is `keccak(rlp(logs[]))`, -pub fn log_rlp_hash(logs: Vec) -> B256 { - let mut stream = RlpStream::new(); - stream.begin_unbounded_list(); - for log in logs { - let topics = log.topics.into_iter().map(|t| t.to_ethers()).collect::>(); - stream.begin_list(3); - stream.append(&(log.address.to_ethers())); - stream.append_list(&topics); - stream.append(&log.data.0); - } - stream.finalize_unbounded_list(); - let out = stream.out().freeze(); - - let out = ethers::utils::keccak256(out); - B256::from_slice(out.as_slice()) -} - /// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &Map) -> (AsHashDB, B256) { +pub fn storage_trie_db(storage: &Map) -> (AsHashDB, B256) { // Populate DB with full trie from entries. let (db, root) = { let mut db = , _>>::default(); let mut root = Default::default(); { let mut trie = RefSecTrieDBMut::new(&mut db, &mut root); - for (k, v) in storage.iter().filter(|(_k, v)| *v != &rU256::from(0)) { - let mut temp: [u8; 32] = [0; 32]; - (*k).to_ethers().to_big_endian(&mut temp); - let key = B256::from(temp).to_ethers(); - let value = rlp::encode(&(*v).to_ethers()); - trie.insert(key.as_bytes(), value.as_ref()).unwrap(); + for (k, v) in storage.iter().filter(|(_k, v)| *v != &U256::from(0)) { + let key = B256::from(*k); + let mut value: Vec = Vec::new(); + U256::encode(v, &mut value); + trie.insert(key.as_slice(), value.as_ref()).unwrap(); } } (db, root) @@ -95,13 +73,14 @@ pub fn state_merkle_trie_root(accounts: &Map) -> B256 { } /// Returns the RLP for this account. -pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&info.nonce); - stream.append(&info.balance.to_ethers()); - stream.append(&storage_trie_db(storage).1.to_ethers()); - stream.append(&info.code_hash.as_slice()); - stream.out().freeze().into() +pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { + let mut out: Vec = Vec::new(); + let list: [&dyn Encodable; 4] = + [&info.nonce, &info.balance, &storage_trie_db(storage).1, &info.code_hash]; + + alloy_rlp::encode_list::<_, dyn Encodable>(&list, &mut out); + + out.into() } /// Applies the given state overrides to the state, returning a new CacheDB state diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 8757ef3a5e81..b84a34dc7312 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -6,6 +6,7 @@ use crate::eth::{ }, pool::transactions::PoolTransaction, }; +use alloy_network::Sealable; use alloy_primitives::{Bytes, TxHash, B256, U256, U64}; use alloy_rpc_trace_types::{ geth::{DefaultFrame, GethDefaultTracingOptions}, @@ -16,10 +17,8 @@ use alloy_rpc_types::{ }; use anvil_core::eth::{ block::{Block, PartialHeader}, - receipt::TypedReceipt, - transaction::{MaybeImpersonatedTransaction, TransactionInfo}, + transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt}, }; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::{ revm::primitives::Env, traces::{GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig}, @@ -233,10 +232,10 @@ impl BlockchainStorage { // create a dummy genesis block let partial_header = PartialHeader { timestamp, - base_fee: base_fee.map(|b| b.to_ethers()), - gas_limit: env.block.gas_limit.to_ethers(), - beneficiary: env.block.coinbase.to_ethers(), - difficulty: env.block.difficulty.to_ethers(), + base_fee: base_fee.map(|b| b.to::()), + gas_limit: env.block.gas_limit.to::(), + beneficiary: env.block.coinbase, + difficulty: env.block.difficulty, ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); @@ -245,11 +244,11 @@ impl BlockchainStorage { let best_number: U64 = U64::from(0u64); Self { - blocks: HashMap::from([(genesis_hash.to_alloy(), block)]), - hashes: HashMap::from([(best_number, genesis_hash.to_alloy())]), - best_hash: best_hash.to_alloy(), + blocks: HashMap::from([(genesis_hash, block)]), + hashes: HashMap::from([(best_number, genesis_hash)]), + best_hash, best_number, - genesis_hash: genesis_hash.to_alloy(), + genesis_hash, transactions: Default::default(), total_difficulty: Default::default(), } @@ -291,7 +290,7 @@ impl BlockchainStorage { pub fn remove_block_transactions(&mut self, block_hash: B256) { if let Some(block) = self.blocks.get_mut(&block_hash) { for tx in block.transactions.iter() { - self.transactions.remove(&tx.hash().to_alloy()); + self.transactions.remove(&tx.hash()); } block.transactions.clear(); } @@ -408,7 +407,7 @@ impl MinedTransaction { TracingInspectorConfig::default_parity(), ) .into_localized_transaction_traces(RethTransactionInfo { - hash: Some(self.info.transaction_hash.to_alloy()), + hash: Some(self.info.transaction_hash), index: Some(self.info.transaction_index as u64), block_hash: Some(self.block_hash), block_number: Some(self.block_number), @@ -419,7 +418,7 @@ impl MinedTransaction { pub fn geth_trace(&self, opts: GethDefaultTracingOptions) -> DefaultFrame { GethTraceBuilder::new(self.info.traces.clone(), TracingInspectorConfig::default_geth()) .geth_traces( - self.receipt.gas_used().as_u64(), + self.receipt.gas_used().to::(), self.info.out.clone().unwrap_or_default().0.into(), opts, ) diff --git a/crates/anvil/src/eth/backend/notifications.rs b/crates/anvil/src/eth/backend/notifications.rs index a463dfb9446d..795de0cca9a5 100644 --- a/crates/anvil/src/eth/backend/notifications.rs +++ b/crates/anvil/src/eth/backend/notifications.rs @@ -1,7 +1,7 @@ //! Notifications emitted from the backed +use alloy_consensus::Header; use alloy_primitives::B256; -use anvil_core::eth::block::Header; use futures::channel::mpsc::UnboundedReceiver; use std::sync::Arc; diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index a92df9ce92e6..d0b48e411af6 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,20 +1,16 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; +use alloy_primitives::{Bytes, SignatureError as AlloySignatureError, U256}; +use alloy_signer::Error as AlloySignerError; use alloy_transport::TransportError; use anvil_rpc::{ error::{ErrorCode, RpcError}, response::ResponseResult, }; -use ethers::{ - abi::AbiDecode, - providers::ProviderError, - signers::WalletError, - types::{Bytes, SignatureError, U256}, -}; -use foundry_common::SELECTOR_LEN; use foundry_evm::{ backend::DatabaseError, + decode::maybe_decode_revert, revm::{ self, interpreter::InstructionResult, @@ -46,9 +42,9 @@ pub enum BlockchainError { #[error("Prevrandao not in th EVM's environment after merge")] PrevrandaoNotSet, #[error(transparent)] - SignatureError(#[from] SignatureError), + AlloySignatureError(#[from] AlloySignatureError), #[error(transparent)] - WalletError(#[from] WalletError), + AlloySignerError(#[from] AlloySignerError), #[error("Rpc Endpoint not implemented")] RpcUnimplemented, #[error("Rpc error {0:?}")] @@ -58,8 +54,6 @@ pub enum BlockchainError { #[error(transparent)] FeeHistory(#[from] FeeHistoryError), #[error(transparent)] - ForkProvider(#[from] ProviderError), - #[error(transparent)] AlloyForkProvider(#[from] TransportError), #[error("EVM error {0:?}")] EvmError(InstructionResult), @@ -263,17 +257,6 @@ impl From for InvalidTransactionError { } } -/// Returns the revert reason from the `revm::TransactOut` data, if it's an abi encoded String. -/// -/// **Note:** it's assumed the `out` buffer starts with the call's signature -pub(crate) fn decode_revert_reason(out: impl AsRef<[u8]>) -> Option { - let out = out.as_ref(); - if out.len() < SELECTOR_LEN { - return None - } - String::decode(&out[SELECTOR_LEN..]).ok() -} - /// Helper trait to easily convert results to rpc results pub(crate) trait ToRpcResponseResult { fn to_rpc_result(self) -> ResponseResult; @@ -319,7 +302,9 @@ impl ToRpcResponseResult for Result { InvalidTransactionError::Revert(data) => { // this mimics geth revert error let mut msg = "execution reverted".to_string(); - if let Some(reason) = data.as_ref().and_then(decode_revert_reason) { + if let Some(reason) = + data.as_ref().and_then(|data| maybe_decode_revert(data, None, None)) + { msg = format!("{msg}: {reason}"); } RpcError { @@ -360,8 +345,10 @@ impl ToRpcResponseResult for Result { BlockchainError::FailedToDecodeStateDump => { RpcError::invalid_params("Failed to decode state dump") } - BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()), - BlockchainError::WalletError(err) => RpcError::invalid_params(err.to_string()), + BlockchainError::AlloySignerError(err) => RpcError::invalid_params(err.to_string()), + BlockchainError::AlloySignatureError(err) => { + RpcError::invalid_params(err.to_string()) + } BlockchainError::RpcUnimplemented => { RpcError::internal_error_with("Not implemented") } @@ -370,10 +357,6 @@ impl ToRpcResponseResult for Result { BlockchainError::InvalidFeeInput => RpcError::invalid_params( "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`", ), - BlockchainError::ForkProvider(err) => { - error!(%err, "fork provider error"); - RpcError::internal_error_with(format!("Fork Error: {err:?}")) - } BlockchainError::AlloyForkProvider(err) => { error!(%err, "alloy fork provider error"); RpcError::internal_error_with(format!("Fork Error: {err:?}")) diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 1b8680981916..ef597331a6cd 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -4,7 +4,6 @@ use crate::eth::{ }; use alloy_primitives::{B256, U256}; use anvil_core::eth::transaction::TypedTransaction; -use foundry_common::types::ToAlloy; use foundry_evm::revm::primitives::SpecId; use futures::StreamExt; use parking_lot::{Mutex, RwLock}; @@ -231,10 +230,10 @@ impl FeeHistoryService { let current_receipts = self.storage_info.receipts(hash); if let (Some(block), Some(receipts)) = (current_block, current_receipts) { - block_number = Some(block.header.number.as_u64()); + block_number = Some(block.header.number); - let gas_used = block.header.gas_used.as_u64() as f64; - let gas_limit = block.header.gas_limit.as_u64() as f64; + let gas_used = block.header.gas_used as f64; + let gas_limit = block.header.gas_limit as f64; let gas_target = gas_limit / elasticity; item.gas_used_ratio = gas_used / (gas_target * elasticity); @@ -244,25 +243,25 @@ impl FeeHistoryService { .iter() .enumerate() .map(|(i, receipt)| { - let gas_used = receipt.gas_used().as_u64(); + let gas_used = receipt.gas_used(); let effective_reward = match block.transactions.get(i).map(|tx| &tx.transaction) { Some(TypedTransaction::Legacy(t)) => { - t.gas_price.to_alloy().saturating_sub(base_fee).to::() + U256::from(t.gas_price).saturating_sub(base_fee).to::() } Some(TypedTransaction::EIP2930(t)) => { - t.gas_price.to_alloy().saturating_sub(base_fee).to::() + U256::from(t.gas_price).saturating_sub(base_fee).to::() + } + Some(TypedTransaction::EIP1559(t)) => { + U256::from(t.max_priority_fee_per_gas) + .min(U256::from(t.max_fee_per_gas).saturating_sub(base_fee)) + .to::() } - Some(TypedTransaction::EIP1559(t)) => t - .max_priority_fee_per_gas - .to_alloy() - .min(t.max_fee_per_gas.to_alloy().saturating_sub(base_fee)) - .to::(), Some(TypedTransaction::Deposit(_)) => 0, None => 0, }; - (gas_used, effective_reward) + (gas_used.to::(), effective_reward) }) .collect(); diff --git a/crates/anvil/src/eth/mod.rs b/crates/anvil/src/eth/mod.rs index a9acd1d9af4c..393a9ff21330 100644 --- a/crates/anvil/src/eth/mod.rs +++ b/crates/anvil/src/eth/mod.rs @@ -1,5 +1,6 @@ pub mod api; pub mod otterscan; +pub mod sign; pub use api::EthApi; pub mod backend; @@ -10,5 +11,4 @@ pub mod fees; pub(crate) mod macros; pub mod miner; pub mod pool; -pub mod sign; pub mod util; diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index d8e0deb05501..8848a954bd37 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -39,7 +39,6 @@ use crate::{ use alloy_primitives::{TxHash, U64}; use alloy_rpc_types::txpool::TxpoolStatus; use anvil_core::eth::transaction::PendingTransaction; -use foundry_common::types::ToAlloy; use futures::channel::mpsc::{channel, Receiver, Sender}; use parking_lot::{Mutex, RwLock}; use std::{collections::VecDeque, fmt, sync::Arc}; @@ -157,7 +156,7 @@ impl Pool { let mut dropped = None; if !removed.is_empty() { - dropped = removed.into_iter().find(|t| t.pending_transaction.hash().to_alloy() == tx); + dropped = removed.into_iter().find(|t| *t.pending_transaction.hash() == tx); } dropped } diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index d2f6a662b7bc..312bde4818bf 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,7 +1,6 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; use alloy_primitives::{Address, TxHash, U256}; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; -use foundry_common::types::ToAlloy; use parking_lot::RwLock; use std::{ cmp::Ordering, @@ -45,7 +44,7 @@ impl TransactionOrder { pub fn priority(&self, tx: &TypedTransaction) -> TransactionPriority { match self { TransactionOrder::Fifo => TransactionPriority::default(), - TransactionOrder::Fees => TransactionPriority(tx.gas_price().to_alloy()), + TransactionOrder::Fees => TransactionPriority(tx.gas_price()), } } } @@ -89,12 +88,12 @@ pub struct PoolTransaction { impl PoolTransaction { /// Returns the hash of this transaction pub fn hash(&self) -> TxHash { - self.pending_transaction.hash().to_alloy() + *self.pending_transaction.hash() } /// Returns the gas pric of this transaction pub fn gas_price(&self) -> U256 { - self.pending_transaction.transaction.gas_price().to_alloy() + self.pending_transaction.transaction.gas_price() } } @@ -479,9 +478,7 @@ impl ReadyTransactions { // (addr + nonce) then we check for gas price if to_remove.provides() == tx.provides { // check if underpriced - if tx.pending_transaction.transaction.gas_price().to_alloy() <= - to_remove.gas_price() - { + if tx.pending_transaction.transaction.gas_price() <= to_remove.gas_price() { warn!(target: "txpool", "ready replacement transaction underpriced [{:?}]", tx.hash()); return Err(PoolError::ReplacementUnderpriced(Box::new(tx.clone()))) } else { diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 112f8c7ab46b..34b3fa285562 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,20 +1,12 @@ use crate::eth::error::BlockchainError; +use alloy_dyn_abi::TypedData; +use alloy_network::{Signed, Transaction}; +use alloy_primitives::{Address, Signature, B256, U256}; +use alloy_signer::{LocalWallet, Signer as AlloySigner, SignerSync as AlloySignerSync}; use anvil_core::eth::transaction::{ - DepositTransaction, DepositTransactionRequest, EIP1559Transaction, EIP1559TransactionRequest, - EIP2930Transaction, EIP2930TransactionRequest, LegacyTransaction, LegacyTransactionRequest, + optimism::{DepositTransaction, DepositTransactionRequest}, TypedTransaction, TypedTransactionRequest, }; -use ethers::{ - core::k256::ecdsa::SigningKey, - prelude::{Address, Wallet}, - signers::Signer as EthersSigner, - types::{ - transaction::{ - eip2718::TypedTransaction as EthersTypedTransactionRequest, eip712::TypedData, - }, - Signature, H256, U256, - }, -}; use std::collections::HashMap; /// A transaction signer @@ -31,13 +23,17 @@ pub trait Signer: Send + Sync { /// Returns the signature async fn sign(&self, address: Address, message: &[u8]) -> Result; - /// Encodes and signs the typed data according EIP-712. Payload must implement Eip712 trait. + /// Encodes and signs the typed data according EIP-712. Payload must conform to the EIP-712 + /// standard. async fn sign_typed_data( &self, address: Address, payload: &TypedData, ) -> Result; + /// Signs the given hash. + async fn sign_hash(&self, address: Address, hash: B256) -> Result; + /// signs a transaction request using the given account in request fn sign_transaction( &self, @@ -49,11 +45,11 @@ pub trait Signer: Send + Sync { /// Maintains developer keys pub struct DevSigner { addresses: Vec
, - accounts: HashMap>, + accounts: HashMap, } impl DevSigner { - pub fn new(accounts: Vec>) -> Self { + pub fn new(accounts: Vec) -> Self { let addresses = accounts.iter().map(|wallet| wallet.address()).collect::>(); let accounts = addresses.iter().cloned().zip(accounts).collect(); Self { addresses, accounts } @@ -81,8 +77,24 @@ impl Signer for DevSigner { address: Address, payload: &TypedData, ) -> Result { + let mut signer = + self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?.to_owned(); + + // Explicitly set chainID as none, to avoid any EIP-155 application to `v` when signing + // typed data. + signer.set_chain_id(None); + + Ok(signer + .sign_hash( + payload.eip712_signing_hash().map_err(|_| BlockchainError::NoSignerAvailable)?, + ) + .await?) + } + + async fn sign_hash(&self, address: Address, hash: B256) -> Result { let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?; - Ok(signer.sign_typed_data(payload).await?) + + Ok(signer.sign_hash(hash).await?) } fn sign_transaction( @@ -91,9 +103,12 @@ impl Signer for DevSigner { address: &Address, ) -> Result { let signer = self.accounts.get(address).ok_or(BlockchainError::NoSignerAvailable)?; - let ethers_tx: EthersTypedTransactionRequest = request.into(); - - Ok(signer.sign_transaction_sync(ðers_tx)?) + match request { + TypedTransactionRequest::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + TypedTransactionRequest::EIP2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + TypedTransactionRequest::EIP1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + TypedTransactionRequest::Deposit(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + } } } @@ -108,92 +123,16 @@ pub fn build_typed_transaction( ) -> Result { let tx = match request { TypedTransactionRequest::Legacy(tx) => { - let LegacyTransactionRequest { - nonce, gas_price, gas_limit, kind, value, input, .. - } = tx; - TypedTransaction::Legacy(LegacyTransaction { - nonce, - gas_price, - gas_limit, - kind, - value, - input, - signature, - }) + let sighash = tx.signature_hash(); + TypedTransaction::Legacy(Signed::new_unchecked(tx, signature, sighash)) } TypedTransactionRequest::EIP2930(tx) => { - let EIP2930TransactionRequest { - chain_id, - nonce, - gas_price, - gas_limit, - kind, - value, - input, - access_list, - } = tx; - - let recid: u8 = signature.recovery_id()?.into(); - - TypedTransaction::EIP2930(EIP2930Transaction { - chain_id, - nonce, - gas_price, - gas_limit, - kind, - value, - input, - access_list: access_list.into(), - odd_y_parity: recid != 0, - r: { - let mut rarr = [0_u8; 32]; - signature.r.to_big_endian(&mut rarr); - H256::from(rarr) - }, - s: { - let mut sarr = [0_u8; 32]; - signature.s.to_big_endian(&mut sarr); - H256::from(sarr) - }, - }) + let sighash = tx.signature_hash(); + TypedTransaction::EIP2930(Signed::new_unchecked(tx, signature, sighash)) } TypedTransactionRequest::EIP1559(tx) => { - let EIP1559TransactionRequest { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - kind, - value, - input, - access_list, - } = tx; - - let recid: u8 = signature.recovery_id()?.into(); - - TypedTransaction::EIP1559(EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas, - max_fee_per_gas, - gas_limit, - kind, - value, - input, - access_list: access_list.into(), - odd_y_parity: recid != 0, - r: { - let mut rarr = [0u8; 32]; - signature.r.to_big_endian(&mut rarr); - H256::from(rarr) - }, - s: { - let mut sarr = [0u8; 32]; - signature.s.to_big_endian(&mut sarr); - H256::from(sarr) - }, - }) + let sighash = tx.signature_hash(); + TypedTransaction::EIP1559(Signed::new_unchecked(tx, signature, sighash)) } TypedTransactionRequest::Deposit(tx) => { let DepositTransactionRequest { @@ -216,7 +155,7 @@ pub fn build_typed_transaction( source_hash, mint, is_system_tx, - nonce: U256::zero(), + nonce: U256::ZERO, }) } }; diff --git a/crates/anvil/src/eth/util.rs b/crates/anvil/src/eth/util.rs index 4181df4e8d44..5153178f598c 100644 --- a/crates/anvil/src/eth/util.rs +++ b/crates/anvil/src/eth/util.rs @@ -1,15 +1,9 @@ -use ethers::abi::Address; -use foundry_common::types::ToEthers; +use alloy_primitives::Address; use foundry_evm::revm::{self, precompile::Precompiles, primitives::SpecId}; use std::fmt; pub fn get_precompiles_for(spec_id: SpecId) -> Vec
{ - Precompiles::new(to_precompile_id(spec_id)) - .addresses() - .into_iter() - .copied() - .map(|item| item.to_ethers()) - .collect() + Precompiles::new(to_precompile_id(spec_id)).addresses().into_iter().copied().collect() } /// wrapper type that displays byte as hex diff --git a/crates/anvil/src/genesis.rs b/crates/anvil/src/genesis.rs index cd115c2a90c9..3525838a7756 100644 --- a/crates/anvil/src/genesis.rs +++ b/crates/anvil/src/genesis.rs @@ -1,7 +1,7 @@ //! Bindings for geth's `genesis.json` format use crate::revm::primitives::AccountInfo; use alloy_primitives::{Address, Bytes, B256, U256}; -use ethers::{signers::LocalWallet, types::serde_helpers::*}; +use alloy_signer::LocalWallet; use foundry_common::errors::FsPathError; use foundry_evm::revm::primitives::{Bytecode, Env, KECCAK_EMPTY, U256 as rU256}; use serde::{Deserialize, Serialize}; @@ -19,21 +19,25 @@ pub struct Genesis { pub config: Option, #[serde( default, - deserialize_with = "deserialize_stringified_u64_opt", + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64_opt", skip_serializing_if = "Option::is_none" )] pub nonce: Option, #[serde( default, - deserialize_with = "deserialize_stringified_u64_opt", + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64_opt", skip_serializing_if = "Option::is_none" )] pub timestamp: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub extra_data: Option, - #[serde(deserialize_with = "deserialize_stringified_u64")] + #[serde( + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64" + )] pub gas_limit: u64, - #[serde(deserialize_with = "deserialize_stringified_u64")] + #[serde( + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64" + )] pub difficulty: u64, #[serde(default, skip_serializing_if = "Option::is_none")] pub mix_hash: Option, @@ -43,13 +47,13 @@ pub struct Genesis { pub alloc: Alloc, #[serde( default, - deserialize_with = "deserialize_stringified_u64_opt", + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64_opt", skip_serializing_if = "Option::is_none" )] pub number: Option, #[serde( default, - deserialize_with = "deserialize_stringified_u64_opt", + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64_opt", skip_serializing_if = "Option::is_none" )] pub gas_used: Option, @@ -116,7 +120,7 @@ pub struct GenesisAccount { pub balance: U256, #[serde( default, - deserialize_with = "deserialize_stringified_u64_opt", + deserialize_with = "anvil_core::eth::serde_helpers::numeric::deserialize_stringified_u64_opt", skip_serializing_if = "Option::is_none" )] pub nonce: Option, @@ -213,7 +217,9 @@ pub struct CliqueConfig { /// serde support for `secretKey` in genesis pub mod secret_key { - use ethers::{core::k256::SecretKey, signers::LocalWallet, types::Bytes}; + use alloy_primitives::Bytes; + use alloy_signer::LocalWallet; + use k256::{ecdsa::SigningKey, SecretKey}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; pub fn serialize(value: &Option, serializer: S) -> Result @@ -221,7 +227,10 @@ pub mod secret_key { S: Serializer, { if let Some(wallet) = value { - Bytes::from(wallet.signer().to_bytes().as_ref()).serialize(serializer) + let signer: SigningKey = wallet.signer().clone(); + let signer_bytes = signer.to_bytes(); + let signer_bytes2: [u8; 32] = *signer_bytes.as_ref(); + Bytes::from(signer_bytes2).serialize(serializer) } else { serializer.serialize_none() } diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index 322c5071ac3a..a5e92284cb51 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -1,5 +1,5 @@ +use alloy_rpc_types::BlockNumberOrTag; use ethereum_forkid::{ForkHash, ForkId}; -use ethers::types::BlockNumber; use foundry_evm::revm::primitives::SpecId; use std::str::FromStr; @@ -160,11 +160,11 @@ impl From for SpecId { } } -impl> From for Hardfork { - fn from(block: T) -> Hardfork { +impl> From for Hardfork { + fn from(block: T) -> Self { let num = match block.into() { - BlockNumber::Earliest => 0, - BlockNumber::Number(num) => num.as_u64(), + BlockNumberOrTag::Earliest => 0, + BlockNumberOrTag::Number(num) => num, _ => u64::MAX, }; @@ -190,8 +190,8 @@ impl> From for Hardfork { #[cfg(test)] mod tests { use crate::Hardfork; + use alloy_primitives::hex; use crc::{Crc, CRC_32_ISO_HDLC}; - use ethers::utils::hex; #[test] fn test_hardfork_blocks() { diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 4c478b669e3d..322b14aad60f 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -16,20 +16,10 @@ use crate::{ shutdown::Signal, tasks::TaskManager, }; +use alloy_primitives::{Address, U256}; +use alloy_signer::{LocalWallet, Signer as AlloySigner}; use eth::backend::fork::ClientFork; -use ethers::{ - core::k256::ecdsa::SigningKey, - prelude::Wallet, - signers::Signer, - types::{Address, U256}, -}; -use foundry_common::{ - provider::{ - alloy::{ProviderBuilder, RetryProvider}, - ethers::{ProviderBuilder as EthersProviderBuilder, RetryProvider as EthersRetryProvider}, - }, - types::ToEthers, -}; +use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; use futures::{FutureExt, TryFutureExt}; use parking_lot::Mutex; @@ -280,41 +270,23 @@ impl NodeHandle { // .interval(Duration::from_millis(500)) } - /// Constructs a [`EthersRetryProvider`] for this handle's HTTP endpoint. - /// TODO: Remove once ethers is phased out of cast/alloy and tests. - pub fn ethers_http_provider(&self) -> EthersRetryProvider { - EthersProviderBuilder::new(&self.http_endpoint()) - .build() - .expect("failed to build ethers HTTP provider") - } - /// Constructs a [`RetryProvider`] for this handle's WS endpoint. pub fn ws_provider(&self) -> RetryProvider { ProviderBuilder::new(&self.ws_endpoint()).build().expect("failed to build WS provider") } - pub fn ethers_ws_provider(&self) -> EthersRetryProvider { - EthersProviderBuilder::new(&self.ws_endpoint()) - .build() - .expect("failed to build ethers WS provider") - } - /// Constructs a [`RetryProvider`] for this handle's IPC endpoint, if any. pub fn ipc_provider(&self) -> Option { ProviderBuilder::new(&self.config.get_ipc_path()?).build().ok() } - pub fn ethers_ipc_provider(&self) -> Option { - EthersProviderBuilder::new(&self.config.get_ipc_path()?).build().ok() - } - /// Signer accounts that can sign messages/transactions from the EVM node pub fn dev_accounts(&self) -> impl Iterator + '_ { self.config.signer_accounts.iter().map(|wallet| wallet.address()) } /// Signer accounts that can sign messages/transactions from the EVM node - pub fn dev_wallets(&self) -> impl Iterator> + '_ { + pub fn dev_wallets(&self) -> impl Iterator + '_ { self.config.signer_accounts.iter().cloned() } @@ -325,12 +297,12 @@ impl NodeHandle { /// Native token balance of every genesis account in the genesis block pub fn genesis_balance(&self) -> U256 { - self.config.genesis_balance.to_ethers() + self.config.genesis_balance } /// Default gas price for all txs pub fn gas_price(&self) -> U256 { - self.config.get_gas_price().to_ethers() + self.config.get_gas_price() } /// Returns the shutdown signal diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index ed061567df15..ce2f7e2450c4 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -2,15 +2,12 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, StorageInfo, }; -use alloy_primitives::{TxHash, B256, U256}; +use alloy_consensus::ReceiptWithBloom; +use alloy_network::Sealable; +use alloy_primitives::{Log, TxHash, B256, U256}; use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log as AlloyLog}; -use anvil_core::eth::{ - block::Block, - receipt::{EIP658Receipt, Log, TypedReceipt}, - subscription::SubscriptionId, -}; +use anvil_core::eth::{block::Block, subscription::SubscriptionId, transaction::TypedReceipt}; use anvil_rpc::{request::Version, response::ResponseResult}; -use foundry_common::types::ToAlloy; use futures::{channel::mpsc::Receiver, ready, Stream, StreamExt}; use serde::Serialize; use std::{ @@ -156,9 +153,9 @@ pub fn filter_logs( /// Determines whether to add this log fn add_log(block_hash: B256, l: &Log, block: &Block, params: &FilteredParams) -> bool { let log = AlloyLog { - address: l.address.to_alloy(), - topics: l.topics.clone().into_iter().map(|t| t.to_alloy()).collect::>(), - data: l.data.clone().0.into(), + address: l.address, + topics: l.topics().to_vec(), + data: l.data.data.clone(), block_hash: None, block_number: None, transaction_hash: None, @@ -167,7 +164,7 @@ pub fn filter_logs( removed: false, }; if params.filter.is_some() { - let block_number = block.header.number.as_u64(); + let block_number = block.header.number; if !params.filter_block_range(block_number) || !params.filter_block_hash(block_hash) || !params.filter_address(&log) || @@ -183,21 +180,21 @@ pub fn filter_logs( let mut logs = vec![]; let mut log_index: u32 = 0; for (receipt_index, receipt) in receipts.into_iter().enumerate() { - let receipt: EIP658Receipt = receipt.into(); - let receipt_logs = receipt.logs; + let receipt: ReceiptWithBloom = receipt.into(); + let receipt_logs = receipt.receipt.logs; let transaction_hash: Option = if !receipt_logs.is_empty() { - Some(block.transactions[receipt_index].hash().to_alloy()) + Some(block.transactions[receipt_index].hash()) } else { None }; for log in receipt_logs.into_iter() { - if add_log(block_hash.to_alloy(), &log, &block, filter) { + if add_log(block_hash, &log, &block, filter) { logs.push(AlloyLog { - address: log.address.to_alloy(), - topics: log.topics.into_iter().map(|t| t.to_alloy()).collect::>(), - data: log.data.0.into(), - block_hash: Some(block_hash.to_alloy()), - block_number: Some(U256::from(block.header.number.as_u64())), + address: log.address, + topics: log.topics().to_vec(), + data: log.data.data, + block_hash: Some(block_hash), + block_number: Some(U256::from(block.header.number)), transaction_hash, transaction_index: Some(U256::from(receipt_index)), log_index: Some(U256::from(log_index)), diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 6d65f6fe4593..3a39f7e74664 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -1,5 +1,6 @@ //! tests for anvil specific logic +use crate::utils::ethers_http_provider; use anvil::{spawn, NodeConfig}; use ethers::{prelude::Middleware, types::Address}; use foundry_common::types::ToAlloy; @@ -7,7 +8,7 @@ use foundry_common::types::ToAlloy; #[tokio::test(flavor = "multi_thread")] async fn test_can_change_mining_mode() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); assert!(api.anvil_get_auto_mine().unwrap()); @@ -36,10 +37,16 @@ async fn test_can_change_mining_mode() { #[tokio::test(flavor = "multi_thread")] async fn can_get_default_dev_keys() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let dev_accounts = handle.dev_accounts().collect::>(); - let accounts = provider.get_accounts().await.unwrap(); + let accounts = provider + .get_accounts() + .await + .unwrap() + .into_iter() + .map(ToAlloy::to_alloy) + .collect::>(); assert_eq!(dev_accounts, accounts); } @@ -57,7 +64,7 @@ async fn test_can_set_genesis_timestamp() { let genesis_timestamp = 1000u64; let (_api, handle) = spawn(NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into())).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); assert_eq!(genesis_timestamp, provider.get_block(0).await.unwrap().unwrap().timestamp.as_u64()); } @@ -65,7 +72,7 @@ async fn test_can_set_genesis_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_can_use_default_genesis_timestamp() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); assert_ne!(0u64, provider.get_block(0).await.unwrap().unwrap().timestamp.as_u64()); } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 60a91ee76164..e487033cc179 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -1,5 +1,5 @@ //! tests for custom anvil endpoints -use crate::{abi::*, fork::fork_config}; +use crate::{abi::*, fork::fork_config, utils::ethers_http_provider}; use alloy_rpc_types::BlockNumberOrTag; use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; use anvil_core::{ @@ -26,7 +26,7 @@ use std::{ #[tokio::test(flavor = "multi_thread")] async fn can_set_gas_price() { let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let gas_price: U256 = 1337u64.into(); api.anvil_set_min_gas_price(gas_price.to_alloy()).await.unwrap(); @@ -67,7 +67,7 @@ async fn can_set_storage() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let impersonate = Address::random(); let to = Address::random(); @@ -104,7 +104,7 @@ async fn can_impersonate_account() { #[tokio::test(flavor = "multi_thread")] async fn can_auto_impersonate_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let impersonate = Address::random(); let to = Address::random(); @@ -144,9 +144,9 @@ async fn can_auto_impersonate_account() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_contract() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let provider = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = @@ -156,7 +156,7 @@ async fn can_impersonate_contract() { let to = Address::random(); let val = 1337u64; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // fund the impersonated account api.anvil_set_balance(impersonate.to_alloy(), U256::from(1e18 as u64).to_alloy()) @@ -190,7 +190,7 @@ async fn can_impersonate_contract() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_gnosis_safe() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // let safe: Address = "0xA063Cb7CFd8E57c30c788A0572CBbf2129ae56B6".parse().unwrap(); @@ -220,7 +220,7 @@ async fn can_impersonate_gnosis_safe() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_multiple_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let impersonate0 = Address::random(); let impersonate1 = Address::random(); @@ -267,7 +267,7 @@ async fn can_impersonate_multiple_account() { #[tokio::test(flavor = "multi_thread")] async fn can_mine_manually() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let start_num = provider.get_block_number().await.unwrap(); @@ -281,7 +281,7 @@ async fn can_mine_manually() { #[tokio::test(flavor = "multi_thread")] async fn test_set_next_timestamp() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -308,7 +308,7 @@ async fn test_set_next_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_evm_set_time() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -332,7 +332,7 @@ async fn test_evm_set_time() { #[tokio::test(flavor = "multi_thread")] async fn test_evm_set_time_in_past() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -352,7 +352,7 @@ async fn test_evm_set_time_in_past() { #[tokio::test(flavor = "multi_thread")] async fn test_timestamp_interval() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.evm_mine(None).await.unwrap(); let interval = 10; @@ -403,7 +403,7 @@ async fn test_timestamp_interval() { async fn test_can_set_storage_bsc_fork() { let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some("https://bsc-dataseed.binance.org/"))).await; - let provider = Arc::new(handle.ethers_http_provider()); + let provider = Arc::new(ethers_http_provider(&handle.http_endpoint())); let busd_addr: Address = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56".parse().unwrap(); let idx: U256 = @@ -432,7 +432,7 @@ async fn can_get_node_info() { let node_info = api.anvil_node_info().await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let block_number = provider.get_block_number().await.unwrap(); let block = provider.get_block(block_number).await.unwrap().unwrap(); @@ -465,7 +465,7 @@ async fn can_get_metadata() { let metadata = api.anvil_metadata().await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let block_number = provider.get_block_number().await.unwrap().as_u64(); let chain_id = provider.get_chainid().await.unwrap().as_u64(); @@ -488,7 +488,7 @@ async fn can_get_metadata() { async fn can_get_metadata_on_fork() { let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some("https://bsc-dataseed.binance.org/"))).await; - let provider = Arc::new(handle.ethers_http_provider()); + let provider = Arc::new(ethers_http_provider(&handle.http_endpoint())); let metadata = api.anvil_metadata().await.unwrap(); @@ -532,7 +532,7 @@ async fn metadata_changes_on_reset() { #[tokio::test(flavor = "multi_thread")] async fn test_get_transaction_receipt() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // set the base fee let new_base_fee = U256::from(1_000); @@ -563,7 +563,7 @@ async fn test_get_transaction_receipt() { #[tokio::test(flavor = "multi_thread")] async fn test_set_chain_id() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, U256::from(31337)); @@ -599,7 +599,7 @@ async fn test_fork_revert_next_block_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_revert_call_latest_block_timestamp() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // Mine a new block, and check the new block gas limit api.mine_one().await; diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 84c93ea0338e..50a239b48a25 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -1,11 +1,16 @@ //! general eth api tests -use crate::abi::{MulticallContract, SimpleStorage}; -use alloy_primitives::{B256, U256 as rU256}; +use crate::{ + abi::{MulticallContract, SimpleStorage}, + utils::ethers_http_provider, +}; +use alloy_primitives::{Address as rAddress, B256, U256 as rU256}; +use alloy_providers::provider::TempProvider; use alloy_rpc_types::{ state::{AccountOverride, StateOverride}, CallInput, CallRequest, }; +use alloy_signer::Signer as AlloySigner; use anvil::{ eth::{api::CLIENT_VERSION, EthApi}, spawn, NodeConfig, CHAIN_ID, @@ -27,7 +32,7 @@ async fn can_get_block_number() { let block_num = api.block_number().unwrap(); assert_eq!(block_num, U256::zero().to_alloy()); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let num = provider.get_block_number().await.unwrap(); assert_eq!(num, block_num.to::().into()); @@ -36,7 +41,7 @@ async fn can_get_block_number() { #[tokio::test(flavor = "multi_thread")] async fn can_dev_get_balance() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = handle.http_provider(); let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { @@ -48,7 +53,7 @@ async fn can_dev_get_balance() { #[tokio::test(flavor = "multi_thread")] async fn can_get_price() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let _ = provider.get_gas_price().await.unwrap(); } @@ -56,7 +61,7 @@ async fn can_get_price() { #[tokio::test(flavor = "multi_thread")] async fn can_get_accounts() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let _ = provider.get_accounts().await.unwrap(); } @@ -64,7 +69,7 @@ async fn can_get_accounts() { #[tokio::test(flavor = "multi_thread")] async fn can_get_client_version() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let version = provider.client_version().await.unwrap(); assert_eq!(CLIENT_VERSION, version); @@ -73,7 +78,7 @@ async fn can_get_client_version() { #[tokio::test(flavor = "multi_thread")] async fn can_get_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, CHAIN_ID.into()); @@ -82,7 +87,7 @@ async fn can_get_chain_id() { #[tokio::test(flavor = "multi_thread")] async fn can_modify_chain_id() { let (_api, handle) = spawn(NodeConfig::test().with_chain_id(Some(Chain::Goerli))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, Chain::Goerli.into()); @@ -102,13 +107,16 @@ async fn can_get_network_id() { #[tokio::test(flavor = "multi_thread")] async fn can_get_block_by_number() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); // send a dummy transactions - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new() + .to(to.to_ethers()) + .value(amount.to_ethers()) + .from(from.to_ethers()); let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let block: Block = provider.get_block_with_txs(1u64).await.unwrap().unwrap(); @@ -124,7 +132,7 @@ async fn can_get_block_by_number() { #[tokio::test(flavor = "multi_thread")] async fn can_get_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); let block = provider.get_block(BlockNumber::Pending).await.unwrap().unwrap(); @@ -138,7 +146,7 @@ async fn can_get_pending_block() { let from = accounts[0].address(); let to = accounts[1].address(); - let tx = TransactionRequest::new().to(to).value(100u64).from(from); + let tx = TransactionRequest::new().to(to.to_ethers()).value(100u64).from(from.to_ethers()); let tx = provider.send_transaction(tx, None).await.unwrap(); @@ -158,14 +166,14 @@ async fn can_get_pending_block() { #[tokio::test(flavor = "multi_thread")] async fn can_call_on_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let num = provider.get_block_number().await.unwrap(); assert_eq!(num.as_u64(), 0u64); api.anvil_set_auto_mine(false).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -185,9 +193,9 @@ async fn can_call_on_pending_block() { pending_contract.aggregate(vec![]).block(BlockNumber::Pending).call().await.unwrap(); assert_eq!(ret_block_number.as_u64(), 1u64); - let accounts: Vec
= handle.dev_wallets().map(|w| w.address()).collect(); + let accounts: Vec = handle.dev_wallets().map(|w| w.address()).collect(); for i in 1..10 { - api.anvil_set_coinbase(accounts[i % accounts.len()].to_alloy()).await.unwrap(); + api.anvil_set_coinbase(accounts[i % accounts.len()]).await.unwrap(); api.evm_set_block_gas_limit(rU256::from(30_000_000 + i)).unwrap(); api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); @@ -252,11 +260,11 @@ where #[tokio::test(flavor = "multi_thread")] async fn can_call_with_state_override() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.anvil_set_auto_mine(true).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let account = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 987bc05041d5..d0f60d5e941b 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1,8 +1,13 @@ //! various fork related test -use crate::{abi::*, utils}; +use crate::{ + abi::*, + utils::{self, ethers_http_provider}, +}; use alloy_primitives::U256 as rU256; +use alloy_providers::provider::TempProvider; use alloy_rpc_types::{BlockNumberOrTag, CallRequest}; +use alloy_signer::Signer as AlloySigner; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; use ethers::{ @@ -76,7 +81,7 @@ async fn test_spawn_fork() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_balance() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); for _ in 0..10 { let addr = Address::random(); let balance = api.balance(addr.to_alloy(), None).await.unwrap(); @@ -89,7 +94,7 @@ async fn test_fork_eth_get_balance() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_balance_after_mine() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); @@ -113,7 +118,7 @@ async fn test_fork_eth_get_balance_after_mine() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_code_after_mine() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); @@ -132,7 +137,7 @@ async fn test_fork_eth_get_code_after_mine() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_code() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); for _ in 0..10 { let addr = Address::random(); let code = api.get_code(addr.to_alloy(), None).await.unwrap(); @@ -156,7 +161,7 @@ async fn test_fork_eth_get_code() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_nonce() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); for _ in 0..10 { let addr = Address::random(); @@ -174,7 +179,7 @@ async fn test_fork_eth_get_nonce() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_fee_history() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let count = 10u64; let _history = @@ -185,18 +190,18 @@ async fn test_fork_eth_fee_history() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_reset() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); - let from = accounts[0].address(); - let to = accounts[1].address(); + let from = accounts[0].address().to_ethers(); + let to = accounts[1].address().to_ethers(); let block_number = provider.get_block_number().await.unwrap(); let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.transaction_index, 0u64.into()); @@ -205,7 +210,7 @@ async fn test_fork_reset() { assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance_before.saturating_add(amount), to_balance); + assert_eq!(balance_before.saturating_add(amount.to_ethers()), to_balance); api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(block_number.as_u64()), @@ -219,9 +224,9 @@ async fn test_fork_reset() { let nonce = provider.get_transaction_count(from, None).await.unwrap(); assert_eq!(nonce, initial_nonce); let balance = provider.get_balance(from, None).await.unwrap(); - assert_eq!(balance, handle.genesis_balance()); + assert_eq!(balance, handle.genesis_balance().to_ethers()); let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, handle.genesis_balance()); + assert_eq!(balance, handle.genesis_balance().to_ethers()); // reset to latest api.anvil_reset(Some(Forking::default())).await.unwrap(); @@ -233,7 +238,7 @@ async fn test_fork_reset() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_reset_setup() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let dead_addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); @@ -260,8 +265,7 @@ async fn test_fork_reset_setup() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_snapshotting() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); - + let provider = handle.http_provider(); let snapshot = api.evm_snapshot().await.unwrap(); let accounts: Vec<_> = handle.dev_wallets().collect(); @@ -271,14 +275,20 @@ async fn test_fork_snapshotting() { let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let provider = ethers_http_provider(&handle.http_endpoint()); + let tx = TransactionRequest::new() + .to(to.to_ethers()) + .value(amount.to_ethers()) + .from(from.to_ethers()); let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let provider = handle.http_provider(); + let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + 1); + assert_eq!(nonce, initial_nonce + rU256::from(1)); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -296,7 +306,7 @@ async fn test_fork_snapshotting() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_snapshotting_repeated() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = handle.http_provider(); let snapshot = api.evm_snapshot().await.unwrap(); @@ -307,14 +317,17 @@ async fn test_fork_snapshotting_repeated() { let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); - - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let amount = handle.genesis_balance().checked_div(rU256::from(92u64)).unwrap(); - let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::new() + .to(to.to_ethers()) + .value(amount.to_ethers()) + .from(from.to_ethers()); + let tx_provider = ethers_http_provider(&handle.http_endpoint()); + let _ = tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + 1); + assert_eq!(nonce, initial_nonce + rU256::from(1)); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -342,7 +355,8 @@ async fn test_fork_snapshotting_repeated() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_snapshotting_blocks() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = handle.http_provider(); + let tx_provider = ethers_http_provider(&handle.http_endpoint()); // create a snapshot let snapshot = api.evm_snapshot().await.unwrap(); @@ -354,17 +368,20 @@ async fn test_fork_snapshotting_blocks() { let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); // send the transaction - let tx = TransactionRequest::new().to(to).value(amount).from(from); - let _ = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::new() + .to(to.to_ethers()) + .value(amount.to_ethers()) + .from(from.to_ethers()); + let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + 1); + assert_eq!(nonce, initial_nonce + rU256::from(1)); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -376,14 +393,14 @@ async fn test_fork_snapshotting_blocks() { assert_eq!(block_number_after, block_number); // repeat transaction - let _ = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + 1); + assert_eq!(nonce, initial_nonce + rU256::from(1)); // revert again: nothing to revert since snapshot gone assert!(!api.evm_revert(snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + 1); + assert_eq!(nonce, initial_nonce + rU256::from(1)); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); } @@ -394,7 +411,7 @@ async fn test_fork_snapshotting_blocks() { #[tokio::test(flavor = "multi_thread")] async fn test_separate_states() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(14723772u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); @@ -423,9 +440,9 @@ async fn test_separate_states() { #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_on_fork() { let (_api, handle) = spawn(fork_config().with_fork_block_number(Some(14723772u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -448,49 +465,41 @@ async fn can_deploy_greeter_on_fork() { async fn can_reset_properly() { let (origin_api, origin_handle) = spawn(NodeConfig::test()).await; let account = origin_handle.dev_accounts().next().unwrap(); - let origin_provider = origin_handle.ethers_http_provider(); + let origin_provider = origin_handle.http_provider(); let origin_nonce = rU256::from(1u64); - origin_api.anvil_set_nonce(account.to_alloy(), origin_nonce).await.unwrap(); + origin_api.anvil_set_nonce(account, origin_nonce).await.unwrap(); - assert_eq!( - origin_nonce, - origin_provider.get_transaction_count(account, None).await.unwrap().to_alloy() - ); + assert_eq!(origin_nonce, origin_provider.get_transaction_count(account, None).await.unwrap()); let (fork_api, fork_handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await; - let fork_provider = fork_handle.ethers_http_provider(); - assert_eq!( - origin_nonce, - fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() - ); + let fork_provider = fork_handle.http_provider(); + let fork_tx_provider = ethers_http_provider(&fork_handle.http_endpoint()); + assert_eq!(origin_nonce, fork_provider.get_transaction_count(account, None).await.unwrap()); let to = Address::random(); - let to_balance = fork_provider.get_balance(to, None).await.unwrap(); - let tx = TransactionRequest::new().from(account).to(to).value(1337u64); - let tx = fork_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let to_balance = fork_provider.get_balance(to.to_alloy(), None).await.unwrap(); + let tx = TransactionRequest::new().from(account.to_ethers()).to(to).value(1337u64); + let tx = fork_tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); // nonce incremented by 1 assert_eq!( origin_nonce + rU256::from(1), - fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() + fork_provider.get_transaction_count(account, None).await.unwrap() ); // resetting to origin state fork_api.anvil_reset(Some(Forking::default())).await.unwrap(); // nonce reset to origin - assert_eq!( - origin_nonce, - fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() - ); + assert_eq!(origin_nonce, fork_provider.get_transaction_count(account, None).await.unwrap()); // balance is reset - assert_eq!(to_balance, fork_provider.get_balance(to, None).await.unwrap()); + assert_eq!(to_balance, fork_provider.get_balance(to.to_alloy(), None).await.unwrap()); // tx does not exist anymore - assert!(fork_provider.get_transaction(tx.transaction_hash).await.is_err()) + assert!(fork_tx_provider.get_transaction(tx.transaction_hash).await.is_err()) } #[tokio::test(flavor = "multi_thread")] @@ -498,7 +507,7 @@ async fn test_fork_timestamp() { let start = std::time::Instant::now(); let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); @@ -506,7 +515,7 @@ async fn test_fork_timestamp() { let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); @@ -526,7 +535,7 @@ async fn test_fork_timestamp() { let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); @@ -540,13 +549,13 @@ async fn test_fork_timestamp() { .await .unwrap(); api.evm_set_next_block_timestamp(BLOCK_TIMESTAMP + 1).unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP + 1); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); @@ -572,15 +581,16 @@ async fn test_fork_can_send_tx() { spawn(fork_config().with_blocktime(Some(std::time::Duration::from_millis(800)))).await; let wallet = LocalWallet::new(&mut rand::thread_rng()); + let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = SignerMiddleware::new(provider, wallet); - api.anvil_set_balance(wallet.address().to_alloy(), rU256::from(1e18 as u64)).await.unwrap(); - - let provider = SignerMiddleware::new(handle.ethers_http_provider(), wallet); + api.anvil_set_balance(provider.address().to_alloy(), rU256::MAX).await.unwrap(); + let balance = provider.get_balance(provider.address(), None).await.unwrap(); + assert_eq!(balance, rU256::MAX.to_ethers()); let addr = Address::random(); let val = 1337u64; let tx = TransactionRequest::new().to(addr).value(val); - // broadcast it via the eth_sendTransaction API let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -603,7 +613,8 @@ async fn test_fork_nft_set_approve_all() { let wallet = LocalWallet::new(&mut rand::thread_rng()); api.anvil_set_balance(wallet.address().to_alloy(), rU256::from(1000e18 as u64)).await.unwrap(); - let provider = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet.clone())); + let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = Arc::new(SignerMiddleware::new(provider, wallet.clone())); // pick a random nft let nouns_addr: Address = "0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03".parse().unwrap(); @@ -677,7 +688,7 @@ async fn test_fork_can_send_opensea_tx() { // transfer: impersonate real sender api.anvil_impersonate_account(sender.to_alloy()).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let input: Bytes = "0xfb0f3ee1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ff2e795f5000000000000000000000000000023f28ae3e9756ba982a6290f9081b6a84900b758000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c0000000000000000000000000003235b597a78eabcb08ffcb4d97411073211dbcb0000000000000000000000000000000000000000000000000000000000000e72000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062ad47c20000000000000000000000000000000000000000000000000000000062d43104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df44e65d2a2cf40000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000008de9c5a032463c561423387a9648c5c7bcc5bc900000000000000000000000000000000000000000000000000005543df729c0000000000000000000000000006eb234847a9e3a546539aac57a071c01dc3f398600000000000000000000000000000000000000000000000000000000000000416d39b5352353a22cf2d44faa696c2089b03137a13b5acfee0366306f2678fede043bc8c7e422f6f13a3453295a4a063dac7ee6216ab7bade299690afc77397a51c00000000000000000000000000000000000000000000000000000000000000".parse().unwrap(); let to: Address = "0x00000000006c3852cbef3e08e8df289169ede581".parse().unwrap(); @@ -700,13 +711,13 @@ async fn test_fork_base_fee() { let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.anvil_set_next_block_base_fee_per_gas(rU256::ZERO).await.unwrap(); let addr = Address::random(); let val = 1337u64; - let tx = TransactionRequest::new().from(from).to(addr).value(val); + let tx = TransactionRequest::new().from(from.to_ethers()).to(addr).value(val); let _res = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); } @@ -715,7 +726,7 @@ async fn test_fork_base_fee() { async fn test_fork_init_base_fee() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(13184859u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); // @@ -738,7 +749,7 @@ async fn test_reset_fork_on_new_blocks() { ) .await; - let anvil_provider = handle.ethers_http_provider(); + let anvil_provider = ethers_http_provider(&handle.http_endpoint()); let endpoint = next_http_rpc_endpoint(); let provider = Arc::new(get_http_provider(&endpoint).interval(Duration::from_secs(2))); @@ -821,7 +832,7 @@ async fn test_fork_snapshot_block_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_uncles_fetch() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // Block on ETH mainnet with 2 uncles let block_with_uncles = 190u64; @@ -859,7 +870,7 @@ async fn test_fork_uncles_fetch() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_block_transaction_count() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); let sender = accounts[0].address(); @@ -867,9 +878,9 @@ async fn test_fork_block_transaction_count() { // disable automine (so there are pending transactions) api.anvil_set_auto_mine(false).await.unwrap(); // transfer: impersonate real sender - api.anvil_impersonate_account(sender.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(sender).await.unwrap(); - let tx = TransactionRequest::new().from(sender).value(42u64).gas(100_000); + let tx = TransactionRequest::new().from(sender.to_ethers()).value(42u64).gas(100_000); provider.send_transaction(tx, None).await.unwrap(); let pending_txs = @@ -914,7 +925,7 @@ async fn test_fork_block_transaction_count() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_in_fork() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15347924u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let token_holder: Address = "0x2f0b23f53734252bda2277357e97e1517d6b042a".parse().unwrap(); let to = Address::random(); @@ -950,7 +961,7 @@ async fn test_total_difficulty_fork() { let total_difficulty: U256 = 46_673_965_560_973_856_260_636u128.into(); let difficulty: U256 = 13_680_435_288_526_144u128.into(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); assert_eq!(block.total_difficulty, Some(total_difficulty)); assert_eq!(block.difficulty, difficulty); @@ -1012,9 +1023,9 @@ async fn can_override_fork_chain_id() { .with_chain_id(Some(chain_id_override)), ) .await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -1032,7 +1043,7 @@ async fn can_override_fork_chain_id() { let greeting = greeter_contract.greet().call().await.unwrap(); assert_eq!("Hello World!", greeting); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id.as_u64(), chain_id_override); } @@ -1047,12 +1058,12 @@ async fn test_fork_reset_moonbeam() { .with_fork_block_number(None::), ) .await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); @@ -1064,7 +1075,7 @@ async fn test_fork_reset_moonbeam() { .await .unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from); + let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); } @@ -1105,8 +1116,7 @@ async fn test_arbitrum_fork_dev_balance() { let accounts: Vec<_> = handle.dev_wallets().collect(); for acc in accounts { - let balance = - api.balance(acc.address().to_alloy(), Some(Default::default())).await.unwrap(); + let balance = api.balance(acc.address(), Some(Default::default())).await.unwrap(); assert_eq!(balance, rU256::from(100000000000000000000u128)); } } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index bf1c91c685ce..4f6098bfb2df 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,5 +1,6 @@ //! Gas related tests +use crate::utils::ethers_http_provider; use alloy_primitives::U256; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; use ethers::{ @@ -21,7 +22,7 @@ async fn test_basefee_full_block() { .with_gas_limit(Some(GAS_TRANSFER.to_alloy())), ) .await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let base_fee = @@ -44,7 +45,7 @@ async fn test_basefee_half_block() { .with_gas_limit(Some(GAS_TRANSFER.to_alloy() * U256::from(2))), ) .await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); @@ -60,7 +61,7 @@ async fn test_basefee_empty_block() { let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE.to_alloy()))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let base_fee = @@ -80,7 +81,7 @@ async fn test_basefee_empty_block() { async fn test_respect_base_fee() { let base_fee = 50u64; let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let mut tx = TypedTransaction::default(); tx.set_value(100u64); tx.set_to(Address::random()); @@ -100,7 +101,7 @@ async fn test_respect_base_fee() { async fn test_tip_above_fee_cap() { let base_fee = 50u64; let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TypedTransaction::Eip1559( Eip1559TransactionRequest::new() .max_fee_per_gas(base_fee) diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index 5c79d29ab450..6e3204d9d6b3 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -5,6 +5,8 @@ use anvil::{spawn, NodeConfig}; use ethers::{core::rand, prelude::Middleware}; use futures::StreamExt; +use crate::utils::ethers_ipc_provider; + pub fn rand_ipc_endpoint() -> String { let num: u64 = rand::Rng::gen(&mut rand::thread_rng()); if cfg!(windows) { @@ -25,7 +27,7 @@ async fn can_get_block_number_ipc() { let block_num = api.block_number().unwrap(); assert_eq!(block_num, U256::ZERO); - let provider = handle.ethers_ipc_provider().unwrap(); + let provider = ethers_ipc_provider(handle.ipc_path()).unwrap(); let num = provider.get_block_number().await.unwrap(); assert_eq!(num.as_u64(), block_num.to::()); @@ -35,7 +37,7 @@ async fn can_get_block_number_ipc() { async fn test_sub_new_heads_ipc() { let (api, handle) = spawn(ipc_config()).await; - let provider = handle.ethers_ipc_provider().unwrap(); + let provider = ethers_ipc_provider(handle.ipc_path()).unwrap(); let blocks = provider.subscribe_blocks().await.unwrap(); diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index 307eabf27ed3..5fc9cf9f22f1 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -1,21 +1,25 @@ //! log/event related tests -use crate::abi::*; +use crate::{ + abi::*, + utils::{ethers_http_provider, ethers_ws_provider}, +}; use anvil::{spawn, NodeConfig}; use ethers::{ middleware::SignerMiddleware, prelude::{BlockNumber, Filter, FilterKind, Middleware, Signer, H256}, types::Log, }; +use foundry_common::types::ToEthers; use futures::StreamExt; use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn get_past_events() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let address = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -50,9 +54,9 @@ async fn get_past_events() { #[tokio::test(flavor = "multi_thread")] async fn get_all_events() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) @@ -87,9 +91,9 @@ async fn get_all_events() { #[tokio::test(flavor = "multi_thread")] async fn can_install_filter() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) @@ -129,8 +133,9 @@ async fn can_install_filter() { #[tokio::test(flavor = "multi_thread")] async fn watch_events() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let provider = ethers_http_provider(&handle.http_endpoint()); + let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) .unwrap() @@ -143,7 +148,7 @@ async fn watch_events() { let mut stream = event.stream().await.unwrap(); // Also set up a subscription for the same thing - let ws = Arc::new(handle.ethers_ws_provider()); + let ws = Arc::new(ethers_ws_provider(&handle.ws_endpoint())); let contract2 = SimpleStorage::new(contract.address(), ws); let event2 = contract2.event::(); let mut subscription = event2.subscribe().await.unwrap(); diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index abd4de130484..de9f0c875672 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -1,5 +1,6 @@ //! Tests for OP chain support. +use crate::utils::ethers_http_provider; use anvil::{spawn, Hardfork, NodeConfig}; use ethers::{ abi::Address, @@ -17,7 +18,7 @@ use std::str::FromStr; async fn test_deposits_not_supported_if_optimism_disabled() { // optimism disabled by default let (_, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); @@ -53,7 +54,7 @@ async fn test_send_value_deposit_transaction() { // enable the Optimism flag let (api, handle) = spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let send_value = U256::from(1234); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); @@ -100,7 +101,7 @@ async fn test_send_value_raw_deposit_transaction() { // enable the Optimism flag let (api, handle) = spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let send_value = U256::from(1234); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index ab7725981443..8dca5f7a2e25 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,7 +1,11 @@ //! tests for otterscan endpoints -use crate::abi::MulticallContract; +use crate::{ + abi::MulticallContract, + utils::{ethers_http_provider, ethers_ws_provider}, +}; use alloy_primitives::U256 as rU256; use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; +use alloy_signer::Signer as AlloySigner; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, @@ -12,7 +16,7 @@ use ethers::{ abi::Address, prelude::{ContractFactory, ContractInstance, Middleware, SignerMiddleware}, signers::Signer, - types::{Bytes, TransactionRequest, U256}, + types::{Bytes, TransactionRequest}, utils::get_contract_address, }; use ethers_solc::{project_util::TempProject, Artifact}; @@ -41,9 +45,9 @@ async fn can_call_ots_get_api_level() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_internal_operations_contract_deploy() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -70,16 +74,18 @@ async fn can_call_ots_get_internal_operations_contract_deploy() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_internal_operations_contract_transfer() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); let to = accounts[1].address(); - //let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new() + .to(to.to_ethers()) + .value(amount.to_ethers()) + .from(from.to_ethers()); let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -90,9 +96,9 @@ async fn can_call_ots_get_internal_operations_contract_transfer() { res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Transfer, - from: from.to_alloy(), - to: to.to_alloy(), - value: amount.to_alloy() + from, + to, + value: amount } ); } @@ -125,8 +131,8 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); - let wallets = handle.dev_wallets().collect::>(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully @@ -136,7 +142,7 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), + SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), ); let call = contract.method::<_, ()>("deploy", ()).unwrap(); @@ -183,8 +189,8 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); - let wallets = handle.dev_wallets().collect::>(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully @@ -194,7 +200,7 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), + SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); @@ -217,9 +223,9 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_has_code() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); api.mine_one().await; @@ -296,8 +302,8 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); - let wallets = handle.dev_wallets().collect::>(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully @@ -307,7 +313,7 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), + SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), ); let call = contract.method::<_, ()>("run", ()).unwrap().value(1337); let receipt = call.send().await.unwrap().await.unwrap().unwrap(); @@ -330,7 +336,7 @@ contract Contract { depth: 1, from: contract.address().to_alloy(), to: contract.address().to_alloy(), - value: U256::zero().to_alloy(), + value: rU256::ZERO, input: Bytes::from_str("0x6a6758fe").unwrap().0.into() }, OtsTrace { @@ -338,7 +344,7 @@ contract Contract { depth: 1, from: contract.address().to_alloy(), to: contract.address().to_alloy(), - value: U256::zero().to_alloy(), + value: rU256::ZERO, input: Bytes::from_str("0x96385e39").unwrap().0.into() }, OtsTrace { @@ -346,7 +352,7 @@ contract Contract { depth: 2, from: contract.address().to_alloy(), to: wallets[0].address().to_alloy(), - value: U256::from(1337).to_alloy(), + value: rU256::from(1337), input: Bytes::from_str("0x").unwrap().0.into() }, OtsTrace { @@ -354,7 +360,7 @@ contract Contract { depth: 2, from: contract.address().to_alloy(), to: contract.address().to_alloy(), - value: U256::zero().to_alloy(), + value: rU256::ZERO, input: Bytes::from_str("0xa1325397").unwrap().0.into() }, ] @@ -385,9 +391,9 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); // deploy successfully @@ -406,9 +412,9 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let tx = TransactionRequest::new().to(Address::random()).value(100u64); @@ -428,9 +434,9 @@ async fn can_call_ots_get_block_details() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details_by_hash() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let tx = TransactionRequest::new().to(Address::random()).value(100u64); @@ -451,9 +457,9 @@ async fn can_call_ots_get_block_details_by_hash() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); // disable automine @@ -493,9 +499,9 @@ async fn can_call_ots_get_block_transactions() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_search_transactions_before() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -529,9 +535,9 @@ async fn can_call_ots_search_transactions_before() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_search_transactions_after() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -565,10 +571,10 @@ async fn can_call_ots_search_transactions_after() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_transaction_by_sender_and_nonce() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.mine_one().await; - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -594,10 +600,10 @@ async fn can_call_ots_get_transaction_by_sender_and_nonce() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_contract_creator() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.mine_one().await; - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); diff --git a/crates/anvil/tests/it/proof/mod.rs b/crates/anvil/tests/it/proof/mod.rs index cbe803fb2817..85e8c630f7d5 100644 --- a/crates/anvil/tests/it/proof/mod.rs +++ b/crates/anvil/tests/it/proof/mod.rs @@ -1,15 +1,11 @@ //! tests for `eth_getProof` use crate::proof::eip1186::verify_proof; +use alloy_primitives::{keccak256, Address, B256, U256}; +use alloy_rlp::Decodable; use alloy_rpc_types::EIP1186AccountProofResponse; use anvil::{spawn, NodeConfig}; use anvil_core::eth::{proof::BasicAccount, trie::ExtensionLayout}; -use ethers::{ - abi::ethereum_types::BigEndianHash, - types::{Address, H256, U256}, - utils::{keccak256, rlp}, -}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::revm::primitives::KECCAK_EMPTY; mod eip1186; @@ -20,50 +16,48 @@ async fn can_get_proof() { let acc: Address = "0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa".parse().unwrap(); - let key = U256::zero(); - let value = U256::one(); + let key = U256::ZERO; + let value = U256::from(1); - api.anvil_set_storage_at(acc.to_alloy(), key.to_alloy(), H256::from_uint(&value).to_alloy()) - .await - .unwrap(); + api.anvil_set_storage_at(acc, key, B256::from(value)).await.unwrap(); let proof: EIP1186AccountProofResponse = - api.get_proof(acc.to_alloy(), vec![H256::from_uint(&key).to_alloy()], None).await.unwrap(); + api.get_proof(acc, vec![B256::from(key)], None).await.unwrap(); let account = BasicAccount { - nonce: 0.into(), - balance: 0.into(), - storage_root: proof.storage_hash.to_ethers(), - code_hash: KECCAK_EMPTY.to_ethers(), + nonce: U256::from(0), + balance: U256::from(0), + storage_root: proof.storage_hash, + code_hash: KECCAK_EMPTY, }; - let rlp_account = rlp::encode(&account); + let rlp_account = alloy_rlp::encode(&account); - let root: H256 = api.state_root().await.unwrap().to_ethers(); + let root: B256 = api.state_root().await.unwrap(); let acc_proof: Vec> = proof .account_proof .into_iter() - .map(|node| rlp::decode::>(&node).unwrap()) + .map(|node| Vec::::decode(&mut &node[..]).unwrap()) .collect(); verify_proof::( &root.0, &acc_proof, - &keccak256(acc.as_bytes())[..], + &keccak256(acc.as_slice())[..], Some(rlp_account.as_ref()), ) .unwrap(); assert_eq!(proof.storage_proof.len(), 1); - let expected_value = rlp::encode(&value); + let expected_value = alloy_rlp::encode(value); let proof = proof.storage_proof[0].clone(); let storage_proof: Vec> = - proof.proof.into_iter().map(|node| rlp::decode::>(&node).unwrap()).collect(); - let key = H256::from(keccak256(proof.key.0 .0)); + proof.proof.into_iter().map(|node| Vec::::decode(&mut &node[..]).unwrap()).collect(); + let key = B256::from(keccak256(proof.key.0 .0)); verify_proof::( &account.storage_root.0, &storage_proof, - key.as_bytes(), + key.as_slice(), Some(expected_value.as_ref()), ) .unwrap(); @@ -75,7 +69,7 @@ async fn can_get_random_account_proofs() { for acc in std::iter::repeat_with(Address::random).take(10) { let _ = api - .get_proof(acc.to_alloy(), Vec::new(), None) + .get_proof(acc, Vec::new(), None) .await .unwrap_or_else(|_| panic!("Failed to get proof for {acc:?}")); } diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index 31671a53809e..a8dd6d3d7555 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -1,5 +1,6 @@ //! tests for subscriptions +use crate::utils::{ethers_http_provider, ethers_ws_provider}; use anvil::{spawn, NodeConfig}; use ethers::{ contract::abigen, @@ -9,7 +10,7 @@ use ethers::{ signers::Signer, types::{Address, Block, Filter, TransactionRequest, TxHash, ValueOrArray, U256}, }; -use foundry_common::types::ToAlloy; +use foundry_common::types::{ToAlloy, ToEthers}; use futures::StreamExt; use std::sync::Arc; @@ -17,7 +18,7 @@ use std::sync::Arc; async fn test_sub_new_heads() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); let blocks = provider.subscribe_blocks().await.unwrap(); @@ -35,9 +36,9 @@ async fn test_sub_logs_legacy() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let msg = "First Message".to_string(); @@ -74,9 +75,9 @@ async fn test_sub_logs() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let msg = "First Message".to_string(); @@ -112,7 +113,7 @@ async fn test_sub_logs_impersonated() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); // impersonate account let impersonate = Address::random(); @@ -120,7 +121,7 @@ async fn test_sub_logs_impersonated() { api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let msg = "First Message".to_string(); @@ -138,7 +139,7 @@ async fn test_sub_logs_impersonated() { let tx = TransactionRequest::new().from(impersonate).to(contract.address()).data(data); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -153,9 +154,9 @@ async fn test_filters_legacy() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -194,9 +195,9 @@ async fn test_filters() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -254,7 +255,7 @@ async fn test_subscriptions() { async fn test_sub_new_heads_fast() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); let blocks = provider.subscribe_blocks().await.unwrap(); diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index c802c0ff6e67..79b8efbf1769 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -1,9 +1,12 @@ +use crate::utils::ethers_http_provider; +use alloy_dyn_abi::TypedData; use anvil::{spawn, NodeConfig}; use ethers::{ prelude::{Middleware, SignerMiddleware}, signers::Signer, - types::{transaction::eip712::TypedData, Address, Chain, TransactionRequest}, + types::{Address, Chain, TransactionRequest}, }; +use foundry_common::types::ToEthers; #[tokio::test(flavor = "multi_thread")] async fn can_sign_typed_data() { @@ -283,23 +286,24 @@ async fn can_sign_typed_data_os() { #[tokio::test(flavor = "multi_thread")] async fn rejects_different_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = SignerMiddleware::new(provider, wallet.with_chain_id(Chain::Mainnet)); let tx = TransactionRequest::new().to(Address::random()).value(100u64); let res = client.send_transaction(tx, None).await; let err = res.unwrap_err(); - assert!(err.to_string().contains("signed for another chain")); + assert!(err.to_string().contains("signed for another chain"), "{}", err.to_string()); } #[tokio::test(flavor = "multi_thread")] async fn rejects_invalid_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().with_chain_id(99u64); - let provider = handle.ethers_http_provider(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = wallet.with_chain_id(99u64); + let provider = ethers_http_provider(&handle.http_endpoint()); let client = SignerMiddleware::new(provider, wallet); let tx = TransactionRequest::new().to(Address::random()).value(100u64); let res = client.send_transaction(tx, None).await; diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index be192fec23ea..e7d42051b1a4 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,4 +1,8 @@ -use crate::fork::fork_config; +use crate::{ + fork::fork_config, + utils::{ethers_http_provider, ethers_ws_provider}, +}; +use alloy_primitives::U256; use anvil::{spawn, NodeConfig}; use ethers::{ contract::ContractInstance, @@ -10,20 +14,20 @@ use ethers::{ utils::hex, }; use ethers_solc::{project_util::TempProject, Artifact}; -use foundry_common::types::ToAlloy; +use foundry_common::types::{ToAlloy, ToEthers}; use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn test_get_transfer_parity_traces() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); // specify the `from` field so that the client knows which account to use - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); // broadcast it via the eth_sendTransaction API let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -35,7 +39,7 @@ async fn test_get_transfer_parity_traces() { Action::Call(ref call) => { assert_eq!(call.from, from); assert_eq!(call.to, to); - assert_eq!(call.value, amount); + assert_eq!(call.value, amount.to_ethers()); } _ => unreachable!("unexpected action"), } @@ -73,8 +77,8 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); - let wallets = handle.dev_wallets().collect::>(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully @@ -84,13 +88,15 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), + SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); let tx = call.send().await.unwrap().await.unwrap().unwrap(); - let traces = - handle.ethers_http_provider().trace_transaction(tx.transaction_hash).await.unwrap(); + let traces = ethers_http_provider(&handle.http_endpoint()) + .trace_transaction(tx.transaction_hash) + .await + .unwrap(); assert!(!traces.is_empty()); assert_eq!(traces[1].action_type, ActionType::Suicide); } @@ -121,8 +127,8 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); - let wallets = handle.dev_wallets().collect::>(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully @@ -132,12 +138,11 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), + SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); - let traces = handle - .ethers_http_provider() + let traces = ethers_http_provider(&handle.http_endpoint()) .debug_trace_call(call.tx, None, GethDebugTracingCallOptions::default()) .await .unwrap(); @@ -160,7 +165,7 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn test_trace_address_fork() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15291050u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let input = hex::decode("43bcfab60000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000e0bd811c8769a824b00000000000000000000000000000000000000000000000e0ae9925047d8440b60000000000000000000000002e4777139254ff76db957e284b186a4507ff8c67").unwrap(); @@ -353,7 +358,7 @@ async fn test_trace_address_fork() { #[tokio::test(flavor = "multi_thread")] async fn test_trace_address_fork2() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15314401u64))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let input = hex::decode("30000003000000000000000000000000adda1059a6c6c102b0fa562b9bb2cb9a0de5b1f4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a300000004fffffffffffffffffffffffffffffffffffffffffffff679dc91ecfe150fb980c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f4d2888d29d722226fafa5d9b24f9164c092421e000bb8000000000000004319b52bf08b65295d49117e790000000000000000000000000000000000000000000000008b6d9e8818d6141f000000000000000000000000000000000000000000000000000000086a23af210000000000000000000000000000000000000000000000000000000000").unwrap(); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index b93286debd6f..9facf3dca9c4 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1,4 +1,9 @@ -use crate::abi::*; +use crate::{ + abi::*, + utils::{ethers_http_provider, ethers_ws_provider}, +}; +use alloy_primitives::U256 as rU256; +use alloy_signer::Signer as AlloySigner; use anvil::{spawn, Hardfork, NodeConfig}; use ethers::{ abi::ethereum_types::BigEndianHash, @@ -19,9 +24,9 @@ use tokio::time::timeout; #[tokio::test(flavor = "multi_thread")] async fn can_transfer_eth() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); @@ -30,11 +35,11 @@ async fn can_transfer_eth() { let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); // craft the tx // specify the `from` field so that the client knows which account to use - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); // broadcast it via the eth_sendTransaction API let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -48,31 +53,36 @@ async fn can_transfer_eth() { let to_balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance_before.saturating_add(amount), to_balance); + assert_eq!(balance_before.saturating_add(amount.to_ethers()), to_balance); } #[tokio::test(flavor = "multi_thread")] async fn can_order_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // disable automine api.anvil_set_auto_mine(false).await.unwrap(); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); // craft the tx with lower price - let tx = TransactionRequest::new().to(to).from(from).value(amount).gas_price(gas_price); + let tx = + TransactionRequest::new().to(to).from(from).value(amount.to_ethers()).gas_price(gas_price); let tx_lower = provider.send_transaction(tx, None).await.unwrap(); // craft the tx with higher price - let tx = TransactionRequest::new().to(from).from(to).value(amount).gas_price(gas_price + 1); + let tx = TransactionRequest::new() + .to(from) + .from(to) + .value(amount.to_ethers()) + .gas_price(gas_price + 1); let tx_higher = provider.send_transaction(tx, None).await.unwrap(); // manually mine the block with the transactions @@ -88,16 +98,16 @@ async fn can_order_transactions() { #[tokio::test(flavor = "multi_thread")] async fn can_respect_nonces() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(3u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); // send the transaction with higher nonce than on chain let higher_pending_tx = @@ -127,17 +137,17 @@ async fn can_replace_transaction() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); - let amount = handle.genesis_balance().checked_div(3u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from).nonce(nonce); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from).nonce(nonce); // send transaction with lower gas price let lower_priced_pending_tx = @@ -165,16 +175,16 @@ async fn can_replace_transaction() { #[tokio::test(flavor = "multi_thread")] async fn can_reject_too_high_gas_limits() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); let gas_limit = api.gas_limit(); - let amount = handle.genesis_balance().checked_div(3u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); // send transaction with the exact gas limit let pending = provider.send_transaction(tx.clone().gas(gas_limit.to_ethers()), None).await; @@ -202,17 +212,17 @@ async fn can_reject_underpriced_replacement() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); - let amount = handle.genesis_balance().checked_div(3u64.into()).unwrap(); + let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from).nonce(nonce); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from).nonce(nonce); // send transaction with higher gas price let higher_priced_pending_tx = @@ -237,9 +247,9 @@ async fn can_reject_underpriced_replacement() { #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_http() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -270,9 +280,9 @@ async fn can_deploy_and_mine_manually() { // can mine in manual mode api.evm_mine(None).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let tx = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; @@ -303,12 +313,12 @@ async fn can_deploy_and_mine_manually() { #[tokio::test(flavor = "multi_thread")] async fn can_mine_automatically() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let tx = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; @@ -324,9 +334,9 @@ async fn can_mine_automatically() { #[tokio::test(flavor = "multi_thread")] async fn can_call_greeter_historic() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -359,9 +369,9 @@ async fn can_call_greeter_historic() { #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_ws() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -384,9 +394,9 @@ async fn can_deploy_greeter_ws() { #[tokio::test(flavor = "multi_thread")] async fn can_deploy_get_code() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -403,9 +413,9 @@ async fn can_deploy_get_code() { #[tokio::test(flavor = "multi_thread")] async fn get_blocktimestamp_works() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = @@ -442,9 +452,9 @@ async fn get_blocktimestamp_works() { #[tokio::test(flavor = "multi_thread")] async fn call_past_state() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) @@ -499,9 +509,9 @@ async fn call_past_state() { async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); @@ -529,9 +539,9 @@ async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { #[tokio::test(flavor = "multi_thread")] async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let nonce = client.get_transaction_count(from, None).await.unwrap(); @@ -563,9 +573,9 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { #[tokio::test(flavor = "multi_thread")] async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -622,10 +632,10 @@ async fn can_get_pending_transaction() { // disable auto mining so we can check if we can return pending tx from the mempool api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let from = handle.dev_wallets().next().unwrap().address(); - let tx = TransactionRequest::new().from(from).value(1337u64).to(Address::random()); + let tx = TransactionRequest::new().from(from.to_ethers()).value(1337u64).to(Address::random()); let tx = provider.send_transaction(tx, None).await.unwrap(); let pending = provider.get_transaction(tx.tx_hash()).await.unwrap(); @@ -643,11 +653,11 @@ async fn test_first_noce_is_zero() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let from = handle.dev_wallets().next().unwrap().address(); let nonce = provider - .get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))) + .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) .await .unwrap(); @@ -660,8 +670,8 @@ async fn can_handle_different_sender_nonce_calculation() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let provider = ethers_http_provider(&handle.http_endpoint()); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from_first = accounts[0].address(); let from_second = accounts[1].address(); @@ -695,17 +705,18 @@ async fn includes_pending_tx_for_transaction_count() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let from = handle.dev_wallets().next().unwrap().address(); let tx_count = 10u64; // send a bunch of tx to the mempool and check nonce is returned correctly for idx in 1..=tx_count { - let tx = TransactionRequest::new().from(from).value(1337u64).to(Address::random()); + let tx = + TransactionRequest::new().from(from.to_ethers()).value(1337u64).to(Address::random()); let _tx = provider.send_transaction(tx, None).await.unwrap(); let nonce = provider - .get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))) + .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) .await .unwrap(); assert_eq!(nonce, idx.into()); @@ -713,7 +724,7 @@ async fn includes_pending_tx_for_transaction_count() { api.mine_one().await; let nonce = provider - .get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))) + .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) .await .unwrap(); assert_eq!(nonce, tx_count.into()); @@ -722,14 +733,14 @@ async fn includes_pending_tx_for_transaction_count() { #[tokio::test(flavor = "multi_thread")] async fn can_get_historic_info() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts: Vec<_> = handle.dev_wallets().collect(); + let accounts = handle.dev_wallets().collect::>().to_ethers(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(2u64.into()).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount).from(from); + let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let nonce_pre = provider @@ -750,7 +761,7 @@ async fn can_get_historic_info() { assert!(balance_post < balance_pre); let to_balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance_pre.saturating_add(amount), to_balance); + assert_eq!(balance_pre.saturating_add(amount.to_ethers()), to_balance); } // @@ -758,8 +769,9 @@ async fn can_get_historic_info() { async fn test_tx_receipt() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let client = + Arc::new(SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallet)); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); @@ -780,8 +792,8 @@ async fn can_stream_pending_transactions() { let (_api, handle) = spawn(NodeConfig::test().with_blocktime(Some(Duration::from_secs(2)))).await; let num_txs = 5; - let provider = handle.ethers_http_provider(); - let ws_provider = handle.ethers_ws_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); + let ws_provider = ethers_ws_provider(&handle.ws_endpoint()); let accounts = provider.get_accounts().await.unwrap(); let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64); @@ -863,8 +875,9 @@ async fn test_tx_access_list() { // - The sender shouldn't be in the AL let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let client = + Arc::new(SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallet)); let sender = Address::random(); let other_acc = Address::random(); @@ -933,9 +946,9 @@ async fn estimates_gas_on_pending_by_default() { // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let sender = wallet.address(); let recipient = Address::random(); @@ -952,7 +965,7 @@ async fn estimates_gas_on_pending_by_default() { #[tokio::test(flavor = "multi_thread")] async fn test_reject_gas_too_low() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); let account = handle.dev_accounts().next().unwrap(); @@ -960,7 +973,7 @@ async fn test_reject_gas_too_low() { let tx = TransactionRequest::new() .to(Address::random()) .value(U256::from(1337u64)) - .from(account) + .from(account.to_ethers()) .gas(gas); let resp = provider.send_transaction(tx, None).await; @@ -974,9 +987,9 @@ async fn test_reject_gas_too_low() { async fn can_call_with_high_gas_limit() { let (_api, handle) = spawn(NodeConfig::test().with_gas_limit(Some(U256::from(100_000_000).to_alloy()))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) @@ -992,9 +1005,9 @@ async fn can_call_with_high_gas_limit() { #[tokio::test(flavor = "multi_thread")] async fn test_reject_eip1559_pre_london() { let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap(); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); let gas_limit = api.gas_limit(); diff --git a/crates/anvil/tests/it/txpool.rs b/crates/anvil/tests/it/txpool.rs index 3f17e5beeaac..3c10a577f306 100644 --- a/crates/anvil/tests/it/txpool.rs +++ b/crates/anvil/tests/it/txpool.rs @@ -1,5 +1,6 @@ //! txpool related tests +use crate::utils::ethers_http_provider; use anvil::{spawn, NodeConfig}; use ethers::{ prelude::Middleware, @@ -9,7 +10,7 @@ use ethers::{ #[tokio::test(flavor = "multi_thread")] async fn geth_txpool() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_http_provider(); + let provider = ethers_http_provider(&handle.http_endpoint()); api.anvil_set_auto_mine(false).await.unwrap(); let account = provider.get_accounts().await.unwrap()[0]; diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 0a566609300f..7fdd90823a3d 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -2,6 +2,7 @@ use ethers::{ addressbook::contract, types::{Address, Chain}, }; +use foundry_common::provider::ethers::{ProviderBuilder, RetryProvider}; /// Returns a set of various contract addresses pub fn contract_addresses(chain: Chain) -> Vec
{ @@ -13,3 +14,18 @@ pub fn contract_addresses(chain: Chain) -> Vec
{ contract("uniswapV3SwapRouter02").unwrap().address(chain).unwrap(), ] } + +/// Builds an ethers HTTP [RetryProvider] +pub fn ethers_http_provider(http_endpoint: &str) -> RetryProvider { + ProviderBuilder::new(http_endpoint).build().expect("failed to build ethers HTTP provider") +} + +/// Builds an ethers ws [RetryProvider] +pub fn ethers_ws_provider(ws_endpoint: &str) -> RetryProvider { + ProviderBuilder::new(ws_endpoint).build().expect("failed to build ethers HTTP provider") +} + +/// Builds an ethers ws [RetryProvider] +pub fn ethers_ipc_provider(ipc_endpoint: Option) -> Option { + ProviderBuilder::new(&ipc_endpoint?).build().ok() +} diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index f1212bd410e5..7d31bdbcab75 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,7 +1,8 @@ //! general eth api tests with websocket provider +use alloy_providers::provider::TempProvider; use anvil::{spawn, NodeConfig}; -use ethers::{prelude::Middleware, types::U256}; +use ethers::types::U256; use foundry_common::types::ToAlloy; #[tokio::test(flavor = "multi_thread")] @@ -10,16 +11,16 @@ async fn can_get_block_number_ws() { let block_num = api.block_number().unwrap(); assert_eq!(block_num, U256::zero().to_alloy()); - let provider = handle.ethers_ws_provider(); + let provider = handle.ws_provider(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, block_num.to::().into()); + assert_eq!(num, block_num.to::()); } #[tokio::test(flavor = "multi_thread")] async fn can_dev_get_balance_ws() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ethers_ws_provider(); + let provider = handle.ws_provider(); let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index a7def0f5c6c7..530d75d8ef70 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,6 +17,7 @@ foundry-config.workspace = true ethers-core.workspace = true ethers-middleware.workspace = true ethers-providers = { workspace = true, features = ["ws", "ipc"] } +ethers-signers.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true @@ -25,6 +26,7 @@ alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true alloy-providers.workspace = true alloy-transport.workspace = true +alloy-signer.workspace = true alloy-transport-http.workspace = true alloy-transport-ws.workspace = true alloy-transport-ipc.workspace = true diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 4247cc1eb808..aea3bf6319bc 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -3,6 +3,7 @@ use alloy_json_abi::{Event, EventParam, Function, InternalType, Param, StateMutability}; use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U128, U256, U64}; use alloy_rpc_types::{AccessList, AccessListItem, CallInput, CallRequest, Signature, Transaction}; +use alloy_signer::{LocalWallet, Signer}; use ethers_core::{ abi as ethabi, types::{ @@ -140,6 +141,26 @@ impl ToAlloy for ethers_core::types::Transaction { } } +impl ToEthers for alloy_signer::LocalWallet { + type To = ethers_signers::LocalWallet; + + fn to_ethers(self) -> Self::To { + ethers_signers::LocalWallet::new_with_signer( + self.signer().clone(), + self.address().to_ethers(), + self.chain_id().unwrap(), + ) + } +} + +impl ToEthers for Vec { + type To = Vec; + + fn to_ethers(self) -> Self::To { + self.into_iter().map(ToEthers::to_ethers).collect() + } +} + /// Converts from a [TransactionRequest] to a [CallRequest]. pub fn to_call_request_from_tx_request(tx: TransactionRequest) -> CallRequest { CallRequest { diff --git a/deny.toml b/deny.toml index 816574f430e9..e3ae679d035c 100644 --- a/deny.toml +++ b/deny.toml @@ -67,9 +67,6 @@ exceptions = [ { allow = ["CC0-1.0"], name = "constant_time_eq" }, { allow = ["CC0-1.0"], name = "dunce" }, { allow = ["CC0-1.0"], name = "aurora-engine-modexp" }, - - { allow = ["GPL-3.0"], name = "fastrlp" }, - { allow = ["GPL-3.0"], name = "fastrlp-derive" }, ] #copyleft = "deny"