From d46bcb3ddc768b35da8d1d29f85886476f974128 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Mon, 8 Jan 2024 14:48:19 +0100 Subject: [PATCH] refactor: migrate to alloy providers (#6219) * wip * fmt * wip * more wip * wip * wip * rebase stuff * more rebase stuff * wip * fmt * wip resolve warnings * wip * wip * wip * aaand cast started complaining gn * feat(`anvil`): RPC Types migration (#6256) * wip * fmt * wip * feat: start migrating basic types * more type changes across /eth and backend * wip type replacements * chore: replace mistaken type * feat: rpc types without pubsub migrated * chore: changes from rpc types * chore: re-add changes * feat: rpc types done, onto providers * we on that grind * feat: types migrated, tests remaining * chore: comment tests for now to get proper compile errors --------- Co-authored-by: Oliver Nordbjerg * fix latest rebase errors * chore: `alloy-transports` -> `alloy-transport` (#6341) * fix: use new `TransportResult` (#6342) * fix: `TransportResult` for multi wallet (#6343) * feat: use TransportError on Anvil (#6344) * fix: uncomment serde tests, fix genesis test (#6346) * refactor: split ethers/alloy providers (#6378) * split ethers/alloy providers * chore: complete Transaction ToAlloy compat trait * chore: clone txs --------- Co-authored-by: Enrique Ortiz * fix: block numbers are u64 (#6382) * fix: remove U64 cast (#6384) * chore(`anvil`): fix tests (#6383) * wip * chore: fix u64 * fix: access lists * chore: use latest ethers * silly ahhh otterscan test failing * more fixdy * almost done lesgo * pub sob * chre: fix otterscan test --------- Co-authored-by: Oliver Nordbjerg * fix(anvil): port serde helpers, remove multiline jsons (#6404) * chore(`anvil`): extend alloy types until entry lib, cleanup warnings (#6413) * chore: extend alloy types until entry lib, cleanup warnings * chore: fix review comments * chore: pin alloy providers to repo & test fixup (#6414) * chore: pin alloy providers to repo * chore: fix doctests * clippy * clippy * clippy * fix fixtures * update * feat: reimplement rpc cheatcode * chore: fix warnings * chore: fmt, switch to local tx request type in the meanwhile for optimism tests * [Alloy providers migration]: Fix tests (#6462) * fix: properly return tx-only blocks instead of full blocks * chore: fix up fork tests * chore: mine blocks first before getting tx receipt * clippy/fmt * clippy * clippy * chore: add more delta for timestamp test * chore: fix up anvil API tests (#6517) * chore: fix up anvil API tests * fmt * feat: `RuntimeTransport` for `ws`/`ipc` support (#6525) * wip * chore: change to using the naked transport instead of an RpcClient * chore: properly implement request * always poll ready * chore: docs * chore: clippy and other fixes * chore: use call to pass down the tower stack * chore: no unwraps * chore: new fn * feat: implement runtimetransport on the alloy provider builder * chore: refactor into separate functions depending on transport * feat: RuntimeTransportBuilder, refactor * feat: cleanup, docs * feat: IPC * clippy * cosmetics * feat: enable anvil ipc tests (#6570) * refactor: replace tracers with reth tracers (#6428) * wip * wip not working still -.- - gas reports - `forge script` cannot set `gas_used` for root trace since that requires mut access to the arena - event decoding not impl possibly broken - anvil traces (not tested) - test traces (not tested) - debugging (not tested) but hey, at least it compiles... * wip * chore: fix deps * remove utils import * chore: remove errors * chore: use render_trace_arena * derive debug * fix contract identification * fix formatting * remove stray todo * fix empty output * fix unrelated test :) --------- Co-authored-by: evalir * fix lockfile * chore: fix anvil test * fixup: missing import * chore: lint * fixup: imports * fixup: more fixes * chore: fmt * clippy clippy clippy always clippy * and fmt * test: fix test * chore: add todo * clippy * feat(`anvil`/`evm`): re-add support for additional fields (#6573) * feat: re-add additional fields support * chore: clippy/fmt * bump Cargo.lock * chore: handle mixhash being optional (#6575) * fix: always ensure prevrandao is set (#6576) * chore: cleanup, reintroduce trace endpoints (#6577) * feat: re-enable debug_traceTransaction support (#6583) * chore: fix ipc test * chore: re-enable trace tests (#6586) * refactor: rm unused param * refactor: rm gas adjustment in script This should be solved in the tracer inspector itself already, cc @mattsse * chore: fix ci failures (#6595) * chore: use self.debug instead of self.verbose * chore: update lock * chore: fix deny check * feat(`RuntimeTransport`): port cups/retry logic (#6594) * some random stuff * fmt: toml * chore: use u64 in BlockRequest::Number * update * chore: remove some more ethers * chore: more ethers removals (units) * wip: remove reth * wip * rm ice * fix(`rpc-types`): use newer `alloy-rpc-types`, `alloy-rpc-trace-types` (#6705) * fix(rpc-types): use newer alloy-rpc-types, alloy-rpc-trace-types and reth inspectors * chore: remove ToReth * chore: fix imports * chore: quicknode retry (#6712) * fix(`general`): get ci green on 6219 (#6711) * chore: update doctests * chore: update cargo lock to make anvil test pass * chore: bump cargo lock again * fix: parse unit logics (#6713) --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: remove borrows * chore: add removal note * chore: use alloy chains to compare * chore: remove some conversions, migrate feemanager/newblocknotifications * chore: clippy * chore: fix doctests --------- Co-authored-by: evalir Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 1058 ++++++++++++++++- Cargo.toml | 32 +- crates/anvil/Cargo.toml | 6 + crates/anvil/core/Cargo.toml | 8 +- crates/anvil/core/src/eth/mod.rs | 195 +-- crates/anvil/core/src/eth/serde_helpers.rs | 57 + .../core/src/eth/transaction/ethers_compat.rs | 287 ++++- crates/anvil/core/src/eth/transaction/mod.rs | 44 +- crates/anvil/core/src/types.rs | 19 +- crates/anvil/src/cmd.rs | 12 +- crates/anvil/src/config.rs | 121 +- crates/anvil/src/eth/api.rs | 586 +++++---- crates/anvil/src/eth/backend/db.rs | 58 +- crates/anvil/src/eth/backend/executor.rs | 16 +- crates/anvil/src/eth/backend/fork.rs | 325 ++--- crates/anvil/src/eth/backend/genesis.rs | 29 +- crates/anvil/src/eth/backend/info.rs | 9 +- crates/anvil/src/eth/backend/mem/cache.rs | 10 +- crates/anvil/src/eth/backend/mem/fork_db.rs | 27 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 75 +- crates/anvil/src/eth/backend/mem/inspector.rs | 13 +- crates/anvil/src/eth/backend/mem/mod.rs | 714 ++++++----- crates/anvil/src/eth/backend/mem/state.rs | 48 +- crates/anvil/src/eth/backend/mem/storage.rs | 164 ++- crates/anvil/src/eth/backend/notifications.rs | 4 +- crates/anvil/src/eth/error.rs | 7 + crates/anvil/src/eth/fees.rs | 38 +- crates/anvil/src/eth/miner.rs | 2 +- crates/anvil/src/eth/otterscan/api.rs | 56 +- crates/anvil/src/eth/otterscan/types.rs | 125 +- crates/anvil/src/eth/pool/mod.rs | 25 +- crates/anvil/src/eth/pool/transactions.rs | 37 +- crates/anvil/src/filter.rs | 10 +- crates/anvil/src/genesis.rs | 35 +- crates/anvil/src/lib.rs | 41 +- crates/anvil/src/pubsub.rs | 69 +- crates/anvil/src/server/handler.rs | 19 +- crates/anvil/tests/it/anvil.rs | 13 +- crates/anvil/tests/it/anvil_api.rs | 150 +-- crates/anvil/tests/it/api.rs | 70 +- crates/anvil/tests/it/fork.rs | 230 ++-- crates/anvil/tests/it/gas.rs | 27 +- crates/anvil/tests/it/genesis.rs | 11 +- crates/anvil/tests/it/ipc.rs | 11 +- crates/anvil/tests/it/logs.rs | 10 +- crates/anvil/tests/it/optimism.rs | 25 +- crates/anvil/tests/it/otterscan.rs | 194 +-- crates/anvil/tests/it/proof/mod.rs | 23 +- crates/anvil/tests/it/pubsub.rs | 21 +- crates/anvil/tests/it/sign.rs | 4 +- crates/anvil/tests/it/traces.rs | 26 +- crates/anvil/tests/it/transaction.rs | 85 +- crates/anvil/tests/it/txpool.rs | 2 +- crates/anvil/tests/it/wsapi.rs | 9 +- crates/cast/bin/cmd/call.rs | 8 +- crates/cast/bin/cmd/run.rs | 12 +- crates/cast/bin/cmd/storage.rs | 6 +- crates/cast/src/lib.rs | 107 +- crates/cheatcodes/Cargo.toml | 3 +- crates/cheatcodes/src/evm/fork.rs | 52 +- crates/cheatcodes/src/inspector.rs | 2 +- crates/chisel/src/dispatcher.rs | 7 +- crates/chisel/src/runner.rs | 5 +- crates/cli/src/opts/wallet/multi_wallet.rs | 18 +- crates/cli/src/utils/cmd.rs | 28 +- crates/cli/src/utils/mod.rs | 14 +- crates/common/Cargo.toml | 11 + crates/common/src/compile.rs | 6 +- crates/common/src/lib.rs | 3 +- crates/common/src/provider/alloy.rs | 318 +++++ .../src/{provider.rs => provider/ethers.rs} | 4 +- crates/common/src/provider/mod.rs | 7 + crates/common/src/provider/retry.rs | 104 ++ .../common/src/provider/runtime_transport.rs | 310 +++++ crates/common/src/provider/tower.rs | 192 +++ crates/common/src/runtime_client.rs | 4 +- crates/common/src/serde_helpers.rs | 129 ++ crates/common/src/types.rs | 90 +- crates/config/src/endpoints.rs | 29 +- crates/config/src/macros.rs | 6 +- crates/evm/core/Cargo.toml | 5 +- crates/evm/core/src/abi/console.rs | 8 +- crates/evm/core/src/backend/error.rs | 2 +- crates/evm/core/src/backend/mod.rs | 107 +- crates/evm/core/src/fork/backend.rs | 151 ++- crates/evm/core/src/fork/database.rs | 4 +- crates/evm/core/src/fork/init.rs | 56 +- crates/evm/core/src/fork/multi.rs | 28 +- crates/evm/core/src/opts.rs | 16 +- crates/evm/core/src/utils.rs | 48 +- crates/evm/evm/src/executors/mod.rs | 4 +- crates/evm/evm/src/inspectors/mod.rs | 2 +- crates/evm/evm/src/inspectors/stack.rs | 18 +- crates/evm/fuzz/src/lib.rs | 1 + crates/evm/traces/Cargo.toml | 4 +- crates/evm/traces/src/decoder/mod.rs | 189 ++- crates/evm/traces/src/decoder/precompiles.rs | 20 +- crates/evm/traces/src/lib.rs | 660 +++------- crates/forge/bin/cmd/create.rs | 4 +- crates/forge/bin/cmd/inspect.rs | 3 + crates/forge/bin/cmd/script/broadcast.rs | 38 +- crates/forge/bin/cmd/script/cmd.rs | 12 +- crates/forge/bin/cmd/script/executor.rs | 28 +- crates/forge/bin/cmd/script/mod.rs | 45 +- crates/forge/bin/cmd/script/multi.rs | 4 +- crates/forge/bin/cmd/script/providers.rs | 6 +- crates/forge/bin/cmd/script/receipts.rs | 6 +- crates/forge/bin/cmd/script/runner.rs | 10 +- crates/forge/bin/cmd/script/transaction.rs | 8 +- crates/forge/bin/cmd/test/mod.rs | 90 +- crates/forge/src/gas_report.rs | 61 +- crates/forge/src/result.rs | 1 + crates/forge/tests/cli/multi_script.rs | 22 +- crates/forge/tests/cli/script.rs | 12 +- crates/forge/tests/it/config.rs | 21 +- crates/forge/tests/it/repros.rs | 21 +- crates/test-utils/src/script.rs | 3 +- deny.toml | 1 + 118 files changed, 5461 insertions(+), 3025 deletions(-) create mode 100644 crates/common/src/provider/alloy.rs rename crates/common/src/{provider.rs => provider/ethers.rs} (99%) create mode 100644 crates/common/src/provider/mod.rs create mode 100644 crates/common/src/provider/retry.rs create mode 100644 crates/common/src/provider/runtime_transport.rs create mode 100644 crates/common/src/provider/tower.rs create mode 100644 crates/common/src/serde_helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 2d453fcd442f..053e5c49a80c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,7 +82,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa1873637aa7f20369eae38b312cf7550c266d13ebc60f176fd5c82c5127810b" dependencies = [ - "num_enum", + "num_enum 0.7.1", "serde", "strum", ] @@ -120,6 +120,27 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-json-rpc" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-networks" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-rlp", +] + [[package]] name = "alloy-primitives" version = "0.5.4" @@ -146,6 +167,41 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-providers" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-networks", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-trace-types", + "alloy-rpc-types", + "alloy-transport", + "alloy-transport-http", + "async-trait", + "auto_impl", + "reqwest", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-pubsub" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde_json", + "tokio", + "tower", + "tracing", +] + [[package]] name = "alloy-rlp" version = "0.3.4" @@ -168,6 +224,52 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "alloy-rpc-client" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "alloy-transport-http", + "futures", + "pin-project", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-trace-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types", + "itertools 0.12.0", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "itertools 0.12.0", + "jsonrpsee-types", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "alloy-sol-macro" version = "0.5.4" @@ -178,7 +280,7 @@ dependencies = [ "const-hex", "dunce", "heck", - "indexmap", + "indexmap 2.1.0", "proc-macro-error", "proc-macro2", "quote", @@ -210,6 +312,69 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-transport" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "base64 0.21.5", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "url", + "wasm-bindgen-futures", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy/#ec44bb040721e4dd85a3f1de5aeb92d4b29563ce" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + [[package]] name = "ammonia" version = "3.3.0" @@ -296,7 +461,12 @@ dependencies = [ name = "anvil" version = "0.2.0" dependencies = [ + "alloy-chains", "alloy-primitives", + "alloy-providers", + "alloy-rpc-trace-types", + "alloy-rpc-types", + "alloy-transport", "anvil-core", "anvil-rpc", "anvil-server", @@ -314,6 +484,7 @@ dependencies = [ "ethers", "ethers-core", "ethers-solc", + "eyre", "fdlimit", "flate2", "foundry-common", @@ -347,6 +518,8 @@ name = "anvil-core" version = "0.2.0" dependencies = [ "alloy-primitives", + "alloy-rpc-trace-types", + "alloy-rpc-types", "anvil-core", "bytes", "ethers-contract", @@ -558,13 +731,37 @@ dependencies = [ "term", ] +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-lock" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +dependencies = [ + "event-listener 4.0.0", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-priority-channel" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c21678992e1b21bebfe2bc53ab5f5f68c106eddab31b24e0bb06e9b715a86640" dependencies = [ - "event-listener", + "event-listener 2.5.3", ] [[package]] @@ -578,6 +775,12 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + [[package]] name = "async-trait" version = "0.1.77" @@ -615,6 +818,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aurora-engine-modexp" version = "1.0.0" @@ -740,6 +949,21 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bindgen" version = "0.66.1" @@ -825,6 +1049,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "fastrand", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + [[package]] name = "blst" version = "0.3.11" @@ -837,6 +1077,144 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boa_ast" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73498e9b2f0aa7db74977afa4d594657611e90587abf0dd564c0b55b4a130163" +dependencies = [ + "bitflags 2.4.1", + "boa_interner", + "boa_macros", + "indexmap 2.1.0", + "num-bigint", + "rustc-hash", +] + +[[package]] +name = "boa_engine" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16377479d5d6d33896e7acdd1cc698d04a8f72004025bbbddf47558cd29146a6" +dependencies = [ + "bitflags 2.4.1", + "boa_ast", + "boa_gc", + "boa_icu_provider", + "boa_interner", + "boa_macros", + "boa_parser", + "boa_profiler", + "chrono", + "dashmap", + "fast-float", + "icu_normalizer", + "indexmap 2.1.0", + "itertools 0.11.0", + "num-bigint", + "num-integer", + "num-traits", + "num_enum 0.6.1", + "once_cell", + "pollster", + "rand 0.8.5", + "regress", + "rustc-hash", + "ryu-js", + "serde", + "serde_json", + "sptr", + "static_assertions", + "tap", + "thin-vec", + "thiserror", +] + +[[package]] +name = "boa_gc" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c97b44beaef9d4452342d117d94607fdfa8d474280f1ba0fd97853834e3a49b2" +dependencies = [ + "boa_macros", + "boa_profiler", + "thin-vec", +] + +[[package]] +name = "boa_icu_provider" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30e52e34e451dd0bfc2c654a9a43ed34b0073dbd4ae3394b40313edda8627aa" +dependencies = [ + "icu_collections", + "icu_normalizer", + "icu_properties", + "icu_provider", + "icu_provider_adapters", + "icu_provider_blob", + "once_cell", +] + +[[package]] +name = "boa_interner" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e5afa991908cfbe79bd3109b824e473a1dc5f74f31fab91bb44c9e245daa77" +dependencies = [ + "boa_gc", + "boa_macros", + "hashbrown 0.14.3", + "indexmap 2.1.0", + "once_cell", + "phf 0.11.2", + "rustc-hash", + "static_assertions", +] + +[[package]] +name = "boa_macros" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005fa0c5bd20805466dda55eb34cd709bb31a2592bb26927b47714eeed6914d8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "synstructure 0.13.0", +] + +[[package]] +name = "boa_parser" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e09afb035377a9044443b598187a7d34cd13164617182a4d7c348522ee3f052" +dependencies = [ + "bitflags 2.4.1", + "boa_ast", + "boa_icu_provider", + "boa_interner", + "boa_macros", + "boa_profiler", + "fast-float", + "icu_locid", + "icu_properties", + "icu_provider", + "icu_provider_macros", + "num-bigint", + "num-traits", + "once_cell", + "regress", + "rustc-hash", + "tinystr", +] + +[[package]] +name = "boa_profiler" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190f92dfe48224adc92881c620f08ccf37ff62b91a094bb357fe53bd5e84647" + [[package]] name = "bs58" version = "0.5.0" @@ -1246,6 +1624,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "coins-bip32" version = "0.8.7" @@ -1378,6 +1762,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.7" @@ -1518,6 +1911,12 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.10" @@ -1641,6 +2040,41 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.48", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.48", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1677,6 +2111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1814,6 +2249,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -1883,6 +2329,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "ena" version = "0.14.2" @@ -2171,7 +2623,7 @@ dependencies = [ "ethabi", "generic-array", "k256", - "num_enum", + "num_enum 0.7.1", "once_cell", "open-fastrlp", "rand 0.8.5", @@ -2193,6 +2645,7 @@ source = "git+https://github.com/gakonst/ethers-rs?rev=f0e5b194f09c533feb10d1a68 dependencies = [ "chrono", "ethers-core", + "ethers-solc", "reqwest", "semver 1.0.21", "serde", @@ -2335,6 +2788,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.0", + "pin-project-lite", +] + [[package]] name = "evm-disassembler" version = "0.3.0" @@ -2364,6 +2838,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fast-float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" + [[package]] name = "fastrand" version = "2.0.1" @@ -2658,10 +3138,11 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-providers", + "alloy-rpc-types", "alloy-sol-types", "const-hex", "ethers-core", - "ethers-providers", "ethers-signers", "eyre", "foundry-cheatcodes-spec", @@ -2735,8 +3216,17 @@ version = "0.2.0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", + "alloy-json-rpc", "alloy-primitives", + "alloy-providers", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", "alloy-sol-types", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", "async-trait", "clap", "comfy-table", @@ -2763,6 +3253,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "tower", "tracing", "url", "walkdir", @@ -2894,11 +3385,14 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-providers", + "alloy-rpc-types", "alloy-sol-types", + "alloy-transport", "const-hex", "derive_more", + "ethers", "ethers-core", - "ethers-providers", "eyre", "foundry-cheatcodes-spec", "foundry-common", @@ -2968,7 +3462,6 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "const-hex", - "ethers-core", "eyre", "foundry-block-explorers", "foundry-common", @@ -2980,7 +3473,7 @@ dependencies = [ "itertools 0.11.0", "once_cell", "ordered-float", - "revm", + "reth-revm-inspectors", "serde", "tempfile", "tokio", @@ -3109,6 +3602,16 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-locks" version = "0.7.1" @@ -3504,7 +4007,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -3555,6 +4058,15 @@ dependencies = [ "ahash 0.7.7", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.7", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -3791,6 +4303,128 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8302d8dfd6044d3ddb3f807a5ef3d7bbca9a574959c6d6e4dc39aa7012d0d5" +dependencies = [ + "displaydoc", + "serde", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3003f85dccfc0e238ff567693248c59153a46f4e6125ba4020b973cef4d1d335" +dependencies = [ + "displaydoc", + "litemap", + "serde", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652869735c9fb9f5a64ba180ee16f2c848390469c116deef517ecc53f4343598" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_properties", + "icu_provider", + "serde", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_properties" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0e1aa26851f16c9e04412a5911c86b7f8768dac8f8d4c5f1c568a7e5d7a434" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_provider", + "serde", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_provider" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc312a7b6148f7dfe098047ae2494d12d4034f48ade58d4f353000db376e305" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "postcard", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_adapters" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ae1e2bd0c41728b77e7c46e9afdec5e2127d1eedacc684724667d50c126bd3" +dependencies = [ + "icu_locid", + "icu_provider", + "serde", + "tinystr", + "yoke", + "zerovec", +] + +[[package]] +name = "icu_provider_blob" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd364c9a01f791a4bc04a74cf2a1d01d9f6926a40fd5ae1c28004e1e70d8338b" +dependencies = [ + "icu_provider", + "postcard", + "serde", + "writeable", + "yoke", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -3879,6 +4513,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -3887,6 +4532,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -3940,18 +4586,45 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array", + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", ] [[package]] -name = "instant" -version = "0.1.12" +name = "interprocess" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" dependencies = [ + "blocking", "cfg-if", + "futures-core", + "futures-io", + "intmap", + "libc", + "once_cell", + "rustc_version 0.4.0", + "spinning", + "thiserror", + "to_method", + "tokio", + "winapi", ] +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + [[package]] name = "ipnet" version = "2.9.0" @@ -3987,6 +4660,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -4022,6 +4704,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonrpsee-types" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "jsonwebtoken" version = "8.3.0" @@ -4218,6 +4914,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litemap" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" + [[package]] name = "lock_api" version = "0.4.11" @@ -4577,6 +5279,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "serde", ] [[package]] @@ -4641,13 +5344,34 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + [[package]] name = "num_enum" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.7.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.48", ] [[package]] @@ -4691,6 +5415,10 @@ name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "oorandom" @@ -4873,9 +5601,15 @@ checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ "proc-macro2", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -5046,7 +5780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.1.0", ] [[package]] @@ -5181,6 +5915,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -5225,12 +5970,29 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + [[package]] name = "portable-atomic" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -5654,6 +6416,16 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "regress" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a9ecfa0cb04d0b04dddb99b8ccf4f66bc8dfd23df694b398570bd8ae3a50fb" +dependencies = [ + "hashbrown 0.13.2", + "memchr", +] + [[package]] name = "reqwest" version = "0.11.23" @@ -5698,6 +6470,43 @@ dependencies = [ "winreg", ] +[[package]] +name = "reth-revm-inspectors" +version = "0.1.0-alpha.13" +source = "git+https://github.com/paradigmxyz/reth/?branch=main#8b56f50f3e326c1ef861ac3899dffd7f8a533c94" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "boa_engine", + "boa_gc", + "reth-rpc-types", + "revm", + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "reth-rpc-types" +version = "0.1.0-alpha.13" +source = "git+https://github.com/paradigmxyz/reth/?branch=main#8b56f50f3e326c1ef861ac3899dffd7f8a533c94" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-trace-types", + "alloy-rpc-types", + "bytes", + "itertools 0.12.0", + "jsonrpsee-types", + "secp256k1 0.27.0", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", +] + [[package]] name = "revm" version = "3.5.0" @@ -5730,7 +6539,7 @@ dependencies = [ "once_cell", "revm-primitives", "ripemd", - "secp256k1", + "secp256k1 0.28.1", "sha2 0.10.8", "substrate-bn", ] @@ -6118,6 +6927,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + [[package]] name = "salsa20" version = "0.10.2" @@ -6235,13 +7050,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "rand 0.8.5", + "secp256k1-sys 0.8.1", +] + [[package]] name = "secp256k1" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.9.2", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", ] [[package]] @@ -6352,7 +7186,7 @@ version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ - "indexmap", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -6410,6 +7244,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -6632,6 +7495,15 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -6642,6 +7514,18 @@ dependencies = [ "der", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -6812,6 +7696,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "unicode-xid", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -6906,6 +7802,12 @@ dependencies = [ "phf_codegen 0.11.2", ] +[[package]] +name = "thin-vec" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" + [[package]] name = "thiserror" version = "1.0.56" @@ -6985,6 +7887,17 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8faa444297615a4e020acb64146b0603c9c395c03a97c17fd9028816d3b4d63e" +dependencies = [ + "displaydoc", + "serde", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -7010,6 +7923,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + [[package]] name = "tokio" version = "1.35.1" @@ -7107,6 +8026,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -7128,7 +8048,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ - "indexmap", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -7150,7 +8070,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.1.0", "toml_datetime", "winnow", ] @@ -7161,7 +8081,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap", + "indexmap 2.1.0", "toml_datetime", "winnow", ] @@ -7172,7 +8092,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -7517,6 +8437,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.1" @@ -8021,6 +8953,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -8067,6 +9011,30 @@ version = "1.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +[[package]] +name = "yoke" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "synstructure 0.13.0", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -8087,6 +9055,27 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "zerofrom" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "synstructure 0.13.0", +] + [[package]] name = "zeroize" version = "1.7.0" @@ -8107,6 +9096,29 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "zerovec" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591691014119b87047ead4dcf3e6adfbf73cb7c38ab6980d4f18a32138f35d46" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4a1638a1934450809c2266a70362bfc96cd90550c073f5b8a55014d1010157" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 3cbe17fa2e13..e166bc65adf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,6 +123,7 @@ foundry-evm-traces = { path = "crates/evm/traces" } foundry-macros = { path = "crates/macros" } foundry-test-utils = { path = "crates/test-utils" } +# solc & compilation utilities foundry-block-explorers = { version = "0.1.3", default-features = false } foundry-compilers = { version = "0.1.4", default-features = false } @@ -142,10 +143,20 @@ ethers-middleware = { version = "2.0", default-features = false } ethers-solc = { version = "2.0", default-features = false } ## alloy -alloy-primitives = "0.5.0" -alloy-dyn-abi = "0.5.0" -alloy-json-abi = "0.5.0" -alloy-sol-types = "0.5.0" +alloy-providers = "0.1.0" +alloy-transport = "0.1.0" +alloy-transport-http = "0.1.0" +alloy-transport-ws = "0.1.0" +alloy-transport-ipc = "0.1.0" +alloy-rpc-types = "0.1.0" +alloy-rpc-trace-types = "0.1.0" +alloy-json-rpc = "0.1.0" +alloy-pubsub = "0.1.0" +alloy-rpc-client = "0.1.0" +alloy-primitives = "0.5.1" +alloy-dyn-abi = "0.5.1" +alloy-json-abi = "0.5.1" +alloy-sol-types = "0.5.1" syn-solidity = "0.5.0" alloy-chains = "0.1.5" @@ -197,7 +208,18 @@ ethers-signers = { git = "https://github.com/gakonst/ethers-rs", rev = "f0e5b194 ethers-middleware = { git = "https://github.com/gakonst/ethers-rs", rev = "f0e5b194f09c533feb10d1a686ddb9e5946ec107" } ethers-solc = { git = "https://github.com/gakonst/ethers-rs", rev = "f0e5b194f09c533feb10d1a686ddb9e5946ec107" } +alloy-providers = { git = "https://github.com/alloy-rs/alloy/" } +alloy-transport = { git = "https://github.com/alloy-rs/alloy/" } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy/" } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy/" } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy/" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy/" } +alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy/" } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy/" } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy/" } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy/" } + revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } +revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-precompile = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } -revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index dbced4c69d71..8db3dc725053 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -36,6 +36,11 @@ trie-db = "0.23" hash-db = "0.15" memory-db = "0.29" alloy-primitives = { workspace = true, features = ["serde"] } +alloy-rpc-types.workspace = true +alloy-rpc-trace-types.workspace = true +alloy-providers.workspace = true +alloy-transport.workspace = true +alloy-chains.workspace = true # axum related axum.workspace = true @@ -62,6 +67,7 @@ yansi = "0.5" tempfile = "3" itertools.workspace = true rand = "0.8" +eyre.workspace = true # cli clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 91d86986da82..6828bb9f7936 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -12,9 +12,11 @@ repository.workspace = true [dependencies] foundry-common.workspace = true foundry-evm.workspace = true +revm = { workspace = true, default-features = false, features = ["std", "serde", "memory_limit"] } alloy-primitives = { workspace = true, features = ["serde"] } -revm = { workspace = true, default-features = false, features = ["std", "serde", "memory_limit"] } +alloy-rpc-types = { workspace = true } +alloy-rpc-trace-types.workspace = true ethers-core = { workspace = true, features = ["optimism"] } # theses are not used by anvil-core, but are required by ethers, because pulled in via foundry-common ethers-contract = { workspace = true, features = ["optimism"] } @@ -23,7 +25,7 @@ ethers-middleware = { workspace = true, features = ["optimism"] } serde = { workspace = true, optional = true } serde_json.workspace = true -bytes = { version = "1.4" } +bytes = "1.4" open-fastrlp = { version = "0.1.4", optional = true } # trie @@ -40,4 +42,4 @@ anvil-core = { path = ".", features = ["serde"] } default = ["serde"] impersonated-tx = [] fastrlp = ["dep:open-fastrlp"] -serde = ["dep:serde"] \ No newline at end of file +serde = ["dep:serde"] diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index e8b793a786c4..dbe28104a7a3 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -1,18 +1,15 @@ -use self::state::StateOverride; use crate::{ - eth::{ - subscription::{SubscriptionId, SubscriptionKind, SubscriptionParams}, - transaction::EthTransactionRequest, - }, + eth::subscription::SubscriptionId, types::{EvmMineOptions, Forking, Index}, }; -use ethers_core::{ - abi::ethereum_types::H64, - types::{ - transaction::eip712::TypedData, Address, BlockId, BlockNumber, Bytes, Filter, - GethDebugTracingOptions, TxHash, H256, U256, - }, +use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256}; +use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; +use alloy_rpc_types::{ + pubsub::{Params as SubscriptionParams, SubscriptionKind}, + state::StateOverride, + BlockId, BlockNumberOrTag as BlockNumber, CallRequest, Filter, }; +use ethers_core::types::transaction::eip712::TypedData; pub mod block; pub mod proof; @@ -27,10 +24,13 @@ pub mod utils; pub mod serde_helpers; #[cfg(feature = "serde")] -use ethers_core::types::serde_helpers::*; +use self::serde_helpers::*; +use self::transaction::EthTransactionRequest; #[cfg(feature = "serde")] -use self::serde_helpers::*; +use foundry_common::serde_helpers::{ + deserialize_number, deserialize_number_opt, deserialize_number_seq, +}; /// Wrapper type that ensures the type is named `params` #[derive(Clone, Debug, PartialEq, Eq)] @@ -88,11 +88,14 @@ pub enum EthRequest { EthGetStorageAt(Address, U256, Option), #[cfg_attr(feature = "serde", serde(rename = "eth_getBlockByHash"))] - EthGetBlockByHash(H256, bool), + EthGetBlockByHash(B256, bool), #[cfg_attr(feature = "serde", serde(rename = "eth_getBlockByNumber"))] EthGetBlockByNumber( - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number"))] + #[cfg_attr( + feature = "serde", + serde(deserialize_with = "lenient_block_number::lenient_block_number") + )] BlockNumber, bool, ), @@ -104,13 +107,13 @@ pub enum EthRequest { feature = "serde", serde(rename = "eth_getBlockTransactionCountByHash", with = "sequence") )] - EthGetTransactionCountByHash(H256), + EthGetTransactionCountByHash(B256), #[cfg_attr( feature = "serde", serde( rename = "eth_getBlockTransactionCountByNumber", - deserialize_with = "lenient_block_number_seq" + deserialize_with = "lenient_block_number::lenient_block_number_seq" ) )] EthGetTransactionCountByNumber(BlockNumber), @@ -119,13 +122,13 @@ pub enum EthRequest { feature = "serde", serde(rename = "eth_getUncleCountByBlockHash", with = "sequence") )] - EthGetUnclesCountByHash(H256), + EthGetUnclesCountByHash(B256), #[cfg_attr( feature = "serde", serde( rename = "eth_getUncleCountByBlockNumber", - deserialize_with = "lenient_block_number_seq" + deserialize_with = "lenient_block_number::lenient_block_number_seq" ) )] EthGetUnclesCountByNumber(BlockNumber), @@ -136,7 +139,7 @@ pub enum EthRequest { /// Returns the account and storage values of the specified account including the Merkle-proof. /// This call can be used to verify that the data you are pulling from is not tampered with. #[cfg_attr(feature = "serde", serde(rename = "eth_getProof"))] - EthGetProof(Address, Vec, Option), + EthGetProof(Address, Vec, Option), /// The sign method calculates an Ethereum specific signature with: #[cfg_attr(feature = "serde", serde(rename = "eth_sign"))] @@ -165,22 +168,19 @@ pub enum EthRequest { #[cfg_attr(feature = "serde", serde(rename = "eth_call"))] EthCall( - EthTransactionRequest, + CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_createAccessList"))] EthCreateAccessList( - EthTransactionRequest, + CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_estimateGas"))] - EthEstimateGas( - EthTransactionRequest, - #[cfg_attr(feature = "serde", serde(default))] Option, - ), + EthEstimateGas(CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option), #[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionByHash", with = "sequence"))] EthGetTransactionByHash(TxHash), @@ -189,21 +189,20 @@ pub enum EthRequest { EthGetTransactionByBlockHashAndIndex(TxHash, Index), #[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionByBlockNumberAndIndex"))] - EthGetTransactionByBlockNumberAndIndex( - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number"))] - BlockNumber, - Index, - ), + EthGetTransactionByBlockNumberAndIndex(BlockNumber, Index), #[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionReceipt", with = "sequence"))] - EthGetTransactionReceipt(H256), + EthGetTransactionReceipt(B256), #[cfg_attr(feature = "serde", serde(rename = "eth_getUncleByBlockHashAndIndex"))] - EthGetUncleByBlockHashAndIndex(H256, Index), + EthGetUncleByBlockHashAndIndex(B256, Index), #[cfg_attr(feature = "serde", serde(rename = "eth_getUncleByBlockNumberAndIndex"))] EthGetUncleByBlockNumberAndIndex( - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number"))] + #[cfg_attr( + feature = "serde", + serde(deserialize_with = "lenient_block_number::lenient_block_number") + )] BlockNumber, Index, ), @@ -244,10 +243,10 @@ pub enum EthRequest { EthGetWork(()), #[cfg_attr(feature = "serde", serde(rename = "eth_submitWork"))] - EthSubmitWork(H64, H256, H256), + EthSubmitWork(B64, B256, B256), #[cfg_attr(feature = "serde", serde(rename = "eth_submitHashrate"))] - EthSubmitHashRate(U256, H256), + EthSubmitHashRate(U256, B256), #[cfg_attr(feature = "serde", serde(rename = "eth_feeHistory"))] EthFeeHistory( @@ -262,26 +261,29 @@ pub enum EthRequest { /// geth's `debug_traceTransaction` endpoint #[cfg_attr(feature = "serde", serde(rename = "debug_traceTransaction"))] DebugTraceTransaction( - H256, + B256, #[cfg_attr(feature = "serde", serde(default))] GethDebugTracingOptions, ), /// geth's `debug_traceCall` endpoint #[cfg_attr(feature = "serde", serde(rename = "debug_traceCall"))] DebugTraceCall( - EthTransactionRequest, + CallRequest, #[cfg_attr(feature = "serde", serde(default))] Option, - #[cfg_attr(feature = "serde", serde(default))] GethDebugTracingOptions, + #[cfg_attr(feature = "serde", serde(default))] GethDefaultTracingOptions, ), /// Trace transaction endpoint for parity's `trace_transaction` #[cfg_attr(feature = "serde", serde(rename = "trace_transaction", with = "sequence"))] - TraceTransaction(H256), + TraceTransaction(B256), /// Trace transaction endpoint for parity's `trace_block` #[cfg_attr( feature = "serde", - serde(rename = "trace_block", deserialize_with = "lenient_block_number_seq") + serde( + rename = "trace_block", + deserialize_with = "lenient_block_number::lenient_block_number_seq" + ) )] TraceBlock(BlockNumber), @@ -363,7 +365,7 @@ pub enum EthRequest { with = "sequence" ) )] - DropTransaction(H256), + DropTransaction(B256), /// Reset the fork to a fresh forked state, and optionally update the fork config #[cfg_attr(feature = "serde", serde(rename = "anvil_reset", alias = "hardhat_reset"))] @@ -411,7 +413,7 @@ pub enum EthRequest { /// slot U256, /// value - H256, + B256, ), /// Sets the coinbase address @@ -622,7 +624,10 @@ pub enum EthRequest { /// Related upstream issue: https://github.com/otterscan/otterscan/issues/1081 #[cfg_attr(feature = "serde", serde(rename = "erigon_getHeaderByNumber"))] ErigonGetHeaderByNumber( - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number_seq"))] + #[cfg_attr( + feature = "serde", + serde(deserialize_with = "lenient_block_number::lenient_block_number_seq") + )] BlockNumber, ), @@ -635,26 +640,29 @@ pub enum EthRequest { /// Traces internal ETH transfers, contracts creation (CREATE/CREATE2) and self-destructs for a /// certain transaction. #[cfg_attr(feature = "serde", serde(rename = "ots_getInternalOperations", with = "sequence"))] - OtsGetInternalOperations(H256), + OtsGetInternalOperations(B256), /// Otterscan's `ots_hasCode` endpoint /// Check if an ETH address contains code at a certain block number. #[cfg_attr(feature = "serde", serde(rename = "ots_hasCode"))] OtsHasCode( Address, - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number", default))] + #[cfg_attr( + feature = "serde", + serde(deserialize_with = "lenient_block_number::lenient_block_number", default) + )] BlockNumber, ), /// Otterscan's `ots_traceTransaction` endpoint /// Trace a transaction and generate a trace call tree. #[cfg_attr(feature = "serde", serde(rename = "ots_traceTransaction", with = "sequence"))] - OtsTraceTransaction(H256), + OtsTraceTransaction(B256), /// Otterscan's `ots_getTransactionError` endpoint /// Given a transaction hash, returns its raw revert reason. #[cfg_attr(feature = "serde", serde(rename = "ots_getTransactionError", with = "sequence"))] - OtsGetTransactionError(H256), + OtsGetTransactionError(B256), /// Otterscan's `ots_getBlockDetails` endpoint /// Given a block number, return its data. Similar to the standard eth_getBlockByNumber/Hash @@ -662,14 +670,17 @@ pub enum EthRequest { /// logBloom #[cfg_attr(feature = "serde", serde(rename = "ots_getBlockDetails"))] OtsGetBlockDetails( - #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_block_number_seq"))] + #[cfg_attr( + feature = "serde", + serde(deserialize_with = "lenient_block_number::lenient_block_number", default) + )] BlockNumber, ), /// Otterscan's `ots_getBlockDetails` endpoint /// Same as `ots_getBlockDetails`, but receiving a block hash instead of number #[cfg_attr(feature = "serde", serde(rename = "ots_getBlockDetailsByHash", with = "sequence"))] - OtsGetBlockDetailsByHash(H256), + OtsGetBlockDetailsByHash(B256), /// Otterscan's `ots_getBlockTransactions` endpoint /// Gets paginated transaction data for a certain block. Return data is similar to @@ -806,14 +817,16 @@ mod tests { #[test] fn test_custom_impersonate_account() { - let s = r#"{"method": "anvil_impersonateAccount", "params": ["0xd84de507f3fada7df80908082d3239466db55a71"]}"#; + let s = r#"{"method": "anvil_impersonateAccount", "params": +["0xd84de507f3fada7df80908082d3239466db55a71"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } #[test] fn test_custom_stop_impersonate_account() { - let s = r#"{"method": "anvil_stopImpersonatingAccount", "params": ["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"]}"#; + let s = r#"{"method": "anvil_stopImpersonatingAccount", "params": +["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -844,8 +857,8 @@ mod tests { } _ => unreachable!(), } - let s = - r#"{"method": "anvil_mine", "params": ["0xd84de507f3fada7df80908082d3239466db55a71"]}"#; + let s = r#"{"method": "anvil_mine", "params": +["0xd84de507f3fada7df80908082d3239466db55a71"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let req = serde_json::from_value::(value).unwrap(); match req { @@ -889,7 +902,8 @@ mod tests { #[test] fn test_custom_drop_tx() { - let s = r#"{"method": "anvil_dropTransaction", "params": ["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff"]}"#; + let s = r#"{"method": "anvil_dropTransaction", "params": +["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -1034,57 +1048,71 @@ mod tests { #[test] fn test_custom_set_balance() { - let s = r#"{"method": "anvil_setBalance", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; + let s = r#"{"method": "anvil_setBalance", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "anvil_setBalance", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", 1337]}"#; + let s = r#"{"method": "anvil_setBalance", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", 1337]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } #[test] fn test_custom_set_code() { - let s = r#"{"method": "anvil_setCode", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0123456789abcdef"]}"#; + let s = r#"{"method": "anvil_setCode", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x0123456789abcdef"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "anvil_setCode", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x"]}"#; + let s = r#"{"method": "anvil_setCode", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "anvil_setCode", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", ""]}"#; + let s = r#"{"method": "anvil_setCode", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", ""]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } #[test] fn test_custom_set_nonce() { - let s = r#"{"method": "anvil_setNonce", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; + let s = r#"{"method": "anvil_setNonce", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "hardhat_setNonce", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; + let s = r#"{"method": +"hardhat_setNonce", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "evm_setAccountNonce", "params": ["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; + let s = r#"{"method": "evm_setAccountNonce", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x0"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } #[test] fn test_serde_custom_set_storage_at() { - let s = r#"{"method": "anvil_setStorageAt", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", "0x0000000000000000000000000000000000000000000000000000000000003039"]}"#; + let s = r#"{"method": "anvil_setStorageAt", "params": +["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", +"0x0000000000000000000000000000000000000000000000000000000000003039"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "hardhat_setStorageAt", "params": ["0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", "0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49", "0x0000000000000000000000000000000000000000000000000000000000003039"]}"#; + let s = r#"{"method": "hardhat_setStorageAt", "params": +["0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", +"0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49", +"0x0000000000000000000000000000000000000000000000000000000000003039"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } #[test] fn test_serde_custom_coinbase() { - let s = r#"{"method": "anvil_setCoinbase", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251"]}"#; + let s = r#"{"method": "anvil_setCoinbase", "params": +["0x295a70b2de5e3953354a6a8344e616ed314d7251"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -1340,7 +1368,8 @@ mod tests { #[test] fn test_serde_eth_unsubscribe() { - let s = r#"{"id": 1, "method": "eth_unsubscribe", "params": ["0x9cef478923ff08bf67fde6c64013158d"]}"#; + let s = r#"{"id": 1, "method": "eth_unsubscribe", "params": +["0x9cef478923ff08bf67fde6c64013158d"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -1351,7 +1380,9 @@ mod tests { let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"id": 1, "method": "eth_subscribe", "params": ["logs", {"address": "0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", "topics": ["0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"]}]}"#; + let s = r#"{"id": 1, "method": "eth_subscribe", "params": ["logs", {"address": +"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", "topics": +["0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"]}]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); @@ -1366,15 +1397,19 @@ mod tests { #[test] fn test_serde_debug_trace_transaction() { - let s = r#"{"method": "debug_traceTransaction", "params": ["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff"]}"#; + let s = r#"{"method": "debug_traceTransaction", "params": +["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "debug_traceTransaction", "params": ["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff", {}]}"#; + let s = r#"{"method": "debug_traceTransaction", "params": +["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff", {}]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); - let s = r#"{"method": "debug_traceTransaction", "params": ["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff", {"disableStorage": true}]}"#; + let s = r#"{"method": "debug_traceTransaction", "params": +["0x4a3b0fce2cb9707b0baa68640cf2fe858c8bb4121b2a8cb904ff369d38a560ff", {"disableStorage": +true}]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -1404,7 +1439,8 @@ mod tests { #[test] fn test_serde_eth_storage() { - let s = r#"{"method": "eth_getStorageAt", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", "latest"]}"#; + let s = r#"{"method": "eth_getStorageAt", "params": +["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", "latest"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } @@ -1412,27 +1448,28 @@ mod tests { #[test] fn test_eth_call() { let req = r#"{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}"#; - let _req = serde_json::from_str::(req).unwrap(); + let _req = serde_json::from_str::(req).unwrap(); - let s = r#"{"method": "eth_call", "params": [{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"},"latest"]}"#; + let s = r#"{"method": "eth_call", "params":[{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"},"latest"]}"#; let _req = serde_json::from_str::(s).unwrap(); - let s = r#"{"method": "eth_call", "params": [{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}]}"#; + let s = r#"{"method": "eth_call", "params":[{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}]}"#; let _req = serde_json::from_str::(s).unwrap(); - let s = r#"{"method": "eth_call", "params": [{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockNumber": "latest" }]}"#; + let s = r#"{"method": "eth_call", "params":[{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockNumber": "latest" }]}"#; let _req = serde_json::from_str::(s).unwrap(); - let s = r#"{"method": "eth_call", "params": [{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockNumber": "0x0" }]}"#; + let s = r#"{"method": "eth_call", "params":[{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockNumber": "0x0" }]}"#; let _req = serde_json::from_str::(s).unwrap(); - let s = r#"{"method": "eth_call", "params": [{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }]}"#; + let s = r#"{"method": "eth_call", "params":[{"data":"0xcfae3217","from":"0xd84de507f3fada7df80908082d3239466db55a71","to":"0xcbe828fdc46e3b1c351ec90b1a5e7d9742c0398d"}, { "blockHash":"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }]}"#; let _req = serde_json::from_str::(s).unwrap(); } #[test] fn test_serde_eth_balance() { - let s = r#"{"method": "eth_getBalance", "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "latest"]}"#; + let s = r#"{"method": "eth_getBalance", "params": +["0x295a70b2de5e3953354a6a8344e616ed314d7251", "latest"]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); diff --git a/crates/anvil/core/src/eth/serde_helpers.rs b/crates/anvil/core/src/eth/serde_helpers.rs index 0deeb69d6c8c..f7d5bd46dba2 100644 --- a/crates/anvil/core/src/eth/serde_helpers.rs +++ b/crates/anvil/core/src/eth/serde_helpers.rs @@ -51,3 +51,60 @@ pub mod empty_params { Ok(()) } } + +/// A module that deserializes either a BlockNumberOrTag, or a simple number. +pub mod lenient_block_number { + use alloy_rpc_types::BlockNumberOrTag; + use serde::{Deserialize, Deserializer}; + /// Following the spec the block parameter is either: + /// + /// > HEX String - an integer block number + /// > String "earliest" for the earliest/genesis block + /// > String "latest" - for the latest mined block + /// > String "pending" - for the pending state/transactions + /// + /// and with EIP-1898: + /// > blockNumber: QUANTITY - a block number + /// > blockHash: DATA - a block hash + /// + /// + /// + /// EIP-1898 does not all calls that use `BlockNumber` like `eth_getBlockByNumber` and doesn't + /// list raw integers as supported. + /// + /// However, there are dev node implementations that support integers, such as ganache: + /// + /// N.B.: geth does not support ints in `eth_getBlockByNumber` + pub fn lenient_block_number<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + LenientBlockNumber::deserialize(deserializer).map(Into::into) + } + + /// Same as `lenient_block_number` but requires to be `[num; 1]` + pub fn lenient_block_number_seq<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let num = <[LenientBlockNumber; 1]>::deserialize(deserializer)?[0].into(); + Ok(num) + } + + /// Various block number representations, See [`lenient_block_number()`] + #[derive(Clone, Copy, Deserialize)] + #[serde(untagged)] + pub enum LenientBlockNumber { + BlockNumber(BlockNumberOrTag), + Num(u64), + } + + impl From for BlockNumberOrTag { + fn from(b: LenientBlockNumber) -> Self { + match b { + LenientBlockNumber::BlockNumber(b) => b, + LenientBlockNumber::Num(b) => b.into(), + } + } + } +} diff --git a/crates/anvil/core/src/eth/transaction/ethers_compat.rs b/crates/anvil/core/src/eth/transaction/ethers_compat.rs index 54e2c16c535a..8844c1bf43fd 100644 --- a/crates/anvil/core/src/eth/transaction/ethers_compat.rs +++ b/crates/anvil/core/src/eth/transaction/ethers_compat.rs @@ -1,20 +1,172 @@ //! ethers compatibility, this is mainly necessary so we can use all of `ethers` signers use super::EthTransactionRequest; -use crate::eth::transaction::{ - DepositTransactionRequest, EIP1559TransactionRequest, EIP2930TransactionRequest, - LegacyTransactionRequest, MaybeImpersonatedTransaction, TypedTransaction, - TypedTransactionRequest, +use crate::eth::{ + proof::AccountProof, + state::{AccountOverride, StateOverride as EthStateOverride}, + transaction::{ + DepositTransactionRequest, EIP1559TransactionRequest, EIP2930TransactionRequest, + LegacyTransactionRequest, MaybeImpersonatedTransaction, TypedTransaction, + TypedTransactionRequest, + }, +}; +use alloy_primitives::{U128 as rU128, U256 as rU256, U64 as rU64}; +use alloy_rpc_types::{ + state::{AccountOverride as AlloyAccountOverride, StateOverride}, + transaction::request::TransactionRequest as AlloyTransactionRequest, + AccessList as AlloyAccessList, CallRequest, Signature, Transaction as AlloyTransaction, }; use ethers_core::types::{ transaction::{ - eip2718::TypedTransaction as EthersTypedTransactionRequest, optimism::DepositTransaction, + eip2718::TypedTransaction as EthersTypedTransactionRequest, + eip2930::{AccessList, AccessListItem}, + optimism::DepositTransaction, }, - Address, Eip1559TransactionRequest as EthersEip1559TransactionRequest, - Eip2930TransactionRequest as EthersEip2930TransactionRequest, NameOrAddress, + 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_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 to_internal_tx_request(request: &AlloyTransactionRequest) -> 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.data.clone().map(|b| b.clone().0.into()), + nonce: request.nonce.map(|n| n.to::().into()), + chain_id: None, + 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 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()) +} + +pub fn to_ethers_state_override(ov: StateOverride) -> EthStateOverride { + ov.into_iter() + .map(|(addr, o)| { + ( + addr.to_ethers(), + AccountOverride { + nonce: o.nonce.map(|n| n.to::()), + balance: o.balance.map(|b| b.to_ethers()), + code: o.code.map(|c| c.0.into()), + state_diff: o.state_diff.map(|s| { + s.into_iter() + .map(|(k, v)| (k.to_ethers(), H256::from_uint(&v.to_ethers()))) + .collect() + }), + state: o.state.map(|s| { + s.into_iter() + .map(|(k, v)| (k.to_ethers(), H256::from_uint(&v.to_ethers()))) + .collect() + }), + }, + ) + }) + .collect() +} + +pub fn to_alloy_state_override(ov: EthStateOverride) -> StateOverride { + ov.into_iter() + .map(|(addr, o)| { + ( + addr.to_alloy(), + AlloyAccountOverride { + nonce: o.nonce.map(rU64::from), + balance: o.balance.map(|b| b.to_alloy()), + code: o.code.map(|c| c.0.into()), + state_diff: o.state_diff.map(|s| { + s.into_iter() + .map(|(k, v)| (k.to_alloy(), rU256::from_be_bytes(v.to_alloy().0))) + .collect() + }), + state: o.state.map(|s| { + s.into_iter() + .map(|(k, v)| (k.to_alloy(), rU256::from_be_bytes(v.to_alloy().0))) + .collect() + }), + }, + ) + }) + .collect() +} impl From for EthersTypedTransactionRequest { fn from(tx: TypedTransactionRequest) -> Self { @@ -230,6 +382,118 @@ fn to_ethers_transaction_with_hash_and_sender( } } +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(); @@ -246,6 +510,15 @@ impl From for EthersTransaction { } } +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; diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index b324c63a5776..7c259b8cdbe0 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -15,7 +15,7 @@ use ethers_core::{ }, }; use foundry_common::types::ToAlloy; -use foundry_evm::traces::CallTraceArena; +use foundry_evm::traces::CallTraceNode; use revm::{ interpreter::InstructionResult, primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, @@ -25,6 +25,11 @@ use std::ops::Deref; /// compatibility with `ethers-rs` types mod ethers_compat; +pub use ethers_compat::{ + call_to_internal_tx_request, from_ethers_access_list, to_alloy_proof, to_alloy_state_override, + to_ethers_access_list, to_ethers_state_override, to_internal_tx_request, +}; + /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC #[cfg(feature = "impersonated-tx")] pub const IMPERSONATED_SIGNATURE: Signature = @@ -1508,47 +1513,12 @@ pub struct TransactionInfo { pub contract_address: Option
, pub logs: Vec, pub logs_bloom: Bloom, - pub traces: CallTraceArena, + pub traces: Vec, pub exit: InstructionResult, pub out: Option, pub nonce: u64, } -// === impl TransactionInfo === - -impl TransactionInfo { - /// Returns the `traceAddress` of the node in the arena - /// - /// The `traceAddress` field of all returned traces, gives the exact location in the call trace - /// [index in root, index in first CALL, index in second CALL, …]. - /// - /// # Panics - /// - /// if the `idx` does not belong to a node - pub fn trace_address(&self, idx: usize) -> Vec { - if idx == 0 { - // root call has empty traceAddress - return vec![] - } - let mut graph = vec![]; - let mut node = &self.traces.arena[idx]; - while let Some(parent) = node.parent { - // the index of the child call in the arena - let child_idx = node.idx; - node = &self.traces.arena[parent]; - // find the index of the child call in the parent node - let call_idx = node - .children - .iter() - .position(|child| *child == child_idx) - .expect("child exists in parent"); - graph.push(call_idx); - } - graph.reverse(); - graph - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index 147db557ef99..d1386854d7cd 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -1,7 +1,6 @@ -use std::collections::BTreeMap; - -use ethers_core::types::{TxHash, H256, U256, U64}; +use alloy_primitives::{TxHash, B256, U256, U64}; use revm::primitives::SpecId; +use std::collections::BTreeMap; #[cfg(feature = "serde")] use serde::{de::Error, Deserializer, Serializer}; @@ -92,9 +91,9 @@ impl Default for EvmMineOptions { /// This may or may not include the block number #[derive(Debug, Default, PartialEq, Eq)] pub struct Work { - pub pow_hash: H256, - pub seed_hash: H256, - pub target: H256, + pub pow_hash: B256, + pub seed_hash: B256, + pub target: B256, pub number: Option, } @@ -180,7 +179,7 @@ impl<'a> serde::Deserialize<'a> for Index { pub struct NodeInfo { pub current_block_number: U64, pub current_block_timestamp: u64, - pub current_block_hash: H256, + pub current_block_hash: B256, pub hard_fork: SpecId, pub transaction_order: String, pub environment: NodeEnvironment, @@ -215,11 +214,11 @@ pub struct NodeForkConfig { pub struct AnvilMetadata { pub client_version: &'static str, pub chain_id: u64, - pub instance_id: H256, + pub instance_id: B256, pub latest_block_number: u64, - pub latest_block_hash: H256, + pub latest_block_hash: B256, pub forked_network: Option, - pub snapshots: BTreeMap, + pub snapshots: BTreeMap, } /// Information about the forked network. diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 576e1f169762..b684caffd71b 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -4,6 +4,7 @@ use crate::{ genesis::Genesis, AccountGenerator, Hardfork, NodeConfig, CHAIN_ID, }; +use alloy_primitives::U256; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; @@ -11,6 +12,7 @@ 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}; @@ -192,14 +194,14 @@ impl NodeArgs { }; NodeConfig::default() - .with_gas_limit(self.evm_opts.gas_limit) + .with_gas_limit(self.evm_opts.gas_limit.map(U256::from)) .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) - .with_gas_price(self.evm_opts.gas_price) + .with_gas_price(self.evm_opts.gas_price.map(U256::from)) .with_hardfork(self.hardfork) .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) + .with_genesis_balance(genesis_balance.to_alloy()) .with_genesis_timestamp(self.timestamp) .with_port(self.port) .with_fork_block_number( @@ -208,13 +210,13 @@ impl NodeArgs { .or_else(|| self.evm_opts.fork_url.as_ref().and_then(|f| f.block)), ) .with_fork_headers(self.evm_opts.fork_headers) - .with_fork_chain_id(self.evm_opts.fork_chain_id.map(u64::from)) + .with_fork_chain_id(self.evm_opts.fork_chain_id.map(u64::from).map(U256::from)) .fork_request_timeout(self.evm_opts.fork_request_timeout.map(Duration::from_millis)) .fork_request_retries(self.evm_opts.fork_request_retries) .fork_retry_backoff(self.evm_opts.fork_retry_backoff.map(Duration::from_millis)) .fork_compute_units_per_second(compute_units_per_second) .with_eth_rpc_url(self.evm_opts.fork_url.map(|fork| fork.url)) - .with_base_fee(self.evm_opts.block_base_fee_per_gas) + .with_base_fee(self.evm_opts.block_base_fee_per_gas.map(U256::from)) .with_storage_caching(self.evm_opts.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 612a7ec071c5..1d0a1a60dd15 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -15,28 +15,30 @@ use crate::{ mem::in_memory_db::MemDb, FeeManager, Hardfork, }; +use alloy_primitives::{hex, U256}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::BlockNumberOrTag; +use alloy_transport::TransportError; use anvil_server::ServerConfig; use ethers::{ core::k256::ecdsa::SigningKey, - prelude::{rand::thread_rng, Wallet, U256}, - providers::Middleware, + prelude::{rand::thread_rng, Wallet}, signers::{ coins_bip39::{English, Mnemonic}, MnemonicBuilder, Signer, }, - types::BlockNumber, - utils::{format_ether, hex, to_checksum, WEI_IN_ETHER}, + utils::WEI_IN_ETHER, }; use foundry_common::{ - types::{ToAlloy, ToEthers}, - ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT, + provider::alloy::ProviderBuilder, types::ToAlloy, ALCHEMY_FREE_TIER_CUPS, + NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT, }; use foundry_config::Config; use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, revm, - revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv, U256 as rU256}, + revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}, utils::apply_chain_and_block_specific_env_changes, }; use parking_lot::RwLock; @@ -192,13 +194,10 @@ Available Accounts ================== "# ); - let balance = format_ether(self.genesis_balance); + let balance = alloy_primitives::utils::format_ether(self.genesis_balance); for (idx, wallet) in self.genesis_accounts.iter().enumerate() { - let _ = write!( - config_string, - "\n({idx}) {:?} ({balance} ETH)", - to_checksum(&wallet.address(), None) - ); + write!(config_string, "\n({idx}) {} ({balance} ETH)", wallet.address().to_alloy()) + .unwrap(); } let _ = write!( @@ -375,7 +374,7 @@ impl Default for NodeConfig { genesis_timestamp: None, genesis_accounts, // 100ETH default balance - genesis_balance: WEI_IN_ETHER.saturating_mul(100u64.into()), + genesis_balance: WEI_IN_ETHER.to_alloy().saturating_mul(U256::from(100u64)), block_time: None, no_mining: false, port: NODE_PORT, @@ -418,12 +417,12 @@ impl NodeConfig { pub fn get_base_fee(&self) -> U256 { self.base_fee .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas)) - .unwrap_or_else(|| INITIAL_BASE_FEE.into()) + .unwrap_or_else(|| U256::from(INITIAL_BASE_FEE)) } /// Returns the base fee to use pub fn get_gas_price(&self) -> U256 { - self.gas_price.unwrap_or_else(|| INITIAL_GAS_PRICE.into()) + self.gas_price.unwrap_or_else(|| U256::from(INITIAL_GAS_PRICE)) } /// Returns the base fee to use @@ -473,9 +472,9 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub fn with_gas_limit>(mut self, gas_limit: Option) -> Self { + pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { if let Some(gas_limit) = gas_limit { - self.gas_limit = gas_limit.into(); + self.gas_limit = gas_limit; } self } @@ -491,7 +490,7 @@ impl NodeConfig { /// Sets the gas price #[must_use] - pub fn with_gas_price>(mut self, gas_price: Option) -> Self { + pub fn with_gas_price(mut self, gas_price: Option) -> Self { self.gas_price = gas_price.map(Into::into); self } @@ -515,7 +514,7 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee>(mut self, base_fee: Option) -> Self { + pub fn with_base_fee(mut self, base_fee: Option) -> Self { self.base_fee = base_fee.map(Into::into); self } @@ -660,7 +659,7 @@ impl NodeConfig { /// Sets the `fork_chain_id` to use to fork off local cache from #[must_use] - pub fn with_fork_chain_id>(mut self, fork_chain_id: Option) -> Self { + pub fn with_fork_chain_id(mut self, fork_chain_id: Option) -> Self { self.fork_chain_id = fork_chain_id.map(Into::into); self } @@ -769,7 +768,7 @@ impl NodeConfig { .expect("Failed writing json"); } if self.silent { - return + return; } println!("{}", self.as_string(fork)) @@ -780,7 +779,7 @@ impl NodeConfig { /// See also [ Config::foundry_block_cache_file()] pub fn block_cache_path(&self, block: u64) -> Option { if self.no_storage_caching || self.eth_rpc_url.is_none() { - return None + return None; } let chain_id = self.get_chain_id(); @@ -815,8 +814,8 @@ impl NodeConfig { let mut env = revm::primitives::Env { cfg, block: BlockEnv { - gas_limit: self.gas_limit.to_alloy(), - basefee: self.get_base_fee().to_alloy(), + gas_limit: self.gas_limit, + basefee: self.get_base_fee(), ..Default::default() }, tx: TxEnv { chain_id: self.get_chain_id().into(), ..Default::default() }, @@ -837,8 +836,8 @@ impl NodeConfig { let genesis = GenesisConfig { timestamp: self.get_genesis_timestamp(), - balance: self.genesis_balance.to_alloy(), - accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(), + balance: self.genesis_balance, + accounts: self.genesis_accounts.iter().map(|acc| acc.address().to_alloy()).collect(), fork_genesis_account_infos: Arc::new(Default::default()), genesis_init: self.genesis.clone(), }; @@ -862,7 +861,7 @@ impl NodeConfig { // if the option is not disabled and we are not forking. if !self.disable_default_create2_deployer && self.eth_rpc_url.is_none() { backend - .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER.to_ethers()) + .set_create2_deployer(DEFAULT_CREATE2_DEPLOYER) .await .expect("Failed to create default create2 deployer"); } @@ -936,13 +935,13 @@ impl NodeConfig { // auto adjust hardfork if not specified // but only if we're forking mainnet let chain_id = - provider.get_chainid().await.expect("Failed to fetch network chain id"); - if chain_id == ethers::types::Chain::Mainnet.into() { + provider.get_chain_id().await.expect("Failed to fetch network chain ID"); + if alloy_chains::NamedChain::Mainnet == chain_id.to::() { let hardfork: Hardfork = fork_block_number.into(); env.cfg.spec_id = hardfork.into(); self.hardfork = Some(hardfork); } - Some(chain_id) + Some(U256::from(chain_id)) } else { None }; @@ -950,14 +949,13 @@ impl NodeConfig { (fork_block_number, chain_id) } else { // pick the last block number but also ensure it's not pending anymore - ( - find_latest_fork_block(&provider).await.expect("Failed to get fork block number"), - None, - ) + let bn = + find_latest_fork_block(&provider).await.expect("Failed to get fork block number"); + (bn, None) }; let block = provider - .get_block(BlockNumber::Number(fork_block_number.into())) + .get_block(BlockNumberOrTag::Number(fork_block_number).into(), false) .await .expect("Failed to get fork block"); @@ -972,7 +970,7 @@ latest block number: {latest_block}" // If the `eth_getBlockByNumber` call succeeds, but returns null instead of // the block, and the block number is less than equal the latest block, then // the user is forking from a non-archive node with an older block number. - if fork_block_number <= latest_block.as_u64() { + if fork_block_number <= latest_block { message.push_str(&format!("\n{}", NON_ARCHIVE_NODE_WARNING)); } panic!("{}", message); @@ -983,18 +981,18 @@ latest block number: {latest_block}" // we only use the gas limit value of the block if it is non-zero and the block gas // limit is enabled, since there are networks where this is not used and is always // `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also - let gas_limit = if self.disable_block_gas_limit || block.gas_limit.is_zero() { - rU256::from(u64::MAX) + let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit.is_zero() { + U256::from(u64::MAX) } else { - block.gas_limit.to_alloy() + block.header.gas_limit }; env.block = BlockEnv { - number: rU256::from(fork_block_number), - timestamp: block.timestamp.to_alloy(), - difficulty: block.difficulty.to_alloy(), + number: U256::from(fork_block_number), + timestamp: block.header.timestamp, + difficulty: block.header.difficulty, // ensures prevrandao is set - prevrandao: Some(block.mix_hash.unwrap_or_default()).map(|h| h.to_alloy()), + prevrandao: Some(block.header.mix_hash.unwrap_or_default()), gas_limit, // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, @@ -1007,18 +1005,18 @@ latest block number: {latest_block}" // if not set explicitly we use the base fee of the latest block if self.base_fee.is_none() { - if let Some(base_fee) = block.base_fee_per_gas { + if let Some(base_fee) = block.header.base_fee_per_gas { self.base_fee = Some(base_fee); - env.block.basefee = base_fee.to_alloy(); + env.block.basefee = base_fee; // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( - block.gas_used, - block.gas_limit, - block.base_fee_per_gas.unwrap_or_default(), + block.header.gas_used, + block.header.gas_limit, + block.header.base_fee_per_gas.unwrap_or_default(), ); // update next base fee - fees.set_base_fee(next_block_base_fee.into()); + fees.set_base_fee(U256::from(next_block_base_fee)); } } @@ -1030,17 +1028,16 @@ latest block number: {latest_block}" } } - let block_hash = block.hash.unwrap_or_default(); + let block_hash = block.header.hash.unwrap_or_default(); let chain_id = if let Some(chain_id) = self.chain_id { chain_id } else { let chain_id = if let Some(fork_chain_id) = fork_chain_id { - fork_chain_id + fork_chain_id.to::() } else { - provider.get_chainid().await.unwrap() - } - .as_u64(); + provider.get_chain_id().await.unwrap().to::() + }; // need to update the dev signers and env with the chain id self.set_chain_id(Some(chain_id)); @@ -1072,8 +1069,8 @@ latest block number: {latest_block}" provider, chain_id, override_chain_id, - timestamp: block.timestamp.as_u64(), - base_fee: block.base_fee_per_gas, + timestamp: block.header.timestamp.to::(), + base_fee: block.header.base_fee_per_gas, timeout: self.fork_request_timeout, retries: self.fork_request_retries, backoff: self.fork_retry_backoff, @@ -1192,15 +1189,15 @@ pub fn anvil_tmp_dir() -> Option { /// /// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field /// is present). This prevents edge cases where anvil forks the "latest" block but `eth_getBlockByNumber` still returns a pending block, -async fn find_latest_fork_block(provider: M) -> Result { - let mut num = provider.get_block_number().await?.as_u64(); +async fn find_latest_fork_block(provider: P) -> Result { + let mut num = provider.get_block_number().await?; // walk back from the head of the chain, but at most 2 blocks, which should be more than enough // leeway for _ in 0..2 { - if let Some(block) = provider.get_block(num).await? { - if block.hash.is_some() { - break + if let Some(block) = provider.get_block(num.into(), false).await? { + if block.header.hash.is_some() { + break; } } // block not actually finalized, so we try the block before diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 232045e7552d..84419c9ce3b4 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -29,12 +29,24 @@ use crate::{ revm::primitives::Output, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; +use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256, U64}; +use alloy_rpc_trace_types::{ + geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace, +}; +use alloy_rpc_types::{ + state::StateOverride, + txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, + AccessList, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, + BlockTransactions, CallRequest, EIP1186AccountProofResponse, FeeHistory, Filter, + FilteredParams, Log, Transaction, TransactionReceipt, +}; +use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ block::BlockInfo, - proof::AccountProof, - state::StateOverride, transaction::{ + call_to_internal_tx_request, to_alloy_proof, to_ethers_access_list, EthTransactionRequest, LegacyTransaction, PendingTransaction, TransactionKind, TypedTransaction, TypedTransactionRequest, }, @@ -46,22 +58,11 @@ use anvil_core::{ }, }; use anvil_rpc::{error::RpcError, response::ResponseResult}; -use ethers::{ - abi::ethereum_types::H64, - prelude::{DefaultFrame, TxpoolInspect}, - providers::ProviderError, - types::{ - transaction::{ - eip2930::{AccessList, AccessListWithGasUsed}, - eip712::TypedData, - }, - Address, Block, BlockId, BlockNumber, Bytes, FeeHistory, Filter, FilteredParams, - GethDebugTracingOptions, GethTrace, Log, Signature, Trace, Transaction, TransactionReceipt, - TxHash, TxpoolContent, TxpoolInspectSummary, TxpoolStatus, H256, U256, U64, - }, - utils::rlp, +use ethers::{types::transaction::eip712::TypedData, utils::rlp}; +use foundry_common::{ + provider::alloy::ProviderBuilder, + types::{ToAlloy, ToEthers}, }; -use foundry_common::{types::ToEthers, ProviderBuilder}; use foundry_evm::{ backend::DatabaseError, revm::{ @@ -109,7 +110,7 @@ pub struct EthApi { /// Whether we're listening for RPC calls net_listening: bool, /// The instance ID. Changes on every reset. - instance_id: Arc>, + instance_id: Arc>, } // === impl Eth RPC API === @@ -140,7 +141,7 @@ impl EthApi { filters, net_listening: true, transaction_order: Arc::new(RwLock::new(transactions_order)), - instance_id: Arc::new(RwLock::new(H256::random())), + instance_id: Arc::new(RwLock::new(B256::random())), } } @@ -323,16 +324,22 @@ impl EthApi { EthRequest::EvmRevert(id) => self.evm_revert(id).await.to_rpc_result(), EthRequest::EvmIncreaseTime(time) => self.evm_increase_time(time).await.to_rpc_result(), EthRequest::EvmSetNextBlockTimeStamp(time) => { - match u64::try_from(time).map_err(BlockchainError::UintConversion) { - Ok(time) => self.evm_set_next_block_timestamp(time).to_rpc_result(), - err @ Err(_) => err.to_rpc_result(), + if time >= U256::from(u64::MAX) { + return ResponseResult::Error(RpcError::invalid_params( + "The timestamp is too big", + )) } + let time = time.to::(); + self.evm_set_next_block_timestamp(time).to_rpc_result() } EthRequest::EvmSetTime(timestamp) => { - match u64::try_from(timestamp).map_err(BlockchainError::UintConversion) { - Ok(timestamp) => self.evm_set_time(timestamp).to_rpc_result(), - err @ Err(_) => err.to_rpc_result(), + if timestamp >= U256::from(u64::MAX) { + return ResponseResult::Error(RpcError::invalid_params( + "The timestamp is too big", + )) } + let time = timestamp.to::(); + self.evm_set_time(time).to_rpc_result() } EthRequest::EvmSetBlockGasLimit(gas_limit) => { self.evm_set_block_gas_limit(gas_limit).to_rpc_result() @@ -410,14 +417,18 @@ impl EthApi { ) -> Result { match request { TypedTransactionRequest::Deposit(_) => { - const NIL_SIGNATURE: ethers::types::Signature = - Signature { r: U256::zero(), s: U256::zero(), v: 0 }; + 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) } _ => { for signer in self.signers.iter() { - if signer.accounts().contains(from) { - let signature = signer.sign_transaction(request.clone(), from)?; + if signer.accounts().contains(&from.to_ethers()) { + let signature = + signer.sign_transaction(request.clone(), &from.to_ethers())?; return build_typed_transaction(request, signature) } } @@ -439,7 +450,7 @@ impl EthApi { } _ => { let number = self.backend.ensure_block_number(block_number).await?; - BlockRequest::Number(number.into()) + BlockRequest::Number(number) } }; Ok(block_request) @@ -475,7 +486,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_hashrate` pub fn hashrate(&self) -> Result { node_info!("eth_hashrate"); - Ok(U256::zero()) + Ok(U256::ZERO) } /// Returns the client coinbase address. @@ -501,7 +512,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_chainId` pub fn eth_chain_id(&self) -> Result> { node_info!("eth_chainId"); - Ok(Some(self.backend.chain_id().into())) + Ok(Some(self.backend.chain_id().to::())) } /// Returns the same as `chain_id` @@ -509,7 +520,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_networkId` pub fn network_id(&self) -> Result> { node_info!("eth_networkId"); - let chain_id = self.backend.chain_id(); + let chain_id = self.backend.chain_id().to::(); Ok(Some(format!("{chain_id}"))) } @@ -562,7 +573,7 @@ impl EthApi { .into_iter() .filter(|acc| unique.insert(*acc)), ); - Ok(accounts) + Ok(accounts.into_iter().map(|acc| acc.to_alloy()).collect()) } /// Returns the number of most recent block. @@ -570,7 +581,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_blockNumber` pub fn block_number(&self) -> Result { node_info!("eth_blockNumber"); - Ok(self.backend.best_number().as_u64().into()) + Ok(U256::from(self.backend.best_number())) } /// Returns balance of the given account. @@ -581,10 +592,13 @@ impl EthApi { let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { - return Ok(fork.get_balance(address, number.as_u64()).await?) + if fork.predates_fork(number) { + return fork + .get_balance(address, number) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } @@ -600,17 +614,23 @@ impl EthApi { address: Address, index: U256, block_number: Option, - ) -> Result { + ) -> Result { node_info!("eth_getStorageAt"); let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { - return Ok(fork - .storage_at(address, index, Some(BlockNumber::Number(*number))) - .await?) + if fork.predates_fork(number) { + return Ok(B256::from( + fork.storage_at( + address, + B256::from(index), + Some(BlockNumber::Number(number)), + ) + .await + .map_err(|_| BlockchainError::DataUnavailable)?, + )); } } } @@ -621,7 +641,7 @@ impl EthApi { /// Returns block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash(&self, hash: H256) -> Result>> { + pub async fn block_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash(hash).await } @@ -629,7 +649,7 @@ impl EthApi { /// Returns a _full_ block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockByHash` - pub async fn block_by_hash_full(&self, hash: H256) -> Result>> { + pub async fn block_by_hash_full(&self, hash: B256) -> Result> { node_info!("eth_getBlockByHash"); self.backend.block_by_hash_full(hash).await } @@ -637,10 +657,10 @@ impl EthApi { /// Returns block with given number. /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number(&self, number: BlockNumber) -> Result>> { + pub async fn block_by_number(&self, number: BlockNumber) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { - return Ok(Some(self.pending_block().await)) + return Ok(Some(self.pending_block().await)); } self.backend.block_by_number(number).await @@ -649,13 +669,10 @@ impl EthApi { /// Returns a _full_ block with given number /// /// Handler for ETH RPC call: `eth_getBlockByNumber` - pub async fn block_by_number_full( - &self, - number: BlockNumber, - ) -> Result>> { + pub async fn block_by_number_full(&self, number: BlockNumber) -> Result> { node_info!("eth_getBlockByNumber"); if number == BlockNumber::Pending { - return Ok(self.pending_block_full().await) + return Ok(self.pending_block_full().await); } self.backend.block_by_number_full(number).await } @@ -678,10 +695,15 @@ impl EthApi { /// Returns the number of transactions in a block with given hash. /// /// Handler for ETH RPC call: `eth_getBlockTransactionCountByHash` - pub async fn block_transaction_count_by_hash(&self, hash: H256) -> Result> { + pub async fn block_transaction_count_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getBlockTransactionCountByHash"); let block = self.backend.block_by_hash(hash).await?; - Ok(block.map(|b| b.transactions.len().into())) + let txs = block.map(|b| match b.transactions { + BlockTransactions::Full(txs) => U256::from(txs.len()), + BlockTransactions::Hashes(txs) => U256::from(txs.len()), + BlockTransactions::Uncle => U256::from(0), + }); + Ok(txs) } /// Returns the number of transactions in a block with given block number. @@ -695,20 +717,25 @@ impl EthApi { let block_request = self.block_request(Some(block_number.into())).await?; if let BlockRequest::Pending(txs) = block_request { let block = self.backend.pending_block(txs).await; - return Ok(Some(block.transactions.len().into())) + return Ok(Some(U256::from(block.transactions.len()))); } let block = self.backend.block_by_number(block_number).await?; - Ok(block.map(|b| b.transactions.len().into())) + let txs = block.map(|b| match b.transactions { + BlockTransactions::Full(txs) => U256::from(txs.len()), + BlockTransactions::Hashes(txs) => U256::from(txs.len()), + BlockTransactions::Uncle => U256::from(0), + }); + Ok(txs) } /// Returns the number of uncles in a block with given hash. /// /// Handler for ETH RPC call: `eth_getUncleCountByBlockHash` - pub async fn block_uncles_count_by_hash(&self, hash: H256) -> Result { + pub async fn block_uncles_count_by_hash(&self, hash: B256) -> Result { node_info!("eth_getUncleCountByBlockHash"); let block = self.backend.block_by_hash(hash).await?.ok_or(BlockchainError::BlockNotFound)?; - Ok(block.uncles.len().into()) + Ok(U256::from(block.uncles.len())) } /// Returns the number of uncles in a block with given block number. @@ -721,7 +748,7 @@ impl EthApi { .block_by_number(block_number) .await? .ok_or(BlockchainError::BlockNotFound)?; - Ok(block.uncles.len().into()) + Ok(U256::from(block.uncles.len())) } /// Returns the code at given address at given time (block number). @@ -731,10 +758,13 @@ impl EthApi { node_info!("eth_getCode"); let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { - return Ok(fork.get_code(address, number.as_u64()).await?) + if fork.predates_fork(number) { + return fork + .get_code(address, number) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } @@ -748,24 +778,27 @@ impl EthApi { pub async fn get_proof( &self, address: Address, - keys: Vec, + keys: Vec, block_number: Option, - ) -> Result { + ) -> Result { node_info!("eth_getProof"); let block_request = self.block_request(block_number).await?; - if let BlockRequest::Number(number) = &block_request { + // If we're in forking mode, or still on the forked block (no blocks mined yet) then we can + // delegate the call. + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - // if we're in forking mode, or still on the forked block (no blocks mined yet) then - // we can delegate the call - if fork.predates_fork_inclusive(number.as_u64()) { - return Ok(fork.get_proof(address, keys, Some((*number).into())).await?) + if fork.predates_fork_inclusive(number) { + return fork + .get_proof(address, keys, Some(number.into())) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } let proof = self.backend.prove_account_at(address, keys, Some(block_request)).await?; - Ok(proof) + Ok(to_alloy_proof(proof)) } /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md). @@ -798,7 +831,7 @@ 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, data).await?; + let signature = signer.sign_typed_data(address.to_ethers(), data).await?; Ok(format!("0x{signature}")) } @@ -808,7 +841,7 @@ 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, content.as_ref()).await?; + let signature = signer.sign(address.to_ethers(), content.as_ref()).await?; Ok(format!("0x{signature}")) } @@ -818,16 +851,24 @@ 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) - })?; + 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 (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)?; + let signature = signer.sign_transaction(request, &from.to_ethers())?; Ok(format!("0x{signature}")) } @@ -837,12 +878,19 @@ 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) - })?; + 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 (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) { @@ -850,7 +898,7 @@ impl EthApi { 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) + PendingTransaction::with_impersonated(transaction, from.to_ethers()) } else { let transaction = self.sign_request(&from, request)?; self.ensure_typed_transaction_supported(&transaction)?; @@ -861,7 +909,7 @@ impl EthApi { self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.as_u64(), from)]; + let provides = vec![to_marker(nonce.to::(), from)]; debug_assert!(requires != provides); self.add_pending_transaction(pending_transaction, requires, provides) @@ -874,7 +922,7 @@ impl EthApi { node_info!("eth_sendRawTransaction"); let data = tx.as_ref(); if data.is_empty() { - return Err(BlockchainError::EmptyRawTransactionData) + return Err(BlockchainError::EmptyRawTransactionData); } let transaction = if data[0] > 0x7f { // legacy transaction @@ -903,15 +951,16 @@ impl EthApi { // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; - let on_chain_nonce = self.backend.current_nonce(*pending_transaction.sender()).await?; + let on_chain_nonce = + self.backend.current_nonce(pending_transaction.sender().to_alloy()).await?; let from = *pending_transaction.sender(); let nonce = *pending_transaction.transaction.nonce(); - let requires = required_marker(nonce, on_chain_nonce, from); + let requires = required_marker(nonce.to_alloy(), on_chain_nonce, from.to_alloy()); let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = PoolTransaction { requires, - provides: vec![to_marker(nonce.as_u64(), *pending_transaction.sender())], + provides: vec![to_marker(nonce.as_u64(), pending_transaction.sender().to_alloy())], pending_transaction, priority, }; @@ -926,22 +975,25 @@ impl EthApi { /// Handler for ETH RPC call: `eth_call` pub async fn call( &self, - request: EthTransactionRequest, + request: CallRequest, block_number: Option, overrides: Option, ) -> Result { node_info!("eth_call"); let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { + if fork.predates_fork(number) { if overrides.is_some() { return Err(BlockchainError::StateOverrideError( "not available on past forked blocks".to_string(), - )) + )); } - return Ok(fork.call(&request, Some(number.into())).await?) + return fork + .call(&request, Some(number.into())) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } @@ -952,7 +1004,7 @@ impl EthApi { request.max_priority_fee_per_gas, )? .or_zero_fees(); - + let request = call_to_internal_tx_request(&request); // this can be blocking for a bit, especially in forking mode // self.on_blocking_task(|this| async move { @@ -980,20 +1032,25 @@ impl EthApi { /// Handler for ETH RPC call: `eth_createAccessList` pub async fn create_access_list( &self, - mut request: EthTransactionRequest, + request: CallRequest, block_number: Option, ) -> Result { node_info!("eth_createAccessList"); let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { - return Ok(fork.create_access_list(&request, Some(number.into())).await?) + if fork.predates_fork(number) { + return fork + .create_access_list(&request, Some(number.into())) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } + let mut request = call_to_internal_tx_request(&request); + self.backend .with_database_at(Some(block_request), |state, block_env| { let (exit, out, _, access_list) = self.backend.build_access_list_with_state( @@ -1005,7 +1062,7 @@ impl EthApi { ensure_return_ok(exit, &out)?; // execute again but with access list set - request.access_list = Some(access_list.0.clone()); + request.access_list = Some(to_ethers_access_list(access_list.clone()).0); let (exit, out, gas_used, _) = self.backend.call_with_state( &state, @@ -1017,7 +1074,7 @@ impl EthApi { Ok(AccessListWithGasUsed { access_list: AccessList(access_list.0), - gas_used: gas_used.into(), + gas_used: U256::from(gas_used), }) }) .await? @@ -1029,7 +1086,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_estimateGas` pub async fn estimate_gas( &self, - request: EthTransactionRequest, + request: CallRequest, block_number: Option, ) -> Result { node_info!("eth_estimateGas"); @@ -1043,12 +1100,12 @@ impl EthApi { /// this will also scan the mempool for a matching pending transaction /// /// Handler for ETH RPC call: `eth_getTransactionByHash` - pub async fn transaction_by_hash(&self, hash: H256) -> Result> { + pub async fn transaction_by_hash(&self, hash: B256) -> Result> { node_info!("eth_getTransactionByHash"); let mut tx = self.pool.get_transaction(hash).map(|pending| { let from = *pending.sender(); let mut tx = transaction_build( - Some(*pending.hash()), + Some(pending.hash().to_alloy()), pending.transaction, None, None, @@ -1056,7 +1113,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; + tx.from = from.to_alloy(); tx }); if tx.is_none() { @@ -1071,7 +1128,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_getTransactionByBlockHashAndIndex` pub async fn transaction_by_block_hash_and_index( &self, - hash: H256, + hash: B256, index: Index, ) -> Result> { node_info!("eth_getTransactionByBlockHashAndIndex"); @@ -1093,11 +1150,11 @@ impl EthApi { /// Returns transaction receipt by transaction hash. /// /// Handler for ETH RPC call: `eth_getTransactionReceipt` - pub async fn transaction_receipt(&self, hash: H256) -> Result> { + pub async fn transaction_receipt(&self, hash: B256) -> Result> { node_info!("eth_getTransactionReceipt"); let tx = self.pool.get_transaction(hash); if tx.is_some() { - return Ok(None) + return Ok(None); } self.backend.transaction_receipt(hash).await } @@ -1107,14 +1164,18 @@ impl EthApi { /// Handler for ETH RPC call: `eth_getUncleByBlockHashAndIndex` pub async fn uncle_by_block_hash_and_index( &self, - block_hash: H256, + block_hash: B256, idx: Index, - ) -> Result>> { + ) -> Result> { node_info!("eth_getUncleByBlockHashAndIndex"); - let number = self.backend.ensure_block_number(Some(BlockId::Hash(block_hash))).await?; + let number = + self.backend.ensure_block_number(Some(BlockId::Hash(block_hash.into()))).await?; if let Some(fork) = self.get_fork() { if fork.predates_fork_inclusive(number) { - return Ok(fork.uncle_by_block_hash_and_index(block_hash, idx.into()).await?) + return fork + .uncle_by_block_hash_and_index(block_hash, idx.into()) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } // It's impossible to have uncles outside of fork mode @@ -1128,12 +1189,15 @@ impl EthApi { &self, block_number: BlockNumber, idx: Index, - ) -> Result>> { + ) -> Result> { node_info!("eth_getUncleByBlockNumberAndIndex"); let number = self.backend.ensure_block_number(Some(BlockId::Number(block_number))).await?; if let Some(fork) = self.get_fork() { if fork.predates_fork_inclusive(number) { - return Ok(fork.uncle_by_block_number_and_index(number, idx.into()).await?) + return fork + .uncle_by_block_number_and_index(number, idx.into()) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } // It's impossible to have uncles outside of fork mode @@ -1167,7 +1231,7 @@ impl EthApi { /// Used for submitting a proof-of-work solution. /// /// Handler for ETH RPC call: `eth_submitWork` - pub fn submit_work(&self, _: H64, _: H256, _: H256) -> Result { + pub fn submit_work(&self, _: B64, _: B256, _: B256) -> Result { node_info!("eth_submitWork"); Err(BlockchainError::RpcUnimplemented) } @@ -1175,7 +1239,7 @@ impl EthApi { /// Used for submitting mining hashrate. /// /// Handler for ETH RPC call: `eth_submitHashrate` - pub fn submit_hashrate(&self, _: U256, _: H256) -> Result { + pub fn submit_hashrate(&self, _: U256, _: B256) -> Result { node_info!("eth_submitHashrate"); Err(BlockchainError::RpcUnimplemented) } @@ -1192,13 +1256,13 @@ impl EthApi { node_info!("eth_feeHistory"); // max number of blocks in the requested range - let current = self.backend.best_number().as_u64(); + let current = self.backend.best_number(); let slots_in_an_epoch = 32u64; let number = match newest_block { BlockNumber::Latest | BlockNumber::Pending => current, BlockNumber::Earliest => 0, - BlockNumber::Number(n) => n.as_u64(), + BlockNumber::Number(n) => n, BlockNumber::Safe => current.saturating_sub(slots_in_an_epoch), BlockNumber::Finalized => current.saturating_sub(slots_in_an_epoch * 2), }; @@ -1208,28 +1272,23 @@ impl EthApi { // if we're still at the forked block we don't have any history and can't compute it // efficiently, instead we fetch it from the fork if fork.predates_fork_inclusive(number) { - return Ok(fork - .fee_history( - block_count, - BlockNumber::Number(number.into()), - &reward_percentiles, - ) - .await?) + return fork + .fee_history(block_count, BlockNumber::Number(number), &reward_percentiles) + .await + .map_err(BlockchainError::AlloyForkProvider); } } const MAX_BLOCK_COUNT: u64 = 1024u64; - let range_limit = U256::from(MAX_BLOCK_COUNT); - let block_count = - if block_count > range_limit { range_limit.as_u64() } else { block_count.as_u64() }; + let block_count = block_count.to::().max(MAX_BLOCK_COUNT); // highest and lowest block num in the requested range let highest = number; let lowest = highest.saturating_sub(block_count.saturating_sub(1)); // only support ranges that are in cache range - if lowest < self.backend.best_number().as_u64().saturating_sub(self.fee_history_limit) { - return Err(FeeHistoryError::InvalidBlockRange.into()) + if lowest < self.backend.best_number().saturating_sub(self.fee_history_limit) { + return Err(FeeHistoryError::InvalidBlockRange.into()); } let fee_history = self.fee_history_cache.lock(); @@ -1238,7 +1297,7 @@ impl EthApi { oldest_block: U256::from(lowest), base_fee_per_gas: Vec::new(), gas_used_ratio: Vec::new(), - reward: Default::default(), + reward: Some(Default::default()), }; let mut rewards = Vec::new(); @@ -1259,7 +1318,7 @@ impl EthApi { let reward = if let Some(r) = block.rewards.get(index as usize) { U256::from(*r) } else { - U256::zero() + U256::ZERO }; block_rewards.push(reward); } @@ -1268,14 +1327,14 @@ impl EthApi { } } - response.reward = rewards; + response.reward = Some(rewards); // calculate next base fee if let (Some(last_gas_used), Some(last_fee_per_gas)) = (response.gas_used_ratio.last(), response.base_fee_per_gas.last()) { let elasticity = self.backend.elasticity(); - let last_fee_per_gas = last_fee_per_gas.as_u64() as f64; + let last_fee_per_gas = last_fee_per_gas.to::() as f64; if last_gas_used > &0.5 { // increase base gas let increase = ((last_gas_used - 0.5) * 2f64) * elasticity; @@ -1376,14 +1435,10 @@ impl EthApi { /// Handler for RPC call: `debug_traceTransaction` pub async fn debug_trace_transaction( &self, - tx_hash: H256, + tx_hash: B256, opts: GethDebugTracingOptions, ) -> Result { node_info!("debug_traceTransaction"); - if opts.tracer.is_some() { - return Err(RpcError::invalid_params("non-default tracer not supported yet").into()) - } - self.backend.debug_trace_transaction(tx_hash, opts).await } @@ -1392,14 +1447,11 @@ impl EthApi { /// Handler for RPC call: `debug_traceCall` pub async fn debug_trace_call( &self, - request: EthTransactionRequest, + request: CallRequest, block_number: Option, - opts: GethDebugTracingOptions, + opts: GethDefaultTracingOptions, ) -> Result { node_info!("debug_traceCall"); - if opts.tracer.is_some() { - return Err(RpcError::invalid_params("non-default tracer not supported yet").into()) - } let block_request = self.block_request(block_number).await?; let fees = FeeDetails::new( request.gas_price, @@ -1408,13 +1460,15 @@ impl EthApi { )? .or_zero_fees(); + let request = call_to_internal_tx_request(&request); + self.backend.call_with_tracing(request, fees, Some(block_request), opts).await } /// Returns traces for the transaction hash via parity's tracing endpoint /// /// Handler for RPC call: `trace_transaction` - pub async fn trace_transaction(&self, tx_hash: H256) -> Result> { + pub async fn trace_transaction(&self, tx_hash: B256) -> Result> { node_info!("trace_transaction"); self.backend.trace_transaction(tx_hash).await } @@ -1422,7 +1476,7 @@ impl EthApi { /// Returns traces for the transaction hash via parity's tracing endpoint /// /// Handler for RPC call: `trace_block` - pub async fn trace_block(&self, block: BlockNumber) -> Result> { + pub async fn trace_block(&self, block: BlockNumber) -> Result> { node_info!("trace_block"); self.backend.trace_block(block).await } @@ -1474,7 +1528,7 @@ impl EthApi { node_info!("evm_setAutomine"); if self.miner.is_auto_mine() { if enable_automine { - return Ok(()) + return Ok(()); } self.miner.set_mining_mode(MiningMode::None); } else if enable_automine { @@ -1490,14 +1544,14 @@ impl EthApi { /// Handler for ETH RPC call: `anvil_mine` pub async fn anvil_mine(&self, num_blocks: Option, interval: Option) -> Result<()> { node_info!("anvil_mine"); - let interval = interval.map(|i| i.as_u64()); - let blocks = num_blocks.unwrap_or_else(U256::one); - if blocks == U256::zero() { - return Ok(()) + let interval = interval.map(|i| i.to::()); + let blocks = num_blocks.unwrap_or(U256::from(1)); + if blocks == U256::ZERO { + return Ok(()); } // mine all the blocks - for _ in 0..blocks.as_u64() { + for _ in 0..blocks.to::() { self.mine_one().await; // If we have an interval, jump forwards in time to the "next" timestamp @@ -1531,9 +1585,9 @@ impl EthApi { /// Removes transactions from the pool /// /// Handler for RPC call: `anvil_dropTransaction` - pub async fn anvil_drop_transaction(&self, tx_hash: H256) -> Result> { + pub async fn anvil_drop_transaction(&self, tx_hash: B256) -> Result> { node_info!("anvil_dropTransaction"); - Ok(self.pool.drop_transaction(tx_hash).map(|tx| *tx.hash())) + Ok(self.pool.drop_transaction(tx_hash).map(|tx| tx.hash())) } /// Reset the fork to a fresh forked state, and optionally update the fork config. @@ -1592,7 +1646,7 @@ impl EthApi { &self, address: Address, slot: U256, - val: H256, + val: B256, ) -> Result { node_info!("anvil_setStorageAt"); self.backend.set_storage_at(address, slot, val).await?; @@ -1617,7 +1671,7 @@ impl EthApi { return Err(RpcError::invalid_params( "anvil_setMinGasPrice is not supported when EIP-1559 is active", ) - .into()) + .into()); } self.backend.set_gas_price(gas); Ok(()) @@ -1632,7 +1686,7 @@ impl EthApi { return Err(RpcError::invalid_params( "anvil_setNextBlockBaseFeePerGas is only supported when EIP-1559 is active", ) - .into()) + .into()); } self.backend.set_base_fee(basefee); Ok(()) @@ -1681,7 +1735,7 @@ impl EthApi { let tx_order = self.transaction_order.read(); Ok(NodeInfo { - current_block_number: self.backend.best_number(), + current_block_number: U64::from(self.backend.best_number()), current_block_timestamp: env.block.timestamp.try_into().unwrap_or(u64::MAX), current_block_hash: self.backend.best_hash(), hard_fork: env.cfg.spec_id, @@ -1691,7 +1745,7 @@ impl EthApi { }, environment: NodeEnvironment { base_fee: self.backend.base_fee(), - chain_id: self.backend.chain_id(), + chain_id: self.backend.chain_id().to::(), gas_limit: self.backend.gas_limit(), gas_price: self.backend.gas_price(), }, @@ -1719,9 +1773,9 @@ impl EthApi { Ok(AnvilMetadata { client_version: CLIENT_VERSION, - chain_id: self.backend.chain_id(), + chain_id: self.backend.chain_id().to::(), latest_block_hash: self.backend.best_hash(), - latest_block_number: self.backend.best_number().as_u64(), + latest_block_number: self.backend.best_number(), instance_id: *self.instance_id.read(), forked_network: fork_config.map(|cfg| ForkedNetwork { chain_id: cfg.chain_id(), @@ -1828,27 +1882,28 @@ impl EthApi { /// **Note**: This behaves exactly as [Self::evm_mine] but returns different output, for /// compatibility reasons, this is a separate call since `evm_mine` is not an anvil original. /// and `ganache` may change the `0x0` placeholder. - pub async fn evm_mine_detailed( - &self, - opts: Option, - ) -> Result>> { + pub async fn evm_mine_detailed(&self, opts: Option) -> Result> { node_info!("evm_mine_detailed"); let mined_blocks = self.do_evm_mine(opts).await?; let mut blocks = Vec::with_capacity(mined_blocks as usize); - let latest = self.backend.best_number().as_u64(); + let latest = self.backend.best_number(); for offset in (0..mined_blocks).rev() { let block_num = latest - offset; if let Some(mut block) = - self.backend.block_by_number_full(BlockNumber::Number(block_num.into())).await? + self.backend.block_by_number_full(BlockNumber::Number(block_num)).await? { - for tx in block.transactions.iter_mut() { + let mut block_txs = match block.transactions { + BlockTransactions::Full(txs) => txs, + BlockTransactions::Hashes(_) | BlockTransactions::Uncle => unreachable!(), + }; + for tx in block_txs.iter_mut() { if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { if let Some(output) = receipt.out { // insert revert reason if failure - if receipt.inner.status.unwrap_or_default().as_u64() == 0 { + if receipt.inner.status_code.unwrap_or_default().to::() == 0 { if let Some(reason) = decode_revert_reason(&output) { tx.other.insert( "revertReason".to_string(), @@ -1863,6 +1918,7 @@ impl EthApi { } } } + block.transactions = BlockTransactions::Full(block_txs); blocks.push(block); } } @@ -1886,16 +1942,16 @@ impl EthApi { node_info!("anvil_setRpcUrl"); if let Some(fork) = self.backend.get_fork() { let mut config = fork.config.write(); - let interval = config.provider.get_interval(); + // let interval = config.provider.get_interval(); let new_provider = Arc::new( - ProviderBuilder::new(&url) - .max_retry(10) - .initial_backoff(1000) - .build() - .map_err(|_| { - ProviderError::CustomError(format!("Failed to parse invalid url {url}")) - })? - .interval(interval), + ProviderBuilder::new(&url).max_retry(10).initial_backoff(1000).build().map_err( + |_| { + TransportErrorKind::custom_str( + format!("Failed to parse invalid url {url}").as_str(), + ) + }, + // TODO: Add interval + )?, // .interval(interval), ); config.provider = new_provider; trace!(target: "backend", "Updated fork rpc from \"{}\" to \"{}\"", config.eth_rpc_url, url); @@ -1922,7 +1978,7 @@ 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)?; + let from = request.from.ok_or(BlockchainError::NoSignerAvailable)?.to_alloy(); let (nonce, on_chain_nonce) = self.request_nonce(&request, from).await?; @@ -1933,13 +1989,14 @@ impl EthApi { self.ensure_typed_transaction_supported(&transaction)?; - let pending_transaction = PendingTransaction::with_impersonated(transaction, from); + let pending_transaction = + PendingTransaction::with_impersonated(transaction, from.to_ethers()); // pre-validate self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.as_u64(), from)]; + let provides = vec![to_marker(nonce.to::(), from)]; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -1966,10 +2023,10 @@ impl EthApi { fn convert(tx: Arc) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; - let to = tx.to().copied(); - let gas_price = tx.gas_price(); - let value = tx.value(); - let gas = tx.gas_limit(); + 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(); TxpoolInspectSummary { to, value, gas, gas_price } } @@ -1980,12 +2037,14 @@ 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()).or_default(); + let entry = + inspect.pending.entry(pending.pending_transaction.sender().to_alloy()).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()).or_default(); + let entry = + inspect.pending.entry(queued.pending_transaction.sender().to_alloy()).or_default(); let key = queued.pending_transaction.nonce().to_string(); entry.insert(key, convert(queued)); } @@ -2004,7 +2063,7 @@ impl EthApi { fn convert(tx: Arc) -> Transaction { let from = *tx.pending_transaction.sender(); let mut tx = transaction_build( - Some(*tx.hash()), + Some(tx.hash()), tx.pending_transaction.transaction.clone(), None, None, @@ -2013,17 +2072,19 @@ 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; + tx.from = from.to_alloy(); tx } for pending in self.pool.ready_transactions() { - let entry = content.pending.entry(*pending.pending_transaction.sender()).or_default(); + let entry = + content.pending.entry(pending.pending_transaction.sender().to_alloy()).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()).or_default(); + let entry = + content.pending.entry(queued.pending_transaction.sender().to_alloy()).or_default(); let key = queued.pending_transaction.nonce().to_string(); entry.insert(key, convert(queued)); } @@ -2084,15 +2145,18 @@ impl EthApi { async fn do_estimate_gas( &self, - request: EthTransactionRequest, + request: CallRequest, block_number: Option, ) -> Result { let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork(number.as_u64()) { - return Ok(fork.estimate_gas(&request, Some(number.into())).await?) + if fork.predates_fork(number) { + return fork + .estimate_gas(&request, Some(number.into())) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } @@ -2106,33 +2170,34 @@ impl EthApi { /// Estimates the gas usage of the `request` with the state. /// - /// This will execute the [EthTransactionRequest] and find the best gas limit via binary search + /// This will execute the [CallRequest] and find the best gas limit via binary search fn do_estimate_gas_with_state( &self, - mut request: EthTransactionRequest, + request: CallRequest, state: D, block_env: BlockEnv, ) -> Result where D: DatabaseRef, { + let mut request = call_to_internal_tx_request(&request); // if the request is a simple transfer we can optimize let likely_transfer = request.data.as_ref().map(|data| data.as_ref().is_empty()).unwrap_or(true); if likely_transfer { if let Some(to) = request.to { - if let Ok(target_code) = self.backend.get_code_with_state(&state, to) { + if let Ok(target_code) = self.backend.get_code_with_state(&state, to.to_alloy()) { if target_code.as_ref().is_empty() { - return Ok(MIN_TRANSACTION_GAS) + return Ok(MIN_TRANSACTION_GAS); } } } } let fees = FeeDetails::new( - request.gas_price, - request.max_fee_per_gas, - request.max_priority_fee_per_gas, + request.gas_price.map(ToAlloy::to_alloy), + request.max_fee_per_gas.map(ToAlloy::to_alloy), + request.max_priority_fee_per_gas.map(ToAlloy::to_alloy), )? .or_zero_fees(); @@ -2143,20 +2208,21 @@ impl EthApi { // check with the funds of the sender if let Some(from) = request.from { let gas_price = fees.gas_price.unwrap_or_default(); - if gas_price > U256::zero() { - let mut available_funds = self.backend.get_balance_with_state(&state, from)?; + if gas_price > U256::ZERO { + let mut available_funds = + self.backend.get_balance_with_state(&state, from.to_alloy())?; if let Some(value) = request.value { - if value > available_funds { - return Err(InvalidTransactionError::InsufficientFunds.into()) + if value > available_funds.to_ethers() { + return Err(InvalidTransactionError::InsufficientFunds.into()); } // safe: value < available_funds - available_funds -= value; + available_funds -= value.to_alloy(); } // amount of gas the sender can afford with the `gas_price` let allowance = available_funds.checked_div(gas_price).unwrap_or_default(); - if highest_gas_limit > allowance { + if highest_gas_limit > allowance.to_ethers() { trace!(target: "node", "eth_estimateGas capped by limited user funds"); - highest_gas_limit = allowance; + highest_gas_limit = allowance.to_ethers(); } } } @@ -2184,8 +2250,8 @@ impl EthApi { self.backend.clone(), block_env, fees, - gas_limit, - )) + gas_limit.to_alloy(), + )); } } @@ -2209,16 +2275,17 @@ impl EthApi { self.backend.clone(), block_env, fees, - gas_limit, + gas_limit.to_alloy(), )) } else { // the transaction did fail due to lack of gas from the user - Err(InvalidTransactionError::Revert(Some(convert_transact_out(&out))).into()) - } + Err(InvalidTransactionError::Revert(Some(convert_transact_out(&out).0.into())) + .into()) + }; } reason => { warn!(target: "node", "estimation failed due to {:?}", reason); - return Err(BlockchainError::EvmError(reason)) + return Err(BlockchainError::EvmError(reason)); } } @@ -2226,16 +2293,19 @@ impl EthApi { // transaction succeeds with. we find this by doing a binary search over the // possible range NOTE: this is the gas the transaction used, which is less than the // transaction requires to succeed - let gas: U256 = gas.into(); + 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(request.clone()); // pick a point that's close to the estimated gas - let mut mid_gas_limit = std::cmp::min(gas * 3, (highest_gas_limit + lowest_gas_limit) / 2); + let mut mid_gas_limit = std::cmp::min( + gas * U256::from(3), + ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(), + ); // Binary search for the ideal gas limit - while (highest_gas_limit - lowest_gas_limit) > U256::one() { - request.gas = Some(mid_gas_limit); + while (highest_gas_limit - lowest_gas_limit.to_ethers()).to_alloy() > U256::from(1) { + request.gas = Some(mid_gas_limit.to_ethers()); let ethres = self.backend.call_with_state( &state, request.clone(), @@ -2253,8 +2323,8 @@ impl EthApi { lowest_gas_limit = mid_gas_limit; // new midpoint - mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; - continue + mid_gas_limit = ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(); + continue; } match ethres { @@ -2263,7 +2333,7 @@ impl EthApi { // at the current midpoint, as spending any more gas would // make no sense (as the TX would still succeed). return_ok!() => { - highest_gas_limit = mid_gas_limit; + highest_gas_limit = mid_gas_limit.to_ethers(); } // If the transaction failed due to lack of gas, we can set a floor for the // lowest gas limit at the current midpoint, as spending any @@ -2286,16 +2356,16 @@ impl EthApi { // real error. Err(reason) => { warn!(target: "node", "estimation failed due to {:?}", reason); - return Err(reason) + return Err(reason); } } // new midpoint - mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; + mid_gas_limit = ((highest_gas_limit + lowest_gas_limit.to_ethers()) / 2).to_alloy(); } trace!(target : "node", "Estimated Gas for call {:?}", highest_gas_limit); - Ok(highest_gas_limit) + Ok(highest_gas_limit.to_alloy()) } /// Updates the `TransactionOrder` @@ -2310,27 +2380,28 @@ impl EthApi { /// Returns the chain ID used for transaction pub fn chain_id(&self) -> u64 { - self.backend.chain_id() + self.backend.chain_id().to::() } + /// Returns the configured fork, if any. pub fn get_fork(&self) -> Option { self.backend.get_fork() } /// Returns the current instance's ID. - pub fn instance_id(&self) -> H256 { + pub fn instance_id(&self) -> B256 { *self.instance_id.read() } /// Resets the instance ID. pub fn reset_instance_id(&self) { - *self.instance_id.write() = H256::random(); + *self.instance_id.write() = B256::random(); } /// 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)) + self.signers.iter().find(|signer| signer.is_signer_for(address.to_ethers())) } /// Returns a new block event stream that yields Notifications when a new block was added @@ -2363,14 +2434,14 @@ impl EthApi { } /// Returns the pending block with tx hashes - async fn pending_block(&self) -> Block { + async fn pending_block(&self) -> Block { let transactions = self.pool.ready_transactions().collect::>(); let info = self.backend.pending_block(transactions).await; self.backend.convert_block(info.block) } /// Returns the full pending block with `Transaction` objects - async fn pending_block_full(&self) -> Option> { + async fn pending_block_full(&self) -> Option { let transactions = self.pool.ready_transactions().collect::>(); let BlockInfo { block, transactions, receipts: _ } = self.backend.pending_block(transactions).await; @@ -2384,7 +2455,7 @@ impl EthApi { let tx = block.transactions.get(info.transaction_index as usize)?.clone(); let tx = transaction_build( - Some(info.transaction_hash), + Some(info.transaction_hash.to_alloy()), tx, Some(&block), Some(info), @@ -2405,38 +2476,42 @@ impl EthApi { let max_fee_per_gas = request.max_fee_per_gas; let gas_price = request.gas_price; - let gas_limit = request.gas.map(Ok).unwrap_or_else(|| self.current_gas_limit())?; + let gas_limit = request + .gas + .map(ToAlloy::to_alloy) + .map(Ok) + .unwrap_or_else(|| self.current_gas_limit())?; let request = match request.into_typed_request() { Some(TypedTransactionRequest::Legacy(mut m)) => { - m.nonce = nonce; + m.nonce = nonce.to_ethers(); m.chain_id = Some(chain_id); - m.gas_limit = gas_limit; + m.gas_limit = gas_limit.to_ethers(); if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default(); + m.gas_price = self.gas_price().unwrap_or_default().to_ethers(); } TypedTransactionRequest::Legacy(m) } Some(TypedTransactionRequest::EIP2930(mut m)) => { - m.nonce = nonce; + m.nonce = nonce.to_ethers(); m.chain_id = chain_id; - m.gas_limit = gas_limit; + m.gas_limit = gas_limit.to_ethers(); if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default(); + m.gas_price = self.gas_price().unwrap_or_default().to_ethers(); } TypedTransactionRequest::EIP2930(m) } Some(TypedTransactionRequest::EIP1559(mut m)) => { - m.nonce = nonce; + m.nonce = nonce.to_ethers(); m.chain_id = chain_id; - m.gas_limit = gas_limit; + m.gas_limit = gas_limit.to_ethers(); if max_fee_per_gas.is_none() { - m.max_fee_per_gas = self.gas_price().unwrap_or_default(); + m.max_fee_per_gas = self.gas_price().unwrap_or_default().to_ethers(); } TypedTransactionRequest::EIP1559(m) } Some(TypedTransactionRequest::Deposit(mut m)) => { - m.gas_limit = gas_limit; + m.gas_limit = gas_limit.to_ethers(); TypedTransactionRequest::Deposit(m) } _ => return Err(BlockchainError::FailedToDecodeTransaction), @@ -2446,7 +2521,7 @@ impl EthApi { /// Returns true if the `addr` is currently impersonated pub fn is_impersonated(&self, addr: Address) -> bool { - self.backend.cheats().is_impersonated(addr) + self.backend.cheats().is_impersonated(addr.to_ethers()) } /// Returns the nonce of the `address` depending on the `block_number` @@ -2457,10 +2532,13 @@ impl EthApi { ) -> Result { let block_request = self.block_request(block_number).await?; - if let BlockRequest::Number(number) = &block_request { + if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { - if fork.predates_fork_inclusive(number.as_u64()) { - return Ok(fork.get_nonce(address, number.as_u64()).await?) + if fork.predates_fork_inclusive(number) { + return fork + .get_nonce(address, number) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } } @@ -2484,7 +2562,7 @@ impl EthApi { ) -> Result<(U256, U256)> { let highest_nonce = self.get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))).await?; - let nonce = request.nonce.unwrap_or(highest_nonce); + let nonce = request.nonce.map(ToAlloy::to_alloy).unwrap_or(highest_nonce); Ok((nonce, highest_nonce)) } @@ -2506,7 +2584,7 @@ impl EthApi { } /// Returns the current state root - pub async fn state_root(&self) -> Option { + pub async fn state_root(&self) -> Option { self.backend.get_db().read().await.maybe_state_root() } @@ -2523,11 +2601,11 @@ impl EthApi { fn required_marker(provided_nonce: U256, on_chain_nonce: U256, from: Address) -> Vec { if provided_nonce == on_chain_nonce { - return Vec::new() + return Vec::new(); } - let prev_nonce = provided_nonce.saturating_sub(U256::one()); + let prev_nonce = provided_nonce.saturating_sub(U256::from(1)); if on_chain_nonce <= prev_nonce { - vec![to_marker(prev_nonce.as_u64(), from)] + vec![to_marker(prev_nonce.to::(), from)] } else { Vec::new() } @@ -2546,7 +2624,7 @@ fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result Ok(out), - return_revert!() => Err(InvalidTransactionError::Revert(Some(out)).into()), + return_revert!() => Err(InvalidTransactionError::Revert(Some(out.0.into())).into()), reason => Err(BlockchainError::EvmError(reason)), } } @@ -2565,7 +2643,7 @@ fn map_out_of_gas_err( where D: DatabaseRef, { - request.gas = Some(backend.gas_limit()); + request.gas = Some(backend.gas_limit()).map(|g| g.to_ethers()); let (exit, out, _, _) = match backend.call_with_state(&state, request, fees, block_env) { Ok(res) => res, Err(err) => return err, @@ -2574,11 +2652,11 @@ 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).into() + InvalidTransactionError::BasicOutOfGas(gas_limit.to_ethers()).into() } return_revert!() => { // reverted again after bumping the limit - InvalidTransactionError::Revert(Some(convert_transact_out(&out))).into() + InvalidTransactionError::Revert(Some(convert_transact_out(&out).0.into())).into() } reason => { warn!(target: "node", "estimation failed due to {:?}", reason); diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 87684b9a64b8..7d86e4893d82 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -1,14 +1,10 @@ //! Helper types for working with [revm](foundry_evm::revm) -use crate::{mem::state::trie_hash_db, revm::primitives::AccountInfo, U256}; -use alloy_primitives::{Address as B160, B256, U256 as rU256}; +use crate::{mem::state::trie_hash_db, revm::primitives::AccountInfo}; +use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; +use alloy_rpc_types::BlockId; use anvil_core::eth::trie::KeccakHasher; -use ethers::{ - prelude::{Address, Bytes}, - types::{BlockId, H256}, - utils::keccak256, -}; -use foundry_common::{errors::FsPathError, types::ToAlloy}; +use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, MemDb, RevertSnapshotAction, StateSnapshot}, fork::BlockchainDb, @@ -30,11 +26,11 @@ pub type AsHashDB = Box>>; #[auto_impl::auto_impl(Box)] pub trait MaybeHashDatabase: DatabaseRef { /// Return the DB as read-only hashdb and the root key - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { + fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { None } /// Return the storage DB as read-only hashdb and the storage root of the account - fn maybe_account_db(&self, _addr: Address) -> Option<(AsHashDB, H256)> { + fn maybe_account_db(&self, _addr: Address) -> Option<(AsHashDB, B256)> { None } @@ -52,10 +48,10 @@ impl<'a, T: 'a + MaybeHashDatabase + ?Sized> MaybeHashDatabase for &'a T where &'a T: DatabaseRef, { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { + fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { T::maybe_as_hash_db(self) } - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, H256)> { + fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { T::maybe_account_db(self, addr) } @@ -95,7 +91,7 @@ pub trait Db: /// Sets the nonce of the given address fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> { - let mut info = self.basic(address.to_alloy())?.unwrap_or_default(); + let mut info = self.basic(address)?.unwrap_or_default(); info.nonce = nonce; self.insert_account(address, info); Ok(()) @@ -103,15 +99,15 @@ pub trait Db: /// Sets the balance of the given address fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> { - let mut info = self.basic(address.to_alloy())?.unwrap_or_default(); - info.balance = balance.to_alloy(); + let mut info = self.basic(address)?.unwrap_or_default(); + info.balance = balance; self.insert_account(address, info); Ok(()) } /// Sets the balance of the given address fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> { - let mut info = self.basic(address.to_alloy())?.unwrap_or_default(); + let mut info = self.basic(address)?.unwrap_or_default(); let code_hash = if code.as_ref().is_empty() { KECCAK_EMPTY } else { @@ -127,7 +123,7 @@ pub trait Db: fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()>; /// inserts a blockhash for the given number - fn insert_block_hash(&mut self, number: U256, hash: H256); + fn insert_block_hash(&mut self, number: U256, hash: B256); /// Write all chain data to serialized bytes buffer fn dump_state(&self) -> DatabaseResult>; @@ -135,7 +131,7 @@ pub trait Db: /// Deserialize and add all chain data to the backend storage fn load_state(&mut self, state: SerializableState) -> DatabaseResult { for (addr, account) in state.accounts.into_iter() { - let old_account_nonce = DatabaseRef::basic_ref(self, addr.to_alloy()) + let old_account_nonce = DatabaseRef::basic_ref(self, addr) .ok() .and_then(|acc| acc.map(|acc| acc.nonce)) .unwrap_or_default(); @@ -146,7 +142,7 @@ pub trait Db: self.insert_account( addr, AccountInfo { - balance: account.balance.to_alloy(), + balance: account.balance, code_hash: KECCAK_EMPTY, // will be set automatically code: if account.code.0.is_empty() { None @@ -175,7 +171,7 @@ pub trait Db: fn revert(&mut self, snapshot: U256, action: RevertSnapshotAction) -> bool; /// Returns the state root if possible to compute - fn maybe_state_root(&self) -> Option { + fn maybe_state_root(&self) -> Option { None } @@ -189,15 +185,15 @@ pub trait Db: /// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block()) impl + Send + Sync + Clone + fmt::Debug> Db for CacheDB { fn insert_account(&mut self, address: Address, account: AccountInfo) { - self.insert_account_info(address.to_alloy(), account) + self.insert_account_info(address, account) } fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.insert_account_storage(address.to_alloy(), slot.to_alloy(), val.to_alloy()) + self.insert_account_storage(address, slot, val) } - fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.block_hashes.insert(number.to_alloy(), hash.to_alloy()); + fn insert_block_hash(&mut self, number: U256, hash: B256) { + self.block_hashes.insert(number, hash); } fn dump_state(&self) -> DatabaseResult> { @@ -205,7 +201,7 @@ impl + Send + Sync + Clone + fmt::Debug> D } fn snapshot(&mut self) -> U256 { - U256::zero() + U256::ZERO } fn revert(&mut self, _snapshot: U256, _action: RevertSnapshotAction) -> bool { @@ -218,7 +214,7 @@ impl + Send + Sync + Clone + fmt::Debug> D } impl> MaybeHashDatabase for CacheDB { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { + fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { Some(trie_hash_db(&self.accounts)) } fn clear_into_snapshot(&mut self) -> StateSnapshot { @@ -287,7 +283,7 @@ impl StateDb { impl DatabaseRef for StateDb { type Error = DatabaseError; - fn basic_ref(&self, address: B160) -> DatabaseResult> { + fn basic_ref(&self, address: Address) -> DatabaseResult> { self.0.basic_ref(address) } @@ -295,21 +291,21 @@ impl DatabaseRef for StateDb { self.0.code_by_hash_ref(code_hash) } - fn storage_ref(&self, address: B160, index: rU256) -> DatabaseResult { + fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult { self.0.storage_ref(address, index) } - fn block_hash_ref(&self, number: rU256) -> DatabaseResult { + fn block_hash_ref(&self, number: U256) -> DatabaseResult { self.0.block_hash_ref(number) } } impl MaybeHashDatabase for StateDb { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { + fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { self.0.maybe_as_hash_db() } - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, H256)> { + fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { self.0.maybe_account_db(addr) } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index ed7adfc41331..a3100d5800de 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -20,12 +20,13 @@ use ethers::{ use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::{ backend::DatabaseError, + inspectors::{TracingInspector, TracingInspectorConfig}, revm, revm::{ interpreter::InstructionResult, primitives::{BlockEnv, CfgEnv, EVMError, Env, ExecutionResult, Output, SpecId}, }, - traces::{CallTraceArena, CallTraceNode}, + traces::CallTraceNode, utils::{eval_to_instruction_result, halt_to_instruction_result}, }; use std::sync::Arc; @@ -164,14 +165,14 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let transaction_index = transaction_infos.len() as u32; let info = TransactionInfo { - transaction_hash: *transaction.hash(), + transaction_hash: transaction.hash().to_ethers(), transaction_index, from: *transaction.pending_transaction.sender(), to: transaction.pending_transaction.transaction.to().copied(), contract_address: contract_address.map(|c| c.to_ethers()), logs, logs_bloom: *receipt.logs_bloom(), - traces: CallTraceArena { arena: traces }, + traces, exit, out: match out { Some(Output::Call(b)) => Some(ethers::types::Bytes(b.0)), @@ -192,7 +193,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let partial_header = PartialHeader { parent_hash, beneficiary: beneficiary.to_ethers(), - state_root: self.db.maybe_state_root().unwrap_or_default(), + state_root: self.db.maybe_state_root().unwrap_or_default().to_ethers(), receipts_root, logs_bloom: bloom, difficulty: difficulty.to_ethers(), @@ -321,7 +322,12 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator out, gas_used, logs: logs.unwrap_or_default().into_iter().map(Into::into).collect(), - traces: inspector.tracer.unwrap_or_default().traces.arena, + traces: inspector + .tracer + .unwrap_or(TracingInspector::new(TracingInspectorConfig::all())) + .get_traces() + .clone() + .into_nodes(), nonce, }; diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index c58ed414b38b..9e6add417efd 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -1,18 +1,19 @@ //! Support for forking off another client use crate::eth::{backend::db::Db, error::BlockchainError}; -use anvil_core::eth::{proof::AccountProof, transaction::EthTransactionRequest}; -use ethers::{ - prelude::BlockNumber, - providers::{Middleware, ProviderError}, - types::{ - transaction::eip2930::AccessListWithGasUsed, Address, Block, BlockId, Bytes, FeeHistory, - Filter, GethDebugTracingOptions, GethTrace, Log, Trace, Transaction, TransactionReceipt, - TxHash, H256, U256, - }, +use alloy_primitives::{Address, Bytes, StorageKey, StorageValue, B256, U256, U64}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_trace_types::{ + geth::{GethDebugTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace as Trace, }; -use foundry_common::{ProviderBuilder, RetryProvider}; -use foundry_evm::utils::u256_to_h256_be; +use alloy_rpc_types::{ + AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, + CallRequest, EIP1186AccountProofResponse, FeeHistory, Filter, Log, Transaction, + TransactionReceipt, +}; +use alloy_transport::TransportError; +use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use parking_lot::{ lock_api::{RwLockReadGuard, RwLockWriteGuard}, RawRwLock, RwLock, @@ -63,23 +64,23 @@ impl ClientFork { self.config.write().update_url(url)?; let override_chain_id = self.config.read().override_chain_id; let chain_id = if let Some(chain_id) = override_chain_id { - chain_id.into() + chain_id } else { - self.provider().get_chainid().await? + self.provider().get_chain_id().await?.to::() }; - self.config.write().chain_id = chain_id.as_u64(); + self.config.write().chain_id = chain_id; } let provider = self.provider(); let block = - provider.get_block(block_number).await?.ok_or(BlockchainError::BlockNotFound)?; - let block_hash = block.hash.ok_or(BlockchainError::BlockNotFound)?; - let timestamp = block.timestamp.as_u64(); - let base_fee = block.base_fee_per_gas; + provider.get_block(block_number, false).await?.ok_or(BlockchainError::BlockNotFound)?; + let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; + let timestamp = block.header.timestamp.to::(); + let base_fee = block.header.base_fee_per_gas; let total_difficulty = block.total_difficulty.unwrap_or_default(); self.config.write().update_block( - block.number.ok_or(BlockchainError::BlockNotFound)?.as_u64(), + block.header.number.ok_or(BlockchainError::BlockNotFound)?.to::(), block_hash, timestamp, base_fee, @@ -121,7 +122,7 @@ impl ClientFork { self.config.read().base_fee } - pub fn block_hash(&self) -> H256 { + pub fn block_hash(&self) -> B256 { self.config.read().block_hash } @@ -151,73 +152,75 @@ impl ClientFork { block_count: U256, newest_block: BlockNumber, reward_percentiles: &[f64], - ) -> Result { - self.provider().fee_history(block_count, newest_block, reward_percentiles).await + ) -> Result { + self.provider().get_fee_history(block_count, newest_block, reward_percentiles).await } /// Sends `eth_getProof` pub async fn get_proof( &self, address: Address, - keys: Vec, + keys: Vec, block_number: Option, - ) -> Result { + ) -> Result { self.provider().get_proof(address, keys, block_number).await } /// Sends `eth_call` pub async fn call( &self, - request: &EthTransactionRequest, + request: &CallRequest, block: Option, - ) -> Result { + ) -> Result { let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); if let BlockNumber::Number(num) = block { // check if this request was already been sent - let key = (request.clone(), num.as_u64()); + let key = (request.clone(), num); if let Some(res) = self.storage_read().eth_call.get(&key).cloned() { - return Ok(res) + return Ok(res); } } - let tx = ethers::utils::serialize(request.as_ref()); - let block_value = ethers::utils::serialize(&block); - let res: Bytes = self.provider().request("eth_call", [tx, block_value]).await?; + let block_id: BlockId = block.into(); + + let res: Bytes = self.provider().call((*request).clone(), Some(block_id)).await?; if let BlockNumber::Number(num) = block { // cache result let mut storage = self.storage_write(); - storage.eth_call.insert((request, num.as_u64()), res.clone()); + storage.eth_call.insert((request, num), res.clone()); } + Ok(res) } /// Sends `eth_call` pub async fn estimate_gas( &self, - request: &EthTransactionRequest, + request: &CallRequest, block: Option, - ) -> Result { + ) -> Result { let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); if let BlockNumber::Number(num) = block { // check if this request was already been sent - let key = (request.clone(), num.as_u64()); + let key = (request.clone(), num); if let Some(res) = self.storage_read().eth_gas_estimations.get(&key).cloned() { - return Ok(res) + return Ok(res); } } - let tx = ethers::utils::serialize(request.as_ref()); - let block_value = ethers::utils::serialize(&block); - let res = self.provider().request("eth_estimateGas", [tx, block_value]).await?; + + let block_id: BlockId = block.into(); + + let res = self.provider().estimate_gas((*request).clone(), Some(block_id)).await?; if let BlockNumber::Number(num) = block { // cache result let mut storage = self.storage_write(); - storage.eth_gas_estimations.insert((request, num.as_u64()), res); + storage.eth_gas_estimations.insert((request, num), res); } Ok(res) @@ -226,30 +229,28 @@ impl ClientFork { /// Sends `eth_createAccessList` pub async fn create_access_list( &self, - request: &EthTransactionRequest, + request: &CallRequest, block: Option, - ) -> Result { - let tx = ethers::utils::serialize(request); - let block = ethers::utils::serialize(&block.unwrap_or(BlockNumber::Latest)); - self.provider().request("eth_createAccessList", [tx, block]).await + ) -> Result { + self.provider().create_access_list(request.clone(), block.map(|b| b.into())).await } pub async fn storage_at( &self, address: Address, - index: U256, + index: StorageKey, number: Option, - ) -> Result { - let index = u256_to_h256_be(index); + ) -> Result { + let index = B256::from(index); self.provider().get_storage_at(address, index, number.map(Into::into)).await } - pub async fn logs(&self, filter: &Filter) -> Result, ProviderError> { + pub async fn logs(&self, filter: &Filter) -> Result, TransportError> { if let Some(logs) = self.storage_read().logs.get(filter).cloned() { - return Ok(logs) + return Ok(logs); } - let logs = self.provider().get_logs(filter).await?; + let logs = self.provider().get_logs(filter.clone()).await?; let mut storage = self.storage_write(); storage.logs.insert(filter.clone(), logs.clone()); @@ -260,15 +261,18 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_code={:?}", address); if let Some(code) = self.storage_read().code_at.get(&(address, blocknumber)).cloned() { - return Ok(code) + return Ok(code); } - let code = self.provider().get_code(address, Some(blocknumber.into())).await?; + let block_id = BlockId::Number(blocknumber.into()); + + let code = self.provider().get_code_at(address, block_id).await?; + let mut storage = self.storage_write(); - storage.code_at.insert((address, blocknumber), code.clone()); + storage.code_at.insert((address, blocknumber), code.clone().0.into()); Ok(code) } @@ -277,7 +281,7 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); self.provider().get_balance(address, Some(blocknumber.into())).await } @@ -286,7 +290,7 @@ impl ClientFork { &self, address: Address, blocknumber: u64, - ) -> Result { + ) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); self.provider().get_transaction_count(address, Some(blocknumber.into())).await } @@ -295,10 +299,21 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result, ProviderError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { - if let Some(tx_hash) = block.transactions.get(index) { - return self.transaction_by_hash(*tx_hash).await + match block.transactions { + BlockTransactions::Full(txs) => { + if let Some(tx) = txs.get(index) { + return Ok(Some(tx.clone())); + } + } + BlockTransactions::Hashes(hashes) => { + if let Some(tx_hash) = hashes.get(index) { + return self.transaction_by_hash(*tx_hash).await; + } + } + // TODO(evalir): Is it possible to reach this case? Should we support it + BlockTransactions::Uncle => panic!("Uncles not supported"), } } Ok(None) @@ -306,12 +321,23 @@ impl ClientFork { pub async fn transaction_by_block_hash_and_index( &self, - hash: H256, + hash: B256, index: usize, - ) -> Result, ProviderError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { - if let Some(tx_hash) = block.transactions.get(index) { - return self.transaction_by_hash(*tx_hash).await + match block.transactions { + BlockTransactions::Full(txs) => { + if let Some(tx) = txs.get(index) { + return Ok(Some(tx.clone())); + } + } + BlockTransactions::Hashes(hashes) => { + if let Some(tx_hash) = hashes.get(index) { + return self.transaction_by_hash(*tx_hash).await; + } + } + // TODO(evalir): Is it possible to reach this case? Should we support it + BlockTransactions::Uncle => panic!("Uncles not supported"), } } Ok(None) @@ -319,27 +345,27 @@ impl ClientFork { pub async fn transaction_by_hash( &self, - hash: H256, - ) -> Result, ProviderError> { + hash: B256, + ) -> Result, TransportError> { trace!(target: "backend::fork", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() { - return Ok(tx) + return Ok(tx); } - if let Some(tx) = self.provider().get_transaction(hash).await? { - let mut storage = self.storage_write(); - storage.transactions.insert(hash, tx.clone()); - return Ok(Some(tx)) - } - Ok(None) + let tx = self.provider().get_transaction_by_hash(hash).await?; + + let mut storage = self.storage_write(); + storage.transactions.insert(hash, tx.clone()); + Ok(Some(tx)) } - pub async fn trace_transaction(&self, hash: H256) -> Result, ProviderError> { + pub async fn trace_transaction(&self, hash: B256) -> Result, TransportError> { if let Some(traces) = self.storage_read().transaction_traces.get(&hash).cloned() { - return Ok(traces) + return Ok(traces); } - let traces = self.provider().trace_transaction(hash).await?; + let traces = self.provider().trace_transaction(hash).await?.into_iter().collect::>(); + let mut storage = self.storage_write(); storage.transaction_traces.insert(hash, traces.clone()); @@ -348,26 +374,29 @@ impl ClientFork { pub async fn debug_trace_transaction( &self, - hash: H256, + hash: B256, opts: GethDebugTracingOptions, - ) -> Result { + ) -> Result { if let Some(traces) = self.storage_read().geth_transaction_traces.get(&hash).cloned() { - return Ok(traces) + return Ok(traces); } let trace = self.provider().debug_trace_transaction(hash, opts).await?; + let mut storage = self.storage_write(); storage.geth_transaction_traces.insert(hash, trace.clone()); Ok(trace) } - pub async fn trace_block(&self, number: u64) -> Result, ProviderError> { + pub async fn trace_block(&self, number: u64) -> Result, TransportError> { if let Some(traces) = self.storage_read().block_traces.get(&number).cloned() { - return Ok(traces) + return Ok(traces); } - let traces = self.provider().trace_block(number.into()).await?; + let traces = + self.provider().trace_block(number.into()).await?.into_iter().collect::>(); + let mut storage = self.storage_write(); storage.block_traces.insert(number, traces.clone()); @@ -376,35 +405,36 @@ impl ClientFork { pub async fn transaction_receipt( &self, - hash: H256, - ) -> Result, ProviderError> { + hash: B256, + ) -> Result, TransportError> { if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() { - return Ok(Some(receipt)) + return Ok(Some(receipt)); } if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? { let mut storage = self.storage_write(); storage.transaction_receipts.insert(hash, receipt.clone()); - return Ok(Some(receipt)) + return Ok(Some(receipt)); } Ok(None) } - pub async fn block_by_hash(&self, hash: H256) -> Result>, ProviderError> { - if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { - return Ok(Some(block)) + pub async fn block_by_hash(&self, hash: B256) -> Result, TransportError> { + if let Some(mut block) = self.storage_read().blocks.get(&hash).cloned() { + block.transactions.convert_to_hashes(); + return Ok(Some(block)); } - let block = self.fetch_full_block(hash).await?.map(Into::into); - Ok(block) + + Ok(self.fetch_full_block(hash).await?.map(|mut b| { + b.transactions.convert_to_hashes(); + b + })) } - pub async fn block_by_hash_full( - &self, - hash: H256, - ) -> Result>, ProviderError> { + pub async fn block_by_hash_full(&self, hash: B256) -> Result, TransportError> { if let Some(block) = self.storage_read().blocks.get(&hash).cloned() { - return Ok(Some(self.convert_to_full_block(block))) + return Ok(Some(self.convert_to_full_block(block))); } self.fetch_full_block(hash).await } @@ -412,25 +442,28 @@ impl ClientFork { pub async fn block_by_number( &self, block_number: u64, - ) -> Result>, ProviderError> { - if let Some(block) = self + ) -> Result, TransportError> { + if let Some(mut block) = self .storage_read() .hashes .get(&block_number) - .copied() - .and_then(|hash| self.storage_read().blocks.get(&hash).cloned()) + .and_then(|hash| self.storage_read().blocks.get(hash).cloned()) { - return Ok(Some(block)) + block.transactions.convert_to_hashes(); + return Ok(Some(block)); } - let block = self.fetch_full_block(block_number).await?.map(Into::into); + let mut block = self.fetch_full_block(block_number).await?; + if let Some(block) = &mut block { + block.transactions.convert_to_hashes(); + } Ok(block) } pub async fn block_by_number_full( &self, block_number: u64, - ) -> Result>, ProviderError> { + ) -> Result, TransportError> { if let Some(block) = self .storage_read() .hashes @@ -438,7 +471,7 @@ impl ClientFork { .copied() .and_then(|hash| self.storage_read().blocks.get(&hash).cloned()) { - return Ok(Some(self.convert_to_full_block(block))) + return Ok(Some(self.convert_to_full_block(block))); } self.fetch_full_block(block_number).await @@ -447,16 +480,20 @@ impl ClientFork { async fn fetch_full_block( &self, block_id: impl Into, - ) -> Result>, ProviderError> { - if let Some(block) = self.provider().get_block_with_txs(block_id.into()).await? { - let hash = block.hash.unwrap(); - let block_number = block.number.unwrap().as_u64(); + ) -> Result, TransportError> { + if let Some(block) = self.provider().get_block(block_id.into(), true).await? { + let hash = block.header.hash.unwrap(); + let block_number = block.header.number.unwrap().to::(); let mut storage = self.storage_write(); // also insert all transactions - storage.transactions.extend(block.transactions.iter().map(|tx| (tx.hash, tx.clone()))); + let block_txs = match block.clone().transactions { + BlockTransactions::Full(txs) => txs, + _ => vec![], + }; + storage.transactions.extend(block_txs.iter().map(|tx| (tx.hash, tx.clone()))); storage.hashes.insert(block_number, hash); - storage.blocks.insert(hash, block.clone().into()); - return Ok(Some(block)) + storage.blocks.insert(hash, block.clone()); + return Ok(Some(block)); } Ok(None) @@ -464,11 +501,11 @@ impl ClientFork { pub async fn uncle_by_block_hash_and_index( &self, - hash: H256, + hash: B256, index: usize, - ) -> Result>, ProviderError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { - return self.uncles_by_block_and_index(block, index).await + return self.uncles_by_block_and_index(block, index).await; } Ok(None) } @@ -477,28 +514,31 @@ impl ClientFork { &self, number: u64, index: usize, - ) -> Result>, ProviderError> { + ) -> Result, TransportError> { if let Some(block) = self.block_by_number(number).await? { - return self.uncles_by_block_and_index(block, index).await + return self.uncles_by_block_and_index(block, index).await; } Ok(None) } async fn uncles_by_block_and_index( &self, - block: Block, + block: Block, index: usize, - ) -> Result>, ProviderError> { - let block_hash = block - .hash - .ok_or_else(|| ProviderError::CustomError("missing block-hash".to_string()))?; + ) -> Result, TransportError> { + let block_hash = block.header.hash.expect("Missing block hash"); + let block_number = block.header.number.expect("Missing block number"); if let Some(uncles) = self.storage_read().uncles.get(&block_hash) { - return Ok(uncles.get(index).cloned()) + return Ok(uncles.get(index).cloned()); } let mut uncles = Vec::with_capacity(block.uncles.len()); for (uncle_idx, _) in block.uncles.iter().enumerate() { - let uncle = match self.provider().get_uncle(block_hash, uncle_idx.into()).await? { + let uncle = match self + .provider() + .get_uncle(block_number.to::().into(), U64::from(uncle_idx)) + .await? + { Some(u) => u, None => return Ok(None), }; @@ -509,10 +549,16 @@ impl ClientFork { } /// Converts a block of hashes into a full block - fn convert_to_full_block(&self, block: Block) -> Block { + fn convert_to_full_block(&self, block: Block) -> Block { let storage = self.storage.read(); - let mut transactions = Vec::with_capacity(block.transactions.len()); - for tx in block.transactions.iter() { + let block_txs_len = match block.transactions { + BlockTransactions::Full(ref txs) => txs.len(), + BlockTransactions::Hashes(ref hashes) => hashes.len(), + // TODO: Should this be supported at all? + BlockTransactions::Uncle => 0, + }; + let mut transactions = Vec::with_capacity(block_txs_len); + for tx in block.transactions.hashes() { if let Some(tx) = storage.transactions.get(tx).cloned() { transactions.push(tx); } @@ -526,7 +572,7 @@ impl ClientFork { pub struct ClientForkConfig { pub eth_rpc_url: String, pub block_number: u64, - pub block_hash: H256, + pub block_hash: B256, // TODO make provider agnostic pub provider: Arc, pub chain_id: u64, @@ -556,7 +602,7 @@ impl ClientForkConfig { /// /// This will fail if no new provider could be established (erroneous URL) fn update_url(&mut self, url: String) -> Result<(), BlockchainError> { - let interval = self.provider.get_interval(); + // let interval = self.provider.get_interval(); self.provider = Arc::new( ProviderBuilder::new(url.as_str()) .timeout(self.timeout) @@ -565,8 +611,7 @@ impl ClientForkConfig { .initial_backoff(self.backoff.as_millis() as u64) .compute_units_per_second(self.compute_units_per_second) .build() - .map_err(|_| BlockchainError::InvalidUrl(url.clone()))? - .interval(interval), + .map_err(|_| BlockchainError::InvalidUrl(url.clone()))?, // .interval(interval), ); trace!(target: "fork", "Updated rpc url {}", url); self.eth_rpc_url = url; @@ -576,7 +621,7 @@ impl ClientForkConfig { pub fn update_block( &mut self, block_number: u64, - block_hash: H256, + block_hash: B256, timestamp: u64, base_fee: Option, total_difficulty: U256, @@ -593,17 +638,17 @@ impl ClientForkConfig { /// Contains cached state fetched to serve EthApi requests #[derive(Clone, Debug, Default)] pub struct ForkedStorage { - pub uncles: HashMap>>, - pub blocks: HashMap>, - pub hashes: HashMap, - pub transactions: HashMap, - pub transaction_receipts: HashMap, - pub transaction_traces: HashMap>, + pub uncles: HashMap>, + pub blocks: HashMap, + pub hashes: HashMap, + pub transactions: HashMap, + pub transaction_receipts: HashMap, + pub transaction_traces: HashMap>, pub logs: HashMap>, - pub geth_transaction_traces: HashMap, + pub geth_transaction_traces: HashMap, pub block_traces: HashMap>, - pub eth_gas_estimations: HashMap<(Arc, u64), U256>, - pub eth_call: HashMap<(Arc, u64), Bytes>, + pub eth_gas_estimations: HashMap<(Arc, u64), U256>, + pub eth_call: HashMap<(Arc, u64), Bytes>, pub code_at: HashMap<(Address, u64), Bytes>, } diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index ab8b8984118b..523208ba83b4 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -4,12 +4,7 @@ use crate::{ eth::backend::db::{Db, MaybeHashDatabase}, genesis::Genesis, }; -use alloy_primitives::{Address as aAddress, B256, U256}; -use ethers::{ - abi::ethereum_types::BigEndianHash, - types::{Address, H256}, -}; -use foundry_common::types::{ToAlloy, ToEthers}; +use alloy_primitives::{Address, B256, U256}; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, StateSnapshot}, revm::{ @@ -68,7 +63,7 @@ impl GenesisConfig { db.insert_account(addr, acc.into()); // insert all storage values for (k, v) in storage.iter() { - db.set_storage_at(addr, k.into_uint(), v.into_uint())?; + db.set_storage_at(addr, U256::from_be_bytes(k.0), U256::from_be_bytes(v.0))?; } } } @@ -103,8 +98,8 @@ pub(crate) struct AtGenesisStateDb<'a> { impl<'a> DatabaseRef for AtGenesisStateDb<'a> { type Error = DatabaseError; - fn basic_ref(&self, address: aAddress) -> DatabaseResult> { - if let Some(acc) = self.accounts.get(&(address.to_ethers())).cloned() { + fn basic_ref(&self, address: Address) -> DatabaseResult> { + if let Some(acc) = self.accounts.get(&(address)).cloned() { return Ok(Some(acc)) } self.db.basic_ref(address) @@ -117,18 +112,12 @@ impl<'a> DatabaseRef for AtGenesisStateDb<'a> { self.db.code_by_hash_ref(code_hash) } - fn storage_ref(&self, address: aAddress, index: U256) -> DatabaseResult { - if let Some(acc) = self - .genesis - .as_ref() - .and_then(|genesis| genesis.alloc.accounts.get(&(address.to_ethers()))) + fn storage_ref(&self, address: Address, index: U256) -> DatabaseResult { + if let Some(acc) = + self.genesis.as_ref().and_then(|genesis| genesis.alloc.accounts.get(&(address))) { - let value = acc - .storage - .get(&H256::from_uint(&(index.to_ethers()))) - .copied() - .unwrap_or_default(); - return Ok(value.into_uint().to_alloy()) + let value = acc.storage.get(&B256::from(index)).copied().unwrap_or_default(); + return Ok(U256::from_be_bytes(value.0)) } self.db.storage_ref(address, index) } diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index 8d4e3601b30d..de0a3d2ac54e 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -1,8 +1,9 @@ //! Handler that can get current storage related data use crate::mem::Backend; +use alloy_primitives::B256; +use alloy_rpc_types::Block as AlloyBlock; use anvil_core::eth::{block::Block, receipt::TypedReceipt}; -use ethers::types::{Block as EthersBlock, TxHash, H256}; use std::{fmt, sync::Arc}; /// A type that can fetch data related to the ethereum storage. @@ -33,17 +34,17 @@ impl StorageInfo { } /// Returns the receipts of the block with the given hash - pub fn receipts(&self, hash: H256) -> Option> { + pub fn receipts(&self, hash: B256) -> Option> { self.backend.mined_receipts(hash) } /// Returns the block with the given hash - pub fn block(&self, hash: H256) -> Option { + pub fn block(&self, hash: B256) -> Option { self.backend.get_block_by_hash(hash) } /// Returns the block with the given hash in the format of the ethereum API - pub fn eth_block(&self, hash: H256) -> Option> { + pub fn eth_block(&self, hash: B256) -> Option { let block = self.block(hash)?; Some(self.backend.convert_block(block)) } diff --git a/crates/anvil/src/eth/backend/mem/cache.rs b/crates/anvil/src/eth/backend/mem/cache.rs index a3990e82de2a..d4aff594878d 100644 --- a/crates/anvil/src/eth/backend/mem/cache.rs +++ b/crates/anvil/src/eth/backend/mem/cache.rs @@ -1,5 +1,5 @@ use crate::config::anvil_tmp_dir; -use ethers::prelude::H256; +use alloy_primitives::B256; use foundry_evm::backend::StateSnapshot; use std::{ io, @@ -19,7 +19,7 @@ pub struct DiskStateCache { impl DiskStateCache { /// Returns the cache file for the given hash - fn with_cache_file(&mut self, hash: H256, f: F) -> Option + fn with_cache_file(&mut self, hash: B256, f: F) -> Option where F: FnOnce(PathBuf) -> R, { @@ -56,7 +56,7 @@ impl DiskStateCache { /// Note: this writes the state on a new spawned task /// /// Caution: this requires a running tokio Runtime. - pub fn write(&mut self, hash: H256, state: StateSnapshot) { + pub fn write(&mut self, hash: B256, state: StateSnapshot) { self.with_cache_file(hash, |file| { tokio::task::spawn(async move { match foundry_common::fs::write_json_file(&file, &state) { @@ -74,7 +74,7 @@ impl DiskStateCache { /// Loads the snapshot file for the given hash /// /// Returns None if it doesn't exist or deserialization failed - pub fn read(&mut self, hash: H256) -> Option { + pub fn read(&mut self, hash: B256) -> Option { self.with_cache_file(hash, |file| { match foundry_common::fs::read_json_file::(&file) { Ok(state) => { @@ -91,7 +91,7 @@ impl DiskStateCache { } /// Removes the cache file for the given hash, if it exists - pub fn remove(&mut self, hash: H256) { + pub fn remove(&mut self, hash: B256) { self.with_cache_file(hash, |file| { foundry_common::fs::remove_file(file).map_err(|err| { error!(target: "backend", %err, %hash, "Failed to remove state snapshot"); diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 41cf8cdaf792..c9cc10a22efc 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -4,10 +4,9 @@ use crate::{ StateDb, }, revm::primitives::AccountInfo, - Address, U256, }; -use ethers::{prelude::H256, types::BlockId}; -use foundry_common::types::{ToAlloy, ToEthers}; +use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{DatabaseResult, RevertSnapshotAction, StateSnapshot}, fork::{database::ForkDbSnapshot, BlockchainDb}, @@ -24,12 +23,12 @@ impl Db for ForkedDatabase { fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { // this ensures the account is loaded first - let _ = Database::basic(self, address.to_alloy())?; + let _ = Database::basic(self, address)?; self.database_mut().set_storage_at(address, slot, val) } - fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.inner().block_hashes().write().insert(number.to_alloy(), hash.to_alloy()); + fn insert_block_hash(&mut self, number: U256, hash: B256) { + self.inner().block_hashes().write().insert(number, hash); } fn dump_state(&self) -> DatabaseResult> { @@ -47,16 +46,12 @@ impl Db for ForkedDatabase { } .to_checked(); Ok(( - k.to_ethers(), + k, SerializableAccountRecord { nonce: v.info.nonce, - balance: v.info.balance.to_ethers(), - code: code.bytes()[..code.len()].to_vec().into(), - storage: v - .storage - .into_iter() - .map(|kv| (kv.0.to_ethers(), kv.1.to_ethers())) - .collect(), + balance: v.info.balance, + code: code.original_bytes(), + storage: v.storage.into_iter().collect(), }, )) }) @@ -65,11 +60,11 @@ impl Db for ForkedDatabase { } fn snapshot(&mut self) -> U256 { - self.insert_snapshot().to_ethers() + self.insert_snapshot() } fn revert(&mut self, id: U256, action: RevertSnapshotAction) -> bool { - self.revert_snapshot(id.to_alloy(), action) + self.revert_snapshot(id, action) } fn current_state(&self) -> StateDb { diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index acf929e0c658..66b8c8941ceb 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -7,10 +7,9 @@ use crate::{ }, mem::state::{state_merkle_trie_root, storage_trie_db, trie_hash_db}, revm::primitives::AccountInfo, - Address, U256, }; -use ethers::{prelude::H256, types::BlockId}; -use foundry_common::types::{ToAlloy, ToEthers}; +use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{DatabaseResult, StateSnapshot}, fork::BlockchainDb, @@ -22,15 +21,15 @@ pub use foundry_evm::{backend::MemDb, revm::db::DatabaseRef}; impl Db for MemDb { fn insert_account(&mut self, address: Address, account: AccountInfo) { - self.inner.insert_account_info(address.to_alloy(), account) + self.inner.insert_account_info(address, account) } fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.inner.insert_account_storage(address.to_alloy(), slot.to_alloy(), val.to_alloy()) + self.inner.insert_account_storage(address, slot, val) } - fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.inner.block_hashes.insert(number.to_alloy(), hash.to_alloy()); + fn insert_block_hash(&mut self, number: U256, hash: B256) { + self.inner.block_hashes.insert(number, hash); } fn dump_state(&self) -> DatabaseResult> { @@ -47,16 +46,12 @@ impl Db for MemDb { } .to_checked(); Ok(( - k.to_ethers(), + k, SerializableAccountRecord { nonce: v.info.nonce, - balance: v.info.balance.to_ethers(), - code: code.bytes()[..code.len()].to_vec().into(), - storage: v - .storage - .into_iter() - .map(|k| (k.0.to_ethers(), k.1.to_ethers())) - .collect(), + balance: v.info.balance, + code: code.original_bytes(), + storage: v.storage.into_iter().collect(), }, )) }) @@ -69,13 +64,13 @@ impl Db for MemDb { fn snapshot(&mut self) -> U256 { let id = self.snapshots.insert(self.inner.clone()); trace!(target: "backend::memdb", "Created new snapshot {}", id); - id.to_ethers() + id } fn revert(&mut self, id: U256, action: RevertSnapshotAction) -> bool { - if let Some(snapshot) = self.snapshots.remove(id.to_alloy()) { + if let Some(snapshot) = self.snapshots.remove(id) { if action.is_keep() { - self.snapshots.insert_at(snapshot.clone(), id.to_alloy()); + self.snapshots.insert_at(snapshot.clone(), id); } self.inner = snapshot; trace!(target: "backend::memdb", "Reverted snapshot {}", id); @@ -86,7 +81,7 @@ impl Db for MemDb { } } - fn maybe_state_root(&self) -> Option { + fn maybe_state_root(&self) -> Option { Some(state_merkle_trie_root(&self.inner.accounts)) } @@ -96,12 +91,12 @@ impl Db for MemDb { } impl MaybeHashDatabase for MemDb { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, H256)> { + fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { Some(trie_hash_db(&self.inner.accounts)) } - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, H256)> { - if let Some(acc) = self.inner.accounts.get(&addr.to_alloy()) { + fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { + if let Some(acc) = self.inner.accounts.get(&addr) { Some(storage_trie_db(&acc.storage)) } else { Some(storage_trie_db(&Default::default())) @@ -141,11 +136,8 @@ mod tests { use crate::{ eth::backend::db::{Db, SerializableAccountRecord, SerializableState}, revm::primitives::AccountInfo, - Address, }; - use alloy_primitives::{Bytes, U256 as rU256}; - use ethers::types::U256; - use foundry_common::types::ToAlloy; + use alloy_primitives::{Address, Bytes, U256}; use foundry_evm::{ backend::MemDb, revm::primitives::{Bytecode, KECCAK_EMPTY}, @@ -166,7 +158,7 @@ mod tests { dump_db.insert_account( test_addr, AccountInfo { - balance: rU256::from(123456), + balance: U256::from(123456), code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, @@ -181,15 +173,12 @@ mod tests { load_db.load_state(state).unwrap(); - let loaded_account = load_db.basic_ref(test_addr.to_alloy()).unwrap().unwrap(); + let loaded_account = load_db.basic_ref(test_addr).unwrap().unwrap(); - assert_eq!(loaded_account.balance, rU256::from(123456)); + assert_eq!(loaded_account.balance, U256::from(123456)); assert_eq!(load_db.code_by_hash_ref(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!( - load_db.storage_ref(test_addr.to_alloy(), rU256::from(1234567)).unwrap(), - rU256::from(1) - ); + assert_eq!(load_db.storage_ref(test_addr, U256::from(1234567)).unwrap(), U256::from(1)); } // verifies that multiple accounts can be loaded at a time, and storage is merged within those @@ -208,7 +197,7 @@ mod tests { db.insert_account( test_addr, AccountInfo { - balance: rU256::from(123456), + balance: U256::from(123456), code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, @@ -236,7 +225,7 @@ mod tests { new_state.accounts.insert( test_addr, SerializableAccountRecord { - balance: 100100.into(), + balance: U256::from(100100), code: contract_code.bytes()[..contract_code.len()].to_vec().into(), nonce: 100, storage: new_storage, @@ -245,21 +234,15 @@ mod tests { db.load_state(new_state).unwrap(); - let loaded_account = db.basic_ref(test_addr.to_alloy()).unwrap().unwrap(); - let loaded_account2 = db.basic_ref(test_addr2.to_alloy()).unwrap().unwrap(); + let loaded_account = db.basic_ref(test_addr).unwrap().unwrap(); + let loaded_account2 = db.basic_ref(test_addr2).unwrap().unwrap(); assert_eq!(loaded_account2.nonce, 1); - assert_eq!(loaded_account.balance, rU256::from(100100)); + assert_eq!(loaded_account.balance, U256::from(100100)); assert_eq!(db.code_by_hash_ref(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!( - db.storage_ref(test_addr.to_alloy(), rU256::from(1234567)).unwrap(), - rU256::from(1) - ); - assert_eq!( - db.storage_ref(test_addr.to_alloy(), rU256::from(1234568)).unwrap(), - rU256::from(5) - ); + assert_eq!(db.storage_ref(test_addr, U256::from(1234567)).unwrap(), U256::from(1)); + assert_eq!(db.storage_ref(test_addr, U256::from(1234568)).unwrap(), U256::from(5)); } } diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 6c978ac386c5..d1b45bea453a 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -5,19 +5,20 @@ use ethers::types::Log; use foundry_evm::{ call_inspectors, decode::decode_console_logs, - inspectors::{LogCollector, Tracer}, + inspectors::{LogCollector, TracingInspector}, revm, revm::{ interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, primitives::{Address, Bytes, B256}, EVMData, }, + traces::TracingInspectorConfig, }; /// The [`revm::Inspector`] used when transacting in the evm #[derive(Clone, Debug, Default)] pub struct Inspector { - pub tracer: Option, + pub tracer: Option, /// collects all `console.sol` logs pub log_collector: LogCollector, } @@ -34,15 +35,13 @@ impl Inspector { /// Configures the `Tracer` [`revm::Inspector`] pub fn with_tracing(mut self) -> Self { - self.tracer = Some(Default::default()); + self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all())); self } /// Enables steps recording for `Tracer`. - pub fn with_steps_tracing(mut self) -> Self { - let tracer = self.tracer.get_or_insert_with(Default::default); - tracer.record_steps(); - self + pub fn with_steps_tracing(self) -> Self { + self.with_tracing() } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d8c4cdac8ccc..d87a9e8996a8 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -29,15 +29,24 @@ use crate::{ }, NodeConfig, }; +use alloy_primitives::{Address, Bloom, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; +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, Filter, FilteredParams, Header as AlloyHeader, Log, + Transaction, TransactionReceipt, +}; use anvil_core::{ eth::{ block::{Block, BlockInfo, Header}, proof::{AccountProof, BasicAccount, StorageProof}, receipt::{EIP658Receipt, TypedReceipt}, - state::StateOverride, transaction::{ - EthTransactionRequest, MaybeImpersonatedTransaction, PendingTransaction, - TransactionInfo, TypedTransaction, + from_ethers_access_list, EthTransactionRequest, MaybeImpersonatedTransaction, + PendingTransaction, TransactionInfo, TypedTransaction, }, trie::RefTrieDB, utils::to_revm_access_list, @@ -47,12 +56,7 @@ use anvil_core::{ use anvil_rpc::error::RpcError; use ethers::{ abi::ethereum_types::BigEndianHash, - prelude::{BlockNumber, GethTraceFrame, TxHash, H256, U256, U64}, - types::{ - transaction::eip2930::AccessList, Address, Block as EthersBlock, BlockId, Bytes, - DefaultFrame, Filter, FilteredParams, GethDebugTracingOptions, GethTrace, Log, OtherFields, - Trace, Transaction, TransactionReceipt, H160, - }, + types::transaction::eip2930::AccessList as EthersAccessList, utils::{keccak256, rlp}, }; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; @@ -67,14 +71,16 @@ use foundry_evm::{ db::CacheDB, interpreter::InstructionResult, primitives::{ - Account, BlockEnv, CreateScheme, EVMError, Env, ExecutionResult, InvalidHeader, Output, - SpecId, TransactTo, TxEnv, KECCAK_EMPTY, + BlockEnv, CreateScheme, EVMError, Env, ExecutionResult, InvalidHeader, Output, SpecId, + TransactTo, TxEnv, KECCAK_EMPTY, }, }, + traces::{TracingInspector, TracingInspectorConfig}, utils::{eval_to_instruction_result, halt_to_instruction_result, u256_to_h256_be}, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use hash_db::HashDB; +use itertools::Itertools; use parking_lot::{Mutex, RwLock}; use std::{ collections::{BTreeMap, HashMap}, @@ -95,25 +101,24 @@ pub mod state; pub mod storage; // Gas per transaction not creating a contract. -pub const MIN_TRANSACTION_GAS: U256 = U256([21_000, 0, 0, 0]); +pub const MIN_TRANSACTION_GAS: U256 = U256::from_limbs([21_000, 0, 0, 0]); // Gas per transaction creating a contract. -pub const MIN_CREATE_GAS: U256 = U256([53_000, 0, 0, 0]); +pub const MIN_CREATE_GAS: U256 = U256::from_limbs([53_000, 0, 0, 0]); -// TODO: This is the same as foundry_evm::utils::StateChangeset but with ethers H160 -pub type State = foundry_evm::hashbrown::HashMap; +pub type State = foundry_evm::utils::StateChangeset; /// A block request, which includes the Pool Transactions if it's Pending #[derive(Debug)] pub enum BlockRequest { Pending(Vec>), - Number(U64), + Number(u64), } impl BlockRequest { pub fn block_number(&self) -> BlockNumber { - match self { + match *self { BlockRequest::Pending(_) => BlockNumber::Pending, - BlockRequest::Number(n) => BlockNumber::Number(*n), + BlockRequest::Number(n) => BlockNumber::Number(n), } } } @@ -159,7 +164,7 @@ pub struct Backend { /// listeners for new blocks that get notified when a new block was imported new_block_listeners: Arc>>>, /// keeps track of active snapshots at a specific block - active_snapshots: Arc>>, + active_snapshots: Arc>>, enable_steps_tracing: bool, /// How to keep history state prune_state_history_config: PruneStateHistoryConfig, @@ -267,7 +272,7 @@ impl Backend { // accounts concurrently by spawning the job to a new task genesis_accounts_futures.push(tokio::task::spawn(async move { let db = db.read().await; - let info = db.basic_ref(address.to_alloy())?.unwrap_or_default(); + let info = db.basic_ref(address)?.unwrap_or_default(); Ok::<_, DatabaseError>((address, info)) })); } @@ -307,20 +312,20 @@ impl Backend { /// /// Returns `true` if the account is already impersonated pub async fn impersonate(&self, addr: Address) -> DatabaseResult { - if self.cheats.impersonated_accounts().contains(&addr) { - return Ok(true) + if self.cheats.impersonated_accounts().contains(&addr.to_ethers()) { + return Ok(true); } // Ensure EIP-3607 is disabled let mut env = self.env.write(); env.cfg.disable_eip3607 = true; - Ok(self.cheats.impersonate(addr)) + Ok(self.cheats.impersonate(addr.to_ethers())) } /// Removes the account that from the impersonated set /// /// If the impersonated `addr` is a contract then we also reset the code here pub async fn stop_impersonating(&self, addr: Address) -> DatabaseResult<()> { - self.cheats.stop_impersonating(&addr); + self.cheats.stop_impersonating(&addr.to_ethers()); Ok(()) } @@ -341,7 +346,7 @@ impl Backend { /// Returns the `AccountInfo` from the database pub async fn get_account(&self, address: Address) -> DatabaseResult { - Ok(self.db.read().await.basic_ref(address.to_alloy())?.unwrap_or_default()) + Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default()) } /// Whether we're forked off some remote client @@ -351,6 +356,9 @@ 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 @@ -379,7 +387,7 @@ impl Backend { return Err(RpcError::invalid_params( "Forking not enabled and RPC URL not provided to start forking", ) - .into()) + .into()); } } @@ -391,7 +399,8 @@ impl Backend { let fork_block_number = fork.block_number(); let fork_block = fork .block_by_number(fork_block_number) - .await? + .await + .map_err(|_| BlockchainError::DataUnavailable)? .ok_or(BlockchainError::BlockNotFound)?; // update all settings related to the forked block { @@ -400,11 +409,10 @@ impl Backend { env.block = BlockEnv { number: rU256::from(fork_block_number), - timestamp: fork_block.timestamp.to_alloy(), - gas_limit: fork_block.gas_limit.to_alloy(), - difficulty: fork_block.difficulty.to_alloy(), - // ensures prevrandao is set - prevrandao: Some(fork_block.mix_hash.map(|h| h.to_alloy()).unwrap_or_default()), + timestamp: fork_block.header.timestamp, + gas_limit: fork_block.header.gas_limit, + difficulty: fork_block.header.difficulty, + prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -416,12 +424,12 @@ impl Backend { // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - fork_block.gas_used, - fork_block.gas_limit, - fork_block.base_fee_per_gas.unwrap_or_default(), + fork_block.header.gas_used, + fork_block.header.gas_limit, + fork_block.header.base_fee_per_gas.unwrap_or_default(), ); - self.fees.set_base_fee(next_block_base_fee.into()); + self.fees.set_base_fee(U256::from(next_block_base_fee)); // also reset the total difficulty self.blockchain.storage.write().total_difficulty = fork.total_difficulty(); @@ -479,29 +487,29 @@ impl Backend { } /// Returns the current best hash of the chain - pub fn best_hash(&self) -> H256 { + pub fn best_hash(&self) -> B256 { self.blockchain.storage.read().best_hash } /// Returns the current best number of the chain - pub fn best_number(&self) -> U64 { - self.env.read().block.number.saturating_to::().into() + pub fn best_number(&self) -> u64 { + self.env.read().block.number.try_into().unwrap_or(u64::MAX) } /// Sets the block number pub fn set_block_number(&self, number: U256) { let mut env = self.env.write(); - env.block.number = number.to_alloy(); + env.block.number = number; } /// Returns the client coinbase address. pub fn coinbase(&self) -> Address { - self.env.read().block.coinbase.to_ethers() + self.env.read().block.coinbase } /// Returns the client coinbase address. - pub fn chain_id(&self) -> u64 { - self.env.read().cfg.chain_id + pub fn chain_id(&self) -> U256 { + U256::from(self.env.read().cfg.chain_id) } pub fn set_chain_id(&self, chain_id: u64) { @@ -510,17 +518,17 @@ impl Backend { /// Returns balance of the given account. pub async fn current_balance(&self, address: Address) -> DatabaseResult { - Ok(self.get_account(address).await?.balance.to_ethers()) + Ok(self.get_account(address).await?.balance) } /// Returns balance of the given account. pub async fn current_nonce(&self, address: Address) -> DatabaseResult { - Ok(self.get_account(address).await?.nonce.into()) + Ok(U256::from(self.get_account(address).await?.nonce)) } /// Sets the coinbase address pub fn set_coinbase(&self, address: Address) { - self.env.write().block.coinbase = address.to_alloy(); + self.env.write().block.coinbase = address; } /// Sets the nonce of the given address @@ -535,7 +543,7 @@ impl Backend { /// Sets the code of the given address pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> { - self.db.write().await.set_code(address, code) + self.db.write().await.set_code(address, code.0.into()) } /// Sets the value for the given slot of the given address @@ -543,9 +551,9 @@ impl Backend { &self, address: Address, slot: U256, - val: H256, + val: B256, ) -> DatabaseResult<()> { - self.db.write().await.set_storage_at(address, slot, val.into_uint()) + self.db.write().await.set_storage_at(address, slot, val.to_ethers().into_uint().to_alloy()) } /// Returns the configured specid @@ -576,7 +584,7 @@ impl Backend { /// Returns an error if EIP1559 is not active (pre Berlin) pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> { if self.is_eip1559() { - return Ok(()) + return Ok(()); } Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork) } @@ -584,7 +592,7 @@ impl Backend { /// Returns an error if EIP1559 is not active (pre muirGlacier) pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> { if self.is_eip2930() { - return Ok(()) + return Ok(()); } Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork) } @@ -599,12 +607,12 @@ impl Backend { /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { - self.env.read().block.gas_limit.to_ethers() + self.env.read().block.gas_limit } /// Sets the block gas limit pub fn set_gas_limit(&self, gas_limit: U256) { - self.env.write().block.gas_limit = gas_limit.to_alloy(); + self.env.write().block.gas_limit = gas_limit; } /// Returns the current base fee @@ -648,7 +656,7 @@ impl Backend { /// /// Returns the id of the snapshot created pub async fn create_snapshot(&self) -> U256 { - let num = self.best_number().as_u64(); + let num = self.best_number(); let hash = self.best_hash(); let id = self.db.write().await.snapshot(); trace!(target: "backend", "creating snapshot {} at {}", id, num); @@ -662,39 +670,39 @@ impl Backend { if let Some((num, hash)) = block { let best_block_hash = { // revert the storage that's newer than the snapshot - let current_height = self.best_number().as_u64(); + let current_height = self.best_number(); let mut storage = self.blockchain.storage.write(); for n in ((num + 1)..=current_height).rev() { trace!(target: "backend", "reverting block {}", n); - let n: U64 = n.into(); + let n = U64::from(n); 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()); + let _ = storage.transactions.remove(&tx.hash().to_alloy()); } } } } - storage.best_number = num.into(); + storage.best_number = U64::from(num); storage.best_hash = hash; hash }; let block = self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?; - let reset_time = block.timestamp.as_u64(); + let reset_time = block.header.timestamp.to::(); self.time.reset(reset_time); let mut env = self.env.write(); env.block = BlockEnv { number: rU256::from(num), - timestamp: block.timestamp.to_alloy(), - difficulty: block.difficulty.to_alloy(), + timestamp: block.header.timestamp, + difficulty: block.header.difficulty, // ensures prevrandao is set - prevrandao: Some(block.mix_hash.unwrap_or_default()).map(|h| h.to_alloy()), - gas_limit: block.gas_limit.to_alloy(), + prevrandao: Some(block.header.mix_hash.unwrap_or_default()), + gas_limit: block.header.gas_limit, // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -704,7 +712,7 @@ impl Backend { Ok(self.db.write().await.revert(id, RevertSnapshotAction::RevertRemove)) } - pub fn list_snapshots(&self) -> BTreeMap { + pub fn list_snapshots(&self) -> BTreeMap { self.active_snapshots.lock().clone().into_iter().collect() } @@ -758,7 +766,7 @@ impl Backend { let mut env = self.env.read().clone(); // increase block number for this block env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = self.base_fee().to_alloy(); + env.block.basefee = self.base_fee(); env.block.timestamp = rU256::from(self.time.current_call_timestamp()); env } @@ -784,8 +792,6 @@ impl Backend { Err(e) => return Err(e.into()), }; let state = result_and_state.state; - let state: revm::primitives::HashMap = - state.into_iter().map(|kv| (kv.0.to_ethers(), kv.1)).collect(); let (exit_reason, gas_used, out, logs) = match result_and_state.result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { (eval_to_instruction_result(reason), gas_used, Some(output), Some(logs)) @@ -834,8 +840,8 @@ impl Backend { pending: pool_transactions.into_iter(), block_env: env.block.clone(), cfg_env: env.cfg, - parent_hash: storage.best_hash, - gas_used: U256::zero(), + parent_hash: storage.best_hash.to_ethers(), + gas_used: U256::ZERO.to_ethers(), enable_steps_tracing: self.enable_steps_tracing, }; @@ -874,7 +880,7 @@ impl Backend { // increase block number for this block env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = current_base_fee.to_alloy(); + env.block.basefee = current_base_fee; env.block.timestamp = rU256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -893,15 +899,18 @@ impl Backend { pending: pool_transactions.into_iter(), block_env: env.block.clone(), cfg_env: env.cfg.clone(), - parent_hash: best_hash, - gas_used: U256::zero(), + parent_hash: best_hash.to_ethers(), + gas_used: U256::ZERO.to_ethers(), 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, block_hash); + db.insert_block_hash( + executed_tx.block.block.header.number.to_alloy(), + block_hash.to_alloy(), + ); (executed_tx, block_hash) }; @@ -911,7 +920,7 @@ impl Backend { let BlockInfo { block, transactions, receipts } = block; let header = block.header.clone(); - let block_number: U64 = (env.block.number.to_ethers()).as_u64().into(); + let block_number: U64 = env.block.number.to::(); trace!( target: "backend", @@ -924,16 +933,16 @@ impl Backend { let mut storage = self.blockchain.storage.write(); // update block metadata storage.best_number = block_number; - storage.best_hash = block_hash; + storage.best_hash = block_hash.to_alloy(); // 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); + storage.total_difficulty.saturating_add(header.difficulty.to_alloy()); } - storage.blocks.insert(block_hash, block); - storage.hashes.insert(block_number, block_hash); + storage.blocks.insert(block_hash.to_alloy(), block); + storage.hashes.insert(block_number, block_hash.to_alloy()); node_info!(""); // insert all transactions @@ -957,17 +966,17 @@ impl Backend { let mined_tx = MinedTransaction { info, receipt, - block_hash, - block_number: block_number.as_u64(), + block_hash: block_hash.to_alloy(), + block_number: block_number.to::(), }; - storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx); + storage.transactions.insert(mined_tx.info.transaction_hash.to_alloy(), mined_tx); } // remove old transactions that exceed the transaction block keeper if let Some(transaction_block_keeper) = self.transaction_block_keeper { if storage.blocks.len() > transaction_block_keeper { let to_clear = block_number - .as_u64() + .to::() .saturating_sub(transaction_block_keeper.try_into().unwrap()); storage.remove_block_transactions_by_number(to_clear) } @@ -990,16 +999,16 @@ impl Backend { (outcome, header, block_hash) }; let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - header.gas_used, - header.gas_limit, - header.base_fee_per_gas.unwrap_or_default(), + header.gas_used.to_alloy(), + header.gas_limit.to_alloy(), + header.base_fee_per_gas.unwrap_or_default().to_alloy(), ); // notify all listeners - self.notify_on_new_block(header, block_hash); + self.notify_on_new_block(header, block_hash.to_alloy()); // update next base fee - self.fees.set_base_fee(next_block_base_fee.into()); + self.fees.set_base_fee(U256::from(next_block_base_fee)); outcome } @@ -1021,7 +1030,7 @@ impl Backend { let (exit, out, gas, state) = match overrides { None => self.call_with_state(state, request, fee_details, block), Some(overrides) => { - let state = state::apply_state_override(overrides, state)?; + let state = state::apply_state_override(overrides.into_iter().collect(), state)?; self.call_with_state(state, request, fee_details, block) }, }?; @@ -1048,17 +1057,18 @@ impl Backend { env.cfg.disable_block_gas_limit = true; if let Some(base) = max_fee_per_gas { - env.block.basefee = base.to_alloy(); + env.block.basefee = base; } - let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); + let gas_price = + gas_price.map(|g| g).or(max_fee_per_gas.map(|g| g)).unwrap_or_else(|| self.gas_price()); let caller = from.unwrap_or_default(); env.tx = TxEnv { caller: caller.to_alloy(), gas_limit: gas_limit.as_u64(), - gas_price: gas_price.to_alloy(), - gas_priority_fee: max_priority_fee_per_gas.map(|f| f.to_alloy()), + gas_price, + gas_priority_fee: max_priority_fee_per_gas.map(|f| f), transact_to: match to { Some(addr) => TransactTo::Call(addr.to_alloy()), None => TransactTo::Create(CreateScheme::Create), @@ -1112,8 +1122,6 @@ impl Backend { }, }; let state = result_and_state.state; - let state: revm::primitives::HashMap = - state.into_iter().map(|kv| (kv.0.to_ethers(), kv.1)).collect(); let (exit_reason, gas_used, out) = match result_and_state.result { ExecutionResult::Success { reason, gas_used, output, .. } => { (eval_to_instruction_result(reason), gas_used, Some(output)) @@ -1134,7 +1142,7 @@ impl Backend { request: EthTransactionRequest, fee_details: FeeDetails, block_request: Option, - opts: GethDebugTracingOptions, + opts: GethDefaultTracingOptions, ) -> Result { self.with_database_at(block_request, |state, block| { let mut inspector = Inspector::default().with_steps_tracing(); @@ -1158,7 +1166,10 @@ impl Backend { (halt_to_instruction_result(reason), gas_used, None) }, }; - let res = inspector.tracer.unwrap_or_default().traces.geth_trace(rU256::from(gas_used), opts); + let res = inspector.tracer.unwrap_or(TracingInspector::new(TracingInspectorConfig::all())).into_geth_builder().geth_traces(gas_used, match &out { + Some(out) => out.data().clone(), + None => Bytes::new() + }, opts); trace!(target: "backend", "trace call return {:?} out: {:?} gas {} on block {}", exit_reason, out, gas_used, block_number); Ok(res) }) @@ -1184,10 +1195,10 @@ impl Backend { }; let mut tracer = AccessListTracer::new( - AccessList(request.access_list.clone().unwrap_or_default()), + EthersAccessList(request.access_list.clone().unwrap_or_default()), from, to, - self.precompiles().into_iter().map(|p| p.to_alloy()).collect(), + self.precompiles(), ); let mut evm = revm::EVM::new(); @@ -1209,7 +1220,7 @@ impl Backend { } }; let access_list = tracer.access_list(); - Ok((exit_reason, out, gas_used, access_list)) + Ok((exit_reason, out, gas_used, from_ethers_access_list(access_list))) } /// returns all receipts for the given transactions @@ -1230,14 +1241,14 @@ impl Backend { async fn logs_for_block( &self, filter: Filter, - hash: H256, + hash: B256, ) -> Result, BlockchainError> { if let Some(block) = self.blockchain.get_block_by_hash(&hash) { - return Ok(self.mined_logs_for_block(filter, block)) + return Ok(self.mined_logs_for_block(filter, block)); } if let Some(fork) = self.get_fork() { - return Ok(fork.logs(&filter).await?) + return fork.logs(&filter).await.map_err(|_| BlockchainError::DataUnavailable); } Ok(Vec::new()) @@ -1255,7 +1266,9 @@ impl Backend { block .transactions .iter() - .filter_map(|tx| storage.transactions.get(&tx.hash()).map(|tx| tx.info.clone())) + .filter_map(|tx| { + storage.transactions.get(&tx.hash().to_alloy()).map(|tx| tx.info.clone()) + }) .collect() }; @@ -1263,26 +1276,24 @@ impl Backend { let logs = transaction.logs.clone(); let transaction_hash = transaction.transaction_hash; - for (log_idx, log) in logs.into_iter().enumerate() { + for log in logs.into_iter() { let mut log = Log { - address: log.address, - topics: log.topics, - data: log.data, + address: log.address.to_alloy(), + topics: log.topics.into_iter().map(ToAlloy::to_alloy).collect(), + data: log.data.0.into(), block_hash: None, block_number: None, transaction_hash: None, transaction_index: None, log_index: None, - transaction_log_index: None, - log_type: None, - removed: Some(false), + removed: false, }; let mut is_match: bool = true; - if filter.address.is_some() && filter.has_topics() { + if !filter.address.is_empty() && filter.has_topics() { if !params.filter_address(&log) || !params.filter_topics(&log) { is_match = false; } - } else if filter.address.is_some() { + } else if !filter.address.is_empty() { if !params.filter_address(&log) { is_match = false; } @@ -1291,12 +1302,11 @@ impl Backend { } if is_match { - log.block_hash = Some(block_hash); - log.block_number = Some(block.header.number.as_u64().into()); - log.transaction_hash = Some(transaction_hash); - log.transaction_index = Some(transaction.transaction_index.into()); + log.block_hash = Some(block_hash.to_alloy()); + log.block_number = Some(block.header.number.to_alloy()); + log.transaction_hash = Some(transaction_hash.to_alloy()); + log.transaction_index = Some(U256::from(transaction.transaction_index)); log.log_index = Some(U256::from(block_log_index)); - log.transaction_log_index = Some(U256::from(log_idx)); all_logs.push(log); } block_log_index += 1; @@ -1327,7 +1337,8 @@ impl Backend { if fork.predates_fork(from) { // this data is only available on the forked client let filter = filter.clone().from_block(from).to_block(to_on_fork); - all_logs = fork.logs(&filter).await?; + all_logs = + fork.logs(&filter).await.map_err(|_| BlockchainError::DataUnavailable)?; // update the range from = fork.block_number() + 1; @@ -1349,31 +1360,28 @@ impl Backend { if let Some(hash) = filter.get_block_hash() { self.logs_for_block(filter, hash).await } else { - let best = self.best_number().as_u64(); + let best = self.best_number(); let to_block = self.convert_block_number(filter.block_option.get_to_block().copied()).min(best); let from_block = self.convert_block_number(filter.block_option.get_from_block().copied()); if from_block > best { // requested log range does not exist yet - return Ok(vec![]) + return Ok(vec![]); } self.logs_for_range(&filter, from_block, to_block).await } } - pub async fn block_by_hash( - &self, - hash: H256, - ) -> Result>, BlockchainError> { + pub async fn block_by_hash(&self, hash: B256) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.mined_block_by_hash(hash) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { - return Ok(fork.block_by_hash(hash).await?) + return fork.block_by_hash(hash).await.map_err(|_| BlockchainError::DataUnavailable); } Ok(None) @@ -1381,21 +1389,24 @@ impl Backend { pub async fn block_by_hash_full( &self, - hash: H256, - ) -> Result>, BlockchainError> { + hash: B256, + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by hash {:?}", hash); if let tx @ Some(_) = self.get_full_block(hash) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { - return Ok(fork.block_by_hash_full(hash).await?) + return fork + .block_by_hash_full(hash) + .await + .map_err(|_| BlockchainError::DataUnavailable); } Ok(None) } - fn mined_block_by_hash(&self, hash: H256) -> Option> { + fn mined_block_by_hash(&self, hash: B256) -> Option { let block = self.blockchain.get_block_by_hash(&hash)?; Some(self.convert_block(block)) } @@ -1405,7 +1416,7 @@ impl Backend { number: BlockNumber, ) -> Option> { if let Some(block) = self.get_block(number) { - return self.mined_transactions_in_block(&block) + return self.mined_transactions_in_block(&block); } None } @@ -1416,10 +1427,16 @@ 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)?.info.clone(); + let info = storage.transactions.get(&hash.to_alloy())?.info.clone(); let tx = block.transactions.get(info.transaction_index as usize)?.clone(); - let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee); + let tx = transaction_build( + Some(hash.to_alloy()), + tx, + Some(block), + Some(info), + base_fee.map(|f| f.to_alloy()), + ); transactions.push(tx); } Some(transactions) @@ -1428,16 +1445,19 @@ impl Backend { pub async fn block_by_number( &self, number: BlockNumber, - ) -> Result>, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.mined_block_by_number(number) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { let number = self.convert_block_number(Some(number)); if fork.predates_fork_inclusive(number) { - return Ok(fork.block_by_number(number).await?) + return fork + .block_by_number(number) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } @@ -1447,16 +1467,19 @@ impl Backend { pub async fn block_by_number_full( &self, number: BlockNumber, - ) -> Result>, BlockchainError> { + ) -> Result, BlockchainError> { trace!(target: "backend", "get block by number {:?}", number); if let tx @ Some(_) = self.get_full_block(number) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { let number = self.convert_block_number(Some(number)); if fork.predates_fork_inclusive(number) { - return Ok(fork.block_by_number_full(number).await?) + return fork + .block_by_number_full(number) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } @@ -1465,7 +1488,7 @@ impl Backend { pub fn get_block(&self, id: impl Into) -> Option { let hash = match id.into() { - BlockId::Hash(hash) => hash, + BlockId::Hash(hash) => hash.block_hash, BlockId::Number(number) => { let storage = self.blockchain.storage.read(); let slots_in_an_epoch = U64::from(32u64); @@ -1473,7 +1496,7 @@ impl Backend { BlockNumber::Latest => storage.best_hash, BlockNumber::Earliest => storage.genesis_hash, BlockNumber::Pending => return None, - BlockNumber::Number(num) => *storage.hashes.get(&num)?, + BlockNumber::Number(num) => *storage.hashes.get(&U64::from(num))?, BlockNumber::Safe => { if storage.best_number > (slots_in_an_epoch) { *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch)))? @@ -1482,8 +1505,12 @@ impl Backend { } } BlockNumber::Finalized => { - if storage.best_number > (slots_in_an_epoch * 2) { - *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch * 2)))? + 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(), + )? } else { storage.genesis_hash } @@ -1494,15 +1521,18 @@ impl Backend { self.get_block_by_hash(hash) } - pub fn get_block_by_hash(&self, hash: H256) -> Option { + pub fn get_block_by_hash(&self, hash: B256) -> Option { self.blockchain.get_block_by_hash(&hash) } - pub fn mined_block_by_number(&self, number: BlockNumber) -> Option> { - Some(self.convert_block(self.get_block(number)?)) + pub fn mined_block_by_number(&self, number: BlockNumber) -> Option { + let block = self.get_block(number)?; + let mut block = self.convert_block(block); + block.transactions.convert_to_hashes(); + Some(block) } - pub fn get_full_block(&self, id: impl Into) -> Option> { + pub fn get_full_block(&self, id: impl Into) -> Option { let block = self.get_block(id)?; let transactions = self.mined_transactions_in_block(&block)?; let block = self.convert_block(block); @@ -1510,7 +1540,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) -> EthersBlock { + pub fn convert_block(&self, block: Block) -> AlloyBlock { let size = U256::from(rlp::encode(&block).len() as u32); let Block { header, transactions, .. } = block; @@ -1535,31 +1565,38 @@ impl Backend { base_fee_per_gas, } = header; - EthersBlock { - hash: Some(hash), - parent_hash, - uncles_hash: ommers_hash, - author: Some(beneficiary), - state_root, - transactions_root, - receipts_root, - number: Some(number.as_u64().into()), - gas_used, - gas_limit, - extra_data, - logs_bloom: Some(logs_bloom), - timestamp: timestamp.into(), - difficulty, + AlloyBlock { total_difficulty: Some(self.total_difficulty()), - seal_fields: { vec![mix_hash.as_bytes().to_vec().into(), nonce.0.to_vec().into()] }, - uncles: vec![], - transactions: transactions.into_iter().map(|tx| tx.hash()).collect(), + 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(), + 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()), + timestamp: U256::from(timestamp), + difficulty: difficulty.to_alloy(), + mix_hash: Some(mix_hash.to_alloy()), + nonce: Some(B64::from_slice(nonce.as_bytes())), + base_fee_per_gas: base_fee_per_gas.map(|f| f.to_alloy()), + withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, + parent_beacon_block_root: None, + }, size: Some(size), - mix_hash: Some(mix_hash), - nonce: Some(nonce), - base_fee_per_gas, + transactions: alloy_rpc_types::BlockTransactions::Hashes( + transactions.into_iter().map(|tx| tx.hash().to_alloy()).collect(), + ), + uncles: vec![], + withdrawals: None, other: Default::default(), - ..Default::default() } } @@ -1572,23 +1609,28 @@ impl Backend { &self, block_id: Option, ) -> Result { - let current = self.best_number().as_u64(); + let current = self.best_number(); let slots_in_an_epoch = 32u64; let requested = match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) { BlockId::Hash(hash) => self - .block_by_hash(hash) + .block_by_hash(hash.block_hash) .await? .ok_or(BlockchainError::BlockNotFound)? + .header .number .ok_or(BlockchainError::BlockNotFound)? - .as_u64(), + .to::(), BlockId::Number(num) => match num { - BlockNumber::Latest | BlockNumber::Pending => self.best_number().as_u64(), - BlockNumber::Earliest => 0, - BlockNumber::Number(num) => num.as_u64(), - BlockNumber::Safe => current.saturating_sub(slots_in_an_epoch), - BlockNumber::Finalized => current.saturating_sub(slots_in_an_epoch * 2), + BlockNumber::Latest | BlockNumber::Pending => self.best_number(), + BlockNumber::Earliest => U64::ZERO.to::(), + BlockNumber::Number(num) => num, + BlockNumber::Safe => { + U64::from(current).saturating_sub(U64::from(slots_in_an_epoch)).to::() + } + BlockNumber::Finalized => U64::from(current) + .saturating_sub(U64::from(slots_in_an_epoch) * U64::from(2)) + .to::(), }, }; @@ -1600,12 +1642,12 @@ impl Backend { } pub fn convert_block_number(&self, block: Option) -> u64 { - let current = self.best_number().as_u64(); + let current = self.best_number(); let slots_in_an_epoch = 32u64; match block.unwrap_or(BlockNumber::Latest) { BlockNumber::Latest | BlockNumber::Pending => current, BlockNumber::Earliest => 0, - BlockNumber::Number(num) => num.as_u64(), + BlockNumber::Number(num) => num, BlockNumber::Safe => current.saturating_sub(slots_in_an_epoch), BlockNumber::Finalized => current.saturating_sub(slots_in_an_epoch * 2), } @@ -1638,20 +1680,20 @@ impl Backend { f(state, block) }) .await; - return Ok(result) + return Ok(result); } Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)), None => None, }; - let block_number: U256 = self.convert_block_number(block_number).into(); + let block_number: U256 = U256::from(self.convert_block_number(block_number)); - if block_number.to_alloy() < self.env.read().block.number { + if block_number < self.env.read().block.number { { let mut states = self.states.write(); if let Some((state, block)) = self - .get_block(block_number.as_u64()) - .and_then(|block| Some((states.get(&block.header.hash())?, block))) + .get_block(block_number.to::()) + .and_then(|block| Some((states.get(&block.header.hash().to_alloy())?, block))) { let block = BlockEnv { number: block.header.number.to_alloy(), @@ -1663,7 +1705,7 @@ impl Backend { gas_limit: block.header.gas_limit.to_alloy(), ..Default::default() }; - return Ok(f(Box::new(state), block)) + return Ok(f(Box::new(state), block)); } } @@ -1678,19 +1720,19 @@ impl Backend { let db = self.db.read().await; let gen_db = self.genesis.state_db_at_genesis(Box::new(&*db)); - block.number = block_number.to_alloy(); + block.number = block_number; block.timestamp = rU256::from(fork.timestamp()); - block.basefee = fork.base_fee().unwrap_or_default().to_alloy(); + block.basefee = fork.base_fee().unwrap_or_default(); - return Ok(f(Box::new(&gen_db), block)) + return Ok(f(Box::new(&gen_db), block)); } } warn!(target: "backend", "Not historic state found for block={}", block_number); return Err(BlockchainError::BlockOutOfRange( self.env.read().block.number.to_ethers().as_u64(), - block_number.as_u64(), - )) + block_number.to::(), + )); } let db = self.db.read().await; @@ -1703,11 +1745,11 @@ impl Backend { address: Address, index: U256, block_request: Option, - ) -> Result { + ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get storage for {:?} at {:?}", address, index); - let val = db.storage_ref(address.to_alloy(), index.to_alloy())?; - Ok(u256_to_h256_be(val.to_ethers())) + let val = db.storage_ref(address, index)?; + Ok(u256_to_h256_be(val.to_ethers()).to_alloy()) }) .await? } @@ -1733,10 +1775,10 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get code for {:?}", address); - let account = state.basic_ref(address.to_alloy())?.unwrap_or_default(); + let account = state.basic_ref(address)?.unwrap_or_default(); if account.code_hash == KECCAK_EMPTY { // if the code hash is `KECCAK_EMPTY`, we check no further - return Ok(Default::default()) + return Ok(Default::default()); } let code = if let Some(code) = account.code { code @@ -1767,7 +1809,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get balance for {:?}", address); - Ok(state.basic_ref(address.to_alloy())?.unwrap_or_default().balance.to_ethers()) + Ok(state.basic_ref(address)?.unwrap_or_default().balance) } /// Returns the nonce of the address @@ -1779,8 +1821,9 @@ 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) { - return Ok(value) + if let Some(value) = get_pool_transactions_nonce(pool_transactions, address.to_ethers()) + { + return Ok(value); } } let final_block_request = match block_request { @@ -1790,15 +1833,18 @@ impl Backend { }; self.with_database_at(final_block_request, |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(db.basic_ref(address.to_alloy())?.unwrap_or_default().nonce.into()) + Ok(U256::from(db.basic_ref(address)?.unwrap_or_default().nonce)) }) .await? } /// Returns the traces for the given transaction - pub async fn trace_transaction(&self, hash: H256) -> Result, BlockchainError> { + pub async fn trace_transaction( + &self, + hash: B256, + ) -> Result, BlockchainError> { if let Some(traces) = self.mined_parity_trace_transaction(hash) { - return Ok(traces) + return Ok(traces); } if let Some(fork) = self.get_fork() { @@ -1809,22 +1855,28 @@ impl Backend { } /// Returns the traces for the given transaction - pub(crate) fn mined_parity_trace_transaction(&self, hash: H256) -> Option> { + pub(crate) fn mined_parity_trace_transaction( + &self, + hash: B256, + ) -> Option> { self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces()) } /// Returns the traces for the given transaction - pub(crate) fn mined_transaction(&self, hash: H256) -> Option { + pub(crate) fn mined_transaction(&self, hash: B256) -> Option { self.blockchain.storage.read().transactions.get(&hash).cloned() } /// Returns the traces for the given block - pub(crate) fn mined_parity_trace_block(&self, block: u64) -> Option> { + pub(crate) fn mined_parity_trace_block( + &self, + block: u64, + ) -> Option> { let block = self.get_block(block)?; let mut traces = vec![]; let storage = self.blockchain.storage.read(); for tx in block.transactions { - traces.extend(storage.transactions.get(&tx.hash())?.parity_traces()); + traces.extend(storage.transactions.get(&tx.hash().to_alloy())?.parity_traces()); } Some(traces) } @@ -1832,38 +1884,44 @@ impl Backend { /// Returns the traces for the given transaction pub async fn debug_trace_transaction( &self, - hash: H256, + hash: B256, opts: GethDebugTracingOptions, ) -> Result { if let Some(traces) = self.mined_geth_trace_transaction(hash, opts.clone()) { - return Ok(GethTrace::Known(GethTraceFrame::Default(traces))) + return Ok(GethTrace::Default(traces)); } if let Some(fork) = self.get_fork() { - return Ok(fork.debug_trace_transaction(hash, opts).await?) + return fork + .debug_trace_transaction(hash, opts) + .await + .map_err(|_| BlockchainError::DataUnavailable) } - Ok(GethTrace::Known(GethTraceFrame::Default(Default::default()))) + Ok(GethTrace::Default(Default::default())) } fn mined_geth_trace_transaction( &self, - hash: H256, + hash: B256, opts: GethDebugTracingOptions, ) -> Option { - self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.geth_trace(opts)) + self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.geth_trace(opts.config)) } /// Returns the traces for the given block - pub async fn trace_block(&self, block: BlockNumber) -> Result, BlockchainError> { + pub async fn trace_block( + &self, + block: BlockNumber, + ) -> Result, BlockchainError> { let number = self.convert_block_number(Some(block)); if let Some(traces) = self.mined_parity_trace_block(number) { - return Ok(traces) + return Ok(traces); } if let Some(fork) = self.get_fork() { if fork.predates_fork(number) { - return Ok(fork.trace_block(number).await?) + return fork.trace_block(number).await.map_err(|_| BlockchainError::DataUnavailable) } } @@ -1872,20 +1930,26 @@ impl Backend { pub async fn transaction_receipt( &self, - hash: H256, + hash: B256, ) -> Result, BlockchainError> { if let Some(receipt) = self.mined_transaction_receipt(hash) { - return Ok(Some(receipt.inner)) + return Ok(Some(receipt.inner)); } if let Some(fork) = self.get_fork() { - let receipt = fork.transaction_receipt(hash).await?; + let receipt = fork + .transaction_receipt(hash) + .await + .map_err(|_| BlockchainError::DataUnavailable)?; let number = self.convert_block_number( - receipt.clone().and_then(|r| r.block_number).map(|n| BlockNumber::from(n.as_u64())), + receipt + .clone() + .and_then(|r| r.block_number) + .map(|n| BlockNumber::from(n.to::())), ); if fork.predates_fork_inclusive(number) { - return Ok(receipt) + return Ok(receipt); } } @@ -1893,19 +1957,19 @@ impl Backend { } /// Returns all receipts of the block - pub fn mined_receipts(&self, hash: H256) -> Option> { + pub fn mined_receipts(&self, hash: B256) -> Option> { let block = self.mined_block_by_hash(hash)?; let mut receipts = Vec::new(); let storage = self.blockchain.storage.read(); - for tx in block.transactions { - let receipt = storage.transactions.get(&tx)?.receipt.clone(); + for tx in block.transactions.hashes() { + let receipt = storage.transactions.get(tx)?.receipt.clone(); receipts.push(receipt); } Some(receipts) } /// Returns the transaction receipt for the given hash - pub(crate) fn mined_transaction_receipt(&self, hash: H256) -> Option { + pub(crate) fn mined_transaction_receipt(&self, hash: B256) -> Option { let MinedTransaction { info, receipt, block_hash, .. } = self.blockchain.get_transaction_by_hash(&hash)?; @@ -1916,11 +1980,11 @@ impl Backend { 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())); + let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash().to_alloy())); - let mut cumulative_gas_used = U256::zero(); + 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()); + cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used().to_alloy()); } // cumulative_gas_used = cumulative_gas_used.saturating_sub(gas_used); @@ -1933,29 +1997,30 @@ impl Backend { let transaction_type = transaction.transaction.r#type(); let effective_gas_price = match transaction.transaction { - TypedTransaction::Legacy(t) => t.gas_price, - TypedTransaction::EIP2930(t) => t.gas_price, + TypedTransaction::Legacy(t) => t.gas_price.to_alloy(), + TypedTransaction::EIP2930(t) => t.gas_price.to_alloy(), 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) - .unwrap_or_else(U256::max_value), + .checked_add(t.max_priority_fee_per_gas.to_alloy()) + .unwrap_or(U256::MAX), TypedTransaction::Deposit(_) => U256::from(0), }; let deposit_nonce = transaction_type.and_then(|x| (x == 0x7E).then_some(info.nonce)); - let inner = TransactionReceipt { - transaction_hash: info.transaction_hash, - transaction_index: info.transaction_index.into(), + let mut inner = TransactionReceipt { + transaction_hash: Some(info.transaction_hash.to_alloy()), + transaction_index: U64::from(info.transaction_index), block_hash: Some(block_hash), - block_number: Some(block.header.number.as_u64().into()), - from: info.from, - to: info.to, + block_number: Some(U256::from(block.header.number.as_u64())), + from: info.from.to_alloy(), + to: info.to.map(|t| t.to_alloy()), cumulative_gas_used, - gas_used: Some(gas_used), - contract_address: info.contract_address, + gas_used: Some(gas_used.to_alloy()), + contract_address: info.contract_address.map(|t| t.to_alloy()), logs: { let mut pre_receipts_log_index = None; if !cumulative_receipts.is_empty() { @@ -1966,36 +2031,36 @@ impl Backend { logs.iter() .enumerate() .map(|(i, log)| Log { - address: log.address, - topics: log.topics.clone(), - data: log.data.clone(), + address: log.address.to_alloy(), + topics: log.topics.clone().into_iter().map(|t| t.to_alloy()).collect(), + data: log.data.clone().0.into(), block_hash: Some(block_hash), - block_number: Some(block.header.number.as_u64().into()), - transaction_hash: Some(info.transaction_hash), - transaction_index: Some(info.transaction_index.into()), + block_number: Some(U256::from(block.header.number.as_u64())), + transaction_hash: Some(info.transaction_hash.to_alloy()), + transaction_index: Some(U256::from(info.transaction_index)), log_index: Some(U256::from( (pre_receipts_log_index.unwrap_or(0)) + i as u32, )), - transaction_log_index: Some(U256::from(i)), - log_type: None, - removed: Some(false), + removed: false, }) .collect() }, - status: Some(status_code.into()), - root: None, - logs_bloom, - transaction_type: transaction_type.map(Into::into), - effective_gas_price: Some(effective_gas_price), - deposit_nonce, - l1_fee: None, - l1_fee_scalar: None, - l1_gas_price: None, - l1_gas_used: None, - other: OtherFields::default(), + status_code: Some(U64::from(status_code)), + state_root: None, + logs_bloom: Bloom::from_slice(logs_bloom.as_bytes()), + transaction_type: transaction_type.map(U8::from).unwrap_or_default(), + effective_gas_price: effective_gas_price.to::(), + blob_gas_price: None, + blob_gas_used: None, + other: Default::default(), }; - Some(MinedTransactionReceipt { inner, out: info.out }) + inner.other.insert( + "deposit_nonce".to_string(), + serde_json::to_value(deposit_nonce).expect("Infallible"), + ); + + Some(MinedTransactionReceipt { inner, out: info.out.map(|o| o.0.into()) }) } pub async fn transaction_by_block_number_and_index( @@ -2003,14 +2068,17 @@ impl Backend { number: BlockNumber, index: Index, ) -> Result, BlockchainError> { - if let Some(hash) = self.mined_block_by_number(number).and_then(|b| b.hash) { - return Ok(self.mined_transaction_by_block_hash_and_index(hash, index)) + if let Some(hash) = self.mined_block_by_number(number).and_then(|b| b.header.hash) { + return Ok(self.mined_transaction_by_block_hash_and_index(hash, index)); } if let Some(fork) = self.get_fork() { let number = self.convert_block_number(Some(number)); if fork.predates_fork(number) { - return Ok(fork.transaction_by_block_number_and_index(number, index.into()).await?) + return fork + .transaction_by_block_number_and_index(number, index.into()) + .await + .map_err(|_| BlockchainError::DataUnavailable); } } @@ -2019,15 +2087,18 @@ impl Backend { pub async fn transaction_by_block_hash_and_index( &self, - hash: H256, + hash: B256, index: Index, ) -> Result, BlockchainError> { if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { - return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?) + return fork + .transaction_by_block_hash_and_index(hash, index.into()) + .await + .map_err(|_| BlockchainError::DataUnavailable); } Ok(None) @@ -2035,7 +2106,7 @@ impl Backend { fn mined_transaction_by_block_hash_and_index( &self, - block_hash: H256, + block_hash: B256, index: Index, ) -> Option { let (info, block, tx) = { @@ -2043,36 +2114,36 @@ 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())?.info.clone(); + let info = storage.transactions.get(&tx.hash().to_alloy())?.info.clone(); (info, block, tx) }; Some(transaction_build( - Some(info.transaction_hash), + Some(info.transaction_hash.to_alloy()), tx, Some(&block), Some(info), - block.header.base_fee_per_gas, + block.header.base_fee_per_gas.map(|g| g.to_alloy()), )) } pub async fn transaction_by_hash( &self, - hash: H256, + hash: B256, ) -> Result, BlockchainError> { trace!(target: "backend", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.mined_transaction_by_hash(hash) { - return Ok(tx) + return Ok(tx); } if let Some(fork) = self.get_fork() { - return Ok(fork.transaction_by_hash(hash).await?) + return fork.transaction_by_hash(hash).await.map_err(BlockchainError::AlloyForkProvider) } Ok(None) } - fn mined_transaction_by_hash(&self, hash: H256) -> Option { + fn mined_transaction_by_hash(&self, hash: B256) -> Option { let (info, block) = { let storage = self.blockchain.storage.read(); let MinedTransaction { info, block_hash, .. } = @@ -2083,11 +2154,11 @@ impl Backend { let tx = block.transactions.get(info.transaction_index as usize)?.clone(); Some(transaction_build( - Some(info.transaction_hash), + Some(info.transaction_hash.to_alloy()), tx, Some(&block), Some(info), - block.header.base_fee_per_gas, + block.header.base_fee_per_gas.map(|g| g.to_alloy()), )) } @@ -2097,10 +2168,10 @@ impl Backend { pub async fn prove_account_at( &self, address: Address, - keys: Vec, + keys: Vec, block_request: Option, ) -> Result { - let account_key = H256::from(keccak256(address.as_bytes())); + let account_key = B256::from(keccak256(address.to_ethers().as_bytes())); let block_number = block_request.as_ref().map(|r| r.block_number()); self.with_database_at(block_request, |block_db, _| { @@ -2119,7 +2190,7 @@ impl Backend { }) }; let query = (&mut recorder, acc_decoder); - trie.get_with(account_key.as_bytes(), query) + trie.get_with(account_key.to_ethers().as_bytes(), query) .map_err(|err| BlockchainError::TrieError(err.to_string()))? }; let account = maybe_account.unwrap_or_default(); @@ -2140,7 +2211,7 @@ impl Backend { block_db.maybe_account_db(address).ok_or(BlockchainError::DataUnavailable)?; let account_proof = AccountProof { - address, + address: address.to_ethers(), balance: account.balance, nonce: account.nonce.as_u64().into(), code_hash: account.code_hash, @@ -2150,11 +2221,11 @@ impl Backend { .into_iter() .map(|storage_key| { // the key that should be proofed is the keccak256 of the storage key - let key = H256::from(keccak256(storage_key)); + let key = B256::from(keccak256(storage_key)); prove_storage(&account, &account_db.0, key).map( |(storage_proof, storage_value)| StorageProof { - key: storage_key, - value: storage_value.into_uint(), + key: storage_key.to_ethers(), + value: storage_value.to_ethers().into_uint(), proof: storage_proof .into_iter() .map(|proof| { @@ -2184,7 +2255,7 @@ impl Backend { } /// Notifies all `new_block_listeners` about the new block - fn notify_on_new_block(&self, header: Header, hash: H256) { + fn notify_on_new_block(&self, header: Header, hash: B256) { // cleanup closed notification streams first, if the channel is closed we can remove the // sender half for the set self.new_block_listeners.lock().retain(|tx| !tx.is_closed()); @@ -2214,7 +2285,9 @@ fn get_pool_transactions_nonce( } }); if let Some(highest_nonce_tx) = highest_nonce_tx { - return Some(highest_nonce_tx.pending_transaction.nonce().saturating_add(U256::one())) + return Some( + highest_nonce_tx.pending_transaction.nonce().to_alloy().saturating_add(U256::from(1)), + ); } None } @@ -2226,7 +2299,7 @@ impl TransactionValidator for Backend { tx: &PendingTransaction, ) -> Result<(), BlockchainError> { let address = *tx.sender(); - let account = self.get_account(address).await?; + let account = self.get_account(address.to_alloy()).await?; let env = self.next_env(); Ok(self.validate_pool_transaction_for(tx, &account, &env)?) } @@ -2241,24 +2314,25 @@ impl TransactionValidator for Backend { if let Some(tx_chain_id) = tx.chain_id() { let chain_id = self.chain_id(); - if chain_id != tx_chain_id { + if chain_id.to::() != tx_chain_id { if let Some(legacy) = tx.as_legacy() { // - if env.cfg.spec_id >= SpecId::SPURIOUS_DRAGON && !legacy.meets_eip155(chain_id) + if env.cfg.spec_id >= SpecId::SPURIOUS_DRAGON && + !legacy.meets_eip155(chain_id.to::()) { warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V"); - return Err(InvalidTransactionError::IncompatibleEIP155) + return Err(InvalidTransactionError::IncompatibleEIP155); } } else { warn!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id"); - return Err(InvalidTransactionError::InvalidChainId) + return Err(InvalidTransactionError::InvalidChainId); } } } - if tx.gas_limit() < MIN_TRANSACTION_GAS { + if tx.gas_limit() < MIN_TRANSACTION_GAS.to_ethers() { warn!(target: "backend", "[{:?}] gas too low", tx.hash()); - return Err(InvalidTransactionError::GasTooLow) + return Err(InvalidTransactionError::GasTooLow); } // Check gas limit, iff block gas limit is set. @@ -2266,7 +2340,7 @@ impl TransactionValidator for Backend { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { detail: String::from("tx.gas_limit > env.block.gas_limit"), - })) + })); } // check nonce @@ -2276,13 +2350,13 @@ impl TransactionValidator for Backend { (*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) + 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 { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); - return Err(InvalidTransactionError::FeeCapTooLow) + return Err(InvalidTransactionError::FeeCapTooLow); } if let (Some(max_priority_fee_per_gas), Some(max_fee_per_gas)) = @@ -2290,7 +2364,7 @@ impl TransactionValidator for Backend { { if max_priority_fee_per_gas > max_fee_per_gas { warn!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas); - return Err(InvalidTransactionError::TipAboveFeeCap) + return Err(InvalidTransactionError::TipAboveFeeCap); } } } @@ -2306,7 +2380,7 @@ impl TransactionValidator for Backend { if account.balance < req_funds.to_alloy() { warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); - return Err(InvalidTransactionError::InsufficientFunds) + return Err(InvalidTransactionError::InsufficientFunds); } Ok(()) } @@ -2319,7 +2393,7 @@ impl TransactionValidator for Backend { ) -> Result<(), InvalidTransactionError> { self.validate_pool_transaction_for(tx, account, env)?; if tx.nonce().as_u64() > account.nonce { - return Err(InvalidTransactionError::NonceTooHigh) + return Err(InvalidTransactionError::NonceTooHigh); } Ok(()) } @@ -2328,15 +2402,15 @@ impl TransactionValidator for Backend { /// Creates a `Transaction` as it's expected for the `eth` RPC api from storage data #[allow(clippy::too_many_arguments)] pub fn transaction_build( - tx_hash: Option, + tx_hash: Option, eth_transaction: MaybeImpersonatedTransaction, block: Option<&Block>, info: Option, base_fee: Option, ) -> Transaction { let mut transaction: Transaction = eth_transaction.clone().into(); - if info.is_some() && transaction.transaction_type.unwrap_or(U64::zero()).as_u64() == 0x7E { - transaction.nonce = U256::from(info.as_ref().unwrap().nonce); + if info.is_some() && transaction.transaction_type.unwrap_or(U64::ZERO).to::() == 0x7E { + transaction.nonce = U64::from(info.as_ref().unwrap().nonce); } if eth_transaction.is_dynamic_fee() { @@ -2346,11 +2420,11 @@ pub fn transaction_build( } else { // if transaction is already mined, gas price is considered base fee + priority fee: the // effective gas price. - let base_fee = base_fee.unwrap_or(U256::zero()); + let base_fee = base_fee.unwrap_or(U256::ZERO); let max_priority_fee_per_gas = - transaction.max_priority_fee_per_gas.unwrap_or(U256::zero()); + transaction.max_priority_fee_per_gas.map(|g| g.to::()).unwrap_or(U256::ZERO); transaction.gas_price = Some( - base_fee.checked_add(max_priority_fee_per_gas).unwrap_or_else(U256::max_value), + base_fee.checked_add(max_priority_fee_per_gas).unwrap_or(U256::MAX).to::(), ); } } else { @@ -2359,20 +2433,22 @@ pub fn transaction_build( } transaction.block_hash = - block.as_ref().map(|block| H256::from(keccak256(&rlp::encode(&block.header)))); + block.as_ref().map(|block| B256::from(keccak256(&rlp::encode(&block.header)))); - transaction.block_number = block.as_ref().map(|block| block.header.number.as_u64().into()); + transaction.block_number = block.as_ref().map(|block| U256::from(block.header.number.as_u64())); - transaction.transaction_index = info.as_ref().map(|status| status.transaction_index.into()); + transaction.transaction_index = + info.as_ref().map(|status| U256::from(status.transaction_index)); // need to check if the signature of the transaction is impersonated, if so then we // 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(); - transaction.hash = eth_transaction.impersonated_hash(transaction.from); + 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(); } else { - transaction.from = eth_transaction.recover().expect("can recover signed tx"); + transaction.from = eth_transaction.recover().expect("can recover signed tx").to_alloy(); } // if a specific hash was provided we update the transaction's hash @@ -2384,7 +2460,10 @@ pub fn transaction_build( transaction.hash = tx_hash; } - transaction.to = info.as_ref().map_or(eth_transaction.to().cloned(), |status| status.to); + transaction.to = info + .as_ref() + .map_or(eth_transaction.to().cloned(), |status| status.to) + .map(|t| t.to_alloy()); transaction } @@ -2397,8 +2476,8 @@ pub fn transaction_build( pub fn prove_storage( acc: &BasicAccount, data: &AsHashDB, - storage_key: H256, -) -> Result<(Vec>, H256), BlockchainError> { + storage_key: B256, +) -> Result<(Vec>, B256), BlockchainError> { let data: &dyn HashDB<_, _> = data.deref(); let mut recorder = Recorder::new(); let trie = RefTrieDB::new(&data, &acc.storage_root.0) @@ -2408,10 +2487,11 @@ pub fn prove_storage( let item: U256 = { let decode_value = |bytes: &[u8]| rlp::decode(bytes).expect("decoding db value failed"); let query = (&mut recorder, decode_value); - trie.get_with(storage_key.as_bytes(), query) + trie.get_with(storage_key.to_ethers().as_bytes(), query) .map_err(|err| BlockchainError::TrieError(err.to_string()))? - .unwrap_or_else(U256::zero) + .unwrap_or_else(|| U256::ZERO.to_ethers()) + .to_alloy() }; - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), BigEndianHash::from_uint(&item))) + 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 bda955860d4a..d5a2d40a7a8b 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -1,13 +1,11 @@ //! Support for generating the state root for memdb storage use crate::eth::{backend::db::AsHashDB, error::BlockchainError}; -use alloy_primitives::{Address, Bytes, U256 as rU256}; -use anvil_core::eth::{state::StateOverride, trie::RefSecTrieDBMut}; -use ethers::{ - types::H256, - utils::{rlp, rlp::RlpStream}, -}; -use foundry_common::types::{ToAlloy, ToEthers}; +use alloy_primitives::{Address, Bytes, B256, U256 as rU256}; +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, @@ -22,7 +20,7 @@ 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) -> H256 { +pub fn log_rlp_hash(logs: Vec) -> B256 { let mut stream = RlpStream::new(); stream.begin_unbounded_list(); for log in logs { @@ -36,11 +34,11 @@ pub fn log_rlp_hash(logs: Vec) -> H256 { let out = stream.out().freeze(); let out = ethers::utils::keccak256(out); - H256::from_slice(out.as_slice()) + B256::from_slice(out.as_slice()) } /// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { +pub fn storage_trie_db(storage: &Map) -> (AsHashDB, B256) { // Populate DB with full trie from entries. let (db, root) = { let mut db = , _>>::default(); @@ -50,7 +48,7 @@ pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { 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 = H256::from(temp); + let key = B256::from(temp).to_ethers(); let value = rlp::encode(&(*v).to_ethers()); trie.insert(key.as_bytes(), value.as_ref()).unwrap(); } @@ -58,11 +56,11 @@ pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { (db, root) }; - (Box::new(db), H256::from(root)) + (Box::new(db), B256::from(root)) } /// Returns the account data as `HashDB` -pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { +pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, B256) { let accounts = trie_accounts(accounts); // Populate DB with full trie from entries. @@ -78,7 +76,7 @@ pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { (db, root) }; - (Box::new(db), H256::from(root)) + (Box::new(db), B256::from(root)) } /// Returns all RLP-encoded Accounts @@ -92,7 +90,7 @@ pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes) .collect() } -pub fn state_merkle_trie_root(accounts: &Map) -> H256 { +pub fn state_merkle_trie_root(accounts: &Map) -> B256 { trie_hash_db(accounts).1 } @@ -101,7 +99,7 @@ pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Byte let mut stream = RlpStream::new_list(4); stream.append(&info.nonce); stream.append(&info.balance.to_ethers()); - stream.append(&storage_trie_db(storage).1); + stream.append(&storage_trie_db(storage).1.to_ethers()); stream.append(&info.code_hash.as_slice()); stream.out().freeze().into() } @@ -116,19 +114,19 @@ where { let mut cache_db = CacheDB::new(state); for (account, account_overrides) in overrides.iter() { - let mut account_info = cache_db.basic_ref((*account).to_alloy())?.unwrap_or_default(); + let mut account_info = cache_db.basic_ref(*account)?.unwrap_or_default(); if let Some(nonce) = account_overrides.nonce { - account_info.nonce = nonce; + account_info.nonce = nonce.to::(); } if let Some(code) = &account_overrides.code { account_info.code = Some(Bytecode::new_raw(code.to_vec().into())); } if let Some(balance) = account_overrides.balance { - account_info.balance = balance.to_alloy(); + account_info.balance = balance; } - cache_db.insert_account_info((*account).to_alloy(), account_info); + cache_db.insert_account_info(*account, account_info); // We ensure that not both state and state_diff are set. // If state is set, we must mark the account as "NewlyCreated", so that the old storage @@ -142,20 +140,16 @@ where (None, None) => (), (Some(new_account_state), None) => { cache_db.replace_account_storage( - (*account).to_alloy(), + *account, new_account_state .iter() - .map(|(key, value)| (key.to_alloy().into(), (value.to_alloy().into()))) + .map(|(key, value)| ((*key).into(), (*value))) .collect(), )?; } (None, Some(account_state_diff)) => { for (key, value) in account_state_diff.iter() { - cache_db.insert_account_storage( - (*account).to_alloy(), - key.to_alloy().into(), - value.to_alloy().into(), - )?; + cache_db.insert_account_storage(*account, (*key).into(), *value)?; } } }; diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 327bb9697449..8757ef3a5e81 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -6,17 +6,24 @@ use crate::eth::{ }, pool::transactions::PoolTransaction, }; +use alloy_primitives::{Bytes, TxHash, B256, U256, U64}; +use alloy_rpc_trace_types::{ + geth::{DefaultFrame, GethDefaultTracingOptions}, + parity::LocalizedTransactionTrace, +}; +use alloy_rpc_types::{ + BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo, TransactionReceipt, +}; use anvil_core::eth::{ block::{Block, PartialHeader}, receipt::TypedReceipt, transaction::{MaybeImpersonatedTransaction, TransactionInfo}, }; -use ethers::{ - prelude::{BlockId, BlockNumber, DefaultFrame, Trace, H256, H256 as TxHash, U64}, - types::{ActionType, Bytes, GethDebugTracingOptions, TransactionReceipt, U256}, -}; use foundry_common::types::{ToAlloy, ToEthers}; -use foundry_evm::revm::{interpreter::InstructionResult, primitives::Env}; +use foundry_evm::{ + revm::primitives::Env, + traces::{GethTraceBuilder, ParityTraceBuilder, TracingInspectorConfig}, +}; use parking_lot::RwLock; use std::{ collections::{HashMap, VecDeque}, @@ -37,9 +44,9 @@ const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600; /// Represents the complete state of single block pub struct InMemoryBlockStates { /// The states at a certain block - states: HashMap, + states: HashMap, /// states which data is moved to disk - on_disk_states: HashMap, + on_disk_states: HashMap, /// How many states to store at most in_memory_limit: usize, /// minimum amount of states we keep in memory @@ -49,9 +56,9 @@ pub struct InMemoryBlockStates { /// Limiting the states will prevent disk blow up, especially in interval mining mode max_on_disk_limit: usize, /// the oldest states written to disk - oldest_on_disk: VecDeque, + oldest_on_disk: VecDeque, /// all states present, used to enforce `in_memory_limit` - present: VecDeque, + present: VecDeque, /// Stores old states on disk disk_cache: DiskStateCache, } @@ -109,7 +116,7 @@ impl InMemoryBlockStates { /// the number of states/blocks until we reached the `min_limit`. /// /// When a state that was previously written to disk is requested, it is simply read from disk. - pub fn insert(&mut self, hash: H256, state: StateDb) { + pub fn insert(&mut self, hash: B256, state: StateDb) { if !self.is_memory_only() && self.present.len() >= self.in_memory_limit { // once we hit the max limit we gradually decrease it self.in_memory_limit = @@ -153,7 +160,7 @@ impl InMemoryBlockStates { } /// Returns the state for the given `hash` if present - pub fn get(&mut self, hash: &H256) -> Option<&StateDb> { + pub fn get(&mut self, hash: &B256) -> Option<&StateDb> { self.states.get(hash).or_else(|| { if let Some(state) = self.on_disk_states.get_mut(hash) { if let Some(cached) = self.disk_cache.read(*hash) { @@ -204,15 +211,15 @@ impl Default for InMemoryBlockStates { #[derive(Clone)] pub struct BlockchainStorage { /// all stored blocks (block hash -> block) - pub blocks: HashMap, + pub blocks: HashMap, /// mapping from block number -> block hash - pub hashes: HashMap, + pub hashes: HashMap, /// The current best hash - pub best_hash: H256, + pub best_hash: B256, /// The current best block number pub best_number: U64, /// genesis hash of the chain - pub genesis_hash: H256, + pub genesis_hash: B256, /// Mapping from the transaction hash to a tuple containing the transaction as well as the /// transaction receipt pub transactions: HashMap, @@ -226,7 +233,7 @@ impl BlockchainStorage { // create a dummy genesis block let partial_header = PartialHeader { timestamp, - base_fee, + 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(), @@ -235,25 +242,25 @@ impl BlockchainStorage { let block = Block::new::(partial_header, vec![], vec![]); let genesis_hash = block.header.hash(); let best_hash = genesis_hash; - let best_number: U64 = 0u64.into(); + let best_number: U64 = U64::from(0u64); Self { - blocks: HashMap::from([(genesis_hash, block)]), - hashes: HashMap::from([(best_number, genesis_hash)]), - best_hash, + blocks: HashMap::from([(genesis_hash.to_alloy(), block)]), + hashes: HashMap::from([(best_number, genesis_hash.to_alloy())]), + best_hash: best_hash.to_alloy(), best_number, - genesis_hash, + genesis_hash: genesis_hash.to_alloy(), transactions: Default::default(), total_difficulty: Default::default(), } } - pub fn forked(block_number: u64, block_hash: H256, total_difficulty: U256) -> Self { + pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self { BlockchainStorage { blocks: Default::default(), - hashes: HashMap::from([(block_number.into(), block_hash)]), + hashes: HashMap::from([(U64::from(block_number), block_hash)]), best_hash: block_hash, - best_number: block_number.into(), + best_number: U64::from(block_number), genesis_hash: Default::default(), transactions: Default::default(), total_difficulty, @@ -275,16 +282,16 @@ impl BlockchainStorage { /// Removes all stored transactions for the given block number pub fn remove_block_transactions_by_number(&mut self, num: u64) { - if let Some(hash) = self.hashes.get(&(num.into())).copied() { + if let Some(hash) = self.hashes.get(&(U64::from(num))).copied() { self.remove_block_transactions(hash); } } /// Removes all stored transactions for the given block hash - pub fn remove_block_transactions(&mut self, block_hash: H256) { + 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()); + self.transactions.remove(&tx.hash().to_alloy()); } block.transactions.clear(); } @@ -294,24 +301,26 @@ impl BlockchainStorage { // === impl BlockchainStorage === impl BlockchainStorage { - /// Returns the hash for [BlockNumber] - pub fn hash(&self, number: BlockNumber) -> Option { + /// Returns the hash for [BlockNumberOrTag] + pub fn hash(&self, number: BlockNumberOrTag) -> Option { let slots_in_an_epoch = U64::from(32u64); match number { - BlockNumber::Latest => Some(self.best_hash), - BlockNumber::Earliest => Some(self.genesis_hash), - BlockNumber::Pending => None, - BlockNumber::Number(num) => self.hashes.get(&num).copied(), - BlockNumber::Safe => { + BlockNumberOrTag::Latest => Some(self.best_hash), + BlockNumberOrTag::Earliest => Some(self.genesis_hash), + BlockNumberOrTag::Pending => None, + BlockNumberOrTag::Number(num) => self.hashes.get(&U64::from(num)).copied(), + BlockNumberOrTag::Safe => { if self.best_number > (slots_in_an_epoch) { self.hashes.get(&(self.best_number - (slots_in_an_epoch))).copied() } else { Some(self.genesis_hash) // treat the genesis block as safe "by definition" } } - BlockNumber::Finalized => { - if self.best_number > (slots_in_an_epoch * 2) { - self.hashes.get(&(self.best_number - (slots_in_an_epoch * 2))).copied() + BlockNumberOrTag::Finalized => { + if self.best_number > (slots_in_an_epoch * U64::from(2)) { + self.hashes + .get(&(self.best_number - (slots_in_an_epoch * U64::from(2)))) + .copied() } else { Some(self.genesis_hash) } @@ -335,7 +344,7 @@ impl Blockchain { Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) } } - pub fn forked(block_number: u64, block_hash: H256, total_difficulty: U256) -> Self { + pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self { Self { storage: Arc::new(RwLock::new(BlockchainStorage::forked( block_number, @@ -346,18 +355,18 @@ impl Blockchain { } /// returns the header hash of given block - pub fn hash(&self, id: BlockId) -> Option { + pub fn hash(&self, id: BlockId) -> Option { match id { - BlockId::Hash(h) => Some(h), + BlockId::Hash(h) => Some(h.block_hash), BlockId::Number(num) => self.storage.read().hash(num), } } - pub fn get_block_by_hash(&self, hash: &H256) -> Option { + pub fn get_block_by_hash(&self, hash: &B256) -> Option { self.storage.read().blocks.get(hash).cloned() } - pub fn get_transaction_by_hash(&self, hash: &H256) -> Option { + pub fn get_transaction_by_hash(&self, hash: &B256) -> Option { self.storage.read().transactions.get(hash).cloned() } @@ -384,7 +393,7 @@ pub struct MinedBlockOutcome { pub struct MinedTransaction { pub info: TransactionInfo, pub receipt: TypedReceipt, - pub block_hash: H256, + pub block_hash: B256, pub block_number: u64, } @@ -392,38 +401,28 @@ pub struct MinedTransaction { impl MinedTransaction { /// Returns the traces of the transaction for `trace_transaction` - pub fn parity_traces(&self) -> Vec { - let mut traces = Vec::with_capacity(self.info.traces.arena.len()); - for (idx, node) in self.info.traces.arena.iter().cloned().enumerate() { - let action = node.parity_action(); - let result = node.parity_result(); - - let action_type = if node.status() == InstructionResult::SelfDestruct { - ActionType::Suicide - } else { - node.kind().into() - }; - - let trace = Trace { - action, - result: Some(result), - trace_address: self.info.trace_address(idx), - subtraces: node.children.len(), - transaction_position: Some(self.info.transaction_index as usize), - transaction_hash: Some(self.info.transaction_hash), - block_number: self.block_number, - block_hash: self.block_hash, - action_type, - error: None, - }; - traces.push(trace) - } - - traces + pub fn parity_traces(&self) -> Vec { + ParityTraceBuilder::new( + self.info.traces.clone(), + None, + TracingInspectorConfig::default_parity(), + ) + .into_localized_transaction_traces(RethTransactionInfo { + hash: Some(self.info.transaction_hash.to_alloy()), + index: Some(self.info.transaction_index as u64), + block_hash: Some(self.block_hash), + block_number: Some(self.block_number), + base_fee: None, + }) } - pub fn geth_trace(&self, opts: GethDebugTracingOptions) -> DefaultFrame { - self.info.traces.geth_trace(self.receipt.gas_used().to_alloy(), opts) + 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.info.out.clone().unwrap_or_default().0.into(), + opts, + ) } } @@ -440,8 +439,7 @@ pub struct MinedTransactionReceipt { mod tests { use super::*; use crate::eth::backend::db::Db; - use ethers::{abi::ethereum_types::BigEndianHash, types::Address}; - use foundry_common::types::ToAlloy; + use alloy_primitives::{Address, B256, U256}; use foundry_evm::{ backend::MemDb, revm::{ @@ -460,8 +458,8 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn can_read_write_cached_state() { let mut storage = InMemoryBlockStates::new(1); - let one = H256::from_uint(&U256::from(1)); - let two = H256::from_uint(&U256::from(2)); + let one = B256::from(U256::from(1)); + let two = B256::from(U256::from(2)); let mut state = MemDb::default(); let addr = Address::random(); @@ -478,7 +476,7 @@ mod tests { let loaded = storage.get(&one).unwrap(); - let acc = loaded.basic_ref(addr.to_alloy()).unwrap().unwrap(); + let acc = loaded.basic_ref(addr).unwrap().unwrap(); assert_eq!(acc.balance, rU256::from(1337u64)); } @@ -490,8 +488,8 @@ mod tests { let num_states = 30; for idx in 0..num_states { let mut state = MemDb::default(); - let hash = H256::from_uint(&U256::from(idx)); - let addr = Address::from(hash); + let hash = B256::from(U256::from(idx)); + let addr = Address::from_word(hash); let balance = (idx * 2) as u64; let info = AccountInfo::from_balance(rU256::from(balance)); state.insert_account(addr, info); @@ -505,10 +503,10 @@ mod tests { assert_eq!(storage.present.len(), storage.min_in_memory_limit); for idx in 0..num_states { - let hash = H256::from_uint(&U256::from(idx)); - let addr = Address::from(hash); + let hash = B256::from(U256::from(idx)); + let addr = Address::from_word(hash); let loaded = storage.get(&hash).unwrap(); - let acc = loaded.basic_ref(addr.to_alloy()).unwrap().unwrap(); + let acc = loaded.basic_ref(addr).unwrap().unwrap(); let balance = (idx * 2) as u64; assert_eq!(acc.balance, rU256::from(balance)); } diff --git a/crates/anvil/src/eth/backend/notifications.rs b/crates/anvil/src/eth/backend/notifications.rs index 7a5f428db7a4..a463dfb9446d 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_primitives::B256; use anvil_core::eth::block::Header; -use ethers::types::H256; use futures::channel::mpsc::UnboundedReceiver; use std::sync::Arc; @@ -9,7 +9,7 @@ use std::sync::Arc; #[derive(Clone, Debug)] pub struct NewBlockNotification { /// Hash of the imported block - pub hash: H256, + pub hash: B256, /// block header pub header: Arc
, } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 2cb67e496c6a..a92df9ce92e6 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,6 +1,7 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; +use alloy_transport::TransportError; use anvil_rpc::{ error::{ErrorCode, RpcError}, response::ResponseResult, @@ -58,6 +59,8 @@ pub enum BlockchainError { FeeHistory(#[from] FeeHistoryError), #[error(transparent)] ForkProvider(#[from] ProviderError), + #[error(transparent)] + AlloyForkProvider(#[from] TransportError), #[error("EVM error {0:?}")] EvmError(InstructionResult), #[error("Invalid url {0:?}")] @@ -371,6 +374,10 @@ impl ToRpcResponseResult for Result { 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:?}")) + } err @ BlockchainError::EvmError(_) => { RpcError::internal_error_with(err.to_string()) } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 43228e0a6e61..685119456abc 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -2,8 +2,9 @@ use crate::eth::{ backend::{info::StorageInfo, notifications::NewBlockNotifications}, error::BlockchainError, }; +use alloy_primitives::{B256, U256}; use anvil_core::eth::transaction::TypedTransaction; -use ethers::types::{H256, U256}; +use foundry_common::types::ToAlloy; use foundry_evm::revm::primitives::SpecId; use futures::StreamExt; use parking_lot::{Mutex, RwLock}; @@ -90,7 +91,7 @@ impl FeeManager { if self.is_eip1559() { *self.base_fee.read() } else { - U256::zero() + U256::ZERO } } @@ -99,7 +100,7 @@ impl FeeManager { /// This mirrors geth's auto values for `SuggestGasTipCap` which is: `priority fee + 2x current /// basefee`. pub fn max_priority_fee_per_gas(&self) -> U256 { - self.suggested_priority_fee() + *self.base_fee.read() * 2 + self.suggested_priority_fee() + *self.base_fee.read() * U256::from(2) } /// Returns the current gas price @@ -125,13 +126,13 @@ impl FeeManager { // It's naturally impossible for base fee to be 0; // It means it was set by the user deliberately and therefore we treat it as a constant. // Therefore, we skip the base fee calculation altogether and we return 0. - if self.base_fee() == U256::zero() { + if self.base_fee() == U256::ZERO { return 0 } calculate_next_block_base_fee( - gas_used.as_u64(), - gas_limit.as_u64(), - last_fee_per_gas.as_u64(), + gas_used.to::(), + gas_limit.to::(), + last_fee_per_gas.to::(), ) } } @@ -202,7 +203,7 @@ impl FeeHistoryService { /// Create a new history entry for the block fn create_cache_entry( &self, - hash: H256, + hash: B256, elasticity: f64, ) -> (FeeHistoryCacheItem, Option) { // percentile list from 0.0 to 100.0 with a 0.5 resolution. @@ -221,7 +222,7 @@ impl FeeHistoryService { let mut block_number: Option = None; let base_fee = self.fees.base_fee(); let mut item = FeeHistoryCacheItem { - base_fee: base_fee.as_u64(), + base_fee: base_fee.to::(), gas_used_ratio: 0f64, rewards: Vec::new(), }; @@ -247,15 +248,16 @@ impl FeeHistoryService { let effective_reward = match block.transactions.get(i).map(|tx| &tx.transaction) { Some(TypedTransaction::Legacy(t)) => { - t.gas_price.saturating_sub(base_fee).as_u64() + t.gas_price.to_alloy().saturating_sub(base_fee).to::() } Some(TypedTransaction::EIP2930(t)) => { - t.gas_price.saturating_sub(base_fee).as_u64() + t.gas_price.to_alloy().saturating_sub(base_fee).to::() } Some(TypedTransaction::EIP1559(t)) => t .max_priority_fee_per_gas - .min(t.max_fee_per_gas.saturating_sub(base_fee)) - .as_u64(), + .to_alloy() + .min(t.max_fee_per_gas.to_alloy().saturating_sub(base_fee)) + .to::(), Some(TypedTransaction::Deposit(_)) => 0, None => 0, }; @@ -347,9 +349,9 @@ impl FeeDetails { /// All values zero pub fn zero() -> Self { Self { - gas_price: Some(U256::zero()), - max_fee_per_gas: Some(U256::zero()), - max_priority_fee_per_gas: Some(U256::zero()), + gas_price: Some(U256::ZERO), + max_fee_per_gas: Some(U256::ZERO), + max_priority_fee_per_gas: Some(U256::ZERO), } } @@ -358,8 +360,8 @@ impl FeeDetails { let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; let no_fees = gas_price.is_none() && max_fee_per_gas.is_none(); - let gas_price = if no_fees { Some(U256::zero()) } else { gas_price }; - let max_fee_per_gas = if no_fees { Some(U256::zero()) } else { max_fee_per_gas }; + let gas_price = if no_fees { Some(U256::ZERO) } else { gas_price }; + let max_fee_per_gas = if no_fees { Some(U256::ZERO) } else { max_fee_per_gas }; Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } } diff --git a/crates/anvil/src/eth/miner.rs b/crates/anvil/src/eth/miner.rs index 4478328434ec..2199a5061351 100644 --- a/crates/anvil/src/eth/miner.rs +++ b/crates/anvil/src/eth/miner.rs @@ -1,7 +1,7 @@ //! Mines transactions use crate::eth::pool::{transactions::PoolTransaction, Pool}; -use ethers::prelude::TxHash; +use alloy_primitives::TxHash; use futures::{ channel::mpsc::Receiver, stream::{Fuse, Stream, StreamExt}, diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 95a255051ea9..ac092f36c7f7 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -7,10 +7,11 @@ use crate::eth::{ macros::node_info, EthApi, }; -use ethers::types::{ - Action, Address, Block, BlockId, BlockNumber, Bytes, Call, Create, CreateResult, Res, Reward, - Transaction, TxHash, H256, U256, U64, +use alloy_primitives::{Address, Bytes, B256, U256, U64}; +use alloy_rpc_trace_types::parity::{ + Action, CallAction, CreateAction, CreateOutput, RewardAction, TraceOutput, }; +use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction}; use itertools::Itertools; impl EthApi { @@ -19,10 +20,7 @@ impl EthApi { /// /// As a faster alternative to eth_getBlockByNumber (by excluding uncle block /// information), which is not relevant in the context of an anvil node - pub async fn erigon_get_header_by_number( - &self, - number: BlockNumber, - ) -> Result>> { + pub async fn erigon_get_header_by_number(&self, number: BlockNumber) -> Result> { node_info!("ots_getApiLevel"); self.backend.block_by_number(number).await @@ -41,7 +39,7 @@ impl EthApi { /// certain transaction. pub async fn ots_get_internal_operations( &self, - hash: H256, + hash: B256, ) -> Result> { node_info!("ots_getInternalOperations"); @@ -59,19 +57,19 @@ impl EthApi { } /// Trace a transaction and generate a trace call tree. - pub async fn ots_trace_transaction(&self, hash: H256) -> Result> { + pub async fn ots_trace_transaction(&self, hash: B256) -> Result> { node_info!("ots_traceTransaction"); Ok(OtsTrace::batch_build(self.backend.trace_transaction(hash).await?)) } /// Given a transaction hash, returns its raw revert reason. - pub async fn ots_get_transaction_error(&self, hash: H256) -> Result> { + pub async fn ots_get_transaction_error(&self, hash: B256) -> Result> { node_info!("ots_getTransactionError"); if let Some(receipt) = self.backend.mined_transaction_receipt(hash) { - if receipt.inner.status == Some(U64::zero()) { - return Ok(receipt.out) + if receipt.inner.status_code == Some(U64::ZERO) { + return Ok(receipt.out.map(|b| b.0.into())) } } @@ -96,7 +94,7 @@ impl EthApi { /// For simplicity purposes, we return the entire block instead of emptying the values that /// Otterscan doesn't want. This is the original purpose of the endpoint (to save bandwidth), /// but it doesn't seem necessary in the context of an anvil node - pub async fn ots_get_block_details_by_hash(&self, hash: H256) -> Result { + pub async fn ots_get_block_details_by_hash(&self, hash: B256) -> Result { node_info!("ots_getBlockDetailsByHash"); if let Some(block) = self.backend.block_by_hash(hash).await? { @@ -133,7 +131,7 @@ impl EthApi { ) -> Result { node_info!("ots_searchTransactionsBefore"); - let best = self.backend.best_number().as_u64(); + let best = self.backend.best_number(); // we go from given block (defaulting to best) down to first block // considering only post-fork let from = if block_number == 0 { best } else { block_number }; @@ -153,8 +151,10 @@ impl EthApi { let hashes = traces .into_iter() .rev() - .filter_map(|trace| match trace.action { - Action::Call(Call { from, to, .. }) if from == address || to == address => { + .filter_map(|trace| match trace.trace.action { + Action::Call(CallAction { from, to, .. }) + if from == address || to == address => + { trace.transaction_hash } _ => None, @@ -181,7 +181,7 @@ impl EthApi { ) -> Result { node_info!("ots_searchTransactionsAfter"); - let best = self.backend.best_number().as_u64(); + let best = self.backend.best_number(); // we go from the first post-fork block, up to the tip let from = if block_number == 0 { self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1) @@ -204,14 +204,16 @@ impl EthApi { let hashes = traces .into_iter() .rev() - .filter_map(|trace| match trace.action { - Action::Call(Call { from, to, .. }) if from == address || to == address => { + .filter_map(|trace| match trace.trace.action { + Action::Call(CallAction { from, to, .. }) + if from == address || to == address => + { trace.transaction_hash } - Action::Create(Create { from, .. }) if from == address => { + Action::Create(CreateAction { from, .. }) if from == address => { trace.transaction_hash } - Action::Reward(Reward { author, .. }) if author == address => { + Action::Reward(RewardAction { author, .. }) if author == address => { trace.transaction_hash } _ => None, @@ -240,12 +242,12 @@ impl EthApi { node_info!("ots_getTransactionBySenderAndNonce"); let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); - let to = self.backend.best_number().as_u64(); + let to = self.backend.best_number(); for n in (from..=to).rev() { if let Some(txs) = self.backend.mined_transactions_by_block_number(n.into()).await { for tx in txs { - if tx.nonce == nonce && tx.from == address { + if U256::from(tx.nonce) == nonce && tx.from == address { return Ok(Some(tx)) } } @@ -264,16 +266,16 @@ impl EthApi { node_info!("ots_getContractCreator"); let from = self.get_fork().map(|f| f.block_number()).unwrap_or_default(); - let to = self.backend.best_number().as_u64(); + let to = self.backend.best_number(); // loop in reverse, since we want the latest deploy to the address for n in (from..=to).rev() { if let Some(traces) = self.backend.mined_parity_trace_block(n) { for trace in traces.into_iter().rev() { - match (trace.action, trace.result) { + match (trace.trace.action, trace.trace.result) { ( - Action::Create(Create { from, .. }), - Some(Res::Create(CreateResult { address, .. })), + Action::Create(CreateAction { from, .. }), + Some(TraceOutput::Create(CreateOutput { address, .. })), ) if address == addr => { return Ok(Some(OtsContractCreator { hash: trace.transaction_hash.unwrap(), diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index cc9e763e4c33..ff1f99ce5d13 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -2,22 +2,20 @@ use crate::eth::{ backend::mem::{storage::MinedTransaction, Backend}, error::{BlockchainError, Result}, }; -use alloy_primitives::U256 as rU256; -use ethers::types::{ - Action, Address, Block, Bytes, CallType, Trace, Transaction, TransactionReceipt, H256, U256, -}; -use foundry_common::types::ToEthers; -use foundry_evm::{revm::interpreter::InstructionResult, utils::CallKind}; +use alloy_primitives::{Address, Bytes, B256, U256 as rU256, U256}; +use alloy_rpc_trace_types::parity::{Action, CallType, LocalizedTransactionTrace}; +use alloy_rpc_types::{Block, BlockTransactions, Transaction, TransactionReceipt}; +use foundry_evm::{revm::interpreter::InstructionResult, traces::CallKind}; use futures::future::join_all; -use serde::{de::DeserializeOwned, Serialize}; +use serde::Serialize; use serde_repr::Serialize_repr; /// Patched Block struct, to include the additional `transactionCount` field expected by Otterscan -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase", bound = "TX: Serialize + DeserializeOwned")] -pub struct OtsBlock { +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OtsBlock { #[serde(flatten)] - pub block: Block, + pub block: Block, pub transaction_count: usize, } @@ -25,7 +23,7 @@ pub struct OtsBlock { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OtsBlockDetails { - pub block: OtsBlock, + pub block: OtsBlock, pub total_fees: U256, pub issuance: Issuance, } @@ -39,9 +37,9 @@ pub struct Issuance { } /// Holds both transactions and receipts for a block -#[derive(Debug, Serialize)] +#[derive(Clone, Serialize, Debug)] pub struct OtsBlockTransactions { - pub fullblock: OtsBlock, + pub fullblock: OtsBlock, pub receipts: Vec, } @@ -57,7 +55,7 @@ pub struct OtsTransactionReceipt { /// Information about the creator address and transaction for a contract #[derive(Debug, Serialize)] pub struct OtsContractCreator { - pub hash: H256, + pub hash: B256, pub creator: Address, } @@ -124,9 +122,14 @@ impl OtsBlockDetails { /// - It breaks the abstraction built in `OtsBlock` which computes `transaction_count` /// based on the existing list. /// Therefore we keep it simple by keeping the data in the response - pub async fn build(block: Block, backend: &Backend) -> Result { + pub async fn build(block: Block, backend: &Backend) -> Result { + let block_txs = match block.transactions.clone() { + BlockTransactions::Full(txs) => txs.into_iter().map(|tx| tx.hash).collect(), + BlockTransactions::Hashes(txs) => txs, + BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), + }; let receipts_futs = - block.transactions.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); + block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); // fetch all receipts let receipts: Vec = join_all(receipts_futs) @@ -138,8 +141,8 @@ impl OtsBlockDetails { }) .collect::>()?; - let total_fees = receipts.iter().fold(U256::zero(), |acc, receipt| { - acc + receipt.gas_used.unwrap() * (receipt.effective_gas_price.unwrap()) + let total_fees = receipts.iter().fold(U256::ZERO, |acc, receipt| { + acc + receipt.gas_used.unwrap() * (U256::from(receipt.effective_gas_price)) }); Ok(Self { @@ -153,9 +156,13 @@ impl OtsBlockDetails { /// Converts a regular block into the patched OtsBlock format /// which includes the `transaction_count` field -impl From> for OtsBlock { - fn from(block: Block) -> Self { - let transaction_count = block.transactions.len(); +impl From for OtsBlock { + fn from(block: Block) -> Self { + let transaction_count = match block.transactions { + BlockTransactions::Full(ref txs) => txs.len(), + BlockTransactions::Hashes(ref txs) => txs.len(), + BlockTransactions::Uncle => 0, + }; Self { block, transaction_count } } @@ -164,18 +171,32 @@ impl From> for OtsBlock { impl OtsBlockTransactions { /// Fetches all receipts for the blocks's transactions, as required by the [`ots_getBlockTransactions`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails) endpoint spec, and returns the final response object. pub async fn build( - mut block: Block, + mut block: Block, backend: &Backend, page: usize, page_size: usize, ) -> Result { - block.transactions = - block.transactions.into_iter().skip(page * page_size).take(page_size).collect(); + let block_txs = match block.transactions.clone() { + BlockTransactions::Full(txs) => txs.into_iter().map(|tx| tx.hash).collect(), + BlockTransactions::Hashes(txs) => txs, + BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), + }; - let receipt_futs = block - .transactions - .iter() - .map(|tx| async { backend.transaction_receipt(tx.hash).await }); + let block_txs = + block_txs.into_iter().skip(page * page_size).take(page_size).collect::>(); + + block.transactions = match block.transactions { + BlockTransactions::Full(txs) => BlockTransactions::Full( + txs.into_iter().skip(page * page_size).take(page_size).collect(), + ), + BlockTransactions::Hashes(txs) => BlockTransactions::Hashes( + txs.into_iter().skip(page * page_size).take(page_size).collect(), + ), + BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), + }; + + let receipt_futs = + block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); let receipts: Vec = join_all(receipt_futs) .await @@ -186,7 +207,7 @@ impl OtsBlockTransactions { }) .collect::>()?; - let fullblock: OtsBlock<_> = block.into(); + let fullblock: OtsBlock = block.into(); Ok(Self { fullblock, receipts }) } @@ -197,7 +218,7 @@ impl OtsSearchTransactions { /// `ots_searchTransactionsAfter`](lrequires not only the transactions, but also the /// corresponding receipts, which are fetched here before constructing the final) pub async fn build( - hashes: Vec, + hashes: Vec, backend: &Backend, first_page: bool, last_page: bool, @@ -216,8 +237,11 @@ impl OtsSearchTransactions { join_all(hashes.iter().map(|hash| async { match backend.transaction_receipt(*hash).await { Ok(Some(receipt)) => { - let timestamp = - backend.get_block(receipt.block_number.unwrap()).unwrap().header.timestamp; + let timestamp = backend + .get_block(receipt.block_number.unwrap().to::()) + .unwrap() + .header + .timestamp; Ok(OtsTransactionReceipt { receipt, timestamp }) } Ok(None) => Err(BlockchainError::DataUnavailable), @@ -238,35 +262,34 @@ impl OtsInternalOperation { traces .info .traces - .arena .iter() .filter_map(|node| { - match (node.kind(), node.status()) { + match (node.trace.kind, node.trace.status) { (CallKind::Call, _) if node.trace.value != rU256::ZERO => Some(Self { r#type: OtsInternalOperationType::Transfer, - from: node.trace.caller.to_ethers(), - to: node.trace.address.to_ethers(), - value: node.trace.value.to_ethers(), + from: node.trace.caller, + to: node.trace.address, + value: node.trace.value, }), (CallKind::Create, _) => Some(Self { r#type: OtsInternalOperationType::Create, - from: node.trace.caller.to_ethers(), - to: node.trace.address.to_ethers(), - value: node.trace.value.to_ethers(), + from: node.trace.caller, + to: node.trace.address, + value: node.trace.value, }), (CallKind::Create2, _) => Some(Self { r#type: OtsInternalOperationType::Create2, - from: node.trace.caller.to_ethers(), - to: node.trace.address.to_ethers(), - value: node.trace.value.to_ethers(), + from: node.trace.caller, + to: node.trace.address, + value: node.trace.value, }), (_, InstructionResult::SelfDestruct) => { Some(Self { r#type: OtsInternalOperationType::SelfDestruct, - from: node.trace.address.to_ethers(), + from: node.trace.address, // the foundry CallTraceNode doesn't have a refund address to: Default::default(), - value: node.trace.value.to_ethers(), + value: node.trace.value, }) } _ => None, @@ -279,26 +302,26 @@ impl OtsInternalOperation { impl OtsTrace { /// Converts the list of traces for a transaction into the expected Otterscan format, as /// specified in the [`ots_traceTransaction`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_tracetransaction) spec - pub fn batch_build(traces: Vec) -> Vec { + pub fn batch_build(traces: Vec) -> Vec { traces .into_iter() - .filter_map(|trace| match trace.action { + .filter_map(|trace| match trace.trace.action { Action::Call(call) => { if let Ok(ots_type) = call.call_type.try_into() { Some(OtsTrace { r#type: ots_type, - depth: trace.trace_address.len(), + depth: trace.trace.trace_address.len(), from: call.from, to: call.to, value: call.value, - input: call.input, + input: call.input.0.into(), }) } else { None } } Action::Create(_) => None, - Action::Suicide(_) => None, + Action::Selfdestruct(_) => None, Action::Reward(_) => None, }) .collect() diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index d66fd4a52231..d8e0deb05501 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -36,11 +36,10 @@ use crate::{ }, mem::storage::MinedBlockOutcome, }; +use alloy_primitives::{TxHash, U64}; +use alloy_rpc_types::txpool::TxpoolStatus; use anvil_core::eth::transaction::PendingTransaction; -use ethers::{ - prelude::TxpoolStatus, - types::{TxHash, U64}, -}; +use foundry_common::types::ToAlloy; use futures::channel::mpsc::{channel, Receiver, Sender}; use parking_lot::{Mutex, RwLock}; use std::{collections::VecDeque, fmt, sync::Arc}; @@ -77,8 +76,8 @@ impl Pool { /// Returns the number of tx that are ready and queued for further execution pub fn txpool_status(&self) -> TxpoolStatus { // Note: naming differs here compared to geth's `TxpoolStatus` - let pending = self.ready_transactions().count().into(); - let queued = self.inner.read().pending_transactions.len().into(); + let pending = U64::from(self.ready_transactions().count()); + let queued = U64::from(self.inner.read().pending_transactions.len()); TxpoolStatus { pending, queued } } @@ -89,7 +88,7 @@ impl Pool { let MinedBlockOutcome { block_number, included, invalid } = outcome; // remove invalid transactions from the pool - self.remove_invalid(invalid.into_iter().map(|tx| *tx.hash()).collect()); + self.remove_invalid(invalid.into_iter().map(|tx| tx.hash()).collect()); // prune all the markers the mined transactions provide let res = self @@ -158,7 +157,7 @@ impl Pool { let mut dropped = None; if !removed.is_empty() { - dropped = removed.into_iter().find(|t| *t.pending_transaction.hash() == tx); + dropped = removed.into_iter().find(|t| t.pending_transaction.hash().to_alloy() == tx); } dropped } @@ -226,7 +225,7 @@ impl PoolInner { } fn add_transaction(&mut self, tx: PoolTransaction) -> Result { - if self.contains(tx.hash()) { + if self.contains(&tx.hash()) { warn!(target: "txpool", "[{:?}] Already imported", tx.hash()); return Err(PoolError::AlreadyImported(Box::new(tx))) } @@ -236,7 +235,7 @@ impl PoolInner { // If all markers are not satisfied import to future if !tx.is_ready() { - let hash = *tx.transaction.hash(); + let hash = tx.transaction.hash(); self.pending_transactions.add_transaction(tx)?; return Ok(AddedTransaction::Pending { hash }) } @@ -248,7 +247,7 @@ impl PoolInner { &mut self, tx: PendingPoolTransaction, ) -> Result { - let hash = *tx.transaction.hash(); + let hash = tx.transaction.hash(); trace!(target: "txpool", "adding ready transaction [{:?}]", hash); let mut ready = ReadyTransaction::new(hash); @@ -263,7 +262,7 @@ impl PoolInner { self.pending_transactions.mark_and_unlock(¤t_tx.transaction.provides), ); - let current_hash = *current_tx.transaction.hash(); + let current_hash = current_tx.transaction.hash(); // try to add the transaction to the ready pool match self.ready_transactions.add_transaction(current_tx) { Ok(replaced_transactions) => { @@ -316,7 +315,7 @@ impl PoolInner { let mut promoted = vec![]; let mut failed = vec![]; for tx in imports { - let hash = *tx.transaction.hash(); + let hash = tx.transaction.hash(); match self.add_ready_transaction(tx) { Ok(res) => promoted.push(res), Err(e) => { diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 458c811caa3a..d2f6a662b7bc 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,6 +1,7 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; +use alloy_primitives::{Address, TxHash, U256}; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; -use ethers::types::{Address, TxHash, U256}; +use foundry_common::types::ToAlloy; use parking_lot::RwLock; use std::{ cmp::Ordering, @@ -44,7 +45,7 @@ impl TransactionOrder { pub fn priority(&self, tx: &TypedTransaction) -> TransactionPriority { match self { TransactionOrder::Fifo => TransactionPriority::default(), - TransactionOrder::Fees => TransactionPriority(tx.gas_price()), + TransactionOrder::Fees => TransactionPriority(tx.gas_price().to_alloy()), } } } @@ -87,13 +88,13 @@ pub struct PoolTransaction { impl PoolTransaction { /// Returns the hash of this transaction - pub fn hash(&self) -> &TxHash { - self.pending_transaction.hash() + pub fn hash(&self) -> TxHash { + self.pending_transaction.hash().to_alloy() } /// Returns the gas pric of this transaction pub fn gas_price(&self) -> U256 { - self.pending_transaction.transaction.gas_price() + self.pending_transaction.transaction.gas_price().to_alloy() } } @@ -143,7 +144,7 @@ impl PendingTransactions { pub fn add_transaction(&mut self, tx: PendingPoolTransaction) -> Result<(), PoolError> { assert!(!tx.is_ready(), "transaction must not be ready"); assert!( - !self.waiting_queue.contains_key(tx.transaction.hash()), + !self.waiting_queue.contains_key(&tx.transaction.hash()), "transaction is already added" ); @@ -163,13 +164,13 @@ impl PendingTransactions { // add all missing markers for marker in &tx.missing_markers { - self.required_markers.entry(marker.clone()).or_default().insert(*tx.transaction.hash()); + self.required_markers.entry(marker.clone()).or_default().insert(tx.transaction.hash()); } // also track identifying markers - self.waiting_markers.insert(tx.transaction.provides.clone(), *tx.transaction.hash()); + self.waiting_markers.insert(tx.transaction.provides.clone(), tx.transaction.hash()); // add tx to the queue - self.waiting_queue.insert(*tx.transaction.hash(), tx); + self.waiting_queue.insert(tx.transaction.hash(), tx); Ok(()) } @@ -309,7 +310,7 @@ impl TransactionsIterator { self.independent.insert(tx_ref); } else { // otherwise we're still awaiting for some deps - self.awaiting.insert(*tx_ref.transaction.hash(), (satisfied, tx_ref)); + self.awaiting.insert(tx_ref.transaction.hash(), (satisfied, tx_ref)); } } } @@ -324,7 +325,7 @@ impl Iterator for TransactionsIterator { let hash = best.transaction.hash(); let ready = - if let Some(ready) = self.all.get(hash).cloned() { ready } else { continue }; + if let Some(ready) = self.all.get(&hash).cloned() { ready } else { continue }; // Insert transactions that just got unlocked. for hash in &ready.unlocks { @@ -409,14 +410,14 @@ impl ReadyTransactions { ) -> Result>, PoolError> { assert!(tx.is_ready(), "transaction must be ready",); assert!( - !self.ready_tx.read().contains_key(tx.transaction.hash()), + !self.ready_tx.read().contains_key(&tx.transaction.hash()), "transaction already included" ); let (replaced_tx, unlocks) = self.replaced_transactions(&tx.transaction)?; let id = self.next_id(); - let hash = *tx.transaction.hash(); + let hash = tx.transaction.hash(); let mut independent = true; let mut requires_offset = 0; @@ -473,12 +474,14 @@ impl ReadyTransactions { // construct a list of unlocked transactions // also check for transactions that shouldn't be replaced because underpriced let ready = self.ready_tx.read(); - for to_remove in remove_hashes.iter().filter_map(|hash| ready.get(hash)) { + for to_remove in remove_hashes.iter().filter_map(|hash| ready.get(*hash)) { // if we're attempting to replace a transaction that provides the exact same markers // (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_remove.gas_price() { + if tx.pending_transaction.transaction.gas_price().to_alloy() <= + to_remove.gas_price() + { warn!(target: "txpool", "ready replacement transaction underpriced [{:?}]", tx.hash()); return Err(PoolError::ReplacementUnderpriced(Box::new(tx.clone()))) } else { @@ -534,7 +537,7 @@ impl ReadyTransactions { let prev_hash = self.provided_markers.get(marker)?; let tx2 = ready.get_mut(prev_hash)?; // remove hash - if let Some(idx) = tx2.unlocks.iter().position(|i| i == hash) { + if let Some(idx) = tx2.unlocks.iter().position(|i| i == &hash) { tx2.unlocks.swap_remove(idx); } if tx2.unlocks.is_empty() { @@ -566,7 +569,7 @@ impl ReadyTransactions { for marker in &tx.provides { let removed = self.provided_markers.remove(marker); assert_eq!( - removed.as_ref(), + removed, if current_marker == marker { None } else { Some(tx.hash()) }, "The pool contains exactly one transaction providing given tag; the removed transaction claims to provide that tag, so it has to be mapped to it's hash; qed" diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 9a537a5cb28f..60dce6b66956 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -4,12 +4,10 @@ use crate::{ pubsub::filter_logs, StorageInfo, }; +use alloy_primitives::TxHash; +use alloy_rpc_types::{Filter, FilteredParams, Log as AlloyLog}; use anvil_core::eth::subscription::SubscriptionId; use anvil_rpc::response::ResponseResult; -use ethers::{ - prelude::{Log as EthersLog, H256 as TxHash}, - types::{Filter, FilteredParams}, -}; use futures::{channel::mpsc::Receiver, Stream, StreamExt}; use std::{ collections::HashMap, @@ -164,14 +162,14 @@ pub struct LogsFilter { /// existing logs that matched the filter when the listener was installed /// /// They'll be returned on the first pill - pub historic: Option>, + pub historic: Option>, } // === impl LogsFilter === impl LogsFilter { /// Returns all the logs since the last time this filter was polled - pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { + pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { let mut logs = self.historic.take().unwrap_or_default(); while let Poll::Ready(Some(block)) = self.blocks.poll_next_unpin(cx) { let b = self.storage.block(block.hash); diff --git a/crates/anvil/src/genesis.rs b/crates/anvil/src/genesis.rs index 64cda289c82e..cd115c2a90c9 100644 --- a/crates/anvil/src/genesis.rs +++ b/crates/anvil/src/genesis.rs @@ -1,10 +1,8 @@ //! Bindings for geth's `genesis.json` format use crate::revm::primitives::AccountInfo; -use ethers::{ - signers::LocalWallet, - types::{serde_helpers::*, Address, Bytes, H256, U256}, -}; -use foundry_common::{errors::FsPathError, types::ToAlloy}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use ethers::{signers::LocalWallet, types::serde_helpers::*}; +use foundry_common::errors::FsPathError; use foundry_evm::revm::primitives::{Bytecode, Env, KECCAK_EMPTY, U256 as rU256}; use serde::{Deserialize, Serialize}; use std::{ @@ -38,7 +36,7 @@ pub struct Genesis { #[serde(deserialize_with = "deserialize_stringified_u64")] pub difficulty: u64, #[serde(default, skip_serializing_if = "Option::is_none")] - pub mix_hash: Option, + pub mix_hash: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub coinbase: Option
, #[serde(default)] @@ -56,12 +54,8 @@ pub struct Genesis { )] pub gas_used: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub parent_hash: Option, - #[serde( - default, - deserialize_with = "deserialize_stringified_numeric_opt", - skip_serializing_if = "Option::is_none" - )] + pub parent_hash: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub base_fee_per_gas: Option, } @@ -86,19 +80,19 @@ impl Genesis { env.cfg.chain_id = chain_id; } if let Some(timestamp) = self.timestamp { - env.block.timestamp = rU256::from(timestamp); + env.block.timestamp = U256::from(timestamp); } if let Some(base_fee) = self.base_fee_per_gas { - env.block.basefee = base_fee.to_alloy(); + env.block.basefee = base_fee; } if let Some(number) = self.number { env.block.number = rU256::from(number); } if let Some(coinbase) = self.coinbase { - env.block.coinbase = coinbase.to_alloy(); + env.block.coinbase = coinbase; } - env.block.difficulty = rU256::from(self.difficulty); - env.block.gas_limit = rU256::from(self.gas_limit); + env.block.difficulty = U256::from(self.difficulty); + env.block.gas_limit = U256::from(self.gas_limit); } /// Returns all private keys from the genesis accounts, if they exist @@ -118,8 +112,7 @@ pub struct GenesisAccount { #[serde(default, skip_serializing_if = "Option::is_none")] pub code: Option, #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub storage: HashMap, - #[serde(deserialize_with = "deserialize_stringified_numeric")] + pub storage: HashMap, pub balance: U256, #[serde( default, @@ -141,7 +134,7 @@ impl From for AccountInfo { let GenesisAccount { code, balance, nonce, .. } = acc; let code = code.map(|code| Bytecode::new_raw(code.to_vec().into())); AccountInfo { - balance: balance.to_alloy(), + balance, nonce: nonce.unwrap_or_default(), code_hash: code.as_ref().map(|code| code.hash_slow()).unwrap_or(KECCAK_EMPTY), code, @@ -169,7 +162,7 @@ pub struct Config { #[serde(default, skip_serializing_if = "Option::is_none")] pub eip150_block: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub eip150_hash: Option, + pub eip150_hash: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub eip155_block: Option, #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 4615f6ca7f28..4c478b669e3d 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -23,7 +23,13 @@ use ethers::{ signers::Signer, types::{Address, U256}, }; -use foundry_common::{ProviderBuilder, RetryProvider}; +use foundry_common::{ + provider::{ + alloy::{ProviderBuilder, RetryProvider}, + ethers::{ProviderBuilder as EthersProviderBuilder, RetryProvider as EthersRetryProvider}, + }, + types::ToEthers, +}; use foundry_evm::revm; use futures::{FutureExt, TryFutureExt}; use parking_lot::Mutex; @@ -34,7 +40,6 @@ use std::{ pin::Pin, sync::Arc, task::{Context, Poll}, - time::Duration, }; use tokio::{ runtime::Handle, @@ -271,10 +276,16 @@ impl NodeHandle { /// Constructs a [`RetryProvider`] for this handle's HTTP endpoint. pub fn http_provider(&self) -> RetryProvider { - ProviderBuilder::new(&self.http_endpoint()) + ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider") + // .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 HTTP provider") - .interval(Duration::from_millis(500)) + .expect("failed to build ethers HTTP provider") } /// Constructs a [`RetryProvider`] for this handle's WS endpoint. @@ -282,11 +293,21 @@ impl NodeHandle { 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()) @@ -304,12 +325,12 @@ impl NodeHandle { /// Native token balance of every genesis account in the genesis block pub fn genesis_balance(&self) -> U256 { - self.config.genesis_balance + self.config.genesis_balance.to_ethers() } /// Default gas price for all txs pub fn gas_price(&self) -> U256 { - self.config.get_gas_price() + self.config.get_gas_price().to_ethers() } /// Returns the shutdown signal @@ -353,7 +374,7 @@ impl Future for NodeHandle { // poll the ipc task if let Some(mut ipc) = pin.ipc_task.take() { if let Poll::Ready(res) = ipc.poll_unpin(cx) { - return Poll::Ready(res.map(|res| res.map_err(NodeError::from))) + return Poll::Ready(res.map(|res| res.map_err(NodeError::from))); } else { pin.ipc_task = Some(ipc); } @@ -361,13 +382,13 @@ impl Future for NodeHandle { // poll the node service task if let Poll::Ready(res) = pin.node_service.poll_unpin(cx) { - return Poll::Ready(res) + return Poll::Ready(res); } // poll the axum server handles for server in pin.servers.iter_mut() { if let Poll::Ready(res) = server.poll_unpin(cx) { - return Poll::Ready(res) + return Poll::Ready(res); } } diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index 0d311d810a2d..ed061567df15 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -1,17 +1,16 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, - StorageInfo, U256, + StorageInfo, }; +use alloy_primitives::{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, SubscriptionResult}, + subscription::SubscriptionId, }; use anvil_rpc::{request::Version, response::ResponseResult}; -use ethers::{ - prelude::{Log as EthersLog, H256, H256 as TxHash, U64}, - types::FilteredParams, -}; +use foundry_common::types::ToAlloy; use futures::{channel::mpsc::Receiver, ready, Stream, StreamExt}; use serde::Serialize; use std::{ @@ -26,7 +25,7 @@ pub struct LogsSubscription { pub blocks: NewBlockNotifications, pub storage: StorageInfo, pub filter: FilteredParams, - pub queued: VecDeque, + pub queued: VecDeque, pub id: SubscriptionId, } @@ -40,7 +39,7 @@ impl LogsSubscription { subscription: self.id.clone(), result: to_rpc_result(log), }; - return Poll::Ready(Some(EthSubscriptionResponse::new(params))) + return Poll::Ready(Some(EthSubscriptionResponse::new(params))); } if let Some(block) = ready!(self.blocks.poll_next_unpin(cx)) { @@ -52,16 +51,16 @@ impl LogsSubscription { // this ensures we poll the receiver until it is pending, in which case the // underlying `UnboundedReceiver` will register the new waker, see // [`futures::channel::mpsc::UnboundedReceiver::poll_next()`] - continue + continue; } self.queued.extend(logs) } } else { - return Poll::Ready(None) + return Poll::Ready(None); } if self.queued.is_empty() { - return Poll::Pending + return Poll::Pending; } } } @@ -115,10 +114,10 @@ impl EthSubscription { subscription: id.clone(), result: to_rpc_result(block), }; - return Poll::Ready(Some(EthSubscriptionResponse::new(params))) + return Poll::Ready(Some(EthSubscriptionResponse::new(params))); } } else { - return Poll::Ready(None) + return Poll::Ready(None); } } } @@ -153,21 +152,19 @@ pub fn filter_logs( block: Block, receipts: Vec, filter: &FilteredParams, -) -> Vec { +) -> Vec { /// Determines whether to add this log - fn add_log(block_hash: H256, l: &Log, block: &Block, params: &FilteredParams) -> bool { - let log = EthersLog { - address: l.address, - topics: l.topics.clone(), - data: l.data.clone(), + 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(), block_hash: None, block_number: None, transaction_hash: None, transaction_index: None, log_index: None, - transaction_log_index: None, - log_type: None, - removed: Some(false), + removed: false, }; if params.filter.is_some() { let block_number = block.header.number.as_u64(); @@ -176,7 +173,7 @@ pub fn filter_logs( !params.filter_address(&log) || !params.filter_topics(&log) { - return false + return false; } } true @@ -188,25 +185,23 @@ pub fn filter_logs( for (receipt_index, receipt) in receipts.into_iter().enumerate() { let receipt: EIP658Receipt = receipt.into(); let receipt_logs = receipt.logs; - let transaction_hash: Option = if !receipt_logs.is_empty() { - Some(block.transactions[receipt_index].hash()) + let transaction_hash: Option = if !receipt_logs.is_empty() { + Some(block.transactions[receipt_index].hash().to_alloy()) } else { None }; - for (transaction_log_index, log) in receipt_logs.into_iter().enumerate() { - if add_log(block_hash, &log, &block, filter) { - logs.push(EthersLog { - address: log.address, - topics: log.topics, - data: log.data, - block_hash: Some(block_hash), - block_number: Some(block.header.number.as_u64().into()), + for log in receipt_logs.into_iter() { + if add_log(block_hash.to_alloy(), &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())), transaction_hash, - transaction_index: Some(U64::from(receipt_index)), + transaction_index: Some(U256::from(receipt_index)), log_index: Some(U256::from(log_index)), - transaction_log_index: Some(U256::from(transaction_log_index)), - log_type: None, - removed: Some(false), + removed: false, }); } log_index += 1; diff --git a/crates/anvil/src/server/handler.rs b/crates/anvil/src/server/handler.rs index 7306d603430c..2cab0d5b7ae0 100644 --- a/crates/anvil/src/server/handler.rs +++ b/crates/anvil/src/server/handler.rs @@ -4,13 +4,13 @@ use crate::{ pubsub::{EthSubscription, LogsSubscription}, EthApi, }; -use anvil_core::eth::{ - subscription::{SubscriptionId, SubscriptionKind}, - EthPubSub, EthRequest, EthRpcCall, +use alloy_rpc_types::{ + pubsub::{Params, SubscriptionKind}, + FilteredParams, }; +use anvil_core::eth::{subscription::SubscriptionId, EthPubSub, EthRequest, EthRpcCall}; use anvil_rpc::{error::RpcError, response::ResponseResult}; use anvil_server::{PubSubContext, PubSubRpcHandler, RpcHandler}; -use ethers::types::FilteredParams; /// A `RpcHandler` that expects `EthRequest` rpc calls via http #[derive(Clone)] @@ -61,7 +61,16 @@ impl PubSubEthRpcHandler { ResponseResult::Success(canceled.into()) } EthPubSub::EthSubscribe(kind, params) => { - let params = FilteredParams::new(params.filter); + let filter = match *params { + Params::None => None, + Params::Logs(filter) => Some(*filter), + Params::Bool(_) => { + return ResponseResult::Error(RpcError::invalid_params( + "Expected params for logs subscription", + )) + } + }; + let params = FilteredParams::new(filter); let subscription = match kind { SubscriptionKind::Logs => { diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 07d8e3f96712..6d65f6fe4593 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -2,11 +2,12 @@ use anvil::{spawn, NodeConfig}; use ethers::{prelude::Middleware, types::Address}; +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.http_provider(); + let provider = handle.ethers_http_provider(); assert!(api.anvil_get_auto_mine().unwrap()); @@ -35,7 +36,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let dev_accounts = handle.dev_accounts().collect::>(); let accounts = provider.get_accounts().await.unwrap(); @@ -46,8 +47,8 @@ async fn can_get_default_dev_keys() { async fn can_set_empty_code() { let (api, _handle) = spawn(NodeConfig::test()).await; let addr = Address::random(); - api.anvil_set_code(addr, Vec::new().into()).await.unwrap(); - let code = api.get_code(addr, None).await.unwrap(); + api.anvil_set_code(addr.to_alloy(), Vec::new().into()).await.unwrap(); + let code = api.get_code(addr.to_alloy(), None).await.unwrap(); assert!(code.as_ref().is_empty()); } @@ -56,7 +57,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.http_provider(); + let provider = handle.ethers_http_provider(); assert_eq!(genesis_timestamp, provider.get_block(0).await.unwrap().unwrap().timestamp.as_u64()); } @@ -64,7 +65,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.http_provider(); + let provider = handle.ethers_http_provider(); 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 1a7c19981ba7..60a91ee76164 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -1,5 +1,6 @@ //! tests for custom anvil endpoints use crate::{abi::*, fork::fork_config}; +use alloy_rpc_types::BlockNumberOrTag; use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; use anvil_core::{ eth::EthRequest, @@ -14,6 +15,7 @@ use ethers::{ }, utils::hex, }; +use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::revm::primitives::SpecId; use std::{ str::FromStr, @@ -24,10 +26,10 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); - let gas_price = 1337u64.into(); - api.anvil_set_min_gas_price(gas_price).await.unwrap(); + let gas_price: U256 = 1337u64.into(); + api.anvil_set_min_gas_price(gas_price.to_alloy()).await.unwrap(); assert_eq!(gas_price, provider.get_gas_price().await.unwrap()); } @@ -35,12 +37,13 @@ async fn can_set_gas_price() { async fn can_set_block_gas_limit() { let (api, _) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let block_gas_limit = 1337u64.into(); - assert!(api.evm_set_block_gas_limit(block_gas_limit).unwrap()); + let block_gas_limit: U256 = 1337u64.into(); + assert!(api.evm_set_block_gas_limit(block_gas_limit.to_alloy()).unwrap()); // Mine a new block, and check the new block gas limit api.mine_one().await; - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block_gas_limit, latest_block.gas_limit); + let latest_block = + api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert_eq!(block_gas_limit.to_alloy(), latest_block.header.gas_limit); } // Ref @@ -58,31 +61,31 @@ async fn can_set_storage() { let storage_value = api.storage_at(addr, slot, None).await.unwrap(); assert_eq!(val, storage_value); - assert_eq!(val, H256::from_uint(&U256::from(12345))); + assert_eq!(val.to_ethers(), H256::from_uint(&U256::from(12345))); } #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let impersonate = Address::random(); let to = Address::random(); let val = 1337u64; let funding = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(impersonate, funding).await.unwrap(); + api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); - let balance = api.balance(impersonate, None).await.unwrap(); - assert_eq!(balance, funding); + let balance = api.balance(impersonate.to_alloy(), None).await.unwrap(); + assert_eq!(balance, funding.to_alloy()); let tx = TransactionRequest::new().from(impersonate).to(to).value(val); let res = provider.send_transaction(tx.clone(), None).await; res.unwrap_err(); - api.anvil_impersonate_account(impersonate).await.unwrap(); - assert!(api.accounts().unwrap().contains(&impersonate)); + api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); + assert!(api.accounts().unwrap().contains(&impersonate.to_alloy())); let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); assert_eq!(res.from, impersonate); @@ -93,7 +96,7 @@ async fn can_impersonate_account() { let balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance, val.into()); - api.anvil_stop_impersonating_account(impersonate).await.unwrap(); + api.anvil_stop_impersonating_account(impersonate.to_alloy()).await.unwrap(); let res = provider.send_transaction(tx, None).await; res.unwrap_err(); } @@ -101,17 +104,17 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let impersonate = Address::random(); let to = Address::random(); let val = 1337u64; let funding = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(impersonate, funding).await.unwrap(); + api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); - let balance = api.balance(impersonate, None).await.unwrap(); - assert_eq!(balance, funding); + let balance = api.balance(impersonate.to_alloy(), None).await.unwrap(); + assert_eq!(balance, funding.to_alloy()); let tx = TransactionRequest::new().from(impersonate).to(to).value(val); @@ -134,14 +137,14 @@ async fn can_auto_impersonate_account() { res.unwrap_err(); // explicitly impersonated accounts get returned by `eth_accounts` - api.anvil_impersonate_account(impersonate).await.unwrap(); - assert!(api.accounts().unwrap().contains(&impersonate)); + api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); + assert!(api.accounts().unwrap().contains(&impersonate.to_alloy())); } #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_contract() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let provider = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -153,10 +156,12 @@ async fn can_impersonate_contract() { let to = Address::random(); let val = 1337u64; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); // fund the impersonated account - api.anvil_set_balance(impersonate, U256::from(1e18 as u64)).await.unwrap(); + api.anvil_set_balance(impersonate.to_alloy(), U256::from(1e18 as u64).to_alloy()) + .await + .unwrap(); let tx = TransactionRequest::new().from(impersonate).to(to).value(val); @@ -166,7 +171,7 @@ async fn can_impersonate_contract() { let greeting = greeter_contract.greet().call().await.unwrap(); assert_eq!("Hello World!", greeting); - api.anvil_impersonate_account(impersonate).await.unwrap(); + api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); assert_eq!(res.from, impersonate); @@ -174,7 +179,7 @@ async fn can_impersonate_contract() { let balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance, val.into()); - api.anvil_stop_impersonating_account(impersonate).await.unwrap(); + api.anvil_stop_impersonating_account(impersonate.to_alloy()).await.unwrap(); let res = provider.send_transaction(tx, None).await; res.unwrap_err(); @@ -185,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.http_provider(); + let provider = handle.ethers_http_provider(); // let safe: Address = "0xA063Cb7CFd8E57c30c788A0572CBbf2129ae56B6".parse().unwrap(); @@ -193,19 +198,19 @@ async fn can_impersonate_gnosis_safe() { let code = provider.get_code(safe, None).await.unwrap(); assert!(!code.is_empty()); - api.anvil_impersonate_account(safe).await.unwrap(); + api.anvil_impersonate_account(safe.to_alloy()).await.unwrap(); let code = provider.get_code(safe, None).await.unwrap(); assert!(!code.is_empty()); let balance = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(safe, balance).await.unwrap(); + api.anvil_set_balance(safe.to_alloy(), balance.to_alloy()).await.unwrap(); let on_chain_balance = provider.get_balance(safe, None).await.unwrap(); assert_eq!(on_chain_balance, balance); - api.anvil_stop_impersonating_account(safe).await.unwrap(); + api.anvil_stop_impersonating_account(safe.to_alloy()).await.unwrap(); let code = provider.get_code(safe, None).await.unwrap(); // code is added back after stop impersonating @@ -215,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.http_provider(); + let provider = handle.ethers_http_provider(); let impersonate0 = Address::random(); let impersonate1 = Address::random(); @@ -224,13 +229,13 @@ async fn can_impersonate_multiple_account() { let val = 1337u64; let funding = U256::from(1e18 as u64); // fund the impersonated accounts - api.anvil_set_balance(impersonate0, funding).await.unwrap(); - api.anvil_set_balance(impersonate1, funding).await.unwrap(); + api.anvil_set_balance(impersonate0.to_alloy(), funding.to_alloy()).await.unwrap(); + api.anvil_set_balance(impersonate1.to_alloy(), funding.to_alloy()).await.unwrap(); let tx = TransactionRequest::new().from(impersonate0).to(to).value(val); - api.anvil_impersonate_account(impersonate0).await.unwrap(); - api.anvil_impersonate_account(impersonate1).await.unwrap(); + api.anvil_impersonate_account(impersonate0.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(impersonate1.to_alloy()).await.unwrap(); let res0 = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); assert_eq!(res0.from, impersonate0); @@ -262,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.http_provider(); + let provider = handle.ethers_http_provider(); let start_num = provider.get_block_number().await.unwrap(); @@ -276,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.http_provider(); + let provider = handle.ethers_http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -303,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.http_provider(); + let provider = handle.ethers_http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -327,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.http_provider(); + let provider = handle.ethers_http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -347,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.http_provider(); + let provider = handle.ethers_http_provider(); api.evm_mine(None).await.unwrap(); let interval = 10; @@ -398,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.http_provider()); + let provider = Arc::new(handle.ethers_http_provider()); let busd_addr: Address = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56".parse().unwrap(); let idx: U256 = @@ -406,9 +411,9 @@ async fn test_can_set_storage_bsc_fork() { let value: H256 = "0x0000000000000000000000000000000000000000000000000000000000003039".parse().unwrap(); - api.anvil_set_storage_at(busd_addr, idx, value).await.unwrap(); - let storage = api.storage_at(busd_addr, idx, None).await.unwrap(); - assert_eq!(storage, value); + api.anvil_set_storage_at(busd_addr.to_alloy(), idx.to_alloy(), value.to_alloy()).await.unwrap(); + let storage = api.storage_at(busd_addr.to_alloy(), idx.to_alloy(), None).await.unwrap(); + assert_eq!(storage.to_ethers(), value); let input = hex::decode("70a082310000000000000000000000000000000000000000000000000000000000000000") @@ -427,22 +432,22 @@ async fn can_get_node_info() { let node_info = api.anvil_node_info().await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let block_number = provider.get_block_number().await.unwrap(); let block = provider.get_block(block_number).await.unwrap().unwrap(); let expected_node_info = NodeInfo { - current_block_number: U64([0]), + current_block_number: U64([0]).to_alloy(), current_block_timestamp: 1, - current_block_hash: block.hash.unwrap(), + current_block_hash: block.hash.unwrap().to_alloy(), hard_fork: SpecId::SHANGHAI, transaction_order: "fees".to_owned(), environment: NodeEnvironment { - base_fee: U256::from_str("0x3b9aca00").unwrap(), + base_fee: U256::from_str("0x3b9aca00").unwrap().to_alloy(), chain_id: 0x7a69, - gas_limit: U256::from_str("0x1c9c380").unwrap(), - gas_price: U256::from_str("0x77359400").unwrap(), + gas_limit: U256::from_str("0x1c9c380").unwrap().to_alloy(), + gas_price: U256::from_str("0x77359400").unwrap().to_alloy(), }, fork_config: NodeForkConfig { fork_url: None, @@ -460,14 +465,14 @@ async fn can_get_metadata() { let metadata = api.anvil_metadata().await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let block_number = provider.get_block_number().await.unwrap().as_u64(); let chain_id = provider.get_chainid().await.unwrap().as_u64(); let block = provider.get_block(block_number).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { - latest_block_hash: block.hash.unwrap(), + latest_block_hash: block.hash.unwrap().to_alloy(), latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION, @@ -483,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.http_provider()); + let provider = Arc::new(handle.ethers_http_provider()); let metadata = api.anvil_metadata().await.unwrap(); @@ -492,7 +497,7 @@ async fn can_get_metadata_on_fork() { let block = provider.get_block(block_number).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { - latest_block_hash: block.hash.unwrap(), + latest_block_hash: block.hash.unwrap().to_alloy(), latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION, @@ -500,7 +505,7 @@ async fn can_get_metadata_on_fork() { forked_network: Some(ForkedNetwork { chain_id, fork_block_number: block_number, - fork_block_hash: block.hash.unwrap(), + fork_block_hash: block.hash.unwrap().to_alloy(), }), snapshots: Default::default(), }; @@ -527,11 +532,11 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); // set the base fee let new_base_fee = U256::from(1_000); - api.anvil_set_next_block_base_fee_per_gas(new_base_fee).await.unwrap(); + api.anvil_set_next_block_base_fee_per_gas(new_base_fee.to_alloy()).await.unwrap(); // send a EIP-1559 transaction let tx = @@ -558,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.http_provider(); + let provider = handle.ethers_http_provider(); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, U256::from(31337)); @@ -576,17 +581,17 @@ async fn test_fork_revert_next_block_timestamp() { // Mine a new block, and check the new block gas limit api.mine_one().await; - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); let snapshot_id = api.evm_snapshot().await.unwrap(); api.mine_one().await; api.evm_revert(snapshot_id).await.unwrap(); - let block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); assert_eq!(block, latest_block); api.mine_one().await; - let block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - assert!(block.timestamp > latest_block.timestamp); + let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert!(block.header.timestamp > latest_block.header.timestamp); } // test that after a snapshot revert, the env block is reset @@ -594,11 +599,11 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); // Mine a new block, and check the new block gas limit api.mine_one().await; - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); let snapshot_id = api.evm_snapshot().await.unwrap(); api.mine_one().await; @@ -609,11 +614,20 @@ async fn test_fork_revert_call_latest_block_timestamp() { provider.into(), ); - assert_eq!(multicall.get_current_block_timestamp().await.unwrap(), latest_block.timestamp); - assert_eq!(multicall.get_current_block_difficulty().await.unwrap(), latest_block.difficulty); - assert_eq!(multicall.get_current_block_gas_limit().await.unwrap(), latest_block.gas_limit); + assert_eq!( + multicall.get_current_block_timestamp().await.unwrap(), + latest_block.header.timestamp.to_ethers() + ); + assert_eq!( + multicall.get_current_block_difficulty().await.unwrap(), + latest_block.header.difficulty.to_ethers() + ); + assert_eq!( + multicall.get_current_block_gas_limit().await.unwrap(), + latest_block.header.gas_limit.to_ethers() + ); assert_eq!( multicall.get_current_block_coinbase().await.unwrap(), - latest_block.author.unwrap_or_default() + latest_block.header.miner.to_ethers() ); } diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 32c6fcf2ba6c..7a6a64ae379a 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -1,11 +1,13 @@ //! general eth api tests use crate::abi::{MulticallContract, SimpleStorage}; +use alloy_primitives::U256 as rU256; +use alloy_rpc_types::{CallInput, CallRequest}; use anvil::{ eth::{api::CLIENT_VERSION, EthApi}, spawn, NodeConfig, CHAIN_ID, }; -use anvil_core::eth::{state::AccountOverride, transaction::EthTransactionRequest}; +use anvil_core::eth::{state::AccountOverride, transaction::to_alloy_state_override}; use ethers::{ abi::{Address, Tokenizable}, prelude::{builders::ContractCall, decode_function_data, Middleware, SignerMiddleware}, @@ -13,6 +15,7 @@ use ethers::{ types::{Block, BlockNumber, Chain, Transaction, TransactionRequest, H256, U256}, utils::get_contract_address, }; +use foundry_common::types::ToAlloy; use std::{collections::HashMap, sync::Arc, time::Duration}; #[tokio::test(flavor = "multi_thread")] @@ -20,18 +23,18 @@ async fn can_get_block_number() { let (api, handle) = spawn(NodeConfig::test()).await; let block_num = api.block_number().unwrap(); - assert_eq!(block_num, U256::zero()); + assert_eq!(block_num, U256::zero().to_alloy()); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, block_num.as_u64().into()); + assert_eq!(num, block_num.to::().into()); } #[tokio::test(flavor = "multi_thread")] async fn can_dev_get_balance() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { @@ -43,7 +46,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.http_provider(); + let provider = handle.ethers_http_provider(); let _ = provider.get_gas_price().await.unwrap(); } @@ -51,7 +54,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.http_provider(); + let provider = handle.ethers_http_provider(); let _ = provider.get_accounts().await.unwrap(); } @@ -59,7 +62,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.http_provider(); + let provider = handle.ethers_http_provider(); let version = provider.client_version().await.unwrap(); assert_eq!(CLIENT_VERSION, version); @@ -68,7 +71,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.http_provider(); + let provider = handle.ethers_http_provider(); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, CHAIN_ID.into()); @@ -77,7 +80,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.http_provider(); + let provider = handle.ethers_http_provider(); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id, Chain::Goerli.into()); @@ -97,7 +100,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); let to = accounts[1].address(); @@ -119,7 +122,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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let block = provider.get_block(BlockNumber::Pending).await.unwrap().unwrap(); @@ -153,7 +156,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let num = provider.get_block_number().await.unwrap(); assert_eq!(num.as_u64(), 0u64); @@ -182,36 +185,41 @@ async fn can_call_on_pending_block() { let accounts: Vec
= handle.dev_wallets().map(|w| w.address()).collect(); for i in 1..10 { - api.anvil_set_coinbase(accounts[i % accounts.len()]).await.unwrap(); - api.evm_set_block_gas_limit((30_000_000 + i).into()).unwrap(); + api.anvil_set_coinbase(accounts[i % accounts.len()].to_alloy()).await.unwrap(); + api.evm_set_block_gas_limit(rU256::from(30_000_000 + i)).unwrap(); - api.anvil_mine(Some(1.into()), None).await.unwrap(); + api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); tokio::time::sleep(Duration::from_secs(1)).await; } // Ensure that the right header values are set when calling a past block - for block_number in 1..(api.block_number().unwrap().as_usize() + 1) { - let block_number = BlockNumber::Number(block_number.into()); - let block = api.block_by_number(block_number).await.unwrap().unwrap(); + for block_number in 1..(api.block_number().unwrap().to::() + 1) { + let block_number_alloy = alloy_rpc_types::BlockNumberOrTag::Number(block_number as u64); + let block_number_ethers = BlockNumber::Number((block_number as u64).into()); + let block = api.block_by_number(block_number_alloy).await.unwrap().unwrap(); let block_timestamp = pending_contract .get_current_block_timestamp() - .block(block_number) + .block(block_number_ethers) .call() .await .unwrap(); - assert_eq!(block.timestamp, block_timestamp); + assert_eq!(block.header.timestamp, block_timestamp.to_alloy()); let block_gas_limit = pending_contract .get_current_block_gas_limit() - .block(block_number) + .block(block_number_ethers) .call() .await .unwrap(); - assert_eq!(block.gas_limit, block_gas_limit); + assert_eq!(block.header.gas_limit, block_gas_limit.to_alloy()); - let block_coinbase = - pending_contract.get_current_block_coinbase().block(block_number).call().await.unwrap(); - assert_eq!(block.author.unwrap(), block_coinbase); + let block_coinbase = pending_contract + .get_current_block_coinbase() + .block(block_number_ethers) + .call() + .await + .unwrap(); + assert_eq!(block.header.miner, block_coinbase.to_alloy()); } } @@ -226,13 +234,13 @@ where { let result = api .call( - EthTransactionRequest { - data: call.tx.data().cloned(), - to: Some(to), + CallRequest { + input: CallInput::maybe_input(call.tx.data().cloned().map(|b| b.0.into())), + to: Some(to.to_alloy()), ..Default::default() }, None, - Some(overrides), + Some(to_alloy_state_override(overrides)), ) .await .unwrap(); @@ -242,7 +250,7 @@ where #[tokio::test(flavor = "multi_thread")] async fn can_call_with_state_override() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); api.anvil_set_auto_mine(true).await.unwrap(); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index a128483a4225..f3efb4475fab 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1,8 +1,10 @@ //! various fork related test use crate::{abi::*, utils}; +use alloy_primitives::U256 as rU256; +use alloy_rpc_types::{BlockNumberOrTag, CallRequest}; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; -use anvil_core::{eth::transaction::EthTransactionRequest, types::Forking}; +use anvil_core::types::Forking; use ethers::{ core::rand, prelude::{Bytes, LocalWallet, Middleware, SignerMiddleware}, @@ -14,7 +16,8 @@ use ethers::{ }, }; use foundry_common::{ - get_http_provider, rpc, + provider::ethers::get_http_provider, + rpc, rpc::next_http_rpc_endpoint, types::{ToAlloy, ToEthers}, }; @@ -67,18 +70,18 @@ async fn test_spawn_fork() { assert!(api.is_fork()); let head = api.block_number().unwrap(); - assert_eq!(head, BLOCK_NUMBER.into()) + assert_eq!(head, rU256::from(BLOCK_NUMBER)) } #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_balance() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); for _ in 0..10 { let addr = Address::random(); - let balance = api.balance(addr, None).await.unwrap(); + let balance = api.balance(addr.to_alloy(), None).await.unwrap(); let provider_balance = provider.get_balance(addr, None).await.unwrap(); - assert_eq!(balance, provider_balance) + assert_eq!(balance, provider_balance.to_alloy()) } } @@ -86,7 +89,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.http_provider(); + let provider = handle.ethers_http_provider(); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); @@ -110,7 +113,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.http_provider(); + let provider = handle.ethers_http_provider(); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); @@ -129,23 +132,23 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); for _ in 0..10 { let addr = Address::random(); - let code = api.get_code(addr, None).await.unwrap(); + let code = api.get_code(addr.to_alloy(), None).await.unwrap(); let provider_code = provider.get_code(addr, None).await.unwrap(); - assert_eq!(code, provider_code) + assert_eq!(code, provider_code.to_alloy()) } for address in utils::contract_addresses(Chain::Mainnet) { let prev_code = api - .get_code(address, Some(BlockNumber::Number((BLOCK_NUMBER - 10).into()).into())) + .get_code(address.to_alloy(), Some(BlockNumberOrTag::Number(BLOCK_NUMBER - 10).into())) .await .unwrap(); - let code = api.get_code(address, None).await.unwrap(); + let code = api.get_code(address.to_alloy(), None).await.unwrap(); let provider_code = provider.get_code(address, None).await.unwrap(); assert_eq!(code, prev_code); - assert_eq!(code, provider_code); + assert_eq!(code, provider_code.to_alloy()); assert!(!code.as_ref().is_empty()); } } @@ -153,35 +156,36 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); for _ in 0..10 { let addr = Address::random(); - let api_nonce = api.transaction_count(addr, None).await.unwrap(); + let api_nonce = api.transaction_count(addr.to_alloy(), None).await.unwrap(); let provider_nonce = provider.get_transaction_count(addr, None).await.unwrap(); - assert_eq!(api_nonce, provider_nonce); + assert_eq!(api_nonce, provider_nonce.to_alloy()); } let addr = Config::DEFAULT_SENDER; - let api_nonce = api.transaction_count(addr.to_ethers(), None).await.unwrap(); + let api_nonce = api.transaction_count(addr, None).await.unwrap(); let provider_nonce = provider.get_transaction_count(addr.to_ethers(), None).await.unwrap(); - assert_eq!(api_nonce, provider_nonce); + assert_eq!(api_nonce, provider_nonce.to_alloy()); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_fee_history() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let count = 10u64; - let _history = api.fee_history(count.into(), BlockNumber::Latest, vec![]).await.unwrap(); + let _history = + api.fee_history(rU256::from(count), BlockNumberOrTag::Latest, vec![]).await.unwrap(); let _provider_history = provider.fee_history(count, BlockNumber::Latest, &[]).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_reset() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -229,7 +233,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.http_provider(); + let provider = handle.ethers_http_provider(); let dead_addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); @@ -256,7 +260,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.http_provider(); + let provider = handle.ethers_http_provider(); let snapshot = api.evm_snapshot().await.unwrap(); @@ -292,7 +296,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.http_provider(); + let provider = handle.ethers_http_provider(); let snapshot = api.evm_snapshot().await.unwrap(); @@ -338,7 +342,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); // create a snapshot let snapshot = api.evm_snapshot().await.unwrap(); @@ -390,14 +394,14 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); let remote_balance = provider.get_balance(addr, None).await.unwrap(); assert_eq!(remote_balance, 12556104082473169733500u128.into()); - api.anvil_set_balance(addr, 1337u64.into()).await.unwrap(); + api.anvil_set_balance(addr.to_alloy(), rU256::from(1337u64)).await.unwrap(); let balance = provider.get_balance(addr, None).await.unwrap(); assert_eq!(balance, 1337u64.into()); @@ -419,7 +423,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -444,17 +448,23 @@ 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.http_provider(); - let origin_nonce = 1u64.into(); - origin_api.anvil_set_nonce(account, origin_nonce).await.unwrap(); + let origin_provider = origin_handle.ethers_http_provider(); + let origin_nonce = rU256::from(1u64); + origin_api.anvil_set_nonce(account.to_alloy(), origin_nonce).await.unwrap(); - assert_eq!(origin_nonce, origin_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce, + origin_provider.get_transaction_count(account, None).await.unwrap().to_alloy() + ); let (fork_api, fork_handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await; - let fork_provider = fork_handle.http_provider(); - assert_eq!(origin_nonce, fork_provider.get_transaction_count(account, None).await.unwrap()); + let fork_provider = fork_handle.ethers_http_provider(); + assert_eq!( + origin_nonce, + fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() + ); let to = Address::random(); let to_balance = fork_provider.get_balance(to, None).await.unwrap(); @@ -462,19 +472,25 @@ async fn can_reset_properly() { let tx = fork_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); // nonce incremented by 1 - assert_eq!(origin_nonce + 1, fork_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce + rU256::from(1), + fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() + ); // 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()); + assert_eq!( + origin_nonce, + fork_provider.get_transaction_count(account, None).await.unwrap().to_alloy() + ); // balance is reset assert_eq!(to_balance, fork_provider.get_balance(to, None).await.unwrap()); // tx does not exist anymore - assert!(fork_provider.get_transaction(tx.transaction_hash).await.unwrap().is_none()) + assert!(fork_provider.get_transaction(tx.transaction_hash).await.is_err()) } #[tokio::test(flavor = "multi_thread")] @@ -482,7 +498,7 @@ async fn test_fork_timestamp() { let start = std::time::Instant::now(); let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); @@ -494,10 +510,10 @@ async fn test_fork_timestamp() { let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); - let elapsed = start.elapsed().as_secs(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let elapsed = start.elapsed().as_secs() + 1; + // ensure the diff between the new mined block and the original block is within the elapsed time let diff = block.timestamp - BLOCK_TIMESTAMP; assert!(diff <= elapsed.into(), "diff={diff}, elapsed={elapsed}"); @@ -557,9 +573,9 @@ async fn test_fork_can_send_tx() { let wallet = LocalWallet::new(&mut rand::thread_rng()); - api.anvil_set_balance(wallet.address(), U256::from(1e18 as u64)).await.unwrap(); + api.anvil_set_balance(wallet.address().to_alloy(), rU256::from(1e18 as u64)).await.unwrap(); - let provider = SignerMiddleware::new(handle.http_provider(), wallet); + let provider = SignerMiddleware::new(handle.ethers_http_provider(), wallet); let addr = Address::random(); let val = 1337u64; @@ -585,9 +601,9 @@ async fn test_fork_nft_set_approve_all() { // create and fund a random wallet let wallet = LocalWallet::new(&mut rand::thread_rng()); - api.anvil_set_balance(wallet.address(), U256::from(1000e18 as u64)).await.unwrap(); + api.anvil_set_balance(wallet.address().to_alloy(), rU256::from(1000e18 as u64)).await.unwrap(); - let provider = Arc::new(SignerMiddleware::new(handle.http_provider(), wallet.clone())); + let provider = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet.clone())); // pick a random nft let nouns_addr: Address = "0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03".parse().unwrap(); @@ -603,14 +619,16 @@ async fn test_fork_nft_set_approve_all() { let tx = approval.send().await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); + let real_owner = real_owner.to_alloy(); + // transfer: impersonate real owner and transfer nft api.anvil_impersonate_account(real_owner).await.unwrap(); - api.anvil_set_balance(real_owner, U256::from(10000e18 as u64)).await.unwrap(); + api.anvil_set_balance(real_owner, rU256::from(10000e18 as u64)).await.unwrap(); - let call = nouns.transfer_from(real_owner, wallet.address(), token_id); + let call = nouns.transfer_from(real_owner.to_ethers(), wallet.address(), token_id); let mut tx: TypedTransaction = call.tx; - tx.set_from(real_owner); + tx.set_from(real_owner.to_ethers()); provider.fill_transaction(&mut tx, None).await.unwrap(); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); @@ -639,7 +657,7 @@ async fn test_fork_with_custom_chain_id() { let config_chain_id = handle.config().chain_id; // check that the chainIds are the same - assert_eq!(eth_chain_id.unwrap().unwrap().as_u64(), 3145u64); + assert_eq!(eth_chain_id.unwrap().unwrap().to::(), 3145u64); assert_eq!(txn_chain_id, 3145u64); assert_eq!(config_chain_id, Some(3145u64)); } @@ -657,9 +675,9 @@ async fn test_fork_can_send_opensea_tx() { let sender: Address = "0x8fdbae54b6d9f3fc2c649e3dd4602961967fd42f".parse().unwrap(); // transfer: impersonate real sender - api.anvil_impersonate_account(sender).await.unwrap(); + api.anvil_impersonate_account(sender.to_alloy()).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let input: Bytes = "0xfb0f3ee1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ff2e795f5000000000000000000000000000023f28ae3e9756ba982a6290f9081b6a84900b758000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c0000000000000000000000000003235b597a78eabcb08ffcb4d97411073211dbcb0000000000000000000000000000000000000000000000000000000000000e72000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062ad47c20000000000000000000000000000000000000000000000000000000062d43104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df44e65d2a2cf40000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000008de9c5a032463c561423387a9648c5c7bcc5bc900000000000000000000000000000000000000000000000000005543df729c0000000000000000000000000006eb234847a9e3a546539aac57a071c01dc3f398600000000000000000000000000000000000000000000000000000000000000416d39b5352353a22cf2d44faa696c2089b03137a13b5acfee0366306f2678fede043bc8c7e422f6f13a3453295a4a063dac7ee6216ab7bade299690afc77397a51c00000000000000000000000000000000000000000000000000000000000000".parse().unwrap(); let to: Address = "0x00000000006c3852cbef3e08e8df289169ede581".parse().unwrap(); @@ -682,9 +700,9 @@ async fn test_fork_base_fee() { let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); - api.anvil_set_next_block_base_fee_per_gas(U256::zero()).await.unwrap(); + api.anvil_set_next_block_base_fee_per_gas(rU256::ZERO).await.unwrap(); let addr = Address::random(); let val = 1337u64; @@ -697,7 +715,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.http_provider(); + let provider = handle.ethers_http_provider(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); // @@ -720,7 +738,7 @@ async fn test_reset_fork_on_new_blocks() { ) .await; - let anvil_provider = handle.http_provider(); + let anvil_provider = handle.ethers_http_provider(); let endpoint = next_http_rpc_endpoint(); let provider = Arc::new(get_http_provider(&endpoint).interval(Duration::from_secs(2))); @@ -757,25 +775,29 @@ async fn test_fork_call() { let res1 = api .call( - EthTransactionRequest { to: Some(to), data: Some(input), ..Default::default() }, + CallRequest { + to: Some(to.to_alloy()), + input: input.to_alloy().into(), + ..Default::default() + }, None, None, ) .await .unwrap(); - assert_eq!(res0, res1); + assert_eq!(res0, res1.to_ethers()); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_block_timestamp() { let (api, _) = spawn(fork_config()).await; - let initial_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - api.anvil_mine(Some(1.into()), None).await.unwrap(); - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert!(initial_block.timestamp.as_u64() < latest_block.timestamp.as_u64()); + assert!(initial_block.header.timestamp.to::() < latest_block.header.timestamp.to::()); } #[tokio::test(flavor = "multi_thread")] @@ -783,33 +805,36 @@ async fn test_fork_snapshot_block_timestamp() { let (api, _) = spawn(fork_config()).await; let snapshot_id = api.evm_snapshot().await.unwrap(); - api.anvil_mine(Some(1.into()), None).await.unwrap(); - let initial_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); api.evm_revert(snapshot_id).await.unwrap(); - api.evm_set_next_block_timestamp(initial_block.timestamp.as_u64()).unwrap(); - api.anvil_mine(Some(1.into()), None).await.unwrap(); - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - - assert_eq!(initial_block.timestamp.as_u64(), latest_block.timestamp.as_u64()); + api.evm_set_next_block_timestamp(initial_block.header.timestamp.to::()).unwrap(); + api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + + assert_eq!( + initial_block.header.timestamp.to::(), + latest_block.header.timestamp.to::() + ); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_uncles_fetch() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); // Block on ETH mainnet with 2 uncles let block_with_uncles = 190u64; let block = - api.block_by_number(BlockNumber::Number(block_with_uncles.into())).await.unwrap().unwrap(); + api.block_by_number(BlockNumberOrTag::Number(block_with_uncles)).await.unwrap().unwrap(); assert_eq!(block.uncles.len(), 2); let count = provider.get_uncle_count(block_with_uncles).await.unwrap(); assert_eq!(count.as_usize(), block.uncles.len()); - let count = provider.get_uncle_count(block.hash.unwrap()).await.unwrap(); + let count = provider.get_uncle_count(block.header.hash.unwrap().to_ethers()).await.unwrap(); assert_eq!(count.as_usize(), block.uncles.len()); for (uncle_idx, uncle_hash) in block.uncles.iter().enumerate() { @@ -819,22 +844,22 @@ async fn test_fork_uncles_fetch() { .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.hash.unwrap()); + assert_eq!(*uncle_hash, uncle.hash.unwrap().to_alloy()); // Try with block hash let uncle = provider - .get_uncle(block.hash.unwrap(), (uncle_idx as u64).into()) + .get_uncle(block.header.hash.unwrap().to_ethers(), (uncle_idx as u64).into()) .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.hash.unwrap()); + assert_eq!(*uncle_hash, uncle.hash.unwrap().to_alloy()); } } #[tokio::test(flavor = "multi_thread")] async fn test_fork_block_transaction_count() { let (api, handle) = spawn(fork_config()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let sender = accounts[0].address(); @@ -842,36 +867,39 @@ 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).await.unwrap(); + api.anvil_impersonate_account(sender.to_alloy()).await.unwrap(); let tx = TransactionRequest::new().from(sender).value(42u64).gas(100_000); provider.send_transaction(tx, None).await.unwrap(); let pending_txs = - api.block_transaction_count_by_number(BlockNumber::Pending).await.unwrap().unwrap(); - assert_eq!(pending_txs.as_usize(), 1); + api.block_transaction_count_by_number(BlockNumberOrTag::Pending).await.unwrap().unwrap(); + assert_eq!(pending_txs.to::(), 1); // mine a new block api.anvil_mine(None, None).await.unwrap(); let pending_txs = - api.block_transaction_count_by_number(BlockNumber::Pending).await.unwrap().unwrap(); - assert_eq!(pending_txs.as_usize(), 0); + api.block_transaction_count_by_number(BlockNumberOrTag::Pending).await.unwrap().unwrap(); + assert_eq!(pending_txs.to::(), 0); let latest_txs = - api.block_transaction_count_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(latest_txs.as_usize(), 1); - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); - let latest_txs = - api.block_transaction_count_by_hash(latest_block.hash.unwrap()).await.unwrap().unwrap(); - assert_eq!(latest_txs.as_usize(), 1); + api.block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert_eq!(latest_txs.to::(), 1); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + let latest_txs = api + .block_transaction_count_by_hash(latest_block.header.hash.unwrap()) + .await + .unwrap() + .unwrap(); + assert_eq!(latest_txs.to::(), 1); // check txs count on an older block: 420000 has 3 txs on mainnet let count_txs = api - .block_transaction_count_by_number(BlockNumber::Number(420000.into())) + .block_transaction_count_by_number(BlockNumberOrTag::Number(420000)) .await .unwrap() .unwrap(); - assert_eq!(count_txs.as_usize(), 3); + assert_eq!(count_txs.to::(), 3); let count_txs = api .block_transaction_count_by_hash( "0xb3b0e3e0c64e23fb7f1ccfd29245ae423d2f6f1b269b63b70ff882a983ce317c".parse().unwrap(), @@ -879,28 +907,28 @@ async fn test_fork_block_transaction_count() { .await .unwrap() .unwrap(); - assert_eq!(count_txs.as_usize(), 3); + assert_eq!(count_txs.to::(), 3); } // #[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.http_provider(); + let provider = handle.ethers_http_provider(); let token_holder: Address = "0x2f0b23f53734252bda2277357e97e1517d6b042a".parse().unwrap(); let to = Address::random(); let val = 1337u64; // fund the impersonated account - api.anvil_set_balance(token_holder, U256::from(1e18 as u64)).await.unwrap(); + api.anvil_set_balance(token_holder.to_alloy(), rU256::from(1e18 as u64)).await.unwrap(); let tx = TransactionRequest::new().from(token_holder).to(to).value(val); let res = provider.send_transaction(tx.clone(), None).await; res.unwrap_err(); - api.anvil_impersonate_account(token_holder).await.unwrap(); + api.anvil_impersonate_account(token_holder.to_alloy()).await.unwrap(); let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); assert_eq!(res.from, token_holder); @@ -909,7 +937,7 @@ async fn can_impersonate_in_fork() { let balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance, val.into()); - api.anvil_stop_impersonating_account(token_holder).await.unwrap(); + api.anvil_stop_impersonating_account(token_holder.to_alloy()).await.unwrap(); let res = provider.send_transaction(tx, None).await; res.unwrap_err(); } @@ -922,7 +950,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.http_provider(); + let provider = handle.ethers_http_provider(); let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); assert_eq!(block.total_difficulty, Some(total_difficulty)); assert_eq!(block.difficulty, difficulty); @@ -970,7 +998,7 @@ async fn can_override_fork_chain_id() { .with_chain_id(Some(chain_id_override)), ) .await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -990,7 +1018,7 @@ async fn can_override_fork_chain_id() { let greeting = greeter_contract.greet().call().await.unwrap(); assert_eq!("Hello World!", greeting); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let chain_id = provider.get_chainid().await.unwrap(); assert_eq!(chain_id.as_u64(), chain_id_override); } @@ -1005,7 +1033,7 @@ async fn test_fork_reset_moonbeam() { .with_fork_block_number(None::), ) .await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -1034,10 +1062,10 @@ async fn test_fork_reset_basefee() { let (api, _handle) = spawn(fork_config().with_fork_block_number(Some(18835000u64))).await; api.mine_one().await; - let latest = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of +1 block: - assert_eq!(latest.base_fee_per_gas.unwrap(), 59455969592u64.into()); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59455969592u64)); // now reset to block 18835000 -1 api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(18835000u64 - 1) })) @@ -1045,8 +1073,8 @@ async fn test_fork_reset_basefee() { .unwrap(); api.mine_one().await; - let latest = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of the forked block: - assert_eq!(latest.base_fee_per_gas.unwrap(), 59017001138u64.into()); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59017001138u64)); } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index 27c6055be3ee..bf1c91c685ce 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,5 +1,6 @@ //! Gas related tests +use alloy_primitives::U256; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; use ethers::{ prelude::Middleware, @@ -8,16 +9,19 @@ use ethers::{ TransactionRequest, }, }; +use foundry_common::types::ToAlloy; const GAS_TRANSFER: u64 = 21_000u64; #[tokio::test(flavor = "multi_thread")] async fn test_basefee_full_block() { let (_api, handle) = spawn( - NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE)).with_gas_limit(Some(GAS_TRANSFER)), + NodeConfig::test() + .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) + .with_gas_limit(Some(GAS_TRANSFER.to_alloy())), ) .await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let base_fee = @@ -36,11 +40,11 @@ async fn test_basefee_full_block() { async fn test_basefee_half_block() { let (_api, handle) = spawn( NodeConfig::test() - .with_base_fee(Some(INITIAL_BASE_FEE)) - .with_gas_limit(Some(GAS_TRANSFER * 2)), + .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) + .with_gas_limit(Some(GAS_TRANSFER.to_alloy() * U256::from(2))), ) .await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); 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); @@ -53,9 +57,10 @@ async fn test_basefee_half_block() { } #[tokio::test(flavor = "multi_thread")] async fn test_basefee_empty_block() { - let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE))).await; + let (api, handle) = + spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE.to_alloy()))).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let base_fee = @@ -74,8 +79,8 @@ async fn test_basefee_empty_block() { #[tokio::test(flavor = "multi_thread")] async fn test_respect_base_fee() { let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; - let provider = handle.http_provider(); + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let provider = handle.ethers_http_provider(); let mut tx = TypedTransaction::default(); tx.set_value(100u64); tx.set_to(Address::random()); @@ -94,8 +99,8 @@ async fn test_respect_base_fee() { #[tokio::test(flavor = "multi_thread")] async fn test_tip_above_fee_cap() { let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; - let provider = handle.http_provider(); + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let provider = handle.ethers_http_provider(); let tx = TypedTransaction::Eip1559( Eip1559TransactionRequest::new() .max_fee_per_gas(base_fee) diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index 6c028599fc14..2a0c2e8eed24 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -1,7 +1,10 @@ //! genesis.json tests +use std::str::FromStr; + +use alloy_primitives::{Address, U256, U64}; +use alloy_providers::provider::TempProvider; use anvil::{genesis::Genesis, spawn, NodeConfig}; -use ethers::{abi::Address, prelude::Middleware, types::U256}; #[tokio::test(flavor = "multi_thread")] async fn can_apply_genesis() { @@ -37,11 +40,11 @@ async fn can_apply_genesis() { let provider = handle.http_provider(); - assert_eq!(provider.get_chainid().await.unwrap(), 19763u64.into()); + assert_eq!(provider.get_chain_id().await.unwrap(), U64::from(19763u64)); - let addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); + let addr: Address = Address::from_str("71562b71999873db5b286df957af199ec94617f7").unwrap(); let balance = provider.get_balance(addr, None).await.unwrap(); - let expected: U256 = "ffffffffffffffffffffffffff".parse().unwrap(); + let expected: U256 = U256::from_str_radix("ffffffffffffffffffffffffff", 16).unwrap(); assert_eq!(balance, expected); } diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index b25bbba5249f..5c79d29ab450 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -1,7 +1,8 @@ //! IPC tests +use alloy_primitives::U256; use anvil::{spawn, NodeConfig}; -use ethers::{core::rand, prelude::Middleware, types::U256}; +use ethers::{core::rand, prelude::Middleware}; use futures::StreamExt; pub fn rand_ipc_endpoint() -> String { @@ -22,19 +23,19 @@ async fn can_get_block_number_ipc() { let (api, handle) = spawn(ipc_config()).await; let block_num = api.block_number().unwrap(); - assert_eq!(block_num, U256::zero()); + assert_eq!(block_num, U256::ZERO); - let provider = handle.ipc_provider().unwrap(); + let provider = handle.ethers_ipc_provider().unwrap(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, block_num.as_u64().into()); + assert_eq!(num.as_u64(), block_num.to::()); } #[tokio::test(flavor = "multi_thread")] async fn test_sub_new_heads_ipc() { let (api, handle) = spawn(ipc_config()).await; - let provider = handle.ipc_provider().unwrap(); + let provider = handle.ethers_ipc_provider().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 913015de5edf..307eabf27ed3 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -13,7 +13,7 @@ use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn get_past_events() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let address = wallet.address(); @@ -50,7 +50,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -87,7 +87,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -130,7 +130,7 @@ async fn can_install_filter() { 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.http_provider(), wallet)); + let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) .unwrap() @@ -143,7 +143,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.ws_provider()); + let ws = Arc::new(handle.ethers_ws_provider()); 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 e7ee430bb257..abd4de130484 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -10,13 +10,14 @@ use ethers::{ }, }; use ethers_core::types::{Bytes, H256}; +use foundry_common::types::ToAlloy; use std::str::FromStr; #[tokio::test(flavor = "multi_thread")] async fn test_deposits_not_supported_if_optimism_disabled() { // optimism disabled by default let (_, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); @@ -52,14 +53,14 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let send_value = U256::from(1234); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); // fund the sender - api.anvil_set_balance(from_addr, send_value).await.unwrap(); + api.anvil_set_balance(from_addr.to_alloy(), send_value.to_alloy()).await.unwrap(); let deposit_tx: TypedTransaction = TypedTransaction::DepositTransaction(DepositTransaction { tx: TransactionRequest { @@ -81,13 +82,14 @@ async fn test_send_value_deposit_transaction() { }); let pending = provider.send_transaction(deposit_tx.clone(), None).await.unwrap(); - let receipt = pending.await.unwrap().expect("dropped"); - assert_eq!(receipt.from, from_addr); - assert_eq!(receipt.to, Some(to_addr)); // mine block api.evm_mine(None).await.unwrap(); + let receipt = provider.get_transaction_receipt(pending.tx_hash()).await.unwrap().unwrap(); + assert_eq!(receipt.from, from_addr); + assert_eq!(receipt.to, Some(to_addr)); + // the recipient should have received the value let balance = provider.get_balance(to_addr, None).await.unwrap(); assert_eq!(balance, send_value); @@ -98,14 +100,14 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let send_value = U256::from(1234); let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); // fund the sender - api.anvil_set_balance(from_addr, send_value).await.unwrap(); + api.anvil_set_balance(from_addr.to_alloy(), send_value.to_alloy()).await.unwrap(); let deposit_tx: TypedTransaction = TypedTransaction::DepositTransaction(DepositTransaction { tx: TransactionRequest { @@ -128,13 +130,14 @@ async fn test_send_value_raw_deposit_transaction() { let rlpbytes = deposit_tx.rlp(); let pending = provider.send_raw_transaction(rlpbytes).await.unwrap(); - let receipt = pending.await.unwrap().expect("dropped"); - assert_eq!(receipt.from, from_addr); - assert_eq!(receipt.to, Some(to_addr)); // mine block api.evm_mine(None).await.unwrap(); + let receipt = provider.get_transaction_receipt(pending.tx_hash()).await.unwrap().unwrap(); + assert_eq!(receipt.from, from_addr); + assert_eq!(receipt.to, Some(to_addr)); + // the recipient should have received the value let balance = provider.get_balance(to_addr, None).await.unwrap(); assert_eq!(balance, send_value); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index becbcb852e53..ab7725981443 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,5 +1,7 @@ //! tests for otterscan endpoints use crate::abi::MulticallContract; +use alloy_primitives::U256 as rU256; +use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, @@ -10,10 +12,11 @@ use ethers::{ abi::Address, prelude::{ContractFactory, ContractInstance, Middleware, SignerMiddleware}, signers::Signer, - types::{BlockNumber, Bytes, TransactionRequest, U256}, + types::{Bytes, TransactionRequest, U256}, utils::get_contract_address, }; use ethers_solc::{project_util::TempProject, Artifact}; +use foundry_common::types::{ToAlloy, ToEthers}; use std::{collections::VecDeque, str::FromStr, sync::Arc}; #[tokio::test(flavor = "multi_thread")] @@ -24,8 +27,8 @@ async fn can_call_erigon_get_header_by_number() { let res0 = api.erigon_get_header_by_number(0.into()).await.unwrap().unwrap(); let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); - assert_eq!(res0.number, Some(0.into())); - assert_eq!(res1.number, Some(1.into())); + assert_eq!(res0.header.number, Some(rU256::from(0))); + assert_eq!(res1.header.number, Some(rU256::from(1))); } #[tokio::test(flavor = "multi_thread")] @@ -38,7 +41,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); @@ -50,16 +53,16 @@ async fn can_call_ots_get_internal_operations_contract_deploy() { let receipt = client.send_transaction(deploy_tx, None).await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Create, - from: sender, - to: contract_address, - value: 0.into() + from: sender.to_alloy(), + to: contract_address.to_alloy(), + value: rU256::from(0) } ); } @@ -67,7 +70,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -80,16 +83,16 @@ async fn can_call_ots_get_internal_operations_contract_transfer() { let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Transfer, - from, - to, - value: amount + from: from.to_alloy(), + to: to.to_alloy(), + value: amount.to_alloy() } ); } @@ -122,7 +125,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallets = handle.dev_wallets().collect::>(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); @@ -133,21 +136,23 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), + SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), ); let call = contract.method::<_, ()>("deploy", ()).unwrap(); let receipt = call.send().await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Create2, - from: Address::from_str("0x4e59b44847b379578588920cA78FbF26c0B4956C").unwrap(), - to: Address::from_str("0x347bcdad821abc09b8c275881b368de36476b62c").unwrap(), - value: 0.into() + from: Address::from_str("0x4e59b44847b379578588920cA78FbF26c0B4956C") + .unwrap() + .to_alloy(), + to: Address::from_str("0x347bcdad821abc09b8c275881b368de36476b62c").unwrap().to_alloy(), + value: rU256::from(0) } ); } @@ -178,7 +183,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallets = handle.dev_wallets().collect::>(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); @@ -189,22 +194,22 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), + SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); let receipt = call.send().await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::SelfDestruct, - from: contract.address(), + from: contract.address().to_alloy(), to: Default::default(), - value: 0.into() + value: rU256::from(0) } ); } @@ -212,7 +217,7 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_has_code() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); @@ -226,7 +231,7 @@ async fn can_call_ots_has_code() { // no code in the address before deploying assert!(!api - .ots_has_code(pending_contract_address, BlockNumber::Number(1.into())) + .ots_has_code(pending_contract_address.to_alloy(), BlockNumberOrTag::Number(1)) .await .unwrap()); @@ -237,11 +242,17 @@ async fn can_call_ots_has_code() { assert_eq!(num, receipt.block_number.unwrap()); // code is detected after deploying - assert!(api.ots_has_code(pending_contract_address, BlockNumber::Number(num)).await.unwrap()); + assert!(api + .ots_has_code(pending_contract_address.to_alloy(), BlockNumberOrTag::Number(num.as_u64())) + .await + .unwrap()); // code is not detected for the previous block assert!(!api - .ots_has_code(pending_contract_address, BlockNumber::Number(num - 1)) + .ots_has_code( + pending_contract_address.to_alloy(), + BlockNumberOrTag::Number(num.as_u64() - 1) + ) .await .unwrap()); } @@ -285,7 +296,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallets = handle.dev_wallets().collect::>(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); @@ -296,12 +307,12 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), + SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), ); let call = contract.method::<_, ()>("run", ()).unwrap().value(1337); let receipt = call.send().await.unwrap().await.unwrap().unwrap(); - let res = api.ots_trace_transaction(receipt.transaction_hash).await.unwrap(); + let res = api.ots_trace_transaction(receipt.transaction_hash.to_alloy()).await.unwrap(); assert_eq!( res, @@ -309,42 +320,42 @@ contract Contract { OtsTrace { r#type: OtsTraceType::Call, depth: 0, - from: wallets[1].address(), - to: contract.address(), - value: 1337.into(), - input: Bytes::from_str("0xc0406226").unwrap() + from: wallets[1].address().to_alloy(), + to: contract.address().to_alloy(), + value: rU256::from(1337), + input: Bytes::from_str("0xc0406226").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::StaticCall, depth: 1, - from: contract.address(), - to: contract.address(), - value: U256::zero(), - input: Bytes::from_str("0x6a6758fe").unwrap() + from: contract.address().to_alloy(), + to: contract.address().to_alloy(), + value: U256::zero().to_alloy(), + input: Bytes::from_str("0x6a6758fe").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::Call, depth: 1, - from: contract.address(), - to: contract.address(), - value: U256::zero(), - input: Bytes::from_str("0x96385e39").unwrap() + from: contract.address().to_alloy(), + to: contract.address().to_alloy(), + value: U256::zero().to_alloy(), + input: Bytes::from_str("0x96385e39").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::Call, depth: 2, - from: contract.address(), - to: wallets[0].address(), - value: 1337.into(), - input: Bytes::from_str("0x").unwrap() + from: contract.address().to_alloy(), + to: wallets[0].address().to_alloy(), + value: U256::from(1337).to_alloy(), + input: Bytes::from_str("0x").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::DelegateCall, depth: 2, - from: contract.address(), - to: contract.address(), - value: U256::zero(), - input: Bytes::from_str("0xa1325397").unwrap() + from: contract.address().to_alloy(), + to: contract.address().to_alloy(), + value: U256::zero().to_alloy(), + input: Bytes::from_str("0xa1325397").unwrap().0.into() }, ] ); @@ -374,7 +385,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -386,14 +397,16 @@ contract Contract { let call = contract.method::<_, ()>("trigger_revert", ()).unwrap().gas(150_000u64); let receipt = call.send().await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_transaction_error(receipt.transaction_hash).await.unwrap().unwrap(); + let res = + api.ots_get_transaction_error(receipt.transaction_hash.to_alloy()).await.unwrap().unwrap(); + let res: Bytes = res.0.into(); assert_eq!(res, Bytes::from_str("0x8d6ea8be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012526576657274537472696e67466f6f4261720000000000000000000000000000").unwrap()); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -404,13 +417,18 @@ async fn can_call_ots_get_block_details() { let result = api.ots_get_block_details(1.into()).await.unwrap(); assert_eq!(result.block.transaction_count, 1); - assert_eq!(result.block.block.transactions[0], receipt.transaction_hash); + let hash = match result.block.block.transactions { + BlockTransactions::Full(txs) => txs[0].hash, + BlockTransactions::Hashes(hashes) => hashes[0], + BlockTransactions::Uncle => unreachable!(), + }; + assert_eq!(hash, receipt.transaction_hash.to_alloy()); } #[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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -419,16 +437,21 @@ async fn can_call_ots_get_block_details_by_hash() { let receipt = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let block_hash = receipt.block_hash.unwrap(); - let result = api.ots_get_block_details_by_hash(block_hash).await.unwrap(); + let result = api.ots_get_block_details_by_hash(block_hash.to_alloy()).await.unwrap(); assert_eq!(result.block.transaction_count, 1); - assert_eq!(result.block.block.transactions[0], receipt.transaction_hash); + let hash = match result.block.block.transactions { + BlockTransactions::Full(txs) => txs[0].hash, + BlockTransactions::Hashes(hashes) => hashes[0], + BlockTransactions::Uncle => unreachable!(), + }; + assert_eq!(hash.to_ethers(), receipt.transaction_hash); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -450,13 +473,17 @@ async fn can_call_ots_get_block_transactions() { let result = api.ots_get_block_transactions(1, page, page_size).await.unwrap(); assert!(result.receipts.len() <= page_size); - assert!(result.fullblock.block.transactions.len() <= page_size); + let len = result.receipts.len(); + assert!(len <= page_size); assert!(result.fullblock.transaction_count == result.receipts.len()); result.receipts.iter().enumerate().for_each(|(i, receipt)| { let expected = hashes.pop_front(); - assert_eq!(expected, Some(receipt.transaction_hash)); - assert_eq!(expected, Some(result.fullblock.block.transactions[i].hash)); + assert_eq!(expected, receipt.transaction_hash.map(|h| h.to_ethers())); + assert_eq!( + expected.map(|h| h.to_alloy()), + result.fullblock.block.transactions.hashes().nth(i).copied(), + ); }); } @@ -466,7 +493,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); @@ -483,16 +510,17 @@ async fn can_call_ots_search_transactions_before() { let page_size = 2; let mut block = 0; for _ in 0..4 { - let result = api.ots_search_transactions_before(sender, block, page_size).await.unwrap(); + let result = + api.ots_search_transactions_before(sender.to_alloy(), block, page_size).await.unwrap(); assert!(result.txs.len() <= page_size); // check each individual hash result.txs.iter().for_each(|tx| { - assert_eq!(hashes.pop(), Some(tx.hash)); + assert_eq!(hashes.pop(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().as_u64() - 1; + block = result.txs.last().unwrap().block_number.unwrap().to::() - 1; } assert!(hashes.is_empty()); @@ -501,7 +529,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); @@ -518,16 +546,17 @@ async fn can_call_ots_search_transactions_after() { let page_size = 2; let mut block = 0; for _ in 0..4 { - let result = api.ots_search_transactions_after(sender, block, page_size).await.unwrap(); + let result = + api.ots_search_transactions_after(sender.to_alloy(), block, page_size).await.unwrap(); assert!(result.txs.len() <= page_size); // check each individual hash result.txs.iter().for_each(|tx| { - assert_eq!(hashes.pop_back(), Some(tx.hash)); + assert_eq!(hashes.pop_back(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().as_u64() + 1; + block = result.txs.last().unwrap().block_number.unwrap().to::() + 1; } assert!(hashes.is_empty()); @@ -536,7 +565,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); api.mine_one().await; let wallet = handle.dev_wallets().next().unwrap(); @@ -549,17 +578,23 @@ async fn can_call_ots_get_transaction_by_sender_and_nonce() { let receipt1 = client.send_transaction(tx1, None).await.unwrap().await.unwrap().unwrap(); let receipt2 = client.send_transaction(tx2, None).await.unwrap().await.unwrap().unwrap(); - let result1 = api.ots_get_transaction_by_sender_and_nonce(sender, 0.into()).await.unwrap(); - let result2 = api.ots_get_transaction_by_sender_and_nonce(sender, 1.into()).await.unwrap(); + let result1 = api + .ots_get_transaction_by_sender_and_nonce(sender.to_alloy(), rU256::from(0)) + .await + .unwrap(); + let result2 = api + .ots_get_transaction_by_sender_and_nonce(sender.to_alloy(), rU256::from(1)) + .await + .unwrap(); - assert_eq!(result1.unwrap().hash, receipt1.transaction_hash); - assert_eq!(result2.unwrap().hash, receipt2.transaction_hash); + assert_eq!(result1.unwrap().hash, receipt1.transaction_hash.to_alloy()); + assert_eq!(result2.unwrap().hash, receipt2.transaction_hash.to_alloy()); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_contract_creator() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); api.mine_one().await; let wallet = handle.dev_wallets().next().unwrap(); @@ -573,8 +608,9 @@ async fn can_call_ots_get_contract_creator() { let receipt = client.send_transaction(deploy_tx, None).await.unwrap().await.unwrap().unwrap(); - let creator = api.ots_get_contract_creator(pending_contract_address).await.unwrap().unwrap(); + let creator = + api.ots_get_contract_creator(pending_contract_address.to_alloy()).await.unwrap().unwrap(); - assert_eq!(creator.creator, sender); - assert_eq!(creator.hash, receipt.transaction_hash); + assert_eq!(creator.creator, sender.to_alloy()); + assert_eq!(creator.hash, receipt.transaction_hash.to_alloy()); } diff --git a/crates/anvil/tests/it/proof/mod.rs b/crates/anvil/tests/it/proof/mod.rs index 2e6278bb1826..cbe803fb2817 100644 --- a/crates/anvil/tests/it/proof/mod.rs +++ b/crates/anvil/tests/it/proof/mod.rs @@ -1,17 +1,15 @@ //! tests for `eth_getProof` use crate::proof::eip1186::verify_proof; +use alloy_rpc_types::EIP1186AccountProofResponse; use anvil::{spawn, NodeConfig}; -use anvil_core::eth::{ - proof::{AccountProof, BasicAccount}, - trie::ExtensionLayout, -}; +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::ToEthers; +use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::revm::primitives::KECCAK_EMPTY; mod eip1186; @@ -25,20 +23,23 @@ async fn can_get_proof() { let key = U256::zero(); let value = U256::one(); - api.anvil_set_storage_at(acc, key, H256::from_uint(&value)).await.unwrap(); + api.anvil_set_storage_at(acc.to_alloy(), key.to_alloy(), H256::from_uint(&value).to_alloy()) + .await + .unwrap(); - let proof: AccountProof = api.get_proof(acc, vec![H256::from_uint(&key)], None).await.unwrap(); + let proof: EIP1186AccountProofResponse = + api.get_proof(acc.to_alloy(), vec![H256::from_uint(&key).to_alloy()], None).await.unwrap(); let account = BasicAccount { nonce: 0.into(), balance: 0.into(), - storage_root: proof.storage_hash, + storage_root: proof.storage_hash.to_ethers(), code_hash: KECCAK_EMPTY.to_ethers(), }; let rlp_account = rlp::encode(&account); - let root: H256 = api.state_root().await.unwrap(); + let root: H256 = api.state_root().await.unwrap().to_ethers(); let acc_proof: Vec> = proof .account_proof .into_iter() @@ -58,7 +59,7 @@ async fn can_get_proof() { 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.as_bytes())); + let key = H256::from(keccak256(proof.key.0 .0)); verify_proof::( &account.storage_root.0, &storage_proof, @@ -74,7 +75,7 @@ async fn can_get_random_account_proofs() { for acc in std::iter::repeat_with(Address::random).take(10) { let _ = api - .get_proof(acc, Vec::new(), None) + .get_proof(acc.to_alloy(), 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 22df53c96825..31671a53809e 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -9,6 +9,7 @@ use ethers::{ signers::Signer, types::{Address, Block, Filter, TransactionRequest, TxHash, ValueOrArray, U256}, }; +use foundry_common::types::ToAlloy; use futures::StreamExt; use std::sync::Arc; @@ -16,7 +17,7 @@ use std::sync::Arc; async fn test_sub_new_heads() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let blocks = provider.subscribe_blocks().await.unwrap(); @@ -34,7 +35,7 @@ async fn test_sub_logs_legacy() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -73,7 +74,7 @@ async fn test_sub_logs() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -111,13 +112,13 @@ async fn test_sub_logs_impersonated() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); // impersonate account let impersonate = Address::random(); let funding = U256::from(1e18 as u64); - api.anvil_set_balance(impersonate, funding).await.unwrap(); - api.anvil_impersonate_account(impersonate).await.unwrap(); + 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 client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -137,7 +138,7 @@ async fn test_sub_logs_impersonated() { let tx = TransactionRequest::new().from(impersonate).to(contract.address()).data(data); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -152,7 +153,7 @@ async fn test_filters_legacy() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); @@ -193,7 +194,7 @@ async fn test_filters() { abigen!(EmitLogs, "test-data/emit_logs.json"); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); @@ -253,7 +254,7 @@ async fn test_subscriptions() { async fn test_sub_new_heads_fast() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let blocks = provider.subscribe_blocks().await.unwrap(); diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index 368a34da1763..c802c0ff6e67 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -283,7 +283,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = SignerMiddleware::new(provider, wallet.with_chain_id(Chain::Mainnet)); @@ -299,7 +299,7 @@ async fn rejects_different_chain_id() { 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.http_provider(); + let provider = handle.ethers_http_provider(); 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 8a7d1a66ba81..be192fec23ea 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -10,12 +10,13 @@ use ethers::{ utils::hex, }; use ethers_solc::{project_util::TempProject, Artifact}; +use foundry_common::types::ToAlloy; 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -72,7 +73,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallets = handle.dev_wallets().collect::>(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); @@ -83,14 +84,15 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), + SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); let tx = call.send().await.unwrap().await.unwrap().unwrap(); - let traces = handle.http_provider().trace_transaction(tx.transaction_hash).await.unwrap(); + let traces = + handle.ethers_http_provider().trace_transaction(tx.transaction_hash).await.unwrap(); assert!(!traces.is_empty()); - assert_eq!(traces[0].action_type, ActionType::Suicide); + assert_eq!(traces[1].action_type, ActionType::Suicide); } #[tokio::test(flavor = "multi_thread")] @@ -119,7 +121,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallets = handle.dev_wallets().collect::>(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); @@ -130,12 +132,12 @@ contract Contract { let contract = ContractInstance::new( contract.address(), abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), + SignerMiddleware::new(handle.ethers_http_provider(), wallets[1].clone()), ); let call = contract.method::<_, ()>("goodbye", ()).unwrap(); let traces = handle - .http_provider() + .ethers_http_provider() .debug_trace_call(call.tx, None, GethDebugTracingCallOptions::default()) .await .unwrap(); @@ -158,7 +160,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.http_provider(); + let provider = handle.ethers_http_provider(); let input = hex::decode("43bcfab60000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000e0bd811c8769a824b00000000000000000000000000000000000000000000000e0ae9925047d8440b60000000000000000000000002e4777139254ff76db957e284b186a4507ff8c67").unwrap(); @@ -166,7 +168,7 @@ async fn test_trace_address_fork() { let to: Address = "0xe2f2a5c287993345a840db3b0845fbc70f5935a5".parse().unwrap(); let tx = TransactionRequest::new().to(to).from(from).data(input).gas(300_000); - api.anvil_impersonate_account(from).await.unwrap(); + api.anvil_impersonate_account(from.to_alloy()).await.unwrap(); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); @@ -351,7 +353,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.http_provider(); + let provider = handle.ethers_http_provider(); let input = hex::decode("30000003000000000000000000000000adda1059a6c6c102b0fa562b9bb2cb9a0de5b1f4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a300000004fffffffffffffffffffffffffffffffffffffffffffff679dc91ecfe150fb980c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f4d2888d29d722226fafa5d9b24f9164c092421e000bb8000000000000004319b52bf08b65295d49117e790000000000000000000000000000000000000000000000008b6d9e8818d6141f000000000000000000000000000000000000000000000000000000086a23af210000000000000000000000000000000000000000000000000000000000").unwrap(); @@ -359,7 +361,7 @@ async fn test_trace_address_fork2() { let to: Address = "0x99999999d116ffa7d76590de2f427d8e15aeb0b8".parse().unwrap(); let tx = TransactionRequest::new().to(to).from(from).data(input).gas(350_000); - api.anvil_impersonate_account(from).await.unwrap(); + api.anvil_impersonate_account(from.to_alloy()).await.unwrap(); let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); assert_eq!(tx.status, Some(1u64.into())); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 7cb5c4bc6e5a..b93286debd6f 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -11,6 +11,7 @@ use ethers::{ Address, BlockNumber, Transaction, TransactionReceipt, H256, U256, }, }; +use foundry_common::types::{to_call_request_from_tx_request, ToAlloy, ToEthers}; use futures::{future::join_all, FutureExt, StreamExt}; use std::{collections::HashSet, sync::Arc, time::Duration}; use tokio::time::timeout; @@ -18,7 +19,7 @@ use tokio::time::timeout; #[tokio::test(flavor = "multi_thread")] async fn can_transfer_eth() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -53,7 +54,7 @@ async fn can_transfer_eth() { #[tokio::test(flavor = "multi_thread")] async fn can_order_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); // disable automine api.anvil_set_auto_mine(false).await.unwrap(); @@ -87,7 +88,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -126,7 +127,7 @@ async fn can_replace_transaction() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -164,7 +165,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -176,20 +177,21 @@ async fn can_reject_too_high_gas_limits() { let tx = TransactionRequest::new().to(to).value(amount).from(from); // send transaction with the exact gas limit - let pending = provider.send_transaction(tx.clone().gas(gas_limit), None).await; + let pending = provider.send_transaction(tx.clone().gas(gas_limit.to_ethers()), None).await; pending.unwrap(); // send transaction with higher gas limit - let pending = provider.send_transaction(tx.clone().gas(gas_limit + 1u64), None).await; + let pending = + provider.send_transaction(tx.clone().gas(gas_limit.to_ethers() + 1u64), None).await; assert!(pending.is_err()); let err = pending.unwrap_err(); assert!(err.to_string().contains("gas too high")); - api.anvil_set_balance(from, U256::MAX).await.unwrap(); + api.anvil_set_balance(from.to_alloy(), U256::MAX.to_alloy()).await.unwrap(); - let pending = provider.send_transaction(tx.gas(gas_limit), None).await; + let pending = provider.send_transaction(tx.gas(gas_limit.to_ethers()), None).await; pending.unwrap(); } @@ -200,7 +202,7 @@ async fn can_reject_underpriced_replacement() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -235,7 +237,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -268,7 +270,7 @@ async fn can_deploy_and_mine_manually() { // can mine in manual mode api.evm_mine(None).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -301,7 +303,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); @@ -322,7 +324,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -357,7 +359,7 @@ 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.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -382,7 +384,7 @@ 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.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -401,7 +403,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -413,14 +415,15 @@ async fn get_blocktimestamp_works() { assert!(timestamp > U256::one()); - let latest_block = api.block_by_number(BlockNumber::Latest).await.unwrap().unwrap(); + let latest_block = + api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.timestamp); + assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); // repeat call same result let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.timestamp); + assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); // mock timestamp let next_timestamp = timestamp.as_u64() + 1337; @@ -439,7 +442,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -496,7 +499,7 @@ 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.ws_provider(); + let provider = handle.ethers_ws_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -526,7 +529,7 @@ 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.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); @@ -560,7 +563,7 @@ 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.ws_provider(); + let provider = handle.ethers_ws_provider(); let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); @@ -619,7 +622,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let from = handle.dev_wallets().next().unwrap().address(); let tx = TransactionRequest::new().from(from).value(1337u64).to(Address::random()); @@ -640,7 +643,7 @@ async fn test_first_noce_is_zero() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let from = handle.dev_wallets().next().unwrap().address(); let nonce = provider @@ -657,7 +660,7 @@ async fn can_handle_different_sender_nonce_calculation() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from_first = accounts[0].address(); let from_second = accounts[1].address(); @@ -692,7 +695,7 @@ async fn includes_pending_tx_for_transaction_count() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let from = handle.dev_wallets().next().unwrap().address(); let tx_count = 10u64; @@ -719,7 +722,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); @@ -756,7 +759,7 @@ 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.http_provider(), wallet)); + let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); @@ -777,8 +780,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.http_provider(); - let ws_provider = handle.ws_provider(); + let provider = handle.ethers_http_provider(); + let ws_provider = handle.ethers_ws_provider(); let accounts = provider.get_accounts().await.unwrap(); let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64); @@ -861,7 +864,7 @@ async fn test_tx_access_list() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); - let client = Arc::new(SignerMiddleware::new(handle.http_provider(), wallet)); + let client = Arc::new(SignerMiddleware::new(handle.ethers_http_provider(), wallet)); let sender = Address::random(); let other_acc = Address::random(); @@ -930,7 +933,7 @@ async fn estimates_gas_on_pending_by_default() { // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); @@ -943,13 +946,13 @@ async fn estimates_gas_on_pending_by_default() { let tx = TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]); - api.estimate_gas(tx.into(), None).await.unwrap(); + api.estimate_gas(to_call_request_from_tx_request(tx), None).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_reject_gas_too_low() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); let account = handle.dev_accounts().next().unwrap(); @@ -970,8 +973,8 @@ async fn test_reject_gas_too_low() { #[tokio::test(flavor = "multi_thread")] async fn can_call_with_high_gas_limit() { let (_api, handle) = - spawn(NodeConfig::test().with_gas_limit(Some(U256::from(100_000_000)))).await; - let provider = handle.http_provider(); + spawn(NodeConfig::test().with_gas_limit(Some(U256::from(100_000_000).to_alloy()))).await; + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -989,7 +992,7 @@ 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.http_provider(); + let provider = handle.ethers_http_provider(); let wallet = handle.dev_wallets().next().unwrap(); let client = Arc::new(SignerMiddleware::new(provider, wallet)); @@ -998,8 +1001,8 @@ async fn test_reject_eip1559_pre_london() { let gas_price = api.gas_price().unwrap(); let unsupported = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) .unwrap() - .gas(gas_limit) - .gas_price(gas_price) + .gas(gas_limit.to_ethers()) + .gas_price(gas_price.to_ethers()) .send() .await .unwrap_err() diff --git a/crates/anvil/tests/it/txpool.rs b/crates/anvil/tests/it/txpool.rs index ea0f6b4d246a..3f17e5beeaac 100644 --- a/crates/anvil/tests/it/txpool.rs +++ b/crates/anvil/tests/it/txpool.rs @@ -9,7 +9,7 @@ use ethers::{ #[tokio::test(flavor = "multi_thread")] async fn geth_txpool() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); + let provider = handle.ethers_http_provider(); api.anvil_set_auto_mine(false).await.unwrap(); let account = provider.get_accounts().await.unwrap()[0]; diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index f185c6b8ee24..f1212bd410e5 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -2,23 +2,24 @@ use anvil::{spawn, NodeConfig}; use ethers::{prelude::Middleware, types::U256}; +use foundry_common::types::ToAlloy; #[tokio::test(flavor = "multi_thread")] async fn can_get_block_number_ws() { let (api, handle) = spawn(NodeConfig::test()).await; let block_num = api.block_number().unwrap(); - assert_eq!(block_num, U256::zero()); + assert_eq!(block_num, U256::zero().to_alloy()); - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, block_num.as_u64().into()); + assert_eq!(num, block_num.to::().into()); } #[tokio::test(flavor = "multi_thread")] async fn can_dev_get_balance_ws() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); + let provider = handle.ethers_ws_provider(); let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 8217eeb9e1ed..29c0d5f82061 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -165,9 +165,9 @@ impl CallArgs { Err(evm_err) => TraceResult::try_from(evm_err)?, }; - handle_traces(trace, &config, chain, labels, verbose, debug).await?; + handle_traces(trace, &config, chain, labels, verbose).await?; - return Ok(()) + return Ok(()); } // fill the builder after the conditional so we dont move values @@ -199,9 +199,9 @@ impl CallArgs { tx.value().copied().unwrap_or_default().to_alloy(), )?); - handle_traces(trace, &config, chain, labels, verbose, debug).await?; + handle_traces(trace, &config, chain, labels, verbose).await?; - return Ok(()) + return Ok(()); } } }; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 535dc3b46a7b..8f13706e647b 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -104,7 +104,7 @@ impl RunArgs { return Err(eyre::eyre!( "{:?} is a system transaction.\nReplaying system transactions is currently not supported.", tx.hash - )) + )); } let tx_block_number = tx @@ -148,13 +148,13 @@ impl RunArgs { Some(SYSTEM_TRANSACTION_TYPE) { update_progress!(pb, index); - continue + continue; } if tx.hash == tx_hash { - break + break; } - configure_tx_env(&mut env, &tx); + configure_tx_env(&mut env, &tx.clone().to_alloy()); if let Some(to) = tx.to { trace!(tx=?tx.hash,?to, "executing previous call transaction"); @@ -191,7 +191,7 @@ impl RunArgs { let result = { executor.set_trace_printer(self.trace_printer); - configure_tx_env(&mut env, &tx); + configure_tx_env(&mut env, &tx.clone().to_alloy()); if let Some(to) = tx.to { trace!(tx=?tx.hash, to=?to, "executing call transaction"); @@ -205,7 +205,7 @@ impl RunArgs { } }; - handle_traces(result, &config, chain, self.label, self.verbose, self.debug).await?; + handle_traces(result, &config, chain, self.label, self.debug).await?; Ok(()) } diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index da9451ccc8ef..b91258c86664 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -14,8 +14,8 @@ use foundry_cli::{ use foundry_common::{ abi::find_source, compile::{etherscan_project, ProjectCompiler}, + provider::ethers::RetryProvider, types::{ToAlloy, ToEthers}, - RetryProvider, }; use foundry_compilers::{artifacts::StorageLayout, ConfigurableContractArtifact, Project, Solc}; use foundry_config::{ @@ -85,7 +85,7 @@ impl StorageArgs { if let Some(slot) = slot { let cast = Cast::new(provider); println!("{}", cast.storage(address, slot.to_ethers(), block).await?); - return Ok(()) + return Ok(()); } // No slot was provided @@ -109,7 +109,7 @@ impl StorageArgs { let artifact = out.artifacts().find(|(_, artifact)| match_code(artifact).unwrap_or_default()); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address.clone(), artifact, true).await + return fetch_and_print_storage(provider, address.clone(), artifact, true).await; } } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 00f2790b1614..9eb81c4ce567 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1,15 +1,15 @@ use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; use alloy_json_abi::ContractObject; -use alloy_primitives::{Address, I256, U256}; +use alloy_primitives::{ + utils::{keccak256, ParseUnits, Unit}, + Address, I256, U256, +}; use alloy_rlp::Decodable; use base::{Base, NumberWithBase, ToBase}; use chrono::NaiveDateTime; use ethers_core::{ types::{transaction::eip2718::TypedTransaction, *}, - utils::{ - format_bytes32_string, format_units, keccak256, parse_bytes32_string, parse_units, rlp, - Units, - }, + utils::rlp, }; use ethers_providers::{Middleware, PendingTransaction, PubsubClient}; use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; @@ -1079,13 +1079,14 @@ impl SimpleCast { /// # Ok::<_, eyre::Report>(()) /// ``` pub fn from_fixed_point(value: &str, decimals: &str) -> Result { - // first try u32 as Units assumes a string can only be "ether", "gwei"... and not a number - let units = match decimals.parse::() { - Ok(d) => Units::Other(d), - Err(_) => Units::try_from(decimals)?, + // TODO: https://github.com/alloy-rs/core/pull/461 + let units: Unit = if let Ok(x) = decimals.parse() { + Unit::new(x).ok_or_else(|| eyre::eyre!("invalid unit"))? + } else { + decimals.parse()? }; - let n: NumberWithBase = parse_units(value, units.as_num())?.into(); - Ok(format!("{n}")) + let n = ParseUnits::parse_units(value, units)?; + Ok(n.to_string()) } /// Converts integers with specified decimals into fixed point numbers @@ -1234,7 +1235,6 @@ impl SimpleCast { /// assert_eq!(Cast::to_unit("1 wei", "wei")?, "1"); /// assert_eq!(Cast::to_unit("1", "wei")?, "1"); /// assert_eq!(Cast::to_unit("1ether", "wei")?, "1000000000000000000"); - /// assert_eq!(Cast::to_unit("100 gwei", "gwei")?, "100"); /// # Ok::<_, eyre::Report>(()) /// ``` pub fn to_unit(value: &str, unit: &str) -> Result { @@ -1242,31 +1242,18 @@ impl SimpleCast { .as_uint() .wrap_err("Could not convert to uint")? .0; - - Ok(match unit { - "eth" | "ether" => foundry_common::units::format_units(value, 18)? - .trim_end_matches(".000000000000000000") - .to_string(), - "milli" | "milliether" => foundry_common::units::format_units(value, 15)? - .trim_end_matches(".000000000000000") - .to_string(), - "micro" | "microether" => foundry_common::units::format_units(value, 12)? - .trim_end_matches(".000000000000") - .to_string(), - "gwei" | "nano" | "nanoether" => foundry_common::units::format_units(value, 9)? - .trim_end_matches(".000000000") - .to_string(), - "mwei" | "mega" | "megaether" => foundry_common::units::format_units(value, 6)? - .trim_end_matches(".000000") - .to_string(), - "kwei" | "kilo" | "kiloether" => { - foundry_common::units::format_units(value, 3)?.trim_end_matches(".000").to_string() - } - "wei" => { - foundry_common::units::format_units(value, 0)?.trim_end_matches(".0").to_string() + let unit = unit.parse().wrap_err("could not parse units")?; + let mut formatted = ParseUnits::U256(value).format_units(unit); + + // Trim empty fractional part. + if let Some(dot) = formatted.find('.') { + let fractional = &formatted[dot + 1..]; + if fractional.chars().all(|c: char| c == '0') { + formatted = formatted[..dot].to_string(); } - _ => eyre::bail!("invalid unit: \"{}\"", unit), - }) + } + + Ok(formatted) } /// Converts wei into an eth amount @@ -1280,15 +1267,12 @@ impl SimpleCast { /// assert_eq!(Cast::from_wei("12340000005", "gwei")?, "12.340000005"); /// assert_eq!(Cast::from_wei("10", "ether")?, "0.000000000000000010"); /// assert_eq!(Cast::from_wei("100", "eth")?, "0.000000000000000100"); - /// assert_eq!(Cast::from_wei("17", "")?, "0.000000000000000017"); + /// assert_eq!(Cast::from_wei("17", "ether")?, "0.000000000000000017"); /// # Ok::<_, eyre::Report>(()) /// ``` pub fn from_wei(value: &str, unit: &str) -> Result { - let value = NumberWithBase::parse_int(value, None)?.number().to_ethers(); - Ok(match unit { - "gwei" => format_units(value, 9), - _ => format_units(value, 18), - }?) + let value = NumberWithBase::parse_int(value, None)?.number(); + Ok(ParseUnits::U256(value).format_units(unit.parse()?)) } /// Converts an eth amount into wei @@ -1298,18 +1282,14 @@ impl SimpleCast { /// ``` /// use cast::SimpleCast as Cast; /// - /// assert_eq!(Cast::to_wei("1", "")?, "1000000000000000000"); /// assert_eq!(Cast::to_wei("100", "gwei")?, "100000000000"); /// assert_eq!(Cast::to_wei("100", "eth")?, "100000000000000000000"); /// assert_eq!(Cast::to_wei("1000", "ether")?, "1000000000000000000000"); /// # Ok::<_, eyre::Report>(()) /// ``` pub fn to_wei(value: &str, unit: &str) -> Result { - let wei = match unit { - "gwei" => parse_units(value, 9), - _ => parse_units(value, 18), - }?; - Ok(wei.to_string()) + let unit = unit.parse().wrap_err("could not parse units")?; + Ok(ParseUnits::parse_units(value, unit)?.to_string()) } /// Decodes rlp encoded list with hex data @@ -1423,21 +1403,20 @@ impl SimpleCast { /// Encodes string into bytes32 value pub fn format_bytes32_string(s: &str) -> Result { - let formatted = format_bytes32_string(s)?; - Ok(hex::encode_prefixed(formatted)) + let str_bytes: &[u8] = s.as_bytes(); + eyre::ensure!(str_bytes.len() <= 32, "bytes32 strings must not exceed 32 bytes in length"); + + let mut bytes32: [u8; 32] = [0u8; 32]; + bytes32[..str_bytes.len()].copy_from_slice(str_bytes); + Ok(hex::encode_prefixed(bytes32)) } /// Decodes string from bytes32 value pub fn parse_bytes32_string(s: &str) -> Result { let bytes = hex::decode(s)?; - if bytes.len() != 32 { - eyre::bail!("expected 64 byte hex-string, got {}", strip_0x(s)); - } - - let mut buffer = [0u8; 32]; - buffer.copy_from_slice(&bytes); - - Ok(parse_bytes32_string(&buffer)?.to_owned()) + eyre::ensure!(bytes.len() == 32, "expected 32 byte hex-string"); + let len = bytes.iter().take_while(|x| **x != 0).count(); + Ok(std::str::from_utf8(&bytes[..len])?.into()) } /// Decodes checksummed address from bytes32 value @@ -1739,14 +1718,10 @@ impl SimpleCast { /// # Ok::<_, eyre::Report>(()) /// ``` pub fn keccak(data: &str) -> Result { - let hash = match data.as_bytes() { - // 0x prefix => read as hex data - [b'0', b'x', rest @ ..] => keccak256(hex::decode(rest)?), - // No 0x prefix => read as text - _ => keccak256(data), - }; - - Ok(format!("{:?}", H256(hash))) + // Hex-decode if data starts with 0x. + let hash = + if data.starts_with("0x") { keccak256(hex::decode(data)?) } else { keccak256(data) }; + Ok(hash.to_string()) } /// Performs the left shift operation (<<) on a number diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index d4b87891b3fa..bb000f5c3198 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -22,9 +22,10 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-sol-types.workspace = true +alloy-providers.workspace = true +alloy-rpc-types.workspace = true ethers-core.workspace = true -ethers-providers.workspace = true ethers-signers.workspace = true eyre.workspace = true diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 57683a76003b..436dd1c6284e 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,12 +1,10 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; -use alloy_primitives::B256; +use alloy_primitives::{B256, U256}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; -use ethers_core::types::Filter; -use ethers_providers::Middleware; -use foundry_common::{ - types::{ToAlloy, ToEthers}, - ProviderBuilder, -}; +use eyre::WrapErr; +use foundry_common::{provider::alloy::ProviderBuilder, types::ToEthers}; use foundry_compilers::utils::RuntimeOrHandle; use foundry_evm_core::fork::CreateFork; @@ -224,10 +222,10 @@ impl Cheatcode for rpcCall { let url = ccx.data.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; - + let method: &'static str = Box::new(method.clone()).leak(); let params_json: serde_json::Value = serde_json::from_str(params)?; let result = RuntimeOrHandle::new() - .block_on(provider.request(method, params_json)) + .block_on(provider.raw_request(method, params_json)) .map_err(|err| fmt_err!("{method:?}: {err}"))?; let result_as_tokens = crate::json::value_to_token(&result) @@ -252,35 +250,39 @@ impl Cheatcode for eth_getLogsCall { let url = ccx.data.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; - let mut filter = - Filter::new().address(target.to_ethers()).from_block(from_block).to_block(to_block); + let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); for (i, topic) in topics.iter().enumerate() { let topic = topic.to_ethers(); + // todo: needed because rust wants to convert FixedBytes<32> to U256 to convert it back + // to FixedBytes<32> and then to Topic for some reason removing the + // From impl in alloy does not fix the situation, and it is not possible to impl + // From> either because of a conflicting impl match i { - 0 => filter = filter.topic0(topic), - 1 => filter = filter.topic1(topic), - 2 => filter = filter.topic2(topic), - 3 => filter = filter.topic3(topic), + 0 => filter = filter.event_signature(U256::from_be_bytes(topic.to_fixed_bytes())), + 1 => filter = filter.topic1(U256::from_be_bytes(topic.to_fixed_bytes())), + 2 => filter = filter.topic2(U256::from_be_bytes(topic.to_fixed_bytes())), + 3 => filter = filter.topic3(U256::from_be_bytes(topic.to_fixed_bytes())), _ => unreachable!(), }; } + // todo: handle the errors somehow let logs = RuntimeOrHandle::new() - .block_on(provider.get_logs(&filter)) - .map_err(|e| fmt_err!("eth_getLogs: {e}"))?; + .block_on(provider.get_logs(filter)) + .wrap_err("failed to get logs")?; let eth_logs = logs .into_iter() .map(|log| EthGetLogs { - emitter: log.address.to_alloy(), - topics: log.topics.into_iter().map(ToAlloy::to_alloy).collect(), + emitter: log.address, + topics: log.topics.into_iter().collect(), data: log.data.0.into(), - blockHash: log.block_hash.unwrap_or_default().to_alloy(), - blockNumber: log.block_number.unwrap_or_default().to_alloy().to(), - transactionHash: log.transaction_hash.unwrap_or_default().to_alloy(), - transactionIndex: log.transaction_index.unwrap_or_default().to_alloy().to(), - logIndex: log.log_index.unwrap_or_default().to_alloy(), - removed: log.removed.unwrap_or(false), + blockHash: log.block_hash.unwrap_or_default(), + blockNumber: log.block_number.unwrap_or_default().to(), + transactionHash: log.transaction_hash.unwrap_or_default(), + transactionIndex: log.transaction_index.unwrap_or_default().to(), + logIndex: log.log_index.unwrap_or_default(), + removed: log.removed, }) .collect::>(); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index ffe224855543..eae7af5f2c14 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -19,7 +19,7 @@ use ethers_core::types::{ transaction::eip2718::TypedTransaction, NameOrAddress, TransactionRequest, }; use ethers_signers::LocalWallet; -use foundry_common::{evm::Breakpoints, types::ToEthers, RpcUrl}; +use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl, types::ToEthers}; use foundry_evm_core::{ backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_SKIP}, diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 1344690fc8d4..8cd552baee7d 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -15,7 +15,7 @@ use foundry_evm::{ decode::decode_console_logs, traces::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, + render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, }, }; use once_cell::sync::Lazy; @@ -990,11 +990,10 @@ impl ChiselDispatcher { } println!("{}", Paint::green("Traces:")); - for (kind, trace) in &mut result.traces { + for (kind, trace) in &result.traces { // Display all Setup + Execution traces. if matches!(kind, TraceKind::Setup | TraceKind::Execution) { - decoder.decode(trace).await; - println!("{trace}"); + println!("{}", render_trace_arena(trace, decoder).await?); } } diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index 286cc937c994..2127ddc4de90 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -168,7 +168,7 @@ impl ChiselRunner { { // update the gas gas_used = highest_gas_limit; - break + break; } last_highest_gas_limit = highest_gas_limit; } @@ -203,8 +203,7 @@ impl ChiselRunner { traces: traces .map(|traces| { // Manually adjust gas for the trace to add back the stipend/real used gas - // TODO: For chisel, we may not want to perform this adjustment. - // traces.arena[0].trace.gas_cost = gas_used; + vec![(TraceKind::Execution, traces)] }) .unwrap_or_default(), diff --git a/crates/cli/src/opts/wallet/multi_wallet.rs b/crates/cli/src/opts/wallet/multi_wallet.rs index 71baf28f529d..e6d22ff5826a 100644 --- a/crates/cli/src/opts/wallet/multi_wallet.rs +++ b/crates/cli/src/opts/wallet/multi_wallet.rs @@ -6,7 +6,7 @@ use ethers_signers::{ AwsSigner, HDPath as LedgerHDPath, Ledger, LocalWallet, Signer, Trezor, TrezorHDPath, }; use eyre::{Context, ContextCompat, Result}; -use foundry_common::{types::ToAlloy, RetryProvider}; +use foundry_common::{provider::ethers::RetryProvider, types::ToAlloy}; use foundry_config::Config; use itertools::izip; use rusoto_core::{ @@ -239,7 +239,7 @@ impl MultiWallet { local_wallets.insert(address.to_alloy(), signer); if addresses.is_empty() { - return Ok(local_wallets) + return Ok(local_wallets); } } else { // Just to show on error. @@ -270,7 +270,7 @@ impl MultiWallet { for _ in 0..self.interactives { wallets.push(self.get_from_interactive()?); } - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -281,7 +281,7 @@ impl MultiWallet { for private_key in private_keys.iter() { wallets.push(self.get_from_private_key(private_key.trim())?); } - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -317,7 +317,7 @@ impl MultiWallet { let wallet = self.get_from_keystore(Some(&path), passwords_iter.next().as_ref(), password_files_iter.next().as_ref())?.wrap_err("Keystore paths do not have the same length as provided passwords or password files.")?; wallets.push(wallet); } - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -352,7 +352,7 @@ impl MultiWallet { mnemonic_index, )?) } - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -369,7 +369,7 @@ impl MultiWallet { } create_hw_wallets!(args, chain_id, get_from_ledger, wallets); - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -377,7 +377,7 @@ impl MultiWallet { pub async fn trezors(&self, chain_id: u64) -> Result>> { if self.trezor { create_hw_wallets!(self, chain_id, get_from_trezor, wallets); - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } @@ -399,7 +399,7 @@ impl MultiWallet { wallets.push(aws_signer) } - return Ok(Some(wallets)) + return Ok(Some(wallets)); } Ok(None) } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 6d100cb15d08..4c226cf1b519 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -17,7 +17,7 @@ use foundry_evm::{ opts::EvmOpts, traces::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, + render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; use std::{fmt::Write, path::PathBuf, str::FromStr}; @@ -93,7 +93,7 @@ pub fn get_cached_entry_by_name( } if let Some(entry) = cached_entry { - return Ok(entry) + return Ok(entry); } let mut err = format!("could not find artifact: `{name}`"); @@ -175,7 +175,7 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool { NamedChain::Moonriver | NamedChain::Moonbase | NamedChain::MoonbeamDev - ) + ); } false } @@ -189,7 +189,7 @@ pub fn has_batch_support(chain_id: u64) -> bool { NamedChain::ArbitrumTestnet | NamedChain::ArbitrumGoerli | NamedChain::ArbitrumSepolia - ) + ); } true } @@ -374,7 +374,6 @@ pub async fn handle_traces( config: &Config, chain: Option, labels: Vec, - verbose: bool, debug: bool, ) -> Result<()> { let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?; @@ -384,7 +383,7 @@ pub async fn handle_traces( if let Some(addr) = iter.next() { if let (Ok(address), Some(label)) = (Address::from_str(addr), iter.next()) { - return Some((address, label.to_string())) + return Some((address, label.to_string())); } } None @@ -415,29 +414,20 @@ pub async fn handle_traces( .build(); debugger.try_run()?; } else { - print_traces(&mut result, &decoder, verbose).await?; + print_traces(&mut result, &decoder).await?; } Ok(()) } -pub async fn print_traces( - result: &mut TraceResult, - decoder: &CallTraceDecoder, - verbose: bool, -) -> Result<()> { +pub async fn print_traces(result: &mut TraceResult, decoder: &CallTraceDecoder) -> Result<()> { if result.traces.is_empty() { panic!("No traces found") } println!("Traces:"); - for (_, trace) in &mut result.traces { - decoder.decode(trace).await; - if !verbose { - println!("{trace}"); - } else { - println!("{trace:#}"); - } + for (_, arena) in &result.traces { + println!("{}", render_trace_arena(arena, decoder).await?); } println!(); diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 425732de95e6..f900c50b071d 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -1,9 +1,9 @@ use alloy_json_abi::JsonAbi; -use alloy_primitives::U256; +use alloy_primitives::{utils::format_units, U256}; use ethers_core::types::TransactionReceipt; use ethers_providers::Middleware; use eyre::{ContextCompat, Result}; -use foundry_common::{types::ToAlloy, units::format_units}; +use foundry_common::types::ToAlloy; use foundry_config::{Chain, Config}; use std::{ ffi::OsStr, @@ -85,7 +85,7 @@ pub fn abi_to_solidity(abi: &JsonAbi, name: &str) -> Result { /// and chain. /// /// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider(config: &Config) -> Result { +pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } @@ -93,9 +93,11 @@ pub fn get_provider(config: &Config) -> Result { /// URL and chain. /// /// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider_builder(config: &Config) -> Result { +pub fn get_provider_builder( + config: &Config, +) -> Result { let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = foundry_common::ProviderBuilder::new(url.as_ref()); + let mut builder = foundry_common::provider::ethers::ProviderBuilder::new(url.as_ref()); if let Ok(chain) = config.chain.unwrap_or_default().try_into() { builder = builder.chain(chain); @@ -475,7 +477,7 @@ impl<'a> Git<'a> { output.status.code(), stdout.trim(), stderr.trim() - )) + )); } } Ok(()) diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index d6f11bcf5d58..a7def0f5c6c7 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -21,8 +21,19 @@ ethers-providers = { workspace = true, features = ["ws", "ipc"] } alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-rpc-types.workspace = true +alloy-rpc-client.workspace = true +alloy-providers.workspace = true +alloy-transport.workspace = true +alloy-transport-http.workspace = true +alloy-transport-ws.workspace = true +alloy-transport-ipc.workspace = true +alloy-json-rpc.workspace = true +alloy-pubsub.workspace = true alloy-sol-types.workspace = true +tower.workspace = true + async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } comfy-table = "7" diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index df3742dd84b8..f0dd60e020fc 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -246,10 +246,10 @@ impl ProjectCompiler { let dev_functions = artifact.abi.as_ref().map(|abi| abi.functions()).into_iter().flatten().filter( - |&func| { + |func| { func.name.is_test() || - func.name == "IS_TEST" || - func.name == "IS_SCRIPT" + func.name.eq("IS_TEST") || + func.name.eq("IS_SCRIPT") }, ); diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 3e7e6ecbd775..c2825ac2f997 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -22,15 +22,14 @@ pub mod retry; pub mod rpc; pub mod runtime_client; pub mod selectors; +pub mod serde_helpers; pub mod shell; pub mod term; pub mod traits; pub mod transactions; pub mod types; -pub mod units; pub use constants::*; pub use contracts::*; -pub use provider::*; pub use traits::*; pub use transactions::*; diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs new file mode 100644 index 000000000000..bb52778145e9 --- /dev/null +++ b/crates/common/src/provider/alloy.rs @@ -0,0 +1,318 @@ +//! Commonly used helpers to construct `Provider`s + +use crate::{ + provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, +}; +use alloy_primitives::U256; +use alloy_providers::provider::{Provider, TempProvider}; +use alloy_rpc_client::ClientBuilder; +use alloy_transport::BoxTransport; +use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; +use eyre::{Result, WrapErr}; +use foundry_common::types::ToAlloy; +use foundry_config::NamedChain; +use reqwest::Url; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; +use url::ParseError; + +use super::tower::RetryBackoffLayer; + +/// Helper type alias for a retry provider +pub type RetryProvider = Provider; + +/// Helper type alias for a rpc url +pub type RpcUrl = String; + +/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely +/// an anvil or other dev node) and with the default, or 7 second otherwise. +/// +/// See [`try_get_http_provider`] for more details. +/// +/// # Panics +/// +/// Panics if the URL is invalid. +/// +/// # Examples +/// +/// ``` +/// use foundry_common::provider::alloy::get_http_provider; +/// +/// let retry_provider = get_http_provider("http://localhost:8545"); +/// ``` +#[inline] +#[track_caller] +pub fn get_http_provider(builder: impl AsRef) -> RetryProvider { + try_get_http_provider(builder).unwrap() +} + +/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely +/// an anvil or other dev node) and with the default, or 7 second otherwise. +#[inline] +pub fn try_get_http_provider(builder: impl AsRef) -> Result { + ProviderBuilder::new(builder.as_ref()).build() +} + +/// Helper type to construct a `RetryProvider` +#[derive(Debug)] +pub struct ProviderBuilder { + // Note: this is a result, so we can easily chain builder calls + url: Result, + chain: NamedChain, + max_retry: u32, + timeout_retry: u32, + initial_backoff: u64, + timeout: Duration, + /// available CUPS + compute_units_per_second: u64, + /// JWT Secret + jwt: Option, + headers: Vec, +} + +// === impl ProviderBuilder === + +impl ProviderBuilder { + /// Creates a new builder instance + pub fn new(url_str: &str) -> Self { + // a copy is needed for the next lines to work + let mut url_str = url_str; + + // invalid url: non-prefixed URL scheme is not allowed, so we prepend the default http + // prefix + let storage; + if url_str.starts_with("localhost:") { + storage = format!("http://{url_str}"); + url_str = storage.as_str(); + } + + let url = Url::parse(url_str) + .or_else(|err| match err { + ParseError::RelativeUrlWithoutBase => { + let path = Path::new(url_str); + + if let Ok(path) = resolve_path(path) { + Url::parse(&format!("file://{}", path.display())) + } else { + Err(err) + } + } + _ => Err(err), + }) + .wrap_err_with(|| format!("invalid provider URL: {url_str:?}")); + + Self { + url, + chain: NamedChain::Mainnet, + max_retry: 8, + timeout_retry: 8, + initial_backoff: 800, + timeout: REQUEST_TIMEOUT, + // alchemy max cpus + compute_units_per_second: ALCHEMY_FREE_TIER_CUPS, + jwt: None, + headers: vec![], + } + } + + /// Enables a request timeout. + /// + /// The timeout is applied from when the request starts connecting until the + /// response body has finished. + /// + /// Default is no timeout. + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + /// Sets the chain of the node the provider will connect to + pub fn chain(mut self, chain: NamedChain) -> Self { + self.chain = chain; + self + } + + /// How often to retry a failed request + pub fn max_retry(mut self, max_retry: u32) -> Self { + self.max_retry = max_retry; + self + } + + /// How often to retry a failed request. If `None`, defaults to the already-set value. + pub fn maybe_max_retry(mut self, max_retry: Option) -> Self { + self.max_retry = max_retry.unwrap_or(self.max_retry); + self + } + + /// The starting backoff delay to use after the first failed request. If `None`, defaults to + /// the already-set value. + pub fn maybe_initial_backoff(mut self, initial_backoff: Option) -> Self { + self.initial_backoff = initial_backoff.unwrap_or(self.initial_backoff); + self + } + + /// How often to retry a failed request due to connection issues + pub fn timeout_retry(mut self, timeout_retry: u32) -> Self { + self.timeout_retry = timeout_retry; + self + } + + /// The starting backoff delay to use after the first failed request + pub fn initial_backoff(mut self, initial_backoff: u64) -> Self { + self.initial_backoff = initial_backoff; + self + } + + /// Sets the number of assumed available compute units per second + /// + /// See also, + pub fn compute_units_per_second(mut self, compute_units_per_second: u64) -> Self { + self.compute_units_per_second = compute_units_per_second; + self + } + + /// Sets the number of assumed available compute units per second + /// + /// See also, + pub fn compute_units_per_second_opt(mut self, compute_units_per_second: Option) -> Self { + if let Some(cups) = compute_units_per_second { + self.compute_units_per_second = cups; + } + self + } + + /// Sets aggressive `max_retry` and `initial_backoff` values + /// + /// This is only recommend for local dev nodes + pub fn aggressive(self) -> Self { + self.max_retry(100).initial_backoff(100) + } + + /// Sets the JWT secret + pub fn jwt(mut self, jwt: impl Into) -> Self { + self.jwt = Some(jwt.into()); + self + } + + /// Sets http headers + pub fn headers(mut self, headers: Vec) -> Self { + self.headers = headers; + + self + } + + /// Same as [`Self:build()`] but also retrieves the `chainId` in order to derive an appropriate + /// interval. + pub async fn connect(self) -> Result { + let provider = self.build()?; + // todo: port poll interval hint + /*if let Some(blocktime) = provider.get_chainid().await.ok().and_then(|id| { + }) { + provider = provider.interval(blocktime / 2); + }*/ + Ok(provider) + } + + /// Constructs the `RetryProvider` taking all configs into account. + pub fn build(self) -> Result { + let ProviderBuilder { + url, + chain: _, + max_retry, + timeout_retry, + initial_backoff, + timeout, + compute_units_per_second, + jwt, + headers, + } = self; + let url = url?; + + let retry_layer = RetryBackoffLayer::new( + max_retry, + timeout_retry, + initial_backoff, + compute_units_per_second, + ); + let transport = RuntimeTransportBuilder::new(url.clone()) + .with_timeout(timeout) + .with_headers(headers) + .with_jwt(jwt) + .build(); + let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); + + // todo: provider polling interval + Ok(Provider::new_with_client(client.boxed())) + } +} + +/// Estimates EIP1559 fees depending on the chain +/// +/// Uses custom gas oracles for +/// - polygon +/// +/// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation +pub async fn estimate_eip1559_fees( + provider: &P, + chain: Option, +) -> Result<(U256, U256)> { + let chain = if let Some(chain) = chain { + chain + } else { + provider.get_chain_id().await.wrap_err("Failed to get chain id")?.to() + }; + + if let Ok(chain) = NamedChain::try_from(chain) { + // handle chains that deviate from `eth_feeHistory` and have their own oracle + match chain { + NamedChain::Polygon | NamedChain::PolygonMumbai => { + // TODO: phase this out somehow + let chain = match chain { + NamedChain::Polygon => ethers_core::types::Chain::Polygon, + NamedChain::PolygonMumbai => ethers_core::types::Chain::PolygonMumbai, + _ => unreachable!(), + }; + let estimator = Polygon::new(chain)?.category(GasCategory::Standard); + let (a, b) = estimator.estimate_eip1559_fees().await?; + return Ok((a.to_alloy(), b.to_alloy())); + } + _ => {} + } + } + provider.estimate_eip1559_fees(None).await.wrap_err("Failed fetch EIP1559 fees") +} + +#[cfg(not(windows))] +fn resolve_path(path: &Path) -> Result { + if path.is_absolute() { + Ok(path.to_path_buf()) + } else { + std::env::current_dir().map(|d| d.join(path)).map_err(drop) + } +} + +#[cfg(windows)] +fn resolve_path(path: &Path) -> Result { + if let Some(s) = path.to_str() { + if s.starts_with(r"\\.\pipe\") { + return Ok(path.to_path_buf()); + } + } + Err(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_auto_correct_missing_prefix() { + let builder = ProviderBuilder::new("localhost:8545"); + assert!(builder.url.is_ok()); + + let url = builder.url.unwrap(); + assert_eq!(url, Url::parse("http://localhost:8545").unwrap()); + } +} diff --git a/crates/common/src/provider.rs b/crates/common/src/provider/ethers.rs similarity index 99% rename from crates/common/src/provider.rs rename to crates/common/src/provider/ethers.rs index 730260d9ab4d..0d4acd2ce377 100644 --- a/crates/common/src/provider.rs +++ b/crates/common/src/provider/ethers.rs @@ -34,7 +34,7 @@ pub type RpcUrl = String; /// # Examples /// /// ``` -/// use foundry_common::get_http_provider; +/// use foundry_common::provider::ethers::get_http_provider; /// /// let retry_provider = get_http_provider("http://localhost:8545"); /// ``` @@ -281,7 +281,7 @@ where _ => unreachable!(), }; let estimator = Polygon::new(chain)?.category(GasCategory::Standard); - return Ok(estimator.estimate_eip1559_fees().await?) + return Ok(estimator.estimate_eip1559_fees().await?); } _ => {} } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs new file mode 100644 index 000000000000..cbd9ecbd00ef --- /dev/null +++ b/crates/common/src/provider/mod.rs @@ -0,0 +1,7 @@ +//! Provider-related instantiation and usage utilities. + +pub mod alloy; +pub mod ethers; +pub mod retry; +pub mod runtime_transport; +pub mod tower; diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs new file mode 100644 index 000000000000..e640728e0d6a --- /dev/null +++ b/crates/common/src/provider/retry.rs @@ -0,0 +1,104 @@ +//! An utility trait for retrying requests based on the error type. See [TransportError]. +use alloy_json_rpc::ErrorPayload; +use alloy_transport::TransportError; +use serde::Deserialize; + +/// [RetryPolicy] defines logic for which [JsonRpcClient::Error] instances should +/// the client retry the request and try to recover from. +pub trait RetryPolicy: Send + Sync + std::fmt::Debug { + /// Whether to retry the request based on the given `error` + fn should_retry(&self, error: &TransportError) -> bool; + + /// Providers may include the `backoff` in the error response directly + fn backoff_hint(&self, error: &TransportError) -> Option; +} + +/// Implements [RetryPolicy] that will retry requests that errored with +/// status code 429 i.e. TOO_MANY_REQUESTS +/// +/// Infura often fails with a `"header not found"` rpc error which is apparently linked to load +/// balancing, which are retried as well. +#[derive(Clone, Debug, Default)] +pub struct RateLimitRetryPolicy; + +impl RetryPolicy for RateLimitRetryPolicy { + fn should_retry(&self, error: &TransportError) -> bool { + match error { + TransportError::Transport(_) => true, + // The transport could not serialize the error itself. The request was malformed from + // the start. + TransportError::SerError(_) => false, + TransportError::DeserError { text, .. } => { + // some providers send invalid JSON RPC in the error case (no `id:u64`), but the + // text should be a `JsonRpcError` + #[derive(Deserialize)] + struct Resp { + error: ErrorPayload, + } + + if let Ok(resp) = serde_json::from_str::(text) { + return should_retry_json_rpc_error(&resp.error) + } + false + } + TransportError::ErrorResp(err) => should_retry_json_rpc_error(err), + } + } + + fn backoff_hint(&self, error: &TransportError) -> Option { + if let TransportError::ErrorResp(resp) = error { + let data = resp.try_data_as::(); + if let Some(Ok(data)) = data { + // if daily rate limit exceeded, infura returns the requested backoff in the error + // response + let backoff_seconds = &data["rate"]["backoff_seconds"]; + // infura rate limit error + if let Some(seconds) = backoff_seconds.as_u64() { + return Some(std::time::Duration::from_secs(seconds)) + } + if let Some(seconds) = backoff_seconds.as_f64() { + return Some(std::time::Duration::from_secs(seconds as u64 + 1)) + } + } + } + None + } +} + +/// Analyzes the [ErrorPayload] and decides if the request should be retried based on the +/// error code or the message. +fn should_retry_json_rpc_error(error: &ErrorPayload) -> bool { + let ErrorPayload { code, message, .. } = error; + // alchemy throws it this way + if *code == 429 { + return true + } + + // This is an infura error code for `exceeded project rate limit` + if *code == -32005 { + return true + } + + // alternative alchemy error for specific IPs + if *code == -32016 && message.contains("rate limit") { + return true + } + + // quick node rate limit error: `100/second request limit reached - reduce calls per second or + // upgrade your account at quicknode.com` + if *code == -32007 && message.contains("request limit reached") { + return true + } + + match message.as_str() { + // this is commonly thrown by infura and is apparently a load balancer issue, see also + "header not found" => true, + // also thrown by infura if out of budget for the day and ratelimited + "daily request count exceeded, request rate limited" => true, + msg => { + msg.contains("rate limit") || + msg.contains("too many requests") || + msg.contains("request limit") + } + } +} diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs new file mode 100644 index 000000000000..67f83998d44c --- /dev/null +++ b/crates/common/src/provider/runtime_transport.rs @@ -0,0 +1,310 @@ +//! Runtime transport that connects on first request, which can take either of an HTTP, +//! WebSocket, or IPC transport and supports retries based on CUPS logic. +use crate::REQUEST_TIMEOUT; +use alloy_json_rpc::{RequestPacket, ResponsePacket}; +use alloy_pubsub::{PubSubConnect, PubSubFrontend}; +use alloy_transport::{ + Authorization, BoxTransport, TransportError, TransportErrorKind, TransportFut, +}; +use alloy_transport_http::Http; +use alloy_transport_ipc::IpcConnect; +use alloy_transport_ws::WsConnect; +use ethers_providers::{JwtAuth, JwtKey}; +use reqwest::header::{HeaderName, HeaderValue}; +use std::{path::PathBuf, str::FromStr, sync::Arc}; +use thiserror::Error; +use tokio::sync::RwLock; +use tower::Service; +use url::Url; + +/// An enum representing the different transports that can be used to connect to a runtime. +/// Only meant to be used internally by [RuntimeTransport]. +#[derive(Clone, Debug)] +pub enum InnerTransport { + /// HTTP transport + Http(Http), + /// WebSocket transport + Ws(PubSubFrontend), + // TODO: IPC + /// IPC transport + Ipc(PubSubFrontend), +} + +/// Error type for the runtime transport. +#[derive(Error, Debug)] +pub enum RuntimeTransportError { + /// Internal transport error + #[error(transparent)] + TransportError(TransportError), + + /// Failed to lock the transport + #[error("Failed to lock the transport")] + LockError, + + /// Invalid URL scheme + #[error("URL scheme is not supported: {0}")] + BadScheme(String), + + /// Invalid HTTP header + #[error("Invalid HTTP header: {0}")] + BadHeader(String), + + /// Invalid file path + #[error("Invalid IPC file path: {0}")] + BadPath(String), + + /// Invalid construction of Http provider + #[error(transparent)] + HttpConstructionError(#[from] reqwest::Error), + + /// Invalid JWT + #[error("Invalid JWT: {0}")] + InvalidJwt(String), +} + +/// A runtime transport is a custom [alloy_transport::Transport] that only connects when the *first* +/// request is made. When the first request is made, it will connect to the runtime using either an +/// HTTP WebSocket, or IPC transport depending on the URL used. +/// It also supports retries for rate-limiting and timeout-related errors. +#[derive(Clone, Debug, Error)] +pub struct RuntimeTransport { + /// The inner actual transport used. + inner: Arc>>, + /// The URL to connect to. + url: Url, + /// The headers to use for requests. + headers: Vec, + /// The JWT to use for requests. + jwt: Option, + /// The timeout for requests. + timeout: std::time::Duration, +} + +/// A builder for [RuntimeTransport]. +#[derive(Debug)] +pub struct RuntimeTransportBuilder { + url: Url, + headers: Vec, + jwt: Option, + timeout: std::time::Duration, +} + +impl RuntimeTransportBuilder { + /// Create a new builder with the given URL. + pub fn new(url: Url) -> Self { + Self { url, headers: vec![], jwt: None, timeout: REQUEST_TIMEOUT } + } + + /// Set the URL for the transport. + pub fn with_headers(mut self, headers: Vec) -> Self { + self.headers = headers; + self + } + + /// Set the JWT for the transport. + pub fn with_jwt(mut self, jwt: Option) -> Self { + self.jwt = jwt; + self + } + + /// Set the timeout for the transport. + pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self { + self.timeout = timeout; + self + } + + /// Builds the [RuntimeTransport] and returns it in a disconnected state. + /// The runtime transport will then connect when the first request happens. + pub fn build(self) -> RuntimeTransport { + RuntimeTransport { + inner: Arc::new(RwLock::new(None)), + url: self.url, + headers: self.headers, + jwt: self.jwt, + timeout: self.timeout, + } + } +} + +impl ::core::fmt::Display for RuntimeTransport { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "RuntimeTransport {}", self.url) + } +} + +impl RuntimeTransport { + /// Connects the underlying transport, depending on the URL scheme. + pub async fn connect(&self) -> Result { + match self.url.scheme() { + "http" | "https" => self.connect_http().await, + "ws" | "wss" => self.connect_ws().await, + "file" => self.connect_ipc().await, + _ => Err(RuntimeTransportError::BadScheme(self.url.scheme().to_string())), + } + } + + /// Connects to an HTTP [alloy_transport_http::Http] transport. + async fn connect_http(&self) -> Result { + let mut client_builder = reqwest::Client::builder().timeout(self.timeout); + let mut headers = reqwest::header::HeaderMap::new(); + + // If there's a JWT, add it to the headers if we can decode it. + if let Some(jwt) = self.jwt.clone() { + let auth = + build_auth(jwt).map_err(|e| RuntimeTransportError::InvalidJwt(e.to_string()))?; + + let mut auth_value: HeaderValue = + HeaderValue::from_str(&auth.to_string()).expect("Header should be valid string"); + auth_value.set_sensitive(true); + + headers.insert(reqwest::header::AUTHORIZATION, auth_value); + }; + + // Add any custom headers. + for header in self.headers.iter() { + let make_err = || RuntimeTransportError::BadHeader(header.to_string()); + + let (key, val) = header.split_once(':').ok_or_else(make_err)?; + + headers.insert( + HeaderName::from_str(key.trim()).map_err(|_| make_err())?, + HeaderValue::from_str(val.trim()).map_err(|_| make_err())?, + ); + } + + client_builder = client_builder.default_headers(headers); + + let client = + client_builder.build().map_err(RuntimeTransportError::HttpConstructionError)?; + + // todo: retry tower layer + Ok(InnerTransport::Http(Http::with_client(client, self.url.clone()))) + } + + /// Connects to a WS transport. + async fn connect_ws(&self) -> Result { + let auth = self.jwt.as_ref().and_then(|jwt| build_auth(jwt.clone()).ok()); + let ws = WsConnect { url: self.url.to_string(), auth } + .into_service() + .await + .map_err(RuntimeTransportError::TransportError)?; + Ok(InnerTransport::Ws(ws)) + } + + /// Connects to an IPC transport. + async fn connect_ipc(&self) -> Result { + let path = url_to_file_path(&self.url) + .map_err(|_| RuntimeTransportError::BadPath(self.url.to_string()))?; + let ipc_connector: IpcConnect = path.into(); + let ipc = + ipc_connector.into_service().await.map_err(RuntimeTransportError::TransportError)?; + Ok(InnerTransport::Ipc(ipc)) + } + + /// Sends a request using the underlying transport. + /// If this is the first request, it will connect to the appropiate transport depending on the + /// URL scheme. When sending the request, retries will be automatically handled depending + /// on the parameters set on the [RuntimeTransport]. + /// For sending the actual request, this action is delegated down to the + /// underlying transport through Tower's [tower::Service::call]. See tower's [tower::Service] + /// trait for more information. + pub fn request(&self, req: RequestPacket) -> TransportFut<'static> { + let this = self.clone(); + Box::pin(async move { + if this.inner.read().await.is_none() { + let mut inner = this.inner.write().await; + *inner = Some(this.connect().await.map_err(TransportErrorKind::custom)?) + } + + let mut inner = this.inner.write().await; + // SAFETY: We just checked that the inner transport exists. + let inner_mut = inner.as_mut().expect("We should have an inner transport."); + + match inner_mut { + InnerTransport::Http(http) => http.call(req), + InnerTransport::Ws(ws) => ws.call(req), + InnerTransport::Ipc(ipc) => ipc.call(req), + } + .await + }) + } + + /// Convert this transport into a boxed trait object. + pub fn boxed(self) -> BoxTransport + where + Self: Sized + Clone + Send + Sync + 'static, + { + BoxTransport::new(self) + } +} + +impl tower::Service for RuntimeTransport { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + #[inline] + fn poll_ready( + &mut self, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } + + #[inline] + fn call(&mut self, req: RequestPacket) -> Self::Future { + self.request(req) + } +} + +impl Service for &RuntimeTransport { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + #[inline] + fn poll_ready( + &mut self, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } + + #[inline] + fn call(&mut self, req: RequestPacket) -> Self::Future { + self.request(req) + } +} + +fn build_auth(jwt: String) -> eyre::Result { + // Decode jwt from hex, then generate claims (iat with current timestamp) + let jwt = hex::decode(jwt)?; + let secret = JwtKey::from_slice(&jwt).map_err(|err| eyre::eyre!("Invalid JWT: {}", err))?; + let auth = JwtAuth::new(secret, None, None); + let token = auth.generate_token()?; + + // Essentially unrolled ethers-rs new_with_auth to accomodate the custom timeout + let auth = Authorization::Bearer(token); + + Ok(auth) +} + +#[cfg(windows)] +fn url_to_file_path(url: &Url) -> Result { + const PREFIX: &str = "file:///pipe/"; + + let url_str = url.as_str(); + + if url_str.starts_with(PREFIX) { + let pipe_name = &url_str[PREFIX.len()..]; + let pipe_path = format!(r"\\.\pipe\{}", pipe_name); + return Ok(PathBuf::from(pipe_path)); + } + + url.to_file_path() +} + +#[cfg(not(windows))] +fn url_to_file_path(url: &Url) -> Result { + url.to_file_path() +} diff --git a/crates/common/src/provider/tower.rs b/crates/common/src/provider/tower.rs new file mode 100644 index 000000000000..5e08b836c991 --- /dev/null +++ b/crates/common/src/provider/tower.rs @@ -0,0 +1,192 @@ +//! Alloy-related tower middleware for retrying rate-limited requests +//! and applying backoff. +use std::{ + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, + task::{Context, Poll}, +}; + +use alloy_json_rpc::{RequestPacket, ResponsePacket}; +use alloy_transport::{TransportError, TransportErrorKind, TransportFut}; +use tower::Service; + +use super::{ + retry::{RateLimitRetryPolicy, RetryPolicy}, + runtime_transport::RuntimeTransport, +}; + +/// An Alloy Tower Layer that is responsible for retrying requests based on the +/// error type. See [TransportError]. +#[derive(Debug, Clone)] +pub struct RetryBackoffLayer { + /// The maximum number of retries for rate limit errors + max_rate_limit_retries: u32, + /// The maximum number of retries for timeout errors + max_timeout_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + /// The number of compute units per second for this provider + compute_units_per_second: u64, +} + +impl RetryBackoffLayer { + /// Creates a new [RetryWithPolicyLayer] with the given parameters + pub fn new( + max_rate_limit_retries: u32, + max_timeout_retries: u32, + initial_backoff: u64, + compute_units_per_second: u64, + ) -> Self { + Self { + max_rate_limit_retries, + max_timeout_retries, + initial_backoff, + compute_units_per_second, + } + } +} + +impl tower::layer::Layer for RetryBackoffLayer { + type Service = RetryBackoffService; + + fn layer(&self, inner: S) -> Self::Service { + RetryBackoffService { + inner, + policy: RateLimitRetryPolicy, + max_rate_limit_retries: self.max_rate_limit_retries, + max_timeout_retries: self.max_timeout_retries, + initial_backoff: self.initial_backoff, + compute_units_per_second: self.compute_units_per_second, + requests_enqueued: Arc::new(AtomicU32::new(0)), + } + } +} + +/// An Alloy Tower Service that is responsible for retrying requests based on the +/// error type. See [TransportError] and [RetryWithPolicyLayer]. +#[derive(Debug, Clone)] +pub struct RetryBackoffService { + /// The inner service + inner: S, + /// The retry policy + policy: RateLimitRetryPolicy, + /// The maximum number of retries for rate limit errors + max_rate_limit_retries: u32, + /// The maximum number of retries for timeout errors + max_timeout_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + /// The number of compute units per second for this service + compute_units_per_second: u64, + /// The number of requests currently enqueued + requests_enqueued: Arc, +} + +// impl tower service +impl Service for RetryBackoffService { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + // Our middleware doesn't care about backpressure, so it's ready as long + // as the inner service is ready. + self.inner.poll_ready(cx) + } + + fn call(&mut self, request: RequestPacket) -> Self::Future { + let mut this = self.clone(); + Box::pin(async move { + let ahead_in_queue = this.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64; + let mut rate_limit_retry_number: u32 = 0; + let mut timeout_retries: u32 = 0; + loop { + let err; + let fut = this.inner.call(request.clone()).await; + + match fut { + Ok(res) => { + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Ok(res) + } + Err(e) => err = e, + } + + let err = TransportError::from(err); + let should_retry = this.policy.should_retry(&err); + if should_retry { + rate_limit_retry_number += 1; + if rate_limit_retry_number > this.max_rate_limit_retries { + return Err(TransportErrorKind::custom_str("Max retries exceeded")) + } + + let current_queued_reqs = this.requests_enqueued.load(Ordering::SeqCst) as u64; + + // try to extract the requested backoff from the error or compute the next + // backoff based on retry count + let mut next_backoff = this + .policy + .backoff_hint(&err) + .unwrap_or_else(|| std::time::Duration::from_millis(this.initial_backoff)); + + // requests are usually weighted and can vary from 10 CU to several 100 CU, + // cheaper requests are more common some example alchemy + // weights: + // - `eth_getStorageAt`: 17 + // - `eth_getBlockByNumber`: 16 + // - `eth_newFilter`: 20 + // + // (coming from forking mode) assuming here that storage request will be the + // driver for Rate limits we choose `17` as the average cost + // of any request + const AVG_COST: u64 = 17u64; + let seconds_to_wait_for_compute_budget = compute_unit_offset_in_secs( + AVG_COST, + this.compute_units_per_second, + current_queued_reqs, + ahead_in_queue, + ); + next_backoff += + std::time::Duration::from_secs(seconds_to_wait_for_compute_budget); + + tokio::time::sleep(next_backoff).await; + } else { + if timeout_retries < this.max_timeout_retries { + timeout_retries += 1; + continue; + } + + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Err(TransportErrorKind::custom_str("Max retries exceeded")) + } + } + }) + } +} + +/// Calculates an offset in seconds by taking into account the number of currently queued requests, +/// number of requests that were ahead in the queue when the request was first issued, the average +/// cost a weighted request (heuristic), and the number of available compute units per seconds. +/// +/// Returns the number of seconds (the unit the remote endpoint measures compute budget) a request +/// is supposed to wait to not get rate limited. The budget per second is +/// `compute_units_per_second`, assuming an average cost of `avg_cost` this allows (in theory) +/// `compute_units_per_second / avg_cost` requests per seconds without getting rate limited. +/// By taking into account the number of concurrent request and the position in queue when the +/// request was first issued and determine the number of seconds a request is supposed to wait, if +/// at all +fn compute_unit_offset_in_secs( + avg_cost: u64, + compute_units_per_second: u64, + current_queued_requests: u64, + ahead_in_queue: u64, +) -> u64 { + let request_capacity_per_second = compute_units_per_second.saturating_div(avg_cost); + if current_queued_requests > request_capacity_per_second { + current_queued_requests.min(ahead_in_queue).saturating_div(request_capacity_per_second) + } else { + 0 + } +} diff --git a/crates/common/src/runtime_client.rs b/crates/common/src/runtime_client.rs index fc53d87caea8..ea0fe593830c 100644 --- a/crates/common/src/runtime_client.rs +++ b/crates/common/src/runtime_client.rs @@ -1,5 +1,5 @@ //! Wrap different providers - +// todo: remove use async_trait::async_trait; use ethers_core::types::U256; use ethers_providers::{ @@ -261,7 +261,7 @@ fn url_to_file_path(url: &Url) -> Result { if url_str.starts_with(PREFIX) { let pipe_name = &url_str[PREFIX.len()..]; let pipe_path = format!(r"\\.\pipe\{}", pipe_name); - return Ok(PathBuf::from(pipe_path)) + return Ok(PathBuf::from(pipe_path)); } url.to_file_path() diff --git a/crates/common/src/serde_helpers.rs b/crates/common/src/serde_helpers.rs new file mode 100644 index 000000000000..c1b61b6e2424 --- /dev/null +++ b/crates/common/src/serde_helpers.rs @@ -0,0 +1,129 @@ +//! Misc Serde helpers for foundry crates. + +use alloy_primitives::U256; +use serde::{de, Deserialize, Deserializer}; +use std::str::FromStr; + +/// Helper type to parse both `u64` and `U256` +#[derive(Copy, Clone, Deserialize)] +#[serde(untagged)] +pub enum Numeric { + /// A [U256] value. + U256(U256), + /// A `u64` value. + Num(u64), +} + +impl From for U256 { + fn from(n: Numeric) -> U256 { + match n { + Numeric::U256(n) => n, + Numeric::Num(n) => U256::from(n), + } + } +} + +impl FromStr for Numeric { + type Err = String; + + fn from_str(s: &str) -> Result { + if let Ok(val) = s.parse::() { + Ok(Numeric::U256(U256::from(val))) + } else if s.starts_with("0x") { + U256::from_str_radix(s, 16).map(Numeric::U256).map_err(|err| err.to_string()) + } else { + U256::from_str(s).map(Numeric::U256).map_err(|err| err.to_string()) + } + } +} + +/// Deserializes the input into an `Option`, using [`from_int_or_hex`] to deserialize the +/// inner value. +pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + match Option::::deserialize(deserializer)? { + Some(val) => val.try_into_u256().map(Some), + None => Ok(None), + } +} + +/// An enum that represents either a [serde_json::Number] integer, or a hex [U256]. +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum NumberOrHexU256 { + /// An integer + Int(serde_json::Number), + /// A hex U256 + Hex(U256), +} + +impl NumberOrHexU256 { + /// Tries to convert this into a [U256]]. + pub fn try_into_u256(self) -> Result { + match self { + NumberOrHexU256::Int(num) => { + U256::from_str(num.to_string().as_str()).map_err(E::custom) + } + NumberOrHexU256::Hex(val) => Ok(val), + } + } +} + +/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with +/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). +pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + NumberOrHexU256::deserialize(deserializer)?.try_into_u256() +} + +/// Helper type to deserialize sequence of numbers +#[derive(Deserialize)] +#[serde(untagged)] +pub enum NumericSeq { + /// Single parameter sequence (e.g [1]) + Seq([Numeric; 1]), + /// U256 + U256(U256), + /// Native u64 + Num(u64), +} + +/// Deserializes a number from hex or int +pub fn deserialize_number<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Numeric::deserialize(deserializer).map(Into::into) +} + +/// Deserializes a number from hex or int, but optionally +pub fn deserialize_number_opt<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let num = match Option::::deserialize(deserializer)? { + Some(Numeric::U256(n)) => Some(n), + Some(Numeric::Num(n)) => Some(U256::from(n)), + _ => None, + }; + + Ok(num) +} + +/// Deserializes single integer params: `1, [1], ["0x01"]` +pub fn deserialize_number_seq<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let num = match NumericSeq::deserialize(deserializer)? { + NumericSeq::Seq(seq) => seq[0].into(), + NumericSeq::U256(n) => n, + NumericSeq::Num(n) => U256::from(n), + }; + + Ok(num) +} diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 0413805a16aa..4247cc1eb808 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -1,12 +1,16 @@ //! Temporary utility conversion traits between ethers-rs and alloy types. use alloy_json_abi::{Event, EventParam, Function, InternalType, Param, StateMutability}; -use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U256, U64}; +use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U128, U256, U64}; +use alloy_rpc_types::{AccessList, AccessListItem, CallInput, CallRequest, Signature, Transaction}; use ethers_core::{ abi as ethabi, types::{ - Bloom as EthersBloom, Bytes as EthersBytes, H160, H256, H64, I256 as EthersI256, - U256 as EthersU256, U64 as EthersU64, + transaction::eip2930::{ + AccessList as EthersAccessList, AccessListItem as EthersAccessListItem, + }, + Bloom as EthersBloom, Bytes as EthersBytes, TransactionRequest, H160, H256, H64, + I256 as EthersI256, U256 as EthersU256, U64 as EthersU64, }, }; @@ -100,6 +104,86 @@ impl ToAlloy for u64 { } } +impl ToAlloy for ethers_core::types::Transaction { + type To = Transaction; + + fn to_alloy(self) -> Self::To { + Transaction { + hash: self.hash.to_alloy(), + nonce: U64::from(self.nonce.as_u64()), + block_hash: self.block_hash.map(ToAlloy::to_alloy), + block_number: self.block_number.map(|b| U256::from(b.as_u64())), + transaction_index: self.transaction_index.map(|b| U256::from(b.as_u64())), + from: self.from.to_alloy(), + to: self.to.map(ToAlloy::to_alloy), + value: self.value.to_alloy(), + gas_price: self.gas_price.map(|a| U128::from(a.as_u128())), + gas: self.gas.to_alloy(), + max_fee_per_gas: self.max_fee_per_gas.map(|f| U128::from(f.as_u128())), + max_priority_fee_per_gas: self + .max_priority_fee_per_gas + .map(|f| U128::from(f.as_u128())), + max_fee_per_blob_gas: None, + input: self.input.0.into(), + signature: Some(Signature { + r: self.r.to_alloy(), + s: self.s.to_alloy(), + v: U256::from(self.v.as_u64()), + y_parity: None, + }), + chain_id: self.chain_id.map(|c| U64::from(c.as_u64())), + blob_versioned_hashes: Vec::new(), + access_list: self.access_list.map(|a| a.0.into_iter().map(ToAlloy::to_alloy).collect()), + transaction_type: self.transaction_type.map(|t| t.to_alloy()), + other: Default::default(), + } + } +} + +/// Converts from a [TransactionRequest] to a [CallRequest]. +pub fn to_call_request_from_tx_request(tx: TransactionRequest) -> CallRequest { + CallRequest { + from: tx.from.map(|f| f.to_alloy()), + to: match tx.to { + Some(to) => match to { + ethers_core::types::NameOrAddress::Address(addr) => Some(addr.to_alloy()), + ethers_core::types::NameOrAddress::Name(_) => None, + }, + None => None, + }, + gas_price: tx.gas_price.map(|g| g.to_alloy()), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + gas: tx.gas.map(|g| g.to_alloy()), + value: tx.value.map(|v| v.to_alloy()), + input: CallInput::maybe_input(tx.data.map(|b| b.0.into())), + nonce: tx.nonce.map(|n| U64::from(n.as_u64())), + chain_id: tx.chain_id.map(|c| c.to_alloy()), + access_list: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: None, + transaction_type: None, + } +} + +impl ToAlloy for EthersAccessList { + type To = AccessList; + fn to_alloy(self) -> Self::To { + AccessList(self.0.into_iter().map(ToAlloy::to_alloy).collect()) + } +} + +impl ToAlloy for EthersAccessListItem { + type To = AccessListItem; + + fn to_alloy(self) -> Self::To { + AccessListItem { + address: self.address.to_alloy(), + storage_keys: self.storage_keys.into_iter().map(ToAlloy::to_alloy).collect(), + } + } +} + impl ToAlloy for ethabi::Event { type To = Event; diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index d482a329b663..36cc8c7b6a83 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -26,10 +26,9 @@ impl RpcEndpoints { endpoints: endpoints .into_iter() .map(|(name, e)| match e.into() { - RpcEndpointType::String(url) => ( - name.into(), - RpcEndpointConfig { endpoint: url.into(), ..Default::default() }, - ), + RpcEndpointType::String(url) => { + (name.into(), RpcEndpointConfig { endpoint: url, ..Default::default() }) + } RpcEndpointType::Config(config) => (name.into(), config), }) .collect(), @@ -79,7 +78,7 @@ impl RpcEndpointType { /// Returns the config variant pub fn as_endpoint_config(&self) -> Option<&RpcEndpointConfig> { match self { - RpcEndpointType::Config(config) => Some(&config), + RpcEndpointType::Config(config) => Some(config), RpcEndpointType::String(_) => None, } } @@ -207,15 +206,15 @@ impl<'de> Deserialize<'de> for RpcEndpoint { } } -impl Into for RpcEndpoint { - fn into(self) -> RpcEndpointType { - RpcEndpointType::String(self) +impl From for RpcEndpointType { + fn from(endpoint: RpcEndpoint) -> Self { + RpcEndpointType::String(endpoint) } } -impl Into for RpcEndpoint { - fn into(self) -> RpcEndpointConfig { - RpcEndpointConfig { endpoint: self, ..Default::default() } +impl From for RpcEndpointConfig { + fn from(endpoint: RpcEndpoint) -> Self { + RpcEndpointConfig { endpoint, ..Default::default() } } } @@ -276,7 +275,7 @@ impl Serialize for RpcEndpointConfig { self.compute_units_per_second.is_none() { // serialize as endpoint if there's no additional config - return self.endpoint.serialize(serializer); + self.endpoint.serialize(serializer) } else { let mut map = serializer.serialize_map(Some(4))?; map.serialize_entry("endpoint", &self.endpoint)?; @@ -317,9 +316,9 @@ impl<'de> Deserialize<'de> for RpcEndpointConfig { } } -impl Into for RpcEndpointConfig { - fn into(self) -> RpcEndpointType { - RpcEndpointType::Config(self) +impl From for RpcEndpointType { + fn from(config: RpcEndpointConfig) -> Self { + RpcEndpointType::Config(config) } } diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index 4bd501354f11..95b111bdb991 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -30,7 +30,7 @@ /// Metadata::default() /// } /// -/// fn data(&self) -> Result, Error> { +/// fn data(&self) -> std::result::Result, Error> { /// let value = Value::serialize(self)?; /// let error = InvalidType(value.to_actual(), "map".into()); /// let mut dict = value.into_dict().ok_or(error)?; @@ -137,7 +137,7 @@ macro_rules! impl_figment_convert { /// Metadata::default() /// } /// -/// fn data(&self) -> Result, Error> { +/// fn data(&self) -> std::result::Result, Error> { /// todo!() /// } /// } @@ -155,7 +155,7 @@ macro_rules! impl_figment_convert { /// Metadata::default() /// } /// -/// fn data(&self) -> Result, Error> { +/// fn data(&self) -> std::result::Result, Error> { /// todo!() /// } /// } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 51d9ad6df13c..3671e08e36b8 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -31,9 +31,12 @@ revm = { workspace = true, default-features = false, features = [ "arbitrary", "optimism", ] } +alloy-providers = { workspace = true } +alloy-transport = { workspace = true } +alloy-rpc-types = { workspace = true } +ethers = { workspace = true, features = ["ethers-solc"] } ethers-core.workspace = true -ethers-providers.workspace = true derive_more.workspace = true eyre = "0.6" diff --git a/crates/evm/core/src/abi/console.rs b/crates/evm/core/src/abi/console.rs index ce8dc6d5752c..54014359b9dd 100644 --- a/crates/evm/core/src/abi/console.rs +++ b/crates/evm/core/src/abi/console.rs @@ -1,7 +1,6 @@ use alloy_primitives::{hex, I256, U256}; use alloy_sol_types::sol; use derive_more::Display; -use foundry_common::types::ToEthers; use itertools::Itertools; // TODO: Use `UiFmt` @@ -84,9 +83,8 @@ fn format_units_int(x: &I256, decimals: &U256) -> String { } fn format_units_uint(x: &U256, decimals: &U256) -> String { - // TODO: rm ethers_core - match ethers_core::utils::format_units(x.to_ethers(), decimals.saturating_to::()) { - Ok(s) => s, - Err(_) => x.to_string(), + match alloy_primitives::utils::Unit::new(decimals.saturating_to::()) { + Some(units) => alloy_primitives::utils::ParseUnits::U256(*x).format_units(units), + None => x.to_string(), } } diff --git a/crates/evm/core/src/backend/error.rs b/crates/evm/core/src/backend/error.rs index b22c768d404d..df5b094a63f8 100644 --- a/crates/evm/core/src/backend/error.rs +++ b/crates/evm/core/src/backend/error.rs @@ -1,5 +1,5 @@ use alloy_primitives::{Address, B256, U256}; -use ethers_core::types::BlockId; +use alloy_rpc_types::BlockId; use futures::channel::mpsc::{SendError, TrySendError}; use std::{ convert::Infallible, diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 3ba7dea53ef1..f2f1a355d6f5 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -7,15 +7,9 @@ use crate::{ utils::configure_tx_env, }; use alloy_primitives::{b256, keccak256, Address, B256, U256, U64}; -use ethers_core::{ - types::{Block, BlockNumber, Transaction}, - utils::GenesisAccount, -}; -use foundry_common::{ - is_known_system_sender, - types::{ToAlloy, ToEthers}, - SYSTEM_TRANSACTION_TYPE, -}; +use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use ethers::utils::GenesisAccount; +use foundry_common::{is_known_system_sender, types::ToAlloy, SYSTEM_TRANSACTION_TYPE}; use revm::{ db::{CacheDB, DatabaseRef}, inspectors::NoOpInspector, @@ -306,7 +300,7 @@ pub trait DatabaseExt: Database { /// Returns an error if [`Self::has_cheatcode_access`] returns `false` fn ensure_cheatcode_access(&self, account: Address) -> Result<(), DatabaseError> { if !self.has_cheatcode_access(account) { - return Err(DatabaseError::NoCheats(account)) + return Err(DatabaseError::NoCheats(account)); } Ok(()) } @@ -315,7 +309,7 @@ pub trait DatabaseExt: Database { /// in forking mode fn ensure_cheatcode_access_forking_mode(&self, account: Address) -> Result<(), DatabaseError> { if self.is_forked_mode() { - return self.ensure_cheatcode_access(account) + return self.ensure_cheatcode_access(account); } Ok(()) } @@ -626,7 +620,7 @@ impl Backend { .cloned() .unwrap_or_default() .present_value(); - return value.as_le_bytes()[1] != 0 + return value.as_le_bytes()[1] != 0; } false @@ -639,7 +633,7 @@ impl Backend { if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS) { let slot: U256 = GLOBAL_FAILURE_SLOT.into(); let value = account.storage.get(&slot).cloned().unwrap_or_default().present_value(); - return value == revm::primitives::U256::from(1) + return value == revm::primitives::U256::from(1); } false @@ -741,7 +735,7 @@ impl Backend { all_logs.extend(f.journaled_state.logs.clone()) } }); - return all_logs + return all_logs; } logs @@ -846,26 +840,27 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(U64, Block)> { + ) -> eyre::Result<(U64, Block)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.db.db.get_transaction(transaction)?; // get the block number we need to fork if let Some(tx_block) = tx.block_number { - let block = fork.db.db.get_full_block(BlockNumber::Number(tx_block))?; + let block = fork.db.db.get_full_block(tx_block.to::())?; // we need to subtract 1 here because we want the state before the transaction // was mined - let fork_block = tx_block - 1; - Ok((U64::from(fork_block.as_u64()), block)) + let fork_block = tx_block.to::() - 1; + Ok((U64::from(fork_block), block)) } else { - let block = fork.db.db.get_full_block(BlockNumber::Latest)?; + let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; let number = block + .header .number - .ok_or_else(|| DatabaseError::BlockNotFound(BlockNumber::Latest.into()))?; + .ok_or_else(|| DatabaseError::BlockNotFound(BlockNumberOrTag::Latest.into()))?; - Ok((U64::from(number.as_u64()), block)) + Ok((number.to::(), block)) } } @@ -884,27 +879,33 @@ impl Backend { let fork_id = self.ensure_fork_id(id)?.clone(); let fork = self.inner.get_fork_by_id_mut(id)?; - let full_block = fork - .db - .db - .get_full_block(BlockNumber::Number(env.block.number.to_ethers().as_u64().into()))?; - - for tx in full_block.transactions.into_iter() { - // System transactions such as on L2s don't contain any pricing info so we skip them - // otherwise this would cause reverts - if is_known_system_sender(tx.from.to_alloy()) || - tx.transaction_type.map(|ty| ty.as_u64()) == Some(SYSTEM_TRANSACTION_TYPE) - { - continue - } + let full_block = fork.db.db.get_full_block(env.block.number.to::())?; + + if let BlockTransactions::Full(txs) = full_block.transactions { + for tx in txs.into_iter() { + // System transactions such as on L2s don't contain any pricing info so we skip them + // otherwise this would cause reverts + if is_known_system_sender(tx.from) || + tx.transaction_type.map(|ty| ty.to::()) == Some(SYSTEM_TRANSACTION_TYPE) + { + continue; + } - if tx.hash == tx_hash.to_ethers() { - // found the target transaction - return Ok(Some(tx)) + if tx.hash == tx_hash { + // found the target transaction + return Ok(Some(tx)) + } + trace!(tx=?tx.hash, "committing transaction"); + + commit_transaction( + tx, + env.clone(), + journaled_state, + fork, + &fork_id, + NoOpInspector, + )?; } - trace!(tx=?tx.hash, "committing transaction"); - - commit_transaction(tx, env.clone(), journaled_state, fork, &fork_id, NoOpInspector)?; } Ok(None) @@ -1048,7 +1049,7 @@ impl DatabaseExt for Backend { trace!(?id, "select fork"); if self.is_active_fork(id) { // nothing to do - return Ok(()) + return Ok(()); } let fork_id = self.ensure_fork_id(id).cloned()?; @@ -1209,13 +1210,13 @@ impl DatabaseExt for Backend { self.roll_fork(Some(id), fork_block.to(), env, journaled_state)?; // update the block's env accordingly - env.block.timestamp = block.timestamp.to_alloy(); - env.block.coinbase = block.author.unwrap_or_default().to_alloy(); - env.block.difficulty = block.difficulty.to_alloy(); - env.block.prevrandao = block.mix_hash.map(|h| h.to_alloy()); - env.block.basefee = block.base_fee_per_gas.unwrap_or_default().to_alloy(); - env.block.gas_limit = block.gas_limit.to_alloy(); - env.block.number = block.number.map(|n| n.to_alloy()).unwrap_or(fork_block).to(); + env.block.timestamp = block.header.timestamp; + env.block.coinbase = block.header.miner; + env.block.difficulty = block.header.difficulty; + env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); + env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); + env.block.gas_limit = block.header.gas_limit; + env.block.number = block.header.number.map(|n| n.to()).unwrap_or(fork_block.to()); // replay all transactions that came before let env = env.clone(); @@ -1263,7 +1264,7 @@ impl DatabaseExt for Backend { fn ensure_fork(&self, id: Option) -> eyre::Result { if let Some(id) = id { if self.inner.issued_local_fork_ids.contains_key(&id) { - return Ok(id) + return Ok(id); } eyre::bail!("Requested fork `{}` does not exit", id) } @@ -1289,7 +1290,7 @@ impl DatabaseExt for Backend { if self.inner.forks.len() == 1 { // we only want to provide additional diagnostics here when in multifork mode with > 1 // forks - return None + return None; } if !active_fork.is_contract(callee) && !is_contract_in_state(journaled_state, callee) { @@ -1316,7 +1317,7 @@ impl DatabaseExt for Backend { active: active_id, available_on, }) - } + }; } None } @@ -1506,7 +1507,7 @@ impl Fork { pub fn is_contract(&self, acc: Address) -> bool { if let Ok(Some(acc)) = self.db.basic_ref(acc) { if acc.code_hash != KECCAK_EMPTY { - return true + return true; } } is_contract_in_state(&self.journaled_state, acc) @@ -1844,7 +1845,7 @@ fn merge_db_account_data( acc } else { // Account does not exist - return + return; }; if let Some(code) = active.contracts.get(&acc.info.code_hash).cloned() { diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index a68a1220715d..7932edf19ca7 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -4,15 +4,10 @@ use crate::{ fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; -use ethers_core::{ - abi::ethereum_types::BigEndianHash, - types::{Block, BlockId, NameOrAddress, Transaction}, -}; -use ethers_providers::Middleware; -use foundry_common::{ - types::{ToAlloy, ToEthers}, - NON_ARCHIVE_NODE_WARNING, -}; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag, Transaction}; +use eyre::WrapErr; +use foundry_common::NON_ARCHIVE_NODE_WARNING; use futures::{ channel::mpsc::{channel, Receiver, Sender}, stream::Stream, @@ -38,20 +33,15 @@ type AccountFuture = Pin, Address)> + Send>>; type StorageFuture = Pin, Address, U256)> + Send>>; type BlockHashFuture = Pin, u64)> + Send>>; -type FullBlockFuture = Pin< - Box< - dyn Future>, Err>, BlockId)> - + Send, - >, ->; -type TransactionFuture = Pin< - Box, Err>, B256)> + Send>, ->; +type FullBlockFuture = + Pin, Err>, BlockId)> + Send>>; +type TransactionFuture = + Pin, B256)> + Send>>; type AccountInfoSender = OneshotSender>; type StorageSender = OneshotSender>; type BlockHashSender = OneshotSender>; -type FullBlockSender = OneshotSender>>; +type FullBlockSender = OneshotSender>; type TransactionSender = OneshotSender>; /// Request variants that are executed by the provider @@ -84,13 +74,13 @@ enum BackendRequest { /// /// This handler will remain active as long as it is reachable (request channel still open) and /// requests are in progress. -#[must_use = "futures do nothing unless polled"] -pub struct BackendHandler { - provider: M, +#[must_use = "BackendHandler does nothing unless polled."] +pub struct BackendHandler

{ + provider: P, /// Stores all the data. db: BlockchainDb, /// Requests currently in progress - pending_requests: Vec>, + pending_requests: Vec>, /// Listeners that wait for a `get_account` related response account_requests: HashMap>, /// Listeners that wait for a `get_storage_at` response @@ -106,12 +96,12 @@ pub struct BackendHandler { block_id: Option, } -impl BackendHandler +impl

BackendHandler

where - M: Middleware + Clone + 'static, + P: TempProvider + Clone + 'static, { fn new( - provider: M, + provider: P, db: BlockchainDb, rx: Receiver, block_id: Option, @@ -191,15 +181,8 @@ where let fut = Box::pin(async move { // serialize & deserialize back to U256 let idx_req = B256::from(idx); - let storage = provider - .get_storage_at( - NameOrAddress::Address(address.to_ethers()), - idx_req.to_ethers(), - block_id, - ) - .await; - let storage = storage.map(|storage| storage.into_uint()).map(|s| s.to_alloy()); - (storage, address, idx) + let storage = provider.get_storage_at(address, idx_req, block_id).await; + (storage.wrap_err("could not fetch slot {idx} from {address}"), address, idx) }); self.pending_requests.push(ProviderRequest::Storage(fut)); } @@ -207,19 +190,16 @@ where } /// returns the future that fetches the account data - fn get_account_req(&self, address: Address) -> ProviderRequest { + fn get_account_req(&self, address: Address) -> ProviderRequest { trace!(target: "backendhandler", "preparing account request, address={:?}", address); let provider = self.provider.clone(); let block_id = self.block_id; let fut = Box::pin(async move { - let balance = - provider.get_balance(NameOrAddress::Address(address.to_ethers()), block_id); - let nonce = provider - .get_transaction_count(NameOrAddress::Address(address.to_ethers()), block_id); - let code = provider.get_code(NameOrAddress::Address(address.to_ethers()), block_id); - let resp = tokio::try_join!(balance, nonce, code).map(|(balance, nonce, code)| { - (balance.to_alloy(), nonce.to_alloy(), Bytes::from(code.0)) - }); + let balance = provider.get_balance(address, block_id); + let nonce = provider.get_transaction_count(address, block_id); + let code = + provider.get_code_at(address, block_id.unwrap_or(BlockNumberOrTag::Latest.into())); + let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); ProviderRequest::Account(fut) @@ -242,7 +222,8 @@ where fn request_full_block(&mut self, number: BlockId, sender: FullBlockSender) { let provider = self.provider.clone(); let fut = Box::pin(async move { - let block = provider.get_block_with_txs(number).await; + let block = + provider.get_block(number, true).await.wrap_err("could not fetch block {number:?}"); (sender, block, number) }); @@ -253,7 +234,10 @@ where fn request_transaction(&mut self, tx: B256, sender: TransactionSender) { let provider = self.provider.clone(); let fut = Box::pin(async move { - let block = provider.get_transaction(tx.to_ethers()).await; + let block = provider + .get_transaction_by_hash(tx) + .await + .wrap_err("could not get transaction {tx}"); (sender, block, tx) }); @@ -267,28 +251,32 @@ where entry.get_mut().push(listener); } Entry::Vacant(entry) => { - trace!(target: "backendhandler", "preparing block hash request, number={}", number); + trace!(target: "backendhandler", number, "preparing block hash request"); entry.insert(vec![listener]); let provider = self.provider.clone(); let fut = Box::pin(async move { - let block = provider.get_block(number).await; + let block = provider + .get_block_by_number(number.into(), false) + .await + .wrap_err("failed to get block"); let block_hash = match block { Ok(Some(block)) => Ok(block + .header .hash .expect("empty block hash on mined block, this should never happen")), Ok(None) => { warn!(target: "backendhandler", ?number, "block not found"); // if no block was returned then the block does not exist, in which case // we return empty hash - Ok(KECCAK_EMPTY.to_ethers()) + Ok(KECCAK_EMPTY) } Err(err) => { error!(target: "backendhandler", %err, ?number, "failed to get block"); Err(err) } }; - (block_hash.map(|h| h.to_alloy()), number) + (block_hash, number) }); self.pending_requests.push(ProviderRequest::BlockHash(fut)); } @@ -296,9 +284,9 @@ where } } -impl Future for BackendHandler +impl

Future for BackendHandler

where - M: Middleware + Clone + Unpin + 'static, + P: TempProvider + Clone + Unpin + 'static, { type Output = (); @@ -318,7 +306,7 @@ where } Poll::Ready(None) => { trace!(target: "backendhandler", "last sender dropped, ready to drop (&flush cache)"); - return Poll::Ready(()) + return Poll::Ready(()); } Poll::Pending => break, } @@ -334,7 +322,7 @@ where let (balance, nonce, code) = match resp { Ok(res) => res, Err(err) => { - let err = Arc::new(eyre::Error::new(err)); + let err = Arc::new(err); if let Some(listeners) = pin.account_requests.remove(&addr) { listeners.into_iter().for_each(|l| { let _ = l.send(Err(DatabaseError::GetAccount( @@ -343,7 +331,7 @@ where ))); }) } - continue + continue; } }; @@ -369,7 +357,7 @@ where let _ = l.send(Ok(acc.clone())); }) } - continue + continue; } } ProviderRequest::Storage(fut) => { @@ -378,7 +366,7 @@ where Ok(value) => value, Err(err) => { // notify all listeners - let err = Arc::new(eyre::Error::new(err)); + let err = Arc::new(err); if let Some(listeners) = pin.storage_requests.remove(&(addr, idx)) { @@ -390,7 +378,7 @@ where ))); }) } - continue + continue; } }; @@ -403,7 +391,7 @@ where let _ = l.send(Ok(value)); }) } - continue + continue; } } ProviderRequest::BlockHash(fut) => { @@ -411,7 +399,7 @@ where let value = match block_hash { Ok(value) => value, Err(err) => { - let err = Arc::new(eyre::Error::new(err)); + let err = Arc::new(err); // notify all listeners if let Some(listeners) = pin.block_requests.remove(&number) { listeners.into_iter().for_each(|l| { @@ -421,7 +409,7 @@ where ))); }) } - continue + continue; } }; @@ -434,7 +422,7 @@ where let _ = l.send(Ok(value)); }) } - continue + continue; } } ProviderRequest::FullBlock(fut) => { @@ -443,26 +431,25 @@ where Ok(Some(block)) => Ok(block), Ok(None) => Err(DatabaseError::BlockNotFound(number)), Err(err) => { - let err = Arc::new(eyre::Error::new(err)); + let err = Arc::new(err); Err(DatabaseError::GetFullBlock(number, err)) } }; let _ = sender.send(msg); - continue + continue; } } ProviderRequest::Transaction(fut) => { if let Poll::Ready((sender, tx, tx_hash)) = fut.poll_unpin(cx) { let msg = match tx { - Ok(Some(tx)) => Ok(tx), - Ok(None) => Err(DatabaseError::TransactionNotFound(tx_hash)), + Ok(tx) => Ok(tx), Err(err) => { - let err = Arc::new(eyre::Error::new(err)); + let err = Arc::new(err); Err(DatabaseError::GetTransaction(tx_hash, err)) } }; let _ = sender.send(msg); - continue + continue; } } } @@ -473,7 +460,7 @@ where // If no new requests have been queued, break to // be polled again later. if pin.queued_requests.is_empty() { - return Poll::Pending + return Poll::Pending; } } } @@ -526,9 +513,9 @@ impl SharedBackend { /// dropped. /// /// NOTE: this should be called with `Arc` - pub async fn spawn_backend(provider: M, db: BlockchainDb, pin_block: Option) -> Self + pub async fn spawn_backend

(provider: P, db: BlockchainDb, pin_block: Option) -> Self where - M: Middleware + Unpin + 'static + Clone, + P: TempProvider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); // spawn the provider handler to a task @@ -539,13 +526,13 @@ impl SharedBackend { /// Same as `Self::spawn_backend` but spawns the `BackendHandler` on a separate `std::thread` in /// its own `tokio::Runtime` - pub fn spawn_backend_thread( - provider: M, + pub fn spawn_backend_thread

( + provider: P, db: BlockchainDb, pin_block: Option, ) -> Self where - M: Middleware + Unpin + 'static + Clone, + P: TempProvider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); @@ -568,13 +555,13 @@ impl SharedBackend { } /// Returns a new `SharedBackend` and the `BackendHandler` - pub fn new( - provider: M, + pub fn new

( + provider: P, db: BlockchainDb, pin_block: Option, - ) -> (Self, BackendHandler) + ) -> (Self, BackendHandler

) where - M: Middleware + Unpin + 'static + Clone, + P: TempProvider + Clone + 'static, { let (backend, backend_rx) = channel(1); let cache = Arc::new(FlushJsonBlockCacheDB(Arc::clone(db.cache()))); @@ -589,7 +576,7 @@ impl SharedBackend { } /// Returns the full block for the given block identifier - pub fn get_full_block(&self, block: impl Into) -> DatabaseResult> { + pub fn get_full_block(&self, block: impl Into) -> DatabaseResult { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::FullBlock(block.into(), sender); @@ -672,7 +659,7 @@ impl DatabaseRef for SharedBackend { fn block_hash_ref(&self, number: U256) -> Result { if number > U256::from(u64::MAX) { - return Ok(KECCAK_EMPTY) + return Ok(KECCAK_EMPTY); } let number: U256 = number; let number = number.to(); @@ -695,7 +682,7 @@ mod tests { fork::{BlockchainDbMeta, CreateFork, JsonBlockCacheDB}, opts::EvmOpts, }; - use foundry_common::get_http_provider; + use foundry_common::provider::alloy::get_http_provider; use foundry_config::{Config, NamedChain}; use std::{collections::BTreeSet, path::PathBuf, sync::Arc}; const ENDPOINT: &str = "https://mainnet.infura.io/v3/40bee2d557ed4b52908c3e62345a3d8b"; @@ -754,7 +741,7 @@ mod tests { async fn can_read_write_cache() { let provider = get_http_provider(ENDPOINT); - let block_num = provider.get_block_number().await.unwrap().as_u64(); + let block_num = provider.get_block_number().await.unwrap(); let config = Config::figment(); let mut evm_opts = config.extract::().unwrap(); diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 67fdc90547d4..fdc34dbafa7d 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -6,7 +6,7 @@ use crate::{ snapshot::Snapshots, }; use alloy_primitives::{Address, B256, U256}; -use ethers_core::types::BlockId; +use alloy_rpc_types::BlockId; use parking_lot::Mutex; use revm::{ db::{CacheDB, DatabaseRef}, @@ -267,7 +267,7 @@ impl DatabaseRef for ForkDbSnapshot { mod tests { use super::*; use crate::fork::BlockchainDbMeta; - use foundry_common::get_http_provider; + use foundry_common::provider::alloy::get_http_provider; use std::collections::BTreeSet; /// Demonstrates that `Database::basic` for `ForkedDatabase` will always return the diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index 35c15f6d96f2..11e65916498d 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,40 +1,32 @@ use crate::utils::apply_chain_and_block_specific_env_changes; use alloy_primitives::{Address, U256}; -use ethers_core::types::{Block, TxHash}; -use ethers_providers::Middleware; +use alloy_providers::provider::TempProvider; +use alloy_rpc_types::{Block, BlockNumberOrTag}; use eyre::WrapErr; -use foundry_common::{types::ToAlloy, NON_ARCHIVE_NODE_WARNING}; -use futures::TryFutureExt; +use foundry_common::NON_ARCHIVE_NODE_WARNING; + use revm::primitives::{BlockEnv, CfgEnv, Env, TxEnv}; /// Initializes a REVM block environment based on a forked /// ethereum provider. -pub async fn environment( - provider: &M, +// todo(onbjerg): these bounds needed cus of the bounds in `Provider`, can simplify? +pub async fn environment( + provider: &P, memory_limit: u64, gas_price: Option, override_chain_id: Option, pin_block: Option, origin: Address, -) -> eyre::Result<(Env, Block)> -where - M::Error: 'static, -{ +) -> eyre::Result<(Env, Block)> { let block_number = if let Some(pin_block) = pin_block { pin_block } else { - provider.get_block_number().await.wrap_err("Failed to get latest block number")?.as_u64() + provider.get_block_number().await.wrap_err("Failed to get latest block number")? }; let (fork_gas_price, rpc_chain_id, block) = tokio::try_join!( - provider - .get_gas_price() - .map_err(|err| { eyre::Error::new(err).wrap_err("Failed to get gas price") }), - provider - .get_chainid() - .map_err(|err| { eyre::Error::new(err).wrap_err("Failed to get chain id") }), - provider.get_block(block_number).map_err(|err| { - eyre::Error::new(err).wrap_err(format!("Failed to get block {block_number}")) - }) + provider.get_gas_price(), + provider.get_chain_id(), + provider.get_block_by_number(BlockNumberOrTag::Number(block_number), false) )?; let block = if let Some(block) = block { block @@ -43,7 +35,7 @@ where // If the `eth_getBlockByNumber` call succeeds, but returns null instead of // the block, and the block number is less than equal the latest block, then // the user is forking from a non-archive node with an older block number. - if block_number <= latest_block.as_u64() { + if block_number <= latest_block { error!("{NON_ARCHIVE_NODE_WARNING}"); } eyre::bail!( @@ -56,7 +48,7 @@ where }; let mut cfg = CfgEnv::default(); - cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id.as_u64()); + cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id.to::()); cfg.memory_limit = memory_limit; cfg.limit_contract_code_size = Some(usize::MAX); // EIP-3607 rejects transactions from senders with deployed code. @@ -67,20 +59,20 @@ where let mut env = Env { cfg, block: BlockEnv { - number: U256::from(block.number.expect("block number not found").as_u64()), - timestamp: block.timestamp.to_alloy(), - coinbase: block.author.unwrap_or_default().to_alloy(), - difficulty: block.difficulty.to_alloy(), - prevrandao: Some(block.mix_hash.map(|h| h.to_alloy()).unwrap_or_default()), - basefee: block.base_fee_per_gas.unwrap_or_default().to_alloy(), - gas_limit: block.gas_limit.to_alloy(), + number: block.header.number.expect("block number not found"), + timestamp: block.header.timestamp, + coinbase: block.header.miner, + difficulty: block.header.difficulty, + prevrandao: Some(block.header.mix_hash.unwrap_or_default()), + basefee: block.header.base_fee_per_gas.unwrap_or_default(), + gas_limit: block.header.gas_limit, ..Default::default() }, tx: TxEnv { caller: origin, - gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price.to_alloy()), - chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id.as_u64())), - gas_limit: block.gas_limit.as_u64(), + gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price), + chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id.to::())), + gas_limit: block.header.gas_limit.to::(), ..Default::default() }, }; diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 480869bff9d6..d09466871fdb 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,9 +4,11 @@ //! concurrently active pairs at once. use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; -use ethers_core::types::{BlockId, BlockNumber}; -use ethers_providers::Provider; -use foundry_common::{runtime_client::RuntimeClient, types::ToEthers, ProviderBuilder}; +use alloy_providers::provider::Provider; +use alloy_transport::BoxTransport; +use ethers::types::BlockNumber; +use foundry_common::provider::alloy::ProviderBuilder; + use foundry_config::Config; use futures::{ channel::mpsc::{channel, Receiver, Sender}, @@ -150,7 +152,7 @@ impl MultiFork { } } -type Handler = BackendHandler>>; +type Handler = BackendHandler>>; type CreateFuture = Pin> + Send>>; type CreateSender = OneshotSender>; @@ -225,7 +227,7 @@ impl MultiForkHandler { #[allow(irrefutable_let_patterns)] if let ForkTask::Create(_, in_progress, _, additional) = task { if in_progress == id { - return Some(additional) + return Some(additional); } } } @@ -243,7 +245,7 @@ impl MultiForkHandler { // there could already be a task for the requested fork in progress if let Some(in_progress) = self.find_in_progress_task(&fork_id) { in_progress.push(sender); - return + return; } // need to create a new fork @@ -304,7 +306,7 @@ impl Future for MultiForkHandler { Poll::Ready(None) => { // channel closed, but we still need to drive the fork handlers to completion trace!(target: "fork::multi", "request channel closed"); - break + break; } Poll::Pending => break, } @@ -365,7 +367,7 @@ impl Future for MultiForkHandler { if pin.handlers.is_empty() && pin.incoming.is_done() { trace!(target: "fork::multi", "completed"); - return Poll::Ready(()) + return Poll::Ready(()); } // periodically flush cached RPC state @@ -461,21 +463,17 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(CreatedFork, Handler // we need to use the block number from the block because the env's number can be different on // some L2s (e.g. Arbitrum). - let number = block - .number - .map(|num| num.as_u64()) - .unwrap_or_else(|| meta.block_env.number.to_ethers().as_u64()); + let number = block.header.number.unwrap_or(meta.block_env.number); // determine the cache path if caching is enabled let cache_path = if fork.enable_caching { - Config::foundry_block_cache_dir(meta.cfg_env.chain_id, number) + Config::foundry_block_cache_dir(meta.cfg_env.chain_id, number.to::()) } else { None }; let db = BlockchainDb::new(meta, cache_path); - let (backend, handler) = - SharedBackend::new(provider, db, Some(BlockId::Number(BlockNumber::Number(number.into())))); + let (backend, handler) = SharedBackend::new(provider, db, Some(number.to::().into())); let fork = CreatedFork::new(fork, backend); Ok((fork, handler)) } diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 6e1ae5aa0764..14b3e2cfcf33 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,10 +1,14 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; -use ethers_core::types::{Block, TxHash}; -use ethers_providers::{Middleware, Provider}; +use alloy_rpc_types::Block; +use ethers::providers::{Middleware, Provider}; use eyre::WrapErr; -use foundry_common::{self, ProviderBuilder, RpcUrl, ALCHEMY_FREE_TIER_CUPS}; +use foundry_common::{ + self, + provider::alloy::{ProviderBuilder, RpcUrl}, + ALCHEMY_FREE_TIER_CUPS, +}; use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::{Chain, Config}; use revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}; @@ -75,7 +79,7 @@ impl EvmOpts { pub async fn fork_evm_env( &self, fork_url: impl AsRef, - ) -> eyre::Result<(revm::primitives::Env, Block)> { + ) -> eyre::Result<(revm::primitives::Env, Block)> { let fork_url = fork_url.as_ref(); let provider = ProviderBuilder::new(fork_url) .compute_units_per_second(self.get_compute_units_per_second()) @@ -158,7 +162,7 @@ impl EvmOpts { /// - mainnet otherwise pub fn get_chain_id(&self) -> u64 { if let Some(id) = self.env.chain_id { - return id + return id; } self.get_remote_chain_id().unwrap_or(Chain::mainnet()).id() } @@ -171,7 +175,7 @@ impl EvmOpts { if self.no_rpc_rate_limit { u64::MAX } else if let Some(cups) = self.compute_units_per_second { - return cups + return cups; } else { ALCHEMY_FREE_TIER_CUPS } diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index fac8e7a5c4ff..996408198caf 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,8 +1,12 @@ use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::{Address, FixedBytes, B256}; -use ethers_core::types::{ActionType, Block, CallType, Chain, Transaction, H256, U256}; +use alloy_rpc_types::{Block, Transaction}; +use ethers::types::{ActionType, CallType, Chain, H256, U256}; use eyre::ContextCompat; use foundry_common::types::ToAlloy; +pub use foundry_compilers::utils::RuntimeOrHandle; +pub use revm::primitives::State as StateChangeset; + use revm::{ interpreter::{opcode, opcode::spec_opcode_gas, CallScheme, CreateInputs, InstructionResult}, primitives::{CreateScheme, Eval, Halt, SpecId, TransactTo}, @@ -10,10 +14,8 @@ use revm::{ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -pub use foundry_compilers::utils::RuntimeOrHandle; -pub use revm::primitives::State as StateChangeset; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +// TODO(onbjerg): Remove this and use `CallKind` from the tracer. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] #[derive(Default)] pub enum CallKind { @@ -140,21 +142,18 @@ pub fn halt_to_instruction_result(halt: Halt) -> InstructionResult { /// /// This checks for: /// - prevrandao mixhash after merge -pub fn apply_chain_and_block_specific_env_changes( - env: &mut revm::primitives::Env, - block: &Block, -) { +pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::Env, block: &Block) { if let Ok(chain) = Chain::try_from(env.cfg.chain_id) { - let block_number = block.number.unwrap_or_default(); + let block_number = block.header.number.unwrap_or_default(); match chain { Chain::Mainnet => { // after merge difficulty is supplanted with prevrandao EIP-4399 - if block_number.as_u64() >= 15_537_351u64 { + if block_number.to::() >= 15_537_351u64 { env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } - return + return; } Chain::Arbitrum | Chain::ArbitrumGoerli | @@ -173,7 +172,7 @@ pub fn apply_chain_and_block_specific_env_changes( } // if difficulty is `0` we assume it's past merge - if block.difficulty.is_zero() { + if block.header.difficulty.is_zero() { env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } } @@ -244,28 +243,29 @@ pub fn get_function( /// Configures the env for the transaction pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { - env.tx.caller = tx.from.to_alloy(); - env.tx.gas_limit = tx.gas.as_u64(); - env.tx.gas_price = tx.gas_price.unwrap_or_default().to_alloy(); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to_alloy()); - env.tx.nonce = Some(tx.nonce.as_u64()); + env.tx.caller = tx.from; + env.tx.gas_limit = tx.gas.to(); + env.tx.gas_price = tx.gas_price.unwrap_or_default().to(); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to()); + env.tx.nonce = Some(tx.nonce.to()); env.tx.access_list = tx .access_list .clone() .unwrap_or_default() - .0 .into_iter() .map(|item| { ( - item.address.to_alloy(), - item.storage_keys.into_iter().map(h256_to_u256_be).map(|g| g.to_alloy()).collect(), + item.address, + item.storage_keys + .into_iter() + .map(|key| alloy_primitives::U256::from_be_bytes(key.0)) + .collect(), ) }) .collect(); - env.tx.value = tx.value.to_alloy(); + env.tx.value = tx.value.to(); env.tx.data = alloy_primitives::Bytes(tx.input.0.clone()); - env.tx.transact_to = - tx.to.map(|tx| tx.to_alloy()).map(TransactTo::Call).unwrap_or_else(TransactTo::create) + env.tx.transact_to = tx.to.map(TransactTo::Call).unwrap_or_else(TransactTo::create) } /// Get the address of a contract creation diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 863de256d976..b57acc4e9b08 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -562,7 +562,7 @@ impl Executor { // network conditions - the actual gas price is kept in `self.block` and is applied by // the cheatcode handler if it is enabled block: BlockEnv { - basefee: U256::from(0), + basefee: U256::ZERO, gas_limit: self.gas_limit, ..self.env.block.clone() }, @@ -572,7 +572,7 @@ impl Executor { data, value, // As above, we set the gas price to 0. - gas_price: U256::from(0), + gas_price: U256::ZERO, gas_priority_fee: None, gas_limit: self.gas_limit.to(), ..self.env.tx.clone() diff --git a/crates/evm/evm/src/inspectors/mod.rs b/crates/evm/evm/src/inspectors/mod.rs index b781ce96c8dd..20dd340d3cda 100644 --- a/crates/evm/evm/src/inspectors/mod.rs +++ b/crates/evm/evm/src/inspectors/mod.rs @@ -3,7 +3,7 @@ pub use foundry_cheatcodes::{self as cheatcodes, Cheatcodes, CheatsConfig}; pub use foundry_evm_coverage::CoverageCollector; pub use foundry_evm_fuzz::Fuzzer; -pub use foundry_evm_traces::Tracer; +pub use foundry_evm_traces::{StackSnapshotType, TracingInspector, TracingInspectorConfig}; mod access_list; pub use access_list::AccessListTracer; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 35e9e67736fd..87cc29b51865 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -1,6 +1,6 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Debugger, Fuzzer, LogCollector, - TracePrinter, Tracer, + StackSnapshotType, TracePrinter, TracingInspector, TracingInspectorConfig, }; use alloy_primitives::{Address, Bytes, B256, U256}; use ethers_core::types::Log; @@ -207,7 +207,7 @@ pub struct InspectorStack { pub fuzzer: Option, pub log_collector: Option, pub printer: Option, - pub tracer: Option, + pub tracer: Option, } impl InspectorStack { @@ -289,7 +289,17 @@ impl InspectorStack { /// Set whether to enable the tracer. #[inline] pub fn tracing(&mut self, yes: bool) { - self.tracer = yes.then(Default::default); + self.tracer = yes.then(|| { + TracingInspector::new(TracingInspectorConfig { + record_steps: false, + record_memory_snapshots: false, + record_stack_snapshots: StackSnapshotType::None, + record_state_diff: false, + exclude_precompile_calls: false, + record_call_return_data: true, + record_logs: true, + }) + }); } /// Collects all the data gathered during inspection into a single struct. @@ -304,7 +314,7 @@ impl InspectorStack { cheatcodes.labels.clone().into_iter().map(|l| (l.0, l.1)).collect() }) .unwrap_or_default(), - traces: self.tracer.map(|tracer| tracer.traces), + traces: self.tracer.map(|tracer| tracer.get_traces().clone()), debug: self.debugger.map(|debugger| debugger.arena), coverage: self.coverage.map(|coverage| coverage.maps), script_wallets: self diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 62181c2300c9..72fded966fb5 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -49,6 +49,7 @@ pub struct BaseCounterExample { /// Contract name if it exists pub contract_name: Option, /// Traces + #[serde(skip)] pub traces: Option, #[serde(skip)] pub args: Vec, diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 7e11d23f8f54..d3fe2338e9b3 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -21,9 +21,7 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-sol-types.workspace = true -revm.workspace = true - -ethers-core.workspace = true +reth-revm-inspectors = { git = "https://github.com/paradigmxyz/reth/", branch = "main" } eyre = "0.6" futures = "0.3" diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index e37880249e74..f7c0abb8dd2e 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -2,11 +2,11 @@ use crate::{ identifier::{ AddressIdentity, LocalTraceIdentifier, SingleSignaturesIdentifier, TraceIdentifier, }, - CallTrace, CallTraceArena, TraceCallData, TraceLog, TraceRetData, + CallTrace, CallTraceArena, DecodedCallData, DecodedCallLog, DecodedCallTrace, }; use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt}; use alloy_json_abi::{Event, Function, JsonAbi as Abi}; -use alloy_primitives::{Address, Selector, B256}; +use alloy_primitives::{Address, Log, Selector, B256}; use foundry_common::{abi::get_indexed_event, fmt::format_token, SELECTOR_LEN}; use foundry_evm_core::{ abi::{Console, HardhatConsole, Vm, HARDHAT_CONSOLE_SELECTOR_PATCHES}, @@ -19,7 +19,6 @@ use foundry_evm_core::{ use itertools::Itertools; use once_cell::sync::OnceCell; use std::collections::{hash_map::Entry, BTreeMap, HashMap}; -use tracing::field; mod precompiles; @@ -198,11 +197,24 @@ impl CallTraceDecoder { #[inline(always)] fn addresses<'a>( &'a self, - trace: &'a CallTraceArena, + arena: &'a CallTraceArena, ) -> impl Iterator)> + 'a { - trace.addresses().into_iter().filter(|&(address, _)| { - !self.labels.contains_key(address) || !self.contracts.contains_key(address) - }) + arena + .nodes() + .iter() + .map(|node| { + ( + &node.trace.address, + if node.trace.kind.is_any_create() { + Some(node.trace.output.as_ref()) + } else { + None + }, + ) + }) + .filter(|(address, _)| { + !self.labels.contains_key(*address) || !self.contracts.contains_key(*address) + }) } fn collect_identities(&mut self, identities: Vec>) { @@ -250,45 +262,31 @@ impl CallTraceDecoder { } } - /// Decodes all nodes in the specified call trace. - pub async fn decode(&self, traces: &mut CallTraceArena) { - for node in &mut traces.arena { - self.decode_function(&mut node.trace).await; - for log in node.logs.iter_mut() { - self.decode_event(log).await; - } - } - } - - async fn decode_function(&self, trace: &mut CallTrace) { - let span = trace_span!("decode_function", label = field::Empty).entered(); - + pub async fn decode_function(&self, trace: &CallTrace) -> DecodedCallTrace { // Decode precompile - if precompiles::decode(trace, 1) { - return; + if let Some((label, func)) = precompiles::decode(trace, 1) { + return DecodedCallTrace { + label: Some(label), + return_data: None, + contract: None, + func: Some(func), + }; } // Set label - if trace.label.is_none() { - if let Some(label) = self.labels.get(&trace.address) { - span.record("label", label); - trace.label = Some(label.clone()); - } - } + let label = self.labels.get(&trace.address).cloned(); // Set contract name - if trace.contract.is_none() { - if let Some(contract) = self.contracts.get(&trace.address) { - trace.contract = Some(contract.clone()); - } - } - - let TraceCallData::Raw(cdata) = &trace.data else { return }; + let contract = self.contracts.get(&trace.address).cloned(); + let cdata = &trace.data; if trace.address == DEFAULT_CREATE2_DEPLOYER { - trace!("decoded as create2"); - trace.data = TraceCallData::Decoded { signature: "create2".to_string(), args: vec![] }; - return; + return DecodedCallTrace { + label, + return_data: None, + contract, + func: Some(DecodedCallData { signature: "create2".to_string(), args: vec![] }), + }; } if cdata.len() >= SELECTOR_LEN { @@ -307,50 +305,57 @@ impl CallTraceDecoder { &functions } }; - let [func, ..] = &functions[..] else { return }; - self.decode_function_input(trace, func); - self.decode_function_output(trace, functions); + let [func, ..] = &functions[..] else { + return DecodedCallTrace { label, return_data: None, contract, func: None }; + }; + + DecodedCallTrace { + label, + func: Some(self.decode_function_input(trace, func)), + return_data: self.decode_function_output(trace, functions), + contract, + } } else { let has_receive = self.receive_contracts.contains(&trace.address); let signature = if cdata.is_empty() && has_receive { "receive()" } else { "fallback()" }.into(); let args = if cdata.is_empty() { Vec::new() } else { vec![cdata.to_string()] }; - trace!(?signature, ?args, "decoded fallback data"); - trace.data = TraceCallData::Decoded { signature, args }; - - if let TraceRetData::Raw(rdata) = &trace.output { - if !trace.success { - let decoded = - decode::decode_revert(rdata, Some(&self.errors), Some(trace.status)); - trace!(?decoded, "decoded fallback output"); - trace.output = TraceRetData::Decoded(decoded); - } + DecodedCallTrace { + label, + return_data: if !trace.success { + Some(decode::decode_revert( + &trace.output, + Some(&self.errors), + Some(trace.status), + )) + } else { + None + }, + contract, + func: Some(DecodedCallData { signature, args }), } } } /// Decodes a function's input into the given trace. - fn decode_function_input(&self, trace: &mut CallTrace, func: &Function) { - let TraceCallData::Raw(data) = &trace.data else { return }; + fn decode_function_input(&self, trace: &CallTrace, func: &Function) -> DecodedCallData { let mut args = None; - if data.len() >= SELECTOR_LEN { + if trace.data.len() >= SELECTOR_LEN { if trace.address == CHEATCODE_ADDRESS { // Try to decode cheatcode inputs in a more custom way - if let Some(v) = self.decode_cheatcode_inputs(func, data) { + if let Some(v) = self.decode_cheatcode_inputs(func, &trace.data) { args = Some(v); } } if args.is_none() { - if let Ok(v) = func.abi_decode_input(&data[SELECTOR_LEN..], false) { + if let Ok(v) = func.abi_decode_input(&trace.data[SELECTOR_LEN..], false) { args = Some(v.iter().map(|value| self.apply_label(value)).collect()); } } } - let signature = func.signature(); - trace!(?signature, ?args, "decoded function input"); - trace.data = TraceCallData::Decoded { signature, args: args.unwrap_or_default() }; + DecodedCallData { signature: func.signature(), args: args.unwrap_or_default() } } /// Custom decoding for cheatcode inputs. @@ -420,38 +425,34 @@ impl CallTraceDecoder { } /// Decodes a function's output into the given trace. - fn decode_function_output(&self, trace: &mut CallTrace, funcs: &[Function]) { - let TraceRetData::Raw(data) = &trace.output else { return }; - let mut s = None; + fn decode_function_output(&self, trace: &CallTrace, funcs: &[Function]) -> Option { + let data = &trace.output; if trace.success { if trace.address == CHEATCODE_ADDRESS { - s = funcs.iter().find_map(|func| self.decode_cheatcode_outputs(func)); + if let Some(decoded) = + funcs.iter().find_map(|func| self.decode_cheatcode_outputs(func)) + { + return Some(decoded); + } } - if s.is_none() { - if let Some(values) = - funcs.iter().find_map(|func| func.abi_decode_output(data, false).ok()) - { - // Functions coming from an external database do not have any outputs specified, - // and will lead to returning an empty list of values. - if !values.is_empty() { - s = Some( - values - .iter() - .map(|value| self.apply_label(value)) - .format(", ") - .to_string(), - ); - } + if let Some(values) = + funcs.iter().find_map(|func| func.abi_decode_output(data, false).ok()) + { + // Functions coming from an external database do not have any outputs specified, + // and will lead to returning an empty list of values. + if values.is_empty() { + return None; } + + return Some( + values.iter().map(|value| self.apply_label(value)).format(", ").to_string(), + ); } - } else { - s = decode::maybe_decode_revert(data, Some(&self.errors), Some(trace.status)); - } - if let Some(decoded) = s { - trace!(?decoded, "decoded function output"); - trace.output = TraceRetData::Decoded(decoded); + None + } else { + Some(decode::decode_revert(data, Some(&self.errors), Some(trace.status))) } } @@ -468,29 +469,26 @@ impl CallTraceDecoder { } /// Decodes an event. - async fn decode_event(&self, log: &mut TraceLog) { - let TraceLog::Raw(raw_log) = log else { return }; - let &[t0, ..] = raw_log.topics() else { return }; + pub async fn decode_event<'a>(&self, log: &'a Log) -> DecodedCallLog<'a> { + let &[t0, ..] = log.topics() else { return DecodedCallLog::Raw(log) }; let mut events = Vec::new(); - let events = match self.events.get(&(t0, raw_log.topics().len() - 1)) { + let events = match self.events.get(&(t0, log.topics().len() - 1)) { Some(es) => es, None => { if let Some(identifier) = &self.signature_identifier { if let Some(event) = identifier.write().await.identify_event(&t0[..]).await { - events.push(get_indexed_event(event, raw_log)); + events.push(get_indexed_event(event, log)); } } &events } }; for event in events { - if let Ok(decoded) = event.decode_log(raw_log, false) { + if let Ok(decoded) = event.decode_log(log, false) { let params = reconstruct_params(event, &decoded); - let name = event.name.clone(); - trace!(?name, ?params, "decoded event"); - *log = TraceLog::Decoded( - name, + return DecodedCallLog::Decoded( + event.name.clone(), params .into_iter() .zip(event.inputs.iter()) @@ -501,9 +499,10 @@ impl CallTraceDecoder { }) .collect(), ); - break; } } + + DecodedCallLog::Raw(log) } fn apply_label(&self, value: &DynSolValue) -> String { diff --git a/crates/evm/traces/src/decoder/precompiles.rs b/crates/evm/traces/src/decoder/precompiles.rs index 801ec92df844..1475208f40c8 100644 --- a/crates/evm/traces/src/decoder/precompiles.rs +++ b/crates/evm/traces/src/decoder/precompiles.rs @@ -1,4 +1,4 @@ -use crate::{CallTrace, TraceCallData}; +use crate::{CallTrace, DecodedCallData}; use alloy_primitives::{B256, U256}; use alloy_sol_types::{abi, sol, SolCall}; use itertools::Itertools; @@ -36,20 +36,20 @@ macro_rules! tri { ($e:expr) => { match $e { Ok(x) => x, - Err(_) => return false, + Err(_) => return None, } }; } -/// Tries to decode a precompile call. Returns `true` if successful. -pub(super) fn decode(trace: &mut CallTrace, _chain_id: u64) -> bool { +/// Tries to decode a precompile call. Returns `Some` if successful. +pub(super) fn decode(trace: &CallTrace, _chain_id: u64) -> Option<(String, DecodedCallData)> { let [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x @ 0x01..=0x0a] = trace.address.0 .0 else { - return false + return None }; - let TraceCallData::Raw(data) = &trace.data else { return false }; + let data = &trace.data; let (signature, args) = match x { 0x01 => { @@ -74,13 +74,7 @@ pub(super) fn decode(trace: &mut CallTrace, _chain_id: u64) -> bool { 0x00 | 0x0b.. => unreachable!(), }; - // TODO: Other chain precompiles - - trace!(?signature, ?args, "decoded precompile call"); - trace.data = TraceCallData::Decoded { signature: signature.to_string(), args }; - trace.label = Some("PRECOMPILES".into()); - - true + Some(("PRECOMPILES".into(), DecodedCallData { signature: signature.to_string(), args })) } // Note: we use the ABI decoder, but this is not necessarily ABI-encoded data. It's just a diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index dc25ab3a7cec..3d5d09635807 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -6,22 +6,12 @@ #[macro_use] extern crate tracing; - -use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; -use ethers_core::types::{DefaultFrame, GethDebugTracingOptions, StructLog}; -use foundry_common::{ - contracts::{ContractsByAddress, ContractsByArtifact}, - types::ToEthers, -}; -use foundry_evm_core::{constants::CHEATCODE_ADDRESS, debug::Instruction, utils::CallKind}; -use hashbrown::HashMap; -use itertools::Itertools; -use revm::interpreter::{opcode, CallContext, InstructionResult, Stack}; +use alloy_primitives::{Log, U256}; +use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_core::constants::CHEATCODE_ADDRESS; +use futures::{future::BoxFuture, FutureExt}; use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeMap, HashSet}, - fmt, -}; +use std::{collections::BTreeMap, fmt::Write}; use yansi::{Color, Paint}; /// Call trace address identifiers. @@ -33,160 +23,38 @@ use identifier::LocalTraceIdentifier; mod decoder; pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; -mod inspector; -pub use inspector::Tracer; - -mod node; -pub use node::CallTraceNode; +use reth_revm_inspectors::tracing::types::LogCallOrder; +pub use reth_revm_inspectors::tracing::{ + types::{CallKind, CallTrace, CallTraceNode}, + CallTraceArena, GethTraceBuilder, ParityTraceBuilder, StackSnapshotType, TracingInspector, + TracingInspectorConfig, +}; pub type Traces = Vec<(TraceKind, CallTraceArena)>; -/// An arena of [CallTraceNode]s -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallTraceArena { - /// The arena of nodes - pub arena: Vec, +#[derive(Default, Debug, Eq, PartialEq)] +pub struct DecodedCallData { + pub signature: String, + pub args: Vec, } -impl Default for CallTraceArena { - fn default() -> Self { - CallTraceArena { arena: vec![Default::default()] } - } +#[derive(Default, Debug)] +pub struct DecodedCallTrace { + pub label: Option, + pub return_data: Option, + pub func: Option, + pub contract: Option, } -impl CallTraceArena { - /// Pushes a new trace into the arena, returning the trace ID - pub fn push_trace(&mut self, entry: usize, new_trace: CallTrace) -> usize { - match new_trace.depth { - // The entry node, just update it - 0 => { - self.arena[0].trace = new_trace; - 0 - } - // We found the parent node, add the new trace as a child - _ if self.arena[entry].trace.depth == new_trace.depth - 1 => { - let id = self.arena.len(); - - let trace_location = self.arena[entry].children.len(); - self.arena[entry].ordering.push(LogCallOrder::Call(trace_location)); - let node = CallTraceNode { - parent: Some(entry), - trace: new_trace, - idx: id, - ..Default::default() - }; - self.arena.push(node); - self.arena[entry].children.push(id); - - id - } - // We haven't found the parent node, go deeper - _ => self.push_trace( - *self.arena[entry].children.last().expect("Disconnected trace"), - new_trace, - ), - } - } - - pub fn addresses(&self) -> HashSet<(&Address, Option<&[u8]>)> { - self.arena - .iter() - .map(|node| { - if node.trace.created() { - if let TraceRetData::Raw(bytes) = &node.trace.output { - return (&node.trace.address, Some(bytes.as_ref())) - } - } - - (&node.trace.address, None) - }) - .collect() - } - - // Recursively fill in the geth trace by going through the traces - fn add_to_geth_trace( - &self, - storage: &mut HashMap>, - trace_node: &CallTraceNode, - struct_logs: &mut Vec, - opts: &GethDebugTracingOptions, - ) { - let mut child_id = 0; - // Iterate over the steps inside the given trace - for step in trace_node.trace.steps.iter() { - let mut log: StructLog = step.into(); - - // Fill in memory and storage depending on the options - if !opts.disable_storage.unwrap_or_default() { - let contract_storage = storage.entry(step.contract).or_default(); - if let Some((key, value)) = step.state_diff { - contract_storage.insert(B256::from(key), B256::from(value)); - log.storage = Some( - contract_storage - .iter_mut() - .map(|t| (t.0.to_ethers(), t.1.to_ethers())) - .collect(), - ); - } - } - if opts.disable_stack.unwrap_or_default() { - log.stack = None; - } - if !opts.enable_memory.unwrap_or_default() { - log.memory = None; - } - - // Add step to geth trace - struct_logs.push(log); - - // Descend into a child trace if the step was a call - if let Instruction::OpCode( - opcode::CREATE | - opcode::CREATE2 | - opcode::DELEGATECALL | - opcode::CALL | - opcode::STATICCALL | - opcode::CALLCODE, - ) = step.op - { - self.add_to_geth_trace( - storage, - &self.arena[trace_node.children[child_id]], - struct_logs, - opts, - ); - child_id += 1; - } - } - } - - /// Generate a geth-style trace e.g. for debug_traceTransaction - pub fn geth_trace( - &self, - receipt_gas_used: U256, - opts: GethDebugTracingOptions, - ) -> DefaultFrame { - if self.arena.is_empty() { - return Default::default() - } - - let mut storage = HashMap::new(); - // Fetch top-level trace - let main_trace_node = &self.arena[0]; - let main_trace = &main_trace_node.trace; - // Start geth trace - let mut acc = DefaultFrame { - // If the top-level trace succeeded, then it was a success - failed: !main_trace.success, - gas: receipt_gas_used.to_ethers(), - return_value: main_trace.output.to_bytes().to_ethers(), - ..Default::default() - }; - - self.add_to_geth_trace(&mut storage, main_trace_node, &mut acc.struct_logs, &opts); - - acc - } +#[derive(Debug)] +pub enum DecodedCallLog<'a> { + /// A raw log. + Raw(&'a Log), + /// A decoded log. + /// + /// The first member of the tuple is the event name, and the second is a vector of decoded + /// parameters. + Decoded(String, Vec<(String, String)>), } const PIPE: &str = " │ "; @@ -195,19 +63,27 @@ const BRANCH: &str = " ├─ "; const CALL: &str = "→ "; const RETURN: &str = "← "; -impl fmt::Display for CallTraceArena { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn inner( - arena: &CallTraceArena, - f: &mut fmt::Formatter<'_>, - idx: usize, - left: &str, - child: &str, - ) -> fmt::Result { - let node = &arena.arena[idx]; +/// Render a collection of call traces. +/// +/// The traces will be decoded using the given decoder, if possible. +pub async fn render_trace_arena( + arena: &CallTraceArena, + decoder: &CallTraceDecoder, +) -> Result { + fn inner<'a>( + arena: &'a [CallTraceNode], + decoder: &'a CallTraceDecoder, + s: &'a mut String, + idx: usize, + left: &'a str, + child: &'a str, + ) -> BoxFuture<'a, Result<(), std::fmt::Error>> { + async move { + let node = &arena[idx]; // Display trace header - writeln!(f, "{left}{}", node.trace)?; + let (trace, return_data) = render_trace(&node.trace, decoder).await?; + writeln!(s, "{left}{}", trace)?; // Display logs and subcalls let left_prefix = format!("{child}{BRANCH}"); @@ -215,11 +91,12 @@ impl fmt::Display for CallTraceArena { for child in &node.ordering { match child { LogCallOrder::Log(index) => { - let log = node.logs[*index].to_string(); + let log = render_trace_log(&node.logs[*index], decoder).await?; + // Prepend our tree structure symbols to each line of the displayed log log.lines().enumerate().try_for_each(|(i, line)| { writeln!( - f, + s, "{}{}", if i == 0 { &left_prefix } else { &right_prefix }, line @@ -227,332 +104,146 @@ impl fmt::Display for CallTraceArena { })?; } LogCallOrder::Call(index) => { - inner(arena, f, node.children[*index], &left_prefix, &right_prefix)?; + inner( + arena, + decoder, + s, + node.children[*index], + &left_prefix, + &right_prefix, + ) + .await?; } } } // Display trace return data let color = trace_color(&node.trace); - write!(f, "{child}{EDGE}{}", color.paint(RETURN))?; - if node.trace.created() { - match &node.trace.output { - TraceRetData::Raw(bytes) => { - writeln!(f, "{} bytes of code", bytes.len())?; + write!(s, "{child}{EDGE}{}", color.paint(RETURN))?; + if node.trace.kind.is_any_create() { + match &return_data { + None => { + writeln!(s, "{} bytes of code", node.trace.data.len())?; } - TraceRetData::Decoded(val) => { - writeln!(f, "{val}")?; + Some(val) => { + writeln!(s, "{val}")?; } } } else { - writeln!(f, "{}", node.trace.output)?; + match &return_data { + None if node.trace.output.is_empty() => writeln!(s, "()")?, + None => writeln!(s, "{}", node.trace.output)?, + Some(val) => writeln!(s, "{val}")?, + } } Ok(()) } - - inner(self, f, 0, " ", " ") + .boxed() } -} -/// A raw or decoded log. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TraceLog { - /// A raw log - Raw(RawLog), - /// A decoded log. - /// - /// The first member of the tuple is the event name, and the second is a vector of decoded - /// parameters. - Decoded(String, Vec<(String, String)>), + let mut s = String::new(); + inner(arena.nodes(), decoder, &mut s, 0, " ", " ").await?; + Ok(s) } -impl fmt::Display for TraceLog { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TraceLog::Raw(log) => { - for (i, topic) in log.topics().iter().enumerate() { - writeln!( - f, - "{:>13}: {}", - if i == 0 { "emit topic 0".to_string() } else { format!("topic {i}") }, - Paint::cyan(format!("{topic:?}")) - )?; - } - - write!(f, " data: {}", Paint::cyan(hex::encode_prefixed(&log.data))) - } - TraceLog::Decoded(name, params) => { - let params = params - .iter() - .map(|(name, value)| format!("{name}: {value}")) - .collect::>() - .join(", "); - - write!(f, "emit {}({params})", Paint::cyan(name.clone())) - } - } - } -} - -/// Ordering enum for calls and logs +/// Render a call trace. /// -/// i.e. if Call 0 occurs before Log 0, it will be pushed into the `CallTraceNode`'s ordering before -/// the log. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum LogCallOrder { - Log(usize), - Call(usize), -} - -/// Raw or decoded calldata. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum TraceCallData { - /// Raw calldata bytes. - Raw(Bytes), - /// Decoded calldata. - Decoded { - /// The function signature. - signature: String, - /// The function arguments. - args: Vec, - }, -} - -impl Default for TraceCallData { - fn default() -> Self { - Self::Raw(Bytes::new()) - } -} - -impl TraceCallData { - pub fn as_bytes(&self) -> &[u8] { - match self { - TraceCallData::Raw(raw) => raw, - TraceCallData::Decoded { .. } => &[], - } - } -} - -/// Raw or decoded return data. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum TraceRetData { - /// Raw return data. - Raw(Bytes), - /// Decoded return data. - Decoded(String), -} - -impl Default for TraceRetData { - fn default() -> Self { - Self::Raw(Bytes::new()) - } -} - -impl TraceRetData { - /// Returns the data as [`Bytes`] - pub fn to_bytes(&self) -> Bytes { - match self { - TraceRetData::Raw(raw) => raw.clone(), - TraceRetData::Decoded(val) => val.as_bytes().to_vec().into(), - } - } - - pub fn to_raw(&self) -> Vec { - self.to_bytes().to_vec() - } -} - -impl fmt::Display for TraceRetData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self { - TraceRetData::Raw(bytes) => { - if bytes.is_empty() { - write!(f, "()") +/// The trace will be decoded using the given decoder, if possible. +pub async fn render_trace( + trace: &CallTrace, + decoder: &CallTraceDecoder, +) -> Result<(String, Option), std::fmt::Error> { + let mut s = String::new(); + write!(&mut s, "[{}] ", trace.gas_used)?; + let address = trace.address.to_checksum(None); + + let decoded = decoder.decode_function(trace).await; + if trace.kind.is_any_create() { + write!( + &mut s, + "{}{} {}@{}", + Paint::yellow(CALL), + Paint::yellow("new"), + decoded.label.as_deref().unwrap_or(""), + address + )?; + } else { + let (func_name, inputs) = match &decoded.func { + Some(DecodedCallData { signature, args }) => { + let name = signature.split('(').next().unwrap(); + (name.to_string(), args.join(", ")) + } + None => { + debug!(target: "evm::traces", trace=?trace, "unhandled raw calldata"); + if trace.data.len() < 4 { + ("fallback".to_string(), hex::encode(&trace.data)) } else { - bytes.fmt(f) + let (selector, data) = trace.data.split_at(4); + (hex::encode(selector), hex::encode(data)) } } - TraceRetData::Decoded(decoded) => f.write_str(decoded), - } - } -} + }; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallTraceStep { - // Fields filled in `step` - /// Call depth - pub depth: u64, - /// Program counter before step execution - pub pc: usize, - /// Opcode to be executed - pub op: Instruction, - /// Current contract address - pub contract: Address, - /// Stack before step execution - pub stack: Stack, - /// Memory before step execution - pub memory: Vec, - /// Remaining gas before step execution - pub gas: u64, - /// Gas refund counter before step execution - pub gas_refund_counter: u64, - - // Fields filled in `step_end` - /// Gas cost of step execution - pub gas_cost: u64, - /// Change of the contract state after step execution (effect of the SLOAD/SSTORE instructions) - pub state_diff: Option<(U256, U256)>, - /// Error (if any) after step execution - pub error: Option, -} + let action = match trace.kind { + CallKind::Call => "", + CallKind::StaticCall => " [staticcall]", + CallKind::CallCode => " [callcode]", + CallKind::DelegateCall => " [delegatecall]", + CallKind::Create | CallKind::Create2 => unreachable!(), + }; -impl From<&CallTraceStep> for StructLog { - fn from(step: &CallTraceStep) -> Self { - StructLog { - depth: step.depth, - error: step.error.clone(), - gas: step.gas, - gas_cost: step.gas_cost, - memory: Some(convert_memory(&step.memory)), - op: step.op.to_string(), - pc: step.pc as u64, - refund_counter: if step.gas_refund_counter > 0 { - Some(step.gas_refund_counter) + let color = trace_color(trace); + write!( + &mut s, + "{addr}::{func_name}{opt_value}({inputs}){action}", + addr = color.paint(decoded.label.as_deref().unwrap_or(&address)), + func_name = color.paint(func_name), + opt_value = if trace.value == U256::ZERO { + String::new() } else { - None + format!("{{value: {}}}", trace.value) }, - stack: Some(step.stack.data().iter().copied().map(|v| v.to_ethers()).collect_vec()), - // Filled in `CallTraceArena::geth_trace` as a result of compounding all slot changes - storage: None, - return_data: None, - mem_size: None, - } + action = Paint::yellow(action), + )?; } -} -/// A trace of a call. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallTrace { - /// The depth of the call - pub depth: usize, - /// Whether the call was successful - pub success: bool, - /// The name of the contract, if any. - /// - /// The format is `":"` for easy lookup in local contracts. - /// - /// This member is not used by the core call tracing functionality (decoding/displaying). The - /// intended use case is for other components that may want to process traces by specific - /// contracts (e.g. gas reports). - pub contract: Option, - /// The label for the destination address, if any - pub label: Option, - /// caller of this call - pub caller: Address, - /// The destination address of the call or the address from the created contract - pub address: Address, - /// The kind of call this is - pub kind: CallKind, - /// The value transferred in the call - pub value: U256, - /// The calldata for the call, or the init code for contract creations - pub data: TraceCallData, - /// The return data of the call if this was not a contract creation, otherwise it is the - /// runtime bytecode of the created contract - pub output: TraceRetData, - /// The gas cost of the call - pub gas_cost: u64, - /// The status of the trace's call - pub status: InstructionResult, - /// call context of the runtime - pub call_context: Option, - /// Opcode-level execution steps - pub steps: Vec, + Ok((s, decoded.return_data)) } -impl CallTrace { - /// Whether this is a contract creation or not - pub fn created(&self) -> bool { - matches!(self.kind, CallKind::Create | CallKind::Create2) - } -} +/// Render a trace log. +async fn render_trace_log( + log: &Log, + decoder: &CallTraceDecoder, +) -> Result { + let mut s = String::new(); + let decoded = decoder.decode_event(log).await; + + match decoded { + DecodedCallLog::Raw(log) => { + for (i, topic) in log.topics().iter().enumerate() { + writeln!( + s, + "{:>13}: {}", + if i == 0 { "emit topic 0".to_string() } else { format!("topic {i}") }, + Paint::cyan(format!("{topic:?}")) + )?; + } -impl Default for CallTrace { - fn default() -> Self { - Self { - depth: Default::default(), - success: Default::default(), - contract: Default::default(), - label: Default::default(), - caller: Default::default(), - address: Default::default(), - kind: Default::default(), - value: Default::default(), - data: Default::default(), - output: Default::default(), - gas_cost: Default::default(), - status: InstructionResult::Continue, - call_context: Default::default(), - steps: Default::default(), + write!(s, " data: {}", Paint::cyan(hex::encode_prefixed(&log.data)))?; } - } -} - -impl fmt::Display for CallTrace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{}] ", self.gas_cost)?; - let address = self.address.to_checksum(None); - if self.created() { - write!( - f, - "{}{} {}@{}", - Paint::yellow(CALL), - Paint::yellow("new"), - self.label.as_deref().unwrap_or(""), - address - ) - } else { - let (func_name, inputs) = match &self.data { - TraceCallData::Raw(bytes) => { - debug!(target: "evm::traces", trace=?self, "unhandled raw calldata"); - if bytes.len() < 4 { - ("fallback".into(), hex::encode(bytes)) - } else { - let (selector, data) = bytes.split_at(4); - (hex::encode(selector), hex::encode(data)) - } - } - TraceCallData::Decoded { signature, args } => { - let name = signature.split('(').next().unwrap(); - (name.to_string(), args.join(", ")) - } - }; - - let action = match self.kind { - // do not show anything for CALLs - CallKind::Call => "", - CallKind::StaticCall => " [staticcall]", - CallKind::CallCode => " [callcode]", - CallKind::DelegateCall => " [delegatecall]", - CallKind::Create | CallKind::Create2 => unreachable!(), - }; - - let color = trace_color(self); - write!( - f, - "{addr}::{func_name}{opt_value}({inputs}){action}", - addr = color.paint(self.label.as_deref().unwrap_or(&address)), - func_name = color.paint(func_name), - opt_value = if self.value == U256::ZERO { - String::new() - } else { - format!("{{value: {}}}", self.value) - }, - action = Paint::yellow(action), - ) + DecodedCallLog::Decoded(name, params) => { + let params = params + .iter() + .map(|(name, value)| format!("{name}: {value}")) + .collect::>() + .join(", "); + + write!(s, "emit {}({params})", Paint::cyan(name.clone()))?; } } + + Ok(s) } /// Specifies the kind of trace. @@ -617,42 +308,9 @@ pub fn load_contracts( .iter() .filter_map(|(addr, name)| { if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) { - return Some((*addr, (name.clone(), abi.clone()))) + return Some((*addr, (name.clone(), abi.clone()))); } None }) .collect() } - -/// creates the memory data in 32byte chunks -/// see -fn convert_memory(data: &[u8]) -> Vec { - let mut memory = Vec::with_capacity((data.len() + 31) / 32); - for idx in (0..data.len()).step_by(32) { - let len = std::cmp::min(idx + 32, data.len()); - memory.push(hex::encode(&data[idx..len])); - } - memory -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_convert_memory() { - let mut data = vec![0u8; 32]; - assert_eq!( - convert_memory(&data), - vec!["0000000000000000000000000000000000000000000000000000000000000000".to_string()] - ); - data.extend(data.clone()); - assert_eq!( - convert_memory(&data), - vec![ - "0000000000000000000000000000000000000000000000000000000000000000".to_string(), - "0000000000000000000000000000000000000000000000000000000000000000".to_string() - ] - ); - } -} diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 93ae99d8cd97..acb9436d838a 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -20,8 +20,8 @@ use foundry_cli::{ }; use foundry_common::{ compile::ProjectCompiler, - estimate_eip1559_fees, fmt::parse_tokens, + provider::ethers::estimate_eip1559_fees, types::{ToAlloy, ToEthers}, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalized}; @@ -312,7 +312,7 @@ impl CreateArgs { }; if !self.verify { - return Ok(()) + return Ok(()); } println!("Starting contract verification..."); diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index 0d038c436ffc..2b3c1d449ac6 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -128,6 +128,7 @@ impl InspectArgs { ContractArtifactField::Errors => { let mut out = serde_json::Map::new(); if let Some(abi) = &artifact.abi { + let abi = &abi; // Print the signature of all errors for er in abi.errors.iter().flat_map(|(_, errors)| errors) { let types = er.inputs.iter().map(|p| p.ty.clone()).collect::>(); @@ -144,6 +145,8 @@ impl InspectArgs { ContractArtifactField::Events => { let mut out = serde_json::Map::new(); if let Some(abi) = &artifact.abi { + let abi = &abi; + // print the signature of all events including anonymous for ev in abi.events.iter().flat_map(|(_, events)| events) { let types = ev.inputs.iter().map(|p| p.ty.clone()).collect::>(); diff --git a/crates/forge/bin/cmd/script/broadcast.rs b/crates/forge/bin/cmd/script/broadcast.rs index a0523ba918ad..b6a8456f5dd9 100644 --- a/crates/forge/bin/cmd/script/broadcast.rs +++ b/crates/forge/bin/cmd/script/broadcast.rs @@ -2,7 +2,7 @@ use super::{ multi::MultiChainSequence, providers::ProvidersManager, receipts::clear_pendings, sequence::ScriptSequence, transaction::TransactionWithMetadata, verify::VerifyBundle, *, }; -use ethers_core::{types::TxHash, utils::format_units}; +use alloy_primitives::{utils::format_units, TxHash}; use ethers_providers::{JsonRpcClient, Middleware, Provider}; use ethers_signers::Signer; use eyre::{bail, ContextCompat, Result, WrapErr}; @@ -12,7 +12,11 @@ use foundry_cli::{ update_progress, utils::{has_batch_support, has_different_gas_calc}, }; -use foundry_common::{estimate_eip1559_fees, shell, try_get_http_provider, RetryProvider}; +use foundry_common::{ + provider::ethers::{estimate_eip1559_fees, try_get_http_provider, RetryProvider}, + shell, + types::{ToAlloy, ToEthers}, +}; use futures::StreamExt; use std::{cmp::min, collections::HashSet, ops::Mul, sync::Arc}; @@ -149,17 +153,13 @@ impl ScriptArgs { if sequential_broadcast { let tx_hash = tx_hash.await?; - deployment_sequence.add_pending(index, tx_hash.to_alloy()); + deployment_sequence.add_pending(index, tx_hash); update_progress!(pb, (index + already_broadcasted)); index += 1; - clear_pendings( - provider.clone(), - deployment_sequence, - Some(vec![tx_hash.to_alloy()]), - ) - .await?; + clear_pendings(provider.clone(), deployment_sequence, Some(vec![tx_hash])) + .await?; } else { pending_transactions.push(tx_hash); } @@ -170,7 +170,7 @@ impl ScriptArgs { while let Some(tx_hash) = buffer.next().await { let tx_hash = tx_hash?; - deployment_sequence.add_pending(index, tx_hash.to_alloy()); + deployment_sequence.add_pending(index, tx_hash); update_progress!(pb, (index + already_broadcasted)); index += 1; @@ -201,9 +201,9 @@ impl ScriptArgs { (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used.mul(gas_price)) }, ); - let paid = format_units(total_paid.to_ethers(), 18).unwrap_or_else(|_| "N/A".to_string()); + let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); let avg_gas_price = - format_units(total_gas_price.to_ethers() / deployment_sequence.receipts.len(), 9) + format_units(total_gas_price / U256::from(deployment_sequence.receipts.len()), 9) .unwrap_or_else(|_| "N/A".to_string()); shell::println(format!( "Total Paid: {} ETH ({} gas * avg {} gwei)", @@ -255,7 +255,7 @@ impl ScriptArgs { // Submit the transaction let pending = provider.send_transaction(tx, None).await?; - Ok(pending.tx_hash()) + Ok(pending.tx_hash().to_alloy()) } SendTransactionKind::Raw(signer) => self.broadcast(provider, signer, tx).await, } @@ -353,7 +353,7 @@ impl ScriptArgs { self.send_transactions(deployment_sequence, &rpc, &result.script_wallets).await?; if self.verify { - return deployment_sequence.verify_contracts(&script_config.config, verify).await + return deployment_sequence.verify_contracts(&script_config.config, verify).await; } Ok(()) } @@ -385,7 +385,7 @@ impl ScriptArgs { &mut script_config.config, returns, ) - .await + .await; } else if self.broadcast { eyre::bail!("No onchain transactions generated in script"); } @@ -508,7 +508,7 @@ impl ScriptArgs { // transactions. if let Some(next_tx) = txes_iter.peek() { if next_tx.rpc == Some(tx_rpc) { - continue + continue; } } @@ -549,7 +549,7 @@ impl ScriptArgs { shell::println(format!( "\nEstimated gas price: {} gwei", - format_units(per_gas.to_ethers(), 9) + format_units(per_gas, 9) .unwrap_or_else(|_| "[Could not calculate]".to_string()) .trim_end_matches('0') .trim_end_matches('.') @@ -557,7 +557,7 @@ impl ScriptArgs { shell::println(format!("\nEstimated total gas used for script: {total_gas}"))?; shell::println(format!( "\nEstimated amount required: {} ETH", - format_units(total_gas.saturating_mul(per_gas).to_ethers(), 18) + format_units(total_gas.saturating_mul(per_gas), 18) .unwrap_or_else(|_| "[Could not calculate]".to_string()) .trim_end_matches('0') ))?; @@ -598,7 +598,7 @@ impl ScriptArgs { // Submit the raw transaction let pending = provider.send_raw_transaction(legacy_or_1559.rlp_signed(&signature)).await?; - Ok(pending.tx_hash()) + Ok(pending.tx_hash().to_alloy()) } async fn estimate_gas(&self, tx: &mut TypedTransaction, provider: &Provider) -> Result<()> diff --git a/crates/forge/bin/cmd/script/cmd.rs b/crates/forge/bin/cmd/script/cmd.rs index 5aadaa977104..0f9a16b5fb07 100644 --- a/crates/forge/bin/cmd/script/cmd.rs +++ b/crates/forge/bin/cmd/script/cmd.rs @@ -5,7 +5,9 @@ use ethers_providers::Middleware; use ethers_signers::Signer; use eyre::Result; use foundry_cli::utils::LoadConfig; -use foundry_common::{contracts::flatten_contracts, try_get_http_provider, types::ToAlloy}; +use foundry_common::{ + contracts::flatten_contracts, provider::ethers::try_get_http_provider, types::ToAlloy, +}; use foundry_debugger::Debugger; use std::sync::Arc; @@ -77,7 +79,7 @@ impl ScriptArgs { result, verify, ) - .await + .await; } let known_contracts = flatten_contracts(&highlevel_known_contracts, true); @@ -154,7 +156,7 @@ impl ScriptArgs { &flatten_contracts(&highlevel_known_contracts, true), )?; - return Ok(Some((new_traces, libraries, highlevel_known_contracts))) + return Ok(Some((new_traces, libraries, highlevel_known_contracts))); } // Add predeploy libraries to the list of broadcastable transactions. @@ -201,7 +203,7 @@ impl ScriptArgs { result.script_wallets, verify, ) - .await + .await; } self.resume_single_deployment( script_config, @@ -212,7 +214,7 @@ impl ScriptArgs { ) .await .map_err(|err| { - eyre::eyre!("{err}\n\nIf you were trying to resume or verify a multi chain deployment, add `--multi` to your command invocation.") + eyre::eyre!("{err}\n\nIf you were trying to resume or verify a multi chain deployment, add `--multi` to your command invocation.") }) } diff --git a/crates/forge/bin/cmd/script/executor.rs b/crates/forge/bin/cmd/script/executor.rs index b38f48e59586..cee58db6852e 100644 --- a/crates/forge/bin/cmd/script/executor.rs +++ b/crates/forge/bin/cmd/script/executor.rs @@ -12,10 +12,13 @@ use forge::{ executors::ExecutorBuilder, inspectors::{cheatcodes::BroadcastableTransactions, CheatsConfig}, traces::{CallTraceDecoder, Traces}, - utils::CallKind, }; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; -use foundry_common::{shell, types::ToEthers, RpcUrl}; +use foundry_common::{ + provider::ethers::RpcUrl, + shell, + types::{ToAlloy, ToEthers}, +}; use foundry_compilers::artifacts::CompactContractBytecode; use futures::future::join_all; use parking_lot::RwLock; @@ -128,7 +131,7 @@ impl ScriptArgs { abi, code, }; - return Some((*addr, info)) + return Some((*addr, info)); } None }) @@ -159,20 +162,20 @@ impl ScriptArgs { .wrap_err("Internal EVM error during simulation")?; if !result.success || result.traces.is_empty() { - return Ok((None, result.traces)) + return Ok((None, result.traces)); } let created_contracts = result .traces .iter() .flat_map(|(_, traces)| { - traces.arena.iter().filter_map(|node| { - if matches!(node.kind(), CallKind::Create | CallKind::Create2) { + traces.nodes().iter().filter_map(|node| { + if node.trace.kind.is_any_create() { return Some(AdditionalContract { - opcode: node.kind(), + opcode: node.trace.kind, address: node.trace.address, - init_code: node.trace.data.as_bytes().to_vec().into(), - }) + init_code: node.trace.data.clone(), + }); } None }) @@ -218,7 +221,7 @@ impl ScriptArgs { // type hint let res: Result = res; - let (tx, mut traces) = res?; + let (tx, traces) = res?; // Transaction will be `None`, if execution didn't pass. if tx.is_none() || script_config.evm_opts.verbosity > 3 { @@ -229,9 +232,8 @@ impl ScriptArgs { ); } - for (_kind, trace) in &mut traces { - decoder.decode(trace).await; - println!("{trace}"); + for (_, trace) in &traces { + println!("{}", render_trace_arena(trace, decoder).await?); } } diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index c3ab3caa7116..4f6a3328a9f6 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -18,9 +18,8 @@ use forge::{ opts::EvmOpts, traces::{ identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, - CallTraceDecoder, CallTraceDecoderBuilder, TraceCallData, TraceKind, TraceRetData, Traces, + render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, - utils::CallKind, }; use foundry_cli::opts::MultiWallet; use foundry_common::{ @@ -29,9 +28,10 @@ use foundry_common::{ errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, fmt::{format_token, format_token_raw}, + provider::ethers::RpcUrl, shell, types::{ToAlloy, ToEthers}, - ContractsByArtifact, RpcUrl, CONTRACT_MAX_SIZE, SELECTOR_LEN, + ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; use foundry_compilers::{ artifacts::{ContractBytecodeSome, Libraries}, @@ -291,7 +291,7 @@ impl ScriptArgs { } shell::println("Traces:")?; - for (kind, trace) in &mut result.traces { + for (kind, trace) in &result.traces { let should_include = match kind { TraceKind::Setup => verbosity >= 5, TraceKind::Execution => verbosity > 3, @@ -299,8 +299,7 @@ impl ScriptArgs { } || !result.success; if should_include { - decoder.decode(trace).await; - shell::println(format!("{trace}"))?; + shell::println(render_trace_arena(trace, decoder).await?)?; } } shell::println(String::new())?; @@ -355,7 +354,7 @@ impl ScriptArgs { return Err(eyre::eyre!( "script failed: {}", decode::decode_revert(&result.returned[..], None, None) - )) + )); } Ok(()) @@ -395,7 +394,7 @@ impl ScriptArgs { if let Some(ns) = new_sender { if sender != ns { shell::println("You have more than one deployer who could predeploy libraries. Using `--sender` instead.")?; - return Ok(None) + return Ok(None); } } else if sender != evm_opts.sender { new_sender = Some(sender); @@ -483,13 +482,13 @@ impl ScriptArgs { // From artifacts for (artifact, bytecode) in known_contracts.iter() { if bytecode.bytecode.object.is_unlinked() { - return Err(UnlinkedByteCode::Bytecode(artifact.identifier()).into()) + return Err(UnlinkedByteCode::Bytecode(artifact.identifier()).into()); } let init_code = bytecode.bytecode.object.as_bytes().unwrap(); // Ignore abstract contracts if let Some(ref deployed_code) = bytecode.deployed_bytecode.bytecode { if deployed_code.object.is_unlinked() { - return Err(UnlinkedByteCode::DeployedBytecode(artifact.identifier()).into()) + return Err(UnlinkedByteCode::DeployedBytecode(artifact.identifier()).into()); } let deployed_code = deployed_code.object.as_bytes().unwrap(); bytecodes.push((artifact.name.clone(), init_code, deployed_code)); @@ -498,27 +497,17 @@ impl ScriptArgs { // From traces let create_nodes = result.traces.iter().flat_map(|(_, traces)| { - traces - .arena - .iter() - .filter(|node| matches!(node.kind(), CallKind::Create | CallKind::Create2)) + traces.nodes().iter().filter(|node| node.trace.kind.is_any_create()) }); let mut unknown_c = 0usize; for node in create_nodes { - // Calldata == init code - if let TraceCallData::Raw(ref init_code) = node.trace.data { - // Output is the runtime code - if let TraceRetData::Raw(ref deployed_code) = node.trace.output { - // Only push if it was not present already - if !bytecodes.iter().any(|(_, b, _)| *b == init_code.as_ref()) { - bytecodes.push((format!("Unknown{unknown_c}"), init_code, deployed_code)); - unknown_c += 1; - } - continue - } + let init_code = &node.trace.data; + let deployed_code = &node.trace.output; + if !bytecodes.iter().any(|(_, b, _)| *b == init_code.as_ref()) { + bytecodes.push((format!("Unknown{unknown_c}"), init_code, deployed_code)); + unknown_c += 1; } - // Both should be raw and not decoded since it's just bytecode - eyre::bail!("Create node returned decoded data: {:?}", node); + continue; } let mut prompt_user = false; @@ -544,7 +533,7 @@ impl ScriptArgs { offset = 32; } } else if to.is_some() { - continue + continue; } // Find artifact with a deployment code same as the data. diff --git a/crates/forge/bin/cmd/script/multi.rs b/crates/forge/bin/cmd/script/multi.rs index fc94fc487642..9fd3427949b2 100644 --- a/crates/forge/bin/cmd/script/multi.rs +++ b/crates/forge/bin/cmd/script/multi.rs @@ -7,7 +7,7 @@ use super::{ use ethers_signers::LocalWallet; use eyre::{ContextCompat, Report, Result, WrapErr}; use foundry_cli::utils::now; -use foundry_common::{fs, get_http_provider}; +use foundry_common::{fs, provider::ethers::get_http_provider}; use foundry_compilers::{artifacts::Libraries, ArtifactId}; use foundry_config::Config; use futures::future::join_all; @@ -237,7 +237,7 @@ impl ScriptArgs { let errors = results.into_iter().filter(|res| res.is_err()).collect::>(); if !errors.is_empty() { - return Err(eyre::eyre!("{errors:?}")) + return Err(eyre::eyre!("{errors:?}")); } Ok(()) diff --git a/crates/forge/bin/cmd/script/providers.rs b/crates/forge/bin/cmd/script/providers.rs index 92638becaac4..f29a72629320 100644 --- a/crates/forge/bin/cmd/script/providers.rs +++ b/crates/forge/bin/cmd/script/providers.rs @@ -1,7 +1,11 @@ use alloy_primitives::U256; use ethers_providers::{Middleware, Provider}; use eyre::{Result, WrapErr}; -use foundry_common::{get_http_provider, runtime_client::RuntimeClient, types::ToAlloy, RpcUrl}; +use foundry_common::{ + provider::ethers::{get_http_provider, RpcUrl}, + runtime_client::RuntimeClient, + types::ToAlloy, +}; use foundry_config::Chain; use std::{ collections::{hash_map::Entry, HashMap}, diff --git a/crates/forge/bin/cmd/script/receipts.rs b/crates/forge/bin/cmd/script/receipts.rs index fb8640903c95..29848aad3564 100644 --- a/crates/forge/bin/cmd/script/receipts.rs +++ b/crates/forge/bin/cmd/script/receipts.rs @@ -5,8 +5,8 @@ use ethers_providers::{Middleware, PendingTransaction}; use eyre::Result; use foundry_cli::{init_progress, update_progress, utils::print_receipt}; use foundry_common::{ + provider::ethers::RetryProvider, types::{ToAlloy, ToEthers}, - RetryProvider, }; use futures::StreamExt; use std::sync::Arc; @@ -36,7 +36,7 @@ pub async fn wait_for_pending( deployment_sequence: &mut ScriptSequence, ) -> Result<()> { if deployment_sequence.pending.is_empty() { - return Ok(()) + return Ok(()); } println!("##\nChecking previously pending transactions."); clear_pendings(provider, deployment_sequence, None).await @@ -138,7 +138,7 @@ async fn check_tx_status( // First check if there's a receipt let receipt_opt = provider.get_transaction_receipt(hash.to_ethers()).await?; if let Some(receipt) = receipt_opt { - return Ok(receipt.into()) + return Ok(receipt.into()); } // If the tx is present in the mempool, run the pending tx future, and diff --git a/crates/forge/bin/cmd/script/runner.rs b/crates/forge/bin/cmd/script/runner.rs index 4dc2857db883..28faf48be9ed 100644 --- a/crates/forge/bin/cmd/script/runner.rs +++ b/crates/forge/bin/cmd/script/runner.rs @@ -244,9 +244,9 @@ impl ScriptRunner { gas_used, logs, traces: traces - .map(|mut traces| { + .map(|traces| { // Manually adjust gas for the trace to add back the stipend/real used gas - traces.arena[0].trace.gas_cost = gas_used; + vec![(TraceKind::Execution, traces)] }) .unwrap_or_default(), @@ -305,9 +305,9 @@ impl ScriptRunner { gas_used, logs, traces: traces - .map(|mut traces| { + .map(|traces| { // Manually adjust gas for the trace to add back the stipend/real used gas - traces.arena[0].trace.gas_cost = gas_used; + vec![(TraceKind::Execution, traces)] }) .unwrap_or_default(), @@ -363,7 +363,7 @@ impl ScriptRunner { { // update the gas gas_used = highest_gas_limit; - break + break; } last_highest_gas_limit = highest_gas_limit; } diff --git a/crates/forge/bin/cmd/script/transaction.rs b/crates/forge/bin/cmd/script/transaction.rs index f3af7470b62e..a577702ba8a7 100644 --- a/crates/forge/bin/cmd/script/transaction.rs +++ b/crates/forge/bin/cmd/script/transaction.rs @@ -6,10 +6,14 @@ use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress}; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{ fmt::format_token_raw, + provider::ethers::RpcUrl, types::{ToAlloy, ToEthers}, - RpcUrl, SELECTOR_LEN, + SELECTOR_LEN, +}; +use foundry_evm::{ + constants::DEFAULT_CREATE2_DEPLOYER, + traces::{CallKind, CallTraceDecoder}, }; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder, utils::CallKind}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1f413696f8cf..c54de4670cd2 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -42,6 +42,7 @@ mod summary; use summary::TestSummaryReporter; pub use filter::FilterArgs; +use forge::traces::render_trace_arena; // Loads project's figment and merges the build cli arguments into it foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); @@ -222,6 +223,8 @@ impl TestArgs { if should_debug { let tests = outcome.clone().into_tests(); + // todo(onbjerg): why do we bother decoding everything and having multiple decoders if + // we are only going to use the first one? (see `DebuggerArgs` below) let mut decoders = Vec::new(); for test in tests { let mut result = test.result; @@ -247,31 +250,9 @@ impl TestArgs { EtherscanIdentifier::new(&config, remote_chain_id)?; // Decode the traces - for (kind, trace) in &mut result.traces { + for (_, trace) in &mut result.traces { decoder.identify(trace, &mut local_identifier); decoder.identify(trace, &mut etherscan_identifier); - - let should_include = match kind { - // At verbosity level 3, we only display traces for failed tests - // At verbosity level 4, we also display the setup trace for failed - // tests At verbosity level 5, we display - // all traces for all tests - TraceKind::Setup => { - (verbosity >= 5) || - (verbosity == 4 && result.status == TestStatus::Failure) - } - TraceKind::Execution => { - verbosity > 3 || - (verbosity == 3 && result.status == TestStatus::Failure) - } - _ => false, - }; - - // We decode the trace if we either need to build a gas report or we need - // to print it - if should_include || self.gas_report { - decoder.decode(trace).await; - } } } @@ -360,7 +341,7 @@ impl TestArgs { if self.json { let results = runner.test_collect(filter, test_options).await; println!("{}", serde_json::to_string(&results)?); - return Ok(TestOutcome::new(results, self.allow_failure)) + return Ok(TestOutcome::new(results, self.allow_failure)); } // Set up identifiers @@ -418,7 +399,7 @@ impl TestArgs { } if result.traces.is_empty() { - continue + continue; } // Identify addresses in each trace @@ -436,9 +417,9 @@ impl TestArgs { // Decode the traces let mut decoded_traces = Vec::with_capacity(result.traces.len()); - for (kind, trace) in &mut result.traces { - decoder.identify(trace, &mut local_identifier); - decoder.identify(trace, &mut etherscan_identifier); + for (kind, arena) in &mut result.traces { + decoder.identify(arena, &mut local_identifier); + decoder.identify(arena, &mut etherscan_identifier); // verbosity: // - 0..3: nothing @@ -455,24 +436,18 @@ impl TestArgs { TraceKind::Deployment => false, }; - // Decode the trace if we either need to build a gas report or we need to print - // it - if should_include || self.gas_report { - decoder.decode(trace).await; - } - if should_include { - decoded_traces.push(trace.to_string()); + decoded_traces.push(render_trace_arena(arena, &decoder).await?); } } if !decoded_traces.is_empty() { - println!("Traces:"); - decoded_traces.into_iter().for_each(|trace| println!("{trace}")); + shell::println("Traces:")?; + decoded_traces.into_iter().try_for_each(shell::println)?; } if self.gas_report { - gas_report.analyze(&result.traces); + gas_report.analyze(&result.traces, &decoder).await; } // If the test failed, we want to stop processing the rest of the tests @@ -489,7 +464,7 @@ impl TestArgs { total_failed += block_outcome.failures().count(); total_skipped += block_outcome.skips().count(); - println!("{}", block_outcome.summary()); + shell::println(block_outcome.summary())?; if self.summary { suite_results.push(block_outcome.clone()); @@ -497,25 +472,22 @@ impl TestArgs { } if self.gas_report { - println!("{}", gas_report.finalize()); + shell::println(gas_report.finalize())?; } let num_test_suites = results.len(); if num_test_suites > 0 { - println!( - "{}", - format_aggregated_summary( - num_test_suites, - total_passed, - total_failed, - total_skipped - ) - ); + shell::println(format_aggregated_summary( + num_test_suites, + total_passed, + total_failed, + total_skipped, + ))?; if self.summary { let mut summary_table = TestSummaryReporter::new(self.detailed); - println!("\n\nTest Summary:"); + shell::println("\n\nTest Summary:")?; summary_table.print_summary(suite_results); } } @@ -654,7 +626,7 @@ impl TestOutcome { pub fn ensure_ok(&self) -> Result<()> { let failures = self.failures().count(); if self.allow_failure || failures == 0 { - return Ok(()) + return Ok(()); } if !shell::verbosity().is_normal() { @@ -662,27 +634,27 @@ impl TestOutcome { std::process::exit(1); } - println!(); - println!("Failing tests:"); + shell::println("")?; + shell::println("Failing tests:")?; for (suite_name, suite) in self.results.iter() { let failures = suite.failures().count(); if failures == 0 { - continue + continue; } let term = if failures > 1 { "tests" } else { "test" }; - println!("Encountered {failures} failing {term} in {suite_name}"); + shell::println(format!("Encountered {failures} failing {term} in {suite_name}"))?; for (name, result) in suite.failures() { short_test_result(name, result); } - println!(); + shell::println("")?; } let successes = self.successes().count(); - println!( + shell::println(format!( "Encountered a total of {} failing tests, {} tests succeeded", Paint::red(failures.to_string()), Paint::green(successes.to_string()) - ); + ))?; std::process::exit(1); } @@ -708,7 +680,7 @@ impl TestOutcome { } fn short_test_result(name: &str, result: &TestResult) { - println!("{result} {name} {}", result.kind.report()); + shell::println(format!("{result} {name} {}", result.kind.report())).unwrap(); } /// Formats the aggregated summary of all test suites into a string (for printing). diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 411e7bdced51..c537643ad618 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -3,7 +3,7 @@ use crate::{ constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, hashbrown::HashSet, - traces::{CallTraceArena, TraceCallData, TraceKind}, + traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData, TraceKind}, }; use alloy_primitives::U256; use comfy_table::{presets::ASCII_MARKDOWN, *}; @@ -46,21 +46,25 @@ impl GasReport { } /// Analyzes the given traces and generates a gas report. - pub fn analyze(&mut self, traces: &[(TraceKind, CallTraceArena)]) { - traces.iter().for_each(|(_, trace)| { - self.analyze_node(0, trace); - }); + pub async fn analyze( + &mut self, + traces: &[(TraceKind, CallTraceArena)], + decoder: &CallTraceDecoder, + ) { + for node in traces.iter().flat_map(|(_, arena)| arena.nodes()) { + self.analyze_node(node, decoder).await; + } } - fn analyze_node(&mut self, node_index: usize, arena: &CallTraceArena) { - let node = &arena.arena[node_index]; + async fn analyze_node(&mut self, node: &CallTraceNode, decoder: &CallTraceDecoder) { let trace = &node.trace; + let decoded = decoder.decode_function(&node.trace).await; if trace.address == CHEATCODE_ADDRESS || trace.address == HARDHAT_CONSOLE_ADDRESS { return } - if let Some(name) = &trace.contract { + if let Some(name) = &decoded.contract { let contract_name = name.rsplit(':').next().unwrap_or(name.as_str()); // If the user listed the contract in 'gas_reports' (the foundry.toml field) a // report for the contract is generated even if it's listed in the ignore @@ -78,35 +82,26 @@ impl GasReport { if self.should_report(contract_name) { let contract_info = self.contracts.entry(name.to_string()).or_default(); - match &trace.data { - TraceCallData::Raw(bytes) => { - if trace.created() { - contract_info.gas = U256::from(trace.gas_cost); - contract_info.size = U256::from(bytes.len()); - } - } - TraceCallData::Decoded { signature, .. } => { - let name = signature.split('(').next().unwrap(); - // ignore any test/setup functions - let should_include = - !(name.is_test() || name.is_invariant_test() || name.is_setup()); - if should_include { - let gas_info = contract_info - .functions - .entry(name.into()) - .or_default() - .entry(signature.clone()) - .or_default(); - gas_info.calls.push(U256::from(trace.gas_cost)); - } + if trace.kind.is_any_create() { + contract_info.gas = U256::from(trace.gas_used); + contract_info.size = U256::from(trace.data.len()); + } else if let Some(DecodedCallData { signature, .. }) = decoded.func { + let name = signature.split('(').next().unwrap(); + // ignore any test/setup functions + let should_include = + !(name.is_test() || name.is_invariant_test() || name.is_setup()); + if should_include { + let gas_info = contract_info + .functions + .entry(name.to_string()) + .or_default() + .entry(signature.clone()) + .or_default(); + gas_info.calls.push(U256::from(trace.gas_used)); } } } } - - node.children.iter().for_each(|index| { - self.analyze_node(*index, arena); - }); } /// Finalizes the gas report by calculating the min, max, mean, and median for each function. diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index b1fccc87e0bd..94551e876327 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -119,6 +119,7 @@ pub struct TestResult { pub kind: TestKind, /// Traces + #[serde(skip)] pub traces: Traces, /// Raw coverage info diff --git a/crates/forge/tests/cli/multi_script.rs b/crates/forge/tests/cli/multi_script.rs index f6f3adb9dadb..d6f7628da169 100644 --- a/crates/forge/tests/cli/multi_script.rs +++ b/crates/forge/tests/cli/multi_script.rs @@ -1,6 +1,6 @@ //! Contains various tests related to forge script use anvil::{spawn, NodeConfig}; -use foundry_common::types::ToEthers; + use foundry_test_utils::{ScriptOutcome, ScriptTester}; forgetest_async!(can_deploy_multi_chain_script_without_lib, |prj, cmd| { @@ -15,23 +15,11 @@ forgetest_async!(can_deploy_multi_chain_script_without_lib, |prj, cmd| { .args(&[&handle1.http_endpoint(), &handle2.http_endpoint()]) .broadcast(ScriptOutcome::OkBroadcast); - assert_eq!( - api1.transaction_count(tester.accounts_pub[0].to_ethers(), None).await.unwrap().as_u32(), - 1 - ); - assert_eq!( - api1.transaction_count(tester.accounts_pub[1].to_ethers(), None).await.unwrap().as_u32(), - 1 - ); + assert_eq!(api1.transaction_count(tester.accounts_pub[0], None).await.unwrap().to::(), 1); + assert_eq!(api1.transaction_count(tester.accounts_pub[1], None).await.unwrap().to::(), 1); - assert_eq!( - api2.transaction_count(tester.accounts_pub[0].to_ethers(), None).await.unwrap().as_u32(), - 2 - ); - assert_eq!( - api2.transaction_count(tester.accounts_pub[1].to_ethers(), None).await.unwrap().as_u32(), - 3 - ); + assert_eq!(api2.transaction_count(tester.accounts_pub[0], None).await.unwrap().to::(), 2); + assert_eq!(api2.transaction_count(tester.accounts_pub[1], None).await.unwrap().to::(), 3); }); forgetest_async!(can_not_deploy_multi_chain_script_with_lib, |prj, cmd| { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 6fd5dcd06a08..f9eade382d27 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1,9 +1,9 @@ //! Contains various tests related to `forge script`. use crate::constants::TEMPLATE_CONTRACT; -use alloy_primitives::Address; +use alloy_primitives::{Address, Bytes}; use anvil::{spawn, NodeConfig}; -use foundry_common::{rpc, types::ToEthers}; +use foundry_common::rpc; use foundry_test_utils::{util::OutputExt, ScriptOutcome, ScriptTester}; use regex::Regex; use serde_json::Value; @@ -561,10 +561,8 @@ forgetest_async!(can_deploy_with_create2, |prj, cmd| { // Prepare CREATE2 Deployer api.anvil_set_code( - foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER.to_ethers(), - ethers_core::types::Bytes::from_static( - foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, - ), + foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER, + Bytes::from_static(foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE), ) .await .unwrap(); @@ -653,7 +651,7 @@ forgetest_async!( // Prepare CREATE2 Deployer let addr = Address::from_str("0x4e59b44847b379578588920ca78fbf26c0b4956c").unwrap(); let code = hex::decode("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3").expect("Could not decode create2 deployer init_code").into(); - api.anvil_set_code(addr.to_ethers(), code).await.unwrap(); + api.anvil_set_code(addr, code).await.unwrap(); tester .load_private_keys(&[0]) diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index 17cace7c908d..22c01f67e155 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -10,9 +10,13 @@ use foundry_config::{ InvariantConfig, RpcEndpoint, RpcEndpoints, }; use foundry_evm::{ - decode::decode_console_logs, inspectors::CheatsConfig, revm::primitives::SpecId, + decode::decode_console_logs, + inspectors::CheatsConfig, + revm::primitives::SpecId, + traces::{render_trace_arena, CallTraceDecoderBuilder}, }; use foundry_test_utils::{init_tracing, Filter}; +use futures::future::join_all; use itertools::Itertools; use std::{collections::BTreeMap, path::Path}; @@ -78,14 +82,25 @@ impl TestConfig { { let logs = decode_console_logs(&result.logs); let outcome = if self.should_fail { "fail" } else { "pass" }; - + let call_trace_decoder = CallTraceDecoderBuilder::default().build(); + let decoded_traces = join_all( + result + .traces + .iter() + .map(|(_, a)| render_trace_arena(a, &call_trace_decoder)) + .collect::>(), + ) + .await + .into_iter() + .map(|x| x.unwrap()) + .collect::>(); eyre::bail!( "Test {} did not {} as expected.\nReason: {:?}\nLogs:\n{}\n\nTraces:\n{}", test_name, outcome, result.reason, logs.join("\n"), - result.traces.iter().map(|(_, a)| a).format("\n"), + decoded_traces.into_iter().format("\n"), ) } } diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 5ea2bd37845c..39aeae72335e 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -7,8 +7,7 @@ use forge::result::TestStatus; use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, - traces::{CallTraceDecoder, TraceCallData, TraceKind}, - utils::CallKind, + traces::{CallKind, CallTraceDecoder, DecodedCallData, TraceKind}, }; use foundry_test_utils::Filter; @@ -245,35 +244,35 @@ test_repro!(6501, false, None, |res| { assert_eq!(test.status, TestStatus::Success); assert_eq!(test.decoded_logs, ["a".to_string(), "1".to_string(), "b 2".to_string()]); - let (kind, mut traces) = test.traces[1].clone(); + let (kind, traces) = test.traces[1].clone(); + let nodes = traces.into_nodes(); assert_eq!(kind, TraceKind::Execution); - let test_call = traces.arena.first().unwrap(); + let test_call = nodes.first().unwrap(); assert_eq!(test_call.idx, 0); assert_eq!(test_call.children, [1, 2, 3]); assert_eq!(test_call.trace.depth, 0); assert!(test_call.trace.success); - CallTraceDecoder::new().decode(&mut traces).await; - let expected = [ ("log(string)", vec!["\"a\""]), ("log(uint256)", vec!["1"]), ("log(string,uint256)", vec!["\"b\"", "2"]), ]; - for (node, expected) in traces.arena[1..=3].iter().zip(expected) { + for (node, expected) in nodes[1..=3].iter().zip(expected) { let trace = &node.trace; + let decoded = CallTraceDecoder::new().decode_function(trace).await; assert_eq!(trace.kind, CallKind::StaticCall); assert_eq!(trace.address, HARDHAT_CONSOLE_ADDRESS); - assert_eq!(trace.label, Some("console".into())); + assert_eq!(decoded.label, Some("console".into())); assert_eq!(trace.depth, 1); assert!(trace.success); assert_eq!( - trace.data, - TraceCallData::Decoded { + decoded.func, + Some(DecodedCallData { signature: expected.0.into(), args: expected.1.into_iter().map(ToOwned::to_owned).collect(), - } + }) ); } }); diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index a2f47ed4d7e4..f4449a7694a8 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -4,9 +4,8 @@ use ethers_core::types::NameOrAddress; use ethers_providers::Middleware; use eyre::Result; use foundry_common::{ - get_http_provider, + provider::ethers::{get_http_provider, RetryProvider}, types::{ToAlloy, ToEthers}, - RetryProvider, }; use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; diff --git a/deny.toml b/deny.toml index 25b3e6a971a6..f46973b5964a 100644 --- a/deny.toml +++ b/deny.toml @@ -64,6 +64,7 @@ exceptions = [ { allow = ["CC0-1.0"], name = "secp256k1" }, { allow = ["CC0-1.0"], name = "secp256k1-sys" }, { allow = ["CC0-1.0"], name = "tiny-keccak" }, + { allow = ["CC0-1.0"], name = "to_method" }, { allow = ["CC0-1.0"], name = "more-asserts" }, { allow = ["CC0-1.0"], name = "trezor-client" }, { allow = ["CC0-1.0"], name = "notify" },