Repository: gakonst/ethers-rs
Pull request #1977 (2022-12-27 09:48:17)

ethers-signers/aws: sighash on the inner/updated tx object
Signed-off-by: Gyuho Lee <gyuho.lee@avalabs.org>

<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation

<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

-   [ ] Added Tests
-   [ ] Added Documentation
-   [ ] Updated the changelog
-   [ ] Breaking changes



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bcfaa4ba..16d62793 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -232,32 +232,7 @@ jobs:
             - 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
-
-                    echo "building $name"
-                    cargo build "${args[@]}"
-                  done
-            - name: Run all examples
+            - name: Build and run all examples
               run: |
                   export PATH=$HOME/bin:$PATH
                   chmod +x ./scripts/examples.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a623363..c5a0d996 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -275,6 +275,7 @@
 
 - `eth-keystore-rs` crate updated. Allow an optional name for the to-be-generated
   keystore file [#910](https://github.com/gakonst/ethers-rs/pull/910)
+- [1983](https://github.com/gakonst/ethers-rs/pull/1983) Added a `from_bytes` function for the `Wallet` type.
 
 ### 0.6.0
 
diff --git a/Cargo.lock b/Cargo.lock
index 0121253b..f6f73319 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1627,6 +1627,111 @@ dependencies = [
  "wee_alloc",
 ]
 
+[[package]]
+name = "examples-anvil"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "tokio",
+]
+
+[[package]]
+name = "examples-big-numbers"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+]
+
+[[package]]
+name = "examples-contracts"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-events"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-middleware"
+version = "1.0.2"
+dependencies = [
+ "async-trait",
+ "ethers",
+ "eyre",
+ "serde_json",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "examples-providers"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-queries"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-subscriptions"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-transactions"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "examples-wallets"
+version = "1.0.2"
+dependencies = [
+ "ethers",
+ "eyre",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
 [[package]]
 name = "eyre"
 version = "0.6.8"
@@ -2616,9 +2721,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
 
 [[package]]
 name = "once_cell"
-version = "1.16.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
 
 [[package]]
 name = "oorandom"
diff --git a/Cargo.toml b/Cargo.toml
index f4c2adf4..45c2d1d1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,8 @@ members = [
     "ethers-middleware",
     "ethers-etherscan",
     "ethers-solc",
-    "examples/ethers-wasm",
+    # Example crates
+    "examples/*",
 ]
 
 default-members = [
@@ -128,62 +129,3 @@ bytes = "1.3.0"
 # Tell `rustc` to optimize for small code size.
 opt-level = "s"
 
-[[example]]
-name = "abigen"
-path = "examples/abigen.rs"
-required-features = ["ethers-solc"]
-
-[[example]]
-name = "contract_human_readable"
-path = "examples/contract_human_readable.rs"
-required-features = ["ethers-solc"]
-
-[[example]]
-name = "contract_with_abi"
-path = "examples/contract_with_abi.rs"
-required-features = ["ethers-solc"]
-
-[[example]]
-name = "contract_with_abi_and_bytecode"
-path = "examples/contract_with_abi_and_bytecode.rs"
-required-features = ["ethers-solc"]
-
-[[example]]
-name = "ipc"
-path = "examples/ipc.rs"
-required-features = ["ipc"]
-
-[[example]]
-name = "ledger"
-path = "examples/ledger.rs"
-required-features = ["ledger"]
-
-[[example]]
-name = "moonbeam_with_abi"
-path = "examples/moonbeam_with_abi.rs"
-required-features = ["legacy", "ethers-solc"]
-
-[[example]]
-name = "trezor"
-path = "examples/trezor.rs"
-required-features = ["trezor"]
-
-[[example]]
-name = "yubi"
-path = "examples/yubi.rs"
-required-features = ["yubi"]
-
-[[example]]
-name = "paginated_logs"
-path = "examples/paginated_logs.rs"
-required-features = ["rustls"]
-
-[[example]]
-name = "subscribe_contract_events"
-path = "examples/subscribe_contract_events.rs"
-required-features = ["rustls", "ws"]
-
-[[example]]
-name = "subscribe_contract_events_with_meta"
-path = "examples/subscribe_contract_events_with_meta.rs"
-required-features = ["rustls", "ws"]
diff --git a/README.md b/README.md
index 9d98bb16..c85e9f9e 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,12 @@ Extensive documentation and examples are available [here](https://docs.rs/ethers
 
 Alternatively, you may clone the repository and run `cd ethers/ && cargo doc --open`
 
-You can also run any of the examples by executing: `cargo run -p ethers --example <name>`
+Examples are organized into individual crates under the `/examples` folder.
+You can run any of the examples by executing: 
+```bash
+# cargo run -p <example-crate-name> --example <name>
+cargo run -p examples-big-numbers --example math_operations
+```
 
 ## Add ethers-rs to your repository
 
diff --git a/docs/mdbook/.gitignore b/docs/mdbook/.gitignore
new file mode 100644
index 00000000..7585238e
--- /dev/null
+++ b/docs/mdbook/.gitignore
@@ -0,0 +1,1 @@
+book
diff --git a/docs/mdbook/book.toml b/docs/mdbook/book.toml
new file mode 100644
index 00000000..5fb585f3
--- /dev/null
+++ b/docs/mdbook/book.toml
@@ -0,0 +1,6 @@
+[book]
+authors = ["The ethers-rs contributors"]
+language = "en"
+multilingual = false
+src = "src"
+title = "Ethers.rs: The Ethereum Library for Rust"
diff --git a/docs/mdbook/src/SUMMARY.md b/docs/mdbook/src/SUMMARY.md
new file mode 100644
index 00000000..01c93f96
--- /dev/null
+++ b/docs/mdbook/src/SUMMARY.md
@@ -0,0 +1,89 @@
+# Summary
+
+# Getting started
+  - [Intro](./getting-started/intro.md)
+  - [Start a new project](./getting-started/start_a_new_project.md)
+  - [Connect to an Ethereum node](./getting-started/connect_to_an_ethereum_node.md)
+
+# Reference guide
+- [Providers]()
+  - [Http]()
+  - [IPC]()
+  - [Mock]()
+  - [Quorum]()
+  - [Retry]()
+  - [RW]()
+  - [WS]()
+- [Middleware]()
+  - [Builder]()
+  - [Create custom middleware]()
+  - [Gas escalator]()
+  - [Gas oracle]()
+  - [Nonce manager]()
+  - [Policy]()
+  - [Signer]()
+  - [Time lag]()
+  - [Transformer]()
+- [Contracts]()
+  - [Abigen]()
+  - [Compile]()
+  - [Creating Instances]()
+  - [Deploy Anvil]()
+  - [Deploy from ABI and bytecode]()
+  - [Deploy Moonbeam]()
+  - [Events]()
+  - [Events with meta]()
+  - [Methods]()
+- [Events]()
+  - [Logs and filtering]()
+  - [Solidity topics]()
+- [Subscriptions]()
+  - [Watch blocks]()
+  - [Subscribe events by type]()
+  - [Subscribe logs]()
+- [Queries]()
+  - [Blocks]()
+  - [Contracts]()
+  - [Events]()
+  - [Paginated logs]()
+  - [UniswapV2 pair]()
+  - [Transactions]()
+- [Transactions]()
+  - [Call override]()
+  - [Create raw transaction]()
+  - [Create typed transaction]()
+  - [Decode input]()
+  - [EIP-1559]()
+  - [ENS]() 
+  - [Estimate gas]()
+  - [Get gas price]()
+  - [Get gas price USD]()
+  - [Remove liquidity]()
+  - [Set gas for a transaction]()
+  - [Send raw transaction]()
+  - [Send typed transaction]()
+  - [Trace]()
+  - [Transaction receipt]()
+  - [Transaction status]()
+  - [Transfer ETH]()
+- [Wallets]()
+  - [Address book]()
+  - [Mnemonic]()
+  - [Ledger]()
+  - [Local]()
+  - [Permit hash]()
+  - [Sign message]()
+  - [Trezor]()
+  - [Yubi]()
+- [Big numbers](./big-numbers/intro.md)
+  - [Comparison and equivalence](./big-numbers/comparison-and-equivalence.md) 
+  - [Conversion](./big-numbers/conversion.md)
+  - [Creating Instances](./big-numbers/creating_instances.md)
+  - [Math operations](./big-numbers/math-operations.md)
+  - [Utilities](./big-numbers/utilities.md)
+- [Anvil]()
+    - [Boot anvil]()
+    - [Deploy contracts]()
+    - [Fork]() 
+    - [Testing]()
+
diff --git a/docs/mdbook/src/big-numbers/comparison-and-equivalence.md b/docs/mdbook/src/big-numbers/comparison-and-equivalence.md
new file mode 100644
index 00000000..a71d2d06
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/comparison-and-equivalence.md
@@ -0,0 +1,6 @@
+
+# Comparison and equivalence 
+
+```rust
+{{#include ../../../../examples/big-numbers/examples/comparison_equivalence.rs}}
+```
diff --git a/docs/mdbook/src/big-numbers/conversion.md b/docs/mdbook/src/big-numbers/conversion.md
new file mode 100644
index 00000000..1eacde55
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/conversion.md
@@ -0,0 +1,5 @@
+# Conversion
+
+```rust
+{{#include ../../../../examples/big-numbers/examples/conversion.rs}}
+```
diff --git a/docs/mdbook/src/big-numbers/creating_instances.md b/docs/mdbook/src/big-numbers/creating_instances.md
new file mode 100644
index 00000000..968ad59b
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/creating_instances.md
@@ -0,0 +1,5 @@
+# Creating instances
+
+```rust
+{{#include ../../../../examples/big-numbers/examples/create_instances.rs}}
+```
\ No newline at end of file
diff --git a/docs/mdbook/src/big-numbers/intro.md b/docs/mdbook/src/big-numbers/intro.md
new file mode 100644
index 00000000..7c6afdce
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/intro.md
@@ -0,0 +1,1 @@
+{{#include ../../../../examples/big-numbers/README.md}}
diff --git a/docs/mdbook/src/big-numbers/math-operations.md b/docs/mdbook/src/big-numbers/math-operations.md
new file mode 100644
index 00000000..d393d552
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/math-operations.md
@@ -0,0 +1,5 @@
+# Math operations
+
+```rust
+{{#include ../../../../examples/big-numbers/examples/math_operations.rs}}
+```
diff --git a/docs/mdbook/src/big-numbers/utilities.md b/docs/mdbook/src/big-numbers/utilities.md
new file mode 100644
index 00000000..c1a757c1
--- /dev/null
+++ b/docs/mdbook/src/big-numbers/utilities.md
@@ -0,0 +1,5 @@
+# Utilities
+
+```rust
+{{#include ../../../../examples/big-numbers/examples/utilities.rs}}
+```
\ No newline at end of file
diff --git a/docs/mdbook/src/getting-started/connect_to_an_ethereum_node.md b/docs/mdbook/src/getting-started/connect_to_an_ethereum_node.md
new file mode 100644
index 00000000..5a19d9c7
--- /dev/null
+++ b/docs/mdbook/src/getting-started/connect_to_an_ethereum_node.md
@@ -0,0 +1,33 @@
+# Connect to an Ethereum node
+Ethers-rs allows application to connect the blockchain using web3 providers. Providers act as an interface between applications and an Ethereum node, allowing you to send requests and receive responses via JSON-RPC messages.
+
+Some common actions you can perform using a provider include:
+
+* Getting the current block number
+* Getting the balance of an Ethereum address
+* Sending a transaction to the blockchain
+* Calling a smart contract function
+* Subscribe logs and smart contract events
+* Getting the transaction history of an address
+
+Providers are an important part of web3 libraries because they allow you to easily interact with the Ethereum blockchain without having to manage the underlying connection to the node yourself.
+
+Code below shows a basic setup to connect a provider to a node:
+```rust
+/// The `prelude` module provides a convenient way to import a number 
+/// of common dependencies at once. This can be useful if you are working 
+/// with multiple parts of the library and want to avoid having 
+/// to import each dependency individually.
+use ethers::prelude::*;
+
+const RPC_URL: &str = "https://mainnet.infura.io/v3/your-project-id";
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {    
+    let provider = Provider::<Http>::try_from(RPC_URL)?;
+    let block_number: U64 = provider.get_block_number().await?;
+    println!("{block_number}");
+
+    Ok(())
+}
+```
\ No newline at end of file
diff --git a/docs/mdbook/src/getting-started/intro.md b/docs/mdbook/src/getting-started/intro.md
new file mode 100644
index 00000000..1345ffb8
--- /dev/null
+++ b/docs/mdbook/src/getting-started/intro.md
@@ -0,0 +1,8 @@
+# Intro
+Welcome to the hands-on guide for the ethers-rs library!
+
+This documentation contains a collection of examples demonstrating how to use the library to build Ethereum-based applications in Rust. The examples cover a range of topics, from basic smart contract interactions to more advanced usage of ethers-rs.
+
+Each example includes a detailed description of the functionality being demonstrated, as well as complete code snippets that you can use as a starting point for your own projects.
+
+We hope that these docs will help you get started with ethers-rs and give you a better understanding of how to use the library to build your own web3 applications in Rust. If you have any questions or need further assistance, please don't hesitate to reach out to the ethers-rs community.
\ No newline at end of file
diff --git a/docs/mdbook/src/getting-started/start_a_new_project.md b/docs/mdbook/src/getting-started/start_a_new_project.md
new file mode 100644
index 00000000..ad97b339
--- /dev/null
+++ b/docs/mdbook/src/getting-started/start_a_new_project.md
@@ -0,0 +1,65 @@
+# Start a new project
+To set up a new project with ethers-rs, you will need to install the Rust programming language and the cargo package manager on your system.
+
+1. Install Rust by following the instructions at [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install).
+2. Once Rust is installed, create a new Rust project by running the following command:
+    ```bash
+    cargo new my-project
+    ```
+   This will create a new directory called my-project with the necessary files for a new Rust project.
+
+3. Navigate to the project directory and add ethers-rs as a dependency in your `Cargo.toml` file:
+   ```toml
+   [dependencies]
+   ethers = "1.0.0" 
+
+   # Most of ethers-rs features rely upon an async Rust runtime.
+   # Since Rust doesn't provide an async runtime itself, you can
+   # include the excellent tokio library
+   tokio = { version = "1.23.0", features = ["macros"] }
+   ```
+
+   If you want to make experiments and/or play around with early ethers-rs features link our GitHub repo in the `Cargo.toml`.
+
+    ```toml
+    [dependencies]
+    ethers = { git = "https://github.com/gakonst/ethers-rs" }
+
+    # Use the "branch" attribute to specify a branch other than master
+    [dependencies]
+    ethers = { git = "https://github.com/gakonst/ethers-rs", branch = "branch-name" }
+
+    # You can specify a tag or commit hash with the "rev" attribute
+    [dependencies]
+    ethers = { git = "https://github.com/gakonst/ethers-rs", rev = "1.0.2" }
+    ```
+   > **Note:** using a Git repository as a dependency is generally not recommended 
+   > for production projects, as it can make it difficult to ensure that you are using 
+   > a specific and stable version of the dependency. 
+   > It is usually better to specify a version number or range to ensure that your project 
+   > is reproducible.
+
+## Enable transports
+Ethers-rs enables interactions with Ethereum nodes through different "transport" types, or communication protocols.
+The following transport types are currently supported by ethers.rs:
+
+* **HTTP(S):** The HTTP(S) transport is used to communicate with Ethereum nodes over the HTTP or HTTPS protocols. This is the most common way to interact with Ethereum nodes. If you are looking to connect to a HTTPS endpoint, then you need to enable the `rustls` or `openssl` features:
+    ```toml
+    [dependencies]
+    ethers = { version = "1.0.0", features = ["rustls"] }
+    ```
+
+* **WebSocket:** The WebSocket transport is used to communicate with Ethereum nodes over the WebSocket protocol, which is a widely-supported standard for establishing a bi-directional communication channel between a client and a server. This can be used for a variety of purposes, including receiving real-time updates from an Ethereum node, or submitting transactions to the Ethereum network. Websockets support is turned on via the feature-flag ws:
+    ```toml
+    [dependencies]
+    ethers = { version = "1.0.0", features = ["ws"] }
+    ```
+
+* **IPC (Interprocess Communication):** The IPC transport is used to communicate with a local Ethereum node using the IPC protocol, which is a way for processes to communicate with each other on a single computer. This is commonly used in Ethereum development to allow applications to communicate with a local Ethereum node, such as geth or parity. IPC support is turned on via the feature-flag `ipc`:
+    ```toml
+    [dependencies]
+    ethers = { version = "1.0.0", features = ["ipc"] }
+    ```
+
+
+
diff --git a/ethers-addressbook/Cargo.toml b/ethers-addressbook/Cargo.toml
index 17907527..3e491ffc 100644
--- a/ethers-addressbook/Cargo.toml
+++ b/ethers-addressbook/Cargo.toml
@@ -11,7 +11,7 @@ repository = "https://github.com/gakonst/ethers-rs"
 keywords = ["ethereum", "web3", "celo", "ethers"]
 
 [dependencies]
-once_cell = "1.16.0"
+once_cell = "1.17.0"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 
diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml
index 821fe3bf..aa9cb7ca 100644
--- a/ethers-contract/Cargo.toml
+++ b/ethers-contract/Cargo.toml
@@ -20,7 +20,7 @@ ethers-derive-eip712 = { version = "^1.0.0", path = "../ethers-core/ethers-deriv
 serde = { version = "1.0.124", default-features = false }
 serde_json = { version = "1.0.64", default-features = false }
 thiserror = { version = "1.0", default-features = false }
-once_cell = { version = "1.16.0" }
+once_cell = { version = "1.17.0" }
 pin-project = { version = "1.0.11", default-features = false }
 futures-util = { version = "^0.3" }
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml
index f69e2f69..164d8b9e 100644
--- a/ethers-contract/ethers-contract-abigen/Cargo.toml
+++ b/ethers-contract/ethers-contract-abigen/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "ethers-contract-abigen"
 version = "1.0.2"
+edition = "2021"
+rust-version = "1.64"
 authors = [
     "Nicholas Rodrigues Lordello <nlordell@gmail.com>",
     "Georgios Konstantopoulos <me@gakonst.com>",
 ]
-edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "Code generation for type-safe bindings to Ethereum smart contracts"
 homepage = "https://docs.rs/ethers"
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/types.rs b/ethers-contract/ethers-contract-abigen/src/contract/types.rs
index 78d66501..609add65 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/types.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/types.rs
@@ -15,7 +15,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
             3..=4 => Ok(quote! { i32 }),
             5..=8 => Ok(quote! { i64 }),
             9..=16 => Ok(quote! { i128 }),
-            17..=32 => Ok(quote! { I256 }),
+            17..=32 => Ok(quote! { #ethers_core::types::I256 }),
             _ => bail!("unsupported solidity type int{}", n),
         },
         ParamType::Uint(n) => match n / 8 {
diff --git a/ethers-contract/ethers-contract-derive/Cargo.toml b/ethers-contract/ethers-contract-derive/Cargo.toml
index 1c49c995..0191d2f5 100644
--- a/ethers-contract/ethers-contract-derive/Cargo.toml
+++ b/ethers-contract/ethers-contract-derive/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "ethers-contract-derive"
 version = "1.0.2"
+edition = "2021"
+rust-version = "1.64"
 authors = [
     "Nicholas Rodrigues Lordello <nlordell@gmail.com>",
     "Georgios Konstantopoulos <me@gakonst.com>",
 ]
-edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "Proc macro for type-safe bindings generation to Ethereum and Celo smart contracts"
 homepage = "https://docs.rs/ethers"
diff --git a/ethers-contract/src/multicall/mod.rs b/ethers-contract/src/multicall/mod.rs
index 56dca427..0b5bc56f 100644
--- a/ethers-contract/src/multicall/mod.rs
+++ b/ethers-contract/src/multicall/mod.rs
@@ -661,7 +661,7 @@ impl<M: Middleware> Multicall<M> {
     /// #
     /// # let multicall = Multicall::new(client, None).await?;
     /// // If the all Solidity function calls `returns (uint256)`:
-    /// let result: Vec<U256> = multicall.call().await?;
+    /// let result: Vec<U256> = multicall.call_array().await?;
     /// # Ok(())
     /// # }
     /// ```
diff --git a/ethers-contract/src/multicall/multicall_abi.json b/ethers-contract/src/multicall/multicall_abi.json
new file mode 100644
index 00000000..75da07c0
--- /dev/null
+++ b/ethers-contract/src/multicall/multicall_abi.json
@@ -0,0 +1,1 @@
+[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3[]","name":"calls","type":"tuple[]"}],"name":"aggregate3","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3Value[]","name":"calls","type":"tuple[]"}],"name":"aggregate3Value","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"blockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getBasefee","outputs":[{"internalType":"uint256","name":"basefee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryAggregate","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryBlockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"}]
\ No newline at end of file
diff --git a/ethers-contract/src/multicall/multicall_contract.rs b/ethers-contract/src/multicall/multicall_contract.rs
index 0b58455e..82878124 100644
--- a/ethers-contract/src/multicall/multicall_contract.rs
+++ b/ethers-contract/src/multicall/multicall_contract.rs
@@ -1,940 +1,1 @@
-#[allow(clippy::too_many_arguments, non_camel_case_types)]
-pub mod multicall_3 {
-    #![allow(clippy::enum_variant_names)]
-    #![allow(dead_code)]
-    #![allow(clippy::type_complexity)]
-    #![allow(unused_imports)]
-
-    // This is a hack to guarantee all ethers-derive macros can find the types.
-    // See [`ethers_core::macros::determine_ethers_crates`].
-    #[doc(hidden)]
-    mod ethers {
-        pub mod contract {
-            pub use crate::*;
-        }
-        pub mod core {
-            pub use ethers_core::*;
-        }
-        pub mod providers {
-            pub use ethers_providers::*;
-        }
-        pub mod types {
-            pub use ethers_core::types::*;
-        }
-    }
-    #[doc(hidden)]
-    mod ethers_contract {
-        pub use crate::*;
-    }
-
-    use self::ethers_contract::{
-        builders::{ContractCall, Event},
-        Contract, Lazy,
-    };
-    use ethers_core::{
-        abi::{Abi, Detokenize, InvalidOutputType, Token, Tokenizable},
-        types::{Address, Bytes, U256},
-    };
-    use ethers_providers::Middleware;
-    use std::sync::Arc;
-
-    #[doc = "Multicall3 was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"]
-    # [rustfmt :: skip] const __ABI : & str = "[{\"type\":\"function\",\"name\":\"aggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3Value\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"uint256\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"blockAndAggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"getBasefee\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockHash\",\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockNumber\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getChainId\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockCoinbase\",\"inputs\":[],\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockDifficulty\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockGasLimit\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockTimestamp\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getEthBalance\",\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getLastBlockHash\",\"inputs\":[],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tryAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"tryBlockAndAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"}]" ;
-    #[doc = r" The parsed JSON-ABI of the contract."]
-    pub static MULTICALL3_ABI: ethers_contract::Lazy<ethers_core::abi::Abi> =
-        ethers_contract::Lazy::new(|| {
-            ethers_core::utils::__serde_json::from_str(__ABI).expect("invalid abi")
-        });
-    pub struct Multicall3<M>(ethers_contract::Contract<M>);
-    impl<M> Clone for Multicall3<M> {
-        fn clone(&self) -> Self {
-            Multicall3(self.0.clone())
-        }
-    }
-    impl<M> std::ops::Deref for Multicall3<M> {
-        type Target = ethers_contract::Contract<M>;
-        fn deref(&self) -> &Self::Target {
-            &self.0
-        }
-    }
-    impl<M> std::fmt::Debug for Multicall3<M> {
-        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-            f.debug_tuple(stringify!(Multicall3)).field(&self.address()).finish()
-        }
-    }
-    impl<M: ethers_providers::Middleware> Multicall3<M> {
-        #[doc = r" Creates a new contract instance with the specified `ethers`"]
-        #[doc = r" client at the given `Address`. The contract derefs to a `crate`"]
-        #[doc = r" object"]
-        pub fn new<T: Into<ethers_core::types::Address>>(
-            address: T,
-            client: ::std::sync::Arc<M>,
-        ) -> Self {
-            ethers_contract::Contract::new(address.into(), MULTICALL3_ABI.clone(), client).into()
-        }
-        #[doc = "Calls the contract's `aggregate` (0x252dba42) function"]
-        pub fn aggregate(
-            &self,
-            calls: ::std::vec::Vec<Call>,
-        ) -> ethers_contract::builders::ContractCall<
-            M,
-            (ethers_core::types::U256, ::std::vec::Vec<ethers_core::types::Bytes>),
-        > {
-            self.0
-                .method_hash([37, 45, 186, 66], calls)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `aggregate3` (0x82ad56cb) function"]
-        pub fn aggregate_3(
-            &self,
-            calls: ::std::vec::Vec<Call3>,
-        ) -> ethers_contract::builders::ContractCall<M, ::std::vec::Vec<Result>> {
-            self.0
-                .method_hash([130, 173, 86, 203], calls)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `aggregate3Value` (0x174dea71) function"]
-        pub fn aggregate_3_value(
-            &self,
-            calls: ::std::vec::Vec<Call3Value>,
-        ) -> ethers_contract::builders::ContractCall<M, ::std::vec::Vec<Result>> {
-            self.0
-                .method_hash([23, 77, 234, 113], calls)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `blockAndAggregate` (0xc3077fa9) function"]
-        pub fn block_and_aggregate(
-            &self,
-            calls: ::std::vec::Vec<Call>,
-        ) -> ethers_contract::builders::ContractCall<
-            M,
-            (ethers_core::types::U256, [u8; 32], ::std::vec::Vec<Result>),
-        > {
-            self.0
-                .method_hash([195, 7, 127, 169], calls)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getBasefee` (0x3e64a696) function"]
-        pub fn get_basefee(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([62, 100, 166, 150], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getBlockHash` (0xee82ac5e) function"]
-        pub fn get_block_hash(
-            &self,
-            block_number: ethers_core::types::U256,
-        ) -> ethers_contract::builders::ContractCall<M, [u8; 32]> {
-            self.0
-                .method_hash([238, 130, 172, 94], block_number)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getBlockNumber` (0x42cbb15c) function"]
-        pub fn get_block_number(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([66, 203, 177, 92], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getChainId` (0x3408e470) function"]
-        pub fn get_chain_id(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([52, 8, 228, 112], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getCurrentBlockCoinbase` (0xa8b0574e) function"]
-        pub fn get_current_block_coinbase(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::Address> {
-            self.0
-                .method_hash([168, 176, 87, 78], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getCurrentBlockDifficulty` (0x72425d9d) function"]
-        pub fn get_current_block_difficulty(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([114, 66, 93, 157], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getCurrentBlockGasLimit` (0x86d516e8) function"]
-        pub fn get_current_block_gas_limit(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([134, 213, 22, 232], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getCurrentBlockTimestamp` (0x0f28c97d) function"]
-        pub fn get_current_block_timestamp(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([15, 40, 201, 125], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getEthBalance` (0x4d2301cc) function"]
-        pub fn get_eth_balance(
-            &self,
-            addr: ethers_core::types::Address,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::U256> {
-            self.0
-                .method_hash([77, 35, 1, 204], addr)
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `getLastBlockHash` (0x27e86d6e) function"]
-        pub fn get_last_block_hash(&self) -> ethers_contract::builders::ContractCall<M, [u8; 32]> {
-            self.0
-                .method_hash([39, 232, 109, 110], ())
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `tryAggregate` (0xbce38bd7) function"]
-        pub fn try_aggregate(
-            &self,
-            require_success: bool,
-            calls: ::std::vec::Vec<Call>,
-        ) -> ethers_contract::builders::ContractCall<M, ::std::vec::Vec<Result>> {
-            self.0
-                .method_hash([188, 227, 139, 215], (require_success, calls))
-                .expect("method not found (this should never happen)")
-        }
-        #[doc = "Calls the contract's `tryBlockAndAggregate` (0x399542e9) function"]
-        pub fn try_block_and_aggregate(
-            &self,
-            require_success: bool,
-            calls: ::std::vec::Vec<Call>,
-        ) -> ethers_contract::builders::ContractCall<
-            M,
-            (ethers_core::types::U256, [u8; 32], ::std::vec::Vec<Result>),
-        > {
-            self.0
-                .method_hash([57, 149, 66, 233], (require_success, calls))
-                .expect("method not found (this should never happen)")
-        }
-    }
-    impl<M: ethers_providers::Middleware> From<ethers_contract::Contract<M>> for Multicall3<M> {
-        fn from(contract: ethers_contract::Contract<M>) -> Self {
-            Self(contract)
-        }
-    }
-    #[doc = "Container type for all input parameters for the `aggregate` function with signature `aggregate((address,bytes)[])` and selector `[37, 45, 186, 66]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "aggregate", abi = "aggregate((address,bytes)[])")]
-    pub struct AggregateCall {
-        pub calls: ::std::vec::Vec<Call>,
-    }
-    #[doc = "Container type for all input parameters for the `aggregate3` function with signature `aggregate3((address,bool,bytes)[])` and selector `[130, 173, 86, 203]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "aggregate3", abi = "aggregate3((address,bool,bytes)[])")]
-    pub struct Aggregate3Call {
-        pub calls: ::std::vec::Vec<Call3>,
-    }
-    #[doc = "Container type for all input parameters for the `aggregate3Value` function with signature `aggregate3Value((address,bool,uint256,bytes)[])` and selector `[23, 77, 234, 113]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "aggregate3Value", abi = "aggregate3Value((address,bool,uint256,bytes)[])")]
-    pub struct Aggregate3ValueCall {
-        pub calls: ::std::vec::Vec<Call3Value>,
-    }
-    #[doc = "Container type for all input parameters for the `blockAndAggregate` function with signature `blockAndAggregate((address,bytes)[])` and selector `[195, 7, 127, 169]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "blockAndAggregate", abi = "blockAndAggregate((address,bytes)[])")]
-    pub struct BlockAndAggregateCall {
-        pub calls: ::std::vec::Vec<Call>,
-    }
-    #[doc = "Container type for all input parameters for the `getBasefee` function with signature `getBasefee()` and selector `[62, 100, 166, 150]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getBasefee", abi = "getBasefee()")]
-    pub struct GetBasefeeCall;
-    #[doc = "Container type for all input parameters for the `getBlockHash` function with signature `getBlockHash(uint256)` and selector `[238, 130, 172, 94]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getBlockHash", abi = "getBlockHash(uint256)")]
-    pub struct GetBlockHashCall {
-        pub block_number: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all input parameters for the `getBlockNumber` function with signature `getBlockNumber()` and selector `[66, 203, 177, 92]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getBlockNumber", abi = "getBlockNumber()")]
-    pub struct GetBlockNumberCall;
-    #[doc = "Container type for all input parameters for the `getChainId` function with signature `getChainId()` and selector `[52, 8, 228, 112]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getChainId", abi = "getChainId()")]
-    pub struct GetChainIdCall;
-    #[doc = "Container type for all input parameters for the `getCurrentBlockCoinbase` function with signature `getCurrentBlockCoinbase()` and selector `[168, 176, 87, 78]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getCurrentBlockCoinbase", abi = "getCurrentBlockCoinbase()")]
-    pub struct GetCurrentBlockCoinbaseCall;
-    #[doc = "Container type for all input parameters for the `getCurrentBlockDifficulty` function with signature `getCurrentBlockDifficulty()` and selector `[114, 66, 93, 157]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getCurrentBlockDifficulty", abi = "getCurrentBlockDifficulty()")]
-    pub struct GetCurrentBlockDifficultyCall;
-    #[doc = "Container type for all input parameters for the `getCurrentBlockGasLimit` function with signature `getCurrentBlockGasLimit()` and selector `[134, 213, 22, 232]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getCurrentBlockGasLimit", abi = "getCurrentBlockGasLimit()")]
-    pub struct GetCurrentBlockGasLimitCall;
-    #[doc = "Container type for all input parameters for the `getCurrentBlockTimestamp` function with signature `getCurrentBlockTimestamp()` and selector `[15, 40, 201, 125]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getCurrentBlockTimestamp", abi = "getCurrentBlockTimestamp()")]
-    pub struct GetCurrentBlockTimestampCall;
-    #[doc = "Container type for all input parameters for the `getEthBalance` function with signature `getEthBalance(address)` and selector `[77, 35, 1, 204]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getEthBalance", abi = "getEthBalance(address)")]
-    pub struct GetEthBalanceCall {
-        pub addr: ethers_core::types::Address,
-    }
-    #[doc = "Container type for all input parameters for the `getLastBlockHash` function with signature `getLastBlockHash()` and selector `[39, 232, 109, 110]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "getLastBlockHash", abi = "getLastBlockHash()")]
-    pub struct GetLastBlockHashCall;
-    #[doc = "Container type for all input parameters for the `tryAggregate` function with signature `tryAggregate(bool,(address,bytes)[])` and selector `[188, 227, 139, 215]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "tryAggregate", abi = "tryAggregate(bool,(address,bytes)[])")]
-    pub struct TryAggregateCall {
-        pub require_success: bool,
-        pub calls: ::std::vec::Vec<Call>,
-    }
-    #[doc = "Container type for all input parameters for the `tryBlockAndAggregate` function with signature `tryBlockAndAggregate(bool,(address,bytes)[])` and selector `[57, 149, 66, 233]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthCall,
-        ethers_contract :: EthDisplay,
-    )]
-    #[ethcall(name = "tryBlockAndAggregate", abi = "tryBlockAndAggregate(bool,(address,bytes)[])")]
-    pub struct TryBlockAndAggregateCall {
-        pub require_success: bool,
-        pub calls: ::std::vec::Vec<Call>,
-    }
-    #[derive(Debug, Clone, PartialEq, Eq, ethers_contract :: EthAbiType)]
-    pub enum Multicall3Calls {
-        Aggregate(AggregateCall),
-        Aggregate3(Aggregate3Call),
-        Aggregate3Value(Aggregate3ValueCall),
-        BlockAndAggregate(BlockAndAggregateCall),
-        GetBasefee(GetBasefeeCall),
-        GetBlockHash(GetBlockHashCall),
-        GetBlockNumber(GetBlockNumberCall),
-        GetChainId(GetChainIdCall),
-        GetCurrentBlockCoinbase(GetCurrentBlockCoinbaseCall),
-        GetCurrentBlockDifficulty(GetCurrentBlockDifficultyCall),
-        GetCurrentBlockGasLimit(GetCurrentBlockGasLimitCall),
-        GetCurrentBlockTimestamp(GetCurrentBlockTimestampCall),
-        GetEthBalance(GetEthBalanceCall),
-        GetLastBlockHash(GetLastBlockHashCall),
-        TryAggregate(TryAggregateCall),
-        TryBlockAndAggregate(TryBlockAndAggregateCall),
-    }
-    impl ethers_core::abi::AbiDecode for Multicall3Calls {
-        fn decode(
-            data: impl AsRef<[u8]>,
-        ) -> ::std::result::Result<Self, ethers_core::abi::AbiError> {
-            if let Ok(decoded) =
-                <AggregateCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::Aggregate(decoded))
-            }
-            if let Ok(decoded) =
-                <Aggregate3Call as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::Aggregate3(decoded))
-            }
-            if let Ok(decoded) =
-                <Aggregate3ValueCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::Aggregate3Value(decoded))
-            }
-            if let Ok(decoded) =
-                <BlockAndAggregateCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::BlockAndAggregate(decoded))
-            }
-            if let Ok(decoded) =
-                <GetBasefeeCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetBasefee(decoded))
-            }
-            if let Ok(decoded) =
-                <GetBlockHashCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetBlockHash(decoded))
-            }
-            if let Ok(decoded) =
-                <GetBlockNumberCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetBlockNumber(decoded))
-            }
-            if let Ok(decoded) =
-                <GetChainIdCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetChainId(decoded))
-            }
-            if let Ok(decoded) =
-                <GetCurrentBlockCoinbaseCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetCurrentBlockCoinbase(decoded))
-            }
-            if let Ok(decoded) =
-                <GetCurrentBlockDifficultyCall as ethers_core::abi::AbiDecode>::decode(
-                    data.as_ref(),
-                )
-            {
-                return Ok(Multicall3Calls::GetCurrentBlockDifficulty(decoded))
-            }
-            if let Ok(decoded) =
-                <GetCurrentBlockGasLimitCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetCurrentBlockGasLimit(decoded))
-            }
-            if let Ok(decoded) =
-                <GetCurrentBlockTimestampCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetCurrentBlockTimestamp(decoded))
-            }
-            if let Ok(decoded) =
-                <GetEthBalanceCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetEthBalance(decoded))
-            }
-            if let Ok(decoded) =
-                <GetLastBlockHashCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::GetLastBlockHash(decoded))
-            }
-            if let Ok(decoded) =
-                <TryAggregateCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::TryAggregate(decoded))
-            }
-            if let Ok(decoded) =
-                <TryBlockAndAggregateCall as ethers_core::abi::AbiDecode>::decode(data.as_ref())
-            {
-                return Ok(Multicall3Calls::TryBlockAndAggregate(decoded))
-            }
-            Err(ethers_core::abi::Error::InvalidData.into())
-        }
-    }
-    impl ethers_core::abi::AbiEncode for Multicall3Calls {
-        fn encode(self) -> Vec<u8> {
-            match self {
-                Multicall3Calls::Aggregate(element) => element.encode(),
-                Multicall3Calls::Aggregate3(element) => element.encode(),
-                Multicall3Calls::Aggregate3Value(element) => element.encode(),
-                Multicall3Calls::BlockAndAggregate(element) => element.encode(),
-                Multicall3Calls::GetBasefee(element) => element.encode(),
-                Multicall3Calls::GetBlockHash(element) => element.encode(),
-                Multicall3Calls::GetBlockNumber(element) => element.encode(),
-                Multicall3Calls::GetChainId(element) => element.encode(),
-                Multicall3Calls::GetCurrentBlockCoinbase(element) => element.encode(),
-                Multicall3Calls::GetCurrentBlockDifficulty(element) => element.encode(),
-                Multicall3Calls::GetCurrentBlockGasLimit(element) => element.encode(),
-                Multicall3Calls::GetCurrentBlockTimestamp(element) => element.encode(),
-                Multicall3Calls::GetEthBalance(element) => element.encode(),
-                Multicall3Calls::GetLastBlockHash(element) => element.encode(),
-                Multicall3Calls::TryAggregate(element) => element.encode(),
-                Multicall3Calls::TryBlockAndAggregate(element) => element.encode(),
-            }
-        }
-    }
-    impl ::std::fmt::Display for Multicall3Calls {
-        fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
-            match self {
-                Multicall3Calls::Aggregate(element) => element.fmt(f),
-                Multicall3Calls::Aggregate3(element) => element.fmt(f),
-                Multicall3Calls::Aggregate3Value(element) => element.fmt(f),
-                Multicall3Calls::BlockAndAggregate(element) => element.fmt(f),
-                Multicall3Calls::GetBasefee(element) => element.fmt(f),
-                Multicall3Calls::GetBlockHash(element) => element.fmt(f),
-                Multicall3Calls::GetBlockNumber(element) => element.fmt(f),
-                Multicall3Calls::GetChainId(element) => element.fmt(f),
-                Multicall3Calls::GetCurrentBlockCoinbase(element) => element.fmt(f),
-                Multicall3Calls::GetCurrentBlockDifficulty(element) => element.fmt(f),
-                Multicall3Calls::GetCurrentBlockGasLimit(element) => element.fmt(f),
-                Multicall3Calls::GetCurrentBlockTimestamp(element) => element.fmt(f),
-                Multicall3Calls::GetEthBalance(element) => element.fmt(f),
-                Multicall3Calls::GetLastBlockHash(element) => element.fmt(f),
-                Multicall3Calls::TryAggregate(element) => element.fmt(f),
-                Multicall3Calls::TryBlockAndAggregate(element) => element.fmt(f),
-            }
-        }
-    }
-    impl ::std::convert::From<AggregateCall> for Multicall3Calls {
-        fn from(var: AggregateCall) -> Self {
-            Multicall3Calls::Aggregate(var)
-        }
-    }
-    impl ::std::convert::From<Aggregate3Call> for Multicall3Calls {
-        fn from(var: Aggregate3Call) -> Self {
-            Multicall3Calls::Aggregate3(var)
-        }
-    }
-    impl ::std::convert::From<Aggregate3ValueCall> for Multicall3Calls {
-        fn from(var: Aggregate3ValueCall) -> Self {
-            Multicall3Calls::Aggregate3Value(var)
-        }
-    }
-    impl ::std::convert::From<BlockAndAggregateCall> for Multicall3Calls {
-        fn from(var: BlockAndAggregateCall) -> Self {
-            Multicall3Calls::BlockAndAggregate(var)
-        }
-    }
-    impl ::std::convert::From<GetBasefeeCall> for Multicall3Calls {
-        fn from(var: GetBasefeeCall) -> Self {
-            Multicall3Calls::GetBasefee(var)
-        }
-    }
-    impl ::std::convert::From<GetBlockHashCall> for Multicall3Calls {
-        fn from(var: GetBlockHashCall) -> Self {
-            Multicall3Calls::GetBlockHash(var)
-        }
-    }
-    impl ::std::convert::From<GetBlockNumberCall> for Multicall3Calls {
-        fn from(var: GetBlockNumberCall) -> Self {
-            Multicall3Calls::GetBlockNumber(var)
-        }
-    }
-    impl ::std::convert::From<GetChainIdCall> for Multicall3Calls {
-        fn from(var: GetChainIdCall) -> Self {
-            Multicall3Calls::GetChainId(var)
-        }
-    }
-    impl ::std::convert::From<GetCurrentBlockCoinbaseCall> for Multicall3Calls {
-        fn from(var: GetCurrentBlockCoinbaseCall) -> Self {
-            Multicall3Calls::GetCurrentBlockCoinbase(var)
-        }
-    }
-    impl ::std::convert::From<GetCurrentBlockDifficultyCall> for Multicall3Calls {
-        fn from(var: GetCurrentBlockDifficultyCall) -> Self {
-            Multicall3Calls::GetCurrentBlockDifficulty(var)
-        }
-    }
-    impl ::std::convert::From<GetCurrentBlockGasLimitCall> for Multicall3Calls {
-        fn from(var: GetCurrentBlockGasLimitCall) -> Self {
-            Multicall3Calls::GetCurrentBlockGasLimit(var)
-        }
-    }
-    impl ::std::convert::From<GetCurrentBlockTimestampCall> for Multicall3Calls {
-        fn from(var: GetCurrentBlockTimestampCall) -> Self {
-            Multicall3Calls::GetCurrentBlockTimestamp(var)
-        }
-    }
-    impl ::std::convert::From<GetEthBalanceCall> for Multicall3Calls {
-        fn from(var: GetEthBalanceCall) -> Self {
-            Multicall3Calls::GetEthBalance(var)
-        }
-    }
-    impl ::std::convert::From<GetLastBlockHashCall> for Multicall3Calls {
-        fn from(var: GetLastBlockHashCall) -> Self {
-            Multicall3Calls::GetLastBlockHash(var)
-        }
-    }
-    impl ::std::convert::From<TryAggregateCall> for Multicall3Calls {
-        fn from(var: TryAggregateCall) -> Self {
-            Multicall3Calls::TryAggregate(var)
-        }
-    }
-    impl ::std::convert::From<TryBlockAndAggregateCall> for Multicall3Calls {
-        fn from(var: TryBlockAndAggregateCall) -> Self {
-            Multicall3Calls::TryBlockAndAggregate(var)
-        }
-    }
-    #[doc = "Container type for all return fields from the `aggregate` function with signature `aggregate((address,bytes)[])` and selector `[37, 45, 186, 66]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct AggregateReturn {
-        pub block_number: ethers_core::types::U256,
-        pub return_data: ::std::vec::Vec<ethers_core::types::Bytes>,
-    }
-    #[doc = "Container type for all return fields from the `aggregate3` function with signature `aggregate3((address,bool,bytes)[])` and selector `[130, 173, 86, 203]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Aggregate3Return {
-        pub return_data: ::std::vec::Vec<Result>,
-    }
-    #[doc = "Container type for all return fields from the `aggregate3Value` function with signature `aggregate3Value((address,bool,uint256,bytes)[])` and selector `[23, 77, 234, 113]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Aggregate3ValueReturn {
-        pub return_data: ::std::vec::Vec<Result>,
-    }
-    #[doc = "Container type for all return fields from the `blockAndAggregate` function with signature `blockAndAggregate((address,bytes)[])` and selector `[195, 7, 127, 169]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct BlockAndAggregateReturn {
-        pub block_number: ethers_core::types::U256,
-        pub block_hash: [u8; 32],
-        pub return_data: ::std::vec::Vec<Result>,
-    }
-    #[doc = "Container type for all return fields from the `getBasefee` function with signature `getBasefee()` and selector `[62, 100, 166, 150]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetBasefeeReturn {
-        pub basefee: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getBlockHash` function with signature `getBlockHash(uint256)` and selector `[238, 130, 172, 94]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetBlockHashReturn {
-        pub block_hash: [u8; 32],
-    }
-    #[doc = "Container type for all return fields from the `getBlockNumber` function with signature `getBlockNumber()` and selector `[66, 203, 177, 92]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetBlockNumberReturn {
-        pub block_number: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getChainId` function with signature `getChainId()` and selector `[52, 8, 228, 112]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetChainIdReturn {
-        pub chainid: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getCurrentBlockCoinbase` function with signature `getCurrentBlockCoinbase()` and selector `[168, 176, 87, 78]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetCurrentBlockCoinbaseReturn {
-        pub coinbase: ethers_core::types::Address,
-    }
-    #[doc = "Container type for all return fields from the `getCurrentBlockDifficulty` function with signature `getCurrentBlockDifficulty()` and selector `[114, 66, 93, 157]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetCurrentBlockDifficultyReturn {
-        pub difficulty: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getCurrentBlockGasLimit` function with signature `getCurrentBlockGasLimit()` and selector `[134, 213, 22, 232]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetCurrentBlockGasLimitReturn {
-        pub gaslimit: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getCurrentBlockTimestamp` function with signature `getCurrentBlockTimestamp()` and selector `[15, 40, 201, 125]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetCurrentBlockTimestampReturn {
-        pub timestamp: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getEthBalance` function with signature `getEthBalance(address)` and selector `[77, 35, 1, 204]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetEthBalanceReturn {
-        pub balance: ethers_core::types::U256,
-    }
-    #[doc = "Container type for all return fields from the `getLastBlockHash` function with signature `getLastBlockHash()` and selector `[39, 232, 109, 110]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct GetLastBlockHashReturn {
-        pub block_hash: [u8; 32],
-    }
-    #[doc = "Container type for all return fields from the `tryAggregate` function with signature `tryAggregate(bool,(address,bytes)[])` and selector `[188, 227, 139, 215]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct TryAggregateReturn {
-        pub return_data: ::std::vec::Vec<Result>,
-    }
-    #[doc = "Container type for all return fields from the `tryBlockAndAggregate` function with signature `tryBlockAndAggregate(bool,(address,bytes)[])` and selector `[57, 149, 66, 233]`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct TryBlockAndAggregateReturn {
-        pub block_number: ethers_core::types::U256,
-        pub block_hash: [u8; 32],
-        pub return_data: ::std::vec::Vec<Result>,
-    }
-    #[doc = "`Call(address,bytes)`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Call {
-        pub target: ethers_core::types::Address,
-        pub call_data: ethers_core::types::Bytes,
-    }
-    #[doc = "`Call3(address,bool,bytes)`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Call3 {
-        pub target: ethers_core::types::Address,
-        pub allow_failure: bool,
-        pub call_data: ethers_core::types::Bytes,
-    }
-    #[doc = "`Call3Value(address,bool,uint256,bytes)`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Call3Value {
-        pub target: ethers_core::types::Address,
-        pub allow_failure: bool,
-        pub value: ethers_core::types::U256,
-        pub call_data: ethers_core::types::Bytes,
-    }
-    #[doc = "`Result(bool,bytes)`"]
-    #[derive(
-        Clone,
-        Debug,
-        Default,
-        Eq,
-        PartialEq,
-        ethers_contract :: EthAbiType,
-        ethers_contract :: EthAbiCodec,
-    )]
-    pub struct Result {
-        pub success: bool,
-        pub return_data: ethers_core::types::Bytes,
-    }
-}
+ethers_contract_derive::abigen!(Multicall3, "src/multicall/multicall_abi.json");
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index fbf0e92d..8aa21a6f 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -1,10 +1,10 @@
 [package]
 name = "ethers-core"
-license = "MIT OR Apache-2.0"
 version = "1.0.2"
+edition = "2021"
 rust-version = "1.64"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
-edition = "2018"
+license = "MIT OR Apache-2.0"
 description = "Core structures for the ethers-rs crate"
 homepage = "https://docs.rs/ethers"
 repository = "https://github.com/gakonst/ethers-rs"
@@ -31,7 +31,7 @@ serde_json = { version = "1.0.64", default-features = false, features = ["arbitr
 thiserror = { version = "1.0", default-features = false }
 bytes = { version = "1.3.0", features = ["serde"] }
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
-once_cell = { version = "1.16.0", optional = true }
+once_cell = { version = "1.17.0", optional = true }
 unicode-xid = "0.2.4"
 strum = { version = "0.24", features = ["derive"] }
 
@@ -48,7 +48,7 @@ num_enum = "0.5.7"
 tempfile = { version = "3.3.0", default-features = false }
 serde_json = { version = "1.0.64", default-features = false }
 bincode = { version = "1.3.3", default-features = false }
-once_cell = { version = "1.16.0" }
+once_cell = { version = "1.17.0" }
 hex-literal = "0.3.4"
 rand = "0.8.5"
 
diff --git a/ethers-core/ethers-derive-eip712/Cargo.toml b/ethers-core/ethers-derive-eip712/Cargo.toml
index 305bfcfd..61073774 100644
--- a/ethers-core/ethers-derive-eip712/Cargo.toml
+++ b/ethers-core/ethers-derive-eip712/Cargo.toml
@@ -1,7 +1,8 @@
 [package]
 name = "ethers-derive-eip712"
 version = "1.0.2"
-edition = "2018"
+edition = "2021"
+rust-version = "1.64"
 description = "Custom derive macro for EIP-712 typed data"
 license = "MIT OR Apache-2.0"
 
@@ -11,7 +12,10 @@ proc-macro = true
 [dependencies]
 quote = "1.0.9"
 syn = "1.0.77"
-ethers-core = { version = "^1.0.0", path = "../", default-features = false, features = ["eip712", "macros"] }
+ethers-core = { version = "^1.0.0", path = "../", default-features = false, features = [
+    "eip712",
+    "macros",
+] }
 hex = "0.4.3"
 serde_json = "1.0.68"
 
diff --git a/ethers-core/src/macros/ethers_crate.rs b/ethers-core/src/macros/ethers_crate.rs
index 78bc38d8..5e984c23 100644
--- a/ethers-core/src/macros/ethers_crate.rs
+++ b/ethers-core/src/macros/ethers_crate.rs
@@ -1,123 +1,606 @@
 use cargo_metadata::MetadataCommand;
 use once_cell::sync::Lazy;
+use std::{
+    collections::HashMap,
+    env, fmt, fs,
+    path::{Path, PathBuf},
+};
+use strum::{EnumCount, EnumIter, EnumString, EnumVariantNames, IntoEnumIterator};
 
-use syn::Path;
+/// `ethers_crate => name`
+type CrateNames = HashMap<EthersCrate, &'static str>;
 
-/// See `determine_ethers_crates`
+const DIRS: [&str; 3] = ["benches", "examples", "tests"];
+
+/// Maps an [`EthersCrate`] to its path string.
+///
+/// See [`ProjectEnvironment`] for more information.
 ///
-/// This ensures that the `MetadataCommand` is only run once
-static ETHERS_CRATES: Lazy<(&'static str, &'static str, &'static str)> =
-    Lazy::new(determine_ethers_crates);
+/// Note: this static variable cannot hold [`syn::Path`] because it is not [`Sync`], so the names
+/// must be parsed at every call.
+static ETHERS_CRATE_NAMES: Lazy<CrateNames> = Lazy::new(|| {
+    ProjectEnvironment::new_from_env()
+        .and_then(|x| x.determine_ethers_crates())
+        .unwrap_or_else(|| EthersCrate::ethers_path_names().collect())
+});
 
-/// Convenience function to turn the `ethers_core` name in `ETHERS_CRATE` into a `Path`
-pub fn ethers_core_crate() -> Path {
-    syn::parse_str(ETHERS_CRATES.0).expect("valid path; qed")
+/// Returns the `core` crate's [`Path`][syn::Path].
+#[inline]
+pub fn ethers_core_crate() -> syn::Path {
+    get_crate_path(EthersCrate::EthersCore)
 }
-/// Convenience function to turn the `ethers_contract` name in `ETHERS_CRATE` into an `Path`
-pub fn ethers_contract_crate() -> Path {
-    syn::parse_str(ETHERS_CRATES.1).expect("valid path; qed")
+
+/// Returns the `contract` crate's [`Path`][syn::Path].
+#[inline]
+pub fn ethers_contract_crate() -> syn::Path {
+    get_crate_path(EthersCrate::EthersContract)
 }
-pub fn ethers_providers_crate() -> Path {
-    syn::parse_str(ETHERS_CRATES.2).expect("valid path; qed")
+
+/// Returns the `providers` crate's [`Path`][syn::Path].
+#[inline]
+pub fn ethers_providers_crate() -> syn::Path {
+    get_crate_path(EthersCrate::EthersProviders)
 }
 
-/// The crates name to use when deriving macros: (`core`, `contract`)
-///
-/// We try to determine which crate ident to use based on the dependencies of
-/// the project in which the macro is used. This is useful because the macros,
-/// like `EthEvent` are provided by the `ethers-contract` crate which depends on
-/// `ethers_core`. Most commonly `ethers` will be used as dependency which
-/// reexports all the different crates, essentially `ethers::core` is
-/// `ethers_core` So depending on the dependency used `ethers` ors `ethers_core
-/// | ethers_contract`, we need to use the fitting crate ident when expand the
-/// macros This will attempt to parse the current `Cargo.toml` and check the
-/// ethers related dependencies.
-///
-/// This determines
-///   - `ethers_*` idents if `ethers-core`, `ethers-contract`, `ethers-providers`  are present in
-///     the manifest or the `ethers` is _not_ present
-///   - `ethers::*` otherwise
-///
-/// This process is a bit hacky, we run `cargo metadata` internally which
-/// resolves the current package but creates a new `Cargo.lock` file in the
-/// process. This is not a problem for regular workspaces but becomes an issue
-/// during publishing with `cargo publish` if the project does not ignore
-/// `Cargo.lock` in `.gitignore`, because then cargo can't proceed with
-/// publishing the crate because the created `Cargo.lock` leads to a modified
-/// workspace, not the `CARGO_MANIFEST_DIR` but the workspace `cargo publish`
-/// created in `./target/package/..`. Therefore we check prior to executing
-/// `cargo metadata` if a `Cargo.lock` file exists and delete it afterwards if
-/// it was created by `cargo metadata`.
-pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
-    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR");
-
-    // if there is no cargo manifest, default to `ethers::`-style imports.
-    let manifest_dir = if let Ok(manifest_dir) = manifest_dir {
-        manifest_dir
-    } else {
-        return ("ethers::core", "ethers::contract", "ethers::providers")
-    };
+/// Returns an [`EthersCrate`]'s [`Path`][syn::Path] in the current project.
+#[inline(always)]
+pub fn get_crate_path(krate: EthersCrate) -> syn::Path {
+    krate.get_path()
+}
 
-    // check if the lock file exists, if it's missing we need to clean up afterward
-    let lock_file = format!("{manifest_dir}/Cargo.lock");
-    let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists();
-
-    let res = MetadataCommand::new()
-        .manifest_path(&format!("{manifest_dir}/Cargo.toml"))
-        .exec()
-        .ok()
-        .and_then(|metadata| {
-            metadata.root_package().and_then(|pkg| {
-                let sub_crates = Some(("ethers_core", "ethers_contract", "ethers_providers"));
-
-                // Note(mattsse): this is super hacky but required in order to compile and test
-                // ethers' internal crates
-                if [
-                    "ethers-contract",
-                    "ethers-derive-eip712",
-                    "ethers-signers",
-                    "ethers-middleware",
-                    "ethers-solc",
-                ]
-                .contains(&pkg.name.as_str())
-                {
-                    return sub_crates
-                }
+/// Represents a generic Rust/Cargo project's environment.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ProjectEnvironment {
+    manifest_dir: PathBuf,
+    crate_name: Option<String>,
+}
 
-                let mut has_ethers_core = false;
-                let mut has_ethers_contract = false;
-                let mut has_ethers_providers = false;
+impl ProjectEnvironment {
+    pub fn new<T: Into<PathBuf>, U: Into<String>>(manifest_dir: T, crate_name: U) -> Self {
+        Self { manifest_dir: manifest_dir.into(), crate_name: Some(crate_name.into()) }
+    }
 
-                for dep in pkg.dependencies.iter() {
-                    match dep.name.as_str() {
-                        "ethers-core" => {
-                            has_ethers_core = true;
-                        }
-                        "ethers-contract" => {
-                            has_ethers_contract = true;
-                        }
-                        "ethers-providers" => {
-                            has_ethers_providers = true;
+    pub fn new_from_env() -> Option<Self> {
+        Some(Self {
+            manifest_dir: env::var_os("CARGO_MANIFEST_DIR")?.into(),
+            crate_name: env::var("CARGO_CRATE_NAME").ok(),
+        })
+    }
+
+    /// Determines the crate paths to use by looking at the [metadata][cargo_metadata] of the
+    /// project.
+    ///
+    /// The names will be:
+    /// - `ethers::*` if `ethers` is a dependency for all crates;
+    /// - for each `crate`:
+    ///   - `ethers_<crate>` if it is a dependency, otherwise `ethers::<crate>`.
+    #[inline]
+    pub fn determine_ethers_crates(&self) -> Option<CrateNames> {
+        let lock_file = self.manifest_dir.join("Cargo.lock");
+        let lock_file_existed = lock_file.exists();
+
+        let names = self.crate_names_from_metadata();
+
+        // remove the lock file created from running the command
+        if !lock_file_existed && lock_file.exists() {
+            let _ = std::fs::remove_file(lock_file);
+        }
+
+        names
+    }
+
+    #[inline]
+    pub fn crate_names_from_metadata(&self) -> Option<CrateNames> {
+        let metadata = MetadataCommand::new().current_dir(&self.manifest_dir).exec().ok()?;
+        let pkg = metadata.root_package()?;
+
+        // return ethers_* if the root package is an internal ethers crate since `ethers` is not
+        // available
+        let crate_is_root = self.is_crate_root();
+        if let Ok(current_pkg) = pkg.name.parse::<EthersCrate>() {
+            // replace `current_pkg`'s name with "crate"
+            let names =
+                EthersCrate::path_names()
+                    .map(|(pkg, name)| {
+                        if crate_is_root && pkg == current_pkg {
+                            (pkg, "crate")
+                        } else {
+                            (pkg, name)
                         }
-                        "ethers" => return None,
-                        _ => {}
-                    }
-                }
+                    })
+                    .collect();
+            return Some(names)
+        } /* else if pkg.name == "ethers" {
+              // should not happen (the root package the `ethers` workspace package itself)
+          } */
 
-                if has_ethers_core && has_ethers_contract && has_ethers_providers {
-                    return sub_crates
+        let mut names: CrateNames = EthersCrate::ethers_path_names().collect();
+        for dep in pkg.dependencies.iter() {
+            let name = dep.name.as_str();
+            if name.starts_with("ethers") {
+                if name == "ethers" {
+                    return None
+                } else if let Ok(dep) = name.parse::<EthersCrate>() {
+                    names.insert(dep, dep.path_name());
                 }
+            }
+        }
+        Some(names)
+    }
 
-                None
-            })
+    /// Returns whether the `crate` path identifier refers to the root package.
+    ///
+    /// This is false for integration tests, benches, and examples, as the `crate` keyword will not
+    /// refer to the root package.
+    ///
+    /// We can find this using some [environment variables set by Cargo during compilation][ref]:
+    /// - `CARGO_TARGET_TMPDIR` is only set when building integration test or benchmark code;
+    /// - When `CARGO_MANIFEST_DIR` contains `/benches/` or `/examples/`
+    /// - `CARGO_CRATE_NAME`, see `is_crate_name_in_dirs`.
+    ///
+    /// [ref]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
+    #[inline]
+    pub fn is_crate_root(&self) -> bool {
+        env::var_os("CARGO_TARGET_TMPDIR").is_none() &&
+            self.manifest_dir.components().all(|c| {
+                let s = c.as_os_str();
+                s != "examples" && s != "benches"
+            }) &&
+            !self.is_crate_name_in_dirs()
+    }
+
+    /// Returns whether `crate_name` is the name of a file or directory in the first level of
+    /// `manifest_dir/{benches,examples,tests}/`.
+    ///
+    /// # Example
+    ///
+    /// With this project structure:
+    ///
+    /// ```text
+    /// .
+    /// ├── Cargo.lock
+    /// ├── Cargo.toml
+    /// ├── src/
+    /// │   ...
+    /// ├── benches/
+    /// │   ├── large-input.rs
+    /// │   └── multi-file-bench/
+    /// │       ├── main.rs
+    /// │       └── bench_module.rs
+    /// ├── examples/
+    /// │   ├── simple.rs
+    /// │   └── multi-file-example/
+    /// │       ├── main.rs
+    /// │       └── ex_module.rs
+    /// └── tests/
+    ///     ├── some-integration-tests.rs
+    ///     └── multi-file-test/
+    ///         ├── main.rs
+    ///         └── test_module.rs
+    /// ```
+    ///
+    /// The resulting `CARGO_CRATE_NAME` values will be:
+    ///
+    /// |                  Path                  |          Value         |
+    /// |:-------------------------------------- | ----------------------:|
+    /// | benches/large-input.rs                 |            large-input |
+    /// | benches/multi-file-bench/\*\*/\*.rs    |       multi-file-bench |
+    /// | examples/simple.rs                     |                 simple |
+    /// | examples/multi-file-example/\*\*/\*.rs |     multi-file-example |
+    /// | tests/some-integration-tests.rs        | some-integration-tests |
+    /// | tests/multi-file-test/\*\*/\*.rs       |        multi-file-test |
+    #[inline]
+    pub fn is_crate_name_in_dirs(&self) -> bool {
+        let crate_name = match self.crate_name.as_ref() {
+            Some(name) => name,
+            None => return false,
+        };
+        let dirs = DIRS.map(|dir| self.manifest_dir.join(dir));
+        dirs.iter().any(|dir| {
+            fs::read_dir(dir)
+                .ok()
+                .and_then(|entries| {
+                    entries
+                        .filter_map(Result::ok)
+                        .find(|entry| file_stem_eq(entry.path(), crate_name))
+                })
+                .is_some()
         })
-        .unwrap_or(("ethers::core", "ethers::contract", "ethers::providers"));
+    }
+}
+
+/// An `ethers-rs` internal crate.
+#[derive(
+    Clone,
+    Copy,
+    Debug,
+    PartialEq,
+    Eq,
+    PartialOrd,
+    Ord,
+    Hash,
+    EnumCount,
+    EnumIter,
+    EnumString,
+    EnumVariantNames,
+)]
+#[strum(serialize_all = "kebab-case")]
+pub enum EthersCrate {
+    EthersAddressbook,
+    EthersContract,
+    EthersContractAbigen,
+    EthersContractDerive,
+    EthersCore,
+    EthersDeriveEip712,
+    EthersEtherscan,
+    EthersMiddleware,
+    EthersProviders,
+    EthersSigners,
+    EthersSolc,
+}
+
+impl AsRef<str> for EthersCrate {
+    fn as_ref(&self) -> &str {
+        self.crate_name()
+    }
+}
+
+impl fmt::Display for EthersCrate {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.pad(self.as_ref())
+    }
+}
+
+impl EthersCrate {
+    /// "`<self as kebab-case>`"
+    #[inline]
+    pub const fn crate_name(self) -> &'static str {
+        match self {
+            Self::EthersAddressbook => "ethers-addressbook",
+            Self::EthersContract => "ethers-contract",
+            Self::EthersContractAbigen => "ethers-contract-abigen",
+            Self::EthersContractDerive => "ethers-contract-derive",
+            Self::EthersCore => "ethers-core",
+            Self::EthersDeriveEip712 => "ethers-derive-eip712",
+            Self::EthersEtherscan => "ethers-etherscan",
+            Self::EthersMiddleware => "ethers-middleware",
+            Self::EthersProviders => "ethers-providers",
+            Self::EthersSigners => "ethers-signers",
+            Self::EthersSolc => "ethers-solc",
+        }
+    }
+
+    /// "`::<self as snake_case>`"
+    #[inline]
+    pub const fn path_name(self) -> &'static str {
+        match self {
+            Self::EthersAddressbook => "::ethers_addressbook",
+            Self::EthersContract => "::ethers_contract",
+            Self::EthersContractAbigen => "::ethers_contract_abigen",
+            Self::EthersContractDerive => "::ethers_contract_derive",
+            Self::EthersCore => "::ethers_core",
+            Self::EthersDeriveEip712 => "::ethers_derive_eip712",
+            Self::EthersEtherscan => "::ethers_etherscan",
+            Self::EthersMiddleware => "::ethers_middleware",
+            Self::EthersProviders => "::ethers_providers",
+            Self::EthersSigners => "::ethers_signers",
+            Self::EthersSolc => "::ethers_solc",
+        }
+    }
+
+    /// "::ethers::`<self in ethers>`"
+    #[inline]
+    pub const fn ethers_path_name(self) -> &'static str {
+        match self {
+            // re-exported in ethers::contract
+            Self::EthersContractAbigen => "::ethers::contract", // partly
+            Self::EthersContractDerive => "::ethers::contract",
+            Self::EthersDeriveEip712 => "::ethers::contract",
+
+            Self::EthersAddressbook => "::ethers::addressbook",
+            Self::EthersContract => "::ethers::contract",
+            Self::EthersCore => "::ethers::core",
+            Self::EthersEtherscan => "::ethers::etherscan",
+            Self::EthersMiddleware => "::ethers::middleware",
+            Self::EthersProviders => "::ethers::providers",
+            Self::EthersSigners => "::ethers::signers",
+            Self::EthersSolc => "::ethers::solc",
+        }
+    }
+
+    /// The path on the file system, from an `ethers-rs` root directory.
+    #[inline]
+    pub const fn fs_path(self) -> &'static str {
+        match self {
+            Self::EthersContractAbigen => "ethers-contract/ethers-contract-abigen",
+            Self::EthersContractDerive => "ethers-contract/ethers-contract-derive",
+            Self::EthersDeriveEip712 => "ethers-core/ethers-derive-eip712",
+            _ => self.crate_name(),
+        }
+    }
+
+    /// `<ethers_*>`
+    #[inline]
+    pub fn path_names() -> impl Iterator<Item = (Self, &'static str)> {
+        Self::iter().map(|x| (x, x.path_name()))
+    }
+
+    /// `<ethers::*>`
+    #[inline]
+    pub fn ethers_path_names() -> impl Iterator<Item = (Self, &'static str)> {
+        Self::iter().map(|x| (x, x.ethers_path_name()))
+    }
+
+    /// Returns the [`Path`][syn::Path] in the current project.
+    #[inline]
+    pub fn get_path(&self) -> syn::Path {
+        let name = ETHERS_CRATE_NAMES[self];
+        syn::parse_str(name).unwrap()
+    }
+}
+
+/// `path.file_stem() == s`
+#[inline]
+fn file_stem_eq<T: AsRef<Path>, U: AsRef<str>>(path: T, s: U) -> bool {
+    if let Some(stem) = path.as_ref().file_stem() {
+        if let Some(stem) = stem.to_str() {
+            return stem == s.as_ref()
+        }
+    }
+    false
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use rand::{
+        distributions::{Distribution, Standard},
+        thread_rng, Rng,
+    };
+    use std::{
+        collections::{BTreeMap, HashSet},
+        env, fs,
+    };
+    use tempfile::TempDir;
+
+    impl Distribution<EthersCrate> for Standard {
+        fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EthersCrate {
+            const RANGE: std::ops::Range<u8> = 0..EthersCrate::COUNT as u8;
+            // SAFETY: generates in the safe range
+            unsafe { std::mem::transmute(rng.gen_range(RANGE)) }
+        }
+    }
+
+    #[test]
+    fn test_names() {
+        fn assert_names(s: &ProjectEnvironment, ethers: bool, dependencies: &[EthersCrate]) {
+            write_manifest(s, ethers, dependencies);
+
+            // speeds up consecutive runs by not having to re-create and delete the lockfile
+            // this is tested separately: test_lock_file
+            std::fs::write(s.manifest_dir.join("Cargo.lock"), "").unwrap();
+
+            let names = s
+                .determine_ethers_crates()
+                .unwrap_or_else(|| EthersCrate::ethers_path_names().collect());
+
+            let krate = s.crate_name.as_ref().and_then(|x| x.parse::<EthersCrate>().ok());
+            let is_internal = krate.is_some();
+            let mut expected: CrateNames = match (is_internal, ethers) {
+                // internal
+                (true, _) => EthersCrate::path_names().collect(),
+
+                // ethers
+                (_, true) => EthersCrate::ethers_path_names().collect(),
+
+                // no ethers
+                (_, false) => {
+                    let mut n: CrateNames = EthersCrate::ethers_path_names().collect();
+                    for &dep in dependencies {
+                        n.insert(dep, dep.path_name());
+                    }
+                    n
+                }
+            };
+
+            if is_internal {
+                expected.insert(krate.unwrap(), "crate");
+            }
+
+            // don't use assert for a better custom message
+            if names != expected {
+                // BTreeMap sorts the keys
+                let names: BTreeMap<_, _> = names.into_iter().collect();
+                let expected: BTreeMap<_, _> = expected.into_iter().collect();
+                panic!("\nCase failed: (`{:?}`, `{ethers}`, `{dependencies:?}`)\nNames: {names:#?}\nExpected: {expected:#?}\n", s.crate_name);
+            }
+        }
+
+        fn gen_unique<const N: usize>() -> [EthersCrate; N] {
+            assert!(N < EthersCrate::COUNT);
+            let rng = &mut thread_rng();
+            let mut set = HashSet::with_capacity(N);
+            while set.len() < N {
+                set.insert(rng.gen());
+            }
+            let vec: Vec<_> = set.into_iter().collect();
+            vec.try_into().unwrap()
+        }
+
+        let (s, _dir) = test_project();
+        // crate_name        -> represents an external crate
+        // "ethers-contract" -> represents an internal crate
+        for name in [s.crate_name.as_ref().unwrap(), "ethers-contract"] {
+            let s = ProjectEnvironment::new(&s.manifest_dir, name);
+            // only ethers
+            assert_names(&s, true, &[]);
+
+            // only others
+            assert_names(&s, false, gen_unique::<3>().as_slice());
+
+            // ethers and others
+            assert_names(&s, true, gen_unique::<3>().as_slice());
+        }
+    }
+
+    #[test]
+    fn test_lock_file() {
+        let (s, _dir) = test_project();
+        write_manifest(&s, true, &[]);
+        let lock_file = s.manifest_dir.join("Cargo.lock");
 
-    if needs_lock_file_cleanup {
-        // delete the `Cargo.lock` file that was created by `cargo metadata`
-        // if the package is not part of a workspace
-        let _ = std::fs::remove_file(lock_file);
+        assert!(!lock_file.exists());
+        s.determine_ethers_crates();
+        assert!(!lock_file.exists());
+
+        std::fs::write(&lock_file, "").unwrap();
+
+        assert!(lock_file.exists());
+        s.determine_ethers_crates();
+        assert!(lock_file.exists());
+        assert!(!std::fs::read(lock_file).unwrap().is_empty());
+    }
+
+    #[test]
+    fn test_is_crate_root() {
+        let (s, _dir) = test_project();
+        assert!(s.is_crate_root());
+
+        // `CARGO_MANIFEST_DIR`
+        // complex path has `/{dir_name}/` in the path
+        // name or path validity not checked
+        let s = ProjectEnvironment::new(
+            s.manifest_dir.join("examples/complex_examples"),
+            "complex-examples",
+        );
+        assert!(!s.is_crate_root());
+        let s = ProjectEnvironment::new(
+            s.manifest_dir.join("benches/complex_benches"),
+            "complex-benches",
+        );
+        assert!(!s.is_crate_root());
+    }
+
+    #[test]
+    fn test_is_crate_name_in_dirs() {
+        let (s, _dir) = test_project();
+        let root = &s.manifest_dir;
+
+        for dir_name in DIRS {
+            for ty in ["simple", "complex"] {
+                let s = ProjectEnvironment::new(root, format!("{ty}_{dir_name}"));
+                assert!(s.is_crate_name_in_dirs(), "{s:?}");
+            }
+        }
+
+        let s = ProjectEnvironment::new(root, "non_existant");
+        assert!(!s.is_crate_name_in_dirs());
+        let s = ProjectEnvironment::new(root.join("does-not-exist"), "foo_bar");
+        assert!(!s.is_crate_name_in_dirs());
+    }
+
+    #[test]
+    fn test_file_stem_eq() {
+        let path = Path::new("/tmp/foo.rs");
+        assert!(file_stem_eq(path, "foo"));
+        assert!(!file_stem_eq(path, "tmp"));
+        assert!(!file_stem_eq(path, "foo.rs"));
+        assert!(!file_stem_eq(path, "fo"));
+        assert!(!file_stem_eq(path, "f"));
+        assert!(!file_stem_eq(path, ""));
+
+        let path = Path::new("/tmp/foo/");
+        assert!(file_stem_eq(path, "foo"));
+        assert!(!file_stem_eq(path, "tmp"));
+        assert!(!file_stem_eq(path, "fo"));
+        assert!(!file_stem_eq(path, "f"));
+        assert!(!file_stem_eq(path, ""));
+    }
+
+    // utils
+
+    /// Creates:
+    ///
+    /// ```text
+    /// - new_dir
+    ///   - src
+    ///     - main.rs
+    ///   - {dir_name} for dir_name in DIRS
+    ///     - simple_{dir_name}.rs
+    ///     - complex_{dir_name}
+    ///       - src if not "tests"
+    ///         - main.rs
+    ///         - module.rs
+    /// ```
+    fn test_project() -> (ProjectEnvironment, TempDir) {
+        // change the prefix to one without the default `.` because it is not a valid crate name
+        let dir = tempfile::Builder::new().prefix("tmp").tempdir().unwrap();
+        let root = dir.path();
+        let name = root.file_name().unwrap().to_str().unwrap();
+
+        // No Cargo.toml, git
+        fs::create_dir_all(root).unwrap();
+        let src = root.join("src");
+        fs::create_dir(&src).unwrap();
+        fs::write(src.join("main.rs"), "fn main(){}").unwrap();
+
+        for dir_name in DIRS {
+            let new_dir = root.join(dir_name);
+            fs::create_dir(&new_dir).unwrap();
+
+            let simple = new_dir.join(format!("simple_{dir_name}.rs"));
+            fs::write(simple, "").unwrap();
+
+            let mut complex = new_dir.join(format!("complex_{dir_name}"));
+            if dir_name != "tests" {
+                fs::create_dir(&complex).unwrap();
+                fs::write(complex.join("Cargo.toml"), "").unwrap();
+                complex.push("src");
+            }
+            fs::create_dir(&complex).unwrap();
+            fs::write(complex.join("main.rs"), "").unwrap();
+            fs::write(complex.join("module.rs"), "").unwrap();
+        }
+
+        // create target dirs
+        let target = root.join("target");
+        fs::create_dir(&target).unwrap();
+        fs::create_dir_all(target.join("tmp")).unwrap();
+
+        (ProjectEnvironment::new(root, name), dir)
     }
 
-    res
+    /// Writes a test manifest to `{root}/Cargo.toml`.
+    fn write_manifest(s: &ProjectEnvironment, ethers: bool, dependencies: &[EthersCrate]) {
+        // use paths to avoid downloading dependencies
+        const ETHERS_CORE: &str = env!("CARGO_MANIFEST_DIR");
+        let ethers_root = Path::new(ETHERS_CORE).parent().unwrap();
+        let mut dependencies_toml =
+            String::with_capacity(150 * (ethers as usize + dependencies.len()));
+
+        if ethers {
+            let ethers = format!("ethers = {{ path = {ethers_root:?} }}\n");
+            dependencies_toml.push_str(&ethers);
+        }
+
+        for dep in dependencies.iter() {
+            let path = ethers_root.join(dep.fs_path());
+            let dep = format!("{dep} = {{ path = {path:?} }}\n");
+            dependencies_toml.push_str(&dep);
+        }
+
+        let contents = format!(
+            r#"
+[package]
+name = "{}"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+{dependencies_toml}
+"#,
+            s.crate_name.as_ref().unwrap()
+        );
+        fs::write(s.manifest_dir.join("Cargo.toml"), contents).unwrap();
+    }
 }
diff --git a/ethers-core/src/macros/mod.rs b/ethers-core/src/macros/mod.rs
index 6a857b11..456f27bd 100644
--- a/ethers-core/src/macros/mod.rs
+++ b/ethers-core/src/macros/mod.rs
@@ -1,2 +1,2 @@
 mod ethers_crate;
-pub use ethers_crate::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
+pub use ethers_crate::*;
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 5a2e1315..ff5ca22a 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -12,7 +12,7 @@ pub use geth::{Geth, GethInstance};
 
 /// Utilities for working with a `genesis.json` and other chain config structs.
 mod genesis;
-pub use genesis::{ChainConfig, Genesis};
+pub use genesis::{ChainConfig, CliqueConfig, EthashConfig, Genesis, GenesisAccount};
 
 /// Utilities for launching an anvil instance
 #[cfg(not(target_arch = "wasm32"))]
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index abea9a1e..8de6d5ac 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -1,13 +1,13 @@
 [package]
 name = "ethers-etherscan"
 version = "1.0.2"
+edition = "2021"
 rust-version = "1.64"
 authors = [
     "Matthias Seitz <matthias.seitz@outlook.de>",
     "Georgios Konstantopoulos <me@gakonst.com>",
 ]
 license = "MIT OR Apache-2.0"
-edition = "2018"
 readme = "../README.md"
 documentation = "https://docs.rs/ethers"
 repository = "https://github.com/gakonst/ethers-rs"
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index 2518a21d..08c79162 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-middleware"
 version = "1.0.2"
-edition = "2018"
+edition = "2021"
 rust-version = "1.64"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 license = "MIT OR Apache-2.0"
@@ -47,7 +47,7 @@ ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-f
     "ws",
     "rustls",
 ] }
-once_cell = "1.16.0"
+once_cell = "1.17.0"
 ethers-solc = { version = "^1.0.0", path = "../ethers-solc" }
 serial_test = "0.10.0"
 reqwest = { version = "0.11.13", default-features = false, features = ["json", "rustls"] }
diff --git a/ethers-middleware/src/gas_oracle/mod.rs b/ethers-middleware/src/gas_oracle/mod.rs
index 9be17750..40c1bf42 100644
--- a/ethers-middleware/src/gas_oracle/mod.rs
+++ b/ethers-middleware/src/gas_oracle/mod.rs
@@ -37,7 +37,7 @@ use std::{error::Error, fmt::Debug};
 use thiserror::Error;
 
 pub(crate) const GWEI_TO_WEI: u64 = 1_000_000_000;
-pub(crate) const GWEI_TO_WEI_U256: U256 = U256([0, 0, 0, GWEI_TO_WEI]);
+pub(crate) const GWEI_TO_WEI_U256: U256 = U256([GWEI_TO_WEI, 0, 0, 0]);
 
 pub type Result<T, E = GasOracleError> = std::result::Result<T, E>;
 
@@ -153,3 +153,15 @@ pub trait GasOracle: Send + Sync + Debug {
 pub(crate) fn from_gwei_f64(gwei: f64) -> U256 {
     ethers_core::types::u256_from_f64_saturating(gwei) * GWEI_TO_WEI_U256
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_gwei_wei_constants() {
+        let as_u256: U256 = GWEI_TO_WEI.into();
+        assert_eq!(as_u256, GWEI_TO_WEI_U256);
+        assert_eq!(GWEI_TO_WEI_U256.as_u64(), GWEI_TO_WEI);
+    }
+}
diff --git a/ethers-middleware/src/gas_oracle/polygon.rs b/ethers-middleware/src/gas_oracle/polygon.rs
index 5fd63545..f7acf85a 100644
--- a/ethers-middleware/src/gas_oracle/polygon.rs
+++ b/ethers-middleware/src/gas_oracle/polygon.rs
@@ -20,7 +20,7 @@ pub struct Polygon {
 
 /// The response from the Polygon gas station API.
 ///
-/// Gas prices are in Gwei.
+/// Gas prices are in __Gwei__.
 #[derive(Debug, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 pub struct Response {
@@ -103,3 +103,14 @@ impl Polygon {
         Ok(response)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn parse_polygon_gas_station_response() {
+        let s = r#"{"safeLow":{"maxPriorityFee":2.1267086610666666,"maxFee":2.1267086760666665},"standard":{"maxPriorityFee":2.3482958369333335,"maxFee":2.3482958519333335},"fast":{"maxPriorityFee":2.793454819,"maxFee":2.793454834},"estimatedBaseFee":1.5e-8,"blockTime":2,"blockNumber":30328888}"#;
+        let _resp: Response = serde_json::from_str(s).unwrap();
+    }
+}
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index d11f4f81..e3117d68 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-providers"
 version = "1.0.2"
-edition = "2018"
+edition = "2021"
 rust-version = "1.64"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 license = "MIT OR Apache-2.0"
@@ -43,7 +43,7 @@ tracing = { version = "0.1.37", default-features = false }
 tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] }
 
 bytes = { version = "1.3.0", default-features = false, optional = true }
-once_cell = "1.16.0"
+once_cell = "1.17.0"
 hashers = "1.0.1"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs
index b7825682..31ef49fe 100644
--- a/ethers-providers/src/lib.rs
+++ b/ethers-providers/src/lib.rs
@@ -723,7 +723,8 @@ pub trait CeloMiddleware: Middleware {
     }
 }
 
-pub use test_provider::{GOERLI, MAINNET, ROPSTEN};
+#[allow(deprecated)]
+pub use test_provider::{GOERLI, MAINNET, ROPSTEN, SEPOLIA};
 
 /// Pre-instantiated Infura HTTP clients which rotate through multiple API keys
 /// to prevent rate limits
@@ -743,9 +744,13 @@ pub mod test_provider {
         "5c812e02193c4ba793f8c214317582bd",
     ];
 
-    pub static GOERLI: Lazy<TestProvider> = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli"));
     pub static MAINNET: Lazy<TestProvider> =
         Lazy::new(|| TestProvider::new(INFURA_KEYS, "mainnet"));
+    pub static GOERLI: Lazy<TestProvider> = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli"));
+    pub static SEPOLIA: Lazy<TestProvider> =
+        Lazy::new(|| TestProvider::new(INFURA_KEYS, "sepolia"));
+
+    #[deprecated = "Ropsten testnet has been deprecated in favor of Goerli or Sepolia."]
     pub static ROPSTEN: Lazy<TestProvider> =
         Lazy::new(|| TestProvider::new(INFURA_KEYS, "ropsten"));
 
@@ -756,16 +761,14 @@ pub mod test_provider {
     }
 
     impl TestProvider {
-        pub fn new(keys: &'static [&'static str], network: &str) -> Self {
-            Self { keys: Mutex::new(keys.iter().cycle()), network: network.to_owned() }
+        pub fn new(keys: &'static [&'static str], network: impl Into<String>) -> Self {
+            Self { keys: keys.iter().cycle().into(), network: network.into() }
         }
 
         pub fn url(&self) -> String {
-            format!(
-                "https://{}.infura.io/v3/{}",
-                self.network,
-                self.keys.lock().unwrap().next().unwrap()
-            )
+            let Self { network, keys } = self;
+            let key = keys.lock().unwrap().next().unwrap();
+            format!("https://{network}.infura.io/v3/{key}")
         }
 
         pub fn provider(&self) -> Provider<Http> {
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index cde771cc..97d5a995 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -352,8 +352,19 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
                 if inner.max_fee_per_gas.is_none() || inner.max_priority_fee_per_gas.is_none() {
                     let (max_fee_per_gas, max_priority_fee_per_gas) =
                         self.estimate_eip1559_fees(None).await?;
-                    inner.max_fee_per_gas = Some(max_fee_per_gas);
-                    inner.max_priority_fee_per_gas = Some(max_priority_fee_per_gas);
+                    // we want to avoid overriding the user if either of these
+                    // are set. In order to do this, we refuse to override the
+                    // `max_fee_per_gas` if already set.
+                    // However, we must preserve the constraint that the tip
+                    // cannot be higher than max fee, so we override user
+                    // intent if that is so. We override by
+                    //   - first: if set, set to the min(current value, MFPG)
+                    //   - second, if still unset, use the RPC estimated amount
+                    let mfpg = inner.max_fee_per_gas.get_or_insert(max_fee_per_gas);
+                    inner
+                        .max_priority_fee_per_gas
+                        .map(|tip| std::cmp::min(tip, *mfpg))
+                        .get_or_insert(max_priority_fee_per_gas);
                 };
             }
         }
diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml
index 02c2bab1..8a25e34f 100644
--- a/ethers-signers/Cargo.toml
+++ b/ethers-signers/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-signers"
 version = "1.0.2"
-edition = "2018"
+edition = "2021"
 rust-version = "1.64"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 license = "MIT OR Apache-2.0"
diff --git a/ethers-signers/src/aws/mod.rs b/ethers-signers/src/aws/mod.rs
index cb5246f2..0e0ed7a2 100644
--- a/ethers-signers/src/aws/mod.rs
+++ b/ethers-signers/src/aws/mod.rs
@@ -243,7 +243,7 @@ impl super::Signer for AwsSigner {
         let chain_id = tx_with_chain.chain_id().map(|id| id.as_u64()).unwrap_or(self.chain_id);
         tx_with_chain.set_chain_id(chain_id);
 
-        let sighash = tx.sighash();
+        let sighash = tx_with_chain.sighash();
         self.sign_digest_with_eip155(sighash, chain_id).await
     }
 
diff --git a/ethers-signers/src/wallet/private_key.rs b/ethers-signers/src/wallet/private_key.rs
index cdddf00a..ed10949c 100644
--- a/ethers-signers/src/wallet/private_key.rs
+++ b/ethers-signers/src/wallet/private_key.rs
@@ -90,6 +90,13 @@ impl Wallet<SigningKey> {
         let address = secret_key_to_address(&signer);
         Self { signer, address, chain_id: 1 }
     }
+
+    /// Creates a new Wallet instance from a raw scalar value (big endian).
+    pub fn from_bytes(bytes: &[u8]) -> Result<Self, WalletError> {
+        let signer = SigningKey::from_bytes(bytes)?;
+        let address = secret_key_to_address(&signer);
+        Ok(Self { signer, address, chain_id: 1 })
+    }
 }
 
 impl PartialEq for Wallet<SigningKey> {
@@ -305,4 +312,17 @@ mod tests {
             Address::from_str("6813Eb9362372EEF6200f3b1dbC3f819671cBA69").expect("Decoding failed")
         );
     }
+
+    #[test]
+    fn key_from_bytes() {
+        let wallet: Wallet<SigningKey> =
+            "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap();
+
+        let key_as_bytes = wallet.signer.to_bytes();
+        let wallet_from_bytes = Wallet::from_bytes(&key_as_bytes).unwrap();
+
+        assert_eq!(wallet.address, wallet_from_bytes.address);
+        assert_eq!(wallet.chain_id, wallet_from_bytes.chain_id);
+        assert_eq!(wallet.signer, wallet_from_bytes.signer);
+    }
 }
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 66002fd5..3555e1b4 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-solc"
 version = "1.0.2"
-edition = "2018"
+edition = "2021"
 rust-version = "1.64"
 authors = [
     "Matthias Seitz <matthias.seitz@outlook.de>",
@@ -23,7 +23,7 @@ semver = { version = "1.0.16", features = ["serde"] }
 walkdir = "2.3.2"
 tokio = { version = "1.18", default-features = false, features = ["rt"] }
 futures-util = { version = "^0.3", optional = true }
-once_cell = "1.16.0"
+once_cell = "1.17.0"
 regex = "1.7.0"
 md-5 = "0.10.5"
 thiserror = "1.0"
diff --git a/ethers-solc/benches/compile_many.rs b/ethers-solc/benches/compile_many.rs
index b667a6b9..b0df3c7a 100644
--- a/ethers-solc/benches/compile_many.rs
+++ b/ethers-solc/benches/compile_many.rs
@@ -36,7 +36,6 @@ fn load_compiler_inputs() -> Vec<CompilerInput> {
     let mut inputs = Vec::new();
     for file in std::fs::read_dir(Path::new(&env!("CARGO_MANIFEST_DIR")).join("test-data/in"))
         .unwrap()
-        .into_iter()
         .take(5)
     {
         let file = file.unwrap();
diff --git a/ethers-solc/src/artifacts/ast/mod.rs b/ethers-solc/src/artifacts/ast/mod.rs
index 82e01a2b..be83cd00 100644
--- a/ethers-solc/src/artifacts/ast/mod.rs
+++ b/ethers-solc/src/artifacts/ast/mod.rs
@@ -1089,11 +1089,11 @@ mod tests {
                 let result: Result<SourceUnit, _> = serde_path_to_error::deserialize(deserializer);
                 match result {
                     Err(e) => {
-                        println!("... {} fail: {e}", path_str);
+                        println!("... {path_str} fail: {e}");
                         panic!();
                     }
                     Ok(_) => {
-                        println!("... {} ok", path_str);
+                        println!("... {path_str} ok");
                     }
                 }
             })
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index fe40cfb9..b34913fe 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -457,7 +457,7 @@ impl Solc {
 
         use sha2::Digest;
         let mut hasher = sha2::Sha256::new();
-        hasher.update(&content);
+        hasher.update(content);
         let checksum_calc = &hasher.finalize()[..];
 
         let checksum_found = &RELEASES.0.get_checksum(&version).expect("checksum not found");
diff --git a/ethers-solc/src/error.rs b/ethers-solc/src/error.rs
index 72dbc95e..0b77fde3 100644
--- a/ethers-solc/src/error.rs
+++ b/ethers-solc/src/error.rs
@@ -27,18 +27,19 @@ pub enum SolcError {
     /// Filesystem IO error
     #[error(transparent)]
     Io(#[from] SolcIoError),
+    #[error("File could not be resolved due to broken symlink: {0}.")]
+    ResolveBadSymlink(SolcIoError),
     /// Failed to resolve a file
     #[error("Failed to resolve file: {0}.\n Check configured remappings.")]
     Resolve(SolcIoError),
-    #[error("File could not be resolved due to broken symlink: {0}.")]
-    ResolveBadSymlink(SolcIoError),
+    #[error("File cannot be resolved due to mismatch of file name case: {error}.\n Found existing file: {existing_file:?}\n Please check the case of the import.")]
+    ResolveCaseSensitiveFileName { error: SolcIoError, existing_file: PathBuf },
     #[error(
-        r#"Failed to resolve file: {0}.
+        r#"{0}.
     --> {1:?}
-        {2:?}
-    Check configured remappings."#
+        {2:?}"#
     )]
-    FailedResolveImport(SolcIoError, PathBuf, PathBuf),
+    FailedResolveImport(Box<SolcError>, PathBuf, PathBuf),
     #[cfg(feature = "svm-solc")]
     #[error(transparent)]
     SvmError(#[from] svm::SolcVmError),
diff --git a/ethers-solc/src/resolver/mod.rs b/ethers-solc/src/resolver/mod.rs
index eceba2e1..732b5d1a 100644
--- a/ethers-solc/src/resolver/mod.rs
+++ b/ethers-solc/src/resolver/mod.rs
@@ -59,6 +59,7 @@ use std::{
 mod parse;
 mod tree;
 
+use crate::utils::find_case_sensitive_existing_file;
 pub use parse::SolImportAlias;
 pub use tree::{print, Charset, TreeOptions};
 
@@ -371,10 +372,12 @@ impl Graph {
                         add_node(&mut unresolved, &mut index, &mut resolved_imports, import)
                             .map_err(|err| {
                                 match err {
-                                    SolcError::Resolve(err) => {
-                                        // make the error more verbose
+                                    err @ SolcError::ResolveCaseSensitiveFileName { .. } |
+                                    err @ SolcError::Resolve(_) => {
+                                        // make the error more helpful by providing additional
+                                        // context
                                         SolcError::FailedResolveImport(
-                                            err,
+                                            Box::new(err),
                                             node.path.clone(),
                                             import_path.clone(),
                                         )
@@ -649,7 +652,7 @@ impl Graph {
                 return Vec::new()
             }
 
-            let mut result = sets.pop().cloned().expect("not empty; qed.").clone();
+            let mut result = sets.pop().cloned().expect("not empty; qed.");
             if !sets.is_empty() {
                 result.retain(|item| sets.iter().all(|set| set.contains(item)));
             }
@@ -832,10 +835,21 @@ impl Node {
     pub fn read(file: impl AsRef<Path>) -> Result<Self> {
         let file = file.as_ref();
         let source = Source::read(file).map_err(|err| {
-            if !err.path().exists() && err.path().is_symlink() {
+            let exists = err.path().exists();
+            if !exists && err.path().is_symlink() {
                 SolcError::ResolveBadSymlink(err)
             } else {
-                SolcError::Resolve(err)
+                // This is an additional check useful on OS that have case-sensitive paths, See also <https://docs.soliditylang.org/en/v0.8.17/path-resolution.html#import-callback>
+                if !exists {
+                    // check if there exists a file with different case
+                    if let Some(existing_file) = find_case_sensitive_existing_file(file) {
+                        SolcError::ResolveCaseSensitiveFileName { error: err, existing_file }
+                    } else {
+                        SolcError::Resolve(err)
+                    }
+                } else {
+                    SolcError::Resolve(err)
+                }
             }
         })?;
         let data = SolData::parse(source.as_ref(), file);
diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs
index 71c10c37..bb4d0a3b 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -385,6 +385,26 @@ pub(crate) fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &st
     p
 }
 
+/// Attempts to find a file with different case that exists next to the `non_existing` file
+pub(crate) fn find_case_sensitive_existing_file(non_existing: &Path) -> Option<PathBuf> {
+    let non_existing_file_name = non_existing.file_name()?;
+    let parent = non_existing.parent()?;
+    WalkDir::new(parent)
+        .max_depth(1)
+        .into_iter()
+        .filter_map(Result::ok)
+        .filter(|e| e.file_type().is_file())
+        .find_map(|e| {
+            let existing_file_name = e.path().file_name()?;
+            if existing_file_name.eq_ignore_ascii_case(non_existing_file_name) &&
+                existing_file_name != non_existing_file_name
+            {
+                return Some(e.path().to_path_buf())
+            }
+            None
+        })
+}
+
 #[cfg(not(target_arch = "wasm32"))]
 use tokio::runtime::{Handle, Runtime};
 
@@ -453,6 +473,7 @@ pub fn create_parent_dir_all(file: impl AsRef<Path>) -> Result<(), SolcError> {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::resolver::Node;
     use solang_parser::pt::SourceUnitPart;
     use std::{
         collections::HashSet,
@@ -460,6 +481,45 @@ mod tests {
     };
     use tempdir;
 
+    #[test]
+    fn can_find_different_case() {
+        let tmp_dir = tempdir("out").unwrap();
+        let path = tmp_dir.path().join("forge-std");
+        create_dir_all(&path).unwrap();
+        let existing = path.join("Test.sol");
+        let non_existing = path.join("test.sol");
+        std::fs::write(&existing, b"").unwrap();
+
+        #[cfg(target_os = "linux")]
+        assert!(!non_existing.exists());
+
+        let found = find_case_sensitive_existing_file(&non_existing).unwrap();
+        assert_eq!(found, existing);
+    }
+
+    #[cfg(target_os = "linux")]
+    #[test]
+    fn can_read_different_case() {
+        let tmp_dir = tempdir("out").unwrap();
+        let path = tmp_dir.path().join("forge-std");
+        create_dir_all(&path).unwrap();
+        let existing = path.join("Test.sol");
+        let non_existing = path.join("test.sol");
+        std::fs::write(
+            &existing,
+            r#"
+pragma solidity ^0.8.10;
+contract A {}
+        "#,
+        )
+        .unwrap();
+
+        assert!(!non_existing.exists());
+
+        let found = Node::read(&non_existing).unwrap_err();
+        matches!(found, SolcError::ResolveCaseSensitiveFileName { .. });
+    }
+
     #[test]
     fn can_create_parent_dirs_with_ext() {
         let tmp_dir = tempdir("out").unwrap();
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 00000000..87fbfc19
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,81 @@
+# Examples
+- [ ] Address book
+- [ ] Anvil
+    - [ ] Boot anvil
+    - [ ] Deploy contracts
+    - [x] Fork
+    - [ ] Testing
+- [x] Big numbers
+    - [x] Comparison and equivalence
+    - [x] Conversion
+    - [x] Creating Instances
+    - [x] Math operations
+    - [x] Utilities
+- [ ] Contracts
+    - [x] Abigen
+    - [x] Compile
+    - [ ] Creating Instances
+    - [x] Deploy Anvil
+    - [x] Deploy from ABI and bytecode
+    - [x] Deploy Moonbeam
+    - [x] Events
+    - [x] Events with meta
+    - [ ] Methods
+- [ ] Events
+  - [ ] Logs and filtering
+  - [ ] Solidity topics
+- [ ] Middleware
+  - [x] Builder
+  - [x] Create custom middleware
+  - [x] Gas escalator
+  - [x] Gas oracle
+  - [x] Nonce manager
+  - [x] Policy
+  - [x] Signer
+  - [ ] Time lag
+  - [ ] Transformer
+- [ ] Providers
+  - [ ] Http
+  - [x] IPC
+  - [ ] Mock 
+  - [x] Quorum
+  - [ ] Retry
+  - [x] RW
+  - [ ] WS
+- [ ] Queries
+  - [ ] Blocks
+  - [x] Contracts
+  - [ ] Events
+  - [x] Paginated logs
+  - [x] UniswapV2 pair
+  - [ ] Transactions
+- [x] Subscriptions
+  - [x] Watch blocks
+  - [x] Subscribe events by type
+  - [x] Subscribe logs
+- [ ] Transactions
+  - [x] Call override
+  - [ ] Create raw transaction
+  - [ ] Create typed transaction
+  - [x] Decode input
+  - [ ] EIP-1559
+  - [x] ENS
+  - [ ] Estimate gas
+  - [ ] Get gas price
+  - [x] Get gas price USD
+  - [x] Remove liquidity
+  - [ ] Set gas for a transaction
+  - [ ] Send raw transaction
+  - [ ] Send typed transaction
+  - [x] Trace
+  - [ ] Transaction receipt
+  - [ ] Transaction status
+  - [x] Transfer ETH
+- [x] Wallets
+  - [x] Mnemonic
+  - [x] Ledger
+  - [x] Local
+  - [x] Permit hash
+  - [x] Sign message
+  - [x] Trezor
+  - [x] Yubi
diff --git a/examples/anvil/Cargo.toml b/examples/anvil/Cargo.toml
new file mode 100644
index 00000000..7e1fb044
--- /dev/null
+++ b/examples/anvil/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "examples-anvil"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0" }
+eyre = "0.6"
+tokio = { version = "1.18", features = ["full"] }
diff --git a/examples/anvil/README.md b/examples/anvil/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/anvil_fork.rs b/examples/anvil/examples/anvil_fork.rs
similarity index 88%
rename from examples/anvil_fork.rs
rename to examples/anvil/examples/anvil_fork.rs
index 31da8670..4fecb8e8 100644
--- a/examples/anvil_fork.rs
+++ b/examples/anvil/examples/anvil_fork.rs
@@ -1,9 +1,10 @@
 //! Spawn an [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) instance in forking mode
 
 use ethers::utils::Anvil;
+use eyre::Result;
 
 #[tokio::main]
-async fn main() -> eyre::Result<()> {
+async fn main() -> Result<()> {
     // ensure `anvil` is available in $PATH
     let anvil =
         Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn();
diff --git a/examples/big-numbers/Cargo.toml b/examples/big-numbers/Cargo.toml
new file mode 100644
index 00000000..04505c19
--- /dev/null
+++ b/examples/big-numbers/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "examples-big-numbers"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+#ethers-core = { version = "^1.0.0", path = "../../ethers-core" }
+ethers = { path = "../..", version = "1.0.0" }
+
diff --git a/examples/big-numbers/README.md b/examples/big-numbers/README.md
new file mode 100644
index 00000000..7ac9428e
--- /dev/null
+++ b/examples/big-numbers/README.md
@@ -0,0 +1,7 @@
+# Big numbers
+Ethereum uses big numbers (also known as "bignums" or "arbitrary-precision integers") to represent certain values in its codebase and in blockchain transactions. This is necessary because [the EVM](https://ethereum.org/en/developers/docs/evm) operates on a 256-bit word size, which is different from the usual 32-bit or 64-bit of modern machines. This was chosen for the ease of use with 256-bit cryptography (such as Keccak-256 hashes or secp256k1 signatures).
+
+It is worth noting that Ethereum is not the only blockchain or cryptocurrency that uses big numbers. Many other blockchains and cryptocurrencies also use big numbers to represent values in their respective systems.
+
+## Utilities
+In order to create an application, it is often necessary to convert between the representation of values that is easily understood by humans (such as ether) and the machine-readable form that is used by contracts and math functions (such as wei). This is particularly important when working with Ethereum, as certain values, such as balances and gas prices, must be expressed in wei when sending transactions, even if they are displayed to the user in a different format, such as ether or gwei. To help with this conversion, ethers-rs provides two functions, `parse_units` and `format_units`, which allow you to easily convert between human-readable and machine-readable forms of values. `parse_units` can be used to convert a string representing a value in ether, such as "1.1", into a big number in wei, which can be used in contracts and math functions. `format_units` can be used to convert a big number value into a human-readable string, which is useful for displaying values to users.
diff --git a/examples/big-numbers/examples/comparison_equivalence.rs b/examples/big-numbers/examples/comparison_equivalence.rs
new file mode 100644
index 00000000..67bbd282
--- /dev/null
+++ b/examples/big-numbers/examples/comparison_equivalence.rs
@@ -0,0 +1,34 @@
+use ethers::types::U256;
+
+/// `U256` implements traits in `std::cmp`, that means `U256` instances
+/// can be easily compared using standard Rust operators.
+fn main() {
+    // a == b
+    let a = U256::from(100_u32);
+    let b = U256::from(100_u32);
+    assert!(a == b);
+
+    // a < b
+    let a = U256::from(1_u32);
+    let b = U256::from(100_u32);
+    assert!(a < b);
+
+    // a <= b
+    let a = U256::from(100_u32);
+    let b = U256::from(100_u32);
+    assert!(a <= b);
+
+    // a > b
+    let a = U256::from(100_u32);
+    let b = U256::from(1_u32);
+    assert!(a > b);
+
+    // a >= b
+    let a = U256::from(100_u32);
+    let b = U256::from(100_u32);
+    assert!(a >= b);
+
+    // a == 0
+    let a = U256::zero();
+    assert!(a.is_zero());
+}
diff --git a/examples/big-numbers/examples/conversion.rs b/examples/big-numbers/examples/conversion.rs
new file mode 100644
index 00000000..a4ace3cb
--- /dev/null
+++ b/examples/big-numbers/examples/conversion.rs
@@ -0,0 +1,31 @@
+use ethers::{types::U256, utils::format_units};
+
+/// `U256` provides useful conversion functions to enable transformation into native Rust types.
+///
+/// It is important to note that converting a big-number to a floating point type (such as a `f32`
+/// or `f64`) can result in a loss of precision, since you cannot fit 256 bits of information into
+/// 64 bits.
+///
+/// However, there may be cases where you want to perform conversions for presentation purposes.
+/// For example, you may want to display a large number to the user in a more readable format.
+fn main() {
+    let num = U256::from(42_u8);
+
+    let a: u128 = num.as_u128();
+    assert_eq!(a, 42);
+
+    let b: u64 = num.as_u64();
+    assert_eq!(b, 42);
+
+    let c: u32 = num.as_u32();
+    assert_eq!(c, 42);
+
+    let d: usize = num.as_usize();
+    assert_eq!(d, 42);
+
+    let e: String = num.to_string();
+    assert_eq!(e, "42");
+
+    let f: String = format_units(num, 4).unwrap();
+    assert_eq!(f, "0.0042");
+}
diff --git a/examples/big-numbers/examples/create_instances.rs b/examples/big-numbers/examples/create_instances.rs
new file mode 100644
index 00000000..35fea7f1
--- /dev/null
+++ b/examples/big-numbers/examples/create_instances.rs
@@ -0,0 +1,47 @@
+use ethers::{
+    types::{serde_helpers::Numeric, U256},
+    utils::{parse_units, ParseUnits},
+};
+
+fn main() {
+    // From strings
+    let a = U256::from_dec_str("42").unwrap();
+    assert_eq!(format!("{a:?}"), "42");
+
+    let amount = "42";
+    let units = 4;
+    let pu: ParseUnits = parse_units(amount, units).unwrap();
+    let b = U256::from(pu);
+    assert_eq!(format!("{b:?}"), "420000");
+
+    // From numbers
+    let c = U256::from(42_u8);
+    assert_eq!(format!("{c:?}"), "42");
+
+    let d = U256::from(42_u16);
+    assert_eq!(format!("{d:?}"), "42");
+
+    let e = U256::from(42_u32);
+    assert_eq!(format!("{e:?}"), "42");
+
+    let f = U256::from(42_u64);
+    assert_eq!(format!("{f:?}"), "42");
+
+    let g = U256::from(42_u128);
+    assert_eq!(format!("{g:?}"), "42");
+
+    let h = U256::from(0x2a);
+    assert_eq!(format!("{h:?}"), "42");
+
+    let i: U256 = 42.into();
+    assert_eq!(format!("{i:?}"), "42");
+
+    // From `Numeric`
+    let num: Numeric = Numeric::U256(U256::one());
+    let l = U256::from(num);
+    assert_eq!(format!("{l:?}"), "1");
+
+    let num: Numeric = Numeric::Num(42);
+    let m = U256::from(num);
+    assert_eq!(format!("{m:?}"), "42");
+}
diff --git a/examples/big-numbers/examples/math_operations.rs b/examples/big-numbers/examples/math_operations.rs
new file mode 100644
index 00000000..21723bdd
--- /dev/null
+++ b/examples/big-numbers/examples/math_operations.rs
@@ -0,0 +1,52 @@
+use ethers::{types::U256, utils::format_units};
+use std::ops::{Div, Mul};
+
+/// `U256` implements traits in `std::ops`, that means it supports arithmetic operations
+/// using standard Rust operators `+`, `-`. `*`, `/`, `%`, along with additional utilities to
+/// perform common mathematical tasks.
+fn main() {
+    let a = U256::from(10);
+    let b = U256::from(2);
+
+    // addition
+    let sum = a + b;
+    assert_eq!(sum, U256::from(12));
+
+    // subtraction
+    let difference = a - b;
+    assert_eq!(difference, U256::from(8));
+
+    // multiplication
+    let product = a * b;
+    assert_eq!(product, U256::from(20));
+
+    // division
+    let quotient = a / b;
+    assert_eq!(quotient, U256::from(5));
+
+    // modulo
+    let remainder = a % b;
+    assert_eq!(remainder, U256::zero()); // equivalent to `U256::from(0)`
+
+    // exponentiation
+    let power = a.pow(b);
+    assert_eq!(power, U256::from(100));
+    // powers of 10 can also be expressed like this:
+    let power_of_10 = U256::exp10(2);
+    assert_eq!(power_of_10, U256::from(100));
+
+    // Multiply two 'ether' numbers:
+    // Big numbers are integers, that can represent fixed point numbers.
+    // For instance, 1 ether has 18 fixed
+    // decimal places (1.000000000000000000), and its big number
+    // representation is 10^18 = 1000000000000000000.
+    // When we multiply such numbers we are summing up their exponents.
+    // So if we multiply 10^18 * 10^18 we get 10^36, that is obviously incorrect.
+    // In order to get the correct result we need to divide by 10^18.
+    let eth1 = U256::from(10_000000000000000000_u128); // 10 ether
+    let eth2 = U256::from(20_000000000000000000_u128); // 20 ether
+    let base = U256::from(10).pow(18.into());
+    let mul = eth1.mul(eth2).div(base); // We also divide by 10^18
+    let s: String = format_units(mul, "ether").unwrap();
+    assert_eq!(s, "200.000000000000000000"); // 200
+}
diff --git a/examples/big-numbers/examples/utilities.rs b/examples/big-numbers/examples/utilities.rs
new file mode 100644
index 00000000..6d0359c7
--- /dev/null
+++ b/examples/big-numbers/examples/utilities.rs
@@ -0,0 +1,92 @@
+use ethers::{
+    types::U256,
+    utils::{format_units, parse_units, ParseUnits},
+};
+
+fn main() {
+    parse_units_example();
+    format_units_example();
+}
+
+/// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain
+/// math, etc.). We provide convenient methods to map user inputs (usually in 'ether' or 'gwei')
+/// into 'wei' format.
+fn parse_units_example() {
+    let pu: ParseUnits = parse_units("1.0", "wei").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::one());
+
+    let pu: ParseUnits = parse_units("1.0", "kwei").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000));
+
+    let pu: ParseUnits = parse_units("1.0", "mwei").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000));
+
+    let pu: ParseUnits = parse_units("1.0", "gwei").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000000));
+
+    let pu: ParseUnits = parse_units("1.0", "szabo").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000000000_u128));
+
+    let pu: ParseUnits = parse_units("1.0", "finney").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000000000000_u128));
+
+    let pu: ParseUnits = parse_units("1.0", "ether").unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000000000000000_u128));
+
+    let pu: ParseUnits = parse_units("1.0", 18).unwrap();
+    let num = U256::from(pu);
+    assert_eq!(num, U256::from(1000000000000000000_u128));
+}
+
+/// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain
+/// math, etc.). On the other hand it is useful to convert big numbers into user readable formats
+/// when displaying on a UI. Generally dApps display numbers in 'ether' and 'gwei' units,
+/// respectively for displaying amounts and gas. The `format_units` function will format a big
+/// number into a user readable string.
+fn format_units_example() {
+    // 1 ETHER = 10^18 WEI
+    let one_ether = U256::from(1000000000000000000_u128);
+
+    let num: String = format_units(one_ether, "wei").unwrap();
+    assert_eq!(num, "1000000000000000000.0");
+
+    let num: String = format_units(one_ether, "gwei").unwrap();
+    assert_eq!(num, "1000000000.000000000");
+
+    let num: String = format_units(one_ether, "ether").unwrap();
+    assert_eq!(num, "1.000000000000000000");
+
+    // 1 GWEI = 10^9 WEI
+    let one_gwei = U256::from(1000000000_u128);
+
+    let num: String = format_units(one_gwei, 0).unwrap();
+    assert_eq!(num, "1000000000.0");
+
+    let num: String = format_units(one_gwei, "wei").unwrap();
+    assert_eq!(num, "1000000000.0");
+
+    let num: String = format_units(one_gwei, "kwei").unwrap();
+    assert_eq!(num, "1000000.000");
+
+    let num: String = format_units(one_gwei, "mwei").unwrap();
+    assert_eq!(num, "1000.000000");
+
+    let num: String = format_units(one_gwei, "gwei").unwrap();
+    assert_eq!(num, "1.000000000");
+
+    let num: String = format_units(one_gwei, "szabo").unwrap();
+    assert_eq!(num, "0.001000000000");
+
+    let num: String = format_units(one_gwei, "finney").unwrap();
+    assert_eq!(num, "0.000001000000000");
+
+    let num: String = format_units(one_gwei, "ether").unwrap();
+    assert_eq!(num, "0.000000001000000000");
+}
diff --git a/examples/contracts/Cargo.toml b/examples/contracts/Cargo.toml
new file mode 100644
index 00000000..4f0e12bd
--- /dev/null
+++ b/examples/contracts/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "examples-contracts"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[features]
+default = ["legacy"]
+legacy = []
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ethers-solc", "legacy", "rustls", "ws"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
+
+
diff --git a/examples/contracts/README.md b/examples/contracts/README.md
new file mode 100644
index 00000000..472592f7
--- /dev/null
+++ b/examples/contracts/README.md
@@ -0,0 +1,39 @@
+# Contracts
+In this guide, we will go over some examples of using ethers-rs to work with contracts, including using abigen to generate Rust bindings for a contract, listening for contract events, calling contract methods, and instantiating contracts.
+
+## Generating Rust bindings with abigen
+To use a contract with ethers-rs, you will need to generate Rust bindings using the abigen tool. abigen is included with the ethers-rs library and can be used to generate Rust bindings for any Solidity contract.
+
+### Generate a Rust file
+This method takes a smart contract's Application Binary Interface (ABI) file and generates a Rust file to interact with it. This is useful if the smart contract is referenced in different places in a project. File generation from ABI can also be easily included as a build step of your application.
+
+Running the code below will generate a file called `token.rs` containing the bindings inside, which exports an `ERC20Token` struct, along with all its events and methods. Put into a `build.rs` file this will generate the bindings during cargo build.
+
+```rust
+Abigen::new("ERC20Token", "./abi.json")?.generate()?.write_to_file("token.rs")?;
+```
+
+### Generate inline Rust bindings
+This method takes a smart contract's solidity definition and generates inline Rust code to interact with it. This is useful for fast prototyping and for tight scoped use-cases of your contracts. Inline Rust generation uses the `abigen!` macro to expand Rust contract bindings.
+
+Running the code below will generate bindings for the `ERC20Token` struct, along with all its events and methods.
+```rust
+abigen!(
+    ERC20Token,
+    r#"[
+        function approve(address spender, uint256 amount) external returns (bool)
+        event Transfer(address indexed from, address indexed to, uint256 value)
+        event Approval(address indexed owner, address indexed spender, uint256 value)
+    ]"#,
+);
+```
+
+Another way to get the same result, is to provide the ABI contract's definition as follows.
+```rust 
+abigen!(ERC20Token, "./abi.json",);
+```
+
+## Contract instances
+## Contract methods
+## Contract events
+
diff --git a/examples/contracts/examples/abi/IERC20.json b/examples/contracts/examples/abi/IERC20.json
new file mode 100644
index 00000000..4827e872
--- /dev/null
+++ b/examples/contracts/examples/abi/IERC20.json
@@ -0,0 +1,153 @@
+[
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "name": "", "type": "string" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      { "name": "guy", "type": "address" },
+      { "name": "wad", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [{ "name": "", "type": "bool" }],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "name": "", "type": "uint256" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      { "name": "src", "type": "address" },
+      { "name": "dst", "type": "address" },
+      { "name": "wad", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [{ "name": "", "type": "bool" }],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [{ "name": "wad", "type": "uint256" }],
+    "name": "withdraw",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "decimals",
+    "outputs": [{ "name": "", "type": "uint8" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [{ "name": "", "type": "address" }],
+    "name": "balanceOf",
+    "outputs": [{ "name": "", "type": "uint256" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "name": "", "type": "string" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      { "name": "dst", "type": "address" },
+      { "name": "wad", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [{ "name": "", "type": "bool" }],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [],
+    "name": "deposit",
+    "outputs": [],
+    "payable": true,
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [
+      { "name": "", "type": "address" },
+      { "name": "", "type": "address" }
+    ],
+    "name": "allowance",
+    "outputs": [{ "name": "", "type": "uint256" }],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  { "payable": true, "stateMutability": "payable", "type": "fallback" },
+  {
+    "anonymous": false,
+    "inputs": [
+      { "indexed": true, "name": "src", "type": "address" },
+      { "indexed": true, "name": "guy", "type": "address" },
+      { "indexed": false, "name": "wad", "type": "uint256" }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      { "indexed": true, "name": "src", "type": "address" },
+      { "indexed": true, "name": "dst", "type": "address" },
+      { "indexed": false, "name": "wad", "type": "uint256" }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      { "indexed": true, "name": "dst", "type": "address" },
+      { "indexed": false, "name": "wad", "type": "uint256" }
+    ],
+    "name": "Deposit",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      { "indexed": true, "name": "src", "type": "address" },
+      { "indexed": false, "name": "wad", "type": "uint256" }
+    ],
+    "name": "Withdrawal",
+    "type": "event"
+  }
+]
diff --git a/examples/contract_abi.json b/examples/contracts/examples/abi/contract_abi.json
similarity index 100%
rename from examples/contract_abi.json
rename to examples/contracts/examples/abi/contract_abi.json
diff --git a/examples/contracts/examples/abigen.rs b/examples/contracts/examples/abigen.rs
new file mode 100644
index 00000000..b29fa2b2
--- /dev/null
+++ b/examples/contracts/examples/abigen.rs
@@ -0,0 +1,77 @@
+use ethers::{
+    prelude::{abigen, Abigen},
+    providers::{Http, Provider},
+    types::Address,
+};
+use eyre::Result;
+use std::sync::Arc;
+
+/// Abigen is used to generate Rust code to interact with smart contracts on the blockchain.
+/// It provides a way to encode and decode data that is passed to and from smart contracts.
+/// The output of abigen is Rust code, that is bound to the contract's interface, allowing
+/// developers to call its methods to read/write on-chain state and subscribe to realtime events.
+///
+/// The abigen tool can be used in two ways, addressing different use-cases scenarios and developer
+/// taste:
+///
+/// 1. **Rust file generation:** takes a smart contract's Application Binary Interface (ABI)
+/// file and generates a Rust file to interact with it. This is useful if the smart contract is
+/// referenced in different places in a project. File generation from ABI can also be easily
+/// included as a build step of your application.
+/// 2. **Rust inline generation:** takes a smart contract's solidity definition and generates inline
+/// Rust code to interact with it. This is useful for fast prototyping and for tight scoped
+/// use-cases of your contracts.
+/// 3. **Rust inline generation from ABI:** similar to the previous point but instead of Solidity
+/// code takes in input a smart contract's Application Binary Interface (ABI) file.
+#[tokio::main]
+async fn main() -> Result<()> {
+    rust_file_generation()?;
+    rust_inline_generation().await?;
+    rust_inline_generation_from_abi();
+    Ok(())
+}
+
+fn rust_file_generation() -> Result<()> {
+    let base_dir = "./examples/contracts/examples/abi";
+    Abigen::new("IERC20", format!("{base_dir}/IERC20.json"))?
+        .generate()?
+        .write_to_file(format!("{base_dir}/ierc20.rs"))?;
+    Ok(())
+}
+
+fn rust_inline_generation_from_abi() {
+    abigen!(IERC20, "./examples/contracts/examples/abi/IERC20.json");
+}
+
+async fn rust_inline_generation() -> Result<()> {
+    // The abigen! macro expands the contract's code in the current scope
+    // so that you can interface your Rust program with the blockchain
+    // counterpart of the contract.
+    abigen!(
+        IERC20,
+        r#"[
+            function totalSupply() external view returns (uint256)
+            function balanceOf(address account) external view returns (uint256)
+            function transfer(address recipient, uint256 amount) external returns (bool)
+            function allowance(address owner, address spender) external view returns (uint256)
+            function approve(address spender, uint256 amount) external returns (bool)
+            function transferFrom( address sender, address recipient, uint256 amount) external returns (bool)
+            event Transfer(address indexed from, address indexed to, uint256 value)
+            event Approval(address indexed owner, address indexed spender, uint256 value)
+        ]"#,
+    );
+
+    const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
+    const WETH_ADDRESS: &str = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
+
+    let provider = Provider::<Http>::try_from(RPC_URL)?;
+    let client = Arc::new(provider);
+    let address: Address = WETH_ADDRESS.parse()?;
+    let contract = IERC20::new(address, client);
+
+    if let Ok(total_supply) = contract.total_supply().call().await {
+        println!("WETH total supply is {total_supply:?}");
+    }
+
+    Ok(())
+}
diff --git a/examples/abigen.rs b/examples/contracts/examples/compile.rs
similarity index 77%
rename from examples/abigen.rs
rename to examples/contracts/examples/compile.rs
index e0886687..21b7586a 100644
--- a/examples/abigen.rs
+++ b/examples/contracts/examples/compile.rs
@@ -1,11 +1,14 @@
-use ethers::{contract::Abigen, solc::Solc};
+use ethers::{prelude::Abigen, solc::Solc};
+use eyre::Result;
 
-fn main() -> eyre::Result<()> {
+fn main() -> Result<()> {
     let mut args = std::env::args();
     args.next().unwrap(); // skip program name
 
     let contract_name = args.next().unwrap_or_else(|| "SimpleStorage".to_owned());
-    let contract: String = args.next().unwrap_or_else(|| "examples/contract.sol".to_owned());
+    let contract: String = args
+        .next()
+        .unwrap_or_else(|| "examples/contracts/examples/contracts/contract.sol".to_owned());
 
     println!("Generating bindings for {contract}\n");
 
diff --git a/examples/contract.sol b/examples/contracts/examples/contracts/contract.sol
similarity index 93%
rename from examples/contract.sol
rename to examples/contracts/examples/contracts/contract.sol
index 9d04f2f4..e9910f32 100644
--- a/examples/contract.sol
+++ b/examples/contracts/examples/contracts/contract.sol
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: Unlicense
 pragma solidity >=0.4.24;
 
 contract SimpleStorage {
diff --git a/examples/contract_human_readable.rs b/examples/contracts/examples/deploy_anvil.rs
similarity index 90%
rename from examples/contract_human_readable.rs
rename to examples/contracts/examples/deploy_anvil.rs
index d0ca18c7..7ef77176 100644
--- a/examples/contract_human_readable.rs
+++ b/examples/contracts/examples/deploy_anvil.rs
@@ -1,10 +1,13 @@
 use ethers::{
-    prelude::*,
-    solc::{Project, ProjectPathsConfig},
-    utils::Anvil,
+    contract::{abigen, ContractFactory},
+    core::utils::Anvil,
+    middleware::SignerMiddleware,
+    providers::{Http, Provider},
+    signers::{LocalWallet, Signer},
+    solc::{Artifact, Project, ProjectPathsConfig},
 };
 use eyre::Result;
-use std::{convert::TryFrom, path::PathBuf, sync::Arc, time::Duration};
+use std::{path::PathBuf, sync::Arc, time::Duration};
 
 // Generate the type-safe contract bindings by providing the ABI
 // definition in human readable format
diff --git a/examples/contract_with_abi_and_bytecode.rs b/examples/contracts/examples/deploy_from_abi_and_bytecode.rs
similarity index 89%
rename from examples/contract_with_abi_and_bytecode.rs
rename to examples/contracts/examples/deploy_from_abi_and_bytecode.rs
index 2748e252..1dd91f37 100644
--- a/examples/contract_with_abi_and_bytecode.rs
+++ b/examples/contracts/examples/deploy_from_abi_and_bytecode.rs
@@ -1,4 +1,10 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    contract::abigen,
+    core::utils::Anvil,
+    middleware::SignerMiddleware,
+    providers::{Http, Provider},
+    signers::{LocalWallet, Signer},
+};
 use eyre::Result;
 use std::{convert::TryFrom, sync::Arc, time::Duration};
 
diff --git a/examples/contract_with_abi.rs b/examples/contracts/examples/deploy_from_solidity.rs
similarity index 85%
rename from examples/contract_with_abi.rs
rename to examples/contracts/examples/deploy_from_solidity.rs
index c77d094f..fa6fb365 100644
--- a/examples/contract_with_abi.rs
+++ b/examples/contracts/examples/deploy_from_solidity.rs
@@ -1,4 +1,11 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    contract::{abigen, ContractFactory},
+    core::utils::Anvil,
+    middleware::SignerMiddleware,
+    providers::{Http, Provider},
+    signers::{LocalWallet, Signer},
+    solc::Solc,
+};
 use eyre::Result;
 use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration};
 
@@ -6,7 +13,7 @@ use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration};
 // definition
 abigen!(
     SimpleContract,
-    "./examples/contract_abi.json",
+    "./examples/contracts/examples/abi/contract_abi.json",
     event_derives(serde::Deserialize, serde::Serialize)
 );
 
@@ -17,8 +24,8 @@ async fn main() -> Result<()> {
     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
-    let source = Path::new(&env!("CARGO_MANIFEST_DIR")).join("examples/contract.sol");
+    // manifest of `example/contracts`. which will be `../` relative to this file
+    let source = Path::new(&env!("CARGO_MANIFEST_DIR")).join("examples/contracts/contract.sol");
     let compiled = Solc::default().compile_source(source).expect("Could not compile contracts");
     let (abi, bytecode, _runtime_bytecode) =
         compiled.find("SimpleStorage").expect("could not find contract").into_parts_or_default();
diff --git a/examples/moonbeam_with_abi.rs b/examples/contracts/examples/deploy_moonbeam.rs
similarity index 95%
rename from examples/moonbeam_with_abi.rs
rename to examples/contracts/examples/deploy_moonbeam.rs
index e17500bc..a26464c8 100644
--- a/examples/moonbeam_with_abi.rs
+++ b/examples/contracts/examples/deploy_moonbeam.rs
@@ -1,8 +1,8 @@
-use ethers::prelude::*;
+use ethers::contract::abigen;
 
 abigen!(
     SimpleContract,
-    "./examples/contract_abi.json",
+    "./examples/contracts/examples/abi/contract_abi.json",
     event_derives(serde::Deserialize, serde::Serialize)
 );
 
@@ -19,7 +19,9 @@ abigen!(
 #[tokio::main]
 #[cfg(feature = "legacy")]
 async fn main() -> eyre::Result<()> {
+    use ethers::prelude::*;
     use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration};
+
     const MOONBEAM_DEV_ENDPOINT: &str = "http://localhost:9933";
 
     // set the path to the contract, `CARGO_MANIFEST_DIR` points to the directory containing the
diff --git a/examples/contracts/examples/events.rs b/examples/contracts/examples/events.rs
new file mode 100644
index 00000000..4039d883
--- /dev/null
+++ b/examples/contracts/examples/events.rs
@@ -0,0 +1,68 @@
+use ethers::{
+    contract::abigen,
+    core::types::Address,
+    providers::{Provider, StreamExt, Ws},
+};
+use eyre::Result;
+use std::sync::Arc;
+
+abigen!(
+    IERC20,
+    r#"[
+        event Transfer(address indexed from, address indexed to, uint256 value)
+        event Approval(address indexed owner, address indexed spender, uint256 value)
+    ]"#,
+);
+
+const WSS_URL: &str = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27";
+const WETH_ADDRESS: &str = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    let provider = Provider::<Ws>::connect(WSS_URL).await?;
+    let client = Arc::new(provider);
+    let address: Address = WETH_ADDRESS.parse()?;
+    let contract = IERC20::new(address, client);
+
+    listen_all_events(&contract).await?;
+    listen_specific_events(&contract).await?;
+
+    Ok(())
+}
+
+/// Given a contract instance, subscribe to all possible events.
+/// This allows to centralize the event handling logic and dispatch
+/// proper actions.
+///
+/// Note that all event bindings have been generated
+/// by abigen. Feel free to investigate the abigen expanded code to
+/// better understand types and functionalities.
+async fn listen_all_events(contract: &IERC20<Provider<Ws>>) -> Result<()> {
+    let events = contract.events().from_block(16232696);
+    let mut stream = events.stream().await?.take(1);
+
+    while let Some(Ok(evt)) = stream.next().await {
+        match evt {
+            IERC20Events::ApprovalFilter(f) => println!("{f:?}"),
+            IERC20Events::TransferFilter(f) => println!("{f:?}"),
+        }
+    }
+
+    Ok(())
+}
+
+/// Given a contract instance subscribe to a single type of event.
+///
+/// Note that all event bindings have been generated
+/// by abigen. Feel free to investigate the abigen expanded code to
+/// better understand types and functionalities.
+async fn listen_specific_events(contract: &IERC20<Provider<Ws>>) -> Result<()> {
+    let events = contract.event::<ApprovalFilter>().from_block(16232696);
+    let mut stream = events.stream().await?.take(1);
+
+    while let Some(Ok(f)) = stream.next().await {
+        println!("ApprovalFilter event: {f:?}");
+    }
+
+    Ok(())
+}
diff --git a/examples/subscribe_contract_events_with_meta.rs b/examples/contracts/examples/events_with_meta.rs
similarity index 80%
rename from examples/subscribe_contract_events_with_meta.rs
rename to examples/contracts/examples/events_with_meta.rs
index c81b4da8..ceb0c68e 100644
--- a/examples/subscribe_contract_events_with_meta.rs
+++ b/examples/contracts/examples/events_with_meta.rs
@@ -1,4 +1,8 @@
-use ethers::prelude::*;
+use ethers::{
+    contract::abigen,
+    core::types::Address,
+    providers::{Provider, StreamExt, Ws},
+};
 use eyre::Result;
 use std::sync::Arc;
 
@@ -11,9 +15,6 @@ abigen!(
     ]"#,
 );
 
-// In order to run this example you need to include Ws and TLS features
-// Run this example with
-// `cargo run -p ethers --example subscribe_contract_events_with_meta --features="ws","rustls"`
 #[tokio::main]
 async fn main() -> Result<()> {
     let client =
@@ -27,8 +28,8 @@ async fn main() -> Result<()> {
     let weth = ERC20::new(address, Arc::clone(&client));
 
     // Subscribe Transfer events
-    let events = weth.events();
-    let mut stream = events.stream().await?.with_meta();
+    let events = weth.events().from_block(16232698);
+    let mut stream = events.stream().await?.with_meta().take(1);
     while let Some(Ok((event, meta))) = stream.next().await {
         println!("src: {:?}, dst: {:?}, wad: {:?}", event.src, event.dst, event.wad);
 
diff --git a/examples/contracts/examples/instances.rs b/examples/contracts/examples/instances.rs
new file mode 100644
index 00000000..7f755fb7
--- /dev/null
+++ b/examples/contracts/examples/instances.rs
@@ -0,0 +1,2 @@
+#[tokio::main]
+async fn main() {}
diff --git a/examples/contracts/examples/methods.rs b/examples/contracts/examples/methods.rs
new file mode 100644
index 00000000..7f755fb7
--- /dev/null
+++ b/examples/contracts/examples/methods.rs
@@ -0,0 +1,2 @@
+#[tokio::main]
+async fn main() {}
diff --git a/examples/contract_with_abi_and_structs.rs b/examples/contracts/examples/methods_abi_and_structs.rs
similarity index 82%
rename from examples/contract_with_abi_and_structs.rs
rename to examples/contracts/examples/methods_abi_and_structs.rs
index b74c9ea9..87b08c52 100644
--- a/examples/contract_with_abi_and_structs.rs
+++ b/examples/contracts/examples/methods_abi_and_structs.rs
@@ -1,7 +1,17 @@
 //! Main entry point for ContractMonitor
 
-use ethers::{prelude::*, utils::Anvil};
-use std::{convert::TryFrom, sync::Arc, time::Duration};
+use std::{sync::Arc, time::Duration};
+
+use ethers::{
+    contract::abigen,
+    core::{
+        types::{Address, U256},
+        utils::Anvil,
+    },
+    middleware::SignerMiddleware,
+    providers::{Http, Provider},
+    signers::LocalWallet,
+};
 
 abigen!(VerifierContract, "ethers-contract/tests/solidity-contracts/verifier_abi.json");
 
diff --git a/examples/ethers-wasm/Cargo.toml b/examples/ethers-wasm/Cargo.toml
index 181e0f81..6ca4f575 100644
--- a/examples/ethers-wasm/Cargo.toml
+++ b/examples/ethers-wasm/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-wasm"
 version = "0.1.0"
-edition = "2018"
+edition = "2021"
 rust-version = "1.64"
 authors = ["Matthias Seitz <matthias.seitz@outlook.de>"]
 license = "MIT OR Apache-2.0"
diff --git a/examples/ethers-wasm/abi/contract_abi.json b/examples/ethers-wasm/abi/contract_abi.json
new file mode 100644
index 00000000..12de10c8
--- /dev/null
+++ b/examples/ethers-wasm/abi/contract_abi.json
@@ -0,0 +1,1 @@
+[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}]
diff --git a/examples/ethers-wasm/src/lib.rs b/examples/ethers-wasm/src/lib.rs
index 2e18ad03..273836ce 100644
--- a/examples/ethers-wasm/src/lib.rs
+++ b/examples/ethers-wasm/src/lib.rs
@@ -24,7 +24,7 @@ macro_rules! log {
 
 abigen!(
     SimpleContract,
-    "./../contract_abi.json",
+    "./abi/contract_abi.json",
     event_derives(serde::Deserialize, serde::Serialize)
 );
 
diff --git a/examples/events/Cargo.toml b/examples/events/Cargo.toml
new file mode 100644
index 00000000..4330bdf7
--- /dev/null
+++ b/examples/events/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "examples-events"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0" }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
\ No newline at end of file
diff --git a/examples/events/README.md b/examples/events/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/events/examples/placeholder.rs b/examples/events/examples/placeholder.rs
new file mode 100644
index 00000000..7f755fb7
--- /dev/null
+++ b/examples/events/examples/placeholder.rs
@@ -0,0 +1,2 @@
+#[tokio::main]
+async fn main() {}
diff --git a/examples/middleware/Cargo.toml b/examples/middleware/Cargo.toml
new file mode 100644
index 00000000..0c6eb85d
--- /dev/null
+++ b/examples/middleware/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "examples-middleware"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["rustls", "ws"] }
+
+async-trait = { version = "0.1.50", default-features = false }
+eyre = "0.6"
+serde_json = "1.0.61"
+thiserror = { version = "1.0", default-features = false }
+tokio = { version = "1.18", features = ["macros", "rt", "rt-multi-thread"] }
+
diff --git a/examples/middleware/README.md b/examples/middleware/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/middleware/examples/builder.rs b/examples/middleware/examples/builder.rs
new file mode 100644
index 00000000..c9fb058a
--- /dev/null
+++ b/examples/middleware/examples/builder.rs
@@ -0,0 +1,62 @@
+use ethers::{
+    core::types::BlockNumber,
+    middleware::{
+        gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
+        gas_oracle::{GasNow, GasOracleMiddleware},
+        MiddlewareBuilder, NonceManagerMiddleware, SignerMiddleware,
+    },
+    providers::{Http, Middleware, Provider},
+    signers::{LocalWallet, Signer},
+};
+use std::convert::TryFrom;
+
+const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
+const SIGNING_KEY: &str = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169";
+
+/// In ethers-rs, middleware is a way to customize the behavior of certain aspects of the library by
+/// injecting custom logic into the process of sending transactions and interacting with contracts
+/// on the Ethereum blockchain. The MiddlewareBuilder trait provides a way to define a chain of
+/// middleware that will be called at different points in this process, allowing you to customize
+/// the behavior of the Provider based on your needs.
+#[tokio::main]
+async fn main() {
+    builder_example().await;
+    builder_example_raw_wrap().await;
+}
+
+async fn builder_example() {
+    let signer = SIGNING_KEY.parse::<LocalWallet>().unwrap();
+    let address = signer.address();
+    let escalator = GeometricGasPrice::new(1.125, 60_u64, None::<u64>);
+    let gas_oracle = GasNow::new();
+
+    let provider = Provider::<Http>::try_from(RPC_URL)
+        .unwrap()
+        .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock))
+        .gas_oracle(gas_oracle)
+        .with_signer(signer)
+        .nonce_manager(address); // Outermost layer
+
+    match provider.get_block(BlockNumber::Latest).await {
+        Ok(Some(block)) => println!("{:?}", block.number),
+        _ => println!("Unable to get latest block"),
+    }
+}
+
+async fn builder_example_raw_wrap() {
+    let signer = SIGNING_KEY.parse::<LocalWallet>().unwrap();
+    let address = signer.address();
+    let escalator = GeometricGasPrice::new(1.125, 60_u64, None::<u64>);
+
+    let provider = Provider::<Http>::try_from(RPC_URL)
+        .unwrap()
+        .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock))
+        .wrap_into(|p| SignerMiddleware::new(p, signer))
+        .wrap_into(|p| GasOracleMiddleware::new(p, GasNow::new()))
+        .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer
+
+    match provider.get_block(BlockNumber::Latest).await {
+        Ok(Some(block)) => println!("{:?}", block.number),
+        _ => println!("Unable to get latest block"),
+    }
+}
diff --git a/examples/middleware/examples/create_custom_middleware.rs b/examples/middleware/examples/create_custom_middleware.rs
new file mode 100644
index 00000000..56a4a21d
--- /dev/null
+++ b/examples/middleware/examples/create_custom_middleware.rs
@@ -0,0 +1,156 @@
+use async_trait::async_trait;
+use ethers::{
+    core::{
+        types::{transaction::eip2718::TypedTransaction, BlockId, TransactionRequest, U256},
+        utils::{parse_units, Anvil},
+    },
+    middleware::MiddlewareBuilder,
+    providers::{FromErr, Http, Middleware, PendingTransaction, Provider},
+    signers::{LocalWallet, Signer},
+};
+use thiserror::Error;
+
+/// This example demonstrates the mechanisms for creating custom middlewares in ethers-rs.
+/// The example includes explanations of the process and code snippets to illustrate the
+/// concepts. It is intended for developers who want to learn how to customize the behavior of
+/// ethers-rs providers by creating and using custom middlewares.
+///
+/// This custom middleware increases the gas value of transactions sent through an ethers-rs
+/// provider by a specified percentage and will be called for each transaction before it is sent.
+/// This can be useful if you want to ensure that transactions have a higher gas value than the
+/// estimated, in order to improve the chances of them not to run out of gas when landing on-chain.
+#[derive(Debug)]
+struct GasMiddleware<M> {
+    inner: M,
+    /// This value is used to raise the gas value before sending transactions
+    contingency: U256,
+}
+
+/// Contingency is expressed with 4 units
+/// e.g.
+/// 50% => 1 + 0.5  => 15000
+/// 20% => 1 + 0.2  => 12000
+/// 1%  => 1 + 0.01 => 10100
+const CONTINGENCY_UNITS: usize = 4;
+
+impl<M> GasMiddleware<M>
+where
+    M: Middleware,
+{
+    /// Creates an instance of GasMiddleware
+    /// `ìnner` the inner Middleware
+    /// `perc` This is an unsigned integer representing the percentage increase in the amount of gas
+    /// to be used for the transaction. The percentage is relative to the gas value specified in the
+    /// transaction. Valid contingency values are in range 1..=50. Otherwise a custom middleware
+    /// error is raised.
+    pub fn new(inner: M, perc: u32) -> Result<Self, GasMiddlewareError<M>> {
+        let contingency = match perc {
+            0 => Err(GasMiddlewareError::TooLowContingency(perc))?,
+            51.. => Err(GasMiddlewareError::TooHighContingency(perc))?,
+            1..=50 => {
+                let decimals = 2;
+                let perc = U256::from(perc) * U256::exp10(decimals); // e.g. 50 => 5000
+                let one = parse_units(1, CONTINGENCY_UNITS).unwrap();
+                let one = U256::from(one);
+                one + perc // e.g. 50% => 1 + 0.5 => 10000 + 5000 => 15000
+            }
+        };
+
+        Ok(Self { inner, contingency })
+    }
+}
+
+/// Let's implement the `Middleware` trait for our custom middleware.
+/// All trait functions are derived automatically, so we just need to
+/// override the needed functions.
+#[async_trait]
+impl<M> Middleware for GasMiddleware<M>
+where
+    M: Middleware,
+{
+    type Error = GasMiddlewareError<M>;
+    type Provider = M::Provider;
+    type Inner = M;
+
+    fn inner(&self) -> &M {
+        &self.inner
+    }
+
+    /// In this function we bump the transaction gas value by the specified percentage
+    /// This can raise a custom middleware error if a gas amount was not set for
+    /// the transaction.
+    async fn send_transaction<T: Into<TypedTransaction> + Send + Sync>(
+        &self,
+        tx: T,
+        block: Option<BlockId>,
+    ) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
+        let mut tx: TypedTransaction = tx.into();
+
+        let curr_gas: U256 = match tx.gas() {
+            Some(gas) => gas.to_owned(),
+            None => Err(GasMiddlewareError::NoGasSetForTransaction)?,
+        };
+
+        println!("Original transaction gas: {curr_gas:?} wei");
+        let units: U256 = U256::exp10(CONTINGENCY_UNITS);
+        let raised_gas: U256 = (curr_gas * self.contingency) / units;
+        tx.set_gas(raised_gas);
+        println!("Raised transaction gas: {raised_gas:?} wei");
+
+        // Dispatch the call to the inner layer
+        self.inner().send_transaction(tx, block).await.map_err(FromErr::from)
+    }
+}
+
+/// This example demonstrates how to handle errors in custom middlewares. It shows how to define
+/// custom error types, use them in middleware implementations, and how to propagate the errors
+/// through the middleware chain. This is intended for developers who want to create custom
+/// middlewares that can handle and propagate errors in a consistent and robust way.
+#[derive(Error, Debug)]
+pub enum GasMiddlewareError<M: Middleware> {
+    /// Thrown when the internal middleware errors
+    #[error("{0}")]
+    MiddlewareError(M::Error),
+    /// Specific errors of this GasMiddleware.
+    /// Please refer to the `thiserror` crate for
+    /// further docs.
+    #[error("{0}")]
+    TooHighContingency(u32),
+    #[error("{0}")]
+    TooLowContingency(u32),
+    #[error("Cannot raise gas! Gas value not provided for this transaction.")]
+    NoGasSetForTransaction,
+}
+
+impl<M: Middleware> FromErr<M::Error> for GasMiddlewareError<M> {
+    fn from(src: M::Error) -> Self {
+        GasMiddlewareError::MiddlewareError(src)
+    }
+}
+
+#[tokio::main]
+async fn main() -> eyre::Result<()> {
+    let anvil = Anvil::new().spawn();
+
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
+    let wallet2: LocalWallet = anvil.keys()[1].clone().into();
+    let signer = wallet.with_chain_id(anvil.chain_id());
+
+    let gas_raise_perc = 50; // 50%;
+    let provider = Provider::<Http>::try_from(anvil.endpoint())?
+        .with_signer(signer)
+        .wrap_into(|s| GasMiddleware::new(s, gas_raise_perc).unwrap());
+
+    let gas = 15000;
+    let tx = TransactionRequest::new().to(wallet2.address()).value(10000).gas(gas);
+
+    let pending_tx = provider.send_transaction(tx, None).await?;
+
+    let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?;
+    let tx = provider.get_transaction(receipt.transaction_hash).await?;
+
+    println!("Sent tx: {}\n", serde_json::to_string(&tx)?);
+    println!("Tx receipt: {}", serde_json::to_string(&receipt)?);
+
+    Ok(())
+}
diff --git a/examples/middleware/examples/gas_escalator.rs b/examples/middleware/examples/gas_escalator.rs
new file mode 100644
index 00000000..c60d304c
--- /dev/null
+++ b/examples/middleware/examples/gas_escalator.rs
@@ -0,0 +1,59 @@
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    middleware::gas_escalator::*,
+    providers::{Http, Middleware, Provider},
+};
+use eyre::Result;
+
+/// The gas escalator middleware in ethers-rs is designed to automatically increase the gas cost of
+/// transactions if they get stuck in the mempool. This can be useful if you want to
+/// ensure that transactions are processed in a timely manner without having to manually adjust the
+/// gas cost yourself.
+#[tokio::main]
+async fn main() -> Result<()> {
+    let every_secs: u64 = 60;
+    let max_price: Option<i32> = None;
+
+    // Linearly increase gas price:
+    // Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs`
+    // seconds until the transaction gets confirmed. There is an optional upper limit.
+    let increase_by: i32 = 100;
+    let linear_escalator = LinearGasPrice::new(increase_by, every_secs, max_price);
+    send_escalating_transaction(linear_escalator).await?;
+
+    // Geometrically increase gas price:
+    // Start with `initial_price`, then increase it every 'every_secs' seconds by a fixed
+    // coefficient. Coefficient defaults to 1.125 (12.5%), the minimum increase for Parity to
+    // replace a transaction. Coefficient can be adjusted, and there is an optional upper limit.
+    let coefficient: f64 = 1.125;
+    let geometric_escalator = GeometricGasPrice::new(coefficient, every_secs, max_price);
+    send_escalating_transaction(geometric_escalator).await?;
+
+    Ok(())
+}
+
+async fn send_escalating_transaction<E>(escalator: E) -> Result<()>
+where
+    E: GasEscalator + Clone + 'static,
+{
+    // Spawn local node
+    let anvil = Anvil::new().spawn();
+    let endpoint = anvil.endpoint();
+
+    // Connect to the node
+    let provider = Provider::<Http>::try_from(endpoint)?;
+    let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock);
+
+    let accounts = provider.get_accounts().await?;
+    let from = accounts[0];
+    let to = accounts[1];
+    let tx = TransactionRequest::new().from(from).to(to).value(1000);
+
+    // Bumps the gas price until transaction gets mined
+    let pending_tx = provider.send_transaction(tx, None).await?;
+    let receipt = pending_tx.await?;
+
+    println!("{receipt:?}");
+
+    Ok(())
+}
diff --git a/examples/middleware/examples/gas_oracle.rs b/examples/middleware/examples/gas_oracle.rs
new file mode 100644
index 00000000..95105aeb
--- /dev/null
+++ b/examples/middleware/examples/gas_oracle.rs
@@ -0,0 +1,88 @@
+use ethers::{
+    core::types::Chain,
+    etherscan::Client,
+    middleware::gas_oracle::{
+        BlockNative, Etherscan, GasCategory, GasNow, GasOracle, Polygon, ProviderOracle,
+    },
+    providers::{Http, Provider},
+};
+
+/// In Ethereum, the "gas" of a transaction refers to the amount of computation required to execute
+/// the transaction on the blockchain. Gas is typically measured in units of "gas," and the cost of
+/// a transaction is determined by the amount of gas it consumes.
+///
+/// A "gas oracle" is a tool or service that provides information about the current price of gas on
+/// the Ethereum network. Gas oracles are often used to help determine the appropriate amount of gas
+/// to include in a transaction, in order to ensure that it will be processed in a timely manner
+/// without running out of gas.
+///
+/// Ethers-rs includes a feature called "gas oracle middleware" that allows you to customize the
+/// behavior of the library when it comes to determining the gas cost of transactions.
+#[tokio::main]
+async fn main() {
+    blocknative().await;
+    etherscan().await;
+    gas_now().await;
+    polygon().await;
+    provider_oracle().await;
+    //etherchain().await; // FIXME: Etherchain URL is broken (Http 404)
+}
+
+async fn blocknative() {
+    let api_key: Option<String> = std::env::var("BLOCK_NATIVE_API_KEY").ok();
+    let oracle = BlockNative::new(api_key).category(GasCategory::Fastest);
+    match oracle.fetch().await {
+        Ok(gas_price) => println!("[Blocknative]: Gas price is {gas_price:?}"),
+        Err(e) => panic!("[Blocknative]: Cannot estimate gas: {e:?}"),
+    }
+}
+
+async fn etherscan() {
+    let chain = Chain::Mainnet;
+    let api_key: String = std::env::var("ETHERSCAN_API_KEY_ETHEREUM").expect("Provide an API key");
+    if let Ok(client) = Client::new(chain, api_key) {
+        let oracle = Etherscan::new(client).category(GasCategory::Fast);
+        match oracle.fetch().await {
+            Ok(gas_price) => println!("[Etherscan]: Gas price is {gas_price:?}"),
+            Err(e) => panic!("[Etherscan]: Cannot estimate gas: {e:?}"),
+        }
+    }
+}
+
+async fn gas_now() {
+    let oracle = GasNow::new().category(GasCategory::Fast);
+    match oracle.fetch().await {
+        Ok(gas_price) => println!("[GasNow]: Gas price is {gas_price:?}"),
+        Err(e) => panic!("[GasNow]: Cannot estimate gas: {e:?}"),
+    }
+}
+
+async fn polygon() {
+    let chain = Chain::Polygon;
+    if let Ok(oracle) = Polygon::new(chain) {
+        match oracle.category(GasCategory::SafeLow).fetch().await {
+            Ok(gas_price) => println!("[Polygon]: Gas price is {gas_price:?}"),
+            Err(e) => panic!("[Polygon]: Cannot estimate gas: {e:?}"),
+        }
+    }
+}
+
+async fn provider_oracle() {
+    const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
+    let provider = Provider::<Http>::try_from(RPC_URL).unwrap();
+    let oracle = ProviderOracle::new(provider);
+    match oracle.fetch().await {
+        Ok(gas_price) => println!("[Provider oracle]: Gas price is {gas_price:?}"),
+        Err(e) => panic!("[Provider oracle]: Cannot estimate gas: {e:?}"),
+    }
+}
+
+/*
+// FIXME: Etherchain URL is broken (Http 404)
+async fn etherchain() {
+    let oracle = Etherchain::new().category(GasCategory::Standard);
+    match oracle.fetch().await {
+        Ok(gas_price) => println!("[Etherchain]: Gas price is {gas_price:?}"),
+        Err(e) => panic!("[Etherchain]: Cannot estimate gas: {e:?}"),
+    }
+}*/
diff --git a/examples/middleware/examples/nonce_manager.rs b/examples/middleware/examples/nonce_manager.rs
new file mode 100644
index 00000000..e76bcc1c
--- /dev/null
+++ b/examples/middleware/examples/nonce_manager.rs
@@ -0,0 +1,46 @@
+use ethers::{
+    core::{
+        types::{BlockNumber, TransactionRequest},
+        utils::Anvil,
+    },
+    middleware::MiddlewareBuilder,
+    providers::{Http, Middleware, Provider},
+};
+use eyre::Result;
+
+/// In Ethereum, the nonce of a transaction is a number that represents the number of transactions
+/// that have been sent from a particular account. The nonce is used to ensure that transactions are
+/// processed in the order they are intended, and to prevent the same transaction from being
+/// processed multiple times.
+///
+/// The nonce manager in ethers-rs is a middleware that helps you manage the nonce
+/// of transactions by keeping track of the current nonce for a given account and automatically
+/// incrementing it as needed. This can be useful if you want to ensure that transactions are sent
+/// in the correct order, or if you want to avoid having to manually manage the nonce yourself.
+#[tokio::main]
+async fn main() -> Result<()> {
+    let anvil = Anvil::new().spawn();
+    let endpoint = anvil.endpoint();
+
+    let provider = Provider::<Http>::try_from(endpoint)?;
+    let accounts = provider.get_accounts().await?;
+    let account = accounts[0];
+    let to = accounts[1];
+    let tx = TransactionRequest::new().from(account).to(to).value(1000);
+
+    let nonce_manager = provider.nonce_manager(account);
+
+    let curr_nonce = nonce_manager
+        .get_transaction_count(account, Some(BlockNumber::Pending.into()))
+        .await?
+        .as_u64();
+
+    assert_eq!(curr_nonce, 0);
+
+    nonce_manager.send_transaction(tx, None).await?;
+    let next_nonce = nonce_manager.next().as_u64();
+
+    assert_eq!(next_nonce, 1);
+
+    Ok(())
+}
diff --git a/examples/middleware/examples/policy.rs b/examples/middleware/examples/policy.rs
new file mode 100644
index 00000000..3e2e0a4b
--- /dev/null
+++ b/examples/middleware/examples/policy.rs
@@ -0,0 +1,39 @@
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    middleware::{
+        policy::{PolicyMiddlewareError, RejectEverything},
+        MiddlewareBuilder, PolicyMiddleware,
+    },
+    providers::{Http, Middleware, Provider},
+};
+use eyre::Result;
+
+/// Policy middleware is a way to inject custom logic into the process of sending transactions and
+/// interacting with contracts on the Ethereum blockchain. It allows you to define rules or policies
+/// that should be followed when performing these actions, and to customize the behavior of the
+/// library based on these policies.
+#[tokio::main]
+async fn main() -> Result<()> {
+    let anvil = Anvil::new().spawn();
+    let endpoint = anvil.endpoint();
+
+    let provider = Provider::<Http>::try_from(endpoint)?;
+
+    let accounts = provider.get_accounts().await?;
+    let account = accounts[0];
+    let to = accounts[1];
+    let tx = TransactionRequest::new().from(account).to(to).value(1000);
+
+    let policy = RejectEverything;
+    let policy_middleware = provider.wrap_into(|p| PolicyMiddleware::new(p, policy));
+
+    match policy_middleware.send_transaction(tx, None).await {
+        Err(e) => {
+            // Given the RejectEverything policy, we expect to execute this branch
+            assert!(matches!(e, PolicyMiddlewareError::PolicyError(())))
+        }
+        _ => panic!("We don't expect this to happen!"),
+    }
+
+    Ok(())
+}
diff --git a/examples/middleware/examples/signer.rs b/examples/middleware/examples/signer.rs
new file mode 100644
index 00000000..38ad057c
--- /dev/null
+++ b/examples/middleware/examples/signer.rs
@@ -0,0 +1,40 @@
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    middleware::SignerMiddleware,
+    providers::{Http, Middleware, Provider},
+    signers::{LocalWallet, Signer},
+};
+use eyre::Result;
+use std::convert::TryFrom;
+
+/// In Ethereum, transactions must be signed with a private key before they can be broadcast to the
+/// network. Ethers-rs provides a way to customize this process by allowing
+/// you to define a signer, called to sign transactions before they are sent.
+#[tokio::main]
+async fn main() -> Result<()> {
+    let anvil = Anvil::new().spawn();
+
+    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(anvil.endpoint())?;
+
+    // connect the wallet to the provider
+    let client = SignerMiddleware::new(provider, wallet.with_chain_id(anvil.chain_id()));
+
+    // craft the transaction
+    let tx = TransactionRequest::new().to(wallet2.address()).value(10000);
+
+    // send it!
+    let pending_tx = client.send_transaction(tx, None).await?;
+
+    // get the mined tx
+    let receipt = pending_tx.await?.ok_or_else(|| eyre::format_err!("tx dropped from mempool"))?;
+    let tx = client.get_transaction(receipt.transaction_hash).await?;
+
+    println!("Sent tx: {}\n", serde_json::to_string(&tx)?);
+    println!("Tx receipt: {}", serde_json::to_string(&receipt)?);
+
+    Ok(())
+}
diff --git a/examples/middleware/examples/timelag.rs b/examples/middleware/examples/timelag.rs
new file mode 100644
index 00000000..7f755fb7
--- /dev/null
+++ b/examples/middleware/examples/timelag.rs
@@ -0,0 +1,2 @@
+#[tokio::main]
+async fn main() {}
diff --git a/examples/middleware/examples/transformer.rs b/examples/middleware/examples/transformer.rs
new file mode 100644
index 00000000..7f755fb7
--- /dev/null
+++ b/examples/middleware/examples/transformer.rs
@@ -0,0 +1,2 @@
+#[tokio::main]
+async fn main() {}
diff --git a/examples/providers/Cargo.toml b/examples/providers/Cargo.toml
new file mode 100644
index 00000000..71a92210
--- /dev/null
+++ b/examples/providers/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "examples-providers"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[features]
+default = ["ipc"]
+ipc = []
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ipc", "rustls", "ws"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
diff --git a/examples/providers/README.md b/examples/providers/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/ipc.rs b/examples/providers/examples/ipc.rs
similarity index 100%
rename from examples/ipc.rs
rename to examples/providers/examples/ipc.rs
diff --git a/examples/quorum.rs b/examples/providers/examples/quorum.rs
similarity index 85%
rename from examples/quorum.rs
rename to examples/providers/examples/quorum.rs
index 95f62af1..a1d50c8c 100644
--- a/examples/quorum.rs
+++ b/examples/providers/examples/quorum.rs
@@ -1,11 +1,15 @@
 //! Example usage for the `QuorumProvider` that requests multiple backends and only returns
 //! a value if the configured `Quorum` was reached.
 
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    core::utils::Anvil,
+    providers::{Http, Middleware, Provider, Quorum, QuorumProvider, WeightedProvider, Ws},
+};
+use eyre::Result;
 use std::{str::FromStr, time::Duration};
 
 #[tokio::main]
-async fn main() -> eyre::Result<()> {
+async fn main() -> Result<()> {
     let anvil = Anvil::new().spawn();
 
     // create a quorum provider with some providers
diff --git a/examples/rw.rs b/examples/providers/examples/rw.rs
similarity index 76%
rename from examples/rw.rs
rename to examples/providers/examples/rw.rs
index 89733a8f..89f1f439 100644
--- a/examples/rw.rs
+++ b/examples/providers/examples/rw.rs
@@ -1,11 +1,15 @@
 //! Example usage for the `RwClient` that uses a didicated client to send transaction and nother one
 //! for read ops
 
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    core::utils::Anvil,
+    providers::{Http, Middleware, Provider, Ws},
+};
+use eyre::Result;
 use std::{str::FromStr, time::Duration};
 
 #[tokio::main]
-async fn main() -> eyre::Result<()> {
+async fn main() -> Result<()> {
     let anvil = Anvil::new().spawn();
 
     let http = Http::from_str(&anvil.endpoint())?;
diff --git a/examples/queries/Cargo.toml b/examples/queries/Cargo.toml
new file mode 100644
index 00000000..fa92ea52
--- /dev/null
+++ b/examples/queries/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "examples-queries"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
\ No newline at end of file
diff --git a/examples/queries/README.md b/examples/queries/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/paginated_logs.rs b/examples/queries/examples/paginated_logs.rs
similarity index 84%
rename from examples/paginated_logs.rs
rename to examples/queries/examples/paginated_logs.rs
index 9f94f9e0..563b90f1 100644
--- a/examples/paginated_logs.rs
+++ b/examples/queries/examples/paginated_logs.rs
@@ -1,4 +1,10 @@
-use ethers::{abi::AbiDecode, prelude::*, providers::Middleware};
+use ethers::{
+    core::{
+        abi::AbiDecode,
+        types::{BlockNumber, Filter, U256},
+    },
+    providers::{Middleware, Provider, StreamExt, Ws},
+};
 use eyre::Result;
 use std::sync::Arc;
 
@@ -15,7 +21,7 @@ async fn main() -> Result<()> {
     let erc20_transfer_filter =
         Filter::new().from_block(last_block - 10000).event("Transfer(address,address,uint256)");
 
-    let mut stream = client.get_logs_paginated(&erc20_transfer_filter, 10);
+    let mut stream = client.get_logs_paginated(&erc20_transfer_filter, 10).take(5);
 
     while let Some(res) = stream.next().await {
         let log = res?;
diff --git a/examples/uniswapv2.rs b/examples/queries/examples/uniswapv2_pair.rs
similarity index 91%
rename from examples/uniswapv2.rs
rename to examples/queries/examples/uniswapv2_pair.rs
index 8b956112..c9d306da 100644
--- a/examples/uniswapv2.rs
+++ b/examples/queries/examples/uniswapv2_pair.rs
@@ -1,4 +1,8 @@
-use ethers::prelude::*;
+use ethers::{
+    contract::abigen,
+    core::types::Address,
+    providers::{Http, Provider},
+};
 use eyre::Result;
 use std::sync::Arc;
 
diff --git a/examples/subscribe_contract_events.rs b/examples/subscribe_contract_events.rs
deleted file mode 100644
index a6f769f9..00000000
--- a/examples/subscribe_contract_events.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use ethers::prelude::*;
-use eyre::Result;
-use std::sync::Arc;
-
-// Generate the type-safe contract bindings by providing the ABI
-// definition in human readable format
-abigen!(
-    ERC20,
-    r#"[
-        event  Transfer(address indexed src, address indexed dst, uint wad)
-    ]"#,
-);
-
-// In order to run this example you need to include Ws and TLS features
-// Run this example with
-// `cargo run -p ethers --example subscribe_contract_events --features="ws","rustls"`
-#[tokio::main]
-async fn main() -> Result<()> {
-    let client =
-        Provider::<Ws>::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
-            .await?;
-
-    let client = Arc::new(client);
-
-    // WETH Token
-    let address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".parse::<Address>()?;
-    let weth = ERC20::new(address, Arc::clone(&client));
-
-    // Subscribe Transfer events
-    let events = weth.events();
-    let mut stream = events.stream().await?;
-
-    while let Some(Ok(event)) = stream.next().await {
-        println!("src: {:?}, dst: {:?}, wad: {:?}", event.src, event.dst, event.wad);
-    }
-
-    Ok(())
-}
diff --git a/examples/subscriptions/Cargo.toml b/examples/subscriptions/Cargo.toml
new file mode 100644
index 00000000..9dd76878
--- /dev/null
+++ b/examples/subscriptions/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "examples-subscriptions"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
\ No newline at end of file
diff --git a/examples/subscriptions/README.md b/examples/subscriptions/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/subscribe_events_by_type.rs b/examples/subscriptions/examples/subscribe_events_by_type.rs
similarity index 82%
rename from examples/subscribe_events_by_type.rs
rename to examples/subscriptions/examples/subscribe_events_by_type.rs
index 76e394ac..355404a3 100644
--- a/examples/subscribe_events_by_type.rs
+++ b/examples/subscriptions/examples/subscribe_events_by_type.rs
@@ -1,5 +1,10 @@
-use ethers::{contract::Contract, prelude::*};
+use ethers::{
+    contract::{abigen, Contract},
+    core::types::ValueOrArray,
+    providers::{Provider, StreamExt, Ws},
+};
 use std::{error::Error, sync::Arc};
+
 abigen!(
     AggregatorInterface,
     r#"[
@@ -14,10 +19,6 @@ const PRICE_FEED_3: &str = "0xebf67ab8cff336d3f609127e8bbf8bd6dd93cd81";
 /// Subscribe to a typed event stream without requiring a `Contract` instance.
 /// In this example we subscribe Chainlink price feeds and filter out them
 /// by address.
-/// -------------------------------------------------------------------------------
-/// In order to run this example you need to include Ws and TLS features
-/// Run this example with
-/// `cargo run -p ethers --example subscribe_events_by_type --features="ws","rustls"`
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn Error>> {
     let client = get_client().await;
diff --git a/examples/subscribe_logs.rs b/examples/subscriptions/examples/subscribe_logs.rs
similarity index 82%
rename from examples/subscribe_logs.rs
rename to examples/subscriptions/examples/subscribe_logs.rs
index b8cb1891..3ee71259 100644
--- a/examples/subscribe_logs.rs
+++ b/examples/subscriptions/examples/subscribe_logs.rs
@@ -1,9 +1,13 @@
-use ethers::{abi::AbiDecode, prelude::*};
+use ethers::{
+    core::{
+        abi::AbiDecode,
+        types::{Address, BlockNumber, Filter, U256},
+    },
+    providers::{Middleware, Provider, StreamExt, Ws},
+};
 use eyre::Result;
 use std::sync::Arc;
 
-// In order to run this example you need to include Ws and TLS features
-// Run this example with `cargo run -p ethers --example subscribe_logs --features="ws","rustls"`
 #[tokio::main]
 async fn main() -> Result<()> {
     let client =
diff --git a/examples/watch_blocks.rs b/examples/subscriptions/examples/watch_blocks.rs
similarity index 58%
rename from examples/watch_blocks.rs
rename to examples/subscriptions/examples/watch_blocks.rs
index af1385da..38fb90e5 100644
--- a/examples/watch_blocks.rs
+++ b/examples/subscriptions/examples/watch_blocks.rs
@@ -1,12 +1,13 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::providers::{Middleware, Provider, StreamExt, Ws};
+use eyre::Result;
 use std::time::Duration;
 
 #[tokio::main]
-async fn main() -> eyre::Result<()> {
-    let anvil = Anvil::new().block_time(1u64).spawn();
-    let ws = Ws::connect(anvil.ws_endpoint()).await?;
+async fn main() -> Result<()> {
+    let ws_endpoint = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27";
+    let ws = Ws::connect(ws_endpoint).await?;
     let provider = Provider::new(ws).interval(Duration::from_millis(2000));
-    let mut stream = provider.watch_blocks().await?.take(5);
+    let mut stream = provider.watch_blocks().await?.take(1);
     while let Some(block) = stream.next().await {
         let block = provider.get_block(block).await?.unwrap();
         println!(
diff --git a/examples/transactions/Cargo.toml b/examples/transactions/Cargo.toml
new file mode 100644
index 00000000..6bdf9412
--- /dev/null
+++ b/examples/transactions/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "examples-transactions"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "rustls", "ws"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
\ No newline at end of file
diff --git a/examples/transactions/README.md b/examples/transactions/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/call_override.rs b/examples/transactions/examples/call_override.rs
similarity index 90%
rename from examples/call_override.rs
rename to examples/transactions/examples/call_override.rs
index 2237ee1e..b80191e8 100644
--- a/examples/call_override.rs
+++ b/examples/transactions/examples/call_override.rs
@@ -1,14 +1,21 @@
 use ethers::{
-    prelude::*,
-    providers::call_raw::RawCall,
-    utils::{parse_ether, Geth},
+    contract::abigen,
+    core::{
+        types::{Address, TransactionRequest, H256},
+        utils::{parse_ether, Geth},
+    },
+    providers::{
+        call_raw::{self, RawCall},
+        Http, Provider,
+    },
 };
+use eyre::Result;
 use std::sync::Arc;
 
 abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",);
 
 #[tokio::main]
-async fn main() -> eyre::Result<()> {
+async fn main() -> Result<()> {
     let geth = Geth::new().spawn();
     let provider = Provider::<Http>::try_from(geth.endpoint()).unwrap();
     let client = Arc::new(provider);
diff --git a/examples/decode_tx_input.rs b/examples/transactions/examples/decode_input.rs
similarity index 94%
rename from examples/decode_tx_input.rs
rename to examples/transactions/examples/decode_input.rs
index f11b6f1b..0eca8c37 100644
--- a/examples/decode_tx_input.rs
+++ b/examples/transactions/examples/decode_input.rs
@@ -1,4 +1,7 @@
-use ethers::{abi::AbiDecode, prelude::*};
+use ethers::{
+    contract::abigen,
+    core::{abi::AbiDecode, types::Bytes},
+};
 use eyre::Result;
 
 // Abigen creates a SwapExactTokensForTokensCall struct that can be used to decode
diff --git a/examples/ens.rs b/examples/transactions/examples/ens.rs
similarity index 87%
rename from examples/ens.rs
rename to examples/transactions/examples/ens.rs
index 5874d5c9..0606288e 100644
--- a/examples/ens.rs
+++ b/examples/transactions/examples/ens.rs
@@ -1,6 +1,8 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    providers::{Http, Middleware, Provider},
+};
 use eyre::Result;
-use std::convert::TryFrom;
 
 #[tokio::main]
 async fn main() -> Result<()> {
diff --git a/examples/gas_price_usd.rs b/examples/transactions/examples/gas_price_usd.rs
similarity index 92%
rename from examples/gas_price_usd.rs
rename to examples/transactions/examples/gas_price_usd.rs
index 307baf82..eef46a72 100644
--- a/examples/gas_price_usd.rs
+++ b/examples/transactions/examples/gas_price_usd.rs
@@ -1,10 +1,18 @@
-use ethers::{prelude::*, utils::format_units};
 use std::{
     error::Error,
     ops::{Div, Mul},
     sync::Arc,
 };
 
+use ethers::{
+    contract::abigen,
+    core::{
+        types::{Address, I256, U256},
+        utils::format_units,
+    },
+    providers::{Http, Middleware, Provider},
+};
+
 abigen!(
     AggregatorInterface,
     r#"[
diff --git a/examples/remove_liquidity.rs b/examples/transactions/examples/remove_liquidity.rs
similarity index 92%
rename from examples/remove_liquidity.rs
rename to examples/transactions/examples/remove_liquidity.rs
index 0d3e889d..587f6436 100644
--- a/examples/remove_liquidity.rs
+++ b/examples/transactions/examples/remove_liquidity.rs
@@ -1,6 +1,13 @@
-use ethers::prelude::*;
+use std::sync::Arc;
+
+use ethers::{
+    contract::abigen,
+    core::types::{Address, U256},
+    middleware::SignerMiddleware,
+    providers::{Http, Middleware, Provider},
+    signers::{LocalWallet, Signer},
+};
 use eyre::Result;
-use std::{convert::TryFrom, sync::Arc};
 
 abigen!(
     UniswapV2Router,
diff --git a/examples/geth_trace_transaction.rs b/examples/transactions/examples/trace.rs
similarity index 85%
rename from examples/geth_trace_transaction.rs
rename to examples/transactions/examples/trace.rs
index ef20f738..35ea370d 100644
--- a/examples/geth_trace_transaction.rs
+++ b/examples/transactions/examples/trace.rs
@@ -1,4 +1,7 @@
-use ethers::prelude::*;
+use ethers::{
+    core::types::{GethDebugTracingOptions, H256},
+    providers::{Http, Middleware, Provider},
+};
 use eyre::Result;
 use std::str::FromStr;
 
diff --git a/examples/transfer_eth.rs b/examples/transactions/examples/transfer_eth.rs
similarity index 90%
rename from examples/transfer_eth.rs
rename to examples/transactions/examples/transfer_eth.rs
index 3be81cc9..00a4e6fc 100644
--- a/examples/transfer_eth.rs
+++ b/examples/transactions/examples/transfer_eth.rs
@@ -1,4 +1,7 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    providers::{Http, Middleware, Provider},
+};
 use eyre::Result;
 use std::convert::TryFrom;
 
diff --git a/examples/wallets/Cargo.toml b/examples/wallets/Cargo.toml
new file mode 100644
index 00000000..c3112132
--- /dev/null
+++ b/examples/wallets/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "examples-wallets"
+version = "1.0.2"
+authors = ["Andrea Simeoni <andreasimeoni84@gmail.com>"]
+edition = "2021"
+
+[features]
+default = ["ledger", "trezor", "yubi"]
+ledger = []
+trezor = []
+yubi = []
+
+[dev-dependencies]
+ethers = { path = "../..", version = "1.0.0", features = ["abigen", "eip712", "ledger", "rustls", "trezor", "ws", "yubi"] }
+
+eyre = "0.6"
+serde = { version = "1.0.144", features = ["derive"] }
+serde_json = "1.0.64"
+tokio = { version = "1.18", features = ["macros"] }
+
diff --git a/examples/wallets/README.md b/examples/wallets/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/ledger.rs b/examples/wallets/examples/ledger.rs
similarity index 100%
rename from examples/ledger.rs
rename to examples/wallets/examples/ledger.rs
diff --git a/examples/local_signer.rs b/examples/wallets/examples/local_signer.rs
similarity index 84%
rename from examples/local_signer.rs
rename to examples/wallets/examples/local_signer.rs
index 764de4f5..b7193076 100644
--- a/examples/local_signer.rs
+++ b/examples/wallets/examples/local_signer.rs
@@ -1,4 +1,9 @@
-use ethers::{prelude::*, utils::Anvil};
+use ethers::{
+    core::{types::TransactionRequest, utils::Anvil},
+    middleware::SignerMiddleware,
+    providers::{Http, Middleware, Provider},
+    signers::{LocalWallet, Signer},
+};
 use eyre::Result;
 use std::convert::TryFrom;
 
diff --git a/examples/mnemonic.rs b/examples/wallets/examples/mnemonic.rs
similarity index 87%
rename from examples/mnemonic.rs
rename to examples/wallets/examples/mnemonic.rs
index 2c14b69d..b04aaea9 100644
--- a/examples/mnemonic.rs
+++ b/examples/wallets/examples/mnemonic.rs
@@ -1,6 +1,10 @@
-use ethers::signers::{coins_bip39::English, MnemonicBuilder};
+use ethers::{
+    core::rand,
+    signers::{coins_bip39::English, MnemonicBuilder},
+};
+use eyre::Result;
 
-fn main() -> eyre::Result<()> {
+fn main() -> Result<()> {
     let phrase = "work man father plunge mystery proud hollow address reunion sauce theory bonus";
     let index = 0u32;
     let password = "TREZOR123";
diff --git a/examples/permit_hash.rs b/examples/wallets/examples/permit_hash.rs
similarity index 89%
rename from examples/permit_hash.rs
rename to examples/wallets/examples/permit_hash.rs
index b313b3a5..ac28588c 100644
--- a/examples/permit_hash.rs
+++ b/examples/wallets/examples/permit_hash.rs
@@ -1,7 +1,9 @@
 use ethers::{
     contract::{Eip712, EthAbiType},
-    core::types::transaction::eip712::Eip712,
-    types::{Address, U256},
+    core::{
+        types::{transaction::eip712::Eip712, Address, U256},
+        utils::hex,
+    },
 };
 
 // Generate the EIP712 permit hash to sign for a Uniswap V2 pair.
diff --git a/examples/sign.rs b/examples/wallets/examples/sign_message.rs
similarity index 91%
rename from examples/sign.rs
rename to examples/wallets/examples/sign_message.rs
index 6f6b592e..07d50d87 100644
--- a/examples/sign.rs
+++ b/examples/wallets/examples/sign_message.rs
@@ -1,9 +1,9 @@
 // use the eyre crate for easy idiomatic error handling
 use eyre::Result;
 // use the ethers_core rand for rng
-use ethers_core::rand::thread_rng;
+use ethers::core::rand::thread_rng;
 // use the ethers_signers crate to manage LocalWallet and Signer
-use ethers_signers::{LocalWallet, Signer};
+use ethers::signers::{LocalWallet, Signer};
 
 // Use the `tokio::main` macro for using async on the main function
 #[tokio::main]
diff --git a/examples/trezor.rs b/examples/wallets/examples/trezor.rs
similarity index 100%
rename from examples/trezor.rs
rename to examples/wallets/examples/trezor.rs
diff --git a/examples/yubi.rs b/examples/wallets/examples/yubi.rs
similarity index 100%
rename from examples/yubi.rs
rename to examples/wallets/examples/yubi.rs
diff --git a/scripts/examples.sh b/scripts/examples.sh
index ee50628e..90537870 100755
--- a/scripts/examples.sh
+++ b/scripts/examples.sh
@@ -1,25 +1,42 @@
-set -e
+#!/bin/bash
+
+set -e; # Fail fast
 # 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"
-  "remove_liquidity"
+  "examples-contracts:deploy_moonbeam"
+  "examples-providers:ipc"
+  "examples-wallets:ledger"
+  "examples-wallets:trezor"
+  "examples-wallets:yubi"
+  "examples-transactions:remove_liquidity"
 )
 
-# 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")" --features "ethers-solc rustls ws"
+example_crates=$(cargo metadata --format-version 1 | 
+                 jq -c '.workspace_members' | 
+                 jq -r 'map(select(startswith("examples")) |
+                 sub("\\s.*$";"")) | .[]')
+
+for crate in $example_crates; do 
+    # Remove "examples-" prefix from crate name (e.g. examples-contracts => contracts)
+    cratedir="${crate#examples-}"
+    srcdir="examples/$cratedir/examples"
+    # Retrieve all example files in crate:
+    # Transform the absolute path into the filename (e.g. some-path/deploy_anvil.rs => deploy_anvil)
+    example_files=$(find $srcdir -type f -name '*.rs' -exec basename {} \; | sed 's/\.[^.]*$//')
+
+    for file in $example_files; do
+        # Build the example
+        echo "Building example: $crate:$file"
+        cargo build -p $crate --example $file
+
+        # Run the example
+        if [[ "${ignored[*]}" =~ $(basename "$crate:$file") ]]; then
+            echo "skipping: $crate:$file"
+            continue
+        fi
+        echo "running $crate:$file"
+        cargo run -p $crate --example $file
+    done
 done


Test patch:
diff --git a/ethers-middleware/tests/gas_escalator.rs b/ethers-middleware/tests/gas_escalator.rs
index c2bfbad6..66d7d4ff 100644
--- a/ethers-middleware/tests/gas_escalator.rs
+++ b/ethers-middleware/tests/gas_escalator.rs
@@ -13,6 +13,7 @@ use std::time::Duration;
 #[ignore]
 async fn gas_escalator_live() {
     // connect to ropsten for getting bad block times
+    #[allow(deprecated)]
     let provider = ethers_providers::ROPSTEN.ws().await;
     let provider = provider.interval(Duration::from_millis(2000u64));
     let wallet = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"
@@ -34,7 +35,7 @@ async fn gas_escalator_live() {
     provider.send_transaction(tx.clone().nonce(nonce + 2), None).await.unwrap();
 
     // Wait a bunch of seconds and refresh etherscan to see the transactions get bumped
-    tokio::time::sleep(std::time::Duration::from_secs(100)).await;
+    tokio::time::sleep(Duration::from_secs(100)).await;
 
     // TODO: Figure out how to test this behavior properly in a local network. If the gas price was
     // bumped then the tx hash will be different
diff --git a/ethers-middleware/tests/nonce_manager.rs b/ethers-middleware/tests/nonce_manager.rs
index a1c74915..1be27fec 100644
--- a/ethers-middleware/tests/nonce_manager.rs
+++ b/ethers-middleware/tests/nonce_manager.rs
@@ -8,7 +8,7 @@ use std::time::Duration;
 
 #[tokio::test]
 async fn nonce_manager() {
-    let provider = ethers_providers::GOERLI.provider().interval(Duration::from_millis(2000u64));
+    let provider = ethers_providers::GOERLI.provider().interval(Duration::from_millis(2000));
     let chain_id = provider.get_chainid().await.unwrap().as_u64();
 
     let wallet = std::env::var("GOERLI_PRIVATE_KEY")
@@ -44,12 +44,12 @@ async fn nonce_manager() {
     }
 
     // sleep a bit to ensure there's no flakiness in the test
-    std::thread::sleep(std::time::Duration::new(5, 0));
+    tokio::time::sleep(Duration::from_secs(10)).await;
 
     let mut nonces = Vec::with_capacity(num_tx);
     for tx_hash in tx_hashes {
         nonces.push(provider.get_transaction(tx_hash).await.unwrap().unwrap().nonce.as_u64());
     }
 
-    assert_eq!(nonces, (nonce..nonce + (num_tx as u64)).collect::<Vec<_>>())
+    assert_eq!(nonces, (nonce..nonce + num_tx as u64).collect::<Vec<_>>())
 }
diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs
index 9c2176ed..eecd1e65 100644
--- a/ethers-middleware/tests/signer.rs
+++ b/ethers-middleware/tests/signer.rs
@@ -294,7 +294,7 @@ impl TestWallets {
     #[allow(unused)]
     pub async fn fund<T: JsonRpcClient, U: Into<u32>>(&self, provider: &Provider<T>, n: U) {
         let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::<Vec<_>>();
-        // hardcoded funder address private key, goerli
+        // hardcoded funder address private key, GOERLI
         let signer = "39aa18eeb5d12c071e5f19d8e9375a872e90cb1f2fa640384ffd8800a2f3e8f1"
             .parse::<LocalWallet>()
             .unwrap()
diff --git a/examples/ethers-wasm/tests/contract_with_abi.rs b/examples/ethers-wasm/tests/contract_with_abi.rs
index 5530df67..df080b38 100644
--- a/examples/ethers-wasm/tests/contract_with_abi.rs
+++ b/examples/ethers-wasm/tests/contract_with_abi.rs
@@ -16,7 +16,7 @@ wasm_bindgen_test_configure!(run_in_browser);
 // definition in human readable format
 abigen!(
     SimpleContract,
-    "../contract_abi.json",
+    "./abi/contract_abi.json",
     event_derives(serde::Deserialize, serde::Serialize)
 );