diff --git a/.gitattributes b/.gitattributes index 0e34b8632..0e0276a95 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ crates/cheatcodes/assets/*.json linguist-generated testdata/cheats/Vm.sol linguist-generated + +# See +*.rs diff=rust diff --git a/.github/changelog.json b/.github/changelog.json index 4fe255e69..aa548adc7 100644 --- a/.github/changelog.json +++ b/.github/changelog.json @@ -1,16 +1,66 @@ { "categories": [ { - "title": "## Features", - "labels": ["T-feature"] + "title": "## Breaking changes", + "labels": ["T-likely-breaking "] }, { - "title": "## Fixes", - "labels": ["T-bug", "T-fix"] + "title": "## Anvil Features", + "labels": ["C-anvil", "T-feature"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Anvil Fixes", + "labels": ["C-anvil", "T-bug"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Cast Features", + "labels": ["C-cast", "T-feature"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Cast Fixes", + "labels": ["C-cast", "T-bug"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Chisel Features", + "labels": ["C-chisel", "T-feature"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Chisel Fixes", + "labels": ["C-chisel", "T-bug"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Forge Features", + "labels": ["C-forge", "T-feature"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Forge Fixes", + "labels": ["C-forge", "T-bug"], + "exhaustive": true, + "exhaustive_rules": false + }, + { + "title": "## Performance improvements", + "labels": ["T-perf"] } ], "ignore_labels": ["L-ignore"], - "template": "${{CHANGELOG}}\n## Other\n\n${{UNCATEGORIZED}}", - "pr_template": "- ${{TITLE}} (#${{NUMBER}})", - "empty_template": "- No changes" + "template": "${{CHANGELOG}}\n## Other\n\n${{UNCATEGORIZED}}\n## Full Changelog:\n ${{RELEASE_DIFF}}", + "pr_template": "- ${{TITLE}} (#${{NUMBER}}) by @${{AUTHOR}}", + "empty_template": "- No changes", + "max_pull_requests": 100, + "max_back_track_time_days": 60 } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b386d180..48421fb95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,7 @@ name: release on: push: tags: + - "v*.*.*" - "*-zksync.*" schedule: - cron: "0 0 * * *" @@ -29,7 +30,7 @@ jobs: - name: Compute release name and tag id: release_info run: | - if [[ $IS_NIGHTLY ]]; then + if [[ ${IS_NIGHTLY} == 'true' ]]; then echo "tag_name=nightly-${GITHUB_SHA}" >> $GITHUB_OUTPUT echo "release_name=foundry-zksync Nightly ($(date '+%Y-%m-%d'))" >> $GITHUB_OUTPUT else @@ -42,7 +43,7 @@ jobs: # which allows users to roll back. It is also used to build # the changelog. - name: Create build-specific nightly tag - if: ${{ env.IS_NIGHTLY }} + if: ${{ env.IS_NIGHTLY == 'true' }} uses: actions/github-script@v7 env: TAG_NAME: ${{ steps.release_info.outputs.tag_name }} @@ -56,12 +57,16 @@ jobs: uses: mikepenz/release-changelog-builder-action@v4 with: configuration: "./.github/changelog.json" - fromTag: ${{ env.IS_NIGHTLY && 'nightly' || '' }} + fromTag: ${{ env.IS_NIGHTLY == 'true' && 'nightly' || '' }} toTag: ${{ steps.release_info.outputs.tag_name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} release: + permissions: + id-token: write + contents: write + attestations: write name: ${{ matrix.target }} (${{ matrix.runner }}) runs-on: ${{ matrix.runner }} timeout-minutes: 240 @@ -205,6 +210,15 @@ jobs: ${{ steps.artifacts.outputs.file_name }} ${{ steps.man.outputs.foundry_man }} + - name: Binaries attestation + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + ${{ env.anvil_bin_path }} + ${{ env.cast_bin_path }} + ${{ env.chisel_bin_path }} + ${{ env.forge_bin_path }} + # If this is a nightly release, it also updates the release # tagged `nightly` for compatibility with `foundryup` - name: Update nightly release @@ -240,7 +254,7 @@ jobs: # Moves the `nightly` tag to `HEAD` - name: Move nightly tag - if: ${{ env.IS_NIGHTLY }} + if: ${{ env.IS_NIGHTLY == 'true' }} uses: actions/github-script@v7 with: script: | diff --git a/Cargo.lock b/Cargo.lock index d1af35aae..fa9a82bb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,9 +121,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.48" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0161082e0edd9013d23083465cc04b20e44b7a15646d36ba7b0cdb7cd6fe18f" +checksum = "56f15afc5993458b42739ab3b69bdb6b4c8112acd3997dbea9bc092c9517137c" dependencies = [ "alloy-primitives", "num_enum 0.7.3", @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba14856660f31807ebb26ce8f667e814c72694e1077e97ef102e326ad580f3f" +checksum = "f4138dc275554afa6f18c4217262ac9388790b2fc393c2dfe03c51d357abf013" dependencies = [ "alloy-eips", "alloy-primitives", @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28666307e76441e7af37a2b90cde7391c28112121bea59f4e0d804df8b20057e" +checksum = "0fa04e1882c31288ce1028fdf31b6ea94cfa9eafa2e497f903ded631c8c6a42c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3510769905590b8991a8e63a5e0ab4aa72cf07a13ab5fbe23f12f4454d161da" +checksum = "5f21886c1fea0626f755a49b2ac653b396fb345233f6170db2da3d0ada31560c" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -181,7 +181,7 @@ dependencies = [ "alloy-transport", "futures 0.3.31", "futures-util", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] @@ -199,9 +199,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41056bde53ae10ffbbf11618efbe1e0290859e5eab0fe9ef82ebdb62f12a866f" +checksum = "44e3b98c37b3218924cd1d2a8570666b89662be54e5b182643855f783ea68b33" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -233,9 +233,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +checksum = "cabf647eb4650c91a9d38cb6f972bb320009e7e9d61765fb688a86f1563b33e8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -248,9 +248,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e922d558006ba371681d484d12aa73fe673d84884f83747730af7433c0e86d" +checksum = "52dd5869ed09e399003e0e0ec6903d981b2a92e74c5d37e6b40890bad2517526" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -266,10 +266,11 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dca170827a7ca156b43588faebf9e9d27c27d0fb07cab82cfd830345e2b24f5" +checksum = "e7d2a7fe5c1a9bd6793829ea21a636f30fc2b3f5d2e7418ba86d96e41dd1f460" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-serde", "alloy-trie", @@ -278,9 +279,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c357da577dfb56998d01f574d81ad7a1958d248740a7981b205d69d65a7da404" +checksum = "731ea743b3d843bc657e120fb1d1e9cc94f5dab8107e35a82125a63e6420a102" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -290,23 +291,23 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9335278f50b0273e0a187680ee742bb6b154a948adf036f448575bacc5ccb315" +checksum = "2008bedb8159a255b46b7c8614516eda06679ea82f620913679afbd8031fea72" dependencies = [ "alloy-primitives", "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "tracing", ] [[package]] name = "alloy-network" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad4e6ad4230df8c4a254c20f8d6a84ab9df151bfca13f463177dbc96571cc1f8" +checksum = "4556f01fe41d0677495df10a648ddcf7ce118b0e8aa9642a0e2b6dd1fb7259de" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -324,14 +325,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "alloy-network-primitives" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4df88a2f8020801e0fefce79471d3946d39ca3311802dbbd0ecfdeee5e972e3" +checksum = "f31c3c6b71340a1d076831823f09cb6e02de01de5c6630a9631bdb36f947ff80" dependencies = [ "alloy-consensus", "alloy-eips", @@ -342,9 +343,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db5cefbc736b2b26a960dcf82279c70a03695dd11a0032a6dc27601eeb29182" +checksum = "4520cd4bc5cec20c32c98e4bc38914c7fb96bf4a712105e44da186a54e65e3ba" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -352,16 +353,16 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "tracing", "url", ] [[package]] name = "alloy-primitives" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" +checksum = "788bb18e8f61d5d9340b52143f27771daf7e1dccbaf2741621d2493f9debf52e" dependencies = [ "alloy-rlp", "arbitrary", @@ -373,7 +374,6 @@ dependencies = [ "foldhash", "getrandom 0.2.15", "hashbrown 0.15.2", - "hex-literal", "indexmap 2.7.0", "itoa", "k256 0.13.4", @@ -391,9 +391,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5115c74c037714e1b02a86f742289113afa5d494b5ea58308ba8aa378e739101" +checksum = "5a22c4441b3ebe2d77fa9cf629ba68c3f713eb91779cff84275393db97eddd82" dependencies = [ "alloy-chains", "alloy-consensus", @@ -425,7 +425,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", "url", @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b073afa409698d1b9a30522565815f3bf7010e5b47b997cf399209e6110df097" +checksum = "2269fd635f7b505f27c63a3cb293148cd02301efce4c8bdd9ff54fbfc4a20e23" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -447,7 +447,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.1", + "tower 0.5.2", "tracing", ] @@ -475,9 +475,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6a0bd0ce5660ac48e4f3bb0c7c5c3a94db287a0be94971599d83928476cbcd" +checksum = "d06a292b37e182e514903ede6e623b9de96420e8109ce300da288a96d88b7e4b" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -493,7 +493,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.1", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374ac12e35bb90ebccd86e7c943ddba9590149a6e35cc4d9cd860d6635fd1018" +checksum = "9383845dd924939e7ab0298bbfe231505e20928907d7905aa3bf112287305e06" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -517,9 +517,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b85a5f5f5d99047544f4ec31330ee15121dcb8ef5af3e791a5207e6b92b05b" +checksum = "11495cb8c8d3141fc27556a4c9188b81531ad5ec3076a0394c61a6dcfbce9f34" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea98f81bcd759dbfa3601565f9d7a02220d8ef1d294ec955948b90aaafbfd857" +checksum = "ca445cef0eb6c2cf51cfb4e214fbf1ebd00893ae2e6f3b944c8101b07990f988" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -540,9 +540,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd14f68a482e67dfba52d404dfff1d3b0d9fc3b4775bd0923f3175d7661c3bd" +checksum = "358d6a8d7340b9eb1a7589a6c1fb00df2c9b26e90737fa5ed0108724dd8dac2c" dependencies = [ "alloy-primitives", "serde", @@ -550,9 +550,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca5898f753ff0d15a0dc955c169523d8fee57e05bb5a38a398b3451b0b988be" +checksum = "4a5f821f30344862a0b6eb9a1c2eb91dfb2ff44c7489f37152a526cdcab79264" dependencies = [ "alloy-consensus", "alloy-eips", @@ -568,9 +568,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e518b0a7771e00728f18be0708f828b18a1cfc542a7153bef630966a26388e0" +checksum = "0938bc615c02421bd86c1733ca7205cc3d99a122d9f9bff05726bd604b76a5c2" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -584,27 +584,28 @@ dependencies = [ "itertools 0.13.0", "serde", "serde_json", + "thiserror 2.0.9", ] [[package]] name = "alloy-rpc-types-trace" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdff93fa38be6982f8613a060e18fa0a37ce440d69ed3b7f37c6c69036ce1c53" +checksum = "cd38207e056cc7d1372367fbb4560ddf9107cbd20731743f641246bf0dede149" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "alloy-rpc-types-txpool" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9dc647985db41fd164e807577134da1179b9f5ba0959f8698d6587eaa568f5" +checksum = "b7fd456a3fa9ea732d1c0611c9d52b5326ee29f4d02d01b07dac453ed68d9eb5" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -614,9 +615,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3dc8d4a08ffc90c1381d39a4afa2227668259a42c97ab6eecf51cbd82a8761" +checksum = "ae0465c71d4dced7525f408d84873aeebb71faf807d22d74c4a426430ccd9b55" dependencies = [ "alloy-primitives", "serde", @@ -625,9 +626,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16188684100f6e0f2a2b949968fe3007749c5be431549064a1bce4e7b3a196a9" +checksum = "9bfa395ad5cc952c82358d31e4c68b27bf4a89a5456d9b27e226e77dac50e4ff" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -641,9 +642,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe06d524ac84fefce1184f2d1273704e62faade7ff1f29c17ac9d493d3ffbdbf" +checksum = "0eb06810c34427d499863817eb506acf57cb9ded9224b374116cae4e22dbd4e9" dependencies = [ "alloy-consensus", "alloy-network", @@ -659,9 +660,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492cedcb4819a588aaef8d59edd5d65291f485d25f64b2aa0806dd86feeafd18" +checksum = "d629e63fec8802ad53706d46e8eceeeae2b135c6648d0de41669a523bf17df4a" dependencies = [ "alloy-consensus", "alloy-network", @@ -677,9 +678,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426409a02587b98e118d2fd32dda3f423805e264a32f9e247a65164163bc0e9b" +checksum = "b426789566a19252cb46b757d91543a6f8e70330c72f312b86c5878595d092ef" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -690,16 +691,16 @@ dependencies = [ "async-trait", "coins-ledger", "futures-util", - "semver 1.0.23", - "thiserror 2.0.6", + "semver 1.0.24", + "thiserror 2.0.9", "tracing", ] [[package]] name = "alloy-signer-local" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2184dab8c9493ab3e1c9f6bd3bdb563ed322b79023d81531935e84a4fdf7cf1" +checksum = "fbdc63ce9eda1283fcbaca66ba4a414b841c0e3edbeef9c86a71242fc9e84ccc" dependencies = [ "alloy-consensus", "alloy-network", @@ -716,26 +717,26 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290ead62e020b751761de95f60056340faba341b20493ae929013d1357b9ba5b" +checksum = "6e7d0c000abd591c9cceac5c07f785f101c9a8c879c6ccd300feca1ae03bdef6" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", - "semver 1.0.23", - "thiserror 2.0.6", + "semver 1.0.24", + "thiserror 2.0.9", "tracing", "trezor-client", ] [[package]] name = "alloy-sol-macro" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d64f851d95619233f74b310f12bcf16e0cbc27ee3762b6115c14a84809280a" +checksum = "a07b74d48661ab2e4b50bb5950d74dbff5e61dd8ed03bb822281b706d54ebacb" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -747,9 +748,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" +checksum = "19cc9c7f20b90f9be1a8f71a3d8e283a43745137b0837b1a1cb13159d37cad72" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -766,9 +767,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c02997ccef5f34f9c099277d4145f183b422938ed5322dc57a089fe9b9ad9ee" +checksum = "713b7e6dfe1cb2f55c80fb05fd22ed085a1b4e48217611365ed0ae598a74c6ac" dependencies = [ "alloy-json-abi", "const-hex", @@ -777,15 +778,15 @@ dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", "serde_json", - "syn 2.0.90", + "syn 2.0.94", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce13ff37285b0870d0a0746992a4ae48efaf34b766ae4c2640fa15e5305f8e73" +checksum = "1eda2711ab2e1fb517fc6e2ffa9728c9a232e296d16810810e6957b781a1b8bc" dependencies = [ "serde", "winnow 0.6.20", @@ -793,9 +794,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1174cafd6c6d810711b4e00383037bdb458efc4fe3dbafafa16567e0320c54d8" +checksum = "e3b478bc9c0c4737a04cd976accde4df7eba0bdc0d90ad6ff43d58bc93cf79c1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -806,9 +807,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628be5b9b75e4f4c4f2a71d985bbaca4f23de356dc83f1625454c505f5eef4df" +checksum = "d17722a198f33bbd25337660787aea8b8f57814febb7c746bc30407bdfc39448" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -816,9 +817,9 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -826,24 +827,24 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e24412cf72f79c95cd9b1d9482e3a31f9d94c24b43c4b3b710cc8d4341eaab0" +checksum = "6e1509599021330a31c4a6816b655e34bf67acb1cc03c564e09fd8754ff6c5de" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest 0.12.9", "serde_json", - "tower 0.5.1", + "tower 0.5.2", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0577a1f67ce70ece3f2b27cf1011da7222ef0a5701f7dcb558e5356278eeb531" +checksum = "fa4da44bc9a5155ab599666d26decafcf12204b72a80eeaba7c5e234ee8ac205" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -862,15 +863,15 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca46272d17f9647fdb56080ed26c72b3ea5078416831130f5ed46f3b4be0ed6" +checksum = "58011745b2f17b334db40df9077d75b181f78360a5bc5c35519e15d4bfce15e2" dependencies = [ "alloy-pubsub", "alloy-transport", "futures 0.3.31", "http 1.2.0", - "rustls 0.23.19", + "rustls 0.23.20", "serde_json", "tokio", "tokio-tungstenite", @@ -880,9 +881,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a5fd8fea044cc9a8c8a50bb6f28e31f0385d820f116c5b98f6f4e55d6e5590b" +checksum = "6917c79e837aa7b77b7a6dae9f89cbe15313ac161c4d3cfaf8909ef21f3d22d8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -949,6 +950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" dependencies = [ "anstyle", + "memchr", "unicode-width 0.2.0", ] @@ -1070,7 +1072,7 @@ dependencies = [ "serde_repr", "similar-asserts", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "tikv-jemallocator", "tokio", "tower 0.4.13", @@ -1101,7 +1103,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] @@ -1135,9 +1137,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -1399,9 +1401,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -1478,9 +1480,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.10" +version = "1.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924" +checksum = "c03a50b30228d3af8865ce83376b4e99e1ffa34728220fe2860e4df0bb5278d6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1489,7 +1491,7 @@ dependencies = [ "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.60.7", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1547,9 +1549,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.4" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" +checksum = "b16d1aa50accc11a4b4d5c50f7fb81cc0cf60328259c587d0e6b0f11385bde46" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1572,15 +1574,15 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.51.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c30f6fd5646b99d9b45ec3a0c22e67112c175b2383100c960d7ee39d96c8d96" +checksum = "a6cf16c0e5853312995505557b876dd3f9fb9941e96d031383528ccef14ace57" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1594,15 +1596,15 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.50.0" +version = "1.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" +checksum = "1605dc0bf9f0a4b05b451441a17fcb0bda229db384f23bf5cead3adbab0664ac" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1616,15 +1618,15 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.51.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abaf490c2e48eed0bb8e2da2fb08405647bd7f253996e0f93b981958ea0f73b0" +checksum = "59f3f73466ff24f6ad109095e0f3f2c830bfb4cd6c8b12f744c8e61ebf4d3ba1" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1638,15 +1640,15 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.51.0" +version = "1.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68fde0d69c8bfdc1060ea7da21df3e39f6014da316783336deff0a9ec28f4bf" +checksum = "249b2acaa8e02fd4718705a9494e3eb633637139aa4bb09d70965b0448e865db" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json 0.61.1", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -1684,9 +1686,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +checksum = "427cb637d15d63d6f9aae26358e1c9a9c09d5aa490d64b09354c8217cfef0f28" dependencies = [ "futures-util", "pin-project-lite", @@ -1713,15 +1715,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aws-smithy-json" -version = "0.60.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" -dependencies = [ - "aws-smithy-types", -] - [[package]] name = "aws-smithy-json" version = "0.61.1" @@ -1743,9 +1736,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.4" +version = "1.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" +checksum = "a05dd41a70fc74051758ee75b5c4db2c0ca070ed9229c3df50e9475cda1cb985" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1758,7 +1751,7 @@ dependencies = [ "http-body 0.4.6", "http-body 1.0.1", "httparse", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", @@ -1787,9 +1780,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.9" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" +checksum = "38ddc9bd6c28aeb303477170ddd183760a956a03e083b3902a990238a7e3792d" dependencies = [ "base64-simd", "bytes", @@ -1845,7 +1838,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "itoa", "matchit", @@ -1859,10 +1852,10 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tokio-tungstenite", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -1883,7 +1876,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -2210,9 +2203,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.3.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f265cdb2e8501f1c952749e78babe8f1937be92c98120e5f78fc72d634682bad" +checksum = "fe7acc34ff59877422326db7d6f2d845a582b16396b6b08194942bf34c6528ab" dependencies = [ "bon-macros", "rustversion", @@ -2220,9 +2213,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.3.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38aa5c627cd7706490e5b003d685f8b9d69bc343b1a00b9fdd01e75fdf6827cf" +checksum = "4159dd617a7fbc9be6a692fe69dc2954f8e6bb6bb5e4d7578467441390d77fd0" dependencies = [ "darling 0.20.10", "ident_case", @@ -2230,7 +2223,7 @@ dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", "rustversion", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -2300,9 +2293,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -2357,9 +2350,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -2547,9 +2540,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.3" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "jobserver", "libc", @@ -2618,7 +2611,7 @@ dependencies = [ "reqwest 0.12.9", "revm", "rustyline", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "serial_test", @@ -2840,9 +2833,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" +checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" dependencies = [ "clap", ] @@ -3061,9 +3054,9 @@ dependencies = [ [[package]] name = "compact_str" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ "castaway", "cfg-if 1.0.0", @@ -3096,15 +3089,15 @@ checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width 0.1.14", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -3251,18 +3244,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -3288,9 +3281,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -3628,7 +3621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -3679,12 +3672,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "digest" version = "0.9.0" @@ -3985,9 +3972,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -4029,9 +4016,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -4039,9 +4026,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -4380,6 +4367,17 @@ dependencies = [ "bytes", ] +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "fd-lock" version = "4.0.2" @@ -4510,7 +4508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide 0.8.2", ] [[package]] @@ -4532,9 +4530,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -4581,7 +4579,6 @@ dependencies = [ "comfy-table", "dialoguer", "dunce", - "ethers-contract-abigen", "evm-disassembler", "eyre", "forge-doc", @@ -4606,7 +4603,7 @@ dependencies = [ "futures 0.3.31", "globset", "humantime-serde", - "hyper 1.5.1", + "hyper 1.5.2", "indicatif", "inferno", "itertools 0.13.0", @@ -4628,13 +4625,12 @@ dependencies = [ "similar", "similar-asserts", "solang-parser", - "solar-ast", "solar-parse", "soldeer-commands", "strum", "svm-rs", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "tikv-jemallocator", "tokio", "toml 0.8.19", @@ -4668,7 +4664,7 @@ dependencies = [ "serde", "serde_json", "solang-parser", - "thiserror 2.0.6", + "thiserror 2.0.9", "toml 0.8.19", "tracing", ] @@ -4683,7 +4679,7 @@ dependencies = [ "itertools 0.13.0", "similar-asserts", "solang-parser", - "thiserror 2.0.6", + "thiserror 2.0.9", "toml 0.8.19", "tracing", "tracing-subscriber", @@ -4729,7 +4725,7 @@ dependencies = [ "itertools 0.13.0", "parking_lot 0.12.3", "revm-inspectors", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "tempfile", @@ -4770,7 +4766,7 @@ dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", "serde_json", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -4799,7 +4795,7 @@ dependencies = [ "regex", "reqwest 0.12.9", "revm-primitives", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "tempfile", @@ -4877,10 +4873,10 @@ dependencies = [ "rand 0.8.5", "revm", "revm-inspectors", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "toml 0.8.19", "tracing", "vergen", @@ -4991,7 +4987,7 @@ dependencies = [ "serde_json", "similar-asserts", "terminal_size", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tower 0.4.13", "tracing", @@ -5022,14 +5018,14 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7235826f00dd9196bcbdbb9c168ea38235601db95883a78819ba2303dee34bb8" +checksum = "f67e3eab56847dcf269eb186226f95874b171e262952cff6c910da36b1469e10" dependencies = [ "alloy-json-abi", "alloy-primitives", "auto_impl", - "derivative", + "derive_more", "dirs 5.0.1", "dyn-clone", "foundry-compilers-artifacts", @@ -5042,7 +5038,7 @@ dependencies = [ "path-slash", "rand 0.8.5", "rayon", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "sha2 0.10.8", @@ -5050,7 +5046,7 @@ dependencies = [ "svm-rs", "svm-rs-builds", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", "winnow 0.6.20", @@ -5059,9 +5055,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "097bc5db7be5acf6d92938ad7daabf1932d7aa7c44326cdfc256531a53034d31" +checksum = "865b00448dc2a5d56bae287c36fa716379ffcdd937aefb7758bd20b62024d234" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -5069,9 +5065,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4168053c1ad217866c677a074517e8d51988e5b1bad044b95f3c513aa5b6caa" +checksum = "668972ba511f80895ea12c75cd12fccd6627c26e64763799d83978b4e0916cae" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5080,11 +5076,11 @@ dependencies = [ "md-5", "path-slash", "rayon", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "serde_repr", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", "walkdir", @@ -5093,24 +5089,24 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7beffe7182551d01d249f022a5eab17c36c73b39ae8efd404e0fb9c98b9f80" +checksum = "5a24f7f2a7458171e055c0cb33272f5eccaefbd96d791d74177d9a1fca048f74" dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers-artifacts-solc", "foundry-compilers-core", "path-slash", - "semver 1.0.23", + "semver 1.0.24", "serde", ] [[package]] name = "foundry-compilers-core" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5247875b96dfb99da12d0cd0f6ce98954116d1cf8a9188d613b2a35cd6937b" +checksum = "8005271a079bc6470c61d4145d2e390a827b1ccbb96abb7b69b088f17ffb95e0" dependencies = [ "alloy-primitives", "cfg-if 1.0.0", @@ -5118,12 +5114,12 @@ dependencies = [ "fs_extra", "path-slash", "regex", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "svm-rs", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "walkdir", ] @@ -5151,14 +5147,14 @@ dependencies = [ "regex", "reqwest 0.12.9", "revm-primitives", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "serde_regex", "similar-asserts", - "solang-parser", + "solar-parse", "tempfile", - "thiserror 2.0.6", + "thiserror 2.0.9", "toml 0.8.19", "toml_edit 0.22.22", "tracing", @@ -5211,7 +5207,7 @@ dependencies = [ "revm", "revm-inspectors", "serde", - "thiserror 2.0.6", + "thiserror 2.0.9", "tracing", ] @@ -5258,7 +5254,7 @@ dependencies = [ "revm-inspectors", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", "url", @@ -5275,7 +5271,7 @@ dependencies = [ "foundry-evm-core", "rayon", "revm", - "semver 1.0.23", + "semver 1.0.24", "tracing", ] @@ -5302,7 +5298,7 @@ dependencies = [ "rand 0.8.5", "revm", "serde", - "thiserror 2.0.6", + "thiserror 2.0.9", "tracing", ] @@ -5337,9 +5333,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491e9f9f138086b3627a8c406730dfbb6afcdcf688e6da0eb15df52f0c8ed163" +checksum = "89a794c8a78ba20568a0c86b035768da7e81fed3c51cecea57f81523123cbcfa" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -5353,7 +5349,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", "url", @@ -5365,8 +5361,8 @@ version = "0.0.2" dependencies = [ "alloy-primitives", "foundry-compilers", - "semver 1.0.23", - "thiserror 2.0.6", + "semver 1.0.24", + "thiserror 2.0.9", ] [[package]] @@ -5459,7 +5455,7 @@ dependencies = [ "gcloud-sdk", "rpassword", "serde", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -5583,7 +5579,7 @@ dependencies = [ name = "fs4" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c6b3bd49c37d2aa3f3f2220233b29a7cd23f79d1fe70e5337d25fb390793de" +checksum = "c29c30684418547d476f0b48e84f4821639119c483b1eccd566c8cd0cd05f521" dependencies = [ "rustix", "windows-sys 0.52.0", @@ -5777,7 +5773,7 @@ dependencies = [ "serde_json", "tokio", "tonic", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-util", "tracing", @@ -5883,19 +5879,19 @@ dependencies = [ "bstr", "gix-path", "libc", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "gix-date" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691142b1a34d18e8ed6e6114bc1a2736516c5ad60ef3aa9bd1b694886e3ca92d" +checksum = "c57c477b645ee248b173bb1176b52dd528872f12c50375801a58aaf5ae91113f" dependencies = [ "bstr", "itoa", "jiff", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] @@ -5986,7 +5982,7 @@ dependencies = [ "gix-trace", "home", "once_cell", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] @@ -6058,14 +6054,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd520d09f9f585b34b32aba1d0b36ada89ab7fefb54a8ca3fe37fc482a750937" dependencies = [ "bstr", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" @@ -6373,12 +6369,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - [[package]] name = "hidapi-rusb" version = "1.3.3" @@ -6411,11 +6401,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6535,9 +6525,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -6559,9 +6549,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -6586,7 +6576,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -6596,13 +6586,13 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "log", "rustls 0.23.19", @@ -6620,7 +6610,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -6648,7 +6638,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "native-tls", "tokio", @@ -6667,7 +6657,7 @@ dependencies = [ "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.5.2", "pin-project-lite", "socket2", "tokio", @@ -6998,7 +6988,7 @@ dependencies = [ "log", "num-format", "once_cell", - "quick-xml 0.37.1", + "quick-xml 0.37.2", "rgb", "str_stack", ] @@ -7040,9 +7030,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e" +checksum = "894813a444908c0c8c0e221b041771d107c4a21de1d317dc49bcc66e3c9e5b3f" dependencies = [ "darling 0.20.10", "indoc", @@ -7143,11 +7133,15 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jiff" -version = "0.1.15" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +checksum = "ed0ce60560149333a8e41ca7dc78799c47c5fd435e2bc18faf6a054382eec037" dependencies = [ "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", "windows-sys 0.59.0", ] @@ -7622,9 +7616,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libdbus-sys" @@ -8025,9 +8019,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -8475,9 +8469,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "nybbles" -version = "0.2.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +checksum = "a3409fc85ac27b27d971ea7cd1aabafd2eefa6de7e481c8d4f707225c117e81a" dependencies = [ "alloy-rlp", "const-hex", @@ -8503,9 +8497,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "op-alloy-consensus" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d95d0ec6457ad4d3d7fc0ad41db490b219587ed837ada87a26b28e535db15f" +checksum = "0adb232ec805af3aa35606c19329aa7dc44c4457ae318ed0b8fc7f799dd7dbfe" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8514,14 +8508,14 @@ dependencies = [ "alloy-serde", "derive_more 1.0.0", "serde", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "op-alloy-rpc-types" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba1b44e2035ec04cc61762cb9b5457d0ecd29d9af631e1a1c107ef571ce2318" +checksum = "e68d1a51fe3ee143f102b82f54fa237f21d12635da363276901e6d3ef6c65b7b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8985,7 +8979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.6", + "thiserror 2.0.9", "ucd-trie", ] @@ -9209,6 +9203,15 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -9232,9 +9235,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "predicates-core", @@ -9242,30 +9245,20 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", ] -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi", -] - [[package]] name = "prettyplease" version = "0.2.25" @@ -9499,9 +9492,9 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -9726,9 +9719,9 @@ dependencies = [ "chrono", "indexmap 2.7.0", "newtype-uuid", - "quick-xml 0.37.1", + "quick-xml 0.37.2", "strip-ansi-escapes", - "thiserror 2.0.6", + "thiserror 2.0.9", "uuid 1.11.0", ] @@ -9752,9 +9745,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.1" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" dependencies = [ "memchr", ] @@ -9772,7 +9765,7 @@ dependencies = [ "rustc-hash 2.1.0", "rustls 0.23.19", "socket2", - "thiserror 2.0.6", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -9791,7 +9784,7 @@ dependencies = [ "rustls 0.23.19", "rustls-pki-types", "slab", - "thiserror 2.0.6", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -9799,9 +9792,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases 0.2.1", "libc", @@ -9824,7 +9817,7 @@ dependencies = [ name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2 1.0.92", ] @@ -10029,7 +10022,7 @@ dependencies = [ name = "redox_syscall" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -10148,7 +10141,7 @@ dependencies = [ name = "reqwest" version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "async-compression", "base64 0.22.1", @@ -10175,7 +10168,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -10241,13 +10234,14 @@ dependencies = [ [[package]] name = "revm" -version = "18.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15689a3c6a8d14b647b4666f2e236ef47b5a5133cdfd423f545947986fff7013" +checksum = "e8905d0c5f10e767f13ea7cb8e502d315f144071a60fe2bd83977922dd3afa26" dependencies = [ "auto_impl", "cfg-if 1.0.0", "dyn-clone", + "once_cell", "revm-interpreter", "revm-precompile", "serde", @@ -10256,9 +10250,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.13.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d056aaa21f36038ab35fe8ce940ee332903a0b4b992b8ca805fb60c85eb2086" +checksum = "dc873bc873e12a1723493e1a35804fa79b673a0bfb1c19cfee659d46def8be42" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -10269,14 +10263,14 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.9", ] [[package]] name = "revm-interpreter" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e3f11d0fed049a4a10f79820c59113a79b38aed4ebec786a79d5c667bfeb51" +checksum = "e5ff76b50b5a9fa861fbc236fc82ce1afdf58861f65012aea807d679e54630d6" dependencies = [ "revm-primitives", "serde", @@ -10284,9 +10278,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "15.0.0" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e381060af24b750069a2b2d2c54bba273d84e8f5f9e8026fc9262298e26cc336" +checksum = "6542fb37650dfdbf4b9186769e49c4a8bc1901a3280b2ebf32f915b6c8850f36" dependencies = [ "aurora-engine-modexp", "blst", @@ -10304,9 +10298,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "14.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3702f132bb484f4f0d0ca4f6fbde3c82cfd745041abbedd6eda67730e1868ef0" +checksum = "48faea1ecf2c9f80d9b043bbde0db9da616431faed84c4cfa3dd7393005598e6" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -10412,7 +10406,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", - "rlp-derive", "rustc-hex", ] @@ -10486,9 +10479,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" dependencies = [ "alloy-rlp", "arbitrary", @@ -10583,7 +10576,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.23", + "semver 1.0.24", ] [[package]] @@ -10613,9 +10606,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "aws-lc-rs", "log", @@ -10661,7 +10654,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.0.1", + "security-framework 3.1.0", ] [[package]] @@ -10684,9 +10677,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" dependencies = [ "web-time", ] @@ -10742,9 +10735,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rusty-fork" @@ -10840,9 +10833,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.5" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" +checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640" dependencies = [ "sdd", ] @@ -10877,14 +10870,14 @@ dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", "serde_derive_internals", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] name = "schnellru" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ "ahash 0.8.11", "cfg-if 1.0.0", @@ -10927,9 +10920,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" [[package]] name = "seahash" @@ -11040,9 +11033,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc" dependencies = [ "bitflags 2.6.0", "core-foundation 0.10.0", @@ -11053,9 +11046,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -11072,9 +11065,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -11216,9 +11209,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -11235,9 +11228,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -11257,9 +11250,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "indexmap 2.7.0", "itoa", @@ -11638,9 +11631,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "snapbox" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1373ce406dfad473059bbc31d807715642182bbc952a811952b58d1c9e41dcfa" +checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" dependencies = [ "anstream", "anstyle", @@ -11703,16 +11696,16 @@ dependencies = [ [[package]] name = "solar-ast" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aeaf7a4bd326242c909bd287291226a540b62b36fa5824880248f4b1d4d6af" +checksum = "5d3f6c4a476a16dcd36933a70ecdb0a807f8949cc5f3c4c1984e3748666bd714" dependencies = [ "alloy-primitives", "bumpalo", "either", "num-bigint 0.4.6", "num-rational", - "semver 1.0.23", + "semver 1.0.24", "solar-data-structures", "solar-interface", "solar-macros", @@ -11722,18 +11715,18 @@ dependencies = [ [[package]] name = "solar-config" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d00d672a40a1a3620d7696f01a2d3301abf883d8168e1a9da3bf83f0c8e343" +checksum = "d40434a61f2c14a9e3777fbc478167bddee9828532fc26c57e416e9277916b09" dependencies = [ "strum", ] [[package]] name = "solar-data-structures" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6e4eb0b72ed7adbb808897c85de08ea99609774a58c72e3dce55c758043ca2" +checksum = "71d07263243b313296eca18f18eda3a190902dc3284bf67ceff29b8b54dac3e6" dependencies = [ "bumpalo", "index_vec", @@ -11746,9 +11739,9 @@ dependencies = [ [[package]] name = "solar-interface" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fb8925638f3da1bba7a9a6ebeac3511e5c6354f921f2bb2e1ddce4ac70c107" +checksum = "9a87009b6989b2cc44d8381e3b86ff3b90280d54a60321919b6416214cd602f3" dependencies = [ "annotate-snippets", "anstream", @@ -11767,16 +11760,16 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 1.0.69", + "thiserror 2.0.9", "tracing", "unicode-width 0.2.0", ] [[package]] name = "solar-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0cc54b74e214647c1bbfc098d080cc5deac77f8dcb99aca91747276b01a15ad" +checksum = "970d7c774741f786d62cab78290e47d845b0b9c0c9d094a1642aced1d7946036" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -11785,9 +11778,9 @@ dependencies = [ [[package]] name = "solar-parse" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82c3659c15975cd80e5e1c44591278c230c59ad89082d797837499a4784e1b" +checksum = "2e1e2d07fae218aca1b4cca81216e5c9ad7822516d48a28f11e2eaa8ffa5b249" dependencies = [ "alloy-primitives", "bitflags 2.6.0", @@ -11838,7 +11831,7 @@ dependencies = [ "regex", "reqwest 0.12.9", "sanitize-filename", - "semver 1.0.23", + "semver 1.0.24", "serde", "serde_json", "sha2 0.10.8", @@ -12198,7 +12191,7 @@ dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", "rustversion", - "syn 2.0.90", + "syn 2.0.94", ] [[package]] @@ -12222,9 +12215,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svm-rs" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aebac1b1ef2b46e2e2bdf3c09db304800f2a77c1fa902bd5231490203042be8" +checksum = "a1e9bc6b09b8a7a919128f8c029ae4048d83f814af557e948115273c75864acf" dependencies = [ "const-hex", "dirs 5.0.1", @@ -12235,20 +12228,20 @@ dependencies = [ "serde_json", "sha2 0.10.8", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.9", "url", "zip", ] [[package]] name = "svm-rs-builds" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fa0f145894cb4d1c14446f08098ee5f21fc37ccbd1a7dd9dd355bbc806de3b" +checksum = "34d0964cd9dfcbf8bd21057c1a4aa293fefab208306461989ce723dd9c51e71e" dependencies = [ "build_const", "const-hex", - "semver 1.0.23", + "semver 1.0.24", "serde_json", "svm-rs", ] @@ -12277,9 +12270,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -12288,9 +12281,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219389c1ebe89f8333df8bdfb871f6631c552ff399c23cac02480b6088aad8f0" +checksum = "31e89d8bf2768d277f40573c83a02a099e96d96dd3104e13ea676194e61ac4b0" dependencies = [ "paste", "proc-macro2 1.0.92", @@ -12310,12 +12303,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -12392,12 +12379,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if 1.0.0", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -12450,9 +12438,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "textwrap" @@ -12476,11 +12464,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.9", ] [[package]] @@ -12496,9 +12484,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -12607,9 +12595,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -12675,7 +12663,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.19", + "rustls 0.23.20", "tokio", ] @@ -12711,7 +12699,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.19", + "rustls 0.23.20", "rustls-pki-types", "tokio", "tokio-rustls 0.26.1", @@ -12817,7 +12805,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-timeout", "hyper-util", "percent-encoding", @@ -12863,14 +12851,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -13032,9 +13020,9 @@ dependencies = [ [[package]] name = "tracing-tracy" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc775fdaf33c3dfd19dc354729e65e87914bc67dcdc390ca1210807b8bee5902" +checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" dependencies = [ "tracing-core", "tracing-subscriber", @@ -13043,9 +13031,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.17.5" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e295eae54124872df35720dc3a5b1e827c7deee352b342ec7f7e626d0d0ef3" +checksum = "d90a2c01305b02b76fdd89ac8608bae27e173c829a35f7d76a345ab5d33836db" dependencies = [ "loom", "once_cell", @@ -13054,9 +13042,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3637e734239e12ab152cd269302500bd063f37624ee210cd04b4936ed671f3b1" +checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" dependencies = [ "cc", "windows-targets 0.52.6", @@ -13170,9 +13158,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" @@ -14052,7 +14040,7 @@ dependencies = [ name = "winnow" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] @@ -14244,9 +14232,9 @@ dependencies = [ [[package]] name = "zip" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" dependencies = [ "arbitrary", "bzip2", @@ -14256,7 +14244,7 @@ dependencies = [ "flate2", "indexmap 2.7.0", "memchr", - "thiserror 2.0.6", + "thiserror 2.0.9", "zopfli", ] diff --git a/Cargo.toml b/Cargo.toml index 7893a22fe..b3f92c6a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,8 @@ uninlined-format-args = "warn" use-self = "warn" redundant-clone = "warn" octal-escapes = "allow" +# until is fixed +literal-string-with-formatting-args = "allow" [workspace.lints.rust] rust-2018-idioms = "warn" @@ -178,57 +180,53 @@ foundry-strategy-zksync = { path = "crates/strategy/zksync" } # solc & compilation utilities foundry-block-explorers = { version = "0.9.0", default-features = false } -foundry-compilers = { version = "0.12.7", default-features = false } -foundry-fork-db = "0.9.0" +foundry-compilers = { version = "0.12.9", default-features = false } +foundry-fork-db = "0.10.0" solang-parser = "=0.3.3" -solar-ast = { version = "=0.1.0", default-features = false } -solar-parse = { version = "=0.1.0", default-features = false } +solar-parse = { version = "=0.1.1", default-features = false } ## revm -revm = { version = "18.0.0", default-features = false } -revm-primitives = { version = "14.0.0", default-features = false } -revm-inspectors = { version = "0.13.0", features = ["serde"] } - -## ethers -ethers-contract-abigen = { version = "2.0.14", default-features = false } +revm = { version = "19.0.0", default-features = false } +revm-primitives = { version = "15.1.0", default-features = false } +revm-inspectors = { version = "0.14.1", features = ["serde"] } ## alloy -alloy-consensus = { version = "0.8.0", default-features = false } -alloy-contract = { version = "0.8.0", default-features = false } -alloy-eips = { version = "0.8.0", default-features = false } -alloy-genesis = { version = "0.8.0", default-features = false } -alloy-json-rpc = { version = "0.8.0", default-features = false } -alloy-network = { version = "0.8.0", default-features = false } -alloy-provider = { version = "0.8.0", default-features = false } -alloy-pubsub = { version = "0.8.0", default-features = false } -alloy-rpc-client = { version = "0.8.0", default-features = false } -alloy-rpc-types = { version = "0.8.0", default-features = true } -alloy-serde = { version = "0.8.0", default-features = false } -alloy-signer = { version = "0.8.0", default-features = false } -alloy-signer-aws = { version = "0.8.0", default-features = false } -alloy-signer-gcp = { version = "0.8.0", default-features = false } -alloy-signer-ledger = { version = "0.8.0", default-features = false } -alloy-signer-local = { version = "0.8.0", default-features = false } -alloy-signer-trezor = { version = "0.8.0", default-features = false } -alloy-transport = { version = "0.8.0", default-features = false } -alloy-transport-http = { version = "0.8.0", default-features = false } -alloy-transport-ipc = { version = "0.8.0", default-features = false } -alloy-transport-ws = { version = "0.8.0", default-features = false } -alloy-node-bindings = { version = "0.8.0", default-features = false } -alloy-network-primitives = { version = "0.8.0", default-features = false } +alloy-consensus = { version = "0.9.0", default-features = false } +alloy-contract = { version = "0.9.0", default-features = false } +alloy-eips = { version = "0.9.0", default-features = false } +alloy-genesis = { version = "0.9.0", default-features = false } +alloy-json-rpc = { version = "0.9.0", default-features = false } +alloy-network = { version = "0.9.0", default-features = false } +alloy-provider = { version = "0.9.0", default-features = false } +alloy-pubsub = { version = "0.9.0", default-features = false } +alloy-rpc-client = { version = "0.9.0", default-features = false } +alloy-rpc-types = { version = "0.9.0", default-features = true } +alloy-serde = { version = "0.9.0", default-features = false } +alloy-signer = { version = "0.9.0", default-features = false } +alloy-signer-aws = { version = "0.9.0", default-features = false } +alloy-signer-gcp = { version = "0.9.0", default-features = false } +alloy-signer-ledger = { version = "0.9.0", default-features = false } +alloy-signer-local = { version = "0.9.0", default-features = false } +alloy-signer-trezor = { version = "0.9.0", default-features = false } +alloy-transport = { version = "0.9.0", default-features = false } +alloy-transport-http = { version = "0.9.0", default-features = false } +alloy-transport-ipc = { version = "0.9.0", default-features = false } +alloy-transport-ws = { version = "0.9.0", default-features = false } +alloy-node-bindings = { version = "0.9.0", default-features = false } +alloy-network-primitives = { version = "0.9.0", default-features = false } ## alloy-core -alloy-dyn-abi = "0.8.14" -alloy-json-abi = "0.8.14" -alloy-primitives = { version = "0.8.14", features = [ +alloy-dyn-abi = "0.8.18" +alloy-json-abi = "0.8.18" +alloy-primitives = { version = "0.8.18", features = [ "getrandom", "rand", "map-foldhash", ] } -alloy-sol-macro-expander = "0.8.14" -alloy-sol-macro-input = "0.8.14" -alloy-sol-types = "0.8.14" -syn-solidity = "0.8.14" +alloy-sol-macro-expander = "0.8.18" +alloy-sol-macro-input = "0.8.18" +alloy-sol-types = "0.8.18" +syn-solidity = "0.8.18" alloy-chains = "0.1" alloy-rlp = "0.3" @@ -237,8 +235,8 @@ alloy-zksync = "0.8.0" alloy-trie = "0.7.0" ## op-alloy -op-alloy-rpc-types = "0.8.0" -op-alloy-consensus = "0.8.0" +op-alloy-rpc-types = "0.9.0" +op-alloy-consensus = "0.9.0" ## cli anstream = "0.6" diff --git a/README.md b/README.md index aa90178e4..032d724c3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ See our [contributing guidelines](./CONTRIBUTING.md). ### Foundry - Foundry is a clean-room rewrite of the testing framework [DappTools](https://github.com/dapphub/dapptools). None of this would have been possible without the DappHub team's work over the years. -- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. +- [Matthias Seitz](https://twitter.com/mattsse_): Created [ethers-solc] (now [foundry-compilers]) which is the backbone of our compilation pipeline, as well as countless contributions to ethers, in particular the `abigen` macros. - [Rohit Narurkar](https://twitter.com/rohitnarurkar): Created the Rust Solidity version manager [svm-rs](https://github.com/roynalnaruto/svm-rs) which we use to auto-detect and manage multiple Solidity versions. - [Brock Elmore](https://twitter.com/brockjelmore): For extending the VM's cheatcodes and implementing [structured call tracing](https://github.com/foundry-rs/foundry/pull/192), a critical feature for debugging smart contract calls. - All the other [contributors](https://github.com/foundry-rs/foundry/graphs/contributors) to the [ethers-rs](https://github.com/gakonst/ethers-rs) & [foundry](https://github.com/foundry-rs/foundry) repositories and chatrooms. diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 50a9a66b3..c9f9048b8 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -65,7 +65,6 @@ impl Block { nonce: partial_header.nonce, base_fee_per_gas: partial_header.base_fee, requests_hash: partial_header.requests_hash, - target_blobs_per_block: None, }, transactions, ommers: vec![], @@ -158,7 +157,6 @@ mod tests { parent_beacon_block_root: Default::default(), base_fee_per_gas: None, requests_hash: None, - target_blobs_per_block: None, }; let encoded = alloy_rlp::encode(&header); @@ -200,7 +198,6 @@ mod tests { nonce: B64::ZERO, base_fee_per_gas: None, requests_hash: None, - target_blobs_per_block: None, }; header.encode(&mut data); @@ -234,7 +231,6 @@ mod tests { parent_beacon_block_root: None, base_fee_per_gas: None, requests_hash: None, - target_blobs_per_block: None, }; let header = Header::decode(&mut data.as_slice()).unwrap(); assert_eq!(header, expected); @@ -267,7 +263,6 @@ mod tests { excess_blob_gas: None, parent_beacon_block_root: None, requests_hash: None, - target_blobs_per_block: None, }; assert_eq!(header.hash_slow(), expected_hash); } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 29b8aee88..38eda6040 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -619,7 +619,6 @@ impl TryFrom for TypedTransaction { TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), TxEnvelope::Eip7702(tx) => Ok(Self::EIP7702(tx)), - _ => Err(ConversionError::Custom("UnsupportedTxType".to_string())), }, AnyTxEnvelope::Unknown(mut tx) => { // Try to convert to deposit transaction @@ -1260,7 +1259,7 @@ impl From>> for OtsReceipt { } as u8; let receipt = ReceiptWithBloom::>::from(value); let status = receipt.status(); - let cumulative_gas_used = receipt.cumulative_gas_used() as u64; + let cumulative_gas_used = receipt.cumulative_gas_used(); let logs = receipt.logs().to_vec(); let logs_bloom = receipt.logs_bloom; @@ -1269,7 +1268,7 @@ impl From>> for OtsReceipt { } impl TypedReceipt { - pub fn cumulative_gas_used(&self) -> u128 { + pub fn cumulative_gas_used(&self) -> u64 { self.as_receipt_with_bloom().cumulative_gas_used() } @@ -1653,7 +1652,7 @@ mod tests { let receipt = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { status: false.into(), - cumulative_gas_used: 0x1u128, + cumulative_gas_used: 0x1, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( @@ -1688,7 +1687,7 @@ mod tests { let expected = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { status: false.into(), - cumulative_gas_used: 0x1u128, + cumulative_gas_used: 0x1, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index 8743f0642..392eb47ac 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -2,7 +2,7 @@ use crate::{error::RequestError, pubsub::PubSubConnection, PubSubRpcHandler}; use anvil_rpc::request::Request; -use bytes::BytesMut; +use bytes::{BufMut, BytesMut}; use futures::{ready, Sink, Stream, StreamExt}; use interprocess::local_socket::{self as ls, tokio::prelude::*}; use std::{ @@ -171,6 +171,8 @@ impl tokio_util::codec::Encoder for JsonRpcCodec { fn encode(&mut self, msg: String, buf: &mut BytesMut) -> io::Result<()> { buf.extend_from_slice(msg.as_bytes()); + // Add newline character + buf.put_u8(b'\n'); Ok(()) } } diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 48e17ed44..ffdcc1755 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -3,7 +3,7 @@ use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; use eyre::Result; -use foundry_cli::{opts::GlobalOpts, utils}; +use foundry_cli::{opts::GlobalArgs, utils}; #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] @@ -13,9 +13,9 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[derive(Parser)] #[command(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] pub struct Anvil { - /// Include the global options. + /// Include the global arguments. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, #[command(flatten)] pub node: NodeArgs, @@ -50,7 +50,7 @@ fn run() -> Result<()> { let mut args = Anvil::parse(); args.global.init()?; - args.node.evm_opts.resolve_rpc_alias(); + args.node.evm.resolve_rpc_alias(); if let Some(cmd) = &args.cmd { match cmd { diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 19e9193f9..8be6be750 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -185,7 +185,7 @@ pub struct NodeArgs { pub transaction_block_keeper: Option, #[command(flatten)] - pub evm_opts: AnvilEvmArgs, + pub evm: AnvilEvmArgs, #[command(flatten)] pub server_config: ServerConfig, @@ -209,15 +209,12 @@ const DEFAULT_DUMP_INTERVAL: Duration = Duration::from_secs(60); impl NodeArgs { pub fn into_node_config(self) -> eyre::Result { let genesis_balance = Unit::ETHER.wei().saturating_mul(U256::from(self.balance)); - let compute_units_per_second = if self.evm_opts.no_rate_limit { - Some(u64::MAX) - } else { - self.evm_opts.compute_units_per_second - }; + let compute_units_per_second = + if self.evm.no_rate_limit { Some(u64::MAX) } else { self.evm.compute_units_per_second }; let hardfork = match &self.hardfork { Some(hf) => { - if self.evm_opts.optimism { + if self.evm.optimism { Some(OptimismHardfork::from_str(hf)?.into()) } else { Some(EthereumHardfork::from_str(hf)?.into()) @@ -227,9 +224,9 @@ impl NodeArgs { }; Ok(NodeConfig::default() - .with_gas_limit(self.evm_opts.gas_limit) - .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) - .with_gas_price(self.evm_opts.gas_price) + .with_gas_limit(self.evm.gas_limit) + .disable_block_gas_limit(self.evm.disable_block_gas_limit) + .with_gas_price(self.evm.gas_price) .with_hardfork(hardfork) .with_blocktime(self.block_time) .with_no_mining(self.no_mining) @@ -238,54 +235,50 @@ impl NodeArgs { .with_genesis_balance(genesis_balance) .with_genesis_timestamp(self.timestamp) .with_port(self.port) - .with_fork_choice( - match (self.evm_opts.fork_block_number, self.evm_opts.fork_transaction_hash) { - (Some(block), None) => Some(ForkChoice::Block(block)), - (None, Some(hash)) => Some(ForkChoice::Transaction(hash)), - _ => { - self.evm_opts.fork_url.as_ref().and_then(|f| f.block).map(ForkChoice::Block) - } - }, - ) - .with_fork_headers(self.evm_opts.fork_headers) - .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)) + .with_fork_choice(match (self.evm.fork_block_number, self.evm.fork_transaction_hash) { + (Some(block), None) => Some(ForkChoice::Block(block)), + (None, Some(hash)) => Some(ForkChoice::Transaction(hash)), + _ => self.evm.fork_url.as_ref().and_then(|f| f.block).map(ForkChoice::Block), + }) + .with_fork_headers(self.evm.fork_headers) + .with_fork_chain_id(self.evm.fork_chain_id.map(u64::from).map(U256::from)) + .fork_request_timeout(self.evm.fork_request_timeout.map(Duration::from_millis)) + .fork_request_retries(self.evm.fork_request_retries) + .fork_retry_backoff(self.evm.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) - .disable_min_priority_fee(self.evm_opts.disable_min_priority_fee) - .with_storage_caching(self.evm_opts.no_storage_caching) + .with_eth_rpc_url(self.evm.fork_url.map(|fork| fork.url)) + .with_base_fee(self.evm.block_base_fee_per_gas) + .disable_min_priority_fee(self.evm.disable_min_priority_fee) + .with_storage_caching(self.evm.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) .set_silent(shell::is_quiet()) .set_config_out(self.config_out) - .with_chain_id(self.evm_opts.chain_id) + .with_chain_id(self.evm.chain_id) .with_transaction_order(self.order) .with_genesis(self.init) - .with_steps_tracing(self.evm_opts.steps_tracing) - .with_print_logs(!self.evm_opts.disable_console_log) - .with_auto_impersonate(self.evm_opts.auto_impersonate) + .with_steps_tracing(self.evm.steps_tracing) + .with_print_logs(!self.evm.disable_console_log) + .with_auto_impersonate(self.evm.auto_impersonate) .with_ipc(self.ipc) - .with_code_size_limit(self.evm_opts.code_size_limit) - .disable_code_size_limit(self.evm_opts.disable_code_size_limit) + .with_code_size_limit(self.evm.code_size_limit) + .disable_code_size_limit(self.evm.disable_code_size_limit) .set_pruned_history(self.prune_history) .with_init_state(self.load_state.or_else(|| self.state.and_then(|s| s.state))) .with_transaction_block_keeper(self.transaction_block_keeper) .with_max_persisted_states(self.max_persisted_states) - .with_optimism(self.evm_opts.optimism) - .with_odyssey(self.evm_opts.odyssey) - .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) + .with_optimism(self.evm.optimism) + .with_odyssey(self.evm.odyssey) + .with_disable_default_create2_deployer(self.evm.disable_default_create2_deployer) .with_slots_in_an_epoch(self.slots_in_an_epoch) - .with_memory_limit(self.evm_opts.memory_limit) + .with_memory_limit(self.evm.memory_limit) .with_cache_path(self.cache_path)) } fn account_generator(&self) -> AccountGenerator { let mut gen = AccountGenerator::new(self.accounts as usize) .phrase(DEFAULT_MNEMONIC) - .chain_id(self.evm_opts.chain_id.unwrap_or_else(|| CHAIN_ID.into())); + .chain_id(self.evm.chain_id.unwrap_or_else(|| CHAIN_ID.into())); if let Some(ref mnemonic) = self.mnemonic { gen = gen.phrase(mnemonic); } else if let Some(count) = self.mnemonic_random { @@ -845,10 +838,7 @@ mod tests { "--fork-header", "Referrer: example.com", ]); - assert_eq!( - args.evm_opts.fork_headers, - vec!["User-Agent: test-agent", "Referrer: example.com"] - ); + assert_eq!(args.evm.fork_headers, vec!["User-Agent: test-agent", "Referrer: example.com"]); } #[test] @@ -869,7 +859,7 @@ mod tests { #[test] fn can_parse_disable_block_gas_limit() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-block-gas-limit"]); - assert!(args.evm_opts.disable_block_gas_limit); + assert!(args.evm.disable_block_gas_limit); let args = NodeArgs::try_parse_from(["anvil", "--disable-block-gas-limit", "--gas-limit", "100"]); @@ -879,7 +869,7 @@ mod tests { #[test] fn can_parse_disable_code_size_limit() { let args: NodeArgs = NodeArgs::parse_from(["anvil", "--disable-code-size-limit"]); - assert!(args.evm_opts.disable_code_size_limit); + assert!(args.evm.disable_code_size_limit); let args = NodeArgs::try_parse_from([ "anvil", diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 9e22adeed..247586bfd 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -498,10 +498,10 @@ impl NodeConfig { blob_excess_gas_and_price.clone() } else if let Some(excess_blob_gas) = self.genesis.as_ref().and_then(|g| g.excess_blob_gas) { - BlobExcessGasAndPrice::new(excess_blob_gas as u64) + BlobExcessGasAndPrice::new(excess_blob_gas, false) } else { // If no excess blob gas is configured, default to 0 - BlobExcessGasAndPrice::new(0) + BlobExcessGasAndPrice::new(0, false) } } @@ -800,7 +800,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 { - self.fork_chain_id = fork_chain_id.map(Into::into); + self.fork_chain_id = fork_chain_id; self } @@ -1213,11 +1213,12 @@ latest block number: {latest_block}" (block.header.excess_blob_gas, block.header.blob_gas_used) { env.block.blob_excess_gas_and_price = - Some(BlobExcessGasAndPrice::new(blob_excess_gas)); + Some(BlobExcessGasAndPrice::new(blob_excess_gas, false)); let next_block_blob_excess_gas = fees .get_next_block_blob_excess_gas(blob_excess_gas as u128, blob_gas_used as u128); fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( next_block_blob_excess_gas, + false, )); } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index daf9dc4a0..23aac7452 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2315,7 +2315,7 @@ impl EthApi { let to = tx.to(); let gas_price = tx.gas_price(); let value = tx.value(); - let gas = tx.gas_limit() as u128; + let gas = tx.gas_limit(); TxpoolInspectSummary { to, value, gas, gas_price } } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index ad28bd03a..35a0abb7f 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -482,7 +482,7 @@ impl From for SerializableBlock { Self { header: block.header, transactions: block.transactions.into_iter().map(Into::into).collect(), - ommers: block.ommers.into_iter().map(Into::into).collect(), + ommers: block.ommers.into_iter().collect(), } } } @@ -492,7 +492,7 @@ impl From for Block { Self { header: block.header, transactions: block.transactions.into_iter().map(Into::into).collect(), - ommers: block.ommers.into_iter().map(Into::into).collect(), + ommers: block.ommers.into_iter().collect(), } } } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index f4b20868f..c07bfab78 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -30,7 +30,7 @@ use foundry_evm::{ traces::CallTraceNode, utils::odyssey_handler_register, }; -use revm::{db::WrapDatabaseRef, primitives::MAX_BLOB_GAS_PER_BLOCK}; +use revm::db::WrapDatabaseRef; use std::sync::Arc; /// Represents an executed transaction (transacted on the DB) @@ -57,7 +57,7 @@ impl ExecutedTransaction { let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); let receipt_with_bloom: ReceiptWithBloom = Receipt { status: (status_code == 1).into(), - cumulative_gas_used: *cumulative_gas_used as u128, + cumulative_gas_used: *cumulative_gas_used, logs, } .into(); @@ -288,7 +288,7 @@ impl Iterator for &mut TransactionExec let max_blob_gas = self.blob_gas_used.saturating_add( transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0), ); - if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK { + if max_blob_gas > alloy_eips::eip4844::MAX_DATA_GAS_PER_BLOCK { return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction)) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 26787cca6..154dae504 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -97,9 +97,7 @@ use op_alloy_consensus::{TxDeposit, DEPOSIT_TX_TYPE_ID}; use parking_lot::{Mutex, RwLock}; use revm::{ db::WrapDatabaseRef, - primitives::{ - calc_blob_gasprice, BlobExcessGasAndPrice, HashMap, OptimismFields, ResultAndState, - }, + primitives::{BlobExcessGasAndPrice, HashMap, OptimismFields, ResultAndState}, }; use std::{ collections::BTreeMap, @@ -922,10 +920,28 @@ impl Backend { let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash())); if let Some((number, hash)) = fork_num_and_hash { - // If loading state file on a fork, set best number to the fork block number. - // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838 - self.blockchain.storage.write().best_number = U64::from(number); - self.blockchain.storage.write().best_hash = hash; + let best_number = state.best_block_number.unwrap_or(block.number.to::()); + trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number); + // If the state.block_number is greater than the fork block number, set best number + // to the state block number. + // Ref: https://github.com/foundry-rs/foundry/issues/9539 + if best_number.to::() > number { + self.blockchain.storage.write().best_number = best_number; + let best_hash = + self.blockchain.storage.read().hash(best_number.into()).ok_or_else( + || { + BlockchainError::RpcError(RpcError::internal_error_with(format!( + "Best hash not found for best number {best_number}", + ))) + }, + )?; + self.blockchain.storage.write().best_hash = best_hash; + } else { + // If loading state file on a fork, set best number to the fork block number. + // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838 + self.blockchain.storage.write().best_number = U64::from(number); + self.blockchain.storage.write().best_hash = hash; + } } else { let best_number = state.best_block_number.unwrap_or(block.number.to::()); self.blockchain.storage.write().best_number = best_number; @@ -1271,8 +1287,10 @@ impl Backend { // update next base fee self.fees.set_base_fee(next_block_base_fee); - self.fees - .set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(next_block_excess_blob_gas)); + self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( + next_block_excess_blob_gas, + false, + )); // notify all listeners self.notify_on_new_block(header, block_hash); @@ -2323,7 +2341,8 @@ impl Backend { // Cancun specific let excess_blob_gas = block.header.excess_blob_gas; - let blob_gas_price = calc_blob_gasprice(excess_blob_gas.unwrap_or_default()); + let blob_gas_price = + alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default()); let blob_gas_used = transaction.blob_gas(); let effective_gas_price = match transaction.transaction { @@ -2391,14 +2410,14 @@ impl Backend { transaction_hash: info.transaction_hash, transaction_index: Some(info.transaction_index), block_number: Some(block.header.number), - gas_used: info.gas_used as u128, + gas_used: info.gas_used, contract_address: info.contract_address, effective_gas_price, block_hash: Some(block_hash), from: info.from, to: info.to, blob_gas_price: Some(blob_gas_price), - blob_gas_used: blob_gas_used.map(|g| g as u128), + blob_gas_used, authorization_list: None, }; @@ -2791,7 +2810,7 @@ impl TransactionValidator for Backend { // Ensure the tx does not exceed the max blobs per block. if blob_count > MAX_BLOBS_PER_BLOCK { - return Err(InvalidTransactionError::TooManyBlobs(MAX_BLOBS_PER_BLOCK, blob_count)) + return Err(InvalidTransactionError::TooManyBlobs(blob_count)) } // Check for any blob validation errors @@ -2966,7 +2985,6 @@ pub fn transaction_build( let new_signed = Signed::new_unchecked(t, sig, hash); AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(new_signed)) } - _ => unreachable!("unknown tx type"), }; let tx = Transaction { diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 5635a7acc..46429d453 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -556,10 +556,7 @@ impl MinedTransaction { GethDebugBuiltInTracerType::CallTracer => { return match tracer_config.into_call_config() { Ok(call_config) => Ok(GethTraceBuilder::new(self.info.traces.clone()) - .geth_call_traces( - call_config, - self.receipt.cumulative_gas_used() as u64, - ) + .geth_call_traces(call_config, self.receipt.cumulative_gas_used()) .into()), Err(e) => Err(RpcError::invalid_params(e.to_string()).into()), }; @@ -578,7 +575,7 @@ impl MinedTransaction { // default structlog tracer Ok(GethTraceBuilder::new(self.info.traces.clone()) .geth_traces( - self.receipt.cumulative_gas_used() as u64, + self.receipt.cumulative_gas_used(), self.info.out.clone().unwrap_or_default(), config, ) diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index dda9b8bb2..0c9723c40 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -246,8 +246,8 @@ pub enum InvalidTransactionError { /// Thrown when there are no `blob_hashes` in the transaction, and it is an EIP-4844 tx. #[error("`blob_hashes` are required for EIP-4844 transactions")] NoBlobHashes, - #[error("too many blobs in one transaction, max: {0}, have: {1}")] - TooManyBlobs(usize, usize), + #[error("too many blobs in one transaction, have: {0}")] + TooManyBlobs(usize), /// Thrown when there's a blob validation error #[error(transparent)] BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError), @@ -297,7 +297,7 @@ impl From for InvalidTransactionError { InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction, InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported, InvalidTransaction::EmptyBlobs => Self::EmptyBlobs, - InvalidTransaction::TooManyBlobs { max, have } => Self::TooManyBlobs(max, have), + InvalidTransaction::TooManyBlobs { have } => Self::TooManyBlobs(have), InvalidTransaction::AuthorizationListNotSupported => { Self::AuthorizationListNotSupported } @@ -305,6 +305,7 @@ impl From for InvalidTransactionError { InvalidTransaction::OptimismError(_) | InvalidTransaction::EofCrateShouldHaveToAddress | InvalidTransaction::EmptyAuthorizationList => Self::Revm(err), + InvalidTransaction::GasFloorMoreThanGasLimit => Self::Revm(err), } } } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index f41c51505..bb405f62d 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -5,6 +5,7 @@ use crate::eth::{ use alloy_consensus::Header; use alloy_eips::{ calc_next_block_base_fee, eip1559::BaseFeeParams, eip4844::MAX_DATA_GAS_PER_BLOCK, + eip7840::BlobParams, }; use alloy_primitives::B256; use anvil_core::eth::transaction::TypedTransaction; @@ -172,7 +173,7 @@ impl FeeManager { /// Calculates the next block blob base fee, using the provided excess blob gas pub fn get_next_block_blob_base_fee_per_gas(&self, excess_blob_gas: u128) -> u128 { - crate::revm::primitives::calc_blob_gasprice(excess_blob_gas as u64) + alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas as u64) } /// Calculates the next block blob excess gas, using the provided parent blob gas used and @@ -182,7 +183,7 @@ impl FeeManager { blob_gas_used: u128, blob_excess_gas: u128, ) -> u64 { - crate::revm::primitives::calc_excess_blob_gas(blob_gas_used as u64, blob_excess_gas as u64) + alloy_eips::eip4844::calc_excess_blob_gas(blob_gas_used as u64, blob_excess_gas as u64) } } @@ -246,7 +247,7 @@ impl FeeHistoryService { let base_fee = header.base_fee_per_gas.map(|g| g as u128).unwrap_or_default(); let excess_blob_gas = header.excess_blob_gas.map(|g| g as u128); let blob_gas_used = header.blob_gas_used.map(|g| g as u128); - let base_fee_per_blob_gas = header.blob_fee(); + let base_fee_per_blob_gas = header.blob_fee(BlobParams::cancun()); let mut item = FeeHistoryCacheItem { base_fee, gas_used_ratio: 0f64, @@ -270,7 +271,7 @@ impl FeeHistoryService { blob_gas_used.map(|g| g / MAX_DATA_GAS_PER_BLOCK as f64).unwrap_or(0 as f64); // extract useful tx info (gas_used, effective_reward) - let mut transactions: Vec<(u128, u128)> = receipts + let mut transactions: Vec<(_, _)> = receipts .iter() .enumerate() .map(|(i, receipt)| { @@ -312,7 +313,7 @@ impl FeeHistoryService { item.rewards = reward_percentiles .into_iter() .filter_map(|p| { - let target_gas = (p * gas_used / 100f64) as u128; + let target_gas = (p * gas_used / 100f64) as u64; let mut sum_gas = 0; for (gas_used, effective_reward) in transactions.iter().cloned() { sum_gas += gas_used; diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index f9a7334e0..703c53b53 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -384,7 +384,7 @@ impl EthApi { let total_fees = receipts .iter() - .fold(0, |acc, receipt| acc + receipt.gas_used * receipt.effective_gas_price); + .fold(0, |acc, receipt| acc + (receipt.gas_used as u128) * receipt.effective_gas_price); let Block { header, uncles, transactions, withdrawals } = block.inner; diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index ea195f000..65bdba611 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -78,7 +78,7 @@ async fn can_send_multiple_blobs_in_one_tx() { let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - assert_eq!(receipt.blob_gas_used, Some(MAX_DATA_GAS_PER_BLOCK as u128)); + assert_eq!(receipt.blob_gas_used, Some(MAX_DATA_GAS_PER_BLOCK)); assert_eq!(receipt.blob_gas_price, Some(0x1)); // 1 wei } @@ -250,7 +250,7 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers() { assert_eq!(receipt.to, Some(bob)); assert_eq!( receipt.blob_gas_used.expect("Expected to be EIP-4844 transaction"), - DATA_GAS_PER_BLOB as u128 + DATA_GAS_PER_BLOB ); } @@ -296,6 +296,6 @@ async fn can_correctly_estimate_blob_gas_with_recommended_fillers_with_signer() assert_eq!(receipt.to, Some(bob)); assert_eq!( receipt.blob_gas_used.expect("Expected to be EIP-4844 transaction"), - DATA_GAS_PER_BLOB as u128 + DATA_GAS_PER_BLOB ); } diff --git a/crates/anvil/tests/it/eip7702.rs b/crates/anvil/tests/it/eip7702.rs index e10633d6c..dfc93bfe0 100644 --- a/crates/anvil/tests/it/eip7702.rs +++ b/crates/anvil/tests/it/eip7702.rs @@ -1,7 +1,7 @@ use crate::utils::http_provider; use alloy_consensus::{transaction::TxEip7702, SignableTransaction}; use alloy_network::{ReceiptResponse, TransactionBuilder, TxSignerSync}; -use alloy_primitives::bytes; +use alloy_primitives::{bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{Authorization, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -44,7 +44,7 @@ async fn can_send_eip7702_tx() { let contract = receipt.contract_address.unwrap(); let authorization = Authorization { - chain_id: 31337u64, + chain_id: U256::from(31337u64), address: contract, nonce: provider.get_transaction_count(from).await.unwrap(), }; diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 8e7736b0d..be91bbc12 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1503,7 +1503,9 @@ async fn test_fork_get_account() { assert_eq!( alice_acc.balance, - alice_bal - (U256::from(142) + U256::from(receipt.gas_used * receipt.effective_gas_price)), + alice_bal - + (U256::from(142) + + U256::from(receipt.gas_used as u128 * receipt.effective_gas_price)), ); assert_eq!(alice_acc.nonce, alice_nonce + 1); diff --git a/crates/anvil/tests/it/state.rs b/crates/anvil/tests/it/state.rs index f7736de2f..5337b36b5 100644 --- a/crates/anvil/tests/it/state.rs +++ b/crates/anvil/tests/it/state.rs @@ -2,7 +2,7 @@ use crate::abi::Greeter; use alloy_network::{ReceiptResponse, TransactionBuilder}; -use alloy_primitives::{address, utils::Unit, Bytes, Uint, U256}; +use alloy_primitives::{address, utils::Unit, Bytes, Uint, U256, U64}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -245,3 +245,34 @@ async fn test_fork_load_state() { assert_eq!(balance_alice + value, latest_balance_alice); } + +// +#[tokio::test(flavor = "multi_thread")] +async fn test_fork_load_state_with_greater_state_block() { + let (api, _handle) = spawn( + NodeConfig::test() + .with_eth_rpc_url(Some(next_http_rpc_endpoint())) + .with_fork_block_number(Some(21070682u64)), + ) + .await; + + api.mine_one().await; + + let block_number = api.block_number().unwrap(); + + let serialized_state = api.serialized_state(false).await.unwrap(); + + assert_eq!(serialized_state.best_block_number, Some(block_number.to::())); + + let (api, _handle) = spawn( + NodeConfig::test() + .with_eth_rpc_url(Some(next_http_rpc_endpoint())) + .with_fork_block_number(Some(21070682u64)) // Forked chain has moved forward + .with_init_state(Some(serialized_state)), + ) + .await; + + let new_block_number = api.block_number().unwrap(); + + assert_eq!(new_block_number, block_number); +} diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 47bf9a884..657fd0249 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -9,7 +9,7 @@ use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types::BlockId; use clap::{Parser, Subcommand, ValueHint}; use eyre::Result; -use foundry_cli::opts::{EtherscanOpts, GlobalOpts, RpcOpts}; +use foundry_cli::opts::{EtherscanOpts, GlobalArgs, RpcOpts}; use foundry_common::ens::NameOrAddress; use std::{path::PathBuf, str::FromStr}; @@ -31,9 +31,9 @@ const VERSION_MESSAGE: &str = concat!( next_display_order = None, )] pub struct Cast { - /// Include the global options. + /// Include the global arguments. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, #[command(subcommand)] pub cmd: CastSubcommand, @@ -606,7 +606,8 @@ pub enum CastSubcommand { formula_id: String, }, - /// Fetch the EIP-1967 implementation account + /// Fetch the EIP-1967 implementation for a contract + /// Can read from the implementation slot or the beacon slot. #[command(visible_alias = "impl")] Implementation { /// The block height to query at. @@ -615,7 +616,13 @@ pub enum CastSubcommand { #[arg(long, short = 'B')] block: Option, - /// The address to get the nonce for. + /// Fetch the implementation from the beacon slot. + /// + /// If not specified, the implementation slot is used. + #[arg(long)] + beacon: bool, + + /// The address for which the implementation will be fetched. #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, @@ -632,7 +639,7 @@ pub enum CastSubcommand { #[arg(long, short = 'B')] block: Option, - /// The address to get the nonce for. + /// The address from which the admin account will be fetched. #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 7121f1a98..4499fea1c 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -10,7 +10,7 @@ use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Cell, Table}; use eyre::Result; use foundry_block_explorers::Client; use foundry_cli::{ - opts::{CoreBuildArgs, EtherscanOpts, RpcOpts}, + opts::{BuildOpts, EtherscanOpts, RpcOpts}, utils, }; use foundry_common::{ @@ -64,7 +64,7 @@ pub struct StorageArgs { etherscan: EtherscanOpts, #[command(flatten)] - build: CoreBuildArgs, + build: BuildOpts, } impl_figment_convert_cast!(StorageArgs); diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 7960cab6e..4234304f2 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,6 +1,6 @@ use alloy_chains::Chain; use alloy_dyn_abi::TypedData; -use alloy_primitives::{hex, Address, PrimitiveSignature as Signature, B256}; +use alloy_primitives::{hex, Address, PrimitiveSignature as Signature, B256, U256}; use alloy_provider::Provider; use alloy_signer::Signer; use alloy_signer_local::{ @@ -380,7 +380,7 @@ impl WalletSubcommands { } else { provider.get_chain_id().await? }; - let auth = Authorization { chain_id, address, nonce }; + let auth = Authorization { chain_id: U256::from(chain_id), address, nonce }; let signature = wallet.sign_hash(&auth.signature_hash()).await?; let auth = auth.into_signed(signature); sh_println!("{}", hex::encode_prefixed(alloy_rlp::encode(&auth)))?; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index e7d94be87..749c31d5c 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -430,11 +430,11 @@ async fn main_args(args: CastArgs) -> Result<()> { let id = stdin::unwrap_line(id)?; sh_println!("{}", foundry_common::erc7201(&id))?; } - CastSubcommand::Implementation { block, who, rpc } => { + CastSubcommand::Implementation { block, beacon, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let who = who.resolve(&provider).await?; - sh_println!("{}", Cast::new(provider).implementation(who, block).await?)?; + sh_println!("{}", Cast::new(provider).implementation(who, beacon, block).await?)?; } CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 1eb0ee201..b54e7ed6d 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -3,7 +3,7 @@ use alloy_json_abi::Function; use alloy_network::{ AnyNetwork, TransactionBuilder, TransactionBuilder4844, TransactionBuilder7702, }; -use alloy_primitives::{hex, Address, Bytes, TxKind}; +use alloy_primitives::{hex, Address, Bytes, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{AccessList, Authorization, TransactionInput, TransactionRequest}; use alloy_serde::WithOtherFields; @@ -379,8 +379,11 @@ where let auth = match auth { CliAuthorizationList::Address(address) => { - let auth = - Authorization { chain_id: self.chain.id(), nonce: tx_nonce + 1, address }; + let auth = Authorization { + chain_id: U256::from(self.chain.id()), + nonce: tx_nonce + 1, + address, + }; let Some(signer) = sender.as_signer() else { eyre::bail!("No signer available to sign authorization"); diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8357f8164..eedeaa201 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -627,14 +627,33 @@ where /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; - /// let implementation = cast.implementation(addr, None).await?; + /// let implementation = cast.implementation(addr, false, None).await?; /// println!("{}", implementation); /// # Ok(()) /// # } /// ``` - pub async fn implementation(&self, who: Address, block: Option) -> Result { - let slot = - B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + pub async fn implementation( + &self, + who: Address, + is_beacon: bool, + block: Option, + ) -> Result { + let slot = match is_beacon { + true => { + // Use the beacon slot : bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1) + B256::from_str( + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + )? + } + false => { + // Use the implementation slot : + // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + B256::from_str( + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + )? + } + }; + let value = self .provider .get_storage_at(who, slot.into()) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 24ff9cea4..1a05c7e09 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,9 +1,10 @@ //! Contains various tests for checking cast commands use alloy_chains::NamedChain; -use alloy_network::TransactionResponse; -use alloy_primitives::{b256, B256}; -use alloy_rpc_types::{BlockNumberOrTag, Index}; +use alloy_network::{TransactionBuilder, TransactionResponse}; +use alloy_primitives::{address, b256, Bytes, B256}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::{BlockNumberOrTag, Index, TransactionRequest}; use anvil::{EthereumHardfork, NodeConfig}; use foundry_test_utils::{ casttest, file, forgetest, forgetest_async, @@ -105,7 +106,6 @@ totalDifficulty [..] blobGasUsed [..] excessBlobGas [..] requestsHash [..] -targetBlobsPerBlock [..] transactions: [ ... ] @@ -627,6 +627,46 @@ casttest!(rlp, |_prj, cmd| { "#]]); }); +// test that `cast impl` works correctly for both the implementation slot and the beacon slot +casttest!(impl_slot, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast impl` for the implementation slot (AAVE Proxy) + cmd.args([ + "impl", + "0x4965f6FA20fE9728deCf5165016fc338a5a85aBF", + "--rpc-url", + eth_rpc_url.as_str(), + "--block", + "21422087", + ]) + .assert_success() + .stdout_eq(str![[r#" +0xb61306c8eb34a2104d9eb8d84f1bb1001067fa4b + +"#]]); +}); + +casttest!(impl_slot_beacon, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast impl` for the beacon slot + cmd.args([ + "impl", + "0xc63d9f0040d35f328274312fc8771a986fc4ba86", + "--beacon", + "--rpc-url", + eth_rpc_url.as_str(), + "--block", + "21422087", + ]) + .assert_success() + .stdout_eq(str![[r#" +0xa748ae65ba11606492a9c57effa0d4b7be551ec2 + +"#]]); +}); + // test for cast_rpc without arguments casttest!(rpc_no_args, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); @@ -770,24 +810,24 @@ casttest!(receipt_revert_reason, |_prj, cmd| { .assert_success() .stdout_eq(str![[r#" -blockHash 0x2cfe65be49863676b6dbc04d58176a14f39b123f1e2f4fea0383a2d82c2c50d0 -blockNumber 16239315 -contractAddress -cumulativeGasUsed 10743428 -effectiveGasPrice 10539984136 -from 0x199D5ED7F45F4eE35960cF22EAde2076e95B253F -gasUsed 21000 -logs [] -logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -root -status 1 (success) -transactionHash 0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e -transactionIndex 116 -type 0 -blobGasPrice -blobGasUsed -authorizationList -to 0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59c +blockHash 0x2cfe65be49863676b6dbc04d58176a14f39b123f1e2f4fea0383a2d82c2c50d0 +blockNumber 16239315 +contractAddress +cumulativeGasUsed 10743428 +effectiveGasPrice 10539984136 +from 0x199D5ED7F45F4eE35960cF22EAde2076e95B253F +gasUsed 21000 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root +status 1 (success) +transactionHash 0x44f2aaa351460c074f2cb1e5a9e28cbc7d83f33e425101d2de14331c7b7ec31e +transactionIndex 116 +type 0 +blobGasPrice +blobGasUsed +authorizationList +to 0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59c "#]]); @@ -804,25 +844,25 @@ to 0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59c .assert_success() .stdout_eq(str![[r#" -blockHash 0x883f974b17ca7b28cb970798d1c80f4d4bb427473dc6d39b2a7fe24edc02902d -blockNumber 14839405 -contractAddress -cumulativeGasUsed 20273649 -effectiveGasPrice 21491736378 -from 0x3cF412d970474804623bb4e3a42dE13F9bCa5436 -gasUsed 24952 -logs [] -logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -root -status 0 (failed) -transactionHash 0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c -transactionIndex 173 -type 2 -blobGasPrice -blobGasUsed -authorizationList -to 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 -revertReason Transaction too old, data: "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000" +blockHash 0x883f974b17ca7b28cb970798d1c80f4d4bb427473dc6d39b2a7fe24edc02902d +blockNumber 14839405 +contractAddress +cumulativeGasUsed 20273649 +effectiveGasPrice 21491736378 +from 0x3cF412d970474804623bb4e3a42dE13F9bCa5436 +gasUsed 24952 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root +status 0 (failed) +transactionHash 0x0e07d8b53ed3d91314c80e53cf25bcde02084939395845cbb625b029d568135c +transactionIndex 173 +type 2 +blobGasPrice +blobGasUsed +authorizationList +to 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 +revertReason Transaction too old, data: "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000" "#]]); }); @@ -1996,3 +2036,37 @@ forgetest_async!(cast_call_custom_chain_id, |_prj, cmd| { ]) .assert_success(); }); + +// https://github.com/foundry-rs/foundry/issues/9541 +forgetest_async!(cast_run_impersonated_tx, |_prj, cmd| { + let (_api, handle) = anvil::spawn( + NodeConfig::test() + .with_auto_impersonate(true) + .with_eth_rpc_url(Some("https://sepolia.base.org")), + ) + .await; + + let http_endpoint = handle.http_endpoint(); + + let provider = ProviderBuilder::new().on_http(http_endpoint.parse().unwrap()); + + // send impersonated tx + let tx = TransactionRequest::default() + .with_from(address!("041563c07028Fc89106788185763Fc73028e8511")) + .with_to(address!("F38aA5909D89F5d98fCeA857e708F6a6033f6CF8")) + .with_input( + Bytes::from_str( + "0x60fe47b1000000000000000000000000000000000000000000000000000000000000000c", + ) + .unwrap(), + ); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert!(receipt.status()); + + // run impersonated tx + cmd.cast_fuse() + .args(["run", &receipt.transaction_hash.to_string(), "--rpc-url", &http_endpoint]) + .assert_success(); +}); diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 6bd4a67b3..24c0ede45 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4971,6 +4971,86 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "expectEmit_4", + "description": "Expect a given number of logs with the provided topics.", + "declaration": "function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, uint64 count) external;", + "visibility": "external", + "mutability": "", + "signature": "expectEmit(bool,bool,bool,bool,uint64)", + "selector": "0x5e1d1c33", + "selectorBytes": [ + 94, + 29, + 28, + 51 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "expectEmit_5", + "description": "Expect a given number of logs from a specific emitter with the provided topics.", + "declaration": "function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter, uint64 count) external;", + "visibility": "external", + "mutability": "", + "signature": "expectEmit(bool,bool,bool,bool,address,uint64)", + "selector": "0xc339d02c", + "selectorBytes": [ + 195, + 57, + 208, + 44 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "expectEmit_6", + "description": "Expect a given number of logs with all topic and data checks enabled.", + "declaration": "function expectEmit(uint64 count) external;", + "visibility": "external", + "mutability": "", + "signature": "expectEmit(uint64)", + "selector": "0x4c74a335", + "selectorBytes": [ + 76, + 116, + 163, + 53 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, + { + "func": { + "id": "expectEmit_7", + "description": "Expect a given number of logs from a specific emitter with all topic and data checks enabled.", + "declaration": "function expectEmit(address emitter, uint64 count) external;", + "visibility": "external", + "mutability": "", + "signature": "expectEmit(address,uint64)", + "selector": "0xb43aece3", + "selectorBytes": [ + 180, + 58, + 236, + 227 + ] + }, + "group": "testing", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "expectPartialRevert_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 36455cf79..e4ed952d0 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1004,6 +1004,23 @@ interface Vm { #[cheatcode(group = Testing, safety = Unsafe)] function expectEmit(address emitter) external; + /// Expect a given number of logs with the provided topics. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, uint64 count) external; + + /// Expect a given number of logs from a specific emitter with the provided topics. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter, uint64 count) + external; + + /// Expect a given number of logs with all topic and data checks enabled. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectEmit(uint64 count) external; + + /// Expect a given number of logs from a specific emitter with all topic and data checks enabled. + #[cheatcode(group = Testing, safety = Unsafe)] + function expectEmit(address emitter, uint64 count) external; + /// Prepare an expected anonymous log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData.). /// Call this function, then emit an anonymous event, then call a function. Internally after the call, we check if /// logs were emitted in the expected order with the expected topics and data (as specified by the booleans). diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 44d209f5a..90d49f3e6 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -5,7 +5,7 @@ use foundry_common::{fs::normalize_path, ContractsByArtifact}; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ cache::StorageCachingConfig, fs_permissions::FsAccessKind, Config, FsPermissions, - ResolvedRpcEndpoints, + ResolvedRpcEndpoint, ResolvedRpcEndpoints, RpcEndpoint, RpcEndpointUrl, }; use foundry_evm_core::opts::EvmOpts; use semver::Version; @@ -191,33 +191,28 @@ impl CheatsConfig { /// - Returns an error if `url_or_alias` is a known alias but references an unresolved env var. /// - Returns an error if `url_or_alias` is not an alias but does not start with a `http` or /// `ws` `scheme` and is not a path to an existing file - pub fn rpc_url(&self, url_or_alias: &str) -> Result { - match self.rpc_endpoints.get(url_or_alias) { - Some(Ok(url)) => Ok(url.clone()), - Some(Err(err)) => { - // try resolve again, by checking if env vars are now set - err.try_resolve().map_err(Into::into) - } - None => { - // check if it's a URL or a path to an existing file to an ipc socket - if url_or_alias.starts_with("http") || - url_or_alias.starts_with("ws") || - // check for existing ipc file - Path::new(url_or_alias).exists() - { - Ok(url_or_alias.into()) - } else { - Err(fmt_err!("invalid rpc url: {url_or_alias}")) - } + pub fn rpc_endpoint(&self, url_or_alias: &str) -> Result { + if let Some(endpoint) = self.rpc_endpoints.get(url_or_alias) { + Ok(endpoint.clone().try_resolve()) + } else { + // check if it's a URL or a path to an existing file to an ipc socket + if url_or_alias.starts_with("http") || + url_or_alias.starts_with("ws") || + // check for existing ipc file + Path::new(url_or_alias).exists() + { + let url = RpcEndpointUrl::Env(url_or_alias.to_string()); + Ok(RpcEndpoint::new(url).resolve()) + } else { + Err(fmt_err!("invalid rpc url: {url_or_alias}")) } } } - /// Returns all the RPC urls and their alias. pub fn rpc_urls(&self) -> Result> { let mut urls = Vec::with_capacity(self.rpc_endpoints.len()); for alias in self.rpc_endpoints.keys() { - let url = self.rpc_url(alias)?; + let url = self.rpc_endpoint(alias)?.url()?; urls.push(Rpc { key: alias.clone(), url }); } Ok(urls) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index f284a7374..d689af437 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -461,7 +461,10 @@ impl Cheatcode for blobBaseFeeCall { "`blobBaseFee` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - ccx.ecx.env.block.set_blob_excess_gas_and_price((*newBlobBaseFee).to()); + ccx.ecx.env.block.set_blob_excess_gas_and_price( + (*newBlobBaseFee).to(), + ccx.ecx.spec_id() >= SpecId::PRAGUE, + ); Ok(Default::default()) } } diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index e1db17a3d..e839d3b1a 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -232,7 +232,7 @@ impl Cheatcode for rpc_0Call { impl Cheatcode for rpc_1Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { urlOrAlias, method, params } = self; - let url = state.config.rpc_url(urlOrAlias)?; + let url = state.config.rpc_endpoint(urlOrAlias)?.url()?; rpc_call(&url, method, params) } } @@ -338,9 +338,15 @@ fn create_fork_request( ) -> Result { persist_caller(ccx); - let url = ccx.state.config.rpc_url(url_or_alias)?; + let rpc_endpoint = ccx.state.config.rpc_endpoint(url_or_alias)?; + let url = rpc_endpoint.url()?; let mut evm_opts = ccx.state.config.evm_opts.clone(); evm_opts.fork_block_number = block; + evm_opts.fork_retries = rpc_endpoint.config.retries; + evm_opts.fork_retry_backoff = rpc_endpoint.config.retry_backoff; + if let Some(Ok(auth)) = rpc_endpoint.auth { + evm_opts.fork_headers = Some(vec![format!("Authorization: {auth}")]); + } let fork = CreateFork { enable_caching: !ccx.state.config.no_storage_caching && ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f0a742b5e..d398ead52 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -10,6 +10,10 @@ use crate::{ strategy::CheatcodeInspectorStrategy, test::{ assume::AssumeNoRevert, + expect::{ + self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmitTracker, + ExpectedRevert, ExpectedRevertKind, + }, expect::{self, ExpectedEmit, ExpectedRevert, ExpectedRevertKind}, }, utils::IgnoredTraces, @@ -467,7 +471,7 @@ pub struct Cheatcodes { /// Expected calls pub expected_calls: ExpectedCallTracker, /// Expected emits - pub expected_emits: VecDeque, + pub expected_emits: ExpectedEmitTracker, /// Map of context depths to memory offset ranges that may be written to within the call depth. pub allowed_mem_writes: HashMap>>, @@ -1560,21 +1564,63 @@ impl Inspector<&mut dyn DatabaseExt> for Cheatcodes { let should_check_emits = self .expected_emits .iter() - .any(|expected| expected.depth == ecx.journaled_state.depth()) && + .any(|(expected, _)| expected.depth == ecx.journaled_state.depth()) && // Ignore staticcalls !call.is_static; if should_check_emits { + let expected_counts = self + .expected_emits + .iter() + .filter_map(|(expected, count_map)| { + let count = match expected.address { + Some(emitter) => match count_map.get(&emitter) { + Some(log_count) => expected + .log + .as_ref() + .map(|l| log_count.count(l)) + .unwrap_or_else(|| log_count.count_unchecked()), + None => 0, + }, + None => match &expected.log { + Some(log) => count_map.values().map(|logs| logs.count(log)).sum(), + None => count_map.values().map(|logs| logs.count_unchecked()).sum(), + }, + }; + + if count != expected.count { + Some((expected, count)) + } else { + None + } + }) + .collect::>(); + // Not all emits were matched. - if self.expected_emits.iter().any(|expected| !expected.found) { + if self.expected_emits.iter().any(|(expected, _)| !expected.found) { outcome.result.result = InstructionResult::Revert; outcome.result.output = "log != expected log".abi_encode().into(); return outcome; - } else { - // All emits were found, we're good. - // Clear the queue, as we expect the user to declare more events for the next call - // if they wanna match further events. - self.expected_emits.clear() } + + if !expected_counts.is_empty() { + let msg = if outcome.result.is_ok() { + let (expected, count) = expected_counts.first().unwrap(); + format!("log emitted {count} times, expected {}", expected.count) + } else { + "expected an emit, but the call reverted instead. \ + ensure you're testing the happy path when using `expectEmit`" + .to_string() + }; + + outcome.result.result = InstructionResult::Revert; + outcome.result.output = Error::encode(msg); + return outcome; + } + + // All emits were found, we're good. + // Clear the queue, as we expect the user to declare more events for the next call + // if they wanna match further events. + self.expected_emits.clear() } // this will ensure we don't have false positives when trying to diagnose reverts in fork @@ -1662,10 +1708,9 @@ impl Inspector<&mut dyn DatabaseExt> for Cheatcodes { } } } - // Check if we have any leftover expected emits // First, if any emits were found at the root call, then we its ok and we remove them. - self.expected_emits.retain(|expected| !expected.found); + self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found); // If not empty, we got mismatched emits if !self.expected_emits.is_empty() { let msg = if outcome.result.is_ok() { diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index b28141ae0..0749d0d41 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -40,7 +40,7 @@ impl Cheatcode for attachDelegationCall { let auth = Authorization { address: *implementation, nonce: *nonce, - chain_id: ccx.ecx.env.cfg.chain_id, + chain_id: U256::from(ccx.ecx.env.cfg.chain_id), }; let signed_auth = SignedAuthorization::new_unchecked( auth, @@ -87,7 +87,11 @@ fn create_auth( let authority_acc = ccx.ecx.journaled_state.load_account(authority, &mut ccx.ecx.db)?; let nonce = authority_acc.data.info.nonce; Ok(( - Authorization { address: implementation, nonce, chain_id: ccx.ecx.env.cfg.chain_id }, + Authorization { + address: implementation, + nonce, + chain_id: U256::from(ccx.ecx.env.cfg.chain_id), + }, nonce, )) } diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index ad62f1410..ca85377f9 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -82,7 +82,8 @@ impl Cheatcode for getFoundryVersionCall { impl Cheatcode for rpcUrlCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { rpcAlias } = self; - state.config.rpc_url(rpcAlias).map(|url| url.abi_encode()) + let url = state.config.rpc_endpoint(rpcAlias)?.url()?.abi_encode(); + Ok(url) } } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 3671317a6..4820128cb 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,6 +1,11 @@ +use std::collections::VecDeque; + use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Error, Result, Vm::*}; use alloy_primitives::{ address, hex, map::hash_map::Entry, Address, Bytes, LogData as RawLog, U256, + address, hex, + map::{hash_map::Entry, AddressHashMap, HashMap}, + Address, Bytes, LogData as RawLog, U256, }; use alloy_sol_types::{SolError, SolValue}; use foundry_cheatcodes_common::expect::{ExpectedCallData, ExpectedCallType}; @@ -75,6 +80,8 @@ pub struct ExpectedEmit { pub anonymous: bool, /// Whether the log was actually found in the subcalls pub found: bool, + /// Number of times the log is expected to be emitted + pub count: u64, } impl Cheatcode for expectCall_0Call { @@ -187,6 +194,7 @@ impl Cheatcode for expectEmit_0Call { [true, checkTopic1, checkTopic2, checkTopic3, checkData], None, false, + 1, ) } } @@ -200,6 +208,7 @@ impl Cheatcode for expectEmit_1Call { [true, checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), false, + 1, ) } } @@ -207,14 +216,63 @@ impl Cheatcode for expectEmit_1Call { impl Cheatcode for expectEmit_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, 1) } } impl Cheatcode for expectEmit_3Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), false, 1) + } +} + +impl Cheatcode for expectEmit_4Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { checkTopic1, checkTopic2, checkTopic3, checkData, count } = *self; + expect_emit( + ccx.state, + ccx.ecx.journaled_state.depth(), + [true, checkTopic1, checkTopic2, checkTopic3, checkData], + None, + false, + count, + ) + } +} + +impl Cheatcode for expectEmit_5Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter, count } = *self; + expect_emit( + ccx.state, + ccx.ecx.journaled_state.depth(), + [true, checkTopic1, checkTopic2, checkTopic3, checkData], + Some(emitter), + false, + count, + ) + } +} + +impl Cheatcode for expectEmit_6Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { count } = *self; + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, false, count) + } +} + +impl Cheatcode for expectEmit_7Call { + fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { emitter, count } = *self; + expect_emit( + ccx.state, + ccx.ecx.journaled_state.depth(), + [true; 5], + Some(emitter), + false, + count, + ) } } @@ -227,6 +285,7 @@ impl Cheatcode for expectEmitAnonymous_0Call { [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData], None, true, + 1, ) } } @@ -240,6 +299,7 @@ impl Cheatcode for expectEmitAnonymous_1Call { [checkTopic0, checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), true, + 1, ) } } @@ -247,14 +307,14 @@ impl Cheatcode for expectEmitAnonymous_1Call { impl Cheatcode for expectEmitAnonymous_2Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], None, true, 1) } } impl Cheatcode for expectEmitAnonymous_3Call { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; - expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 5], Some(emitter), true, 1) } } @@ -600,15 +660,17 @@ fn expect_emit( checks: [bool; 5], address: Option
, anonymous: bool, + count: u64, ) -> Result { - let expected_emit = ExpectedEmit { depth, checks, address, found: false, log: None, anonymous }; - if let Some(found_emit_pos) = state.expected_emits.iter().position(|emit| emit.found) { + let expected_emit = + ExpectedEmit { depth, checks, address, found: false, log: None, anonymous, count }; + if let Some(found_emit_pos) = state.expected_emits.iter().position(|(emit, _)| emit.found) { // The order of emits already found (back of queue) should not be modified, hence push any // new emit before first found emit. - state.expected_emits.insert(found_emit_pos, expected_emit); + state.expected_emits.insert(found_emit_pos, (expected_emit, Default::default())); } else { // If no expected emits then push new one at the back of queue. - state.expected_emits.push_back(expected_emit); + state.expected_emits.push_back((expected_emit, Default::default())); } Ok(Default::default()) @@ -629,18 +691,18 @@ pub fn handle_expect_emit( // First, we can return early if all events have been matched. // This allows a contract to arbitrarily emit more events than expected (additive behavior), // as long as all the previous events were matched in the order they were expected to be. - if state.expected_emits.iter().all(|expected| expected.found) { + if state.expected_emits.iter().all(|(expected, _)| expected.found) { return } - let should_fill_logs = state.expected_emits.iter().any(|expected| expected.log.is_none()); + let should_fill_logs = state.expected_emits.iter().any(|(expected, _)| expected.log.is_none()); let index_to_fill_or_check = if should_fill_logs { // If there's anything to fill, we start with the last event to match in the queue // (without taking into account events already matched). state .expected_emits .iter() - .position(|emit| emit.found) + .position(|(emit, _)| emit.found) .unwrap_or(state.expected_emits.len()) .saturating_sub(1) } else { @@ -649,7 +711,7 @@ pub fn handle_expect_emit( 0 }; - let mut event_to_fill_or_check = state + let (mut event_to_fill_or_check, mut count_map) = state .expected_emits .remove(index_to_fill_or_check) .expect("we should have an emit to fill or check"); @@ -660,7 +722,9 @@ pub fn handle_expect_emit( if event_to_fill_or_check.anonymous || !log.topics().is_empty() { event_to_fill_or_check.log = Some(log.data.clone()); // If we only filled the expected log then we put it back at the same position. - state.expected_emits.insert(index_to_fill_or_check, event_to_fill_or_check); + state + .expected_emits + .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map)); } else { interpreter.instruction_result = InstructionResult::Revert; interpreter.next_action = InterpreterAction::Return { @@ -674,41 +738,120 @@ pub fn handle_expect_emit( return }; - event_to_fill_or_check.found = || -> bool { - // Topic count must match. - if expected.topics().len() != log.topics().len() { - return false + // Increment/set `count` for `log.address` and `log.data` + match count_map.entry(log.address) { + Entry::Occupied(mut entry) => { + // Checks and inserts the log into the map. + // If the log doesn't pass the checks, it is ignored and `count` is not incremented. + let log_count_map = entry.get_mut(); + log_count_map.insert(&log.data); } - // Match topics according to the checks. - if !log - .topics() - .iter() - .enumerate() - .filter(|(i, _)| event_to_fill_or_check.checks[*i]) - .all(|(i, topic)| topic == &expected.topics()[i]) - { + Entry::Vacant(entry) => { + let mut log_count_map = LogCountMap::new(&event_to_fill_or_check); + + if log_count_map.satisfies_checks(&log.data) { + log_count_map.insert(&log.data); + + // Entry is only inserted if it satisfies the checks. + entry.insert(log_count_map); + } + } + } + + event_to_fill_or_check.found = || -> bool { + if !checks_topics_and_data(event_to_fill_or_check.checks, expected, log) { return false } + // Maybe match source address. if event_to_fill_or_check.address.is_some_and(|addr| addr != log.address) { return false; } - // Maybe match data. - if event_to_fill_or_check.checks[4] && expected.data.as_ref() != log.data.data.as_ref() { - return false - } - true + let expected_count = event_to_fill_or_check.count; + + match event_to_fill_or_check.address { + Some(emitter) => count_map + .get(&emitter) + .is_some_and(|log_map| log_map.count(&log.data) >= expected_count), + None => count_map + .values() + .find(|log_map| log_map.satisfies_checks(&log.data)) + .is_some_and(|map| map.count(&log.data) >= expected_count), + } }(); // If we found the event, we can push it to the back of the queue // and begin expecting the next event. if event_to_fill_or_check.found { - state.expected_emits.push_back(event_to_fill_or_check); + state.expected_emits.push_back((event_to_fill_or_check, count_map)); } else { // We did not match this event, so we need to keep waiting for the right one to // appear. - state.expected_emits.push_front(event_to_fill_or_check); + state.expected_emits.push_front((event_to_fill_or_check, count_map)); + } +} + +/// Handles expected emits specified by the `expectEmit` cheatcodes. +/// +/// The second element of the tuple counts the number of times the log has been emitted by a +/// particular address +pub type ExpectedEmitTracker = VecDeque<(ExpectedEmit, AddressHashMap)>; + +#[derive(Clone, Debug, Default)] +pub struct LogCountMap { + checks: [bool; 5], + expected_log: RawLog, + map: HashMap, +} + +impl LogCountMap { + /// Instantiates `LogCountMap`. + fn new(expected_emit: &ExpectedEmit) -> Self { + Self { + checks: expected_emit.checks, + expected_log: expected_emit.log.clone().expect("log should be filled here"), + map: Default::default(), + } + } + + /// Inserts a log into the map and increments the count. + /// + /// The log must pass all checks against the expected log for the count to increment. + /// + /// Returns true if the log was inserted and count was incremented. + fn insert(&mut self, log: &RawLog) -> bool { + // If its already in the map, increment the count without checking. + if self.map.contains_key(log) { + self.map.entry(log.clone()).and_modify(|c| *c += 1); + + return true + } + + if !self.satisfies_checks(log) { + return false + } + + self.map.entry(log.clone()).and_modify(|c| *c += 1).or_insert(1); + + true + } + + /// Checks the incoming raw log against the expected logs topics and data. + fn satisfies_checks(&self, log: &RawLog) -> bool { + checks_topics_and_data(self.checks, &self.expected_log, log) + } + + pub fn count(&self, log: &RawLog) -> u64 { + if !self.satisfies_checks(log) { + return 0 + } + + self.count_unchecked() + } + + pub fn count_unchecked(&self) -> u64 { + self.map.values().sum() } } @@ -872,6 +1015,30 @@ pub fn handle_expect_revert( } } +fn checks_topics_and_data(checks: [bool; 5], expected: &RawLog, log: &RawLog) -> bool { + if log.topics().len() != expected.topics().len() { + return false + } + + // Check topics. + if !log + .topics() + .iter() + .enumerate() + .filter(|(i, _)| checks[*i]) + .all(|(i, topic)| topic == &expected.topics()[i]) + { + return false + } + + // Check data + if checks[4] && expected.data.as_ref() != log.data.as_ref() { + return false + } + + true +} + fn expect_safe_memory(state: &mut Cheatcodes, start: u64, end: u64, depth: u64) -> Result { ensure!(start < end, "memory range start ({start}) is greater than end ({end})"); #[allow(clippy::single_range_in_vec_init)] // Wanted behaviour diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index ca3fc1ff5..797da8a12 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -11,7 +11,7 @@ use clap::{Parser, Subcommand}; use eyre::Context; use foundry_cli::{ handler, - opts::{CoreBuildArgs, GlobalOpts}, + opts::{BuildOpts, GlobalArgs}, utils::{self, LoadConfig}, }; use foundry_common::{evm::EvmArgs, fs}; @@ -35,7 +35,7 @@ extern crate foundry_common; static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(Chisel, opts, evm_args); +foundry_config::merge_impl_figment_convert!(Chisel, build, evm); const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -50,9 +50,9 @@ const VERSION_MESSAGE: &str = concat!( #[derive(Debug, Parser)] #[command(name = "chisel", version = VERSION_MESSAGE)] pub struct Chisel { - /// Include the global options. + /// Include the global arguments. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, #[command(subcommand)] pub cmd: Option, @@ -73,10 +73,10 @@ pub struct Chisel { pub no_vm: bool, #[command(flatten)] - pub opts: CoreBuildArgs, + pub build: BuildOpts, #[command(flatten)] - pub evm_args: EvmArgs, + pub evm: EvmArgs, } /// Chisel binary subcommands diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 2a6a2fc3f..a445e99e7 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -13,7 +13,7 @@ use crate::{ use alloy_json_abi::{InternalType, JsonAbi}; use alloy_primitives::{hex, Address}; use forge_fmt::FormatterConfig; -use foundry_config::{Config, RpcEndpoint}; +use foundry_config::{Config, RpcEndpointUrl}; use foundry_evm::{ decode::decode_console_logs, traces::{ @@ -357,9 +357,9 @@ impl ChiselDispatcher { { endpoint.clone() } else { - RpcEndpoint::Env(arg.to_string()).into() + RpcEndpointUrl::Env(arg.to_string()).into() }; - let fork_url = match endpoint.resolve() { + let fork_url = match endpoint.resolve().url() { Ok(fork_url) => fork_url, Err(e) => { return DispatchResult::CommandFailed(Self::make_error(format!( diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index 465b7b535..c9df0c357 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -15,7 +15,7 @@ use rustyline::{ Helper, }; use solar_parse::{ - interface::{Pos, Session, SessionGlobals}, + interface::{Session, SessionGlobals}, token::{Token, TokenKind}, Lexer, }; diff --git a/crates/cli/src/handler.rs b/crates/cli/src/handler.rs index 37a8c7051..49c46d83f 100644 --- a/crates/cli/src/handler.rs +++ b/crates/cli/src/handler.rs @@ -10,15 +10,16 @@ impl EyreHandler for Handler { if f.alternate() { return fmt::Debug::fmt(error, f) } + let errors = foundry_common::errors::dedup_chain(error); + + let (error, sources) = errors.split_first().unwrap(); write!(f, "{error}")?; - if let Some(cause) = error.source() { + if !sources.is_empty() { write!(f, "\n\nContext:")?; - let multiple = cause.source().is_some(); - let errors = std::iter::successors(Some(cause), |e| (*e).source()); - - for (n, error) in errors.enumerate() { + let multiple = sources.len() > 1; + for (n, error) in sources.iter().enumerate() { writeln!(f)?; if multiple { write!(f, "- Error #{n}: {error}")?; diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 884fe8598..f69fee01f 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -1,5 +1,5 @@ -use super::ProjectPathsArgs; -use crate::{opts::CompilerArgs, utils::LoadConfig}; +use super::ProjectPathOpts; +use crate::{opts::CompilerOpts, utils::LoadConfig}; use clap::{Parser, ValueHint}; use eyre::Result; use foundry_compilers::{ @@ -23,7 +23,7 @@ use std::path::PathBuf; #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Build options")] -pub struct CoreBuildArgs { +pub struct BuildOpts { /// Clear the cache and artifacts folder and recompile. #[arg(long, help_heading = "Cache options")] #[serde(skip)] @@ -138,14 +138,14 @@ pub struct CoreBuildArgs { #[command(flatten)] #[serde(flatten)] - pub compiler: CompilerArgs, + pub compiler: CompilerOpts, #[command(flatten)] #[serde(flatten)] - pub project_paths: ProjectPathsArgs, + pub project_paths: ProjectPathOpts, } -impl CoreBuildArgs { +impl BuildOpts { /// Returns the `Project` for the current workspace /// /// This loads the `foundry_config::Config` for the current workspace (see @@ -164,8 +164,8 @@ impl CoreBuildArgs { } // Loads project's figment and merges the build cli arguments into it -impl<'a> From<&'a CoreBuildArgs> for Figment { - fn from(args: &'a CoreBuildArgs) -> Self { +impl<'a> From<&'a BuildOpts> for Figment { + fn from(args: &'a BuildOpts) -> Self { let mut figment = if let Some(ref config_path) = args.project_paths.config_path { if !config_path.exists() { panic!("error: config-path `{}` does not exist", config_path.display()) @@ -203,8 +203,8 @@ impl<'a> From<&'a CoreBuildArgs> for Figment { } } -impl<'a> From<&'a CoreBuildArgs> for Config { - fn from(args: &'a CoreBuildArgs) -> Self { +impl<'a> From<&'a BuildOpts> for Config { + fn from(args: &'a BuildOpts) -> Self { let figment: Figment = args.into(); let mut config = Self::from_provider(figment).sanitized(); // if `--config-path` is set we need to adjust the config's root path to the actual root @@ -216,7 +216,7 @@ impl<'a> From<&'a CoreBuildArgs> for Config { } } -impl Provider for CoreBuildArgs { +impl Provider for BuildOpts { fn metadata(&self) -> Metadata { Metadata::named("Core Build Args Provider") } diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index 16cbb4990..506bc2b0a 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -3,10 +3,10 @@ use foundry_compilers::artifacts::{output_selection::ContractOutputSelection, Ev use serde::Serialize; mod core; -pub use self::core::CoreBuildArgs; +pub use self::core::BuildOpts; mod paths; -pub use self::paths::ProjectPathsArgs; +pub use self::paths::ProjectPathOpts; mod zksync; pub use self::zksync::ZkSyncArgs; @@ -17,7 +17,7 @@ pub use self::zksync::ZkSyncArgs; // See also `BuildArgs`. #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Compiler options")] -pub struct CompilerArgs { +pub struct CompilerOpts { /// Includes the AST as JSON in the compiler output. #[arg(long, help_heading = "Compiler options")] #[serde(skip)] @@ -69,15 +69,15 @@ mod tests { #[test] fn can_parse_evm_version() { - let args: CompilerArgs = - CompilerArgs::parse_from(["foundry-cli", "--evm-version", "london"]); + let args: CompilerOpts = + CompilerOpts::parse_from(["foundry-cli", "--evm-version", "london"]); assert_eq!(args.evm_version, Some(EvmVersion::London)); } #[test] fn can_parse_extra_output() { - let args: CompilerArgs = - CompilerArgs::parse_from(["foundry-cli", "--extra-output", "metadata", "ir-optimized"]); + let args: CompilerOpts = + CompilerOpts::parse_from(["foundry-cli", "--extra-output", "metadata", "ir-optimized"]); assert_eq!( args.extra_output, vec![ContractOutputSelection::Metadata, ContractOutputSelection::IrOptimized] @@ -86,7 +86,7 @@ mod tests { #[test] fn can_parse_extra_output_files() { - let args: CompilerArgs = CompilerArgs::parse_from([ + let args: CompilerOpts = CompilerOpts::parse_from([ "foundry-cli", "--extra-output-files", "metadata", diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index aa070800a..7a4d83eea 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -16,7 +16,7 @@ use std::path::PathBuf; /// Common arguments for a project's paths. #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Project options")] -pub struct ProjectPathsArgs { +pub struct ProjectPathOpts { /// The project's root path. /// /// By default root of the Git repository, if in one, @@ -63,7 +63,7 @@ pub struct ProjectPathsArgs { pub config_path: Option, } -impl ProjectPathsArgs { +impl ProjectPathOpts { /// Returns the root directory to use for configuring the project. /// /// This will be the `--root` argument if provided, otherwise see [`find_project_root`]. @@ -87,10 +87,10 @@ impl ProjectPathsArgs { } } -foundry_config::impl_figment_convert!(ProjectPathsArgs); +foundry_config::impl_figment_convert!(ProjectPathOpts); // Make this args a `figment::Provider` so that it can be merged into the `Config` -impl Provider for ProjectPathsArgs { +impl Provider for ProjectPathOpts { fn metadata(&self) -> Metadata { Metadata::named("Project Paths Args Provider") } diff --git a/crates/cli/src/opts/global.rs b/crates/cli/src/opts/global.rs index 74ed15a65..6dc34067f 100644 --- a/crates/cli/src/opts/global.rs +++ b/crates/cli/src/opts/global.rs @@ -2,9 +2,9 @@ use clap::{ArgAction, Parser}; use foundry_common::shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbosity}; use serde::{Deserialize, Serialize}; -/// Global options. +/// Global arguments for the CLI. #[derive(Clone, Debug, Default, Serialize, Deserialize, Parser)] -pub struct GlobalOpts { +pub struct GlobalArgs { /// Verbosity level of the log messages. /// /// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv). @@ -36,7 +36,7 @@ pub struct GlobalOpts { threads: Option, } -impl GlobalOpts { +impl GlobalArgs { /// Initialize the global options. pub fn init(&self) -> eyre::Result<()> { // Set the global shell. diff --git a/crates/cli/src/opts/mod.rs b/crates/cli/src/opts/mod.rs index 4e5d35572..3b6b914c1 100644 --- a/crates/cli/src/opts/mod.rs +++ b/crates/cli/src/opts/mod.rs @@ -1,13 +1,13 @@ mod build; mod chain; mod dependency; -mod ethereum; mod global; +mod rpc; mod transaction; pub use build::*; pub use chain::*; pub use dependency::*; -pub use ethereum::*; pub use global::*; +pub use rpc::*; pub use transaction::*; diff --git a/crates/cli/src/opts/ethereum.rs b/crates/cli/src/opts/rpc.rs similarity index 99% rename from crates/cli/src/opts/ethereum.rs rename to crates/cli/src/opts/rpc.rs index 8d2601be1..344efe73e 100644 --- a/crates/cli/src/opts/ethereum.rs +++ b/crates/cli/src/opts/rpc.rs @@ -18,7 +18,7 @@ const FLASHBOTS_URL: &str = "https://rpc.flashbots.net/fast"; #[derive(Clone, Debug, Default, Parser)] pub struct RpcOpts { - /// The RPC endpoint. + /// The RPC endpoint, default value is http://localhost:8545. #[arg(short = 'r', long = "rpc-url", env = "ETH_RPC_URL")] pub url: Option, diff --git a/crates/cli/src/utils/abi.rs b/crates/cli/src/utils/abi.rs index e903804de..301200f70 100644 --- a/crates/cli/src/utils/abi.rs +++ b/crates/cli/src/utils/abi.rs @@ -51,7 +51,7 @@ pub async fn parse_function_args { format!( " -hash {} -type {} +hash {} +type {} {} ", tx.hash.pretty(), @@ -813,7 +811,6 @@ pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) TxEnvelope::Eip4844(tx) => Some(tx.signature()), TxEnvelope::Eip7702(tx) => Some(tx.signature()), TxEnvelope::Legacy(tx) => Some(tx.signature()), - _ => None, }, _ => None, }; @@ -901,7 +898,6 @@ fn pretty_block_basics(block: &Block>) excess_blob_gas, parent_beacon_block_root, requests_hash, - target_blobs_per_block, }, }, uncles: _, @@ -933,8 +929,7 @@ withdrawalsRoot {} totalDifficulty {} blobGasUsed {} excessBlobGas {} -requestsHash {} -targetBlobsPerBlock {}", +requestsHash {}", base_fee_per_gas.pretty(), difficulty.pretty(), extra_data.pretty(), @@ -962,7 +957,6 @@ targetBlobsPerBlock {}", blob_gas_used.pretty(), excess_blob_gas.pretty(), requests_hash.pretty(), - target_blobs_per_block.pretty(), ) } @@ -1415,4 +1409,63 @@ value 0".to_string(); assert_eq!(Some("1424182926".to_string()), get_pretty_block_attr(&block, "timestamp")); assert_eq!(Some("163591".to_string()), get_pretty_block_attr(&block, "totalDifficulty")); } + + #[test] + fn test_receipt_other_fields_alignment() { + let receipt_json = serde_json::json!( + { + "status": "0x1", + "cumulativeGasUsed": "0x74e483", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x91181b0dca3b29aa136eeb2f536be5ce7b0aebc949be1c44b5509093c516097d", + "transactionIndex": "0x10", + "blockHash": "0x54bafb12e8cea9bb355fbf03a4ac49e42a2a1a80fa6cf4364b342e2de6432b5d", + "blockNumber": "0x7b1ab93", + "gasUsed": "0xc222", + "effectiveGasPrice": "0x18961", + "from": "0x2d815240a61731c75fa01b2793e1d3ed09f289d0", + "to": "0x4200000000000000000000000000000000000000", + "contractAddress": null, + "l1BaseFeeScalar": "0x146b", + "l1BlobBaseFee": "0x6a83078", + "l1BlobBaseFeeScalar": "0xf79c5", + "l1Fee": "0x51a9af7fd3", + "l1GasPrice": "0x972fe4acc", + "l1GasUsed": "0x640" + }); + + let receipt: AnyTransactionReceipt = serde_json::from_value(receipt_json).unwrap(); + let formatted = receipt.pretty(); + + let expected = r#" +blockHash 0x54bafb12e8cea9bb355fbf03a4ac49e42a2a1a80fa6cf4364b342e2de6432b5d +blockNumber 129084307 +contractAddress +cumulativeGasUsed 7660675 +effectiveGasPrice 100705 +from 0x2D815240A61731c75Fa01b2793E1D3eD09F289d0 +gasUsed 49698 +logs [] +logsBloom 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +root +status 1 (success) +transactionHash 0x91181b0dca3b29aa136eeb2f536be5ce7b0aebc949be1c44b5509093c516097d +transactionIndex 16 +type 2 +blobGasPrice +blobGasUsed +authorizationList +to 0x4200000000000000000000000000000000000000 +l1BaseFeeScalar 5227 +l1BlobBaseFee 111685752 +l1BlobBaseFeeScalar 1014213 +l1Fee 350739202003 +l1GasPrice 40583973580 +l1GasUsed 1600 +"#; + + assert_eq!(formatted.trim(), expected.trim()); + } } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index 4ff3eb8d7..c67131585 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -1,6 +1,8 @@ //! Commonly used constants. -use alloy_primitives::{address, Address}; +use alloy_consensus::Typed2718; +use alloy_network::AnyTxEnvelope; +use alloy_primitives::{address, Address, PrimitiveSignature, B256}; use std::time::Duration; /// The dev chain-id, inherited from hardhat @@ -53,6 +55,25 @@ pub fn is_known_system_sender(sender: Address) -> bool { [ARBITRUM_SENDER, OPTIMISM_SYSTEM_ADDRESS].contains(&sender) } +pub fn is_impersonated_tx(tx: &AnyTxEnvelope) -> bool { + if let AnyTxEnvelope::Ethereum(tx) = tx { + return is_impersonated_sig(tx.signature(), tx.ty()); + } + false +} + +pub fn is_impersonated_sig(sig: &PrimitiveSignature, ty: u8) -> bool { + let impersonated_sig = PrimitiveSignature::from_scalars_and_parity( + B256::with_last_byte(1), + B256::with_last_byte(1), + false, + ); + if ty != SYSTEM_TRANSACTION_TYPE && sig == &impersonated_sig { + return true; + } + false +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/common/src/errors/mod.rs b/crates/common/src/errors/mod.rs index c8b2c6bcc..5ecd1dcc0 100644 --- a/crates/common/src/errors/mod.rs +++ b/crates/common/src/errors/mod.rs @@ -6,15 +6,41 @@ pub use fs::FsPathError; mod artifacts; pub use artifacts::*; +mod private { + use eyre::Chain; + use std::error::Error; + + pub trait ErrorChain { + fn chain(&self) -> Chain<'_>; + } + + impl ErrorChain for dyn Error + 'static { + fn chain(&self) -> Chain<'_> { + Chain::new(self) + } + } + + impl ErrorChain for eyre::Report { + fn chain(&self) -> Chain<'_> { + self.chain() + } + } +} + /// Displays a chain of errors in a single line. -pub fn display_chain(error: &eyre::Report) -> String { +pub fn display_chain(error: &E) -> String { + dedup_chain(error).join("; ") +} + +/// Deduplicates a chain of errors. +pub fn dedup_chain(error: &E) -> Vec { let mut causes = all_sources(error); // Deduplicate the common pattern `msg1: msg2; msg2` -> `msg1: msg2`. causes.dedup_by(|b, a| a.contains(b.as_str())); - causes.join("; ") + causes } -fn all_sources(err: &eyre::Report) -> Vec { +fn all_sources(err: &E) -> Vec { err.chain().map(|cause| cause.to_string().trim().to_string()).collect() } diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 450bdd85f..46a09b62d 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -248,6 +248,12 @@ impl ProviderBuilder { self } + /// Sets http headers. If `None`, defaults to the already-set value. + pub fn maybe_headers(mut self, headers: Option>) -> Self { + self.headers = headers.unwrap_or(self.headers); + self + } + /// Constructs the `RetryProvider` taking all configs into account. pub fn build(self) -> Result { let Self { diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index b725fc068..9148cd6d9 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -88,7 +88,7 @@ impl UIfmt for TransactionReceiptWithRevertReason { if let Some(revert_reason) = &self.revert_reason { format!( "{} -revertReason {}", +revertReason {}", self.receipt.pretty(), revert_reason ) diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index b02c9904a..160cbfd0e 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -21,7 +21,7 @@ alloy-chains = { workspace = true, features = ["serde"] } alloy-primitives = { workspace = true, features = ["serde"] } revm-primitives.workspace = true -solang-parser.workspace = true +solar-parse.workspace = true dirs-next = "2" dunce.workspace = true diff --git a/crates/config/README.md b/crates/config/README.md index 960461eca..8c3837be5 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -159,7 +159,7 @@ use_literal_content = false # use ipfs method to generate the metadata hash, solc's default. # To not include the metadata hash, to allow for deterministic code: https://docs.soliditylang.org/en/latest/metadata.html, use "none" bytecode_hash = "ipfs" -# Whether to append the metadata hash to the bytecode +# Whether to append the CBOR-encoded metadata file. cbor_metadata = true # How to treat revert (and require) reason strings. # Possible values are: "default", "strip", "debug" and "verboseDebug". diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index 78cda4f73..1758e6a48 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -12,7 +12,7 @@ use std::{ #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct RpcEndpoints { - endpoints: BTreeMap, + endpoints: BTreeMap, } impl RpcEndpoints { @@ -24,9 +24,7 @@ impl RpcEndpoints { endpoints: endpoints .into_iter() .map(|(name, e)| match e.into() { - RpcEndpointType::String(url) => { - (name.into(), RpcEndpointConfig { endpoint: url, ..Default::default() }) - } + RpcEndpointType::String(url) => (name.into(), RpcEndpoint::new(url)), RpcEndpointType::Config(config) => (name.into(), config), }) .collect(), @@ -38,29 +36,16 @@ impl RpcEndpoints { self.endpoints.is_empty() } - /// Returns all (alias -> url) pairs + /// Returns all (alias -> rpc_endpoint) pairs pub fn resolved(self) -> ResolvedRpcEndpoints { ResolvedRpcEndpoints { - endpoints: self - .endpoints - .clone() - .into_iter() - .map(|(name, e)| (name, e.resolve())) - .collect(), - auths: self - .endpoints - .into_iter() - .map(|(name, e)| match e.auth { - Some(auth) => (name, auth.resolve().map(Some)), - None => (name, Ok(None)), - }) - .collect(), + endpoints: self.endpoints.into_iter().map(|(name, e)| (name, e.resolve())).collect(), } } } impl Deref for RpcEndpoints { - type Target = BTreeMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.endpoints @@ -72,14 +57,14 @@ impl Deref for RpcEndpoints { #[serde(untagged)] pub enum RpcEndpointType { /// Raw Endpoint url string - String(RpcEndpoint), + String(RpcEndpointUrl), /// Config object - Config(RpcEndpointConfig), + Config(RpcEndpoint), } impl RpcEndpointType { /// Returns the string variant - pub fn as_endpoint_string(&self) -> Option<&RpcEndpoint> { + pub fn as_endpoint_string(&self) -> Option<&RpcEndpointUrl> { match self { Self::String(url) => Some(url), Self::Config(_) => None, @@ -87,7 +72,7 @@ impl RpcEndpointType { } /// Returns the config variant - pub fn as_endpoint_config(&self) -> Option<&RpcEndpointConfig> { + pub fn as_endpoint_config(&self) -> Option<&RpcEndpoint> { match self { Self::Config(config) => Some(config), Self::String(_) => None, @@ -134,7 +119,7 @@ impl TryFrom for String { /// value of the env var itself. /// In other words, this type does not resolve env vars when it's being deserialized #[derive(Clone, Debug, PartialEq, Eq)] -pub enum RpcEndpoint { +pub enum RpcEndpointUrl { /// A raw Url (ws, http) Url(String), /// An endpoint that contains at least one `${ENV_VAR}` placeholder @@ -143,7 +128,7 @@ pub enum RpcEndpoint { Env(String), } -impl RpcEndpoint { +impl RpcEndpointUrl { /// Returns the url variant pub fn as_url(&self) -> Option<&str> { match self { @@ -173,7 +158,7 @@ impl RpcEndpoint { } } -impl fmt::Display for RpcEndpoint { +impl fmt::Display for RpcEndpointUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Url(url) => url.fmt(f), @@ -182,15 +167,15 @@ impl fmt::Display for RpcEndpoint { } } -impl TryFrom for String { +impl TryFrom for String { type Error = UnresolvedEnvVarError; - fn try_from(value: RpcEndpoint) -> Result { + fn try_from(value: RpcEndpointUrl) -> Result { value.resolve() } } -impl Serialize for RpcEndpoint { +impl Serialize for RpcEndpointUrl { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -199,7 +184,7 @@ impl Serialize for RpcEndpoint { } } -impl<'de> Deserialize<'de> for RpcEndpoint { +impl<'de> Deserialize<'de> for RpcEndpointUrl { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -211,14 +196,14 @@ impl<'de> Deserialize<'de> for RpcEndpoint { } } -impl From for RpcEndpointType { - fn from(endpoint: RpcEndpoint) -> Self { +impl From for RpcEndpointType { + fn from(endpoint: RpcEndpointUrl) -> Self { Self::String(endpoint) } } -impl From for RpcEndpointConfig { - fn from(endpoint: RpcEndpoint) -> Self { +impl From for RpcEndpoint { + fn from(endpoint: RpcEndpointUrl) -> Self { Self { endpoint, ..Default::default() } } } @@ -275,12 +260,9 @@ impl<'de> Deserialize<'de> for RpcAuth { } } -/// Rpc endpoint configuration variant -#[derive(Debug, Clone, PartialEq, Eq)] +// Rpc endpoint configuration +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct RpcEndpointConfig { - /// endpoint url or env - pub endpoint: RpcEndpoint, - /// The number of retries. pub retries: Option, @@ -291,23 +273,11 @@ pub struct RpcEndpointConfig { /// /// See also pub compute_units_per_second: Option, - - /// Token to be used as authentication - pub auth: Option, -} - -impl RpcEndpointConfig { - /// Returns the url this type holds, see [`RpcEndpoint::resolve`] - pub fn resolve(self) -> Result { - self.endpoint.resolve() - } } impl fmt::Display for RpcEndpointConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { endpoint, retries, retry_backoff, compute_units_per_second, auth } = self; - - write!(f, "{endpoint}")?; + let Self { retries, retry_backoff, compute_units_per_second } = self; if let Some(retries) = retries { write!(f, ", retries={retries}")?; @@ -321,38 +291,75 @@ impl fmt::Display for RpcEndpointConfig { write!(f, ", compute_units_per_second={compute_units_per_second}")?; } + Ok(()) + } +} + +/// Rpc endpoint configuration variant +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RpcEndpoint { + /// endpoint url or env + pub endpoint: RpcEndpointUrl, + + /// Token to be used as authentication + pub auth: Option, + + /// additional configuration + pub config: RpcEndpointConfig, +} + +impl RpcEndpoint { + pub fn new(endpoint: RpcEndpointUrl) -> Self { + Self { endpoint, ..Default::default() } + } + + /// Resolves environment variables in fields into their raw values + pub fn resolve(self) -> ResolvedRpcEndpoint { + ResolvedRpcEndpoint { + endpoint: self.endpoint.resolve(), + auth: self.auth.map(|auth| auth.resolve()), + config: self.config, + } + } +} + +impl fmt::Display for RpcEndpoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { endpoint, auth, config } = self; + write!(f, "{endpoint}")?; + write!(f, "{config}")?; if let Some(auth) = auth { write!(f, ", auth={auth}")?; } - Ok(()) } } -impl Serialize for RpcEndpointConfig { +impl Serialize for RpcEndpoint { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - if self.retries.is_none() && - self.retry_backoff.is_none() && - self.compute_units_per_second.is_none() + if self.config.retries.is_none() && + self.config.retry_backoff.is_none() && + self.config.compute_units_per_second.is_none() && + self.auth.is_none() { // serialize as endpoint if there's no additional config self.endpoint.serialize(serializer) } else { let mut map = serializer.serialize_map(Some(4))?; map.serialize_entry("endpoint", &self.endpoint)?; - map.serialize_entry("retries", &self.retries)?; - map.serialize_entry("retry_backoff", &self.retry_backoff)?; - map.serialize_entry("compute_units_per_second", &self.compute_units_per_second)?; + map.serialize_entry("retries", &self.config.retries)?; + map.serialize_entry("retry_backoff", &self.config.retry_backoff)?; + map.serialize_entry("compute_units_per_second", &self.config.compute_units_per_second)?; map.serialize_entry("auth", &self.auth)?; map.end() } } } -impl<'de> Deserialize<'de> for RpcEndpointConfig { +impl<'de> Deserialize<'de> for RpcEndpoint { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -368,7 +375,7 @@ impl<'de> Deserialize<'de> for RpcEndpointConfig { #[derive(Deserialize)] struct RpcEndpointConfigInner { #[serde(alias = "url")] - endpoint: RpcEndpoint, + endpoint: RpcEndpointUrl, retries: Option, retry_backoff: Option, compute_units_per_second: Option, @@ -383,46 +390,81 @@ impl<'de> Deserialize<'de> for RpcEndpointConfig { auth, } = serde_json::from_value(value).map_err(serde::de::Error::custom)?; - Ok(Self { endpoint, retries, retry_backoff, compute_units_per_second, auth }) + Ok(Self { + endpoint, + auth, + config: RpcEndpointConfig { retries, retry_backoff, compute_units_per_second }, + }) } } -impl From for RpcEndpointType { - fn from(config: RpcEndpointConfig) -> Self { +impl From for RpcEndpointType { + fn from(config: RpcEndpoint) -> Self { Self::Config(config) } } -impl Default for RpcEndpointConfig { +impl Default for RpcEndpoint { fn default() -> Self { Self { - endpoint: RpcEndpoint::Url("http://localhost:8545".to_string()), - retries: None, - retry_backoff: None, - compute_units_per_second: None, + endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + config: RpcEndpointConfig::default(), auth: None, } } } -/// Container type for _resolved_ endpoints, see [`RpcEndpoint::resolve`]. +/// Rpc endpoint with environment variables resolved to values, see [`RpcEndpoint::resolve`]. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolvedRpcEndpoint { + pub endpoint: Result, + pub auth: Option>, + pub config: RpcEndpointConfig, +} + +impl ResolvedRpcEndpoint { + /// Returns the url this type holds, see [`RpcEndpoint::resolve`] + pub fn url(&self) -> Result { + self.endpoint.clone() + } + + // Returns true if all environment variables are resolved successfully + pub fn is_unresolved(&self) -> bool { + let endpoint_err = self.endpoint.is_err(); + let auth_err = self.auth.as_ref().map(|auth| auth.is_err()).unwrap_or(false); + endpoint_err || auth_err + } + + // Attempts to resolve unresolved environment variables into a new instance + pub fn try_resolve(mut self) -> Self { + if !self.is_unresolved() { + return self + } + if let Err(err) = self.endpoint { + self.endpoint = err.try_resolve() + } + if let Some(Err(err)) = self.auth { + self.auth = Some(err.try_resolve()) + } + self + } +} + +/// Container type for _resolved_ endpoints. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct ResolvedRpcEndpoints { - /// contains all named endpoints and their URL or an error if we failed to resolve the env var - /// alias - endpoints: BTreeMap>, - auths: BTreeMap, UnresolvedEnvVarError>>, + endpoints: BTreeMap, } impl ResolvedRpcEndpoints { /// Returns true if there's an endpoint that couldn't be resolved pub fn has_unresolved(&self) -> bool { - self.endpoints.values().any(|val| val.is_err()) + self.endpoints.values().any(|e| e.is_unresolved()) } } impl Deref for ResolvedRpcEndpoints { - type Target = BTreeMap>; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.endpoints @@ -448,27 +490,31 @@ mod tests { "compute_units_per_second": 100, "auth": "Bearer 123" }"#; - let config: RpcEndpointConfig = serde_json::from_str(s).unwrap(); + let config: RpcEndpoint = serde_json::from_str(s).unwrap(); assert_eq!( config, - RpcEndpointConfig { - endpoint: RpcEndpoint::Url("http://localhost:8545".to_string()), - retries: Some(5), - retry_backoff: Some(250), - compute_units_per_second: Some(100), + RpcEndpoint { + endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + config: RpcEndpointConfig { + retries: Some(5), + retry_backoff: Some(250), + compute_units_per_second: Some(100), + }, auth: Some(RpcAuth::Raw("Bearer 123".to_string())), } ); let s = "\"http://localhost:8545\""; - let config: RpcEndpointConfig = serde_json::from_str(s).unwrap(); + let config: RpcEndpoint = serde_json::from_str(s).unwrap(); assert_eq!( config, - RpcEndpointConfig { - endpoint: RpcEndpoint::Url("http://localhost:8545".to_string()), - retries: None, - retry_backoff: None, - compute_units_per_second: None, + RpcEndpoint { + endpoint: RpcEndpointUrl::Url("http://localhost:8545".to_string()), + config: RpcEndpointConfig { + retries: None, + retry_backoff: None, + compute_units_per_second: None, + }, auth: None, } ); diff --git a/crates/config/src/inline/natspec.rs b/crates/config/src/inline/natspec.rs index 5774d9e19..2cb605f03 100644 --- a/crates/config/src/inline/natspec.rs +++ b/crates/config/src/inline/natspec.rs @@ -6,7 +6,13 @@ use foundry_compilers::{ }; use itertools::Itertools; use serde_json::Value; -use solang_parser::{helpers::CodeLocation, pt}; +use solar_parse::{ + ast::{ + interface::{self, Session}, + Arena, CommentKind, Item, ItemKind, + }, + Parser, +}; use std::{collections::BTreeMap, path::Path}; /// Convenient struct to hold in-line per-test configurations @@ -30,7 +36,7 @@ impl NatSpec { let mut natspecs: Vec = vec![]; let solc = SolcParser::new(); - let solang = SolangParser::new(); + let solar = SolarParser::new(); for (id, artifact) in output.artifact_ids() { let abs_path = id.source.as_path(); let path = abs_path.strip_prefix(root).unwrap_or(abs_path); @@ -48,7 +54,7 @@ impl NatSpec { if !used_solc_ast { if let Ok(src) = std::fs::read_to_string(abs_path) { - solang.parse(&mut natspecs, &src, &contract, contract_name); + solar.parse(&mut natspecs, &src, &contract, contract_name); } } } @@ -201,11 +207,11 @@ impl SolcParser { } } -struct SolangParser { +struct SolarParser { _private: (), } -impl SolangParser { +impl SolarParser { fn new() -> Self { Self { _private: () } } @@ -222,57 +228,85 @@ impl SolangParser { return; } - let Ok((pt, comments)) = solang_parser::parse(src, 0) else { return }; - - // Collects natspects from the given range. - let mut handle_docs = |contract: &str, func: Option<&str>, start, end| { - let docs = solang_parser::doccomment::parse_doccomments(&comments, start, end); - natspecs.extend( - docs.into_iter() - .flat_map(|doc| doc.into_comments()) - .filter(|doc| doc.value.contains(INLINE_CONFIG_PREFIX)) - .map(|doc| NatSpec { - // not possible to obtain correct value due to solang-parser bug - // https://github.com/hyperledger/solang/issues/1658 - line: "0:0:0".to_string(), - contract: contract.to_string(), - function: func.map(|f| f.to_string()), - docs: doc.value, - }), - ); + let mut handle_docs = |item: &Item<'_>| { + if item.docs.is_empty() { + return; + } + let lines = item + .docs + .iter() + .filter_map(|d| { + let s = d.symbol.as_str(); + if !s.contains(INLINE_CONFIG_PREFIX) { + return None + } + match d.kind { + CommentKind::Line => Some(s.trim().to_string()), + CommentKind::Block => Some( + s.lines() + .filter(|line| line.contains(INLINE_CONFIG_PREFIX)) + .map(|line| line.trim_start().trim_start_matches('*').trim()) + .collect::>() + .join("\n"), + ), + } + }) + .join("\n"); + if lines.is_empty() { + return; + } + let span = + item.docs.iter().map(|doc| doc.span).reduce(|a, b| a.to(b)).unwrap_or_default(); + natspecs.push(NatSpec { + contract: contract_id.to_string(), + function: if let ItemKind::Function(f) = &item.kind { + Some( + f.header + .name + .map(|sym| sym.to_string()) + .unwrap_or_else(|| f.kind.to_string()), + ) + } else { + None + }, + line: format!("{}:{}:0", span.lo().0, span.hi().0), + docs: lines, + }); }; - let mut prev_item_end = 0; - for item in &pt.0 { - let pt::SourceUnitPart::ContractDefinition(c) = item else { - prev_item_end = item.loc().end(); - continue - }; - let Some(id) = c.name.as_ref() else { - prev_item_end = item.loc().end(); - continue - }; - if id.name != contract_name { - prev_item_end = item.loc().end(); - continue - }; - - // Handle doc comments in between the previous contract and the current one. - handle_docs(contract_id, None, prev_item_end, item.loc().start()); - - let mut prev_end = c.loc.start(); - for part in &c.parts { - let pt::ContractPart::FunctionDefinition(f) = part else { continue }; - let start = f.loc.start(); - // Handle doc comments in between the previous function and the current one. - if let Some(name) = &f.name { - handle_docs(contract_id, Some(name.name.as_str()), prev_end, start); + let sess = Session::builder() + .with_silent_emitter(Some("Inline config parsing failed".to_string())) + .build(); + let _ = sess.enter(|| -> interface::Result<()> { + let arena = Arena::new(); + + let mut parser = Parser::from_source_code( + &sess, + &arena, + interface::source_map::FileName::Custom(contract_id.to_string()), + src.to_string(), + )?; + + let source_unit = parser.parse_file().map_err(|e| e.emit())?; + + for item in source_unit.items.iter() { + let ItemKind::Contract(c) = &item.kind else { continue }; + if c.name.as_str() != contract_name { + continue; + } + + // Handle contract level doc comments. + handle_docs(item); + + // Handle function level doc comments. + for item in c.body.iter() { + let ItemKind::Function(_) = &item.kind else { continue }; + handle_docs(item); } - prev_end = f.loc.end(); } - prev_item_end = item.loc().end(); - } + Ok(()) + }); } } @@ -318,7 +352,7 @@ mod tests { } #[test] - fn parse_solang() { + fn parse_solar() { let src = " contract C { /// forge-config: default.fuzz.runs = 600 @@ -336,10 +370,9 @@ function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {} } "; let mut natspecs = vec![]; - let solang = SolangParser::new(); let id = || "path.sol:C".to_string(); - let default_line = || "0:0:0".to_string(); - solang.parse(&mut natspecs, src, &id(), "C"); + let solar_parser = SolarParser::new(); + solar_parser.parse(&mut natspecs, src, &id(), "C"); assert_eq!( natspecs, [ @@ -347,28 +380,28 @@ function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {} NatSpec { contract: id(), function: Some("f1".to_string()), - line: default_line(), + line: "14:134:0".to_string(), docs: "forge-config: default.fuzz.runs = 600\nforge-config: default.fuzz.runs = 601".to_string(), }, // f2 NatSpec { contract: id(), function: Some("f2".to_string()), - line: default_line(), + line: "164:208:0".to_string(), docs: "forge-config: default.fuzz.runs = 700".to_string(), }, // f3 NatSpec { contract: id(), function: Some("f3".to_string()), - line: default_line(), + line: "226:270:0".to_string(), docs: "forge-config: default.fuzz.runs = 800".to_string(), }, // f4 NatSpec { contract: id(), function: Some("f4".to_string()), - line: default_line(), + line: "289:391:0".to_string(), docs: "forge-config: default.fuzz.runs = 1024\nforge-config: default.fuzz.max-test-rejects = 500".to_string(), }, ] @@ -376,7 +409,7 @@ function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {} } #[test] - fn parse_solang_2() { + fn parse_solar_2() { let src = r#" // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; @@ -394,17 +427,16 @@ contract FuzzInlineConf is DSTest { } "#; let mut natspecs = vec![]; - let solang = SolangParser::new(); + let solar = SolarParser::new(); let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(); - let default_line = || "0:0:0".to_string(); - solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); + solar.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); assert_eq!( natspecs, [ NatSpec { contract: id(), function: Some("testInlineConfFuzz".to_string()), - line: default_line(), + line: "141:255:0".to_string(), docs: "forge-config: default.fuzz.runs = 1024\nforge-config: default.fuzz.max-test-rejects = 500".to_string(), }, ] @@ -466,7 +498,7 @@ contract FuzzInlineConf is DSTest { } #[test] - fn parse_solang_multiple_contracts_from_same_file() { + fn parse_solar_multiple_contracts_from_same_file() { let src = r#" // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; @@ -484,29 +516,28 @@ contract FuzzInlineConf2 is DSTest { } "#; let mut natspecs = vec![]; - let solang = SolangParser::new(); + let solar = SolarParser::new(); let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(); - let default_line = || "0:0:0".to_string(); - solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); + solar.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); assert_eq!( natspecs, [NatSpec { contract: id(), function: Some("testInlineConfFuzz1".to_string()), - line: default_line(), + line: "142:181:0".to_string(), docs: "forge-config: default.fuzz.runs = 1".to_string(), },] ); let mut natspecs = vec![]; let id = || "inline/FuzzInlineConf2.t.sol:FuzzInlineConf2".to_string(); - solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf2"); + solar.parse(&mut natspecs, src, &id(), "FuzzInlineConf2"); assert_eq!( natspecs, [NatSpec { contract: id(), function: Some("testInlineConfFuzz2".to_string()), - line: default_line(), + line: "264:303:0".to_string(), // should not get config from previous contract docs: "forge-config: default.fuzz.runs = 2".to_string(), },] @@ -529,23 +560,22 @@ contract FuzzInlineConf is DSTest { function testInlineConfFuzz2() {} }"#; let mut natspecs = vec![]; - let solang = SolangParser::new(); + let solar = SolarParser::new(); let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(); - let default_line = || "0:0:0".to_string(); - solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); + solar.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); assert_eq!( natspecs, [ NatSpec { contract: id(), function: None, - line: default_line(), + line: "101:140:0".to_string(), docs: "forge-config: default.fuzz.runs = 1".to_string(), }, NatSpec { contract: id(), function: Some("testInlineConfFuzz1".to_string()), - line: default_line(), + line: "181:220:0".to_string(), docs: "forge-config: default.fuzz.runs = 3".to_string(), } ] diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 888beed79..5628591f5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -58,7 +58,9 @@ pub mod utils; pub use utils::*; mod endpoints; -pub use endpoints::{ResolvedRpcEndpoints, RpcEndpoint, RpcEndpoints}; +pub use endpoints::{ + ResolvedRpcEndpoint, ResolvedRpcEndpoints, RpcEndpoint, RpcEndpointUrl, RpcEndpoints, +}; mod etherscan; use etherscan::{ @@ -955,6 +957,7 @@ impl Config { } /// Resolves globs and builds a mapping from individual source files to their restrictions + #[expect(clippy::disallowed_macros)] fn restrictions( &self, paths: &ProjectPathsConfig, @@ -983,7 +986,20 @@ impl Config { if !map.contains_key(source) { map.insert(source.clone(), res); } else { - map.get_mut(source.as_path()).unwrap().merge(res); + let value = map.remove(source.as_path()).unwrap(); + if let Some(merged) = value.clone().merge(res) { + map.insert(source.clone(), merged); + } else { + // `sh_warn!` is a circular dependency, preventing us from using it here. + eprintln!( + "{}", + yansi::Paint::yellow(&format!( + "Failed to merge compilation restrictions for {}", + source.display() + )) + ); + map.insert(source.clone(), value); + } } } } @@ -1301,7 +1317,7 @@ impl Config { ) -> Option, UnresolvedEnvVarError>> { let mut endpoints = self.rpc_endpoints.clone().resolved(); if let Some(endpoint) = endpoints.remove(maybe_alias) { - return Some(endpoint.map(Cow::Owned)); + return Some(endpoint.url().map(Cow::Owned)); } if let Ok(Some(endpoint)) = mesc::get_endpoint_by_query(maybe_alias, Some("foundry")) { @@ -2557,10 +2573,10 @@ mod tests { use super::*; use crate::{ cache::{CachedChains, CachedEndpoints}, - endpoints::{RpcEndpointConfig, RpcEndpointType}, + endpoints::{RpcEndpoint, RpcEndpointType}, etherscan::ResolvedEtherscanConfigs, }; - use endpoints::RpcAuth; + use endpoints::{RpcAuth, RpcEndpointConfig}; use figment::error::Kind::InvalidType; use foundry_compilers::artifacts::{ vyper::VyperOptimizationMode, ModelCheckerEngine, YulDetails, @@ -3230,17 +3246,19 @@ mod tests { RpcEndpoints::new([ ( "optimism", - RpcEndpointType::String(RpcEndpoint::Url( + RpcEndpointType::String(RpcEndpointUrl::Url( "https://example.com/".to_string() )) ), ( "mainnet", - RpcEndpointType::Config(RpcEndpointConfig { - endpoint: RpcEndpoint::Env("${_CONFIG_MAINNET}".to_string()), - retries: Some(3), - retry_backoff: Some(1000), - compute_units_per_second: Some(1000), + RpcEndpointType::Config(RpcEndpoint { + endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + config: RpcEndpointConfig { + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000), + }, auth: None, }) ), @@ -3251,10 +3269,23 @@ mod tests { let resolved = config.rpc_endpoints.resolved(); assert_eq!( RpcEndpoints::new([ - ("optimism", RpcEndpoint::Url("https://example.com/".to_string())), + ( + "optimism", + RpcEndpointType::String(RpcEndpointUrl::Url( + "https://example.com/".to_string() + )) + ), ( "mainnet", - RpcEndpoint::Url("https://eth-mainnet.alchemyapi.io/v2/123455".to_string()) + RpcEndpointType::Config(RpcEndpoint { + endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + config: RpcEndpointConfig { + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000), + }, + auth: None, + }) ), ]) .resolved(), @@ -3287,17 +3318,19 @@ mod tests { RpcEndpoints::new([ ( "optimism", - RpcEndpointType::String(RpcEndpoint::Url( + RpcEndpointType::String(RpcEndpointUrl::Url( "https://example.com/".to_string() )) ), ( "mainnet", - RpcEndpointType::Config(RpcEndpointConfig { - endpoint: RpcEndpoint::Env("${_CONFIG_MAINNET}".to_string()), - retries: Some(3), - retry_backoff: Some(1000), - compute_units_per_second: Some(1000), + RpcEndpointType::Config(RpcEndpoint { + endpoint: RpcEndpointUrl::Env("${_CONFIG_MAINNET}".to_string()), + config: RpcEndpointConfig { + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000) + }, auth: Some(RpcAuth::Env("Bearer ${_CONFIG_AUTH}".to_string())), }) ), @@ -3309,19 +3342,21 @@ mod tests { RpcEndpoints::new([ ( "optimism", - RpcEndpointType::String(RpcEndpoint::Url( + RpcEndpointType::String(RpcEndpointUrl::Url( "https://example.com/".to_string() )) ), ( "mainnet", - RpcEndpointType::Config(RpcEndpointConfig { - endpoint: RpcEndpoint::Url( + RpcEndpointType::Config(RpcEndpoint { + endpoint: RpcEndpointUrl::Url( "https://eth-mainnet.alchemyapi.io/v2/123455".to_string() ), - retries: Some(3), - retry_backoff: Some(1000), - compute_units_per_second: Some(1000), + config: RpcEndpointConfig { + retries: Some(3), + retry_backoff: Some(1000), + compute_units_per_second: Some(1000) + }, auth: Some(RpcAuth::Raw("Bearer 123456".to_string())), }) ), @@ -3367,18 +3402,22 @@ mod tests { assert_eq!( endpoints, RpcEndpoints::new([ - ("optimism", RpcEndpoint::Url("https://example.com/".to_string())), + ("optimism", RpcEndpointUrl::Url("https://example.com/".to_string())), ( "mainnet", - RpcEndpoint::Url("https://eth-mainnet.alchemyapi.io/v2/123455".to_string()) + RpcEndpointUrl::Url( + "https://eth-mainnet.alchemyapi.io/v2/123455".to_string() + ) ), ( "mainnet_2", - RpcEndpoint::Url("https://eth-mainnet.alchemyapi.io/v2/123456".to_string()) + RpcEndpointUrl::Url( + "https://eth-mainnet.alchemyapi.io/v2/123456".to_string() + ) ), ( "mainnet_3", - RpcEndpoint::Url( + RpcEndpointUrl::Url( "https://eth-mainnet.alchemyapi.io/v2/123456/98765".to_string() ) ), @@ -3554,17 +3593,17 @@ mod tests { revert_strings: Some(RevertStrings::Strip), allow_paths: vec![PathBuf::from("allow"), PathBuf::from("paths")], rpc_endpoints: RpcEndpoints::new([ - ("optimism", RpcEndpoint::Url("https://example.com/".to_string())), - ("mainnet", RpcEndpoint::Env("${RPC_MAINNET}".to_string())), + ("optimism", RpcEndpointUrl::Url("https://example.com/".to_string())), + ("mainnet", RpcEndpointUrl::Env("${RPC_MAINNET}".to_string())), ( "mainnet_2", - RpcEndpoint::Env( + RpcEndpointUrl::Env( "https://eth-mainnet.alchemyapi.io/v2/${API_KEY}".to_string() ) ), ( "mainnet_3", - RpcEndpoint::Env( + RpcEndpointUrl::Env( "https://eth-mainnet.alchemyapi.io/v2/${API_KEY}/${ANOTHER_KEY}" .to_string() ) @@ -3688,11 +3727,11 @@ mod tests { assert_eq!( config.rpc_endpoints, RpcEndpoints::new([ - ("optimism", RpcEndpoint::Url("https://example.com/".to_string())), - ("mainnet", RpcEndpoint::Env("${RPC_MAINNET}".to_string())), + ("optimism", RpcEndpointUrl::Url("https://example.com/".to_string())), + ("mainnet", RpcEndpointUrl::Env("${RPC_MAINNET}".to_string())), ( "mainnet_2", - RpcEndpoint::Env( + RpcEndpointUrl::Env( "https://eth-mainnet.alchemyapi.io/v2/${API_KEY}".to_string() ) ), diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 621f5191d..5d830cd82 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1358,7 +1358,7 @@ impl DatabaseExt for Backend { self.commit(journaled_state.state.clone()); let res = { - configure_tx_req_env(&mut env, tx)?; + configure_tx_req_env(&mut env, tx, None)?; let env = self.env_with_handler_cfg(env); let mut db = self.clone(); @@ -1943,7 +1943,8 @@ fn update_env_block(env: &mut Env, block: &AnyRpcBlock) { env.block.gas_limit = U256::from(block.header.gas_limit); env.block.number = U256::from(block.header.number); if let Some(excess_blob_gas) = block.header.excess_blob_gas { - env.block.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(excess_blob_gas)); + env.block.blob_excess_gas_and_price = + Some(BlobExcessGasAndPrice::new(excess_blob_gas, false)); } } diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 112e1eeda..490ae7379 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -514,6 +514,7 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, ProviderBuilder::new(fork.url.as_str()) .maybe_max_retry(fork.evm_opts.fork_retries) .maybe_initial_backoff(fork.evm_opts.fork_retry_backoff) + .maybe_headers(fork.evm_opts.fork_headers.clone()) .compute_units_per_second(fork.evm_opts.get_compute_units_per_second()) .build()?, ); diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 76be5937b..4c13369b6 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -29,6 +29,9 @@ pub struct EvmOpts { /// Initial retry backoff. pub fork_retry_backoff: Option, + /// Headers to use with `fork_url` + pub fork_headers: Option>, + /// The available compute units per second. /// /// See also @@ -80,6 +83,7 @@ impl Default for EvmOpts { fork_block_number: None, fork_retries: None, fork_retry_backoff: None, + fork_headers: None, compute_units_per_second: None, no_rpc_rate_limit: false, no_storage_caching: false, diff --git a/crates/evm/core/src/precompiles.rs b/crates/evm/core/src/precompiles.rs index ceaf6d004..c70dc06d2 100644 --- a/crates/evm/core/src/precompiles.rs +++ b/crates/evm/core/src/precompiles.rs @@ -49,7 +49,7 @@ pub const PRECOMPILES: &[Address] = &[ ODYSSEY_P256_ADDRESS, ]; -/// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212) secp256r1 precompile address on Odyssey. +/// [EIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md) secp256r1 precompile address on Odyssey. /// /// pub const ODYSSEY_P256_ADDRESS: Address = address!("0000000000000000000000000000000000000014"); diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index b7e9513e8..215ce3163 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -9,6 +9,7 @@ use alloy_network::AnyTxEnvelope; use alloy_primitives::{Address, Selector, TxKind, B256, U256}; use alloy_provider::{network::BlockResponse, Network}; use alloy_rpc_types::{Transaction, TransactionRequest}; +use foundry_common::is_impersonated_tx; use foundry_config::NamedChain; use foundry_fork_db::DatabaseError; use foundry_zksync_core::DEFAULT_CREATE2_DEPLOYER_ZKSYNC; @@ -18,6 +19,7 @@ use revm::{ return_ok, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, Gas, InstructionResult, InterpreterResult, }, + precompile::secp256r1::P256VERIFY, primitives::{CreateScheme, EVMError, HandlerCfg, SpecId, KECCAK_EMPTY}, FrameOrResult, FrameResult, }; @@ -89,16 +91,21 @@ pub fn get_function<'a>( } /// Configures the env for the given RPC transaction. +/// Accounts for an impersonated transaction by resetting the `env.tx.caller` field to `tx.from`. pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { + let impersonated_from = is_impersonated_tx(&tx.inner).then_some(tx.from); if let AnyTxEnvelope::Ethereum(tx) = &tx.inner { - configure_tx_req_env(env, &tx.clone().into()).expect("cannot fail"); + configure_tx_req_env(env, &tx.clone().into(), impersonated_from).expect("cannot fail"); } } /// Configures the env for the given RPC transaction request. +/// `impersonated_from` is the address of the impersonated account. This helps account for an +/// impersonated transaction by resetting the `env.tx.caller` field to `impersonated_from`. pub fn configure_tx_req_env( env: &mut revm::primitives::Env, tx: &TransactionRequest, + impersonated_from: Option
, ) -> eyre::Result<()> { let TransactionRequest { nonce, @@ -121,7 +128,10 @@ pub fn configure_tx_req_env( // If no `to` field then set create kind: https://eips.ethereum.org/EIPS/eip-2470#deployment-transaction env.tx.transact_to = to.unwrap_or(TxKind::Create); - env.tx.caller = from.ok_or_else(|| eyre::eyre!("missing `from` field"))?; + // If the transaction is impersonated, we need to set the caller to the from + // address Ref: https://github.com/foundry-rs/foundry/issues/9541 + env.tx.caller = + impersonated_from.unwrap_or(from.ok_or_else(|| eyre::eyre!("missing `from` field"))?); env.tx.gas_limit = gas.ok_or_else(|| eyre::eyre!("missing `gas` field"))?; env.tx.nonce = nonce; env.tx.value = value.unwrap_or_default(); @@ -308,7 +318,7 @@ pub fn odyssey_handler_register(handler: &mut EvmHandle handler.pre_execution.load_precompiles = Arc::new(move || { let mut loaded_precompiles = prev(); - loaded_precompiles.extend([ODYSSEY_P256]); + loaded_precompiles.extend([ODYSSEY_P256, P256VERIFY]); loaded_precompiles }); diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 0e6c8975b..60fc3f5ca 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -978,7 +978,7 @@ fn convert_executed_result( (reason.into(), 0_u64, gas_used, None, vec![]) } }; - let stipend = revm::interpreter::gas::validate_initial_tx_gas( + let gas = revm::interpreter::gas::calculate_initial_tx_gas( env.spec_id(), &env.tx.data, env.tx.transact_to.is_create(), @@ -1006,7 +1006,7 @@ fn convert_executed_result( result, gas_used, gas_refunded, - stipend, + stipend: gas.initial_gas, logs, labels, traces, diff --git a/crates/evm/traces/src/debug/sources.rs b/crates/evm/traces/src/debug/sources.rs index b2e37e32d..5c0caa579 100644 --- a/crates/evm/traces/src/debug/sources.rs +++ b/crates/evm/traces/src/debug/sources.rs @@ -11,10 +11,7 @@ use foundry_compilers::{ use foundry_evm_core::utils::PcIcMap; use foundry_linking::Linker; use rayon::prelude::*; -use solar_parse::{ - interface::{Pos, Session}, - Parser, -}; +use solar_parse::{interface::Session, Parser}; use std::{ collections::{BTreeMap, HashMap}, ops::Range, diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 8dd228040..5bdb6178f 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -39,8 +39,6 @@ foundry-zksync-core.workspace = true foundry-zksync-compilers.workspace = true forge-script-sequence.workspace = true -ethers-contract-abigen = { workspace = true, features = ["providers"] } - revm-inspectors.workspace = true comfy-table.workspace = true @@ -94,7 +92,6 @@ semver.workspace = true serde_json.workspace = true similar = { version = "2", features = ["inline"] } solang-parser.workspace = true -solar-ast.workspace = true solar-parse.workspace = true strum = { workspace = true, features = ["derive"] } thiserror.workspace = true diff --git a/crates/forge/assets/workflowTemplate.yml b/crates/forge/assets/workflowTemplate.yml index 762a2966f..34a4a527b 100644 --- a/crates/forge/assets/workflowTemplate.yml +++ b/crates/forge/assets/workflowTemplate.yml @@ -22,8 +22,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - name: Show Forge version run: | diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index ec6b13dfd..c8763d08c 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -1,11 +1,8 @@ use alloy_primitives::map::HashSet; use clap::{Parser, ValueHint}; -use ethers_contract_abigen::{ - Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts, -}; -use eyre::{Result, WrapErr}; +use eyre::Result; use forge_sol_macro_gen::{MultiSolMacroGen, SolMacroGen}; -use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_cli::{opts::BuildOpts, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, fs::json_files}; use foundry_config::impl_figment_convert; use regex::Regex; @@ -14,7 +11,7 @@ use std::{ path::{Path, PathBuf}, }; -impl_figment_convert!(BindArgs, build_args); +impl_figment_convert!(BindArgs, build); const DEFAULT_CRATE_NAME: &str = "foundry-contracts"; const DEFAULT_CRATE_VERSION: &str = "0.1.0"; @@ -83,32 +80,30 @@ pub struct BindArgs { skip_extra_derives: bool, /// Generate bindings for the `alloy` library, instead of `ethers`. - #[arg(long, conflicts_with = "ethers")] + #[arg(long, hide = true)] alloy: bool, /// Specify the alloy version. - #[arg(long, value_name = "ALLOY_VERSION")] + #[arg(long)] alloy_version: Option, - /// Generate bindings for the `ethers` library, instead of `alloy` (default, deprecated). - #[arg(long)] + /// Generate bindings for the `ethers` library, instead of `alloy` (removed). + #[arg(long, hide = true)] ethers: bool, #[command(flatten)] - build_args: CoreBuildArgs, + build: BuildOpts, } impl BindArgs { pub fn run(self) -> Result<()> { - if !self.skip_build { - let project = self.build_args.project()?; - let _ = ProjectCompiler::new().compile(&project)?; + if self.ethers { + eyre::bail!("`--ethers` bindings have been removed. Use `--alloy` (default) instead."); } - if self.ethers { - sh_warn!( - "`--ethers` bindings are deprecated and will be removed in the future. Consider using `--alloy` (default) instead." - )?; + if !self.skip_build { + let project = self.build.project()?; + let _ = ProjectCompiler::new().compile(&project)?; } let config = self.try_load_config_emit_warnings()?; @@ -131,40 +126,7 @@ impl BindArgs { Ok(()) } - /// Returns the filter to use for `MultiAbigen` - fn get_filter(&self) -> Result { - if self.select_all { - return Ok(ContractFilter::All) - } - if !self.select.is_empty() { - return Ok(SelectContracts::default().extend_regex(self.select.clone()).into()) - } - if let Some(skip) = self.build_args.skip.as_ref().filter(|s| !s.is_empty()) { - return Ok(ExcludeContracts::default() - .extend_regex( - skip.clone() - .into_iter() - .map(|s| Regex::new(s.file_pattern())) - .collect::, _>>()?, - ) - .into()) - } - // This excludes all Test/Script and forge-std contracts - Ok(ExcludeContracts::default() - .extend_pattern([ - ".*Test.*", - ".*Script", - "console[2]?", - "CommonBase", - "Components", - "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Toml|Storage(Safe)?)", - "[Vv]m.*", - ]) - .extend_names(["IMulticall3"]) - .into()) - } - - fn get_alloy_filter(&self) -> Result { + fn get_filter(&self) -> Result { if self.select_all { // Select all json files return Ok(Filter::All); @@ -174,7 +136,7 @@ impl BindArgs { return Ok(Filter::Select(self.select.clone())); } - if let Some(skip) = self.build_args.skip.as_ref().filter(|s| !s.is_empty()) { + if let Some(skip) = self.build.skip.as_ref().filter(|s| !s.is_empty()) { return Ok(Filter::Skip( skip.clone() .into_iter() @@ -190,8 +152,6 @@ impl BindArgs { /// Returns an iterator over the JSON files and the contract name in the `artifacts` directory. fn get_json_files(&self, artifacts: &Path) -> Result> { let filter = self.get_filter()?; - let alloy_filter = self.get_alloy_filter()?; - let is_alloy = !self.ethers; Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. @@ -212,35 +172,7 @@ impl BindArgs { Some((name, path)) }) - .filter( - move |(name, _path)| { - if is_alloy { - alloy_filter.is_match(name) - } else { - filter.is_match(name) - } - }, - )) - } - - /// Instantiate the multi-abigen - fn get_multi(&self, artifacts: &Path) -> Result { - let abigens = self - .get_json_files(artifacts)? - .map(|(name, path)| { - trace!(?path, "parsing Abigen from file"); - let abi = Abigen::new(name, path.to_str().unwrap()) - .wrap_err_with(|| format!("failed to parse Abigen from file: {path:?}")); - if !self.skip_extra_derives { - abi?.add_derive("serde::Serialize")?.add_derive("serde::Deserialize") - } else { - abi - } - }) - .collect::, _>>()?; - let multi = MultiAbigen::from_abigens(abigens); - eyre::ensure!(!multi.is_empty(), "No contract artifacts found"); - Ok(multi) + .filter(move |(name, _path)| filter.is_match(name))) } fn get_solmacrogen(&self, artifacts: &Path) -> Result { @@ -264,40 +196,6 @@ impl BindArgs { /// Check that the existing bindings match the expected abigen output fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if self.ethers { - return self.check_ethers(artifacts, bindings_root); - } - - self.check_alloy(artifacts, bindings_root) - } - - fn check_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - let bindings = self.get_multi(artifacts)?.build()?; - sh_println!("Checking bindings for {} contracts.", bindings.len())?; - if !self.module { - bindings - .ensure_consistent_crate( - &self.crate_name, - &self.crate_version, - bindings_root, - self.single_file, - !self.skip_cargo_toml, - ) - .map_err(|err| { - if !self.skip_cargo_toml && err.to_string().contains("Cargo.toml") { - err.wrap_err("To skip Cargo.toml consistency check, pass --skip-cargo-toml") - } else { - err - } - })?; - } else { - bindings.ensure_consistent_module(bindings_root, self.single_file)?; - } - sh_println!("OK.")?; - Ok(()) - } - - fn check_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut bindings = self.get_solmacrogen(artifacts)?; bindings.generate_bindings()?; sh_println!("Checking bindings for {} contracts", bindings.instances.len())?; @@ -316,34 +214,6 @@ impl BindArgs { /// Generate the bindings fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - if self.ethers { - return self.generate_ethers(artifacts, bindings_root); - } - - self.generate_alloy(artifacts, bindings_root) - } - - fn generate_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { - let mut bindings = self.get_multi(artifacts)?.build()?; - sh_println!("Generating bindings for {} contracts", bindings.len())?; - if !self.module { - trace!(single_file = self.single_file, "generating crate"); - if !self.skip_extra_derives { - bindings = bindings.dependencies([r#"serde = "1""#]) - } - bindings.write_to_crate( - &self.crate_name, - &self.crate_version, - bindings_root, - self.single_file, - ) - } else { - trace!(single_file = self.single_file, "generating module"); - bindings.write_to_module(bindings_root, self.single_file) - } - } - - fn generate_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { let mut solmacrogen = self.get_solmacrogen(artifacts)?; sh_println!("Generating bindings for {} contracts", solmacrogen.instances.len())?; diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index d8a361134..593380297 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -1,7 +1,7 @@ use super::eip712::Resolver; use clap::{Parser, ValueHint}; use eyre::Result; -use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_cli::{opts::BuildOpts, utils::LoadConfig}; use foundry_common::{compile::with_compilation_reporter, fs}; use foundry_compilers::{ artifacts::{ @@ -16,16 +16,20 @@ use foundry_compilers::{ use foundry_config::Config; use itertools::Itertools; use rayon::prelude::*; -use solang_parser::pt as solang_ast; +use solar_parse::{ + ast::{self, interface::source_map::FileName, visit::Visit, Arena, FunctionKind, Span, VarMut}, + interface::Session, + Parser as SolarParser, +}; use std::{ collections::{BTreeMap, BTreeSet}, - fmt, - fmt::Write, + fmt::{self, Write}, + ops::ControlFlow, path::PathBuf, sync::Arc, }; -foundry_config::impl_figment_convert!(BindJsonArgs, opts); +foundry_config::impl_figment_convert!(BindJsonArgs, build); /// CLI arguments for `forge bind-json`. #[derive(Clone, Debug, Parser)] @@ -35,7 +39,7 @@ pub struct BindJsonArgs { pub out: Option, #[command(flatten)] - opts: CoreBuildArgs, + build: BuildOpts, } impl BindJsonArgs { @@ -85,85 +89,101 @@ impl BindJsonArgs { .unwrap() .1; - // Insert empty bindings file + let sess = Session::builder().with_stderr_emitter().build(); + let result = sess.enter_parallel(|| -> solar_parse::interface::Result<()> { + sources.0.par_iter_mut().try_for_each(|(path, source)| { + let mut content = Arc::try_unwrap(std::mem::take(&mut source.content)).unwrap(); + + let arena = Arena::new(); + let mut parser = SolarParser::from_source_code( + &sess, + &arena, + FileName::Real(path.clone()), + content.to_string(), + )?; + let ast = parser.parse_file().map_err(|e| e.emit())?; + + let mut visitor = PreprocessorVisitor::new(); + visitor.visit_source_unit(&ast); + visitor.update(&sess, &mut content); + + source.content = Arc::new(content); + Ok(()) + }) + }); + eyre::ensure!(result.is_ok(), "failed parsing"); + + // Insert empty bindings file. sources.insert(target_path.clone(), Source::new("library JsonBindings {}")); - let sources = Sources( - sources - .0 - .into_par_iter() - .map(|(path, source)| { - let mut locs_to_update = Vec::new(); - let mut content = Arc::unwrap_or_clone(source.content); - let (parsed, _) = solang_parser::parse(&content, 0) - .map_err(|errors| eyre::eyre!("Parser failed: {errors:?}"))?; - - // All function definitions in the file - let mut functions = Vec::new(); - - for part in &parsed.0 { - if let solang_ast::SourceUnitPart::FunctionDefinition(def) = part { - functions.push(def); - } - if let solang_ast::SourceUnitPart::ContractDefinition(contract) = part { - for part in &contract.parts { - match part { - solang_ast::ContractPart::FunctionDefinition(def) => { - functions.push(def); - } - // Remove `immutable` attributes - solang_ast::ContractPart::VariableDefinition(def) => { - for attr in &def.attrs { - if let solang_ast::VariableAttribute::Immutable(loc) = - attr - { - locs_to_update.push(( - loc.start(), - loc.end(), - String::new(), - )); - } - } - } - _ => {} - } - } - }; - } + Ok(PreprocessedState { sources, target_path, project, config }) + } +} - for def in functions { - // If there's no body block, keep the function as is - let Some(solang_ast::Statement::Block { loc, .. }) = def.body else { - continue; - }; - let new_body = match def.ty { - solang_ast::FunctionTy::Modifier => "{ _; }", - _ => "{ revert(); }", - }; - let start = loc.start(); - let end = loc.end(); - locs_to_update.push((start, end + 1, new_body.to_string())); - } +struct PreprocessorVisitor { + updates: Vec<(Span, &'static str)>, +} + +impl PreprocessorVisitor { + fn new() -> Self { + Self { updates: Vec::new() } + } - locs_to_update.sort_by_key(|(start, _, _)| *start); + fn update(mut self, sess: &Session, content: &mut String) { + if self.updates.is_empty() { + return; + } - let mut shift = 0_i64; + let sf = sess.source_map().lookup_source_file(self.updates[0].0.lo()); + let base = sf.start_pos.0; - for (start, end, new) in locs_to_update { - let start = ((start as i64) - shift) as usize; - let end = ((end as i64) - shift) as usize; + self.updates.sort_by_key(|(span, _)| span.lo()); + let mut shift = 0_i64; + for (span, new) in self.updates { + let lo = span.lo() - base; + let hi = span.hi() - base; + let start = ((lo.0 as i64) - shift) as usize; + let end = ((hi.0 as i64) - shift) as usize; - content.replace_range(start..end, new.as_str()); - shift += (end - start) as i64; - shift -= new.len() as i64; - } + content.replace_range(start..end, new); + shift += (end - start) as i64; + shift -= new.len() as i64; + } + } +} - Ok((path, Source::new(content))) - }) - .collect::>>()?, - ); +impl<'ast> Visit<'ast> for PreprocessorVisitor { + type BreakValue = solar_parse::interface::data_structures::Never; + + fn visit_item_function( + &mut self, + func: &'ast ast::ItemFunction<'ast>, + ) -> ControlFlow { + // Replace function bodies with a noop statement. + if let Some(block) = &func.body { + if !block.is_empty() { + let span = block.first().unwrap().span.to(block.last().unwrap().span); + let new_body = match func.kind { + FunctionKind::Modifier => "_;", + _ => "revert();", + }; + self.updates.push((span, new_body)); + } + } - Ok(PreprocessedState { sources, target_path, project, config }) + self.walk_item_function(func) + } + + fn visit_variable_definition( + &mut self, + var: &'ast ast::VariableDefinition<'ast>, + ) -> ControlFlow { + // Remove `immutable` attributes. + if let Some(VarMut::Immutable) = var.mutability { + self.updates.push((var.span, "")); + } + + self.walk_variable_definition(var) } } diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 758da4cc1..7f10d0cae 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -1,7 +1,7 @@ use super::{install, watch::WatchArgs}; use clap::Parser; use eyre::Result; -use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_cli::{opts::BuildOpts, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, shell}; use foundry_compilers::{ compilers::{multi::MultiCompilerLanguage, Language}, @@ -20,7 +20,7 @@ use foundry_config::{ use serde::Serialize; use std::path::PathBuf; -foundry_config::merge_impl_figment_convert!(BuildArgs, args); +foundry_config::merge_impl_figment_convert!(BuildArgs, build); /// CLI arguments for `forge build`. /// @@ -68,7 +68,7 @@ pub struct BuildArgs { #[command(flatten)] #[serde(flatten)] - pub args: CoreBuildArgs, + pub build: BuildOpts, #[command(flatten)] #[serde(skip)] @@ -165,7 +165,7 @@ impl BuildArgs { /// [`utils::find_project_root`] and merges the cli `BuildArgs` into it before returning /// [`foundry_config::Config::project()`] pub fn project(&self) -> Result { - self.args.project() + self.build.project() } /// Returns whether `BuildArgs` was configured with `--watch` diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 07fc47290..fd502dd7b 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -87,12 +87,12 @@ pub struct CloneArgs { pub etherscan: EtherscanOpts, #[command(flatten)] - pub opts: DependencyInstallOpts, + pub install: DependencyInstallOpts, } impl CloneArgs { pub async fn run(self) -> Result<()> { - let Self { address, root, opts, etherscan, no_remappings_txt, keep_directory_structure } = + let Self { address, root, install, etherscan, no_remappings_txt, keep_directory_structure } = self; // step 0. get the chain and api key from the config @@ -107,7 +107,7 @@ impl CloneArgs { let meta = Self::collect_metadata_from_client(address, &client).await?; // step 2. initialize an empty project - Self::init_an_empty_project(&root, opts)?; + Self::init_an_empty_project(&root, install)?; // canonicalize the root path // note that at this point, the root directory must have been created let root = dunce::canonicalize(&root)?; @@ -127,7 +127,7 @@ impl CloneArgs { Self::collect_compilation_metadata(&meta, chain, address, &root, &client).await?; // step 5. git add and commit the changes if needed - if !opts.no_commit { + if !install.no_commit { let git = Git::new(&root); git.add(Some("--all"))?; let msg = format!("chore: forge clone {address}"); @@ -157,9 +157,9 @@ impl CloneArgs { /// * `root` - the root directory of the project. /// * `enable_git` - whether to enable git for the project. /// * `quiet` - whether to print messages. - pub(crate) fn init_an_empty_project(root: &Path, opts: DependencyInstallOpts) -> Result<()> { + pub(crate) fn init_an_empty_project(root: &Path, install: DependencyInstallOpts) -> Result<()> { // let's try to init the project with default init args - let init_args = InitArgs { root: root.to_path_buf(), opts, ..Default::default() }; + let init_args = InitArgs { root: root.to_path_buf(), install, ..Default::default() }; init_args.run().map_err(|e| eyre::eyre!("Project init error: {:?}", e))?; // remove the unnecessary example contracts diff --git a/crates/forge/bin/cmd/config.rs b/crates/forge/bin/cmd/config.rs index 0aa1fdb63..42ab29ec2 100644 --- a/crates/forge/bin/cmd/config.rs +++ b/crates/forge/bin/cmd/config.rs @@ -5,7 +5,7 @@ use foundry_cli::utils::LoadConfig; use foundry_common::{evm::EvmArgs, shell}; use foundry_config::fix::fix_tomls; -foundry_config::impl_figment_convert!(ConfigArgs, opts, evm_args); +foundry_config::impl_figment_convert!(ConfigArgs, build, evm); /// CLI arguments for `forge config`. #[derive(Clone, Debug, Parser)] @@ -20,10 +20,10 @@ pub struct ConfigArgs { // support nested build arguments #[command(flatten)] - opts: BuildArgs, + build: BuildArgs, #[command(flatten)] - evm_args: EvmArgs, + evm: EvmArgs, } impl ConfigArgs { diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 45694252e..470d185a8 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -21,6 +21,8 @@ use forge_verify::{zk_provider::CompilerVerificationContext, RetryArgs, Verifier use foundry_cli::{ opts::{CoreBuildArgs, EthereumOpts, EtherscanOpts, TransactionOpts}, utils::{self, read_constructor_args_file, remove_contract, remove_zk_contract, LoadConfig}, + opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, + utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, }; use foundry_common::{ compile::{self, ProjectCompiler}, @@ -49,7 +51,7 @@ use std::{ sync::Arc, }; -merge_impl_figment_convert!(CreateArgs, opts, eth); +merge_impl_figment_convert!(CreateArgs, build, eth); /// CLI arguments for `forge create`. #[derive(Clone, Debug, Parser)] @@ -100,7 +102,7 @@ pub struct CreateArgs { pub timeout: Option, #[command(flatten)] - opts: CoreBuildArgs, + build: BuildOpts, #[command(flatten)] tx: TransactionOpts, @@ -431,11 +433,11 @@ impl CreateArgs { skip_is_verified_check: true, watch: true, retry: self.retry, - libraries: self.opts.libraries.clone(), + libraries: self.build.libraries.clone(), root: None, verifier: self.verifier.clone(), - via_ir: self.opts.via_ir, - evm_version: self.opts.compiler.evm_version, + via_ir: self.build.via_ir, + evm_version: self.build.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, guess_constructor_args: false, compilation_profile: Some(id.profile.to_string()), @@ -597,8 +599,8 @@ impl CreateArgs { sh_println!("Starting contract verification...")?; - let num_of_optimizations = if self.opts.compiler.optimize.unwrap_or_default() { - self.opts.compiler.optimizer_runs + let num_of_optimizations = if self.build.compiler.optimize.unwrap_or_default() { + self.build.compiler.optimizer_runs } else { None }; @@ -616,11 +618,11 @@ impl CreateArgs { skip_is_verified_check: true, watch: true, retry: self.retry, - libraries: self.opts.libraries.clone(), + libraries: self.build.libraries.clone(), root: None, verifier: self.verifier, - via_ir: self.opts.via_ir, - evm_version: self.opts.compiler.evm_version, + via_ir: self.build.via_ir, + evm_version: self.build.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, guess_constructor_args: false, compilation_profile: Some(id.profile.to_string()), diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs deleted file mode 100644 index 5ccfc13d5..000000000 --- a/crates/forge/bin/cmd/debug.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clap::{Parser, ValueHint}; -use forge_script::ScriptArgs; -use forge_verify::retry::RETRY_VERIFY_ON_CREATE; -use foundry_cli::opts::CoreBuildArgs; -use foundry_common::evm::EvmArgs; -use std::path::PathBuf; - -// Loads project's figment and merges the build cli arguments into it -foundry_config::impl_figment_convert!(DebugArgs, opts, evm_args); - -/// CLI arguments for `forge debug`. -#[derive(Clone, Debug, Parser)] -pub struct DebugArgs { - /// The contract you want to run. Either the file path or contract name. - /// - /// If multiple contracts exist in the same file you must specify the target contract with - /// --target-contract. - #[arg(value_hint = ValueHint::FilePath)] - pub path: PathBuf, - - /// Arguments to pass to the script function. - pub args: Vec, - - /// The name of the contract you want to run. - #[arg(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] - pub target_contract: Option, - - /// The signature of the function you want to call in the contract, or raw calldata. - #[arg(long, short, default_value = "run()", value_name = "SIGNATURE")] - pub sig: String, - - /// Open the script in the debugger. - #[arg(long)] - pub debug: bool, - - /// File path to dump execution details as JSON. - #[arg( - long, - requires = "debug", - value_hint = ValueHint::FilePath, - value_name = "PATH" - )] - pub dump: Option, - - #[command(flatten)] - pub opts: CoreBuildArgs, - - #[command(flatten)] - pub evm_args: EvmArgs, -} - -impl DebugArgs { - pub async fn run(self) -> eyre::Result<()> { - let script = ScriptArgs { - path: self.path.to_str().expect("Invalid path string.").to_string(), - args: self.args, - target_contract: self.target_contract, - sig: self.sig, - gas_estimate_multiplier: 130, - opts: self.opts, - evm_args: self.evm_args, - debug: true, - dump: self.dump, - retry: RETRY_VERIFY_ON_CREATE, - ..Default::default() - }; - script.run_script().await - } -} diff --git a/crates/forge/bin/cmd/eip712.rs b/crates/forge/bin/cmd/eip712.rs index 5a7a3a229..86a6d57c3 100644 --- a/crates/forge/bin/cmd/eip712.rs +++ b/crates/forge/bin/cmd/eip712.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueHint}; use eyre::{Ok, OptionExt, Result}; -use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; +use foundry_cli::{opts::BuildOpts, utils::LoadConfig}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::artifacts::{ output_selection::OutputSelection, @@ -9,7 +9,7 @@ use foundry_compilers::artifacts::{ }; use std::{collections::BTreeMap, fmt::Write, path::PathBuf}; -foundry_config::impl_figment_convert!(Eip712Args, opts); +foundry_config::impl_figment_convert!(Eip712Args, build); /// CLI arguments for `forge eip712`. #[derive(Clone, Debug, Parser)] @@ -19,7 +19,7 @@ pub struct Eip712Args { pub target_path: PathBuf, #[command(flatten)] - opts: CoreBuildArgs, + build: BuildOpts, } impl Eip712Args { diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 7bbb0d1e2..3a3fb905e 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueHint}; use eyre::Result; use foundry_cli::{ - opts::{CoreBuildArgs, ProjectPathsArgs}, + opts::{BuildOpts, ProjectPathOpts}, utils::LoadConfig, }; use foundry_common::{compile::with_compilation_reporter, fs}; @@ -31,7 +31,7 @@ pub struct FlattenArgs { pub output: Option, #[command(flatten)] - project_paths: ProjectPathsArgs, + project_paths: ProjectPathOpts, } impl FlattenArgs { @@ -39,8 +39,8 @@ impl FlattenArgs { let Self { target_path, output, project_paths } = self; // flatten is a subset of `BuildArgs` so we can reuse that to get the config - let build_args = CoreBuildArgs { project_paths, ..Default::default() }; - let config = build_args.try_load_config_emit_warnings()?; + let build = BuildOpts { project_paths, ..Default::default() }; + let config = build.try_load_config_emit_warnings()?; let project = config.create_project(false, true)?; let target_path = dunce::canonicalize(target_path)?; diff --git a/crates/forge/bin/cmd/geiger.rs b/crates/forge/bin/cmd/geiger.rs index 6d4c735a9..ace3bbe1a 100644 --- a/crates/forge/bin/cmd/geiger.rs +++ b/crates/forge/bin/cmd/geiger.rs @@ -4,9 +4,11 @@ use foundry_cli::utils::LoadConfig; use foundry_compilers::{resolver::parse::SolData, Graph}; use foundry_config::{impl_figment_convert_basic, Config}; use itertools::Itertools; -use solar_ast::visit::Visit; -use solar_parse::{ast, interface::Session}; -use std::path::{Path, PathBuf}; +use solar_parse::{ast, ast::visit::Visit, interface::Session}; +use std::{ + ops::ControlFlow, + path::{Path, PathBuf}, +}; /// CLI arguments for `forge geiger`. #[derive(Clone, Debug, Parser)] @@ -144,7 +146,9 @@ impl<'a> Visitor<'a> { } impl<'ast> Visit<'ast> for Visitor<'_> { - fn visit_expr(&mut self, expr: &'ast ast::Expr<'ast>) { + type BreakValue = solar_parse::interface::data_structures::Never; + + fn visit_expr(&mut self, expr: &'ast ast::Expr<'ast>) -> ControlFlow { if let ast::ExprKind::Call(lhs, _args) = &expr.kind { if let ast::ExprKind::Member(_lhs, member) = &lhs.kind { if self.unsafe_cheatcodes.iter().any(|c| c.as_str() == member.as_str()) { @@ -154,6 +158,6 @@ impl<'ast> Visit<'ast> for Visitor<'_> { } } } - self.walk_expr(expr); + self.walk_expr(expr) } } diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index ae8341e02..ccb9de1e7 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -38,7 +38,7 @@ pub struct InitArgs { pub vscode: bool, #[command(flatten)] - pub opts: DependencyInstallOpts, + pub install: DependencyInstallOpts, } impl InitArgs { @@ -134,7 +134,7 @@ impl InitArgs { if !dest.exists() { fs::write(dest, config.clone().into_basic().to_string_pretty()?)?; } - let git = self.opts.git(&config); + let git = self.install.git(&config); // set up the repo if !no_git { @@ -145,10 +145,10 @@ impl InitArgs { if !offline { if root.join("lib/forge-std").exists() { sh_warn!("\"lib/forge-std\" already exists, skipping install...")?; - self.opts.install(&mut config, vec![])?; + self.install.install(&mut config, vec![])?; } else { let dep = "https://github.com/foundry-rs/forge-std".parse()?; - self.opts.install(&mut config, vec![dep])?; + self.install.install(&mut config, vec![dep])?; } } diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index 426a8b36e..d1836c9bc 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -3,7 +3,7 @@ use clap::Parser; use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Cell, Table}; use eyre::{Context, Result}; use forge::revm::primitives::Eof; -use foundry_cli::opts::{CompilerArgs, CoreBuildArgs}; +use foundry_cli::opts::{BuildOpts, CompilerOpts}; use foundry_common::{compile::ProjectCompiler, fmt::pretty_eof, shell}; use foundry_compilers::{ artifacts::{ @@ -35,7 +35,7 @@ pub struct InspectArgs { /// All build arguments are supported #[command(flatten)] - build: CoreBuildArgs, + build: BuildOpts, } impl InspectArgs { @@ -58,8 +58,8 @@ impl InspectArgs { }; // Build modified Args - let modified_build_args = CoreBuildArgs { - compiler: CompilerArgs { extra_output: cos, optimize: optimized, ..build.compiler }, + let modified_build_args = BuildOpts { + compiler: CompilerOpts { extra_output: cos, optimize: optimized, ..build.compiler }, ..build }; diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index 427b25fb0..6885819f8 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -24,9 +24,9 @@ //! #[derive(Clone, Debug, Parser)] //! pub struct MyArgs { //! #[command(flatten)] -//! evm_args: EvmArgs, +//! evm: EvmArgs, //! #[command(flatten)] -//! opts: BuildArgs, +//! build: BuildArgs, //! } //! //! // add `Figment` and `Config` converters @@ -48,7 +48,6 @@ pub mod compiler; pub mod config; pub mod coverage; pub mod create; -pub mod debug; pub mod doc; pub mod eip712; pub mod flatten; diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 31992983d..56c25cc00 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -3,7 +3,7 @@ use clap::Parser; use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Table}; use eyre::Result; use foundry_cli::{ - opts::{CompilerArgs, CoreBuildArgs, ProjectPathsArgs}, + opts::{BuildOpts, CompilerOpts, ProjectPathOpts}, utils::{cache_local_signatures, FoundryPathExt}, }; use foundry_common::{ @@ -29,7 +29,7 @@ pub enum SelectorsSubcommands { second_contract: ContractInfo, #[command(flatten)] - build: Box, + build: Box, }, /// Upload selectors to registry @@ -44,7 +44,7 @@ pub enum SelectorsSubcommands { all: bool, #[command(flatten)] - project_paths: ProjectPathsArgs, + project_paths: ProjectPathOpts, }, /// List selectors from current workspace @@ -55,7 +55,7 @@ pub enum SelectorsSubcommands { contract: Option, #[command(flatten)] - project_paths: ProjectPathsArgs, + project_paths: ProjectPathOpts, }, /// Find if a selector is present in the project @@ -66,14 +66,14 @@ pub enum SelectorsSubcommands { selector: String, #[command(flatten)] - project_paths: ProjectPathsArgs, + project_paths: ProjectPathOpts, }, /// Cache project selectors (enables trace with local contracts functions and events). #[command(visible_alias = "c")] Cache { #[command(flatten)] - project_paths: ProjectPathsArgs, + project_paths: ProjectPathOpts, }, } @@ -82,9 +82,9 @@ impl SelectorsSubcommands { match self { Self::Cache { project_paths } => { sh_println!("Caching selectors for contracts in the project...")?; - let build_args = CoreBuildArgs { + let build_args = BuildOpts { project_paths, - compiler: CompilerArgs { + compiler: CompilerOpts { extra_output: vec![ContractOutputSelection::Abi], ..Default::default() }, @@ -97,9 +97,9 @@ impl SelectorsSubcommands { cache_local_signatures(&outcome, Config::foundry_cache_dir().unwrap())? } Self::Upload { contract, all, project_paths } => { - let build_args = CoreBuildArgs { + let build_args = BuildOpts { project_paths: project_paths.clone(), - compiler: CompilerArgs { + compiler: CompilerOpts { extra_output: vec![ContractOutputSelection::Abi], ..Default::default() }, @@ -213,9 +213,9 @@ impl SelectorsSubcommands { } Self::List { contract, project_paths } => { sh_println!("Listing selectors for contracts in the project...")?; - let build_args = CoreBuildArgs { + let build_args = BuildOpts { project_paths, - compiler: CompilerArgs { + compiler: CompilerOpts { extra_output: vec![ContractOutputSelection::Abi], ..Default::default() }, @@ -301,9 +301,9 @@ impl SelectorsSubcommands { Self::Find { selector, project_paths } => { sh_println!("Searching for selector {selector:?} in the project...")?; - let build_args = CoreBuildArgs { + let build_args = BuildOpts { project_paths, - compiler: CompilerArgs { + compiler: CompilerOpts { extra_output: vec![ContractOutputSelection::Abi], ..Default::default() }, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index e7dd34cbc..128474aed 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -17,7 +17,7 @@ use forge::{ MultiContractRunner, MultiContractRunnerBuilder, TestFilter, }; use foundry_cli::{ - opts::{CoreBuildArgs, GlobalOpts}, + opts::{BuildOpts, GlobalArgs}, utils::{self, LoadConfig}, }; use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs, shell, TestFunctionExt}; @@ -60,7 +60,7 @@ use quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite}; use summary::{print_invariant_metrics, TestSummaryReport}; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_args); +foundry_config::merge_impl_figment_convert!(TestArgs, build, evm); /// CLI arguments for `forge test`. #[derive(Clone, Debug, Parser)] @@ -68,7 +68,7 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_args); pub struct TestArgs { // Include global options for users of this struct. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, /// The contract file you want to test, it's a shortcut for --match-path. #[arg(value_hint = ValueHint::FilePath)] @@ -158,23 +158,11 @@ pub struct TestArgs { #[arg(long, conflicts_with_all = ["quiet", "json"], help_heading = "Display options")] pub show_progress: bool, - #[command(flatten)] - filter: FilterArgs, - /// Re-run recorded test failures from last run. /// If no failure recorded then regular test run is performed. #[arg(long)] pub rerun: bool, - #[command(flatten)] - evm_args: EvmArgs, - - #[command(flatten)] - opts: CoreBuildArgs, - - #[command(flatten)] - pub watch: WatchArgs, - /// Print test summary table. #[arg(long, help_heading = "Display options")] pub summary: bool, @@ -182,14 +170,21 @@ pub struct TestArgs { /// Print detailed test summary table. #[arg(long, help_heading = "Display options", requires = "summary")] pub detailed: bool, + + #[command(flatten)] + filter: FilterArgs, + + #[command(flatten)] + evm: EvmArgs, + + #[command(flatten)] + pub build: BuildOpts, + + #[command(flatten)] + pub watch: WatchArgs, } impl TestArgs { - /// Returns the flattened [`CoreBuildArgs`]. - pub fn build_args(&self) -> &CoreBuildArgs { - &self.opts - } - pub async fn run(self) -> Result { trace!(target: "forge::test", "executing test command"); self.execute_tests().await @@ -1024,7 +1019,7 @@ mod tests { fn extract_chain() { let test = |arg: &str, expected: Chain| { let args = TestArgs::parse_from(["foundry-cli", arg]); - assert_eq!(args.evm_args.env.chain, Some(expected)); + assert_eq!(args.evm.env.chain, Some(expected)); let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); assert_eq!(config.chain, Some(expected)); assert_eq!(evm_opts.env.chain_id, Some(expected.id())); diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index 088975d87..fe278e98c 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -1,6 +1,6 @@ use clap::Parser; use eyre::Result; -use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; +use foundry_cli::{opts::ProjectPathOpts, utils::LoadConfig}; use foundry_compilers::{ resolver::{parse::SolData, Charset, TreeOptions}, Graph, @@ -20,10 +20,10 @@ pub struct TreeArgs { charset: Charset, #[command(flatten)] - opts: ProjectPathsArgs, + project_paths: ProjectPathOpts, } -foundry_config::impl_figment_convert!(TreeArgs, opts); +foundry_config::impl_figment_convert!(TreeArgs, project_paths); impl TreeArgs { pub fn run(self) -> Result<()> { diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index b8406565c..0550d4562 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -259,7 +259,7 @@ pub async fn watch_gas_snapshot(args: GasSnapshotArgs) -> Result<()> { /// Executes a [`Watchexec`] that listens for changes in the project's src dir and reruns `forge /// test` pub async fn watch_test(args: TestArgs) -> Result<()> { - let config: Config = args.build_args().into(); + let config: Config = Config::from(&args.build); let filter = args.filter(&config); // Marker to check whether to override the command. let no_reconfigure = filter.args().test_pattern.is_some() || diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 4b8080b42..e8a06c2ba 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -58,7 +58,6 @@ fn run() -> Result<()> { cmd.run().map(drop) } } - ForgeSubcommand::Debug(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::VerifyContract(args) => utils::block_on(args.run()), ForgeSubcommand::VerifyCheck(args) => utils::block_on(args.run()), ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 380cb61d4..e211d03b7 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -1,14 +1,14 @@ use crate::cmd::{ bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, - compiler::CompilerArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, - eip712, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs, + compiler::CompilerArgs, config, coverage, create::CreateArgs, doc::DocArgs, eip712, flatten, + fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; use forge_verify::{VerifyArgs, VerifyBytecodeArgs, VerifyCheckArgs}; -use foundry_cli::opts::GlobalOpts; +use foundry_cli::opts::GlobalArgs; use std::path::PathBuf; const VERSION_MESSAGE: &str = concat!( @@ -29,9 +29,9 @@ const VERSION_MESSAGE: &str = concat!( next_display_order = None, )] pub struct Forge { - /// Include the global options. + /// Include the global arguments. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, #[command(subcommand)] pub cmd: ForgeSubcommand, @@ -61,10 +61,6 @@ pub enum ForgeSubcommand { /// Clone a contract from Etherscan. Clone(CloneArgs), - /// Debugs a single smart contract as a script. - #[command(visible_alias = "d")] - Debug(DebugArgs), - /// Update one or multiple dependencies. /// /// If no arguments are provided, then all dependencies are updated. diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 496cda0ca..b087a8160 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -415,6 +415,23 @@ impl<'a> ContractRunner<'a> { .collect::>(); let duration = start.elapsed(); + let test_fail_deprecations = self + .contract + .abi + .functions() + .filter_map(|func| { + TestFunctionKind::classify(&func.name, !func.inputs.is_empty()) + .is_any_test_fail() + .then_some(func.name.clone()) + }) + .collect::>() + .join(", "); + + if !test_fail_deprecations.is_empty() { + warnings.push(format!( + "`testFail*` has been deprecated and will be removed in the next release. Consider changing to test_Revert[If|When]_Condition and expecting a revert. Found deprecated testFail* function(s): {test_fail_deprecations}.", + )); + } SuiteResult::new(duration, test_results, warnings) } } diff --git a/crates/forge/tests/cli/bind_json.rs b/crates/forge/tests/cli/bind_json.rs index bdc8f0fa1..fcc081f6b 100644 --- a/crates/forge/tests/cli/bind_json.rs +++ b/crates/forge/tests/cli/bind_json.rs @@ -1,3 +1,5 @@ +use foundry_test_utils::snapbox; + // tests complete bind-json workflow // ensures that we can run forge-bind even if files are depending on yet non-existent bindings and // that generated bindings are correct @@ -50,5 +52,74 @@ contract BindJsonTest is Test { .unwrap(); cmd.arg("bind-json").assert_success(); + + snapbox::assert_data_eq!( + snapbox::Data::read_from(&prj.root().join("utils/JsonBindings.sol"), None), + snapbox::str![[r#" +// Automatically generated by forge bind-json. + +pragma solidity >=0.6.2 <0.9.0; +pragma experimental ABIEncoderV2; + +import {BindJsonTest, TopLevelStruct} from "test/JsonBindings.sol"; + +interface Vm { + function parseJsonTypeArray(string calldata json, string calldata key, string calldata typeDescription) external pure returns (bytes memory); + function parseJsonType(string calldata json, string calldata typeDescription) external pure returns (bytes memory); + function parseJsonType(string calldata json, string calldata key, string calldata typeDescription) external pure returns (bytes memory); + function serializeJsonType(string calldata typeDescription, bytes memory value) external pure returns (string memory json); + function serializeJsonType(string calldata objectKey, string calldata valueKey, string calldata typeDescription, bytes memory value) external returns (string memory json); +} + +library JsonBindings { + Vm constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + string constant schema_TopLevelStruct = "TopLevelStruct(uint256 param1,int8 param2)"; + string constant schema_ContractLevelStruct = "ContractLevelStruct(address[][] param1,address addrParam)"; + + function serialize(TopLevelStruct memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_TopLevelStruct, abi.encode(value)); + } + + function serialize(TopLevelStruct memory value, string memory objectKey, string memory valueKey) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_TopLevelStruct, abi.encode(value)); + } + + function deserializeTopLevelStruct(string memory json) public pure returns (TopLevelStruct memory) { + return abi.decode(vm.parseJsonType(json, schema_TopLevelStruct), (TopLevelStruct)); + } + + function deserializeTopLevelStruct(string memory json, string memory path) public pure returns (TopLevelStruct memory) { + return abi.decode(vm.parseJsonType(json, path, schema_TopLevelStruct), (TopLevelStruct)); + } + + function deserializeTopLevelStructArray(string memory json, string memory path) public pure returns (TopLevelStruct[] memory) { + return abi.decode(vm.parseJsonTypeArray(json, path, schema_TopLevelStruct), (TopLevelStruct[])); + } + + function serialize(BindJsonTest.ContractLevelStruct memory value) internal pure returns (string memory) { + return vm.serializeJsonType(schema_ContractLevelStruct, abi.encode(value)); + } + + function serialize(BindJsonTest.ContractLevelStruct memory value, string memory objectKey, string memory valueKey) internal returns (string memory) { + return vm.serializeJsonType(objectKey, valueKey, schema_ContractLevelStruct, abi.encode(value)); + } + + function deserializeContractLevelStruct(string memory json) public pure returns (BindJsonTest.ContractLevelStruct memory) { + return abi.decode(vm.parseJsonType(json, schema_ContractLevelStruct), (BindJsonTest.ContractLevelStruct)); + } + + function deserializeContractLevelStruct(string memory json, string memory path) public pure returns (BindJsonTest.ContractLevelStruct memory) { + return abi.decode(vm.parseJsonType(json, path, schema_ContractLevelStruct), (BindJsonTest.ContractLevelStruct)); + } + + function deserializeContractLevelStructArray(string memory json, string memory path) public pure returns (BindJsonTest.ContractLevelStruct[] memory) { + return abi.decode(vm.parseJsonTypeArray(json, path, schema_ContractLevelStruct), (BindJsonTest.ContractLevelStruct[])); + } +} + +"#]], + ); + cmd.forge_fuse().args(["test"]).assert_success(); }); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 9a9771975..cc378f8c2 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1136,7 +1136,6 @@ Warning (1878): SPDX license identifier not provided in source file. Before publ Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. [FILE] - "#]]); // ignores error code and compiles @@ -1216,7 +1215,6 @@ Error (2314): Expected ';' but got identifier 7 | THIS WILL CAUSE AN ERROR | ^^^^^ - "#]]); // but ensure this cleaned cache and artifacts @@ -1232,7 +1230,6 @@ Error (2314): Expected ';' but got identifier 7 | THIS WILL CAUSE AN ERROR | ^^^^^ - "#]]); // resolve the error by replacing the file @@ -1272,7 +1269,6 @@ Error (2314): Expected ';' but got identifier 7 | THIS WILL CAUSE AN ERROR | ^^^^^ - "#]]); // ensure unchanged cache file @@ -2796,7 +2792,7 @@ contract NestedDeploy is Test { +============================================================================================+ | Deployment Cost | Deployment Size | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| -| 251997 | 739 | | | | | +| 251985 | 739 | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| | | | | | | | |-------------------------------------------+-----------------+-----+--------+-----+---------| @@ -2851,7 +2847,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) { "contract": "test/NestedDeployTest.sol:Parent", "deployment": { - "gas": 251997, + "gas": 251985, "size": 739 }, "functions": { diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index b80ec8819..cff6852cb 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -449,7 +449,6 @@ Error (6553): The msize instruction cannot be used when the Yul optimizer is act 6 | assembly { | ^ (Relevant source part starts here and spans across multiple lines). - "#]]); // disable yul optimizer explicitly diff --git a/crates/forge/tests/cli/inline_config.rs b/crates/forge/tests/cli/inline_config.rs index 5e0273195..085cc88a8 100644 --- a/crates/forge/tests/cli/inline_config.rs +++ b/crates/forge/tests/cli/inline_config.rs @@ -65,7 +65,7 @@ forgetest!(invalid_profile, |prj, cmd| { .unwrap(); cmd.arg("test").assert_failure().stderr_eq(str![[r#" -Error: Inline config error at test/inline.sol:0:0:0: invalid profile `unknown.fuzz.runs = 2`; valid profiles: default +Error: Inline config error at test/inline.sol:80:123:0: invalid profile `unknown.fuzz.runs = 2`; valid profiles: default "#]]); }); @@ -254,14 +254,14 @@ forgetest_init!(evm_version, |prj, cmd| { ) .unwrap(); - cmd.arg("test").arg("--evm-version=cancun").assert_success().stdout_eq(str![[r#" + cmd.args(["test", "--evm-version=cancun", "-j1"]).assert_success().stdout_eq(str![[r#" ... -Ran 2 tests for test/inline.sol:FunctionConfig +Ran 2 tests for test/inline.sol:ContractConfig [PASS] test_new() ([GAS]) [PASS] test_old() ([GAS]) Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] -Ran 2 tests for test/inline.sol:ContractConfig +Ran 2 tests for test/inline.sol:FunctionConfig [PASS] test_new() ([GAS]) [PASS] test_old() ([GAS]) Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 9cf3e746c..09df55668 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1956,7 +1956,7 @@ contract SimpleScript is Script { ]) .assert_success() .stdout_eq(str![[r#" -{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"0x6080604052600c805462ff00ff191662010001179055348015601f575f5ffd5b506101568061002d5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","output":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122060ba6332e526de9b6bc731fb4682b44e42845196324ec33068982984d700cdd964736f6c634300081b0033","gas_used":90639,"gas_limit":1073682810,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":3214,"gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":0,"gas_limit":1056940983,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1056940820,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":24278,"labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} +{"logs":[],"returns":{"success":{"internal_type":"bool","value":"true"}},"success":true,"raw_logs":[],"traces":[["Deployment",{"arena":[{"parent":null,"children":[],"idx":0,"trace":{"depth":0,"success":true,"caller":"0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":false,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CREATE","value":"0x0","data":"0x6080604052600c805462ff00ff191662010001179055348015601f575f5ffd5b506101568061002d5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122051a3965709e156763fe3847b1a8c4c2e1f5ad2088ccbc31509b98951c018fc8764736f6c634300081b0033","output":"0x608060405234801561000f575f5ffd5b5060043610610034575f3560e01c8063c040622614610038578063f8ccbf4714610054575b5f5ffd5b610040610067565b604051901515815260200160405180910390f35b600c546100409062010000900460ff1681565b5f7f885cb69240a935d632d79c317109709ecfa91a80626ff3989d68f67f5b1dd12d5f1c6001600160a01b0316637fb5297f6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156100c2575f5ffd5b505af11580156100d4573d5f5f3e3d5ffd5b50506040515f925090508181818181805af19150503d805f8114610113576040519150601f19603f3d011682016040523d82523d5f602084013e610118565b606091505b50909291505056fea264697066735822122051a3965709e156763fe3847b1a8c4c2e1f5ad2088ccbc31509b98951c018fc8764736f6c634300081b0033","gas_used":90639,"gas_limit":1073682798,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}],["Execution",{"arena":[{"parent":null,"children":[1,2],"idx":0,"trace":{"depth":0,"success":true,"caller":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","address":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0xc0406226","output":"0x0000000000000000000000000000000000000000000000000000000000000001","gas_used":3214,"gas_limit":1073720760,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[{"Call":0},{"Call":1}]},{"parent":0,"children":[],"idx":1,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x7109709ecfa91a80626ff3989d68f67f5b1dd12d","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x7fb5297f","output":"0x","gas_used":0,"gas_limit":1056940983,"status":"Return","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]},{"parent":0,"children":[],"idx":2,"trace":{"depth":1,"success":true,"caller":"0x5b73c5498c1e3b4dba84de0f1833c4a029d90519","address":"0x0000000000000000000000000000000000000000","maybe_precompile":null,"selfdestruct_address":null,"selfdestruct_refund_target":null,"selfdestruct_transferred_value":null,"kind":"CALL","value":"0x0","data":"0x","output":"0x","gas_used":0,"gas_limit":1056940820,"status":"Stop","steps":[],"decoded":{"label":null,"return_data":null,"call_data":null}},"logs":[],"ordering":[]}]}]],"gas_used":24278,"labeled_addresses":{},"returned":"0x0000000000000000000000000000000000000000000000000000000000000001","address":null} {"chain":31337,"estimated_gas_price":"2.000000001","estimated_total_gas_used":29005,"estimated_amount_required":"0.000058010000029005"} {"chain":"anvil-hardhat","status":"success","tx_hash":"0x4f78afe915fceb282c7625a68eb350bc0bf78acb59ad893e5c62b710a37f3156","contract_address":null,"block_number":1,"gas_used":21000,"gas_price":1000000001} {"status":"success","transactions":"[..]/broadcast/Foo.sol/31337/run-latest.json","sensitive":"[..]/cache/Foo.sol/31337/run-latest.json"} diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index e8da6a490..20f865fc9 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -2807,3 +2807,30 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests) "#]], ); }); + +forgetest!(test_fail_deprecation_warning, |prj, cmd| { + prj.insert_ds_test(); + + prj.add_source( + "WarnDeprecationTestFail.t.sol", + r#" + import "./test.sol"; + contract WarnDeprecationTestFail is DSTest { + function testFail_deprecated() public { + revert("deprecated"); + } + + function testFail_deprecated2() public { + revert("deprecated2"); + } + } + "#, + ) + .unwrap(); + + cmd.forge_fuse() + .args(["test", "--mc", "WarnDeprecationTestFail"]) + .assert_success() + .stderr_eq(r#"Warning: `testFail*` has been deprecated and will be removed in the next release. Consider changing to test_Revert[If|When]_Condition and expecting a revert. Found deprecated testFail* function(s): testFail_deprecated, testFail_deprecated2. +"#); +}); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index fb7114af3..ebcf30e21 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -388,3 +388,6 @@ test_repro!(8971; |config| { // https://github.com/foundry-rs/foundry/issues/8639 test_repro!(8639); + +// https://github.com/foundry-rs/foundry/issues/8566 +test_repro!(8566); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index d7472c6a7..d93c4f311 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -18,6 +18,8 @@ use foundry_config::{ zksync::{ZKSYNC_ARTIFACTS_DIR, ZKSYNC_SOLIDITY_FILES_CACHE_FILENAME}, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, InvariantConfig, RpcEndpoint, RpcEndpoints, + fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, + InvariantConfig, RpcEndpointUrl, RpcEndpoints, }; use foundry_evm::{constants::CALLER, opts::EvmOpts}; use foundry_test_utils::{ @@ -518,15 +520,15 @@ pub fn manifest_root() -> &'static Path { /// the RPC endpoints used during tests pub fn rpc_endpoints() -> RpcEndpoints { RpcEndpoints::new([ - ("mainnet", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))), - ("mainnet2", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Mainnet))), - ("sepolia", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Sepolia))), - ("optimism", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Optimism))), - ("arbitrum", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Arbitrum))), - ("polygon", RpcEndpoint::Url(next_rpc_endpoint(NamedChain::Polygon))), - ("avaxTestnet", RpcEndpoint::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), - ("moonbeam", RpcEndpoint::Url("https://moonbeam-rpc.publicnode.com".into())), - ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".into())), + ("mainnet", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Mainnet))), + ("mainnet2", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Mainnet))), + ("sepolia", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Sepolia))), + ("optimism", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Optimism))), + ("arbitrum", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Arbitrum))), + ("polygon", RpcEndpointUrl::Url(next_rpc_endpoint(NamedChain::Polygon))), + ("avaxTestnet", RpcEndpointUrl::Url("https://api.avax-test.network/ext/bc/C/rpc".into())), + ("moonbeam", RpcEndpointUrl::Url("https://moonbeam-rpc.publicnode.com".into())), + ("rpcEnvAlias", RpcEndpointUrl::Env("${RPC_ENV_ALIAS}".into())), ]) } diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 77174e365..6b739e9e1 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -458,11 +458,11 @@ impl BundledState { let (total_gas, total_gas_price, total_paid) = sequence.receipts.iter().fold((0, 0, 0), |acc, receipt| { let gas_used = receipt.gas_used; - let gas_price = receipt.effective_gas_price; + let gas_price = receipt.effective_gas_price as u64; (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used * gas_price) }); let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); - let avg_gas_price = format_units(total_gas_price / sequence.receipts.len() as u128, 9) + let avg_gas_price = format_units(total_gas_price / sequence.receipts.len() as u64, 9) .unwrap_or_else(|_| "N/A".to_string()); seq_progress.inner.write().set_status(&format!( diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index f4a57c07a..9c10d83c4 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -189,7 +189,7 @@ impl PreExecutionState { if let Some(txs) = transactions { // If the user passed a `--sender` don't check anything. if self.build_data.predeploy_libraries.libraries_count() > 0 && - self.args.evm_args.sender.is_none() + self.args.evm.sender.is_none() { for tx in txs.iter() { if tx.transaction.to().is_none() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 16dd31bc4..3cd07b6f0 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -25,10 +25,12 @@ use clap::{Parser, ValueHint}; use dialoguer::Confirm; use eyre::{ContextCompat, Result}; use forge_script_sequence::{AdditionalContract, NestedValue}; -use forge_verify::RetryArgs; +use forge_verify::{RetryArgs, VerifierArgs}; use foundry_cli::{ opts::{CoreBuildArgs, GlobalOpts}, utils::{self, LoadConfig}, + opts::{BuildOpts, GlobalArgs}, + utils::LoadConfig, }; use foundry_common::{ abi::{encode_function_args, get_func}, @@ -73,14 +75,14 @@ mod transaction; mod verify; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(ScriptArgs, opts, evm_args); +foundry_config::merge_impl_figment_convert!(ScriptArgs, build, evm); /// CLI arguments for `forge script`. #[derive(Clone, Debug, Default, Parser)] pub struct ScriptArgs { // Include global options for users of this struct. #[command(flatten)] - pub global: GlobalOpts, + pub global: GlobalArgs, /// The contract you want to run. Either the file path or contract name. /// @@ -204,16 +206,16 @@ pub struct ScriptArgs { pub timeout: Option, #[command(flatten)] - pub opts: CoreBuildArgs, + pub build: BuildOpts, #[command(flatten)] pub wallets: MultiWalletOpts, #[command(flatten)] - pub evm_args: EvmArgs, + pub evm: EvmArgs, #[command(flatten)] - pub verifier: forge_verify::VerifierArgs, + pub verifier: VerifierArgs, #[command(flatten)] pub retry: RetryArgs, @@ -221,8 +223,7 @@ pub struct ScriptArgs { impl ScriptArgs { pub async fn preprocess(self) -> Result { - let script_wallets = - Wallets::new(self.wallets.get_multi_wallet().await?, self.evm_args.sender); + let script_wallets = Wallets::new(self.wallets.get_multi_wallet().await?, self.evm.sender); let (config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; @@ -417,7 +418,7 @@ impl ScriptArgs { } let mut prompt_user = false; - let max_size = match self.evm_args.env.code_size_limit { + let max_size = match self.evm.env.code_size_limit { Some(size) => size, None => CONTRACT_MAX_SIZE, }; @@ -752,7 +753,7 @@ mod tests { "--code-size-limit", "50000", ]); - assert_eq!(args.evm_args.env.code_size_limit, Some(50000)); + assert_eq!(args.evm.env.code_size_limit, Some(50000)); } #[test] diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index cff893b55..ee1c7b46c 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -106,7 +106,7 @@ pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { gas = if gas_price == 0 { format!("Gas Used: {gas_used}") } else { - let paid = format_units(gas_used.saturating_mul(gas_price), 18) + let paid = format_units((gas_used as u128).saturating_mul(gas_price), 18) .unwrap_or_else(|_| "N/A".into()); let gas_price = format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index bf224f041..f89a52b72 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -7,7 +7,7 @@ use alloy_primitives::{hex, Address}; use eyre::{eyre, Result}; use forge_script_sequence::{AdditionalContract, ScriptSequence}; use forge_verify::{provider::VerificationProviderType, RetryArgs, VerifierArgs, VerifyArgs}; -use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; +use foundry_cli::opts::{EtherscanOpts, ProjectPathOpts}; use foundry_common::ContractsByArtifact; use foundry_compilers::{info::ContractInfo, Project}; use foundry_config::{zksync::ZKSYNC_ARTIFACTS_DIR, Chain, Config}; @@ -48,7 +48,7 @@ impl BroadcastedState { pub struct VerifyBundle { pub num_of_optimizations: Option, pub known_contracts: ContractsByArtifact, - pub project_paths: ProjectPathsArgs, + pub project_paths: ProjectPathOpts, pub etherscan: EtherscanOpts, pub retry: RetryArgs, pub verifier: VerifierArgs, @@ -69,7 +69,7 @@ impl VerifyBundle { let config_path = config.get_config_path(); - let project_paths = ProjectPathsArgs { + let project_paths = ProjectPathOpts { root: Some(project.paths.root.clone()), contracts: Some(project.paths.sources.clone()), remappings: project.paths.remappings.clone(), diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 5a9592b4b..d88669d46 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -263,7 +263,7 @@ impl VerifyBytecodeArgs { // configure_tx_rq_env(&mut env, &gen_tx); - configure_tx_req_env(&mut env, &gen_tx_req) + configure_tx_req_env(&mut env, &gen_tx_req, None) .wrap_err("Failed to configure tx request env")?; // Seed deployer account with funds @@ -481,7 +481,7 @@ impl VerifyBytecodeArgs { } // configure_req__env(&mut env, &transaction.inner); - configure_tx_req_env(&mut env, &transaction) + configure_tx_req_env(&mut env, &transaction, None) .wrap_err("Failed to configure tx request env")?; let fork_address = crate::utils::deploy_contract( diff --git a/foundryup-zksync/README.md b/foundryup-zksync/README.md index 2e0a17b3d..2132d20fe 100644 --- a/foundryup-zksync/README.md +++ b/foundryup-zksync/README.md @@ -2,6 +2,8 @@ Update or revert to a specific Foundry-zksync branch with ease. +`foundryup` supports installing and managing multiple versions. + ## Installing @@ -18,7 +20,7 @@ To install the **nightly** version: foundryup-zksync ``` -To install a specific **version** (in this case the `nightly` version): +To **install** a specific **version** (in this case the `nightly` version): ```sh foundryup-zksync --version nightly @@ -64,6 +66,6 @@ foundryup-zksync --path ./git/foundry --- -**Tip**: All flags have a single character shorthand equivalent! You can use `-v` instead of `--version`, etc. +**Tip**: All flags have a single character shorthand equivalent! You can use `-i` instead of `--install`, etc. --- diff --git a/foundryup-zksync/foundryup-zksync b/foundryup-zksync/foundryup-zksync index 21893e061..06b452ee8 100755 --- a/foundryup-zksync/foundryup-zksync +++ b/foundryup-zksync/foundryup-zksync @@ -3,6 +3,7 @@ set -eo pipefail BASE_DIR=${XDG_CONFIG_HOME:-$HOME} FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} +FOUNDRY_VERSIONS_DIR="$FOUNDRY_DIR/versions" FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin" FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" @@ -22,7 +23,9 @@ main() { -r|--repo) shift; FOUNDRYUP_REPO=$1;; -b|--branch) shift; FOUNDRYUP_BRANCH=$1;; - -v|--version) shift; FOUNDRYUP_VERSION=$1;; + -i|--install) shift; FOUNDRYUP_VERSION=$1;; + -l|--list) shift; list;; + -u|--use) shift; FOUNDRYUP_VERSION=$1; use;; -p|--path) shift; FOUNDRYUP_LOCAL_REPO=$1;; -P|--pr) shift; FOUNDRYUP_PR=$1;; -C|--commit) shift; FOUNDRYUP_COMMIT=$1;; @@ -137,15 +140,22 @@ main() { BIN_ARCHIVE_URL="${RELEASE_URL}foundry_${FOUNDRYUP_VERSION}_${PLATFORM}_${ARCHITECTURE}.$EXT" MAN_TARBALL_URL="${RELEASE_URL}foundry_man_${FOUNDRYUP_VERSION}.tar.gz" + ensure mkdir -p $FOUNDRY_VERSIONS_DIR # Download and extract the binaries archive say "downloading latest forge, and cast" if [ "$PLATFORM" = "win32" ]; then tmp="$(mktemp -d 2>/dev/null || echo ".")/foundry-zksync.zip" ensure download "$BIN_ARCHIVE_URL" "$tmp" - ensure unzip "$tmp" -d "$FOUNDRY_BIN_DIR" + ensure unzip "$tmp" -d "$FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_TAG" rm -f "$tmp" else - ensure download "$BIN_ARCHIVE_URL" | ensure tar -xzC "$FOUNDRY_BIN_DIR" + tmp="$(mktemp -d 2>/dev/null || echo ".")/foundry.tar.gz" + ensure download "$BIN_ARCHIVE_URL" "$tmp" + # Make sure it's a valid tar archive. + ensure tar tf $tmp 1> /dev/null + ensure mkdir -p $FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_TAG + ensure tar -C "$FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_TAG" -xvf $tmp + rm -f "$tmp" fi # Optionally download the manuals @@ -159,6 +169,7 @@ main() { for bin in "${BINS[@]}"; do bin_path="$FOUNDRY_BIN_DIR/$bin" + cp $FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_TAG/$bin $bin_path # Print installed msg say "installed - $(ensure "$bin_path" --version)" @@ -292,14 +303,16 @@ The installer for Foundry-zksync. Update or revert to a specific Foundry-zksync version with ease. -By default, the latest nightly version is installed from built binaries. +By default, the latest stable version is installed from built binaries. USAGE: foundryup-zksync OPTIONS: -h, --help Print help information - -v, --version Install a specific version from built binaries + -i, --install Install a specific version from built binaries + -l, --list List versions installed from built binaries + -u, --use Use a specific installed version from built binaries -b, --branch Build and install a specific branch -P, --pr Build and install a specific Pull Request -C, --commit Build and install a specific commit @@ -311,6 +324,41 @@ OPTIONS: EOF } +list() { + if [ -d "$FOUNDRY_VERSIONS_DIR" ]; then + for VERSION in $FOUNDRY_VERSIONS_DIR/*; do + say "${VERSION##*/}" + for bin in "${BINS[@]}"; do + bin_path="$VERSION/$bin" + say "- $(ensure "$bin_path" --version)" + done + printf "\n" + done + else + for bin in "${BINS[@]}"; do + bin_path="$FOUNDRY_BIN_DIR/$bin" + say "- $(ensure "$bin_path" --version)" + done + fi + exit 0 +} + +use() { + [ -z "$FOUNDRYUP_VERSION" ] && err "no version provided" + FOUNDRY_VERSION_DIR="$FOUNDRY_VERSIONS_DIR/$FOUNDRYUP_VERSION" + if [ -d "$FOUNDRY_VERSION_DIR" ]; then + for bin in "${BINS[@]}"; do + bin_path="$FOUNDRY_BIN_DIR/$bin" + cp $FOUNDRY_VERSION_DIR/$bin $bin_path + # Print usage msg + say "use - $(ensure "$bin_path" --version)" + done + exit 0 + else + err "version $FOUNDRYUP_VERSION not installed" + fi +} + say() { printf "foundryup-zksync: %s\n" "$1" } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 20c28d893..61e970b04 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -242,6 +242,10 @@ interface Vm { function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter) external; function expectEmit() external; function expectEmit(address emitter) external; + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, uint64 count) external; + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter, uint64 count) external; + function expectEmit(uint64 count) external; + function expectEmit(address emitter, uint64 count) external; function expectPartialRevert(bytes4 revertData) external; function expectPartialRevert(bytes4 revertData, address reverter) external; function expectRevert() external; diff --git a/testdata/default/cheats/ExpectEmit.t.sol b/testdata/default/cheats/ExpectEmit.t.sol index b8fe5e458..2503faf4b 100644 --- a/testdata/default/cheats/ExpectEmit.t.sol +++ b/testdata/default/cheats/ExpectEmit.t.sol @@ -28,6 +28,12 @@ contract Emitter { emit Something(topic1, topic2, topic3, data); } + function emitNEvents(uint256 topic1, uint256 topic2, uint256 topic3, uint256 data, uint256 n) public { + for (uint256 i = 0; i < n; i++) { + emit Something(topic1, topic2, topic3, data); + } + } + function emitMultiple( uint256[2] memory topic1, uint256[2] memory topic2, @@ -597,3 +603,82 @@ contract ExpectEmitTest is DSTest { // emitter.emitEvent(1, 2, 3, 4); // } } + +contract ExpectEmitCountTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Emitter emitter; + + event Something(uint256 indexed topic1, uint256 indexed topic2, uint256 indexed topic3, uint256 data); + + function setUp() public { + emitter = new Emitter(); + } + + function testCountNoEmit() public { + vm.expectEmit(0); + emit Something(1, 2, 3, 4); + emitter.doesNothing(); + } + + function testFailNoEmit() public { + vm.expectEmit(0); + emit Something(1, 2, 3, 4); + emitter.emitEvent(1, 2, 3, 4); + } + + function testCountNEmits() public { + uint64 count = 2; + vm.expectEmit(count); + emit Something(1, 2, 3, 4); + emitter.emitNEvents(1, 2, 3, 4, count); + } + + function testFailCountLessEmits() public { + uint64 count = 2; + vm.expectEmit(count); + emit Something(1, 2, 3, 4); + emitter.emitNEvents(1, 2, 3, 4, count - 1); + } + + function testCountMoreEmits() public { + uint64 count = 2; + vm.expectEmit(count); + emit Something(1, 2, 3, 4); + emitter.emitNEvents(1, 2, 3, 4, count + 1); + } + + /// Test zero emits from a specific address (emitter). + + function testCountNoEmitFromAddress() public { + vm.expectEmit(address(emitter), 0); + emit Something(1, 2, 3, 4); + emitter.doesNothing(); + } + + function testFailNoEmitFromAddress() public { + vm.expectEmit(address(emitter), 0); + emit Something(1, 2, 3, 4); + emitter.emitEvent(1, 2, 3, 4); + } + + function testCountEmitsFromAddress() public { + uint64 count = 2; + vm.expectEmit(address(emitter), count); + emit Something(1, 2, 3, 4); + emitter.emitNEvents(1, 2, 3, 4, count); + } + + function testFailCountEmitsFromAddress() public { + uint64 count = 3; + vm.expectEmit(address(emitter), count); + emit Something(1, 2, 3, 4); + emitter.emitNEvents(1, 2, 3, 4, count - 1); + } + + function testFailEmitSomethingElse() public { + uint64 count = 2; + vm.expectEmit(count); + emit Something(1, 2, 3, 4); + emitter.emitSomethingElse(23214); + } +} diff --git a/testdata/default/repros/Issue8566.t.sol b/testdata/default/repros/Issue8566.t.sol new file mode 100644 index 000000000..f300d096f --- /dev/null +++ b/testdata/default/repros/Issue8566.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +// https://github.com/foundry-rs/foundry/issues/8566 +contract Issue8566Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testParseJsonUint() public { + string memory json = + "{ \"1284\": { \"addRewardInfo\": { \"amount\": 74258.225772486694040708e18, \"rewardPerSec\": 0.03069536448928848133e20 } } }"; + + assertEq(74258225772486694040708, vm.parseJsonUint(json, ".1284.addRewardInfo.amount")); + assertEq(3069536448928848133, vm.parseJsonUint(json, ".1284.addRewardInfo.rewardPerSec")); + } +} diff --git a/testdata/forge-std-rev b/testdata/forge-std-rev index ff90d09c2..1a0142f7e 100644 --- a/testdata/forge-std-rev +++ b/testdata/forge-std-rev @@ -1 +1 @@ -1eea5bae12ae557d589f9f0f0edae2faa47cb262 \ No newline at end of file +b93cf4bc34ff214c099dc970b153f85ade8c9f66 \ No newline at end of file