diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fda10266fcc7..669002cb0134 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,154 +1,157 @@ name: test on: - push: - branches: - - master - pull_request: + push: + branches: + - master + pull_request: concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true env: - CARGO_TERM_COLOR: always + CARGO_TERM_COLOR: always jobs: - matrices: - name: build matrices - runs-on: ubuntu-latest - outputs: - test-matrix: ${{ steps.gen.outputs.test-matrix }} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: "3.11" - - name: Generate matrices - id: gen - env: - EVENT_NAME: ${{ github.event_name }} - run: | - output=$(python3 .github/scripts/matrices.py) - echo "::debug::test-matrix=$output" - echo "test-matrix=$output" >> $GITHUB_OUTPUT - - test: - name: test ${{ matrix.name }} - runs-on: ${{ matrix.runner_label }} - timeout-minutes: 60 - needs: matrices - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.matrices.outputs.test-matrix) }} + matrices: + name: build matrices + runs-on: ubuntu-latest + outputs: + test-matrix: ${{ steps.gen.outputs.test-matrix }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + - name: Generate matrices + id: gen env: - ETH_RPC_URL: https://eth-mainnet.alchemyapi.io/v2/C3JEvfW6VgtqZQa-Qp1E-2srEiIc02sD - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - target: ${{ matrix.target }} - - uses: taiki-e/install-action@nextest + EVENT_NAME: ${{ github.event_name }} + run: | + output=$(python3 .github/scripts/matrices.py) + echo "::debug::test-matrix=$output" + echo "test-matrix=$output" >> $GITHUB_OUTPUT + + test: + name: test ${{ matrix.name }} + runs-on: ${{ matrix.runner_label }} + timeout-minutes: 60 + needs: matrices + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.matrices.outputs.test-matrix) }} + env: + ETH_RPC_URL: https://eth-mainnet.alchemyapi.io/v2/C3JEvfW6VgtqZQa-Qp1E-2srEiIc02sD + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + target: ${{ matrix.target }} + - uses: taiki-e/install-action@nextest - # External tests dependencies - - name: Setup Node.js - if: contains(matrix.name, 'external') - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Install Bun - if: contains(matrix.name, 'external') && !contains(matrix.runner_label, 'windows') - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - name: Setup Python - if: contains(matrix.name, 'external') - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - name: Install Vyper - if: contains(matrix.name, 'external') - run: pip install vyper + # External tests dependencies + - name: Setup Node.js + if: contains(matrix.name, 'external') + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Bun + if: contains(matrix.name, 'external') && !contains(matrix.runner_label, 'windows') + uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - name: Setup Python + if: contains(matrix.name, 'external') + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Install Vyper + if: contains(matrix.name, 'external') + run: pip install vyper - - name: Forge RPC cache - uses: actions/cache@v3 - with: - path: | - ~/.foundry/cache - ~/.config/.foundry/cache - key: rpc-cache-${{ hashFiles('crates/forge/tests/rpc-cache-keyfile') }} - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: Setup Git config - run: | - git config --global user.name "GitHub Actions Bot" - git config --global user.email "<>" - git config --global url."https://github.com/".insteadOf "git@github.com:" - - name: Test - env: - SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} - run: cargo nextest run ${{ matrix.flags }} + - name: Forge RPC cache + uses: actions/cache@v3 + with: + path: | + ~/.foundry/cache + ~/.config/.foundry/cache + key: rpc-cache-${{ hashFiles('crates/forge/tests/rpc-cache-keyfile') }} + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Setup Git config + run: | + git config --global user.name "GitHub Actions Bot" + git config --global user.email "<>" + git config --global url."https://github.com/".insteadOf "git@github.com:" + - name: Test + env: + SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} + run: cargo nextest run ${{ matrix.flags }} - doctest: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - run: cargo test --workspace --doc + doctest: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo test --workspace --doc - clippy: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@clippy - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - run: cargo clippy --workspace --all-targets --all-features - env: - RUSTFLAGS: -Dwarnings + clippy: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@clippy + with: + toolchain: nightly-2024-02-03 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings - rustfmt: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt - - run: cargo fmt --all --check + rustfmt: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2024-02-03 + components: rustfmt + - run: cargo fmt --all --check - forge-fmt: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: forge fmt - shell: bash - # We have to ignore at shell level because testdata/ is not a valid Foundry project, - # so running `forge fmt` with `--root testdata` won't actually check anything - run: | - shopt -s extglob - cargo run --bin forge -- fmt --check testdata/**/!(Vm).sol + forge-fmt: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: forge fmt + shell: bash + # We have to ignore at shell level because testdata/ is not a valid Foundry project, + # so running `forge fmt` with `--root testdata` won't actually check anything + run: | + shopt -s extglob + cargo run --bin forge -- fmt --check testdata/**/!(Vm).sol - crate-checks: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: taiki-e/install-action@cargo-hack - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - run: cargo hack check + crate-checks: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo hack check diff --git a/Cargo.lock b/Cargo.lock index 4f742d4b408f..b1a0e595dda0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-eips", "alloy-network", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-primitives", "serde", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "alloy-providers" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-network", "alloy-primitives", @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -263,7 +263,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -280,7 +280,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-network", "alloy-primitives", @@ -365,7 +365,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-json-rpc", "base64 0.21.7", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -394,7 +394,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -412,7 +412,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#2be621406132ba034e6c9729e3f1fbd0e26bda4d" +source = "git+https://github.com/alloy-rs/alloy#32618e9243a761858a0843e7e55575e48fdbf500" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -475,9 +475,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -2431,7 +2431,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.48", - "toml 0.8.9", + "toml 0.8.10", "walkdir", ] @@ -2761,7 +2761,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.9", + "toml 0.8.10", "uncased", "version_check", ] @@ -2920,7 +2920,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.9", + "toml 0.8.10", "tracing", ] @@ -2935,7 +2935,7 @@ dependencies = [ "pretty_assertions", "solang-parser", "thiserror", - "toml 0.8.9", + "toml 0.8.10", "tracing", "tracing-subscriber", ] @@ -3164,7 +3164,7 @@ dependencies = [ "serde_regex", "tempfile", "thiserror", - "toml 0.8.9", + "toml 0.8.10", "toml_edit 0.21.1", "tracing", "walkdir", @@ -3931,9 +3931,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "hex" @@ -5046,7 +5046,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.48", @@ -5413,9 +5413,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" dependencies = [ "memchr", "thiserror", @@ -5424,9 +5424,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" dependencies = [ "pest", "pest_generator", @@ -5434,9 +5434,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" dependencies = [ "pest", "pest_meta", @@ -5447,9 +5447,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" dependencies = [ "once_cell", "pest", @@ -5739,6 +5739,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6151,7 +6160,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors#e90052361276aebcdc67cb24d8e2c4d907b6d299" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=e90052361276aebcdc67cb24d8e2c4d907b6d299#e90052361276aebcdc67cb24d8e2c4d907b6d299" dependencies = [ "alloy-primitives", "alloy-rpc-trace-types", @@ -7337,13 +7346,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] @@ -7629,15 +7637,15 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.1", + "toml_edit 0.22.0", ] [[package]] @@ -7676,6 +7684,17 @@ name = "toml_edit" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8dc77def39ce6079c2d0c866cc20848f591b1898f153c9fe7c4f29e1154510b" dependencies = [ "indexmap", "serde", diff --git a/Cargo.toml b/Cargo.toml index e73e80e20eef..70b3c069e097 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,7 +133,7 @@ foundry-compilers = { version = "0.3.1", default-features = false } # no default features to avoid c-kzg revm = { version = "3", default-features = false } revm-primitives = { version = "1", default-features = false } -revm-inspectors = { version = "0.1", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "e90052361276aebcdc67cb24d8e2c4d907b6d299", default-features = false } ## ethers ethers = { version = "2.0", default-features = false } @@ -226,5 +226,3 @@ revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-precompile = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } - -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors" } \ No newline at end of file diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index c969c09420c1..5ff9394821d8 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -3,18 +3,18 @@ use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; /// A fast local Ethereum development node. -#[derive(Debug, Parser)] +#[derive(Parser)] #[clap(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] -pub struct App { +pub struct Anvil { #[clap(flatten)] pub node: NodeArgs, #[clap(subcommand)] - pub cmd: Option, + pub cmd: Option, } -#[derive(Clone, Debug, PartialEq, Eq, Subcommand)] -pub enum Commands { +#[derive(Subcommand)] +pub enum AnvilSubcommand { /// Generate shell completions script. #[clap(visible_alias = "com")] Completions { @@ -29,22 +29,22 @@ pub enum Commands { #[tokio::main] async fn main() -> Result<(), Box> { - let mut app = App::parse(); + let mut app = Anvil::parse(); app.node.evm_opts.resolve_rpc_alias(); if let Some(ref cmd) = app.cmd { match cmd { - Commands::Completions { shell } => { + AnvilSubcommand::Completions { shell } => { clap_complete::generate( *shell, - &mut App::command(), + &mut Anvil::command(), "anvil", &mut std::io::stdout(), ); } - Commands::GenerateFigSpec => clap_complete::generate( + AnvilSubcommand::GenerateFigSpec => clap_complete::generate( clap_complete_fig::Fig, - &mut App::command(), + &mut Anvil::command(), "anvil", &mut std::io::stdout(), ), @@ -62,14 +62,22 @@ async fn main() -> Result<(), Box> { mod tests { use super::*; + #[test] + fn verify_cli() { + Anvil::command().debug_assert(); + } + #[test] fn can_parse_help() { - let _: App = App::parse_from(["anvil", "--help"]); + let _: Anvil = Anvil::parse_from(["anvil", "--help"]); } #[test] fn can_parse_completions() { - let args: App = App::parse_from(["anvil", "completions", "bash"]); - assert_eq!(args.cmd, Some(Commands::Completions { shell: clap_complete::Shell::Bash })); + let args: Anvil = Anvil::parse_from(["anvil", "completions", "bash"]); + assert!(matches!( + args.cmd, + Some(AnvilSubcommand::Completions { shell: clap_complete::Shell::Bash }) + )); } } diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index eb5b7cd4323f..5769360484ad 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -107,7 +107,8 @@ impl StorageArgs { artifact.get_deployed_bytecode_bytes().is_some_and(|b| *b == address_code) }); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address.clone(), artifact, true).await; + return fetch_and_print_storage(provider, address.clone(), block, artifact, true) + .await; } } @@ -173,7 +174,7 @@ impl StorageArgs { // Clear temp directory root.close()?; - fetch_and_print_storage(provider, address, artifact, true).await + fetch_and_print_storage(provider, address, block, artifact, true).await } } @@ -211,6 +212,7 @@ impl StorageValue { async fn fetch_and_print_storage( provider: RetryProvider, address: NameOrAddress, + block: Option, artifact: &ConfigurableContractArtifact, pretty: bool, ) -> Result<()> { @@ -219,7 +221,7 @@ async fn fetch_and_print_storage( Ok(()) } else { let layout = artifact.storage_layout.as_ref().unwrap().clone(); - let values = fetch_storage_slots(provider, address, &layout).await?; + let values = fetch_storage_slots(provider, address, block, &layout).await?; print_storage(layout, values, pretty) } } @@ -227,12 +229,13 @@ async fn fetch_and_print_storage( async fn fetch_storage_slots( provider: RetryProvider, address: NameOrAddress, + block: Option, layout: &StorageLayout, ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); let raw_slot_value = - provider.get_storage_at(address.clone(), slot.to_ethers(), None).await?.to_alloy(); + provider.get_storage_at(address.clone(), slot.to_ethers(), block).await?.to_alloy(); let value = StorageValue { slot, raw_slot_value }; diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 6a026904dcba..2018c4420a8f 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -152,7 +152,14 @@ impl WalletSubcommands { let mut json_values = if json { Some(vec![]) } else { None }; if let Some(path) = path { - let path = dunce::canonicalize(path)?; + let path = match dunce::canonicalize(path.clone()) { + Ok(path) => path, + // If the path doesn't exist, it will fail to be canonicalized, + // so we attach more context to the error message. + Err(e) => { + eyre::bail!("If you specified a directory, please make sure it exists, or create it before running `cast wallet new `.\n{path} is not a directory.\nError: {}", e); + } + }; if !path.is_dir() { // we require path to be an existing directory eyre::bail!("`{}` is not a directory", path.display()); diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 9c992e7667f2..c4ee0e56388e 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -27,7 +27,7 @@ use std::time::Instant; pub mod cmd; pub mod opts; -use opts::{Opts, Subcommands, ToBaseArgs}; +use opts::{Cast as Opts, CastSubcommand, ToBaseArgs}; #[tokio::main] async fn main() -> Result<()> { @@ -37,42 +37,42 @@ async fn main() -> Result<()> { utils::enable_paint(); let opts = Opts::parse(); - match opts.sub { + match opts.cmd { // Constants - Subcommands::MaxInt { r#type } => { + CastSubcommand::MaxInt { r#type } => { println!("{}", SimpleCast::max_int(&r#type)?); } - Subcommands::MinInt { r#type } => { + CastSubcommand::MinInt { r#type } => { println!("{}", SimpleCast::min_int(&r#type)?); } - Subcommands::MaxUint { r#type } => { + CastSubcommand::MaxUint { r#type } => { println!("{}", SimpleCast::max_int(&r#type)?); } - Subcommands::AddressZero => { + CastSubcommand::AddressZero => { println!("{:?}", Address::ZERO); } - Subcommands::HashZero => { + CastSubcommand::HashZero => { println!("{:?}", B256::ZERO); } // Conversions & transformations - Subcommands::FromUtf8 { text } => { + CastSubcommand::FromUtf8 { text } => { let value = stdin::unwrap(text, false)?; println!("{}", SimpleCast::from_utf8(&value)); } - Subcommands::ToAscii { hexdata } => { + CastSubcommand::ToAscii { hexdata } => { let value = stdin::unwrap(hexdata, false)?; println!("{}", SimpleCast::to_ascii(&value)?); } - Subcommands::FromFixedPoint { value, decimals } => { + CastSubcommand::FromFixedPoint { value, decimals } => { let (value, decimals) = stdin::unwrap2(value, decimals)?; println!("{}", SimpleCast::from_fixed_point(&value, &decimals)?); } - Subcommands::ToFixedPoint { value, decimals } => { + CastSubcommand::ToFixedPoint { value, decimals } => { let (value, decimals) = stdin::unwrap2(value, decimals)?; println!("{}", SimpleCast::to_fixed_point(&value, &decimals)?); } - Subcommands::ConcatHex { data } => { + CastSubcommand::ConcatHex { data } => { if data.is_empty() { let s = stdin::read(true)?; println!("{}", SimpleCast::concat_hex(s.split_whitespace())) @@ -80,11 +80,11 @@ async fn main() -> Result<()> { println!("{}", SimpleCast::concat_hex(data)) } } - Subcommands::FromBin => { + CastSubcommand::FromBin => { let hex = stdin::read_bytes(false)?; println!("{}", hex::encode_prefixed(hex)); } - Subcommands::ToHexdata { input } => { + CastSubcommand::ToHexdata { input } => { let value = stdin::unwrap_line(input)?; let output = match value { s if s.starts_with('@') => hex::encode(std::env::var(&s[1..])?), @@ -93,91 +93,91 @@ async fn main() -> Result<()> { }; println!("0x{output}"); } - Subcommands::ToCheckSumAddress { address } => { + CastSubcommand::ToCheckSumAddress { address } => { let value = stdin::unwrap_line(address)?; println!("{}", value.to_checksum(None)); } - Subcommands::ToUint256 { value } => { + CastSubcommand::ToUint256 { value } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_uint256(&value)?); } - Subcommands::ToInt256 { value } => { + CastSubcommand::ToInt256 { value } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_int256(&value)?); } - Subcommands::ToUnit { value, unit } => { + CastSubcommand::ToUnit { value, unit } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_unit(&value, &unit)?); } - Subcommands::FromWei { value, unit } => { + CastSubcommand::FromWei { value, unit } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::from_wei(&value, &unit)?); } - Subcommands::ToWei { value, unit } => { + CastSubcommand::ToWei { value, unit } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_wei(&value, &unit)?); } - Subcommands::FromRlp { value } => { + CastSubcommand::FromRlp { value } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::from_rlp(value)?); } - Subcommands::ToRlp { value } => { + CastSubcommand::ToRlp { value } => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_rlp(&value)?); } - Subcommands::ToHex(ToBaseArgs { value, base_in }) => { + CastSubcommand::ToHex(ToBaseArgs { value, base_in }) => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), "hex")?); } - Subcommands::ToDec(ToBaseArgs { value, base_in }) => { + CastSubcommand::ToDec(ToBaseArgs { value, base_in }) => { let value = stdin::unwrap_line(value)?; println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), "dec")?); } - Subcommands::ToBase { base: ToBaseArgs { value, base_in }, base_out } => { + CastSubcommand::ToBase { base: ToBaseArgs { value, base_in }, base_out } => { let (value, base_out) = stdin::unwrap2(value, base_out)?; println!("{}", SimpleCast::to_base(&value, base_in.as_deref(), &base_out)?); } - Subcommands::ToBytes32 { bytes } => { + CastSubcommand::ToBytes32 { bytes } => { let value = stdin::unwrap_line(bytes)?; println!("{}", SimpleCast::to_bytes32(&value)?); } - Subcommands::FormatBytes32String { string } => { + CastSubcommand::FormatBytes32String { string } => { let value = stdin::unwrap_line(string)?; println!("{}", SimpleCast::format_bytes32_string(&value)?); } - Subcommands::ParseBytes32String { bytes } => { + CastSubcommand::ParseBytes32String { bytes } => { let value = stdin::unwrap_line(bytes)?; println!("{}", SimpleCast::parse_bytes32_string(&value)?); } - Subcommands::ParseBytes32Address { bytes } => { + CastSubcommand::ParseBytes32Address { bytes } => { let value = stdin::unwrap_line(bytes)?; println!("{}", SimpleCast::parse_bytes32_address(&value)?); } // ABI encoding & decoding - Subcommands::AbiDecode { sig, calldata, input } => { + CastSubcommand::AbiDecode { sig, calldata, input } => { let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?; let tokens = format_tokens(&tokens); tokens.for_each(|t| println!("{t}")); } - Subcommands::AbiEncode { sig, args } => { + CastSubcommand::AbiEncode { sig, args } => { println!("{}", SimpleCast::abi_encode(&sig, &args)?); } - Subcommands::CalldataDecode { sig, calldata } => { + CastSubcommand::CalldataDecode { sig, calldata } => { let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?; let tokens = format_tokens(&tokens); tokens.for_each(|t| println!("{t}")); } - Subcommands::CalldataEncode { sig, args } => { + CastSubcommand::CalldataEncode { sig, args } => { println!("{}", SimpleCast::calldata_encode(sig, &args)?); } - Subcommands::Interface(cmd) => cmd.run().await?, - Subcommands::Bind(cmd) => cmd.run().await?, - Subcommands::PrettyCalldata { calldata, offline } => { + CastSubcommand::Interface(cmd) => cmd.run().await?, + CastSubcommand::Bind(cmd) => cmd.run().await?, + CastSubcommand::PrettyCalldata { calldata, offline } => { let calldata = stdin::unwrap_line(calldata)?; println!("{}", pretty_calldata(&calldata, offline).await?); } - Subcommands::Sig { sig, optimize } => { + CastSubcommand::Sig { sig, optimize } => { let sig = stdin::unwrap_line(sig)?; match optimize { Some(opt) => { @@ -193,8 +193,8 @@ async fn main() -> Result<()> { } // Blockchain & RPC queries - Subcommands::AccessList(cmd) => cmd.run().await?, - Subcommands::Age { block, rpc } => { + CastSubcommand::AccessList(cmd) => cmd.run().await?, + CastSubcommand::Age { block, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!( @@ -202,7 +202,7 @@ async fn main() -> Result<()> { Cast::new(provider).age(block.unwrap_or(BlockId::Number(Latest))).await? ); } - Subcommands::Balance { block, who, ether, rpc, erc20 } => { + CastSubcommand::Balance { block, who, ether, rpc, erc20 } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -242,7 +242,7 @@ async fn main() -> Result<()> { } } } - Subcommands::BaseFee { block, rpc } => { + CastSubcommand::BaseFee { block, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!( @@ -250,7 +250,7 @@ async fn main() -> Result<()> { Cast::new(provider).base_fee(block.unwrap_or(BlockId::Number(Latest))).await? ); } - Subcommands::Block { block, full, field, json, rpc } => { + CastSubcommand::Block { block, full, field, json, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!( @@ -260,37 +260,37 @@ async fn main() -> Result<()> { .await? ); } - Subcommands::BlockNumber { rpc } => { + CastSubcommand::BlockNumber { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).block_number().await?); } - Subcommands::Chain { rpc } => { + CastSubcommand::Chain { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).chain().await?); } - Subcommands::ChainId { rpc } => { + CastSubcommand::ChainId { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).chain_id().await?); } - Subcommands::Client { rpc } => { + CastSubcommand::Client { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", provider.client_version().await?); } - Subcommands::Code { block, who, disassemble, rpc } => { + CastSubcommand::Code { block, who, disassemble, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).code(who, block, disassemble).await?); } - Subcommands::Codesize { block, who, rpc } => { + CastSubcommand::Codesize { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).codesize(who, block).await?); } - Subcommands::ComputeAddress { address, nonce, rpc } => { + CastSubcommand::ComputeAddress { address, nonce, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -298,10 +298,10 @@ async fn main() -> Result<()> { let computed = Cast::new(&provider).compute_address(address, nonce).await?; println!("Computed Address: {}", computed.to_checksum(None)); } - Subcommands::Disassemble { bytecode } => { + CastSubcommand::Disassemble { bytecode } => { println!("{}", SimpleCast::disassemble(&bytecode)?); } - Subcommands::Selectors { bytecode, resolve } => { + CastSubcommand::Selectors { bytecode, resolve } => { let selectors_and_args = SimpleCast::extract_selectors(&bytecode)?; if resolve { let selectors_it = selectors_and_args.iter().map(|r| &r.0); @@ -324,31 +324,31 @@ async fn main() -> Result<()> { } } } - Subcommands::FindBlock(cmd) => cmd.run().await?, - Subcommands::GasPrice { rpc } => { + CastSubcommand::FindBlock(cmd) => cmd.run().await?, + CastSubcommand::GasPrice { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).gas_price().await?); } - Subcommands::Index { key_type, key, slot_number } => { + CastSubcommand::Index { key_type, key, slot_number } => { println!("{}", SimpleCast::index(&key_type, &key, &slot_number)?); } - Subcommands::Implementation { block, who, rpc } => { + CastSubcommand::Implementation { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).implementation(who, block).await?); } - Subcommands::Admin { block, who, rpc } => { + CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).admin(who, block).await?); } - Subcommands::Nonce { block, who, rpc } => { + CastSubcommand::Nonce { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!("{}", Cast::new(provider).nonce(who, block).await?); } - Subcommands::Proof { address, slots, rpc, block } => { + CastSubcommand::Proof { address, slots, rpc, block } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let value = provider @@ -356,13 +356,13 @@ async fn main() -> Result<()> { .await?; println!("{}", serde_json::to_string(&value)?); } - Subcommands::Rpc(cmd) => cmd.run().await?, - Subcommands::Storage(cmd) => cmd.run().await?, + CastSubcommand::Rpc(cmd) => cmd.run().await?, + CastSubcommand::Storage(cmd) => cmd.run().await?, // Calls & transactions - Subcommands::Call(cmd) => cmd.run().await?, - Subcommands::Estimate(cmd) => cmd.run().await?, - Subcommands::PublishTx { raw_tx, cast_async, rpc } => { + CastSubcommand::Call(cmd) => cmd.run().await?, + CastSubcommand::Estimate(cmd) => cmd.run().await?, + CastSubcommand::PublishTx { raw_tx, cast_async, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let cast = Cast::new(&provider); @@ -377,7 +377,7 @@ async fn main() -> Result<()> { println!("{}", serde_json::json!(receipt)); } } - Subcommands::Receipt { tx_hash, field, json, cast_async, confirmations, rpc } => { + CastSubcommand::Receipt { tx_hash, field, json, cast_async, confirmations, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; println!( @@ -387,9 +387,9 @@ async fn main() -> Result<()> { .await? ); } - Subcommands::Run(cmd) => cmd.run().await?, - Subcommands::SendTx(cmd) => cmd.run().await?, - Subcommands::Tx { tx_hash, field, raw, json, rpc } => { + CastSubcommand::Run(cmd) => cmd.run().await?, + CastSubcommand::SendTx(cmd) => cmd.run().await?, + CastSubcommand::Tx { tx_hash, field, raw, json, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -400,7 +400,7 @@ async fn main() -> Result<()> { } // 4Byte - Subcommands::FourByte { selector } => { + CastSubcommand::FourByte { selector } => { let selector = stdin::unwrap_line(selector)?; let sigs = decode_function_selector(&selector).await?; if sigs.is_empty() { @@ -410,7 +410,7 @@ async fn main() -> Result<()> { println!("{sig}"); } } - Subcommands::FourByteDecode { calldata } => { + CastSubcommand::FourByteDecode { calldata } => { let calldata = stdin::unwrap_line(calldata)?; let sigs = decode_calldata(&calldata).await?; sigs.iter().enumerate().for_each(|(i, sig)| println!("{}) \"{sig}\"", i + 1)); @@ -429,7 +429,7 @@ async fn main() -> Result<()> { println!("{token}"); } } - Subcommands::FourByteEvent { topic } => { + CastSubcommand::FourByteEvent { topic } => { let topic = stdin::unwrap_line(topic)?; let sigs = decode_event_topic(&topic).await?; if sigs.is_empty() { @@ -439,7 +439,7 @@ async fn main() -> Result<()> { println!("{sig}"); } } - Subcommands::UploadSignature { signatures } => { + CastSubcommand::UploadSignature { signatures } => { let signatures = stdin::unwrap_vec(signatures)?; let ParsedSignatures { signatures, abis } = parse_signatures(signatures); if !abis.is_empty() { @@ -451,11 +451,11 @@ async fn main() -> Result<()> { } // ENS - Subcommands::Namehash { name } => { + CastSubcommand::Namehash { name } => { let name = stdin::unwrap_line(name)?; println!("{}", SimpleCast::namehash(&name)?); } - Subcommands::LookupAddress { who, rpc, verify } => { + CastSubcommand::LookupAddress { who, rpc, verify } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -470,7 +470,7 @@ async fn main() -> Result<()> { } println!("{name}"); } - Subcommands::ResolveName { who, rpc, verify } => { + CastSubcommand::ResolveName { who, rpc, verify } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -487,7 +487,7 @@ async fn main() -> Result<()> { } // Misc - Subcommands::Keccak { data } => { + CastSubcommand::Keccak { data } => { let bytes = match data { Some(data) => data.into_bytes(), None => stdin::read_bytes(false)?, @@ -504,18 +504,18 @@ async fn main() -> Result<()> { } }; } - Subcommands::SigEvent { event_string } => { + CastSubcommand::SigEvent { event_string } => { let event_string = stdin::unwrap_line(event_string)?; let parsed_event = get_event(&event_string)?; println!("{:?}", parsed_event.selector()); } - Subcommands::LeftShift { value, bits, base_in, base_out } => { + CastSubcommand::LeftShift { value, bits, base_in, base_out } => { println!("{}", SimpleCast::left_shift(&value, &bits, base_in.as_deref(), &base_out)?); } - Subcommands::RightShift { value, bits, base_in, base_out } => { + CastSubcommand::RightShift { value, bits, base_in, base_out } => { println!("{}", SimpleCast::right_shift(&value, &bits, base_in.as_deref(), &base_out)?); } - Subcommands::EtherscanSource { address, directory, etherscan } => { + CastSubcommand::EtherscanSource { address, directory, etherscan } => { let config = Config::from(ðerscan); let chain = config.chain.unwrap_or_default(); let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); @@ -529,21 +529,21 @@ async fn main() -> Result<()> { } } } - Subcommands::Create2(cmd) => { + CastSubcommand::Create2(cmd) => { cmd.run()?; } - Subcommands::Wallet { command } => command.run().await?, - Subcommands::Completions { shell } => { + CastSubcommand::Wallet { command } => command.run().await?, + CastSubcommand::Completions { shell } => { generate(shell, &mut Opts::command(), "cast", &mut std::io::stdout()) } - Subcommands::GenerateFigSpec => clap_complete::generate( + CastSubcommand::GenerateFigSpec => clap_complete::generate( clap_complete_fig::Fig, &mut Opts::command(), "cast", &mut std::io::stdout(), ), - Subcommands::Logs(cmd) => cmd.run().await?, - Subcommands::DecodeTransaction { tx } => { + CastSubcommand::Logs(cmd) => cmd.run().await?, + CastSubcommand::DecodeTransaction { tx } => { let tx = stdin::unwrap_line(tx)?; let (tx, sig) = SimpleCast::decode_raw_transaction(&tx)?; diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 89ef6f96744a..2f09c2534830 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -27,13 +27,13 @@ const VERSION_MESSAGE: &str = concat!( after_help = "Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html", next_display_order = None, )] -pub struct Opts { +pub struct Cast { #[clap(subcommand)] - pub sub: Subcommands, + pub cmd: CastSubcommand, } #[derive(Subcommand)] -pub enum Subcommands { +pub enum CastSubcommand { /// Prints the maximum value of the given integer type. #[clap(visible_aliases = &["--max-int", "maxi"])] MaxInt { @@ -898,11 +898,17 @@ pub fn parse_slot(s: &str) -> Result { mod tests { use super::*; use cast::SimpleCast; + use clap::CommandFactory; use ethers_core::types::BlockNumber; + #[test] + fn verify_cli() { + Cast::command().debug_assert(); + } + #[test] fn parse_proof_slot() { - let args: Opts = Opts::parse_from([ + let args: Cast = Cast::parse_from([ "foundry-cli", "proof", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", @@ -912,8 +918,8 @@ mod tests { "0x1", "0x01", ]); - match args.sub { - Subcommands::Proof { slots, .. } => { + match args.cmd { + CastSubcommand::Proof { slots, .. } => { assert_eq!( slots, vec![ @@ -931,15 +937,15 @@ mod tests { #[test] fn parse_call_data() { - let args: Opts = Opts::parse_from([ + let args: Cast = Cast::parse_from([ "foundry-cli", "calldata", "f()", "5c9d55b78febcc2061715ba4f57ecf8ea2711f2c", "2", ]); - match args.sub { - Subcommands::CalldataEncode { args, .. } => { + match args.cmd { + CastSubcommand::CalldataEncode { args, .. } => { assert_eq!( args, vec!["5c9d55b78febcc2061715ba4f57ecf8ea2711f2c".to_string(), "2".to_string()] @@ -952,13 +958,13 @@ mod tests { // #[test] fn parse_signature() { - let args: Opts = Opts::parse_from([ + let args: Cast = Cast::parse_from([ "foundry-cli", "sig", "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)", ]); - match args.sub { - Subcommands::Sig { sig, .. } => { + match args.cmd { + CastSubcommand::Sig { sig, .. } => { let sig = sig.unwrap(); assert_eq!( sig, diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 28703b5a116e..e4009aa4af9f 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -555,6 +555,32 @@ casttest!(storage, |_prj, cmd| { let six = "0x0000000000000000000000000000000000000000000000000000000000000006"; cmd.cast_fuse().args(["storage", usdt, decimals_slot, "--rpc-url", &rpc]); assert_eq!(cmd.stdout_lossy().trim(), six); + + let rpc = next_http_rpc_endpoint(); + let total_supply_slot = "0x01"; + let issued = "0x000000000000000000000000000000000000000000000000000000174876e800"; + let block_before = "4634747"; + let block_after = "4634748"; + cmd.cast_fuse().args([ + "storage", + usdt, + total_supply_slot, + "--rpc-url", + &rpc, + "--block", + block_before, + ]); + assert_eq!(cmd.stdout_lossy().trim(), empty); + cmd.cast_fuse().args([ + "storage", + usdt, + total_supply_slot, + "--rpc-url", + &rpc, + "--block", + block_after, + ]); + assert_eq!(cmd.stdout_lossy().trim(), issued); }); casttest!(balance, |_prj, cmd| { diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index d9028965a627..9da885860b62 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -7,7 +7,7 @@ use chisel::{ history::chisel_history_file, prelude::{ChiselCommand, ChiselDispatcher, DispatchResult, SolidityHelper}, }; -use clap::Parser; +use clap::{Parser, Subcommand}; use eyre::Context; use foundry_cli::{ handler, @@ -28,7 +28,7 @@ use tracing::debug; use yansi::Paint; // Loads project's figment and merges the build cli arguments into it -foundry_config::merge_impl_figment_convert!(ChiselParser, opts, evm_opts); +foundry_config::merge_impl_figment_convert!(Chisel, opts, evm_opts); const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -42,9 +42,9 @@ const VERSION_MESSAGE: &str = concat!( /// Fast, utilitarian, and verbose Solidity REPL. #[derive(Debug, Parser)] #[clap(name = "chisel", version = VERSION_MESSAGE)] -pub struct ChiselParser { +pub struct Chisel { #[command(subcommand)] - pub sub: Option, + pub cmd: Option, /// Path to a directory containing Solidity files to import, or path to a single Solidity file. /// @@ -69,8 +69,8 @@ pub struct ChiselParser { } /// Chisel binary subcommands -#[derive(Debug, clap::Subcommand)] -pub enum ChiselParserSub { +#[derive(Debug, Subcommand)] +pub enum ChiselSubcommand { /// List all cached sessions List, @@ -102,7 +102,7 @@ async fn main() -> eyre::Result<()> { utils::load_dotenv(); // Parse command args - let args = ChiselParser::parse(); + let args = Chisel::parse(); // Keeps track of whether or not an interrupt was the last input let mut interrupt = false; @@ -125,8 +125,8 @@ async fn main() -> eyre::Result<()> { evaluate_prelude(&mut dispatcher, args.prelude).await?; // Check for chisel subcommands - match &args.sub { - Some(ChiselParserSub::List) => { + match &args.cmd { + Some(ChiselSubcommand::List) => { let sessions = dispatcher.dispatch_command(ChiselCommand::ListSessions, &[]).await; match sessions { DispatchResult::CommandSuccess(Some(session_list)) => { @@ -137,7 +137,7 @@ async fn main() -> eyre::Result<()> { } return Ok(()) } - Some(ChiselParserSub::Load { id }) | Some(ChiselParserSub::View { id }) => { + Some(ChiselSubcommand::Load { id }) | Some(ChiselSubcommand::View { id }) => { // For both of these subcommands, we need to attempt to load the session from cache match dispatcher.dispatch_command(ChiselCommand::Load, &[id]).await { DispatchResult::CommandSuccess(_) => { /* Continue */ } @@ -149,7 +149,7 @@ async fn main() -> eyre::Result<()> { } // If the subcommand was `view`, print the source and exit. - if matches!(args.sub, Some(ChiselParserSub::View { .. })) { + if matches!(args.cmd, Some(ChiselSubcommand::View { .. })) { match dispatcher.dispatch_command(ChiselCommand::Source, &[]).await { DispatchResult::CommandSuccess(Some(source)) => { println!("{source}"); @@ -159,7 +159,7 @@ async fn main() -> eyre::Result<()> { return Ok(()) } } - Some(ChiselParserSub::ClearCache) => { + Some(ChiselSubcommand::ClearCache) => { match dispatcher.dispatch_command(ChiselCommand::ClearCache, &[]).await { DispatchResult::CommandSuccess(Some(msg)) => println!("{}", Paint::green(msg)), DispatchResult::CommandFailed(e) => eprintln!("{e}"), @@ -229,7 +229,7 @@ async fn main() -> eyre::Result<()> { } /// [Provider] impl -impl Provider for ChiselParser { +impl Provider for Chisel { fn metadata(&self) -> Metadata { Metadata::named("Script Args Provider") } @@ -294,3 +294,14 @@ async fn load_prelude_file(dispatcher: &mut ChiselDispatcher, file: PathBuf) -> dispatch_repl_line(dispatcher, &prelude).await; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use clap::CommandFactory; + + #[test] + fn verify_cli() { + Chisel::command().debug_assert(); + } +} diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 322598e36940..36fb5cf1c167 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -8,7 +8,7 @@ use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ artifacts::{Source, Sources}, - CompilerInput, CompilerOutput, EvmVersion, Solc, + CompilerInput, CompilerOutput, Solc, }; use foundry_config::{Config, SolcReq}; use foundry_evm::{backend::Backend, opts::EvmOpts}; @@ -105,22 +105,17 @@ impl SessionSourceConfig { match solc_req { SolcReq::Version(version) => { - // We now need to verify if the solc version provided is supported by the evm - // version set. If not, we bail and ask the user to provide a newer version. - // 1. Do we need solc 0.8.18 or higher? - let evm_version = self.foundry_config.evm_version; - let needs_post_merge_solc = evm_version >= EvmVersion::Paris; - // 2. Check if the version provided is less than 0.8.18 and bail, - // or leave it as-is if we don't need a post merge solc version or the version we - // have is good enough. - let v = if needs_post_merge_solc && version < Version::new(0, 8, 18) { - eyre::bail!("solc {version} is not supported by the set evm version: {evm_version}. Please install and use a version of solc higher or equal to 0.8.18. -You can also set the solc version in your foundry.toml.") - } else { - version.to_string() - }; + // Validate that the requested evm version is supported by the solc version + let req_evm_version = self.foundry_config.evm_version; + if let Some(compat_evm_version) = req_evm_version.normalize_version(&version) { + if req_evm_version > compat_evm_version { + eyre::bail!( + "The set evm version, {req_evm_version}, is not supported by solc {version}. Upgrade to a newer solc version." + ); + } + } - let mut solc = Solc::find_svm_installed_version(&v)?; + let mut solc = Solc::find_svm_installed_version(version.to_string())?; if solc.is_none() { if self.foundry_config.offline { @@ -131,7 +126,7 @@ You can also set the solc version in your foundry.toml.") Paint::green(format!("Installing solidity version {version}...")) ); Solc::blocking_install(&version)?; - solc = Solc::find_svm_installed_version(&v)?; + solc = Solc::find_svm_installed_version(version.to_string())?; } solc.ok_or_else(|| eyre::eyre!("Failed to install {version}")) } diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index 9173e8bc791d..c6b9625ebd7c 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -1,6 +1,6 @@ use chisel::session::ChiselSession; use foundry_compilers::EvmVersion; -use foundry_config::Config; +use foundry_config::{Config, SolcReq}; use serial_test::serial; use std::path::Path; @@ -220,3 +220,27 @@ fn test_load_latest_cache() { assert_eq!(new_env.id.unwrap(), "1"); assert_eq!(new_env.session_source.to_repl_source(), env.session_source.to_repl_source()); } + +#[test] +#[serial] +fn test_solc_evm_configuration_mismatch() { + // Create and clear the cache directory + ChiselSession::create_cache_dir().unwrap(); + ChiselSession::clear_cache().unwrap(); + + // Force the solc version to be 0.8.13 which does not support Paris + let foundry_config = Config { + evm_version: EvmVersion::Paris, + solc: Some(SolcReq::Version("0.8.13".parse().expect("invalid semver"))), + ..Default::default() + }; + + // Create a new session that is expected to fail + let error = ChiselSession::new(chisel::session_source::SessionSourceConfig { + foundry_config, + ..Default::default() + }) + .unwrap_err(); + + assert_eq!(error.to_string(), "The set evm version, paris, is not supported by solc 0.8.13. Upgrade to a newer solc version."); +} diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index bf1d0c31cb07..39248a8a38e9 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -1,6 +1,6 @@ //! An utility trait for retrying requests based on the error type. See [TransportError]. use alloy_json_rpc::ErrorPayload; -use alloy_transport::TransportError; +use alloy_transport::{TransportError, TransportErrorKind}; use serde::Deserialize; /// [RetryPolicy] defines logic for which [JsonRpcClient::Error] instances should @@ -24,7 +24,9 @@ pub struct RateLimitRetryPolicy; impl RetryPolicy for RateLimitRetryPolicy { fn should_retry(&self, error: &TransportError) -> bool { match error { - TransportError::Transport(_) => true, + // There was a transport-level error. This is either a non-retryable error, + // or a server error that should be retried. + TransportError::Transport(err) => should_retry_transport_level_error(err), // The transport could not serialize the error itself. The request was malformed from // the start. TransportError::SerError(_) => false, @@ -65,6 +67,18 @@ impl RetryPolicy for RateLimitRetryPolicy { } } +/// Analyzes the [TransportErrorKind] and decides if the request should be retried based on the +/// variant. +fn should_retry_transport_level_error(error: &TransportErrorKind) -> bool { + match error { + // Missing batch response errors can be retried. + TransportErrorKind::MissingBatchResponse(_) => true, + // If the backend is gone, or there's a completely custom error, we should assume it's not + // retryable. + _ => false, + } +} + /// Analyzes the [ErrorPayload] and decides if the request should be retried based on the /// error code or the message. fn should_retry_json_rpc_error(error: &ErrorPayload) -> bool { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d40988a91260..a98dbf578347 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -562,6 +562,29 @@ impl Config { self } + /// Normalizes the evm version if a [SolcReq] is set + pub fn normalized_evm_version(mut self) -> Self { + self.normalize_evm_version(); + self + } + + /// Normalizes the evm version if a [SolcReq] is set to a valid version. + pub fn normalize_evm_version(&mut self) { + self.evm_version = self.get_normalized_evm_version(); + } + + /// Returns the normalized [EvmVersion] if a [SolcReq] is set to a valid version. + /// + /// Otherwise it returns the configured [EvmVersion]. + pub fn get_normalized_evm_version(&self) -> EvmVersion { + if let Some(SolcReq::Version(version)) = &self.solc { + if let Some(evm_version) = self.evm_version.normalize_version(version) { + return evm_version; + } + } + self.evm_version + } + /// Returns a sanitized version of the Config where are paths are set correctly and potential /// duplicates are resolved /// @@ -1554,10 +1577,30 @@ impl Config { figment = figment.merge(provider); figment } + + /// Check if any defaults need to be normalized. + /// + /// This normalizes the default `evm_version` if a `solc` was provided in the config. + /// + /// See also + fn normalize_defaults(&mut self, figment: Figment) -> Figment { + if let Ok(version) = figment.extract_inner::("solc") { + // check if evm_version is set + // TODO: add a warning if evm_version is provided but incompatible + if figment.find_value("evm_version").is_err() { + if let Some(version) = self.evm_version.normalize_version(&version) { + // normalize evm_version based on the provided solc version + self.evm_version = version; + } + } + } + + figment + } } impl From for Figment { - fn from(c: Config) -> Figment { + fn from(mut c: Config) -> Figment { let profile = Config::selected_profile(); let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); @@ -1624,6 +1667,9 @@ impl From for Figment { }; let merge = figment.merge(remappings); + // normalize defaults + let merge = c.normalize_defaults(merge); + Figment::from(c).merge(merge).select(profile) } } @@ -3883,7 +3929,7 @@ mod tests { #[test] fn can_handle_deviating_dapp_aliases() { figment::Jail::expect_with(|jail| { - let addr = Address::random(); + let addr = Address::ZERO; jail.set_env("DAPP_TEST_NUMBER", 1337); jail.set_env("DAPP_TEST_ADDRESS", format!("{addr:?}")); jail.set_env("DAPP_TEST_FUZZ_RUNS", 420); @@ -4383,6 +4429,23 @@ mod tests { }); } + #[test] + fn test_normalize_defaults() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r" + [default] + solc = '0.8.13' + ", + )?; + + let loaded = Config::load().sanitized(); + assert_eq!(loaded.evm_version, EvmVersion::London); + Ok(()) + }); + } + // a test to print the config, mainly used to update the example config in the README #[test] #[ignore] diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 819c82f430ce..5fdc7c408a83 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -10,7 +10,7 @@ mod cmd; mod opts; use cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch}; -use opts::{Opts, Subcommands}; +use opts::{Forge, ForgeSubcommand}; fn main() -> Result<()> { handler::install(); @@ -18,9 +18,9 @@ fn main() -> Result<()> { utils::subscriber(); utils::enable_paint(); - let opts = Opts::parse(); - match opts.sub { - Subcommands::Test(cmd) => { + let opts = Forge::parse(); + match opts.cmd { + ForgeSubcommand::Test(cmd) => { if cmd.is_watch() { utils::block_on(watch::watch_test(cmd)) } else { @@ -28,7 +28,7 @@ fn main() -> Result<()> { outcome.ensure_ok() } } - Subcommands::Script(cmd) => { + ForgeSubcommand::Script(cmd) => { // install the shell before executing the command foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( cmd.opts.args.silent, @@ -36,59 +36,59 @@ fn main() -> Result<()> { ))?; utils::block_on(cmd.run_script()) } - Subcommands::Coverage(cmd) => utils::block_on(cmd.run()), - Subcommands::Bind(cmd) => cmd.run(), - Subcommands::Build(cmd) => { + ForgeSubcommand::Coverage(cmd) => utils::block_on(cmd.run()), + ForgeSubcommand::Bind(cmd) => cmd.run(), + ForgeSubcommand::Build(cmd) => { if cmd.is_watch() { utils::block_on(watch::watch_build(cmd)) } else { cmd.run().map(|_| ()) } } - Subcommands::Debug(cmd) => utils::block_on(cmd.run()), - Subcommands::VerifyContract(args) => utils::block_on(args.run()), - Subcommands::VerifyCheck(args) => utils::block_on(args.run()), - Subcommands::Cache(cmd) => match cmd.sub { + 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::Cache(cmd) => match cmd.sub { CacheSubcommands::Clean(cmd) => cmd.run(), CacheSubcommands::Ls(cmd) => cmd.run(), }, - Subcommands::Create(cmd) => utils::block_on(cmd.run()), - Subcommands::Update(cmd) => cmd.run(), - Subcommands::Install(cmd) => cmd.run(), - Subcommands::Remove(cmd) => cmd.run(), - Subcommands::Remappings(cmd) => cmd.run(), - Subcommands::Init(cmd) => cmd.run(), - Subcommands::Completions { shell } => { - generate(shell, &mut Opts::command(), "forge", &mut std::io::stdout()); + ForgeSubcommand::Create(cmd) => utils::block_on(cmd.run()), + ForgeSubcommand::Update(cmd) => cmd.run(), + ForgeSubcommand::Install(cmd) => cmd.run(), + ForgeSubcommand::Remove(cmd) => cmd.run(), + ForgeSubcommand::Remappings(cmd) => cmd.run(), + ForgeSubcommand::Init(cmd) => cmd.run(), + ForgeSubcommand::Completions { shell } => { + generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout()); Ok(()) } - Subcommands::GenerateFigSpec => { + ForgeSubcommand::GenerateFigSpec => { clap_complete::generate( clap_complete_fig::Fig, - &mut Opts::command(), + &mut Forge::command(), "forge", &mut std::io::stdout(), ); Ok(()) } - Subcommands::Clean { root } => { + ForgeSubcommand::Clean { root } => { let config = utils::load_config_with_root(root); config.project()?.cleanup()?; Ok(()) } - Subcommands::Snapshot(cmd) => { + ForgeSubcommand::Snapshot(cmd) => { if cmd.is_watch() { utils::block_on(watch::watch_snapshot(cmd)) } else { utils::block_on(cmd.run()) } } - Subcommands::Fmt(cmd) => cmd.run(), - Subcommands::Config(cmd) => cmd.run(), - Subcommands::Flatten(cmd) => cmd.run(), - Subcommands::Inspect(cmd) => cmd.run(), - Subcommands::Tree(cmd) => cmd.run(), - Subcommands::Geiger(cmd) => { + ForgeSubcommand::Fmt(cmd) => cmd.run(), + ForgeSubcommand::Config(cmd) => cmd.run(), + ForgeSubcommand::Flatten(cmd) => cmd.run(), + ForgeSubcommand::Inspect(cmd) => cmd.run(), + ForgeSubcommand::Tree(cmd) => cmd.run(), + ForgeSubcommand::Geiger(cmd) => { let check = cmd.check; let n = cmd.run()?; if check && n > 0 { @@ -96,9 +96,9 @@ fn main() -> Result<()> { } Ok(()) } - Subcommands::Doc(cmd) => cmd.run(), - Subcommands::Selectors { command } => utils::block_on(command.run()), - Subcommands::Generate(cmd) => match cmd.sub { + ForgeSubcommand::Doc(cmd) => cmd.run(), + ForgeSubcommand::Selectors { command } => utils::block_on(command.run()), + ForgeSubcommand::Generate(cmd) => match cmd.sub { GenerateSubcommands::Test(cmd) => cmd.run(), }, } diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 3b9c48680e29..e62ac19083dc 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -39,14 +39,14 @@ const VERSION_MESSAGE: &str = concat!( after_help = "Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html", next_display_order = None, )] -pub struct Opts { +pub struct Forge { #[clap(subcommand)] - pub sub: Subcommands, + pub cmd: ForgeSubcommand, } #[derive(Subcommand)] #[allow(clippy::large_enum_variant)] -pub enum Subcommands { +pub enum ForgeSubcommand { /// Run the project's tests. #[clap(visible_alias = "t")] Test(test::TestArgs), @@ -168,3 +168,14 @@ pub enum Subcommands { /// Generate scaffold files. Generate(generate::GenerateArgs), } + +#[cfg(test)] +mod tests { + use super::*; + use clap::CommandFactory; + + #[test] + fn verify_cli() { + Forge::command().debug_assert(); + } +}