Repository: gakonst/ethers-rs
Pull request #1316 (2022-05-27 13:57:00)

fix: wrong unit for gas_price (ether -> gwei)


Patch:
diff --git a/.cargo/audit.toml b/.cargo/audit.toml
new file mode 100644
index 00000000..c73dfd47
--- /dev/null
+++ b/.cargo/audit.toml
@@ -0,0 +1,4 @@
+[advisories]
+ignore = [
+    "RUSTSEC-2021-0127", # serde_cbor dependency through the criterion dev-dependency
+]
diff --git a/.github/workflows/audit-on-push.yml b/.github/workflows/audit-on-push.yml
new file mode 100644
index 00000000..e46bb3cc
--- /dev/null
+++ b/.github/workflows/audit-on-push.yml
@@ -0,0 +1,14 @@
+name: Security audit
+on:
+  push:
+    paths:
+      - '**/Cargo.toml'
+      - '**/Cargo.lock'
+jobs:
+  security_audit:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v1
+      - uses: actions-rs/audit-check@v1
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2ceb2b44..829f3edc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,12 +21,10 @@ jobs:
     steps:
       - name: Checkout sources
         uses: actions/checkout@v2
-      - name: Set up node
-        uses: actions/setup-node@v1
+      - name: Install Anvil
+        uses: foundry-rs/foundry-toolchain@v1
         with:
-          node-version: 10
-      - name: Install ganache
-        run: npm install -g ganache
+          version: nightly
       - name: Install Solc
         run: |
             mkdir -p "$HOME/bin"
@@ -66,13 +64,11 @@ jobs:
     steps:
       - name: Checkout sources
         uses: actions/checkout@v2
-      - name: Set up node
-        uses: actions/setup-node@v1
-        with:
-          node-version: 10
       # TODO: can we combine these shared steps in github actions?
-      - name: Install ganache
-        run: npm install -g ganache
+      - name: Install Anvil
+        uses: foundry-rs/foundry-toolchain@v1
+        with:
+          version: nightly
       - name: Install Solc
         run: |
             mkdir -p "$HOME/bin"
@@ -133,12 +129,8 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - name: Checkout
-        uses: actions/checkout@v1
-      - name: Install node
-        uses: actions/setup-node@v1
-        with:
-          node-version: 10
+      - name: Checkout sources
+        uses: actions/checkout@v2
       - name: Install rust
         uses: actions-rs/toolchain@v1
         with:
@@ -147,6 +139,11 @@ jobs:
           profile: minimal
           override: true
 
+      - name: Install Anvil
+        uses: foundry-rs/foundry-toolchain@v1
+        with:
+          version: nightly
+
       - uses: Swatinem/rust-cache@v1
         with:
           cache-on-failure: true
@@ -157,11 +154,8 @@ jobs:
           command: check
           args: --target wasm32-unknown-unknown
 
-      - name: Launch Ganache
-        run: |
-          cd examples/ethers-wasm
-          npm install
-          npm run ganache &
+      - name: Launch Anvil
+        run: anvil --block-time 2 -m "stuff inherit faith park genre spread huge knee ecology private marble supreme" &
 
       - name: Install wasm-pack
         run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -176,59 +170,66 @@ jobs:
           cd examples/ethers-wasm
           wasm-pack test --headless --chrome
 
-  # TODO: Re-enable once we figure out why examples are running forever.
-  # examples:
-  #   name: Examples
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - name: Checkout sources
-  #       uses: actions/checkout@v2
-  #     - name: Set up node
-  #       uses: actions/setup-node@v1
-  #       with:
-  #         node-version: 10
-  #     - name: Install ganache
-  #       run: npm install -g ganache
-  #     - name: Install Solc
-  #       run: |
-  #           mkdir -p "$HOME/bin"
-  #           wget -q https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux -O $HOME/bin/solc
-  #           chmod u+x "$HOME/bin/solc"
-  #           export PATH=$HOME/bin:$PATH
-  #           solc --version
+  examples:
+    name: Examples
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout sources
+        uses: actions/checkout@v2
+
+      - name: Install Anvil
+        uses: foundry-rs/foundry-toolchain@v1
+        with:
+          version: nightly
+
+      - name: Install Solc
+        run: |
+            mkdir -p "$HOME/bin"
+            wget -q https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux -O $HOME/bin/solc
+            chmod u+x "$HOME/bin/solc"
+            export PATH=$HOME/bin:$PATH
+            solc --version
+
+      - name: Install stable toolchain
+        uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+          components: rustfmt, clippy
+      - uses: Swatinem/rust-cache@v1
+        with:
+          cache-on-failure: true
+      - name: Build all examples
+        run: |
+            export PATH=$HOME/bin:$PATH
+            examples=$(cargo metadata --format-version 1 | \
+              jq -c '.packages[]
+              | select(.name == "ethers")
+              | .targets[]
+              | select(.kind[] | contains("example"))
+              | with_entries(select([.key]
+              | inside(["name", "required-features"])))'
+            )
+            for example in $examples; do
+              name="$(echo "$example" | jq -r '.name')"
+              args=(
+                -p ethers
+                --example "$name"
+              )
+              features="$(echo "$example" | jq -r 'try(."required-features" | join(","))')"
+              if [[ ! -z "$features" ]]; then
+                args+=(--features "$features")
+              fi
 
-  #     - name: Install geth
-  #       run: |
-  #           mkdir -p "$HOME/bin"
-  #           wget -q https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.23-8c2f2715.tar.gz
-  #           tar -xvf geth-linux-amd64-1.9.23-8c2f2715.tar.gz
-  #           mv geth-linux-amd64-1.9.23-8c2f2715/geth $HOME/bin/geth
-  #           chmod u+x "$HOME/bin/geth"
-  #           export PATH=$HOME/bin:$PATH
-  #           geth version
-  #     - name: Install stable toolchain
-  #       uses: actions-rs/toolchain@v1
-  #       with:
-  #         profile: minimal
-  #         toolchain: stable
-  #         override: true
-  #         components: rustfmt, clippy
-  #     - uses: Swatinem/rust-cache@v1
-  #       with:
-  #         cache-on-failure: true
-  #     - name: Build all examples
-  #       run: |
-  #           export PATH=$HOME/bin:$PATH
-  #           for file in examples/*.rs; do
-  #             name="$(echo "$file" | cut -f 1 -d '.')"
-  #             echo "building $name"
-  #             cargo build -p ethers --example "$(basename "$name")"
-  #           done
-  #     - name: Run all examples
-  #       run: |
-  #           export PATH=$HOME/bin:$PATH
-  #           chmod +x ./scripts/examples.sh
-  #           ./scripts/examples.sh
+              echo "building $name"
+              cargo build "${args[@]}"
+            done
+      - name: Run all examples
+        run: |
+            export PATH=$HOME/bin:$PATH
+            chmod +x ./scripts/examples.sh
+            ./scripts/examples.sh
 
   windows-build:
     runs-on: windows-latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8b2a0ac..c0dab0e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@
 
 ### Unreleased
 
+- Add `as_*_mut` methods on `TypedTransaction`
+  [#1310](https://github.com/gakonst/ethers-rs/pull/1310)
 - AWS EIP712 data signing no longer signs with EIP155
 - Added Cronos testnet to etherscan options [1276](https://github.com/gakonst/ethers-rs/pull/1276)
 - Fix parsing of a pending block
@@ -69,6 +71,7 @@
   [#1184](https://github.com/gakonst/ethers-rs/pull/1184)
 - Add `From<H160>` and From<Vec<H160>> traits to `ValueOrArray<H160>` [#1199](https://github.com/gakonst/ethers-rs/pull/1200)
 - Fix handling of Websocket connection errors [#1287](https://github.com/gakonst/ethers-rs/pull/1287)
+- Add Arithmetic Shift Right operation for I256 [#1323](https://github.com/gakonst/ethers-rs/issues/1323)
 
 ## ethers-contract-abigen
 
@@ -96,6 +99,8 @@
 
 ### Unreleased
 
+- Use relative source paths and `solc --base-path`
+  [#1317](https://github.com/gakonst/ethers-rs/pull/1317)
 - Save cache entry objects with relative paths
   [#1307](https://github.com/gakonst/ethers-rs/pull/1307)
 - Bundle svm, svm-builds and sha2 dependencies in new `svm-solc` feature
diff --git a/Cargo.lock b/Cargo.lock
index 139b820c..1a4b1d27 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1258,6 +1258,7 @@ dependencies = [
  "rand 0.8.5",
  "rlp",
  "rlp-derive",
+ "rust_decimal",
  "serde",
  "serde_json",
  "strum",
@@ -1345,7 +1346,7 @@ dependencies = [
  "hex",
  "http",
  "once_cell",
- "parking_lot",
+ "parking_lot 0.11.2",
  "pin-project",
  "reqwest",
  "serde",
@@ -1354,7 +1355,6 @@ dependencies = [
  "thiserror",
  "tokio",
  "tokio-tungstenite",
- "tokio-util 0.7.2",
  "tracing",
  "tracing-futures",
  "url",
@@ -1751,7 +1751,7 @@ dependencies = [
  "indexmap",
  "slab",
  "tokio",
- "tokio-util 0.6.9",
+ "tokio-util",
  "tracing",
 ]
 
@@ -2136,9 +2136,9 @@ dependencies = [
 
 [[package]]
 name = "lock_api"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
 dependencies = [
  "scopeguard",
 ]
@@ -2216,24 +2216,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
 
 [[package]]
 name = "mio"
-version = "0.7.14"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
+checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
 dependencies = [
  "libc",
  "log",
- "miow",
- "ntapi",
- "winapi",
-]
-
-[[package]]
-name = "miow"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
-dependencies = [
- "winapi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
 ]
 
 [[package]]
@@ -2273,15 +2263,6 @@ dependencies = [
  "void",
 ]
 
-[[package]]
-name = "ntapi"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "num-integer"
 version = "0.1.44"
@@ -2458,7 +2439,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
- "parking_lot_core",
+ "parking_lot_core 0.8.5",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.3",
 ]
 
 [[package]]
@@ -2475,6 +2466,19 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "parking_lot_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
 [[package]]
 name = "password-hash"
 version = "0.2.3"
@@ -3153,6 +3157,17 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "rust_decimal"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2ee7337df68898256ad0d4af4aad178210d9e44d2ff900ce44064a97cd86530"
+dependencies = [
+ "arrayvec 0.7.2",
+ "num-traits",
+ "serde",
+]
+
 [[package]]
 name = "rustc-hex"
 version = "2.1.0"
@@ -3317,9 +3332,9 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
 dependencies = [
  "serde_derive",
 ]
@@ -3346,9 +3361,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3385,7 +3400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5bcc41d18f7a1d50525d080fd3e953be87c4f9f1a974f3c21798ca00d54ec15"
 dependencies = [
  "lazy_static",
- "parking_lot",
+ "parking_lot 0.11.2",
  "serial_test_derive",
 ]
 
@@ -3608,7 +3623,7 @@ checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
 dependencies = [
  "lazy_static",
  "new_debug_unreachable",
- "parking_lot",
+ "parking_lot 0.11.2",
  "phf_shared 0.8.0",
  "precomputed-hash",
 ]
@@ -3852,9 +3867,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.16.0"
+version = "1.18.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95e99d373042c30406d317cfc5bfad7b5d604bdd31dab72cf8739abebaa64aee"
+checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
 dependencies = [
  "bytes",
  "libc",
@@ -3862,9 +3877,10 @@ dependencies = [
  "mio",
  "num_cpus",
  "once_cell",
- "parking_lot",
+ "parking_lot 0.12.0",
  "pin-project-lite",
  "signal-hook-registry",
+ "socket2",
  "tokio-macros",
  "winapi",
 ]
@@ -3933,19 +3949,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "tokio-util"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
 [[package]]
 name = "toml"
 version = "0.5.8"
@@ -4218,6 +4221,12 @@ version = "0.10.2+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
 [[package]]
 name = "wasm-bindgen"
 version = "0.2.80"
@@ -4318,7 +4327,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
 dependencies = [
  "futures",
  "js-sys",
- "parking_lot",
+ "parking_lot 0.11.2",
  "pin-utils",
  "wasm-bindgen",
  "wasm-bindgen-futures",
@@ -4397,6 +4406,49 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
 [[package]]
 name = "winreg"
 version = "0.10.1"
diff --git a/Cargo.toml b/Cargo.toml
index de7a3807..2f26d587 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -101,7 +101,7 @@ eyre = "0.6"
 rand = "0.8.5"
 serde = { version = "1.0.124", features = ["derive"] }
 serde_json = "1.0.64"
-tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] }
+tokio = { version = "1.18", features = ["macros", "rt-multi-thread"] }
 hex = "0.4.3"
 bytes = "1.1.0"
 
@@ -115,17 +115,14 @@ opt-level = "s"
 [[example]]
 name = "contract_human_readable"
 path = "examples/contract_human_readable.rs"
-required-features = ["legacy"]
 
 [[example]]
 name = "contract_with_abi"
 path = "examples/contract_with_abi.rs"
-required-features = ["legacy"]
 
 [[example]]
 name = "contract_with_abi_and_bytecode"
 path = "examples/contract_with_abi_and_bytecode.rs"
-required-features = ["legacy"]
 
 [[example]]
 name = "ipc"
@@ -151,3 +148,8 @@ required-features = ["trezor"]
 name = "yubi"
 path = "examples/yubi.rs"
 required-features = ["yubi"]
+
+[[example]]
+name = "paginated_logs"
+path = "examples/paginated_logs.rs"
+required-features = ["rustls"]
diff --git a/README.md b/README.md
index 2d4fdcde..5e76b95a 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ ethers = { git = "https://github.com/gakonst/ethers-rs" }
 Tests require the following installed:
 
 1. [`solc`](https://solidity.readthedocs.io/en/latest/installing-solidity.html) (>=0.8.10). We also recommend using [solc-select](https://github.com/crytic/solc-select) for more flexibility.
-2. [`ganache-cli`](https://github.com/trufflesuite/ganache-cli#installation)
+2. [`anvil`](https://github.com/foundry-rs/foundry/blob/master/anvil/README.md)
 3. [`geth`](https://github.com/ethereum/go-ethereum)
 
 In addition, it is recommended that you set the `ETHERSCAN_API_KEY` environment variable
diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml
index 6126f97c..212311ee 100644
--- a/ethers-contract/Cargo.toml
+++ b/ethers-contract/Cargo.toml
@@ -35,7 +35,7 @@ ethers-derive-eip712 = { version = "^0.2.0", path = "../ethers-core/ethers-deriv
 ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
-tokio = { version = "1.5", default-features = false, features = ["macros"] }
+tokio = { version = "1.18", default-features = false, features = ["macros"] }
 
 [features]
 eip712 = ["ethers-derive-eip712", "ethers-core/eip712"]
diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs
index ad4306d6..1b33ff86 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract.rs
@@ -49,8 +49,8 @@ impl ExpandedContract {
            // export all the created data types
             pub use #module::*;
 
-            #[allow(clippy::too_many_arguments)]
-            mod #module {
+            #[allow(clippy::too_many_arguments, non_camel_case_types)]
+            pub mod #module {
                 #imports
                 #contract
                 #events
diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs
index 3a87fb9e..594a5797 100644
--- a/ethers-contract/ethers-contract-abigen/src/multi.rs
+++ b/ethers-contract/ethers-contract-abigen/src/multi.rs
@@ -558,13 +558,13 @@ serde_json = "1.0.79"
 
     /// Append module declarations to the `lib.rs` or `mod.rs`
     fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
-        let mut mod_names: BTreeSet<_> = self.bindings.keys().collect();
+        let mut mod_names: BTreeSet<_> =
+            self.bindings.keys().map(|name| name.to_snake_case()).collect();
         if let Some(ref shared) = self.shared_types {
-            mod_names.insert(&shared.name);
+            mod_names.insert(shared.name.to_snake_case());
         }
 
-        for module in mod_names.into_iter().map(|name| format!("pub mod {};", name.to_snake_case()))
-        {
+        for module in mod_names.into_iter().map(|name| format!("pub mod {};", name)) {
             writeln!(buf, "{}", module)?;
         }
 
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index e7ee219e..7f80634f 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -40,6 +40,7 @@ cargo_metadata = { version = "0.14.2", optional = true }
 convert_case = { version = "0.5.0", optional = true }
 syn = { version = "1.0.95", optional = true }
 proc-macro2 = { version = "1.0.39", optional = true }
+rust_decimal = "1.23.1"
 
 [dev-dependencies]
 serde_json = { version = "1.0.64", default-features = false }
diff --git a/ethers-core/src/abi/codec.rs b/ethers-core/src/abi/codec.rs
index 283bd076..d9f6c456 100644
--- a/ethers-core/src/abi/codec.rs
+++ b/ethers-core/src/abi/codec.rs
@@ -2,7 +2,7 @@ use crate::{
     abi::{
         AbiArrayType, AbiError, AbiType, Detokenize, Token, Tokenizable, TokenizableItem, Tokenize,
     },
-    types::{Address, Bytes, H256, U128, U256},
+    types::{Address, Bytes, H256, I256, U128, U256},
 };
 
 /// Trait for ABI encoding
@@ -63,6 +63,7 @@ impl_abi_codec!(
     H256,
     U128,
     U256,
+    I256,
     u8,
     u16,
     u32,
diff --git a/ethers-core/src/types/i256.rs b/ethers-core/src/types/i256.rs
index e568c8b2..6a1c4dba 100644
--- a/ethers-core/src/types/i256.rs
+++ b/ethers-core/src/types/i256.rs
@@ -10,7 +10,9 @@ use serde::{Deserialize, Serialize};
 use std::{
     cmp,
     convert::{TryFrom, TryInto},
-    fmt, i128, i64, iter, ops, str, u64,
+    fmt, i128, i64, iter, ops,
+    ops::Sub,
+    str, u64,
 };
 use thiserror::Error;
 
@@ -935,6 +937,35 @@ impl I256 {
         let (result, _) = self.overflowing_pow(exp);
         result
     }
+
+    /// Arithmetic Shift Right operation. Shifts `shift` number of times to the right maintaining
+    /// the original sign. If the number is positive this is the same as logic shift right.
+    pub fn asr(self, shift: u32) -> Self {
+        // Avoid shifting if we are going to know the result regardless of the value.
+        if shift == 0 {
+            self
+        } else if shift >= 255u32 {
+            match self.sign() {
+                // It's always going to be zero (i.e. 00000000...00000000)
+                Sign::Positive => Self::zero(),
+                // It's always going to be -1 (i.e. 11111111...11111111)
+                Sign::Negative => Self::from(-1i8),
+            }
+        } else {
+            // Perform the shift.
+            match self.sign() {
+                Sign::Positive => self >> shift,
+                // We need to do: `for 0..shift { self >> 1 | 2^255 }`
+                // We can avoid the loop by doing: `self >> shift | ~(2^(255 - shift) - 1)`
+                // where '~' represents ones complement
+                Sign::Negative => {
+                    let bitwise_or =
+                        Self::from_raw(!U256::from(2u8).pow(U256::from(255u32 - shift)).sub(1u8));
+                    (self >> shift) | bitwise_or
+                }
+            }
+        }
+    }
 }
 
 macro_rules! impl_from {
@@ -1276,6 +1307,7 @@ mod tests {
     use crate::abi::Tokenizable;
     use once_cell::sync::Lazy;
     use serde_json::json;
+    use std::ops::Neg;
 
     static MIN_ABS: Lazy<U256> = Lazy::new(|| U256::from(1) << 255);
 
@@ -1521,6 +1553,33 @@ mod tests {
         assert_eq!(I256::MIN >> 255, I256::one());
     }
 
+    #[test]
+    fn arithmetic_shift_right() {
+        let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
+        let expected_result = I256::from_raw(U256::MAX.sub(1u8));
+        assert_eq!(value.asr(253u32), expected_result, "1011...1111 >> 253 was not 1111...1110");
+
+        let value = I256::from(-1i8);
+        let expected_result = I256::from(-1i8);
+        assert_eq!(value.asr(250u32), expected_result, "-1 >> any_amount was not -1");
+
+        let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
+        let expected_result = I256::from(-1i8);
+        assert_eq!(value.asr(255u32), expected_result, "1011...1111 >> 255 was not -1");
+
+        let value = I256::from_raw(U256::from(2u8).pow(U256::from(254u8))).neg();
+        let expected_result = I256::from(-1i8);
+        assert_eq!(value.asr(1024u32), expected_result, "1011...1111 >> 1024 was not -1");
+
+        let value = I256::from(1024i32);
+        let expected_result = I256::from(32i32);
+        assert_eq!(value.asr(5u32), expected_result, "1024 >> 5 was not 32");
+
+        let value = I256::MAX;
+        let expected_result = I256::zero();
+        assert_eq!(value.asr(255u32), expected_result, "I256::MAX >> 255 was not 0");
+    }
+
     #[test]
     fn addition() {
         assert_eq!(I256::MIN.overflowing_add(I256::MIN), (I256::zero(), true));
diff --git a/ethers-core/src/types/transaction/eip2718.rs b/ethers-core/src/types/transaction/eip2718.rs
index b87f0abf..6c5fe479 100644
--- a/ethers-core/src/types/transaction/eip2718.rs
+++ b/ethers-core/src/types/transaction/eip2718.rs
@@ -417,6 +417,25 @@ impl TypedTransaction {
             _ => None,
         }
     }
+
+    pub fn as_legacy_mut(&mut self) -> Option<&mut TransactionRequest> {
+        match self {
+            Legacy(tx) => Some(tx),
+            _ => None,
+        }
+    }
+    pub fn as_eip2930_mut(&mut self) -> Option<&mut Eip2930TransactionRequest> {
+        match self {
+            Eip2930(tx) => Some(tx),
+            _ => None,
+        }
+    }
+    pub fn as_eip1559_mut(&mut self) -> Option<&mut Eip1559TransactionRequest> {
+        match self {
+            Eip1559(tx) => Some(tx),
+            _ => None,
+        }
+    }
 }
 
 impl TypedTransaction {
diff --git a/ethers-core/src/utils/anvil.rs b/ethers-core/src/utils/anvil.rs
index 37a15ebd..b762fbb3 100644
--- a/ethers-core/src/utils/anvil.rs
+++ b/ethers-core/src/utils/anvil.rs
@@ -210,7 +210,6 @@ mod tests {
     use super::*;
 
     #[test]
-    #[ignore]
     fn can_launch_anvil() {
         let _ = Anvil::new().spawn();
     }
diff --git a/ethers-core/src/utils/ganache.rs b/ethers-core/src/utils/ganache.rs
index d591ee49..d78691ed 100644
--- a/ethers-core/src/utils/ganache.rs
+++ b/ethers-core/src/utils/ganache.rs
@@ -222,11 +222,13 @@ mod tests {
     use super::*;
 
     #[test]
+    #[ignore]
     fn configurable_startup_timeout() {
         Ganache::new().startup_timeout_millis(100000_u64).spawn();
     }
 
     #[test]
+    #[ignore]
     fn default_startup_works() {
         Ganache::new().spawn();
     }
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 000a5335..1703276f 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -50,6 +50,8 @@ pub enum ConversionError {
     InvalidFloat(#[from] std::num::ParseFloatError),
     #[error(transparent)]
     FromDecStrError(#[from] FromDecStrErr),
+    #[error(transparent)]
+    DecimalError(#[from] rust_decimal::Error),
 }
 
 /// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
@@ -147,9 +149,12 @@ where
     S: ToString,
     K: TryInto<Units, Error = ConversionError> + Copy,
 {
-    let float_n: f64 =
-        amount.to_string().parse::<f64>()? * 10u64.pow(units.try_into()?.as_num()) as f64;
-    let u256_n: U256 = U256::from_dec_str(&float_n.round().to_string())?;
+    use rust_decimal::Decimal;
+    let num: Decimal = amount.to_string().parse()?;
+    let multiplier: Decimal = 10u64.pow(units.try_into()?.as_num()).into();
+    let val =
+        num.checked_mul(multiplier).ok_or(rust_decimal::Error::ExceedsMaximumPossibleValue)?;
+    let u256_n: U256 = U256::from_dec_str(&val.round().to_string())?;
     Ok(u256_n)
 }
 /// The address for an Ethereum contract is deterministically computed from the
@@ -446,6 +451,9 @@ mod tests {
 
         let eth = parse_units(1, "ether").unwrap();
         assert_eq!(eth, WEI_IN_ETHER);
+
+        let val = parse_units("2.3", "ether").unwrap();
+        assert_eq!(val, U256::from_dec_str("2300000000000000000").unwrap());
     }
 
     #[test]
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index 32611707..23c40687 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -26,7 +26,7 @@ semver = "1.0.9"
 
 [dev-dependencies]
 tempfile = "3.3.0"
-tokio = { version = "1.5", features = ["macros", "rt-multi-thread", "time"] }
+tokio = { version = "1.18", features = ["macros", "rt-multi-thread", "time"] }
 serial_test = "0.6.0"
 tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] }
 
diff --git a/ethers-etherscan/src/errors.rs b/ethers-etherscan/src/errors.rs
index c6780ab4..8a23bc7b 100644
--- a/ethers-etherscan/src/errors.rs
+++ b/ethers-etherscan/src/errors.rs
@@ -27,7 +27,7 @@ pub enum EtherscanError {
     RateLimitExceeded,
     #[error(transparent)]
     IO(#[from] std::io::Error),
-    #[error("Local networks (e.g. ganache, geth --dev) cannot be indexed by etherscan")]
+    #[error("Local networks (e.g. anvil, ganache, geth --dev) cannot be indexed by etherscan")]
     LocalNetworksNotSupported,
     #[error("Unknown error: {0}")]
     Unknown(String),
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index d043951b..a1a3df0b 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -36,7 +36,7 @@ serde_json = { version = "1.0.64", default-features = false }
 instant = {version = "0.1.12", features = ["now"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-tokio = { version = "1.5" }
+tokio = { version = "1.18" }
 
 [dev-dependencies]
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
@@ -47,7 +47,7 @@ ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features =
 serial_test = "0.6.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
-tokio = { version = "1.5", default-features = false, features = ["rt", "macros", "time"] }
+tokio = { version = "1.18", default-features = false, features = ["rt", "macros", "time"] }
 
 [features]
 celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo", "ethers-contract/celo"]
diff --git a/ethers-middleware/src/gas_oracle/polygon.rs b/ethers-middleware/src/gas_oracle/polygon.rs
index a450a3ab..47a3415d 100644
--- a/ethers-middleware/src/gas_oracle/polygon.rs
+++ b/ethers-middleware/src/gas_oracle/polygon.rs
@@ -87,5 +87,5 @@ impl GasOracle for Polygon {
 }
 
 fn from_gwei(gwei: f64) -> U256 {
-    u256_from_f64_saturating(gwei * 1.0e18_f64)
+    u256_from_f64_saturating(gwei * 1.0e9_f64)
 }
diff --git a/ethers-middleware/src/signer.rs b/ethers-middleware/src/signer.rs
index 746d2979..8166d6b8 100644
--- a/ethers-middleware/src/signer.rs
+++ b/ethers-middleware/src/signer.rs
@@ -336,7 +336,7 @@ mod tests {
     use super::*;
     use ethers_core::{
         types::TransactionRequest,
-        utils::{self, keccak256, Ganache},
+        utils::{self, keccak256, Anvil},
     };
     use ethers_providers::Provider;
     use ethers_signers::LocalWallet;
@@ -360,11 +360,10 @@ mod tests {
         let chain_id = 1u64;
 
         // Signer middlewares now rely on a working provider which it can query the chain id from,
-        // so we make sure ganache is started with the chain id that the expected tx was signed
+        // so we make sure Anvil is started with the chain id that the expected tx was signed
         // with
-        let ganache =
-            Ganache::new().args(vec!["--chain.chainId".to_string(), chain_id.to_string()]).spawn();
-        let provider = Provider::try_from(ganache.endpoint()).unwrap();
+        let anvil = Anvil::new().args(vec!["--chain-id".to_string(), chain_id.to_string()]).spawn();
+        let provider = Provider::try_from(anvil.endpoint()).unwrap();
         let key = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
             .parse::<LocalWallet>()
             .unwrap()
@@ -384,11 +383,11 @@ mod tests {
     }
 
     #[tokio::test]
-    async fn ganache_consistent_chainid() {
-        let ganache = Ganache::new().spawn();
-        let provider = Provider::try_from(ganache.endpoint()).unwrap();
+    async fn anvil_consistent_chainid() {
+        let anvil = Anvil::new().spawn();
+        let provider = Provider::try_from(anvil.endpoint()).unwrap();
         let chain_id = provider.get_chainid().await.unwrap();
-        assert_eq!(chain_id, U256::from(1337));
+        assert_eq!(chain_id, U256::from(31337));
 
         // Intentionally do not set the chain id here so we ensure that the signer pulls the
         // provider's chain id.
@@ -406,9 +405,9 @@ mod tests {
     }
 
     #[tokio::test]
-    async fn ganache_consistent_chainid_not_default() {
-        let ganache = Ganache::new().args(vec!["--chain.chainId", "13371337"]).spawn();
-        let provider = Provider::try_from(ganache.endpoint()).unwrap();
+    async fn anvil_consistent_chainid_not_default() {
+        let anvil = Anvil::new().args(vec!["--chain-id", "13371337"]).spawn();
+        let provider = Provider::try_from(anvil.endpoint()).unwrap();
         let chain_id = provider.get_chainid().await.unwrap();
         assert_eq!(chain_id, U256::from(13371337));
 
@@ -429,9 +428,9 @@ mod tests {
 
     #[tokio::test]
     async fn handles_tx_from_field() {
-        let ganache = Ganache::new().spawn();
-        let acc = ganache.addresses()[0];
-        let provider = Provider::try_from(ganache.endpoint()).unwrap();
+        let anvil = Anvil::new().spawn();
+        let acc = anvil.addresses()[0];
+        let provider = Provider::try_from(anvil.endpoint()).unwrap();
         let key = LocalWallet::new(&mut rand::thread_rng()).with_chain_id(1u32);
         provider
             .send_transaction(
@@ -459,7 +458,7 @@ mod tests {
         assert_eq!(tx.from, client.address());
 
         // signing a TransactionRequest with a from address that is not the
-        // signer should result in the default ganache account being used
+        // signer should result in the default anvil account being used
         let request_from_other = request.from(acc);
         let hash = *client.send_transaction(request_from_other, None).await.unwrap();
         let tx = client.get_transaction(hash).await.unwrap().unwrap();
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index 213e3b54..3c216993 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -44,8 +44,7 @@ hashers = "1.0.1"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 # tokio
-tokio-util = { version = "0.7.2", default-features = false, features = ["io"], optional = true }
-tokio = { version = "1.5", default-features = false, optional = true }
+tokio = { version = "1.18", features = ["time"] }
 tokio-tungstenite = { version = "0.17.1", default-features = false, features = ["connect"], optional = true }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
@@ -58,14 +57,14 @@ wasm-timer = "0.2"
 parking_lot = { version = "0.11", features = ["wasm-bindgen"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
-tokio = { version = "1.5", default-features = false, features = ["rt", "macros"] }
+tokio = { version = "1.18", default-features = false, features = ["rt", "macros", "time"] }
 tempfile = "3.3.0"
 
 [features]
 default = ["ws", "rustls"]
 celo = ["ethers-core/celo"]
-ws = ["tokio", "tokio-tungstenite", "futures-channel"]
-ipc = ["tokio", "tokio/io-util", "tokio-util", "bytes", "futures-channel"]
+ws = ["tokio-tungstenite", "futures-channel"]
+ipc = ["tokio/io-util", "bytes", "futures-channel"]
 
 openssl = ["tokio-tungstenite/native-tls", "reqwest/native-tls"]
 # we use the webpki roots so we can build static binaries w/o any root cert dependencies
diff --git a/ethers-providers/src/log_query.rs b/ethers-providers/src/log_query.rs
index 77cdaee3..43cf7086 100644
--- a/ethers-providers/src/log_query.rs
+++ b/ethers-providers/src/log_query.rs
@@ -1,4 +1,4 @@
-use super::{JsonRpcClient, Middleware, PinBoxFut, Provider};
+use super::{JsonRpcClient, Middleware, PinBoxFut, Provider, ProviderError};
 use ethers_core::types::{Filter, Log, U64};
 use futures_core::stream::Stream;
 use std::{
@@ -6,6 +6,7 @@ use std::{
     pin::Pin,
     task::{Context, Poll},
 };
+use thiserror::Error;
 
 pub struct LogQuery<'a, P> {
     provider: &'a Provider<P>,
@@ -55,11 +56,19 @@ macro_rules! rewake_with_new_state {
     };
 }
 
+#[derive(Error, Debug)]
+pub enum LogQueryError<E> {
+    #[error(transparent)]
+    LoadLastBlockError(E),
+    #[error(transparent)]
+    LoadLogsError(E),
+}
+
 impl<'a, P> Stream for LogQuery<'a, P>
 where
     P: JsonRpcClient,
 {
-    type Item = Log;
+    type Item = Result<Log, LogQueryError<ProviderError>>;
 
     fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
         match &mut self.state {
@@ -77,27 +86,32 @@ where
                 }
             }
             LogQueryState::LoadLastBlock(fut) => {
-                self.last_block = Some(
-                    futures_util::ready!(fut.as_mut().poll(ctx))
-                        .expect("error occurred loading last block"),
-                );
+                match futures_util::ready!(fut.as_mut().poll(ctx)) {
+                    Ok(last_block) => {
+                        self.last_block = Some(last_block);
 
-                let from_block = self.filter.get_from_block().unwrap();
-                let to_block = from_block + self.page_size;
-                self.from_block = Some(to_block);
+                        // this is okay because we will only enter this state when the filter is
+                        // paginatable i.e. from block is set
+                        let from_block = self.filter.get_from_block().unwrap();
+                        let to_block = from_block + self.page_size;
+                        self.from_block = Some(to_block + 1);
 
-                let filter = self.filter.clone().from_block(from_block).to_block(to_block);
-                let provider = self.provider;
-                // load first page of logs
-                let fut = Box::pin(async move { provider.get_logs(&filter).await });
-                rewake_with_new_state!(ctx, self, LogQueryState::LoadLogs(fut));
-            }
-            LogQueryState::LoadLogs(fut) => {
-                let logs = futures_util::ready!(fut.as_mut().poll(ctx))
-                    .expect("error occurred loading logs");
-                self.current_logs = VecDeque::from(logs);
-                rewake_with_new_state!(ctx, self, LogQueryState::Consume);
+                        let filter = self.filter.clone().from_block(from_block).to_block(to_block);
+                        let provider = self.provider;
+                        // load first page of logs
+                        let fut = Box::pin(async move { provider.get_logs(&filter).await });
+                        rewake_with_new_state!(ctx, self, LogQueryState::LoadLogs(fut));
+                    }
+                    Err(err) => Poll::Ready(Some(Err(LogQueryError::LoadLastBlockError(err)))),
+                }
             }
+            LogQueryState::LoadLogs(fut) => match futures_util::ready!(fut.as_mut().poll(ctx)) {
+                Ok(logs) => {
+                    self.current_logs = VecDeque::from(logs);
+                    rewake_with_new_state!(ctx, self, LogQueryState::Consume);
+                }
+                Err(err) => Poll::Ready(Some(Err(LogQueryError::LoadLogsError(err)))),
+            },
             LogQueryState::Consume => {
                 let log = self.current_logs.pop_front();
                 if log.is_none() {
@@ -106,15 +120,17 @@ where
                         Poll::Ready(None)
                     } else {
                         // load new logs if there are still more pages to go through
+                        // can safely assume this will always be set in this state
                         let from_block = self.from_block.unwrap();
                         let to_block = from_block + self.page_size;
 
                         // no more pages to load, and everything is consumed
+                        // can safely assume this will always be set in this state
                         if from_block > self.last_block.unwrap() {
                             return Poll::Ready(None)
                         }
                         // load next page
-                        self.from_block = Some(to_block);
+                        self.from_block = Some(to_block + 1);
 
                         let filter = self.filter.clone().from_block(from_block).to_block(to_block);
                         let provider = self.provider;
@@ -122,7 +138,7 @@ where
                         rewake_with_new_state!(ctx, self, LogQueryState::LoadLogs(fut));
                     }
                 } else {
-                    Poll::Ready(log)
+                    Poll::Ready(log.map(Ok))
                 }
             }
         }
diff --git a/ethers-providers/src/pending_escalator.rs b/ethers-providers/src/pending_escalator.rs
index 3dca65fb..b813f986 100644
--- a/ethers-providers/src/pending_escalator.rs
+++ b/ethers-providers/src/pending_escalator.rs
@@ -190,7 +190,7 @@ where
                 poll_broadcast_fut!(cx, this, fut);
             }
             Sleeping(delay) => {
-                futures_util::ready!(delay.as_mut().poll(cx));
+                let _ready = futures_util::ready!(delay.as_mut().poll(cx));
                 // if broadcast timer has elapsed and if we have a TX to
                 // broadcast, broadcast it
                 if this.last.elapsed() > *this.broadcast_interval {
diff --git a/ethers-providers/src/pending_transaction.rs b/ethers-providers/src/pending_transaction.rs
index 68757aa8..65549f20 100644
--- a/ethers-providers/src/pending_transaction.rs
+++ b/ethers-providers/src/pending_transaction.rs
@@ -30,15 +30,15 @@ use wasm_timer::Delay;
 ///
 ///```
 /// # use ethers_providers::{Provider, Http};
-/// # use ethers_core::utils::Ganache;
+/// # use ethers_core::utils::Anvil;
 /// # use std::convert::TryFrom;
 /// use ethers_providers::Middleware;
 /// use ethers_core::types::TransactionRequest;
 ///
 /// # #[tokio::main(flavor = "current_thread")]
 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
-/// # let ganache = Ganache::new().spawn();
-/// # let client = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+/// # let anvil = Anvil::new().spawn();
+/// # let client = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
 /// # let accounts = client.get_accounts().await?;
 /// # let from = accounts[0];
 /// # let to = accounts[1];
@@ -172,7 +172,7 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
 
         match this.state {
             PendingTxState::InitialDelay(fut) => {
-                futures_util::ready!(fut.as_mut().poll(ctx));
+                let _ready = futures_util::ready!(fut.as_mut().poll(ctx));
                 tracing::debug!("Starting to poll pending tx {:?}", *this.tx_hash);
                 let fut = Box::pin(this.provider.get_transaction(*this.tx_hash));
                 rewake_with_new_state!(ctx, this, PendingTxState::GettingTx(fut));
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index a75e7cdf..687ff607 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -6,6 +6,9 @@ use crate::{
     PendingTransaction, QuorumProvider, RwClient, SyncingStatus,
 };
 
+#[cfg(not(target_arch = "wasm32"))]
+use crate::transports::{HttpRateLimitRetryPolicy, RetryClient};
+
 #[cfg(feature = "celo")]
 use crate::CeloMiddleware;
 use crate::Middleware;
@@ -1201,7 +1204,7 @@ impl<P: JsonRpcClient> Provider<P> {
     }
 
     #[cfg(test)]
-    /// ganache-only function for mining empty blocks
+    /// Anvil and Ganache-only function for mining empty blocks
     pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
         for _ in 0..num_blocks {
             self.inner.request::<_, U256>("evm_mine", None::<()>).await.map_err(Into::into)?;
@@ -1343,6 +1346,18 @@ impl<'a> TryFrom<&'a String> for Provider<HttpProvider> {
     }
 }
 
+#[cfg(not(target_arch = "wasm32"))]
+impl Provider<RetryClient<HttpProvider>> {
+    pub fn new_client(src: &str, max_retry: u32, initial_backoff: u64) -> Result<Self, ParseError> {
+        Ok(Provider::new(RetryClient::new(
+            HttpProvider::new(Url::parse(src)?),
+            Box::new(HttpRateLimitRetryPolicy),
+            max_retry,
+            initial_backoff,
+        )))
+    }
+}
+
 /// A middleware supporting development-specific JSON RPC methods
 ///
 /// # Example
@@ -1350,13 +1365,13 @@ impl<'a> TryFrom<&'a String> for Provider<HttpProvider> {
 ///```
 /// use ethers_providers::{Provider, Http, Middleware, DevRpcMiddleware};
 /// use ethers_core::types::TransactionRequest;
-/// use ethers_core::utils::Ganache;
+/// use ethers_core::utils::Anvil;
 /// use std::convert::TryFrom;
 ///
 /// # #[tokio::main(flavor = "current_thread")]
 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
-/// let ganache = Ganache::new().spawn();
-/// let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+/// let anvil = Anvil::new().spawn();
+/// let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
 /// let client = DevRpcMiddleware::new(provider);
 ///
 /// // snapshot the initial state
@@ -1435,7 +1450,7 @@ pub mod dev_rpc {
             Self(inner)
         }
 
-        // both ganache and hardhat increment snapshot id even if no state has changed
+        // Ganache, Hardhat and Anvil increment snapshot ID even if no state has changed
         pub async fn snapshot(&self) -> Result<U256, DevRpcMiddlewareError<M>> {
             self.provider().request::<(), U256>("evm_snapshot", ()).await.map_err(From::from)
         }
@@ -1459,14 +1474,13 @@ pub mod dev_rpc {
     mod tests {
         use super::*;
         use crate::{Http, Provider};
-        use ethers_core::utils::Ganache;
+        use ethers_core::utils::Anvil;
         use std::convert::TryFrom;
 
         #[tokio::test]
         async fn test_snapshot() {
-            // launch ganache
-            let ganache = Ganache::new().spawn();
-            let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+            let anvil = Anvil::new().spawn();
+            let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
             let client = DevRpcMiddleware::new(provider);
 
             // snapshot initial state
@@ -1529,7 +1543,7 @@ mod tests {
         types::{
             transaction::eip2930::AccessList, Eip1559TransactionRequest, TransactionRequest, H256,
         },
-        utils::Geth,
+        utils::Anvil,
     };
     use futures_util::StreamExt;
 
@@ -1610,7 +1624,7 @@ mod tests {
     #[cfg_attr(feature = "celo", ignore)]
     async fn test_new_block_filter() {
         let num_blocks = 3;
-        let geth = Geth::new().block_time(2u64).spawn();
+        let geth = Anvil::new().block_time(2u64).spawn();
         let provider = Provider::<Http>::try_from(geth.endpoint())
             .unwrap()
             .interval(Duration::from_millis(1000));
@@ -1629,16 +1643,15 @@ mod tests {
     #[tokio::test]
     #[cfg_attr(feature = "celo", ignore)]
     async fn test_is_signer() {
-        use ethers_core::utils::Ganache;
+        use ethers_core::utils::Anvil;
         use std::str::FromStr;
 
-        let ganache = Ganache::new().spawn();
-        let provider = Provider::<Http>::try_from(ganache.endpoint())
-            .unwrap()
-            .with_sender(ganache.addresses()[0]);
+        let anvil = Anvil::new().spawn();
+        let provider =
+            Provider::<Http>::try_from(anvil.endpoint()).unwrap().with_sender(anvil.addresses()[0]);
         assert!(provider.is_signer().await);
 
-        let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+        let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
         assert!(!provider.is_signer().await);
 
         let sender = Address::from_str("635B4764D1939DfAcD3a8014726159abC277BecC")
@@ -1655,7 +1668,7 @@ mod tests {
     async fn test_new_pending_txs_filter() {
         let num_txs = 5;
 
-        let geth = Geth::new().block_time(2u64).spawn();
+        let geth = Anvil::new().block_time(2u64).spawn();
         let provider = Provider::<Http>::try_from(geth.endpoint())
             .unwrap()
             .interval(Duration::from_millis(1000));
@@ -1678,10 +1691,10 @@ mod tests {
     async fn receipt_on_unmined_tx() {
         use ethers_core::{
             types::TransactionRequest,
-            utils::{parse_ether, Ganache},
+            utils::{parse_ether, Anvil},
         };
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
 
         let accounts = provider.get_accounts().await.unwrap();
         let tx = TransactionRequest::pay(accounts[0], parse_ether(1u64).unwrap()).from(accounts[0]);
@@ -1709,10 +1722,10 @@ mod tests {
     // Celo blocks can not get parsed when used with Ganache
     #[cfg(not(feature = "celo"))]
     async fn block_subscribe() {
-        use ethers_core::utils::Ganache;
+        use ethers_core::utils::Anvil;
         use futures_util::StreamExt;
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let provider = Provider::connect(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let provider = Provider::connect(anvil.ws_endpoint()).await.unwrap();
 
         let stream = provider.subscribe_blocks().await.unwrap();
         let blocks = stream.take(3).map(|x| x.number.unwrap().as_u64()).collect::<Vec<_>>().await;
diff --git a/ethers-providers/src/stream.rs b/ethers-providers/src/stream.rs
index d5ef4f7b..44bb39bf 100644
--- a/ethers-providers/src/stream.rs
+++ b/ethers-providers/src/stream.rs
@@ -265,7 +265,7 @@ mod tests {
     use crate::{Http, Ws};
     use ethers_core::{
         types::{TransactionReceipt, TransactionRequest},
-        utils::{Ganache, Geth},
+        utils::Anvil,
     };
     use futures_util::{FutureExt, StreamExt};
     use std::{collections::HashSet, convert::TryFrom};
@@ -273,7 +273,7 @@ mod tests {
     #[tokio::test]
     async fn can_stream_pending_transactions() {
         let num_txs = 5;
-        let geth = Geth::new().block_time(2u64).spawn();
+        let geth = Anvil::new().block_time(2u64).spawn();
         let provider = Provider::<Http>::try_from(geth.endpoint())
             .unwrap()
             .interval(Duration::from_millis(1000));
@@ -327,17 +327,17 @@ mod tests {
 
     #[tokio::test]
     async fn can_stream_transactions() {
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let provider = Provider::<Http>::try_from(ganache.endpoint())
-            .unwrap()
-            .with_sender(ganache.addresses()[0]);
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let provider =
+            Provider::<Http>::try_from(anvil.endpoint()).unwrap().with_sender(anvil.addresses()[0]);
 
         let accounts = provider.get_accounts().await.unwrap();
 
         let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64);
+        let txs = vec![tx.clone().nonce(0u64), tx.clone().nonce(1u64), tx.clone().nonce(2u64)];
 
         let txs =
-            futures_util::future::join_all(std::iter::repeat(tx.clone()).take(3).map(|tx| async {
+            futures_util::future::join_all(txs.into_iter().map(|tx| async {
                 provider.send_transaction(tx, None).await.unwrap().await.unwrap()
             }))
             .await;
diff --git a/ethers-providers/src/transports/mod.rs b/ethers-providers/src/transports/mod.rs
index 1a369b40..6fc97dba 100644
--- a/ethers-providers/src/transports/mod.rs
+++ b/ethers-providers/src/transports/mod.rs
@@ -39,5 +39,10 @@ pub use quorum::{Quorum, QuorumError, QuorumProvider, WeightedProvider};
 mod rw;
 pub use rw::{RwClient, RwClientError};
 
+#[cfg(not(target_arch = "wasm32"))]
+mod retry;
+#[cfg(not(target_arch = "wasm32"))]
+pub use retry::*;
+
 mod mock;
 pub use mock::{MockError, MockProvider};
diff --git a/ethers-providers/src/transports/retry.rs b/ethers-providers/src/transports/retry.rs
new file mode 100644
index 00000000..17655c56
--- /dev/null
+++ b/ethers-providers/src/transports/retry.rs
@@ -0,0 +1,175 @@
+//! A [JsonRpcClient] implementation that retries requests filtered by [RetryPolicy]
+//! with an exponential backoff.
+
+use super::{common::JsonRpcError, http::ClientError};
+use crate::{provider::ProviderError, JsonRpcClient};
+
+use std::{
+    clone::Clone,
+    fmt::Debug,
+    sync::atomic::{AtomicU32, Ordering},
+    time::Duration,
+};
+
+use async_trait::async_trait;
+use serde::{de::DeserializeOwned, Serialize};
+use thiserror::Error;
+
+/// [RetryPolicy] defines logic for which [JsonRpcClient::Error] instances should
+/// the client retry the request and try to recover from.
+pub trait RetryPolicy<E>: Send + Sync + Debug {
+    fn should_retry(&self, error: &E) -> bool;
+}
+
+/// [RetryClient] presents as a wrapper around [JsonRpcClient] that will retry
+/// requests based with an exponential backoff and filtering based on [RetryPolicy].
+#[derive(Debug)]
+pub struct RetryClient<T>
+where
+    T: JsonRpcClient,
+    T::Error: Sync + Send + 'static,
+{
+    inner: T,
+    requests_enqueued: AtomicU32,
+    policy: Box<dyn RetryPolicy<T::Error>>,
+    max_retry: u32,
+    initial_backoff: u64,
+}
+
+impl<T> RetryClient<T>
+where
+    T: JsonRpcClient,
+    T::Error: Sync + Send + 'static,
+{
+    /// Example:
+    ///
+    /// ```no_run
+    /// use ethers_providers::{Http, RetryClient, HttpRateLimitRetryPolicy};
+    /// use std::time::Duration;
+    /// use url::Url;
+    ///
+    /// let http = Http::new(Url::parse("http://localhost:8545").unwrap());
+    /// let delay = Duration::new(10, 0);
+    /// let client = RetryClient::new(http, Box::new(HttpRateLimitRetryPolicy), 10, 1);
+    /// ```
+    pub fn new(
+        inner: T,
+        policy: Box<dyn RetryPolicy<T::Error>>,
+        max_retry: u32,
+        // in milliseconds
+        initial_backoff: u64,
+    ) -> Self {
+        Self { inner, requests_enqueued: AtomicU32::new(0), policy, max_retry, initial_backoff }
+    }
+}
+
+/// Error thrown when:
+/// 1. Internal client throws an error we do not wish to try to recover from.
+/// 2. Params serialization failed.
+/// 3. Request timed out i.e. max retries were already made.
+#[derive(Error, Debug)]
+pub enum RetryClientError<T>
+where
+    T: JsonRpcClient,
+    T::Error: Sync + Send + 'static,
+{
+    #[error(transparent)]
+    ProviderError(T::Error),
+    TimeoutError,
+    #[error(transparent)]
+    SerdeJson(serde_json::Error),
+}
+
+impl<T> std::fmt::Display for RetryClientError<T>
+where
+    T: JsonRpcClient,
+    <T as JsonRpcClient>::Error: Sync + Send + 'static,
+{
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+impl<T> From<RetryClientError<T>> for ProviderError
+where
+    T: JsonRpcClient + 'static,
+    <T as JsonRpcClient>::Error: Sync + Send + 'static,
+{
+    fn from(src: RetryClientError<T>) -> Self {
+        ProviderError::JsonRpcClientError(Box::new(src))
+    }
+}
+
+#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
+impl<T> JsonRpcClient for RetryClient<T>
+where
+    T: JsonRpcClient + 'static,
+    T::Error: Sync + Send + 'static,
+{
+    type Error = RetryClientError<T>;
+
+    async fn request<A, R>(&self, method: &str, params: A) -> Result<R, Self::Error>
+    where
+        A: std::fmt::Debug + Serialize + Send + Sync,
+        R: DeserializeOwned,
+    {
+        self.requests_enqueued.fetch_add(1, Ordering::SeqCst);
+
+        let params =
+            serde_json::to_value(params).map_err(|err| RetryClientError::SerdeJson(err))?;
+
+        let mut retry_number: u32 = 0;
+
+        loop {
+            let err;
+
+            // hack to not hold `R` across an await in the sleep future and prevent requiring
+            // R: Send + Sync
+            {
+                match self.inner.request(method, params.clone()).await {
+                    Ok(ret) => {
+                        self.requests_enqueued.fetch_sub(1, Ordering::SeqCst);
+                        return Ok(ret)
+                    }
+                    Err(err_) => err = err_,
+                }
+            }
+
+            retry_number += 1;
+            if retry_number > self.max_retry {
+                return Err(RetryClientError::TimeoutError)
+            }
+
+            let should_retry = self.policy.should_retry(&err);
+            if should_retry {
+                let current_queued_requests = self.requests_enqueued.load(Ordering::SeqCst);
+                // using `retry_number + current_queued_requests` for creating back pressure because
+                // of already queued requests
+                let next_backoff =
+                    self.initial_backoff * 2u64.pow(retry_number + current_queued_requests);
+                tokio::time::sleep(Duration::from_millis(next_backoff)).await;
+            } else {
+                self.requests_enqueued.fetch_sub(1, Ordering::SeqCst);
+                return Err(RetryClientError::ProviderError(err))
+            }
+        }
+    }
+}
+
+/// Implements [RetryPolicy] that will retry requests that errored with
+/// status code 429 i.e. TOO_MANY_REQUESTS
+#[derive(Debug)]
+pub struct HttpRateLimitRetryPolicy;
+
+impl RetryPolicy<ClientError> for HttpRateLimitRetryPolicy {
+    fn should_retry(&self, error: &ClientError) -> bool {
+        match error {
+            ClientError::ReqwestError(err) => {
+                err.status() == Some(http::StatusCode::TOO_MANY_REQUESTS)
+            }
+            // alchemy throws it this way
+            ClientError::JsonRpcError(JsonRpcError { code, message: _, data: _ }) => *code == 429,
+            _ => false,
+        }
+    }
+}
diff --git a/ethers-providers/src/transports/ws.rs b/ethers-providers/src/transports/ws.rs
index d44e1c2f..eebbccda 100644
--- a/ethers-providers/src/transports/ws.rs
+++ b/ethers-providers/src/transports/ws.rs
@@ -488,13 +488,13 @@ mod tests {
     use super::*;
     use ethers_core::{
         types::{Block, TxHash, U256},
-        utils::Ganache,
+        utils::Anvil,
     };
 
     #[tokio::test]
     async fn request() {
-        let ganache = Ganache::new().block_time(1u64).spawn();
-        let ws = Ws::connect(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(1u64).spawn();
+        let ws = Ws::connect(anvil.ws_endpoint()).await.unwrap();
 
         let block_num: U256 = ws.request("eth_blockNumber", ()).await.unwrap();
         std::thread::sleep(std::time::Duration::new(3, 0));
@@ -504,8 +504,8 @@ mod tests {
 
     #[tokio::test]
     async fn subscription() {
-        let ganache = Ganache::new().block_time(1u64).spawn();
-        let ws = Ws::connect(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(1u64).spawn();
+        let ws = Ws::connect(anvil.ws_endpoint()).await.unwrap();
 
         // Subscribing requires sending the sub request and then subscribing to
         // the returned sub_id
@@ -519,14 +519,13 @@ mod tests {
             blocks.push(block.number.unwrap_or_default().as_u64());
         }
 
-        assert_eq!(sub_id, 1.into());
         assert_eq!(blocks, vec![1, 2, 3])
     }
 
     #[tokio::test]
     async fn deserialization_fails() {
-        let ganache = Ganache::new().block_time(1u64).spawn();
-        let (ws, _) = tokio_tungstenite::connect_async(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(1u64).spawn();
+        let (ws, _) = tokio_tungstenite::connect_async(anvil.ws_endpoint()).await.unwrap();
         let malformed_data = String::from("not a valid message");
         let (_, stream) = mpsc::unbounded();
         let resp = WsServer::new(ws, stream).handle_text(malformed_data).await;
diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml
index cbe24383..b3e0d6e0 100644
--- a/ethers-signers/Cargo.toml
+++ b/ethers-signers/Cargo.toml
@@ -49,7 +49,7 @@ yubihsm = { version = "0.40.0", features = ["secp256k1", "usb", "mockhsm"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 yubihsm = { version = "0.40.0", features = ["secp256k1", "usb", "mockhsm"] }
-tokio = { version = "1.5", default-features = false, features = ["macros", "rt"] }
+tokio = { version = "1.18", default-features = false, features = ["macros", "rt"] }
 tempfile = "3.3.0"
 
 [features]
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index e06a7c8d..9d7217c7 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -19,7 +19,7 @@ serde_json = "1.0.68"
 serde = { version = "1.0.130", features = ["derive"] }
 semver = { version = "1.0.9", features = ["serde"] }
 walkdir = "2.3.2"
-tokio = { version = "1.15.0", default-features = false, features = ["rt"] }
+tokio = { version = "1.18", default-features = false, features = ["rt"] }
 futures-util = { version = "^0.3", optional = true }
 once_cell = "1.12.0"
 regex = "1.5.6"
@@ -59,7 +59,7 @@ tracing-subscriber = {version = "0.3", default-features = false, features = ["en
 rand = "0.8.5"
 pretty_assertions = "1.2.1"
 tempfile = "3.3.0"
-tokio = { version = "1.15.0", features = ["full"] }
+tokio = { version = "1.18", features = ["full"] }
 
 [[bench]]
 name = "compile_many"
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index c3dea9ea..90736eb3 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -339,6 +339,8 @@ impl ArtifactOutput for ConfigurableArtifacts {
         file.source_file.ast.clone().map(|ast| ConfigurableContractArtifact {
             id: Some(file.source_file.id),
             ast: Some(ast),
+            bytecode: Some(CompactBytecode::empty()),
+            deployed_bytecode: Some(CompactDeployedBytecode::empty()),
             ..Default::default()
         })
     }
diff --git a/ethers-solc/src/artifacts/bytecode.rs b/ethers-solc/src/artifacts/bytecode.rs
index 8f6837ce..eae27f1a 100644
--- a/ethers-solc/src/artifacts/bytecode.rs
+++ b/ethers-solc/src/artifacts/bytecode.rs
@@ -46,6 +46,11 @@ pub struct CompactBytecode {
 }
 
 impl CompactBytecode {
+    /// Returns a new `CompactBytecode` object that contains nothing, as it's the case for
+    /// interfaces and standalone solidity files that don't contain any contract definitions
+    pub fn empty() -> Self {
+        Self { object: Default::default(), source_map: None, link_references: Default::default() }
+    }
     /// Returns the parsed source map
     ///
     /// See also <https://docs.soliditylang.org/en/v0.8.10/internals/source_mappings.html>
@@ -400,6 +405,14 @@ pub struct CompactDeployedBytecode {
     pub immutable_references: BTreeMap<String, Vec<Offsets>>,
 }
 
+impl CompactDeployedBytecode {
+    /// Returns a new `CompactDeployedBytecode` object that contains nothing, as it's the case for
+    /// interfaces and standalone solidity files that don't contain any contract definitions
+    pub fn empty() -> Self {
+        Self { bytecode: Some(CompactBytecode::empty()), immutable_references: Default::default() }
+    }
+}
+
 impl From<DeployedBytecode> for CompactDeployedBytecode {
     fn from(bcode: DeployedBytecode) -> CompactDeployedBytecode {
         CompactDeployedBytecode {
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index 3a8c6ffc..34efb27c 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -1,21 +1,18 @@
 //! Solc artifact types
-use ethers_core::abi::Abi;
-
+use crate::{
+    compile::*, error::SolcIoError, remappings::Remapping, utils, ProjectPathsConfig, SolcError,
+};
 use colored::Colorize;
+use ethers_core::abi::Abi;
 use md5::Digest;
 use semver::{Version, VersionReq};
+use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
 use std::{
     collections::{BTreeMap, HashSet},
     fmt, fs,
     path::{Path, PathBuf},
     str::FromStr,
 };
-
-use crate::{
-    compile::*, error::SolcIoError, remappings::Remapping, utils, ProjectPathsConfig, SolcError,
-};
-
-use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
 use tracing::warn;
 
 pub mod ast;
@@ -189,10 +186,20 @@ impl CompilerInput {
         self.sources = self
             .sources
             .into_iter()
-            .map(|(path, s)| (path.strip_prefix(base).map(|p| p.to_path_buf()).unwrap_or(path), s))
+            .map(|(path, s)| (path.strip_prefix(base).map(Into::into).unwrap_or(path), s))
             .collect();
         self
     }
+
+    /// Similar to `Self::strip_prefix()`. Remove a base path from all
+    /// sources _and_ all paths in solc settings such as remappings
+    ///
+    /// See also `solc --base-path`
+    pub fn with_base_path(mut self, base: impl AsRef<Path>) -> Self {
+        let base = base.as_ref();
+        self.settings = self.settings.with_base_path(base);
+        self.strip_prefix(base)
+    }
 }
 
 /// A `CompilerInput` representation used for verify
@@ -282,7 +289,7 @@ pub struct Settings {
     /// If remappings are used, this source file should match the global path
     /// after remappings were applied.
     /// If this key is an empty string, that refers to a global level.
-    #[serde(default, skip_serializing_if = "Libraries::is_empty")]
+    #[serde(default)]
     pub libraries: Libraries,
 }
 
@@ -385,6 +392,38 @@ impl Settings {
         output.insert("".to_string(), vec!["ast".to_string()]);
         self
     }
+
+    /// Strips `base` from all paths
+    pub fn with_base_path(mut self, base: impl AsRef<Path>) -> Self {
+        let base = base.as_ref();
+        self.remappings.iter_mut().for_each(|r| {
+            r.strip_prefix(base);
+        });
+
+        self.libraries.libs = self
+            .libraries
+            .libs
+            .into_iter()
+            .map(|(file, libs)| (file.strip_prefix(base).map(Into::into).unwrap_or(file), libs))
+            .collect();
+
+        self.output_selection = OutputSelection(
+            self.output_selection
+                .0
+                .into_iter()
+                .map(|(file, selection)| {
+                    (
+                        Path::new(&file)
+                            .strip_prefix(base)
+                            .map(|p| format!("{}", p.display()))
+                            .unwrap_or(file),
+                        selection,
+                    )
+                })
+                .collect(),
+        );
+        self
+    }
 }
 
 impl Default for Settings {
@@ -820,12 +859,18 @@ pub struct Metadata {
 /// Compiler settings
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MetadataSettings {
+    #[serde(default)]
+    pub remappings: Vec<Remapping>,
+    pub optimizer: Optimizer,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub metadata: Option<SettingsMetadata>,
     /// Required for Solidity: File and name of the contract or library this metadata is created
     /// for.
     #[serde(default, rename = "compilationTarget")]
     pub compilation_target: BTreeMap<String, String>,
-    #[serde(flatten)]
-    pub inner: Settings,
+    /// Metadata settings
+    #[serde(default)]
+    pub libraries: Libraries,
 }
 
 /// Compilation source files/source units, keys are file names
@@ -971,9 +1016,9 @@ pub struct Output {
 
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct SolcAbi {
-    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    #[serde(default)]
     pub inputs: Vec<Item>,
-    #[serde(rename = "stateMutability")]
+    #[serde(rename = "stateMutability", skip_serializing_if = "Option::is_none")]
     pub state_mutability: Option<String>,
     #[serde(rename = "type")]
     pub abi_type: String,
@@ -981,6 +1026,9 @@ pub struct SolcAbi {
     pub name: Option<String>,
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub outputs: Vec<Item>,
+    // required to satisfy solidity events
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub anonymous: Option<bool>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -990,6 +1038,11 @@ pub struct Item {
     pub name: String,
     #[serde(rename = "type")]
     pub put_type: String,
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    pub components: Vec<Item>,
+    /// Indexed flag. for solidity events
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub indexed: Option<bool>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index 7c3a77fd..1476ecd0 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -127,6 +127,8 @@ impl fmt::Display for SolcVersion {
 pub struct Solc {
     /// Path to the `solc` executable
     pub solc: PathBuf,
+    /// The base path to set when invoking solc, see also <https://docs.soliditylang.org/en/v0.8.11/path-resolution.html#base-path-and-include-paths>
+    pub base_path: Option<PathBuf>,
     /// Additional arguments passed to the `solc` exectuable
     pub args: Vec<String>,
 }
@@ -163,7 +165,15 @@ impl fmt::Display for Solc {
 impl Solc {
     /// A new instance which points to `solc`
     pub fn new(path: impl Into<PathBuf>) -> Self {
-        Solc { solc: path.into(), args: Vec::new() }
+        Solc { solc: path.into(), base_path: None, args: Vec::new() }
+    }
+
+    /// Sets solc's base path
+    ///
+    /// Ref: <https://docs.soliditylang.org/en/v0.8.11/path-resolution.html#base-path-and-include-paths>
+    pub fn with_base_path(mut self, base_path: impl Into<PathBuf>) -> Self {
+        self.base_path = Some(base_path.into());
+        self
     }
 
     /// Adds an argument to pass to the `solc` command.
@@ -513,6 +523,9 @@ impl Solc {
 
     pub fn compile_output<T: Serialize>(&self, input: &T) -> Result<Vec<u8>> {
         let mut cmd = Command::new(&self.solc);
+        if let Some(ref base_path) = self.base_path {
+            cmd.current_dir(base_path);
+        }
         let mut child = cmd
             .args(&self.args)
             .arg("--standard-json")
@@ -575,7 +588,11 @@ impl Solc {
     pub async fn async_compile_output<T: Serialize>(&self, input: &T) -> Result<Vec<u8>> {
         use tokio::io::AsyncWriteExt;
         let content = serde_json::to_vec(input)?;
-        let mut child = tokio::process::Command::new(&self.solc)
+        let mut cmd = tokio::process::Command::new(&self.solc);
+        if let Some(ref base_path) = self.base_path {
+            cmd.current_dir(base_path);
+        }
+        let mut child = cmd
             .args(&self.args)
             .arg("--standard-json")
             .stdin(Stdio::piped())
diff --git a/ethers-solc/src/compile/output/contracts.rs b/ethers-solc/src/compile/output/contracts.rs
index 2945cd19..ed1f0069 100644
--- a/ethers-solc/src/compile/output/contracts.rs
+++ b/ethers-solc/src/compile/output/contracts.rs
@@ -136,7 +136,7 @@ impl VersionedContracts {
         self.0 = std::mem::take(&mut self.0)
             .into_iter()
             .map(|(contract_path, contracts)| {
-                (root.join(contract_path).to_string_lossy().to_string(), contracts)
+                (format!("{}", root.join(contract_path).display()), contracts)
             })
             .collect();
         self
diff --git a/ethers-solc/src/compile/output/mod.rs b/ethers-solc/src/compile/output/mod.rs
index e6e0a61c..508ffb0e 100644
--- a/ethers-solc/src/compile/output/mod.rs
+++ b/ethers-solc/src/compile/output/mod.rs
@@ -366,6 +366,14 @@ impl AggregatedCompilerOutput {
         (self.sources, self.contracts)
     }
 
+    /// Joins all file path with `root`
+    pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
+        let root = root.as_ref();
+        self.contracts.join_all(root);
+        self.sources.join_all(root);
+        self
+    }
+
     /// Strips the given prefix from all file paths to make them relative to the given
     /// `base` argument.
     ///
diff --git a/ethers-solc/src/compile/project.rs b/ethers-solc/src/compile/project.rs
index 59b6774e..11273f99 100644
--- a/ethers-solc/src/compile/project.rs
+++ b/ethers-solc/src/compile/project.rs
@@ -106,6 +106,7 @@ use crate::{
     artifacts::{Settings, VersionedFilteredSources, VersionedSources},
     cache::ArtifactsCache,
     error::Result,
+    filter::SparseOutputFilter,
     output::AggregatedCompilerOutput,
     report,
     resolver::GraphEdges,
@@ -113,8 +114,6 @@ use crate::{
     Sources,
 };
 use rayon::prelude::*;
-
-use crate::filter::SparseOutputFilter;
 use std::{collections::btree_map::BTreeMap, path::PathBuf, time::Instant};
 
 #[derive(Debug)]
@@ -155,7 +154,9 @@ impl<'a, T: ArtifactOutput> ProjectCompiler<'a, T> {
     pub fn with_sources(project: &'a Project<T>, sources: Sources) -> Result<Self> {
         let graph = Graph::resolve_sources(&project.paths, sources)?;
         let (versions, edges) = graph.into_sources_by_version(project.offline)?;
-        let sources_by_version = versions.get(&project.allowed_lib_paths)?;
+
+        let base_path = project.root();
+        let sources_by_version = versions.get(&project.allowed_lib_paths, base_path)?;
 
         let sources = if project.solc_jobs > 1 && sources_by_version.len() > 1 {
             // if there are multiple different versions, and we can use multiple jobs we can compile
@@ -239,13 +240,20 @@ impl<'a, T: ArtifactOutput> PreprocessedState<'a, T> {
     /// advance to the next state by compiling all sources
     fn compile(self) -> Result<CompiledState<'a, T>> {
         let PreprocessedState { sources, cache, sparse_output } = self;
-        let output = sources.compile(
+        let mut output = sources.compile(
             &cache.project().solc_config.settings,
             &cache.project().paths,
             sparse_output,
             cache.graph(),
         )?;
 
+        // source paths get stripped before handing them over to solc, so solc never uses absolute
+        // paths, instead `--base-path <root dir>` is set. this way any metadata that's derived from
+        // data (paths) is relative to the project dir and should be independent of the current OS
+        // disk. However internally we still want to keep absolute paths, so we join the
+        // contracts again
+        output.join_all(cache.project().root());
+
         Ok(CompiledState { output, cache })
     }
 }
@@ -460,6 +468,7 @@ fn compile_sequential(
                 .settings(opt_settings.clone())
                 .normalize_evm_version(&version)
                 .with_remappings(paths.remappings.clone())
+                .with_base_path(&paths.root)
                 .sanitized(&version);
 
             tracing::trace!(
@@ -471,7 +480,7 @@ fn compile_sequential(
 
             let start = Instant::now();
             report::solc_spawn(&solc, &version, &input, &actually_dirty);
-            let output = solc.compile_exact(&input)?;
+            let output = solc.compile(&input)?;
             report::solc_success(&solc, &version, &output, &start.elapsed());
             tracing::trace!("compiled input, output has error: {}", output.has_error());
             tracing::trace!("received compiler output: {:?}", output.contracts.keys());
@@ -539,6 +548,7 @@ fn compile_parallel(
                 .settings(settings.clone())
                 .normalize_evm_version(&version)
                 .with_remappings(paths.remappings.clone())
+                .with_base_path(&paths.root)
                 .sanitized(&version);
 
             jobs.push((solc.clone(), version.clone(), job, actually_dirty))
@@ -740,7 +750,6 @@ mod tests {
             .unwrap();
         let project = Project::builder().paths(paths).build().unwrap();
         let compiler = ProjectCompiler::new(&project).unwrap();
-        let out = compiler.compile().unwrap();
-        println!("{}", out);
+        let _out = compiler.compile().unwrap();
     }
 }
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index bf6c41fc..58e4dd8a 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -141,10 +141,12 @@ impl<T: ArtifactOutput> Project<T> {
     ///
     /// This will set the `--allow-paths` to the paths configured for the `Project`, if any.
     fn configure_solc(&self, mut solc: Solc) -> Solc {
-        if solc.args.is_empty() && !self.allowed_lib_paths.0.is_empty() {
+        if !self.allowed_lib_paths.0.is_empty() &&
+            !solc.args.iter().any(|arg| arg == "--allow-paths")
+        {
             solc = solc.arg("--allow-paths").arg(self.allowed_lib_paths.to_string());
         }
-        solc
+        solc.with_base_path(self.root())
     }
 
     /// Sets the maximum number of parallel `solc` processes to run simultaneously.
diff --git a/ethers-solc/src/remappings.rs b/ethers-solc/src/remappings.rs
index e8902673..e512fc7d 100644
--- a/ethers-solc/src/remappings.rs
+++ b/ethers-solc/src/remappings.rs
@@ -55,6 +55,14 @@ impl Remapping {
     pub fn into_relative(self, root: impl AsRef<Path>) -> RelativeRemapping {
         RelativeRemapping::new(self, root)
     }
+
+    /// Removes the `base` path from the remapping
+    pub fn strip_prefix(&mut self, base: impl AsRef<Path>) -> &mut Self {
+        if let Ok(stripped) = Path::new(&self.path).strip_prefix(base.as_ref()) {
+            self.path = format!("{}", stripped.display());
+        }
+        self
+    }
 }
 
 #[derive(thiserror::Error, Debug, PartialEq, Eq, PartialOrd)]
@@ -444,6 +452,14 @@ impl Candidate {
                 current_dir.to_path_buf()
             };
 
+            // if the window start and the source dir are the same directory we can end early if
+            // we wrongfully detect something like: `<dep>/src/lib/`
+            if current_level > 0 &&
+                source_dir == window_start &&
+                (is_source_dir(&source_dir) || is_lib_dir(&source_dir))
+            {
+                return
+            }
             candidates.push(Candidate { window_start, source_dir, window_level: current_level });
         }
     }
@@ -751,6 +767,22 @@ mod tests {
         assert_eq!(remappings[0].path, format!("{}/src/", path));
     }
 
+    #[test]
+    fn can_resolve_contract_dir_combinations() {
+        let tmp_dir = tempdir("demo").unwrap();
+        let paths =
+            ["lib/timeless/src/lib/A.sol", "lib/timeless/src/B.sol", "lib/timeless/src/test/C.sol"];
+        mkdir_or_touch(tmp_dir.path(), &paths[..]);
+
+        let tmp_dir_path = tmp_dir.path().join("lib");
+        let remappings = Remapping::find_many(&tmp_dir_path);
+        let expected = vec![Remapping {
+            name: "timeless/".to_string(),
+            path: to_str(tmp_dir_path.join("timeless/src")),
+        }];
+        pretty_assertions::assert_eq!(remappings, expected);
+    }
+
     #[test]
     fn can_resolve_geb_remappings() {
         let tmp_dir = tempdir("geb").unwrap();
diff --git a/ethers-solc/src/resolver/mod.rs b/ethers-solc/src/resolver/mod.rs
index 9183fd0b..15fb77fb 100644
--- a/ethers-solc/src/resolver/mod.rs
+++ b/ethers-solc/src/resolver/mod.rs
@@ -701,12 +701,21 @@ pub struct VersionedSources {
 #[cfg(all(feature = "svm-solc"))]
 impl VersionedSources {
     /// Resolves or installs the corresponding `Solc` installation.
+    ///
+    /// This will also configure following solc arguments:
+    ///    - `allowed_paths`
+    ///    - `base_path`
     pub fn get(
         self,
         allowed_lib_paths: &crate::AllowedLibPaths,
+        base_path: impl AsRef<Path>,
     ) -> Result<std::collections::BTreeMap<crate::Solc, (semver::Version, Sources)>> {
         use crate::Solc;
 
+        // `--base-path` was introduced in 0.6.9 <https://github.com/ethereum/solidity/releases/tag/v0.6.9>
+        static SUPPORTS_BASE_PATH: once_cell::sync::Lazy<VersionReq> =
+            once_cell::sync::Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
+
         // we take the installer lock here to ensure installation checking is done in sync
         #[cfg(any(test, feature = "tests"))]
         let _lock = crate::compile::take_solc_installer_lock();
@@ -743,8 +752,16 @@ impl VersionedSources {
                     tracing::trace!("reinstalled solc: \"{}\"", version);
                 }
             }
-            let solc = solc.arg("--allow-paths").arg(allowed_lib_paths.to_string());
+            let mut solc = solc
+                .arg("--allow-paths")
+                .arg(allowed_lib_paths.to_string())
+                .with_base_path(base_path.as_ref());
             let version = solc.version()?;
+
+            if SUPPORTS_BASE_PATH.matches(&version) {
+                solc = solc.arg("--base-path").arg(format!("{}", base_path.as_ref().display()));
+            }
+
             sources_by_version.insert(solc, (version, sources));
         }
         Ok(sources_by_version)
diff --git a/examples/contract_human_readable.rs b/examples/contract_human_readable.rs
index 67a064a4..a1de69f2 100644
--- a/examples/contract_human_readable.rs
+++ b/examples/contract_human_readable.rs
@@ -1,7 +1,7 @@
 use ethers::{
     prelude::*,
     solc::{Project, ProjectPathsConfig},
-    utils::Ganache,
+    utils::Anvil,
 };
 use eyre::Result;
 use std::{convert::TryFrom, path::PathBuf, sync::Arc, time::Duration};
@@ -33,13 +33,13 @@ async fn main() -> Result<()> {
     let contract = output.find("SimpleStorage").expect("could not find contract").clone();
     let (abi, bytecode, _) = contract.into_parts();
 
-    // 2. instantiate our wallet & ganache
-    let ganache = Ganache::new().spawn();
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
+    // 2. instantiate our wallet & anvil
+    let anvil = Anvil::new().spawn();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
 
     // 3. connect to the network
     let provider =
-        Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
+        Provider::<Http>::try_from(anvil.endpoint())?.interval(Duration::from_millis(10u64));
 
     // 4. instantiate the client with the wallet
     let client = SignerMiddleware::new(provider, wallet);
@@ -49,7 +49,7 @@ async fn main() -> Result<()> {
     let factory = ContractFactory::new(abi.unwrap(), bytecode.unwrap(), client.clone());
 
     // 6. deploy it with the constructor arguments
-    let contract = factory.deploy("initial value".to_string())?.legacy().send().await?;
+    let contract = factory.deploy("initial value".to_string())?.send().await?;
 
     // 7. get the contract's address
     let addr = contract.address();
@@ -59,7 +59,7 @@ async fn main() -> Result<()> {
 
     // 9. call the `setValue` method
     // (first `await` returns a PendingTransaction, second one waits for it to be mined)
-    let _receipt = contract.set_value("hi".to_owned()).legacy().send().await?.await?;
+    let _receipt = contract.set_value("hi".to_owned()).send().await?.await?;
 
     // 10. get all events
     let logs = contract.value_changed_filter().from_block(0u64).query().await?;
diff --git a/examples/contract_with_abi.rs b/examples/contract_with_abi.rs
index 743b3504..67219a78 100644
--- a/examples/contract_with_abi.rs
+++ b/examples/contract_with_abi.rs
@@ -1,4 +1,4 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use eyre::Result;
 use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration};
 
@@ -13,8 +13,8 @@ abigen!(
 #[tokio::main]
 async fn main() -> Result<()> {
     // 1. compile the contract (note this requires that you are inside the `examples` directory) and
-    // launch ganache
-    let ganache = Ganache::new().spawn();
+    // launch anvil
+    let anvil = Anvil::new().spawn();
 
     // set the path to the contract, `CARGO_MANIFEST_DIR` points to the directory containing the
     // manifest of `ethers`. which will be `../` relative to this file
@@ -24,11 +24,11 @@ async fn main() -> Result<()> {
         compiled.find("SimpleStorage").expect("could not find contract").into_parts_or_default();
 
     // 2. instantiate our wallet
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
 
     // 3. connect to the network
     let provider =
-        Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
+        Provider::<Http>::try_from(anvil.endpoint())?.interval(Duration::from_millis(10u64));
 
     // 4. instantiate the client with the wallet
     let client = SignerMiddleware::new(provider, wallet);
@@ -38,7 +38,7 @@ async fn main() -> Result<()> {
     let factory = ContractFactory::new(abi, bytecode, client.clone());
 
     // 6. deploy it with the constructor arguments
-    let contract = factory.deploy("initial value".to_string())?.legacy().send().await?;
+    let contract = factory.deploy("initial value".to_string())?.send().await?;
 
     // 7. get the contract's address
     let addr = contract.address();
@@ -48,7 +48,7 @@ async fn main() -> Result<()> {
 
     // 9. call the `setValue` method
     // (first `await` returns a PendingTransaction, second one waits for it to be mined)
-    let _receipt = contract.set_value("hi".to_owned()).legacy().send().await?.await?;
+    let _receipt = contract.set_value("hi".to_owned()).send().await?.await?;
 
     // 10. get all events
     let logs = contract.value_changed_filter().from_block(0u64).query().await?;
diff --git a/examples/contract_with_abi_and_bytecode.rs b/examples/contract_with_abi_and_bytecode.rs
index deda6c41..c1920b12 100644
--- a/examples/contract_with_abi_and_bytecode.rs
+++ b/examples/contract_with_abi_and_bytecode.rs
@@ -1,4 +1,4 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use eyre::Result;
 use std::{convert::TryFrom, sync::Arc, time::Duration};
 
@@ -11,22 +11,22 @@ abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",);
 #[tokio::main]
 async fn main() -> Result<()> {
     // 1. compile the contract (note this requires that you are inside the `examples` directory) and
-    // launch ganache
-    let ganache = Ganache::new().spawn();
+    // launch anvil
+    let anvil = Anvil::new().spawn();
 
     // 2. instantiate our wallet
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
 
     // 3. connect to the network
     let provider =
-        Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
+        Provider::<Http>::try_from(anvil.endpoint())?.interval(Duration::from_millis(10u64));
 
     // 4. instantiate the client with the wallet
     let client = Arc::new(SignerMiddleware::new(provider, wallet));
 
-    // 5. deploy contract, note the `legacy` call required for non EIP-1559
+    // 5. deploy contract
     let greeter_contract =
-        Greeter::deploy(client, "Hello World!".to_string()).unwrap().legacy().send().await.unwrap();
+        Greeter::deploy(client, "Hello World!".to_string()).unwrap().send().await.unwrap();
 
     // 6. call contract function
     let greeting = greeter_contract.greet().call().await.unwrap();
diff --git a/examples/contract_with_abi_and_structs.rs b/examples/contract_with_abi_and_structs.rs
index 79966e68..b74c9ea9 100644
--- a/examples/contract_with_abi_and_structs.rs
+++ b/examples/contract_with_abi_and_structs.rs
@@ -1,6 +1,6 @@
 //! Main entry point for ContractMonitor
 
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use std::{convert::TryFrom, sync::Arc, time::Duration};
 
 abigen!(VerifierContract, "ethers-contract/tests/solidity-contracts/verifier_abi.json");
@@ -9,10 +9,10 @@ abigen!(VerifierContract, "ethers-contract/tests/solidity-contracts/verifier_abi
 /// have structs as input.
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
     let provider =
-        Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
+        Provider::<Http>::try_from(anvil.endpoint())?.interval(Duration::from_millis(10u64));
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
 
     let client = SignerMiddleware::new(provider, wallet);
     let client = Arc::new(client);
diff --git a/examples/ens.rs b/examples/ens.rs
index 894c466e..97b706e2 100644
--- a/examples/ens.rs
+++ b/examples/ens.rs
@@ -1,16 +1,15 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use eyre::Result;
 use std::convert::TryFrom;
 
 #[tokio::main]
 async fn main() -> Result<()> {
     // fork mainnet
-    let ganache = Ganache::new()
-        .fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27")
-        .spawn();
-    let from = ganache.addresses()[0].clone();
+    let anvil =
+        Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn();
+    let from = anvil.addresses()[0].clone();
     // connect to the network
-    let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap().with_sender(from);
+    let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap().with_sender(from);
 
     // craft the transaction
     let tx = TransactionRequest::new().to("vitalik.eth").value(100_000);
diff --git a/examples/ethers-wasm/Cargo.toml b/examples/ethers-wasm/Cargo.toml
index ccc36499..c1a174d1 100644
--- a/examples/ethers-wasm/Cargo.toml
+++ b/examples/ethers-wasm/Cargo.toml
@@ -20,7 +20,7 @@ default = ["console_error_panic_hook"]
 
 [dependencies]
 ethers = { path = "../..", version = "0.6.0", features = ["abigen", "legacy", "ws"] }
-serde_derive = "1.0.126"
+serde_derive = "1.0.137"
 wasm-bindgen-futures = "0.4.30"
 serde_json = "1.0.64"
 wasm-bindgen = { version = "0.2.80", features = ["serde-serialize"] }
diff --git a/examples/local_signer.rs b/examples/local_signer.rs
index 3ddcda82..64c570c3 100644
--- a/examples/local_signer.rs
+++ b/examples/local_signer.rs
@@ -1,16 +1,16 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use eyre::Result;
 use std::convert::TryFrom;
 
 #[tokio::main]
 async fn main() -> Result<()> {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
-    let wallet2: LocalWallet = ganache.keys()[1].clone().into();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
+    let wallet2: LocalWallet = anvil.keys()[1].clone().into();
 
     // connect to the network
-    let provider = Provider::<Http>::try_from(ganache.endpoint())?;
+    let provider = Provider::<Http>::try_from(anvil.endpoint())?;
 
     // connect the wallet to the provider
     let client = SignerMiddleware::new(provider, wallet);
diff --git a/examples/paginated_logs.rs b/examples/paginated_logs.rs
index f911fecf..689d80ad 100644
--- a/examples/paginated_logs.rs
+++ b/examples/paginated_logs.rs
@@ -1,10 +1,10 @@
-use ethers::{abi::AbiDecode, prelude::*, utils::keccak256};
+use ethers::{abi::AbiDecode, prelude::*, providers::Middleware, utils::keccak256};
 use eyre::Result;
 use std::sync::Arc;
 
 #[tokio::main]
 async fn main() -> Result<()> {
-    let client =
+    let client: Provider<Ws> =
         Provider::<Ws>::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
             .await?;
     let client = Arc::new(client);
@@ -18,7 +18,8 @@ async fn main() -> Result<()> {
 
     let mut stream = client.get_logs_paginated(&erc20_transfer_filter, 10);
 
-    while let Some(log) = stream.next().await {
+    while let Some(res) = stream.next().await {
+        let log = res?;
         println!(
             "block: {:?}, tx: {:?}, token: {:?}, from: {:?}, to: {:?}, amount: {:?}",
             log.block_number,
diff --git a/examples/quorum.rs b/examples/quorum.rs
index 63cd59bd..95f62af1 100644
--- a/examples/quorum.rs
+++ b/examples/quorum.rs
@@ -1,22 +1,22 @@
 //! Example usage for the `QuorumProvider` that requests multiple backends and only returns
 //! a value if the configured `Quorum` was reached.
 
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use std::{str::FromStr, time::Duration};
 
 #[tokio::main]
 async fn main() -> eyre::Result<()> {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
     // create a quorum provider with some providers
     let quorum = QuorumProvider::dyn_rpc()
-        .add_provider(WeightedProvider::new(Box::new(Http::from_str(&ganache.endpoint())?)))
+        .add_provider(WeightedProvider::new(Box::new(Http::from_str(&anvil.endpoint())?)))
         .add_provider(WeightedProvider::with_weight(
-            Box::new(Ws::connect(ganache.ws_endpoint()).await?),
+            Box::new(Ws::connect(anvil.ws_endpoint()).await?),
             2,
         ))
         .add_provider(WeightedProvider::with_weight(
-            Box::new(Ws::connect(ganache.ws_endpoint()).await?),
+            Box::new(Ws::connect(anvil.ws_endpoint()).await?),
             2,
         ))
         // the quorum provider will yield the response if >50% of the weighted inner provider
diff --git a/examples/rw.rs b/examples/rw.rs
index fea6ae80..89733a8f 100644
--- a/examples/rw.rs
+++ b/examples/rw.rs
@@ -1,15 +1,15 @@
-//! Example usage for the `RwClinet` that uses a didicated client to send transaction and nother one
+//! Example usage for the `RwClient` that uses a didicated client to send transaction and nother one
 //! for read ops
 
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use std::{str::FromStr, time::Duration};
 
 #[tokio::main]
 async fn main() -> eyre::Result<()> {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
-    let http = Http::from_str(&ganache.endpoint())?;
-    let ws = Ws::connect(ganache.ws_endpoint()).await?;
+    let http = Http::from_str(&anvil.endpoint())?;
+    let ws = Ws::connect(anvil.ws_endpoint()).await?;
 
     let provider = Provider::rw(http, ws).interval(Duration::from_millis(10u64));
 
diff --git a/examples/transfer_eth.rs b/examples/transfer_eth.rs
index 9aa249d3..5326dd99 100644
--- a/examples/transfer_eth.rs
+++ b/examples/transfer_eth.rs
@@ -1,13 +1,13 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use eyre::Result;
 use std::convert::TryFrom;
 
 #[tokio::main]
 async fn main() -> Result<()> {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
     // connect to the network
-    let provider = Provider::<Http>::try_from(ganache.endpoint())?;
+    let provider = Provider::<Http>::try_from(anvil.endpoint())?;
     let accounts = provider.get_accounts().await?;
     let from = accounts[0];
     let to = accounts[1];
@@ -16,18 +16,16 @@ async fn main() -> Result<()> {
     let tx = TransactionRequest::new().to(to).value(1000).from(from); // specify the `from` field so that the client knows which account to use
 
     let balance_before = provider.get_balance(from, None).await?;
+    let nonce1 = provider.get_transaction_count(from, None).await?;
 
     // broadcast it via the eth_sendTransaction API
     let tx = provider.send_transaction(tx, None).await?.await?;
 
     println!("{}", serde_json::to_string(&tx)?);
 
-    let nonce1 = provider.get_transaction_count(from, Some(BlockNumber::Latest.into())).await?;
+    let nonce2 = provider.get_transaction_count(from, None).await?;
 
-    let nonce2 =
-        provider.get_transaction_count(from, Some(BlockNumber::Number(0.into()).into())).await?;
-
-    assert!(nonce2 < nonce1);
+    assert!(nonce1 < nonce2);
 
     let balance_after = provider.get_balance(from, None).await?;
     assert!(balance_after < balance_before);
diff --git a/examples/trezor.rs b/examples/trezor.rs
index 75c6b454..ab881a3c 100644
--- a/examples/trezor.rs
+++ b/examples/trezor.rs
@@ -10,7 +10,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     // (here: mainnet) for EIP155 support.
     // EIP1559 support
     // No EIP712 support yet.
-    let trezor = Trezor::new(TrezorHDPath::TrezorLive(0), 1).await?;
+    let trezor = Trezor::new(TrezorHDPath::TrezorLive(0), 1, None).await?;
     let client = SignerMiddleware::new(provider, trezor);
 
     // Create and broadcast a transaction (ENS disabled!)
diff --git a/examples/watch_blocks.rs b/examples/watch_blocks.rs
index 9ab2c803..af1385da 100644
--- a/examples/watch_blocks.rs
+++ b/examples/watch_blocks.rs
@@ -1,10 +1,10 @@
-use ethers::{prelude::*, utils::Ganache};
+use ethers::{prelude::*, utils::Anvil};
 use std::time::Duration;
 
 #[tokio::main]
 async fn main() -> eyre::Result<()> {
-    let ganache = Ganache::new().block_time(1u64).spawn();
-    let ws = Ws::connect(ganache.ws_endpoint()).await?;
+    let anvil = Anvil::new().block_time(1u64).spawn();
+    let ws = Ws::connect(anvil.ws_endpoint()).await?;
     let provider = Provider::new(ws).interval(Duration::from_millis(2000));
     let mut stream = provider.watch_blocks().await?.take(5);
     while let Some(block) = stream.next().await {
diff --git a/scripts/examples.sh b/scripts/examples.sh
index 517c3cb7..c9ff12ff 100755
--- a/scripts/examples.sh
+++ b/scripts/examples.sh
@@ -1,7 +1,24 @@
 set -e
 # shellcheck shell=bash
+
+# examples that we can't run because they require some additional infra, docker or ledger for example
+ignored=(
+  "moonbeam_with_abi"
+  "ipc"
+  "ledger"
+  "paginated_logs"
+  "subscribe_logs"
+  "trezor"
+  "yubi"
+)
+
 # run all examples
 for file in examples/*.rs; do
   name="$(echo "$file" | cut -f 1 -d '.')"
+  if [[ "${ignored[*]}" =~ $(basename "$name") ]]; then
+    echo "skipping: $file"
+    continue
+  fi
+  echo "running: $file"
   cargo r -p ethers --example "$(basename "$name")"
 done
diff --git a/src/lib.rs b/src/lib.rs
index da32f5d3..eaf276ca 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,7 +29,7 @@
 //!
 //! Contains all the [necessary data structures](core::types) for interacting
 //! with Ethereum, along with cryptographic utilities for signing and verifying
-//! ECDSA signatures on `secp256k1`. Bindings to the solidity compiler and `ganache-cli`
+//! ECDSA signatures on `secp256k1`. Bindings to the Solidity compiler, Anvil and `ganache-cli`
 //! are also provided as helpers. To simplify your imports, consider using the re-exported
 //! modules described in the next subsection.
 //!


Test patch:
diff --git a/ethers-contract/tests/abigen.rs b/ethers-contract/tests/abigen.rs
index aef9f2f6..5f79dab2 100644
--- a/ethers-contract/tests/abigen.rs
+++ b/ethers-contract/tests/abigen.rs
@@ -4,7 +4,7 @@ use ethers_contract::{abigen, EthCall, EthEvent};
 use ethers_core::{
     abi::{AbiDecode, AbiEncode, Address, Tokenizable},
     types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, U256},
-    utils::Ganache,
+    utils::Anvil,
 };
 use ethers_middleware::SignerMiddleware;
 use ethers_providers::{MockProvider, Provider};
@@ -305,10 +305,10 @@ async fn can_handle_underscore_functions() {
         "ethers-contract/tests/solidity-contracts/simplestorage_abi.json",
     );
 
-    // launcht the network & connect to it
-    let ganache = ethers_core::utils::Ganache::new().spawn();
-    let from = ganache.addresses()[0];
-    let provider = Provider::try_from(ganache.endpoint())
+    // launch the network & connect to it
+    let anvil = Anvil::new().spawn();
+    let from = anvil.addresses()[0];
+    let provider = Provider::try_from(anvil.endpoint())
         .unwrap()
         .with_sender(from)
         .interval(std::time::Duration::from_millis(10));
@@ -484,9 +484,9 @@ fn can_handle_case_sensitive_calls() {
 #[tokio::test]
 async fn can_deploy_greeter() {
     abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",);
-    let ganache = ethers_core::utils::Ganache::new().spawn();
-    let from = ganache.addresses()[0];
-    let provider = Provider::try_from(ganache.endpoint())
+    let anvil = Anvil::new().spawn();
+    let from = anvil.addresses()[0];
+    let provider = Provider::try_from(anvil.endpoint())
         .unwrap()
         .with_sender(from)
         .interval(std::time::Duration::from_millis(10));
@@ -502,9 +502,9 @@ async fn can_deploy_greeter() {
 #[tokio::test]
 async fn can_abiencoderv2_output() {
     abigen!(AbiEncoderv2Test, "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",);
-    let ganache = ethers_core::utils::Ganache::new().spawn();
-    let from = ganache.addresses()[0];
-    let provider = Provider::try_from(ganache.endpoint())
+    let anvil = Anvil::new().spawn();
+    let from = anvil.addresses()[0];
+    let provider = Provider::try_from(anvil.endpoint())
         .unwrap()
         .with_sender(from)
         .interval(std::time::Duration::from_millis(10));
@@ -575,7 +575,7 @@ fn can_handle_overloaded_events() {
 async fn can_send_struct_param() {
     abigen!(StructContract, "./tests/solidity-contracts/StructContract.json");
 
-    let server = Ganache::default().spawn();
+    let server = Anvil::new().spawn();
     let wallet: LocalWallet = server.keys()[0].clone().into();
     let provider = Provider::try_from(server.endpoint()).unwrap();
     let client = Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(1337u64)));
diff --git a/ethers-contract/tests/common/mod.rs b/ethers-contract/tests/common/mod.rs
index 2a1a29c4..bdff9179 100644
--- a/ethers-contract/tests/common/mod.rs
+++ b/ethers-contract/tests/common/mod.rs
@@ -10,7 +10,7 @@ use ethers_contract::EthEvent;
 mod derive;
 
 use ethers_contract::{Contract, ContractFactory};
-use ethers_core::{abi::Abi, types::Bytes, utils::GanacheInstance};
+use ethers_core::{abi::Abi, types::Bytes, utils::AnvilInstance};
 use ethers_providers::{Http, Middleware, Provider};
 use ethers_solc::Solc;
 use std::{convert::TryFrom, sync::Arc, time::Duration};
@@ -38,16 +38,16 @@ pub fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
 }
 
 /// connects the private key to http://localhost:8545
-pub fn connect(ganache: &GanacheInstance, idx: usize) -> Arc<Provider<Http>> {
-    let sender = ganache.addresses()[idx];
-    let provider = Provider::<Http>::try_from(ganache.endpoint())
+pub fn connect(anvil: &AnvilInstance, idx: usize) -> Arc<Provider<Http>> {
+    let sender = anvil.addresses()[idx];
+    let provider = Provider::<Http>::try_from(anvil.endpoint())
         .unwrap()
         .interval(Duration::from_millis(10u64))
         .with_sender(sender);
     Arc::new(provider)
 }
 
-/// Launches a ganache instance and deploys the SimpleStorage contract
+/// Launches a Anvil instance and deploys the SimpleStorage contract
 pub async fn deploy<M: Middleware>(client: Arc<M>, abi: Abi, bytecode: Bytes) -> Contract<M> {
     let factory = ContractFactory::new(abi, bytecode, client);
     let deployer = factory.deploy("initial value".to_string()).unwrap();
diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs
index 6f78dc80..89e0c2e3 100644
--- a/ethers-contract/tests/contract.rs
+++ b/ethers-contract/tests/contract.rs
@@ -11,7 +11,7 @@ mod eth_tests {
     use ethers_core::{
         abi::{Detokenize, Token, Tokenizable},
         types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256},
-        utils::{keccak256, Ganache},
+        utils::{keccak256, Anvil},
     };
     use ethers_derive_eip712::*;
     use ethers_middleware::signer::SignerMiddleware;
@@ -23,15 +23,15 @@ mod eth_tests {
     async fn deploy_and_call_contract() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
 
-        // launch ganache
-        let ganache = Ganache::new().spawn();
+        // launch anvil
+        let anvil = Anvil::new().spawn();
 
         // Instantiate the clients. We assume that clients consume the provider and the wallet
         // (which makes sense), so for multi-client tests, you must clone the provider.
-        let addrs = ganache.addresses().to_vec();
+        let addrs = anvil.addresses().to_vec();
         let addr2 = addrs[1];
-        let client = connect(&ganache, 0);
-        let client2 = connect(&ganache, 1);
+        let client = connect(&anvil, 0);
+        let client2 = connect(&anvil, 1);
 
         // create a factory which will be used to deploy instances of the contract
         let factory = ContractFactory::new(abi, bytecode, client.clone());
@@ -97,8 +97,8 @@ mod eth_tests {
     #[cfg(feature = "abigen")]
     async fn get_past_events() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        let ganache = Ganache::new().spawn();
-        let client = connect(&ganache, 0);
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
         let address = client.get_accounts().await.unwrap()[0];
         let contract = deploy(client.clone(), abi, bytecode).await;
 
@@ -136,9 +136,9 @@ mod eth_tests {
     #[cfg(feature = "abigen")]
     async fn get_events_with_meta() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        let ganache = Ganache::new().spawn();
-        let client = connect(&ganache, 0);
-        let address = ganache.addresses()[0];
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
+        let address = anvil.addresses()[0];
         let contract = deploy(client.clone(), abi, bytecode).await;
 
         // and we can fetch the events
@@ -168,8 +168,8 @@ mod eth_tests {
     #[tokio::test]
     async fn call_past_state() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        let ganache = Ganache::new().spawn();
-        let client = connect(&ganache, 0);
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
         let contract = deploy(client.clone(), abi, bytecode).await;
         let deployed_block = client.get_block_number().await.unwrap();
 
@@ -257,24 +257,21 @@ mod eth_tests {
     #[cfg(feature = "abigen")]
     async fn watch_events() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        let ganache = Ganache::new().spawn();
-        let client = connect(&ganache, 0);
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
         let contract = deploy(client.clone(), abi.clone(), bytecode).await;
 
         // We spawn the event listener:
         let event = contract.event::<ValueChanged>();
         let mut stream = event.stream().await.unwrap();
-        assert_eq!(stream.id, 1.into());
 
         // Also set up a subscription for the same thing
-        let ws = Provider::connect(ganache.ws_endpoint()).await.unwrap();
+        let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap();
         let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws);
         let event2 = contract2.event::<ValueChanged>();
         let mut subscription = event2.subscribe().await.unwrap();
-        assert_eq!(subscription.id, 2.into());
 
         let mut subscription_meta = event2.subscribe().await.unwrap().with_meta();
-        assert_eq!(subscription_meta.0.id, 3.into());
 
         let num_calls = 3u64;
 
@@ -303,12 +300,12 @@ mod eth_tests {
     #[tokio::test]
     async fn watch_subscription_events_multiple_addresses() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        let ganache = Ganache::new().spawn();
-        let client = connect(&ganache, 0);
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
         let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await;
         let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await;
 
-        let ws = Provider::connect(ganache.ws_endpoint()).await.unwrap();
+        let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap();
         let filter = Filter::new()
             .address(ValueOrArray::Array(vec![contract_1.address(), contract_2.address()]));
         let mut stream = ws.subscribe_logs(&filter).await.unwrap();
@@ -332,11 +329,11 @@ mod eth_tests {
     #[tokio::test]
     async fn signer_on_node() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
-        // spawn ganache
-        let ganache = Ganache::new().spawn();
+        // spawn anvil
+        let anvil = Anvil::new().spawn();
 
         // connect
-        let provider = Provider::<Http>::try_from(ganache.endpoint())
+        let provider = Provider::<Http>::try_from(anvil.endpoint())
             .unwrap()
             .interval(std::time::Duration::from_millis(50u64));
 
@@ -373,9 +370,8 @@ mod eth_tests {
         // get ABI and bytecode for the SimpleStorage contract
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
 
-        // launch ganache
-        // some tests expect 100 ether (-e === --wallet.defaultBalance in ether)
-        let ganache = Ganache::new().arg("-e").arg("100").spawn();
+        // launch anvil
+        let anvil = Anvil::new().spawn();
 
         // Instantiate the clients. We assume that clients consume the provider and the wallet
         // (which makes sense), so for multi-client tests, you must clone the provider.
@@ -383,13 +379,13 @@ mod eth_tests {
         // `client2` is used to deploy the first SimpleStorage contract
         // `client3` is used to deploy the second SimpleStorage contract
         // `client4` is used to make the aggregate call
-        let addrs = ganache.addresses().to_vec();
+        let addrs = anvil.addresses().to_vec();
         let addr2 = addrs[1];
         let addr3 = addrs[2];
-        let client = connect(&ganache, 0);
-        let client2 = connect(&ganache, 1);
-        let client3 = connect(&ganache, 2);
-        let client4 = connect(&ganache, 3);
+        let client = connect(&anvil, 0);
+        let client2 = connect(&anvil, 1);
+        let client3 = connect(&anvil, 2);
+        let client4 = connect(&anvil, 3);
 
         // create a factory which will be used to deploy instances of the contract
         let multicall_factory =
@@ -491,7 +487,7 @@ mod eth_tests {
         assert_eq!(return_data.2, multicall_contract.address());
         assert_eq!(return_data.3, multicall_contract.address());
 
-        let addrs = ganache.addresses();
+        let addrs = anvil.addresses();
         // query ETH balances of multiple addresses
         // these keys haven't been used to do any tx
         // so should have 100 ETH
@@ -502,9 +498,9 @@ mod eth_tests {
             .eth_balance_of(addrs[6]);
 
         let balances: (U256, U256, U256) = multicall.call().await.unwrap();
-        assert_eq!(balances.0, U256::from(100_000_000_000_000_000_000u128));
-        assert_eq!(balances.1, U256::from(100_000_000_000_000_000_000u128));
-        assert_eq!(balances.2, U256::from(100_000_000_000_000_000_000u128));
+        assert_eq!(balances.0, U256::from(10_000_000_000_000_000_000_000u128));
+        assert_eq!(balances.1, U256::from(10_000_000_000_000_000_000_000u128));
+        assert_eq!(balances.2, U256::from(10_000_000_000_000_000_000_000u128));
 
         // clear multicall so we can test `call_raw` w/ >16 calls
         multicall.clear_calls();
@@ -569,13 +565,13 @@ mod eth_tests {
         // get ABI and bytecode for the DeriveEip712Test contract
         let (abi, bytecode) = compile_contract("DeriveEip712Test", "DeriveEip712Test.sol");
 
-        // launch ganache
-        let ganache = Ganache::new().spawn();
+        // launch anvil
+        let anvil = Anvil::new().spawn();
 
-        let wallet: LocalWallet = ganache.keys()[0].clone().into();
+        let wallet: LocalWallet = anvil.keys()[0].clone().into();
 
-        let provider = Provider::<Http>::try_from(ganache.endpoint())
-            .expect("failed to instantiate provider from ganache endpoint")
+        let provider = Provider::<Http>::try_from(anvil.endpoint())
+            .expect("failed to instantiate provider from anvil endpoint")
             .interval(Duration::from_millis(10u64));
 
         let client =
diff --git a/ethers-middleware/tests/gas_oracle.rs b/ethers-middleware/tests/gas_oracle.rs
index 5967fa2d..f8fe1f89 100644
--- a/ethers-middleware/tests/gas_oracle.rs
+++ b/ethers-middleware/tests/gas_oracle.rs
@@ -4,7 +4,7 @@ use std::convert::TryFrom;
 
 use async_trait::async_trait;
 
-use ethers_core::{types::*, utils::Ganache};
+use ethers_core::{types::*, utils::Anvil};
 use ethers_middleware::gas_oracle::{
     EthGasStation, Etherchain, Etherscan, GasCategory, GasOracle, GasOracleError,
     GasOracleMiddleware,
@@ -31,14 +31,14 @@ impl GasOracle for FakeGasOracle {
 
 #[tokio::test]
 async fn using_gas_oracle() {
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
-    let from = ganache.addresses()[0];
+    let from = anvil.addresses()[0];
 
     // connect to the network
-    let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
+    let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
 
-    // this is set because ganache now sets 875000000 as the first block's base fee
+    // this is set because anvil now sets 875000000 as the first block's base fee
     let base_fee = 875000000;
     // assign a gas oracle to use
     let gas_oracle = FakeGasOracle { gas_price: (base_fee + 1337).into() };
diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs
index 3b805158..0f1e94ff 100644
--- a/ethers-middleware/tests/signer.rs
+++ b/ethers-middleware/tests/signer.rs
@@ -22,16 +22,16 @@ static WALLETS: Lazy<TestWallets> = Lazy::new(|| {
 #[tokio::test]
 #[cfg(not(feature = "celo"))]
 async fn send_eth() {
-    use ethers_core::utils::Ganache;
+    use ethers_core::utils::Anvil;
 
-    let ganache = Ganache::new().spawn();
+    let anvil = Anvil::new().spawn();
 
     // this private key belongs to the above mnemonic
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
-    let wallet2: LocalWallet = ganache.keys()[1].clone().into();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
+    let wallet2: LocalWallet = anvil.keys()[1].clone().into();
 
     // connect to the network
-    let provider = Provider::<Http>::try_from(ganache.endpoint())
+    let provider = Provider::<Http>::try_from(anvil.endpoint())
         .unwrap()
         .interval(Duration::from_millis(10u64));
     let chain_id = provider.get_chainid().await.unwrap().as_u64();
@@ -156,17 +156,17 @@ async fn test_send_transaction() {
 #[tokio::test]
 #[cfg(not(feature = "celo"))]
 async fn send_transaction_handles_tx_from_field() {
-    use ethers_core::utils::Ganache;
+    use ethers_core::utils::Anvil;
 
-    // launch ganache
-    let ganache = Ganache::new().spawn();
+    // launch anvil
+    let anvil = Anvil::new().spawn();
 
     // grab 2 wallets
-    let signer: LocalWallet = ganache.keys()[0].clone().into();
-    let other: LocalWallet = ganache.keys()[1].clone().into();
+    let signer: LocalWallet = anvil.keys()[0].clone().into();
+    let other: LocalWallet = anvil.keys()[1].clone().into();
 
     // connect to the network
-    let provider = Provider::try_from(ganache.endpoint()).unwrap();
+    let provider = Provider::try_from(anvil.endpoint()).unwrap();
     let provider =
         SignerMiddleware::new_with_provider_chain(provider, signer.clone()).await.unwrap();
 
diff --git a/ethers-middleware/tests/stack.rs b/ethers-middleware/tests/stack.rs
index 9c6c9bd9..c7cb1ea2 100644
--- a/ethers-middleware/tests/stack.rs
+++ b/ethers-middleware/tests/stack.rs
@@ -1,7 +1,7 @@
 #![cfg(not(target_arch = "wasm32"))]
 #[cfg(not(feature = "celo"))]
 mod tests {
-    use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Ganache};
+    use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Anvil};
     use ethers_middleware::{
         gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
         gas_oracle::{EthGasStation, GasCategory, GasOracleMiddleware},
@@ -52,13 +52,13 @@ mod tests {
 
     #[tokio::test]
     async fn can_stack_middlewares() {
-        let ganache = Ganache::new().block_time(5u64).spawn();
+        let anvil = Anvil::new().block_time(5u64).spawn();
         let gas_oracle = EthGasStation::new(None).category(GasCategory::SafeLow);
-        let signer: LocalWallet = ganache.keys()[0].clone().into();
+        let signer: LocalWallet = anvil.keys()[0].clone().into();
         let address = signer.address();
 
         // the base provider
-        let provider = Arc::new(Provider::<Http>::try_from(ganache.endpoint()).unwrap());
+        let provider = Arc::new(Provider::<Http>::try_from(anvil.endpoint()).unwrap());
         let chain_id = provider.get_chainid().await.unwrap().as_u64();
         let signer = signer.with_chain_id(chain_id);
 
diff --git a/ethers-middleware/tests/transformer.rs b/ethers-middleware/tests/transformer.rs
index 26c2375d..8f74d224 100644
--- a/ethers-middleware/tests/transformer.rs
+++ b/ethers-middleware/tests/transformer.rs
@@ -1,7 +1,7 @@
 #![cfg(not(target_arch = "wasm32"))]
 #![allow(unused)]
 use ethers_contract::{BaseContract, ContractFactory};
-use ethers_core::{abi::Abi, types::*, utils::Ganache};
+use ethers_core::{abi::Abi, types::*, utils::Anvil};
 use ethers_middleware::{
     transformer::{DsProxy, TransformerMiddleware},
     SignerMiddleware,
@@ -29,10 +29,10 @@ async fn ds_proxy_transformer() {
     // randomness
     let mut rng = rand::thread_rng();
 
-    // spawn ganache and instantiate a signer middleware.
-    let ganache = Ganache::new().spawn();
-    let wallet: LocalWallet = ganache.keys()[0].clone().into();
-    let provider = Provider::<Http>::try_from(ganache.endpoint())
+    // spawn anvil and instantiate a signer middleware.
+    let anvil = Anvil::new().spawn();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
+    let provider = Provider::<Http>::try_from(anvil.endpoint())
         .unwrap()
         .interval(Duration::from_millis(10u64));
     let chain_id = provider.get_chainid().await.unwrap().as_u64();
@@ -88,10 +88,10 @@ async fn ds_proxy_code() {
     // randomness
     let mut rng = rand::thread_rng();
 
-    // spawn ganache and instantiate a signer middleware.
-    let ganache = Ganache::new().spawn();
-    let wallet: LocalWallet = ganache.keys()[1].clone().into();
-    let provider = Provider::<Http>::try_from(ganache.endpoint())
+    // spawn anvil and instantiate a signer middleware.
+    let anvil = Anvil::new().spawn();
+    let wallet: LocalWallet = anvil.keys()[1].clone().into();
+    let provider = Provider::<Http>::try_from(anvil.endpoint())
         .unwrap()
         .interval(Duration::from_millis(10u64));
     let chain_id = provider.get_chainid().await.unwrap().as_u64();
diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs
index a4123055..666de8e7 100644
--- a/ethers-providers/tests/provider.rs
+++ b/ethers-providers/tests/provider.rs
@@ -7,7 +7,7 @@ mod eth_tests {
     use super::*;
     use ethers_core::{
         types::{Address, BlockId, TransactionRequest, H256},
-        utils::Ganache,
+        utils::Anvil,
     };
     use ethers_providers::RINKEBY;
 
@@ -45,8 +45,8 @@ mod eth_tests {
         use ethers_core::types::H256;
         use ethers_providers::{StreamExt, Ws};
 
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let (ws, _) = tokio_tungstenite::connect_async(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let (ws, _) = tokio_tungstenite::connect_async(anvil.ws_endpoint()).await.unwrap();
         let provider = Provider::new(Ws::new(ws)).interval(Duration::from_millis(500u64));
 
         let stream = provider.watch_blocks().await.unwrap().stream();
@@ -56,9 +56,9 @@ mod eth_tests {
     }
 
     #[tokio::test]
-    async fn pending_txs_with_confirmations_ganache() {
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let provider = Provider::<Http>::try_from(ganache.endpoint())
+    async fn pending_txs_with_confirmations_anvil() {
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let provider = Provider::<Http>::try_from(anvil.endpoint())
             .unwrap()
             .interval(Duration::from_millis(500u64));
         let accounts = provider.get_accounts().await.unwrap();
@@ -66,10 +66,10 @@ mod eth_tests {
     }
 
     #[tokio::test]
-    async fn websocket_pending_txs_with_confirmations_ganache() {
+    async fn websocket_pending_txs_with_confirmations_anvil() {
         use ethers_providers::Ws;
-        let ganache = Ganache::new().block_time(2u64).spawn();
-        let ws = Ws::connect(ganache.ws_endpoint()).await.unwrap();
+        let anvil = Anvil::new().block_time(2u64).spawn();
+        let ws = Ws::connect(anvil.ws_endpoint()).await.unwrap();
         let provider = Provider::new(ws);
         let accounts = provider.get_accounts().await.unwrap();
         generic_pending_txs_test(provider, accounts[0]).await;
diff --git a/ethers-providers/tests/txpool.rs b/ethers-providers/tests/txpool.rs
index df9ad01f..0e98c765 100644
--- a/ethers-providers/tests/txpool.rs
+++ b/ethers-providers/tests/txpool.rs
@@ -1,14 +1,14 @@
 #![cfg(not(target_arch = "wasm32"))]
 use ethers_core::{
     types::{TransactionRequest, U256},
-    utils::Geth,
+    utils::Anvil,
 };
 use ethers_providers::{Http, Middleware, Provider};
 use std::convert::TryFrom;
 
 #[tokio::test]
 async fn txpool() {
-    let geth = Geth::new().block_time(20u64).spawn();
+    let geth = Anvil::new().block_time(20u64).spawn();
     let provider = Provider::<Http>::try_from(geth.endpoint()).unwrap();
 
     let account = provider.get_accounts().await.unwrap()[0];
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 8da70148..5aaad5aa 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -1434,7 +1434,6 @@ fn can_detect_contract_def_source_files() {
         .unwrap();
 
     let compiled = tmp.compile().unwrap();
-    println!("{}", compiled);
     assert!(!compiled.has_compiler_errors());
 
     let mut sources = compiled.output().sources;
@@ -1750,7 +1749,6 @@ contract D { }
         .unwrap();
 
     let compiled = project.compile().unwrap();
-    println!("{}", compiled);
     assert!(!compiled.has_compiler_errors());
 
     let cache = SolFilesCache::read(project.cache_path()).unwrap();