Repository: gakonst/ethers-rs
Pull request #1249 (2022-05-11 12:10:26)

fix: unify name of supported chains with strum
Opening up a pr to check differences

Related to foundry issue: https://github.com/foundry-rs/foundry/issues/1564
Related pr: https://github.com/foundry-rs/foundry/pull/1581


Current problem is build error:
```sh
➜  ethers-rs git:(add-strum-for-chain) cargo build
   Compiling ethers-core v0.6.0 (/Users/m1/CLionProjects/ethers-rs/ethers-core)
warning: unused import: `VariantNames`
 --> ethers-core/src/types/chain.rs:6:43
  |
6 | use strum::{EnumString, EnumVariantNames, VariantNames};
  |                                           ^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0119]: conflicting implementations of trait `std::str::FromStr` for type `types::chain::Chain`
   --> ethers-core/src/types/chain.rs:15:64
    |
15  | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, EnumString, EnumVariantNames)]
    |                                                                ^^^^^^^^^^ conflicting implementation for `types::chain::Chain`
...
132 | impl FromStr for Chain {
    | ---------------------- first implementation here
    |
    = note: this error originates in the derive macro `EnumString` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0119`.
warning: `ethers-core` (lib) generated 1 warning
error: could not compile `ethers-core` due to previous error; 1 warning emitted
warning: build failed, waiting for other jobs to finish...
warning: `ethers-core` (lib) generated 1 warning (1 duplicate)
error: build failed
➜  ethers-rs git:(add-strum-for-chain) 
```

<!--
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


mattsse: we already have a `FromStr` impl and can't use `EnumString` here

Patch:
diff --git a/.github/workflows/release-tag-from.js b/.github/workflows/release-tag-from.js
new file mode 100644
index 00000000..b796824e
--- /dev/null
+++ b/.github/workflows/release-tag-from.js
@@ -0,0 +1,37 @@
+const semver = require("semver");
+
+const previousVersion = (currentVersion, releaseType) => {
+  const parsedVersion = semver.parse(currentVersion);
+
+  switch (releaseType) {
+    case "major": {
+      return `v${parsedVersion.major - 1}.0.0`;
+    }
+    case "minor": {
+      return `v${parsedVersion.major}.${parsedVersion.minor - 1}.0`;
+    }
+    case "patch": {
+      return `v${parsedVersion.major}.${parsedVersion.minor}.${
+        parsedVersion.patch - 1
+      }`;
+    }
+    case "alpha": {
+      return `v${parsedVersion.major}.${parsedVersion.minor}.${
+        parsedVersion.patch
+      }-alpha.${parsedVersion.prerelease[1] - 1}`;
+    }
+    case "beta": {
+      return `v${parsedVersion.major}.${parsedVersion.minor}.${
+        parsedVersion.patch
+      }-beta.${parsedVersion.prerelease[1] - 1}`;
+    }
+    case "rc": {
+      return `v${parsedVersion.major}.${parsedVersion.minor}.${
+        parsedVersion.patch
+      }-rc.${parsedVersion.prerelease[1] - 1}`;
+    }
+  }
+};
+
+const [currentVersion, releaseType] = process.argv.slice(-2);
+console.log(previousVersion(currentVersion, releaseType));
diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml
new file mode 100644
index 00000000..6dc4ad0a
--- /dev/null
+++ b/.github/workflows/releases.yml
@@ -0,0 +1,99 @@
+name: Release
+
+on:
+  schedule:
+    - cron: "0 0 * * 0"
+
+  workflow_dispatch:
+    inputs:
+      release_type:
+        type: choice
+        description: Release type
+        options:
+          - major
+          - minor
+          - patch
+          - rc
+          - beta
+          - alpha
+
+permissions:
+  contents: write
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    env:
+      CARGO_TOKEN: ${{ secrets.CARGO_TOKEN }}
+      RELEASE_TYPE: ${{ github.event.inputs.release_type }}
+    steps:
+      - name: Checkout sources
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+      - name: Configure git
+        run: |
+          git config user.name github-actions
+          git config user.email github-actions@github.com
+      - name: Rust stable
+        uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+      - uses: Swatinem/rust-cache@v1
+        with:
+          cache-on-failure: true
+      - name: Install cargo-release
+        uses: actions-rs/install@v0.1
+        with:
+          crate: cargo-release
+          version: latest
+      - name: Cargo login
+        run: |
+          cargo login $CARGO_TOKEN
+      - name: Dry-run cargo release
+        run: |
+          cargo release --workspace ${RELEASE_TYPE:-alpha} --exclude ethers-wasm
+      - name: Publish release
+        run: |
+          cargo release --workspace ${RELEASE_TYPE:-alpha} --exclude ethers-wasm --execute --no-confirm
+      - name: Setup node
+        uses: actions/setup-node@v3
+        with:
+          node-version: 14
+      - run: |
+          npm i semver
+      - name: Install git-cliff
+        uses: actions-rs/install@v0.1
+        with:
+          crate: git-cliff
+          version: latest
+      - name: Publish changelog
+        id: changelog
+        run: |
+          current_version=$(git tag --contains HEAD -l "v*" | head -1)
+          from_version=$(node .github/workflows/release-tag-from.js $current_version $RELEASE_TYPE)
+          echo from $from_version to $current_version
+
+          echo "::set-output name=release_version::$(echo $current_version)"
+
+          if git rev-parse "$from_version" >/dev/null 2>&1; then
+            echo "tag exists, can generate changelog";
+          else
+            echo "tag does not exist, cannot generate changelog, publish github release manually"
+            exit 0
+          fi
+
+          git cliff $from_version..$current_version > GENERATED_CHANGELOG.md
+          cat GENERATED_CHANGELOG.md
+      - name: Create GitHub release
+        id: release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          RELEASE_VERSION: ${{ steps.changelog.outputs.release_version }}
+        with:
+          tag_name: ${{ env.RELEASE_VERSION }}
+          release_name: ${{ env.RELEASE_VERSION }}
+          body_path: GENERATED_CHANGELOG.md
+          prerelease: ${{ env.RELEASE_TYPE == 'alpha' }}
diff --git a/.gitignore b/.gitignore
index 3668f4ec..252650b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /target
 .vscode
 /.envrc
+.idea
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 853c8d1e..b555aa62 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -121,6 +121,8 @@
   `enum BytecodeObject` [#656](https://github.com/gakonst/ethers-rs/pull/656).
 - Nit: remove accidentally doubled double-quotes in an error message
 - Fix when compiler-out metadata is empty and there's no internalType [#1182](https://github.com/gakonst/ethers-rs/pull/1182)
+- Add basic `solc` model checker options.
+  [#1258](https://github.com/gakonst/ethers-rs/pull/1258)
 
 ### 0.6.0
 
@@ -156,6 +158,8 @@
 
 ### Unreleased
 
+- Load previous logs before subscribing to new logs in case fromBlock is set
+  [1264](https://github.com/gakonst/ethers-rs/pull/1264)
 - Add retries to the pending transaction future
   [1221](https://github.com/gakonst/ethers-rs/pull/1221)
 - Add support for basic and bearer authentication in http and non-wasm websockets.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 42525537..233aa78e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -189,7 +189,7 @@ of `ethers-rs` will create an instance of `Provider` is by using
 `Provider::<T>::try_from`, this is how the documentation test is structured.
 
 Lines that start with `/// #` are removed when the documentation is generated.
-They are only there to get the test to run. 
+They are only there to get the test to run.
 
 ### Commits
 
@@ -370,58 +370,28 @@ _Adapted from the [Tokio contributing guide](https://github.com/tokio-rs/tokio/b
 
 Since the ethers-rs project consists of a number of crates, many of which depend on
 each other, releasing new versions to crates.io can involve some complexities.
-When releasing a new version of a crate, follow these steps:
-
-1. **Ensure that the release crate has no path dependencies.** When the HEAD
-   version of a ethers-rs crate requires unreleased changes in another ethers-rs crate,
-   the crates.io dependency on the second crate will be replaced with a path
-   dependency. Crates with path dependencies cannot be published, so before
-   publishing the dependent crate, any path dependencies must also be published.
-   This should be done through a form of depth-first tree traversal:
-
-   1. Starting with the first path dependency in the crate to be released,
-      inspect the `Cargo.toml` for the dependency. If the dependency has any
-      path dependencies of its own, repeat this step with the first such
-      dependency.
-   2. Begin the release process for the path dependency.
-   3. Once the path dependency has been published to crates.io, update the
-      dependent crate to depend on the crates.io version.
-   4. When all path dependencies have been published, the dependent crate may
-      be published.
-
-   To verify that a crate is ready to publish, run:
+We use [`cargo-release`](https://github.com/crate-ci/cargo-release) to manage these
+complexities and the whole release process.
 
-   ```bash
-   bin/publish --dry-run <CRATE NAME> <CRATE VERSION>
-   ```
+When releasing the workspace:
 
-2. **Update Cargo metadata.** After releasing any path dependencies, update the
-   `version` field in `Cargo.toml` to the new version, and the `documentation`
-   field to the docs.rs URL of the new version.
-3. **Update other documentation links.** Update the `#![doc(html_root_url)]`
-   attribute in the crate's `lib.rs` and the "Documentation" link in the crate's
-   `README.md` to point to the docs.rs URL of the new version.
-4. **Update the changelog for the crate.** Each crate in the ethers-rs repository
-   has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that
-   crate since the last release should be added to the changelog. Change
-   descriptions may be taken from the Git history, but should be edited to
-   ensure a consistent format, based on [Keep A Changelog][keep-a-changelog].
-   Other entries in that crate's changelog may also be used for reference.
-5. **Perform a final audit for breaking changes.** Compare the HEAD version of
+1. **Perform a final audit for breaking changes.** Compare the HEAD version of
    crate with the Git tag for the most recent release version. If there are any
    breaking API changes, determine if those changes can be made without breaking
-   existing APIs. If so, resolve those issues. Otherwise, if it is necessary to
-   make a breaking release, update the version numbers to reflect this.
-6. **Open a pull request with your changes.** Once that pull request has been
-   approved by a maintainer and the pull request has been merged, continue to
-   the next step.
-7. **Release the crate.** Run the following command:
+   existing APIs. If so, resolve those issues and make a `minor` change
+   release. Otherwise, if it is necessary to make a breaking release, make a
+   `major` change release.
+2. **Dry run the release** by running `cargo release --workspace <release_type>`
+3. **Update the changelog for the crate.** Changelog for all crates go in
+   [`CHANGELOG.md`](./CHANGELOG.md). Any unreleased changes changelogs should
+   be moved to respective crates released changelogs. Change descriptions
+   may be taken from the Git history, but should be edited to ensure a consistent
+   format, based on [Keep A Changelog][keep-a-changelog]. Other entries in that
+   crate's changelog may also be used for reference.
+4. **Release the crate.** Run the following command:
 
    ```bash
-   bin/publish <NAME OF CRATE> <VERSION>
+   cargo release --workspace <release_type> --execute
    ```
 
-   Your editor and prompt you to edit a message for the tag. Copy the changelog
-   entry for that release version into your editor and close the window.
-
 [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog/blob/master/CHANGELOG.md
diff --git a/Cargo.lock b/Cargo.lock
index c126dbd1..08375caf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1150,7 +1150,7 @@ dependencies = [
 
 [[package]]
 name = "ethers"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "bytes",
  "ethers-addressbook",
@@ -1181,7 +1181,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-contract"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "ethers-contract-abigen",
  "ethers-contract-derive",
@@ -1203,7 +1203,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-contract-abigen"
-version = "0.6.0"
+version = "0.6.3"
 dependencies = [
  "Inflector",
  "cfg-if 1.0.0",
@@ -1226,7 +1226,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-contract-derive"
-version = "0.6.0"
+version = "0.6.3"
 dependencies = [
  "ethers-contract-abigen",
  "ethers-core",
@@ -1239,12 +1239,13 @@ dependencies = [
 
 [[package]]
 name = "ethers-core"
-version = "0.6.0"
+version = "0.6.3"
 dependencies = [
  "arrayvec 0.7.2",
  "bincode",
  "bytes",
  "cargo_metadata",
+ "chrono",
  "convert_case",
  "elliptic-curve",
  "ethabi",
@@ -1259,6 +1260,7 @@ dependencies = [
  "rlp-derive",
  "serde",
  "serde_json",
+ "strum",
  "syn",
  "thiserror",
  "tiny-keccak",
@@ -1267,7 +1269,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-derive-eip712"
-version = "0.2.0"
+version = "0.2.2"
 dependencies = [
  "ethers-contract",
  "ethers-contract-derive",
@@ -1281,7 +1283,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-etherscan"
-version = "0.2.0"
+version = "0.2.2"
 dependencies = [
  "ethers-core",
  "ethers-solc",
@@ -1300,7 +1302,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-middleware"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "ethers-contract",
@@ -1328,7 +1330,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-providers"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "auto_impl",
@@ -1352,7 +1354,7 @@ dependencies = [
  "thiserror",
  "tokio",
  "tokio-tungstenite",
- "tokio-util 0.7.1",
+ "tokio-util 0.7.2",
  "tracing",
  "tracing-futures",
  "url",
@@ -1365,7 +1367,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-signers"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "coins-bip32",
@@ -1400,6 +1402,7 @@ dependencies = [
 name = "ethers-solc"
 version = "0.3.0"
 dependencies = [
+ "cfg-if 1.0.0",
  "colored",
  "criterion",
  "dunce",
@@ -2279,17 +2282,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "num-bigint"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "num-integer"
 version = "0.1.44"
@@ -2300,18 +2292,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-rational"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
-dependencies = [
- "autocfg",
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "num-traits"
 version = "0.2.14"
@@ -2905,9 +2885,9 @@ dependencies = [
 
 [[package]]
 name = "rayon"
-version = "1.5.2"
+version = "1.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221"
+checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
 dependencies = [
  "autocfg",
  "crossbeam-deque",
@@ -3578,15 +3558,13 @@ dependencies = [
 
 [[package]]
 name = "solang-parser"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b2ad9c159bd02219a59368133301e6195fdaa2b5d55c628ccdcd611d49235f"
+checksum = "395b6e1ec5af117bd08f963c7cd80f8efd4eed51c5a332aed42b13e3f9bc860b"
 dependencies = [
+ "itertools",
  "lalrpop",
  "lalrpop-util",
- "num-bigint",
- "num-rational",
- "num-traits",
  "phf",
  "unicode-xid",
 ]
@@ -3641,6 +3619,28 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
+[[package]]
+name = "strum"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
 [[package]]
 name = "subtle"
 version = "2.4.1"
@@ -3690,9 +3690,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.93"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2"
+checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3935,9 +3935,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764"
+checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
 dependencies = [
  "bytes",
  "futures-core",
diff --git a/Cargo.toml b/Cargo.toml
index d63af71e..de7a3807 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ethers"
-version = "0.6.0"
+version = "0.6.2"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 license = "MIT OR Apache-2.0"
 edition = "2021"
@@ -150,4 +150,4 @@ required-features = ["trezor"]
 [[example]]
 name = "yubi"
 path = "examples/yubi.rs"
-required-features = ["yubi"]
\ No newline at end of file
+required-features = ["yubi"]
diff --git a/ethers-addressbook/Cargo.toml b/ethers-addressbook/Cargo.toml
index 73a8d47a..15ec1b64 100644
--- a/ethers-addressbook/Cargo.toml
+++ b/ethers-addressbook/Cargo.toml
@@ -2,10 +2,16 @@
 name = "ethers-addressbook"
 version = "0.1.0"
 edition = "2021"
+license = "MIT OR Apache-2.0"
+authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
+description = "Common Ethereum name to address mappings"
+homepage = "https://docs.rs/ethers"
+repository = "https://github.com/gakonst/ethers-rs"
+keywords = ["ethereum", "web3", "celo", "ethers"]
 
 [dependencies]
 once_cell = "1.10.0"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 
-ethers-core = { path = "../ethers-core" }
+ethers-core = { path = "../ethers-core", version = "^0.6.0" }
diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml
index 554e2971..1f0cd464 100644
--- a/ethers-contract/Cargo.toml
+++ b/ethers-contract/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-contract"
 license = "MIT OR Apache-2.0"
-version = "0.6.0"
+version = "0.6.2"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 description = "Smart contract bindings for the ethers-rs crate"
diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml
index dbe63ad5..759a535a 100644
--- a/ethers-contract/ethers-contract-abigen/Cargo.toml
+++ b/ethers-contract/ethers-contract-abigen/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ethers-contract-abigen"
-version = "0.6.0"
+version = "0.6.3"
 authors = ["Nicholas Rodrigues Lordello <nlordell@gmail.com>", "Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
@@ -41,4 +41,4 @@ rustls = ["reqwest/rustls-tls"]
 
 [dev-dependencies]
 tempfile = "3.2.0"
-ethers-solc = { path = "../../ethers-solc", default-features = false, features = ["project-util"] }
+ethers-solc = { version = "^0.3.0", path = "../../ethers-solc", default-features = false, features = ["project-util"] }
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
index a9ca643a..bf782967 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/methods.rs
@@ -44,57 +44,57 @@ impl Context {
 
     /// Returns all deploy (constructor) implementations
     pub(crate) fn deployment_methods(&self) -> TokenStream {
-        if self.contract_bytecode.is_some() {
-            let ethers_core = ethers_core_crate();
-            let ethers_contract = ethers_contract_crate();
-
-            let abi_name = self.inline_abi_ident();
-            let get_abi = quote! {
-                #abi_name.clone()
-            };
+        if self.contract_bytecode.is_none() {
+            // don't generate deploy if no bytecode
+            return quote! {}
+        }
+        let ethers_core = ethers_core_crate();
+        let ethers_contract = ethers_contract_crate();
 
-            let bytecode_name = self.inline_bytecode_ident();
-            let get_bytecode = quote! {
-                #bytecode_name.clone().into()
-            };
+        let abi_name = self.inline_abi_ident();
+        let get_abi = quote! {
+            #abi_name.clone()
+        };
 
-            let deploy = quote! {
-                /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it.
-                /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction
-                ///
-                /// Notes:
-                /// 1. If there are no constructor arguments, you should pass `()` as the argument.
-                /// 1. The default poll duration is 7 seconds.
-                /// 1. The default number of confirmations is 1 block.
-                ///
-                ///
-                /// # Example
-                ///
-                /// Generate contract bindings with `abigen!` and deploy a new contract instance.
-                ///
-                /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact.
-                ///
-                /// ```ignore
-                /// # async fn deploy<M: ethers::providers::Middleware>(client: ::std::sync::Arc<M>) {
-                ///     abigen!(Greeter,"../greeter.json");
-                ///
-                ///    let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap();
-                ///    let msg = greeter_contract.greet().call().await.unwrap();
-                /// # }
-                /// ```
-                pub fn deploy<T: #ethers_core::abi::Tokenize >(client: ::std::sync::Arc<M>, constructor_args: T) -> Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
-                   let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client);
-                   let deployer = factory.deploy(constructor_args)?;
-                   let deployer = #ethers_contract::ContractDeployer::new(deployer);
-                   Ok(deployer)
-                }
+        let bytecode_name = self.inline_bytecode_ident();
+        let get_bytecode = quote! {
+            #bytecode_name.clone().into()
+        };
 
-            };
+        let deploy = quote! {
+            /// Constructs the general purpose `Deployer` instance based on the provided constructor arguments and sends it.
+            /// Returns a new instance of a deployer that returns an instance of this contract after sending the transaction
+            ///
+            /// Notes:
+            /// 1. If there are no constructor arguments, you should pass `()` as the argument.
+            /// 1. The default poll duration is 7 seconds.
+            /// 1. The default number of confirmations is 1 block.
+            ///
+            ///
+            /// # Example
+            ///
+            /// Generate contract bindings with `abigen!` and deploy a new contract instance.
+            ///
+            /// *Note*: this requires a `bytecode` and `abi` object in the `greeter.json` artifact.
+            ///
+            /// ```ignore
+            /// # async fn deploy<M: ethers::providers::Middleware>(client: ::std::sync::Arc<M>) {
+            ///     abigen!(Greeter,"../greeter.json");
+            ///
+            ///    let greeter_contract = Greeter::deploy(client, "Hello world!".to_string()).unwrap().send().await.unwrap();
+            ///    let msg = greeter_contract.greet().call().await.unwrap();
+            /// # }
+            /// ```
+            pub fn deploy<T: #ethers_core::abi::Tokenize >(client: ::std::sync::Arc<M>, constructor_args: T) -> Result<#ethers_contract::builders::ContractDeployer<M, Self>, #ethers_contract::ContractError<M>> {
+               let factory = #ethers_contract::ContractFactory::new(#get_abi, #get_bytecode, client);
+               let deployer = factory.deploy(constructor_args)?;
+               let deployer = #ethers_contract::ContractDeployer::new(deployer);
+               Ok(deployer)
+            }
 
-            return deploy
-        }
+        };
 
-        quote! {}
+        deploy
     }
 
     /// Expands to the corresponding struct type based on the inputs of the given function
diff --git a/ethers-contract/ethers-contract-abigen/src/rawabi.rs b/ethers-contract/ethers-contract-abigen/src/rawabi.rs
index 501ecc8a..086bec4c 100644
--- a/ethers-contract/ethers-contract-abigen/src/rawabi.rs
+++ b/ethers-contract/ethers-contract-abigen/src/rawabi.rs
@@ -168,10 +168,18 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
                     abi = Some(RawAbi(map.next_value::<Vec<Item>>()?));
                 }
                 "bytecode" | "byteCode" => {
-                    bytecode = map.next_value::<BytecodeObject>().ok().map(|obj| obj.object);
+                    bytecode = map
+                        .next_value::<BytecodeObject>()
+                        .ok()
+                        .map(|obj| obj.object)
+                        .filter(|bytecode| !bytecode.0.is_empty());
                 }
                 "bin" => {
-                    bytecode = map.next_value::<DeserializeBytes>().ok().map(|b| b.0);
+                    bytecode = map
+                        .next_value::<DeserializeBytes>()
+                        .ok()
+                        .map(|b| b.0)
+                        .filter(|b| !b.0.is_empty());
                 }
                 _ => {
                     map.next_value::<serde::de::IgnoredAny>()?;
@@ -185,7 +193,7 @@ impl<'de> Visitor<'de> for AbiObjectVisitor {
 }
 
 impl<'de> Deserialize<'de> for AbiObject {
-    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
@@ -269,4 +277,30 @@ mod tests {
         let artifact = include_str!("../../tests/solidity-contracts/greeter.json");
         assert_has_bytecode(artifact);
     }
+
+    #[test]
+    fn ignores_empty_bytecode() {
+        let abi_str = r#"[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"number","type":"uint64"}],"name":"MyEvent","type":"event"},{"inputs":[],"name":"greet","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#;
+        let s = format!(r#"{{"abi": {}, "bin" : "0x" }}"#, abi_str);
+
+        match serde_json::from_str::<JsonAbi>(&s).unwrap() {
+            JsonAbi::Object(abi) => {
+                assert!(abi.bytecode.is_none());
+            }
+            _ => {
+                panic!("expected abi object")
+            }
+        }
+
+        let s = format!(r#"{{"abi": {}, "bytecode" : {{ "object": "0x" }} }}"#, abi_str);
+
+        match serde_json::from_str::<JsonAbi>(&s).unwrap() {
+            JsonAbi::Object(abi) => {
+                assert!(abi.bytecode.is_none());
+            }
+            _ => {
+                panic!("expected abi object")
+            }
+        }
+    }
 }
diff --git a/ethers-contract/ethers-contract-derive/Cargo.toml b/ethers-contract/ethers-contract-derive/Cargo.toml
index a4311090..251e98f5 100644
--- a/ethers-contract/ethers-contract-derive/Cargo.toml
+++ b/ethers-contract/ethers-contract-derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ethers-contract-derive"
-version = "0.6.0"
+version = "0.6.3"
 authors = ["Nicholas Rodrigues Lordello <nlordell@gmail.com>", "Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 license = "MIT OR Apache-2.0"
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index 4476237f..a788cbcd 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-core"
 license = "MIT OR Apache-2.0"
-version = "0.6.0"
+version = "0.6.3"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 description = "Core structures for the ethers-rs crate"
@@ -23,6 +23,7 @@ rand = { version = "0.8.5", default-features = false }
 tiny-keccak = { version = "2.0.2", default-features = false }
 
 # misc
+chrono = { version = "0.4", default-features = false }
 serde = { version = "1.0.124", default-features = false, features = ["derive"] }
 serde_json = { version = "1.0.64", default-features = false }
 thiserror = { version = "1.0.31", default-features = false }
@@ -30,13 +31,14 @@ bytes = { version = "1.1.0", features = ["serde"] }
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
 once_cell = { version = "1.10.0", optional = true }
 unicode-xid = "0.2.3"
+strum = { version = "0.24", features = ["derive"] }
 
 # macros feature enabled dependencies
 cargo_metadata = { version = "0.14.2", optional = true }
 
 # eip712 feature enabled dependencies
 convert_case = { version = "0.5.0", optional = true }
-syn = { version = "1.0.93", optional = true }
+syn = { version = "1.0.94", optional = true }
 proc-macro2 = { version = "1.0.38", optional = true }
 
 [dev-dependencies]
diff --git a/ethers-core/ethers-derive-eip712/Cargo.toml b/ethers-core/ethers-derive-eip712/Cargo.toml
index 82155397..3194fc6a 100644
--- a/ethers-core/ethers-derive-eip712/Cargo.toml
+++ b/ethers-core/ethers-derive-eip712/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ethers-derive-eip712"
-version = "0.2.0"
+version = "0.2.2"
 edition = "2018"
 description = "Custom derive macro for EIP-712 typed data"
 license = "MIT OR Apache-2.0"
diff --git a/ethers-core/src/types/block.rs b/ethers-core/src/types/block.rs
index c3038dc8..cf05c85a 100644
--- a/ethers-core/src/types/block.rs
+++ b/ethers-core/src/types/block.rs
@@ -1,9 +1,16 @@
 // Taken from <https://github.com/tomusdrw/rust-web3/blob/master/src/types/block.rs>
 use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64};
+use chrono::{DateTime, TimeZone, Utc};
 #[cfg(not(feature = "celo"))]
 use core::cmp::Ordering;
-use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
-use std::str::FromStr;
+
+use serde::{
+    de::{MapAccess, Visitor},
+    ser::SerializeStruct,
+    Deserialize, Deserializer, Serialize, Serializer,
+};
+use std::{fmt::Formatter, str::FromStr};
+use thiserror::Error;
 
 /// The block type returned from RPC calls.
 /// This is generic over a `TX` type which will be either the hash or the full transaction,
@@ -91,6 +98,18 @@ pub struct Block<TX> {
     pub epoch_snark_data: Option<EpochSnarkData>,
 }
 
+/// Error returned by [`Block::time`].
+#[derive(Clone, Copy, Debug, Error)]
+pub enum TimeError {
+    /// Timestamp is zero.
+    #[error("timestamp is zero")]
+    TimestampZero,
+
+    /// Timestamp is too large for [`DateTime<Utc>`].
+    #[error("timestamp is too large")]
+    TimestampOverflow,
+}
+
 // ref <https://eips.ethereum.org/EIPS/eip-1559>
 #[cfg(not(feature = "celo"))]
 pub const ELASTICITY_MULTIPLIER: U256 = U256([2u64, 0, 0, 0]);
@@ -135,6 +154,26 @@ impl<TX> Block<TX> {
             Ordering::Equal => self.base_fee_per_gas,
         }
     }
+
+    /// Parse [`Self::timestamp`] into a [`DateTime<Utc>`].
+    ///
+    /// # Errors
+    ///
+    /// * [`TimeError::TimestampZero`] if the timestamp is zero, or
+    /// * [`TimeError::TimestampOverflow`] if the timestamp is too large to be represented as a
+    ///   [`DateTime<Utc>`].
+    pub fn time(&self) -> Result<DateTime<Utc>, TimeError> {
+        if self.timestamp.is_zero() {
+            return Err(TimeError::TimestampZero)
+        }
+        if self.timestamp.bits() > 63 {
+            return Err(TimeError::TimestampOverflow)
+        }
+        // Casting to i64 is safe because the timestamp is guaranteed to be less than 2^63.
+        // TODO: It would be nice if there was `TryInto<i64> for U256`.
+        let secs = self.timestamp.as_u64() as i64;
+        Ok(Utc.timestamp(secs, 0))
+    }
 }
 
 impl Block<TxHash> {
@@ -412,6 +451,67 @@ impl Serialize for BlockId {
     }
 }
 
+impl<'de> Deserialize<'de> for BlockId {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        struct BlockIdVisitor;
+
+        impl<'de> Visitor<'de> for BlockIdVisitor {
+            type Value = BlockId;
+
+            fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
+                formatter.write_str("Block identifier following EIP-1898")
+            }
+
+            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
+            }
+
+            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
+            where
+                A: MapAccess<'de>,
+            {
+                let mut number = None;
+                let mut hash = None;
+
+                while let Some(key) = map.next_key::<String>()? {
+                    match key.as_str() {
+                        "blockNumber" => {
+                            if number.is_some() || hash.is_some() {
+                                return Err(serde::de::Error::duplicate_field("blockNumber"))
+                            }
+                            number = Some(BlockId::Number(map.next_value::<BlockNumber>()?))
+                        }
+                        "blockHash" => {
+                            if number.is_some() || hash.is_some() {
+                                return Err(serde::de::Error::duplicate_field("blockHash"))
+                            }
+                            hash = Some(BlockId::Hash(map.next_value::<H256>()?))
+                        }
+                        key => {
+                            return Err(serde::de::Error::unknown_field(
+                                key,
+                                &["blockNumber", "blockHash"],
+                            ))
+                        }
+                    }
+                }
+
+                number.or(hash).ok_or_else(|| {
+                    serde::de::Error::custom("Expected `blockNumber` or `blockHash`")
+                })
+            }
+        }
+
+        deserializer.deserialize_any(BlockIdVisitor)
+    }
+}
+
 /// A block Number (or tag - "latest", "earliest", "pending")
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum BlockNumber {
@@ -461,12 +561,21 @@ impl<'de> Deserialize<'de> for BlockNumber {
         D: Deserializer<'de>,
     {
         let s = String::deserialize(deserializer)?.to_lowercase();
-        Ok(match s.as_str() {
+        s.parse().map_err(serde::de::Error::custom)
+    }
+}
+
+impl FromStr for BlockNumber {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let block = match s {
             "latest" => Self::Latest,
             "earliest" => Self::Earliest,
             "pending" => Self::Pending,
-            n => BlockNumber::Number(U64::from_str(n).map_err(serde::de::Error::custom)?),
-        })
+            n => BlockNumber::Number(n.parse::<U64>().map_err(|err| err.to_string())?),
+        };
+        Ok(block)
     }
 }
 
@@ -476,6 +585,62 @@ mod tests {
     use super::*;
     use crate::types::{Transaction, TxHash};
 
+    #[test]
+    fn can_parse_eip1898_block_ids() {
+        let num = serde_json::json!(
+            { "blockNumber": "0x0" }
+        );
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
+
+        let num = serde_json::json!(
+            { "blockNumber": "pending" }
+        );
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Pending));
+
+        let num = serde_json::json!(
+            { "blockNumber": "latest" }
+        );
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Latest));
+
+        let num = serde_json::json!(
+            { "blockNumber": "earliest" }
+        );
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
+
+        let num = serde_json::json!("0x0");
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
+
+        let num = serde_json::json!("pending");
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Pending));
+
+        let num = serde_json::json!("latest");
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Latest));
+
+        let num = serde_json::json!("earliest");
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
+
+        let num = serde_json::json!(
+            { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }
+        );
+        let id = serde_json::from_value::<BlockId>(num).unwrap();
+        assert_eq!(
+            id,
+            BlockId::Hash(
+                "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
+                    .parse()
+                    .unwrap()
+            )
+        );
+    }
+
     #[test]
     fn serde_block_number() {
         for b in &[BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending] {
diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs
index c05c1ed7..1a0c5269 100644
--- a/ethers-core/src/types/chain.rs
+++ b/ethers-core/src/types/chain.rs
@@ -2,23 +2,26 @@ use serde::Deserialize;
 use thiserror::Error;
 
 use core::convert::TryFrom;
-use std::{default, fmt, str::FromStr};
+use std::{convert::TryInto, default, fmt, str::FromStr};
 
 use crate::types::U256;
+use strum::{EnumVariantNames};
 
 #[derive(Debug, Clone, Error)]
 #[error("Failed to parse chain: {0}")]
 pub struct ParseChainError(String);
 
 #[repr(u64)]
-#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, EnumVariantNames)]
 #[serde(rename_all = "snake_case")]
+#[strum(serialize_all = "kebab-case")]
 pub enum Chain {
     Mainnet = 1,
     Ropsten = 3,
     Rinkeby = 4,
     Goerli = 5,
     Kovan = 42,
+    #[strum(serialize = "xdai")]
     XDai = 100,
     Polygon = 137,
     Fantom = 250,
@@ -33,12 +36,14 @@ pub enum Chain {
     Moonriver = 1285,
     Optimism = 10,
     OptimismKovan = 69,
-    BinanceSmartChain = 56,
-    BinanceSmartChainTestnet = 97,
     Arbitrum = 42161,
     ArbitrumTestnet = 421611,
     Cronos = 25,
     CronosTestnet = 338,
+    #[strum(serialize = "bsc")]
+    BinanceSmartChain = 56,
+    #[strum(serialize = "bsc-testnet")]
+    BinanceSmartChainTestnet = 97,
 }
 
 impl fmt::Display for Chain {
@@ -128,6 +133,17 @@ impl TryFrom<u64> for Chain {
     }
 }
 
+impl TryFrom<U256> for Chain {
+    type Error = ParseChainError;
+
+    fn try_from(chain: U256) -> Result<Chain, Self::Error> {
+        if chain.bits() > 64 {
+            return Err(ParseChainError(chain.to_string()))
+        }
+        chain.as_u64().try_into()
+    }
+}
+
 impl FromStr for Chain {
     type Err = ParseChainError;
     fn from_str(chain: &str) -> Result<Self, Self::Err> {
diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs
index b19273ff..994cc8ed 100644
--- a/ethers-core/src/types/mod.rs
+++ b/ethers-core/src/types/mod.rs
@@ -31,7 +31,7 @@ mod bytes;
 pub use self::bytes::{deserialize_bytes, serialize_bytes, Bytes, ParseBytesError};
 
 mod block;
-pub use block::{Block, BlockId, BlockNumber};
+pub use block::{Block, BlockId, BlockNumber, TimeError};
 
 #[cfg(feature = "celo")]
 pub use block::Randomness;
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index 0687567d..e5ee05d0 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "ethers-etherscan"
-version = "0.2.0"
+version = "0.2.2"
 authors = ["Matthias Seitz <matthias.seitz@outlook.de>", "Georgios Konstantopoulos <me@gakonst.com>"]
 license = "MIT OR Apache-2.0"
 edition = "2018"
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index e16d26ff..8da735d7 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-middleware"
 license = "MIT OR Apache-2.0"
-version = "0.6.0"
+version = "0.6.2"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 description = "Middleware implementations for the ethers-rs crate"
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index 90f0069b..abbb3fa7 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-providers"
 license = "MIT OR Apache-2.0"
-version = "0.6.0"
+version = "0.6.2"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 description = "Provider implementations for the ethers-rs crate"
@@ -44,7 +44,7 @@ hashers = "1.0.1"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 # tokio
-tokio-util = { version = "0.7.1", default-features = false, features = ["io"], optional = true }
+tokio-util = { version = "0.7.2", default-features = false, features = ["io"], optional = true }
 tokio = { version = "1.5", default-features = false, optional = true }
 tokio-tungstenite = { version = "0.17.1", default-features = false, features = ["connect"], optional = true }
 
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index 1bdfe200..ef287a53 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -16,9 +16,9 @@ use ethers_core::{
     types::{
         transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
         Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, FeeHistory,
-        Filter, Log, NameOrAddress, Selector, Signature, Trace, TraceFilter, TraceType,
-        Transaction, TransactionReceipt, TransactionRequest, TxHash, TxpoolContent, TxpoolInspect,
-        TxpoolStatus, H256, U256, U64,
+        Filter, FilterBlockOption, Log, NameOrAddress, Selector, Signature, Trace, TraceFilter,
+        TraceType, Transaction, TransactionReceipt, TransactionRequest, TxHash, TxpoolContent,
+        TxpoolInspect, TxpoolStatus, H256, U256, U64,
     },
     utils,
 };
@@ -28,7 +28,9 @@ use thiserror::Error;
 use url::{ParseError, Url};
 
 use futures_util::{lock::Mutex, try_join};
-use std::{convert::TryFrom, fmt::Debug, str::FromStr, sync::Arc, time::Duration};
+use std::{
+    collections::VecDeque, convert::TryFrom, fmt::Debug, str::FromStr, sync::Arc, time::Duration,
+};
 use tracing::trace;
 use tracing_futures::Instrument;
 
@@ -1102,9 +1104,24 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
     where
         P: PubsubClient,
     {
+        let loaded_logs = match filter.block_option {
+            FilterBlockOption::Range { from_block, to_block: _ } => {
+                if from_block.is_none() {
+                    vec![]
+                } else {
+                    self.get_logs(filter).await?
+                }
+            }
+            FilterBlockOption::AtBlockHash(_block_hash) => self.get_logs(filter).await?,
+        };
+        let loaded_logs = VecDeque::from(loaded_logs);
+
         let logs = utils::serialize(&"logs"); // TODO: Make this a static
         let filter = utils::serialize(filter);
-        self.subscribe([logs, filter]).await
+        self.subscribe([logs, filter]).await.map(|mut stream| {
+            stream.set_loaded_elements(loaded_logs);
+            stream
+        })
     }
 
     async fn fee_history<T: Into<U256> + Send + Sync>(
diff --git a/ethers-providers/src/pubsub.rs b/ethers-providers/src/pubsub.rs
index 9aed8252..63339437 100644
--- a/ethers-providers/src/pubsub.rs
+++ b/ethers-providers/src/pubsub.rs
@@ -7,6 +7,7 @@ use pin_project::{pin_project, pinned_drop};
 use serde::de::DeserializeOwned;
 use serde_json::value::RawValue;
 use std::{
+    collections::VecDeque,
     marker::PhantomData,
     pin::Pin,
     task::{Context, Poll},
@@ -31,6 +32,8 @@ pub struct SubscriptionStream<'a, P: PubsubClient, R: DeserializeOwned> {
     /// The subscription's installed id on the ethereum node
     pub id: U256,
 
+    loaded_elements: VecDeque<R>,
+
     provider: &'a Provider<P>,
 
     #[pin]
@@ -54,13 +57,17 @@ where
     pub fn new(id: U256, provider: &'a Provider<P>) -> Result<Self, P::Error> {
         // Call the underlying PubsubClient's subscribe
         let rx = provider.as_ref().subscribe(id)?;
-        Ok(Self { id, provider, rx, ret: PhantomData })
+        Ok(Self { id, provider, rx, ret: PhantomData, loaded_elements: VecDeque::new() })
     }
 
     /// Unsubscribes from the subscription.
     pub async fn unsubscribe(&self) -> Result<bool, crate::ProviderError> {
         self.provider.unsubscribe(self.id).await
     }
+
+    pub fn set_loaded_elements(&mut self, loaded_elements: VecDeque<R>) {
+        self.loaded_elements = loaded_elements;
+    }
 }
 
 // Each subscription item is a serde_json::Value which must be decoded to the
@@ -74,6 +81,11 @@ where
     type Item = R;
 
     fn poll_next(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Self::Item>> {
+        if !self.loaded_elements.is_empty() {
+            let next_element = self.get_mut().loaded_elements.pop_front();
+            return Poll::Ready(next_element)
+        }
+
         let this = self.project();
         match futures_util::ready!(this.rx.poll_next(ctx)) {
             Some(item) => match serde_json::from_str(item.get()) {
diff --git a/ethers-providers/src/transports/common.rs b/ethers-providers/src/transports/common.rs
index 483114cd..ad8fa665 100644
--- a/ethers-providers/src/transports/common.rs
+++ b/ethers-providers/src/transports/common.rs
@@ -212,6 +212,8 @@ impl fmt::Display for Authorization {
 
 #[cfg(test)]
 mod tests {
+    use ethers_core::types::U64;
+
     use super::*;
 
     #[test]
@@ -247,10 +249,28 @@ mod tests {
             }
             _ => panic!("expected `Error` response"),
         }
+
+        let response: Response<'_> =
+            serde_json::from_str(r#"{"jsonrpc":"2.0","result":"0xfa","id":0}"#).unwrap();
+
+        match response {
+            Response::Success { id, result } => {
+                assert_eq!(id, 0);
+                let result: U64 = serde_json::from_str(result.get()).unwrap();
+                assert_eq!(result.as_u64(), 250);
+            }
+            _ => panic!("expected `Success` response"),
+        }
     }
 
     #[test]
     fn ser_request() {
+        let request: Request<()> = Request::new(0, "eth_chainId", ());
+        assert_eq!(
+            &serde_json::to_string(&request).unwrap(),
+            r#"{"id":0,"jsonrpc":"2.0","method":"eth_chainId"}"#
+        );
+
         let request: Request<()> = Request::new(300, "method_name", ());
         assert_eq!(
             &serde_json::to_string(&request).unwrap(),
diff --git a/ethers-providers/src/transports/http.rs b/ethers-providers/src/transports/http.rs
index 56bdf851..242126d8 100644
--- a/ethers-providers/src/transports/http.rs
+++ b/ethers-providers/src/transports/http.rs
@@ -149,7 +149,7 @@ impl Provider {
     /// let provider = Http::new_with_client(url, client);
     /// ```
     pub fn new_with_client(url: impl Into<Url>, client: reqwest::Client) -> Self {
-        Self { id: AtomicU64::new(0), client, url: url.into() }
+        Self { id: AtomicU64::new(1), client, url: url.into() }
     }
 }
 
@@ -164,7 +164,7 @@ impl FromStr for Provider {
 
 impl Clone for Provider {
     fn clone(&self) -> Self {
-        Self { id: AtomicU64::new(0), client: self.client.clone(), url: self.url.clone() }
+        Self { id: AtomicU64::new(1), client: self.client.clone(), url: self.url.clone() }
     }
 }
 
diff --git a/ethers-providers/src/transports/ws.rs b/ethers-providers/src/transports/ws.rs
index 53039b6a..99e4a406 100644
--- a/ethers-providers/src/transports/ws.rs
+++ b/ethers-providers/src/transports/ws.rs
@@ -114,7 +114,7 @@ impl Ws {
         // Spawn the server
         WsServer::new(ws, stream).spawn();
 
-        Self { id: Arc::new(AtomicU64::new(0)), instructions: sink }
+        Self { id: Arc::new(AtomicU64::new(1)), instructions: sink }
     }
 
     /// Returns true if the WS connection is active, false otherwise
diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml
index 25ec3bf3..cbe24383 100644
--- a/ethers-signers/Cargo.toml
+++ b/ethers-signers/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "ethers-signers"
 license = "MIT OR Apache-2.0"
-version = "0.6.0"
+version = "0.6.2"
 authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
 edition = "2018"
 description = "Signer implementations for the ethers-rs crate"
@@ -42,7 +42,7 @@ home = { version = "0.5.3", optional = true }
 
 [dev-dependencies]
 ethers-contract = { version = "^0.6.0", path = "../ethers-contract", features = ["eip712", "abigen"]}
-ethers-derive-eip712 = { version = "0.2.0", path = "../ethers-core/ethers-derive-eip712" }
+ethers-derive-eip712 = { version = "^0.2.0", path = "../ethers-core/ethers-derive-eip712" }
 serde_json = { version = "1.0.64" }
 tracing-subscriber = "0.3.11"
 yubihsm = { version = "0.40.0", features = ["secp256k1", "usb", "mockhsm"] }
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 0fdf5c43..45350789 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -19,7 +19,7 @@ serde_json = "1.0.68"
 serde = { version = "1.0.130", features = ["derive"] }
 semver = { version = "1.0.9", features = ["serde"] }
 walkdir = "2.3.2"
-tokio = { version = "1.15.0", default-features = false, features = ["process", "io-util", "fs", "time"], optional = true }
+tokio = { version = "1.15.0", default-features = false, features = ["rt"] }
 futures-util = { version = "^0.3", optional = true }
 once_cell = "1.10.0"
 regex = "1.5.5"
@@ -35,17 +35,18 @@ tempfile = { version = "3.3.0", optional = true }
 fs_extra = { version = "1.2.0", optional = true }
 sha2 = { version = "0.9.8", default-features = false, optional = true }
 dunce = "1.0.2"
-solang-parser = { default-features = false, version = "0.1.12" }
-rayon = "1.5.2"
+solang-parser = { default-features = false, version = "=0.1.13" }
+rayon = "1.5.3"
 rand = { version = "0.8.5", optional = true }
 path-slash = "0.1.4"
+cfg-if = "1.0.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 home = "0.5.3"
 # SVM is not WASM compatible yet.
 # svm = { package = "svm-rs", default-features = false, version = "0.2.7", optional = true }
-svm = { package = "svm-rs", default-features = false, git = "https://github.com/roynalnaruto/svm-rs", optional = true, features = ["blocking"] }
-svm-builds = { package = "svm-rs-builds", git = "https://github.com/roynalnaruto/svm-rs", optional = true}
+svm = { package = "svm-rs", version = "0.2.9", default-features = false, git = "https://github.com/roynalnaruto/svm-rs", optional = true, features = ["blocking"] }
+svm-builds = { package = "svm-rs-builds", version = "0.1.0", git = "https://github.com/roynalnaruto/svm-rs", optional = true}
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 # NOTE: this enables wasm compatibility for getrandom indirectly
@@ -80,7 +81,7 @@ required-features = ["full", "project-util"]
 
 [features]
 default = ["rustls"]
-async = ["tokio", "futures-util"]
+async = ["tokio/process", "tokio/io-util", "tokio/fs", "tokio/time", "futures-util"]
 full = ["async", "svm-solc"]
 svm-solc = ["svm/blocking", "svm-builds", "sha2"]
 # Utilities for creating and testing project workspaces
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index fba1074b..588d0533 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -113,7 +113,8 @@ impl<'a> From<&'a ConfigurableContractArtifact> for CompactContractBytecodeCow<'
 ///  {
 ///    "abi": [],
 ///    "bytecode": {...},
-///    "deployedBytecode": {...}
+///    "deployedBytecode": {...},
+///    "methodIdentifiers": {...},
 ///    // additional values
 ///  }
 /// ```
@@ -290,13 +291,11 @@ impl ArtifactOutput for ConfigurableArtifacts {
 
             artifact_bytecode = bytecode.map(Into::into);
             artifact_deployed_bytecode = deployed_bytecode.map(Into::into);
+            artifact_method_identifiers = Some(method_identifiers);
 
             if self.additional_values.gas_estimates {
                 artifact_gas_estimates = gas_estimates;
             }
-            if self.additional_values.method_identifiers {
-                artifact_method_identifiers = Some(method_identifiers);
-            }
             if self.additional_values.assembly {
                 artifact_assembly = assembly;
             }
diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs
index f0875643..fa736bc3 100644
--- a/ethers-solc/src/artifact_output/mod.rs
+++ b/ethers-solc/src/artifact_output/mod.rs
@@ -22,6 +22,7 @@ use crate::{
         SourceFile,
     },
     compile::output::{contracts::VersionedContracts, sources::VersionedSourceFiles},
+    sourcemap::{SourceMap, SyntaxError},
 };
 pub use configurable::*;
 
@@ -398,6 +399,22 @@ pub trait Artifact {
     fn get_abi(&self) -> Option<Cow<Abi>> {
         self.get_contract_bytecode().abi
     }
+
+    /// Returns the `sourceMap` of the contract
+    ///
+    /// Returns `None` if no `sourceMap` string was included in the compiler output
+    /// Returns `Some(Err)` if parsing the sourcemap failed
+    fn get_source_map(&self) -> Option<std::result::Result<SourceMap, SyntaxError>> {
+        self.get_bytecode()?.source_map()
+    }
+
+    /// Returns the `sourceMap` as str if it was included in the compiler output
+    fn get_source_map_str(&self) -> Option<Cow<str>> {
+        match self.get_bytecode()? {
+            Cow::Borrowed(code) => code.source_map.as_deref().map(Cow::Borrowed),
+            Cow::Owned(code) => code.source_map.map(Cow::Owned),
+        }
+    }
 }
 
 impl<T> Artifact for T
diff --git a/ethers-solc/src/artifacts/ast.rs b/ethers-solc/src/artifacts/ast.rs
index 91f3a6aa..236c3bdb 100644
--- a/ethers-solc/src/artifacts/ast.rs
+++ b/ethers-solc/src/artifacts/ast.rs
@@ -27,7 +27,8 @@ pub struct Ast {
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Node {
     /// The node ID.
-    pub id: usize,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub id: Option<usize>,
 
     /// The node type.
     #[serde(rename = "nodeType")]
diff --git a/ethers-solc/src/artifacts/bytecode.rs b/ethers-solc/src/artifacts/bytecode.rs
index 6025669c..8f6837ce 100644
--- a/ethers-solc/src/artifacts/bytecode.rs
+++ b/ethers-solc/src/artifacts/bytecode.rs
@@ -46,6 +46,13 @@ pub struct CompactBytecode {
 }
 
 impl CompactBytecode {
+    /// Returns the parsed source map
+    ///
+    /// See also <https://docs.soliditylang.org/en/v0.8.10/internals/source_mappings.html>
+    pub fn source_map(&self) -> Option<Result<SourceMap, SyntaxError>> {
+        self.source_map.as_ref().map(|map| sourcemap::parse(map))
+    }
+
     /// Tries to link the bytecode object with the `file` and `library` name.
     /// Replaces all library placeholders with the given address.
     ///
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index 3001716d..497e008a 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -123,6 +123,9 @@ impl CompilerInput {
                 // <https://github.com/ethereum/solidity/releases/tag/v0.8.10>
                 debug.debug_info.clear();
             }
+
+            // 0.8.10 is the earliest version that has all model checker options.
+            self.settings.model_checker = None;
         }
 
         self
@@ -241,6 +244,9 @@ pub struct Settings {
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub remappings: Vec<Remapping>,
     pub optimizer: Optimizer,
+    /// Model Checker options.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub model_checker: Option<ModelCheckerSettings>,
     /// Metadata settings
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub metadata: Option<SettingsMetadata>,
@@ -386,6 +392,7 @@ impl Default for Settings {
             debug: None,
             libraries: Default::default(),
             remappings: Default::default(),
+            model_checker: None,
         }
         .with_ast()
     }
@@ -837,6 +844,112 @@ pub struct MetadataSource {
     pub license: Option<String>,
 }
 
+/// Model checker settings for solc
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub struct ModelCheckerSettings {
+    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
+    pub contracts: BTreeMap<String, Vec<String>>,
+    #[serde(
+        default,
+        with = "serde_helpers::display_from_str_opt",
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub engine: Option<ModelCheckerEngine>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub timeout: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub targets: Option<Vec<ModelCheckerTarget>>,
+}
+
+/// Which model checker engine to run.
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub enum ModelCheckerEngine {
+    Default,
+    All,
+    BMC,
+    CHC,
+}
+
+impl fmt::Display for ModelCheckerEngine {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let string = match self {
+            ModelCheckerEngine::Default => "none",
+            ModelCheckerEngine::All => "all",
+            ModelCheckerEngine::BMC => "bmc",
+            ModelCheckerEngine::CHC => "chc",
+        };
+        write!(f, "{}", string)
+    }
+}
+
+impl FromStr for ModelCheckerEngine {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "none" => Ok(ModelCheckerEngine::Default),
+            "all" => Ok(ModelCheckerEngine::All),
+            "bmc" => Ok(ModelCheckerEngine::BMC),
+            "chc" => Ok(ModelCheckerEngine::CHC),
+            s => Err(format!("Unknown model checker engine: {}", s)),
+        }
+    }
+}
+
+impl Default for ModelCheckerEngine {
+    fn default() -> Self {
+        ModelCheckerEngine::Default
+    }
+}
+
+/// Which model checker targets to check.
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum ModelCheckerTarget {
+    Assert,
+    Underflow,
+    Overflow,
+    DivByZero,
+    ConstantCondition,
+    PopEmptyArray,
+    OutOfBounds,
+    Balance,
+}
+
+impl fmt::Display for ModelCheckerTarget {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let string = match self {
+            ModelCheckerTarget::Assert => "assert",
+            ModelCheckerTarget::Underflow => "underflow",
+            ModelCheckerTarget::Overflow => "overflow",
+            ModelCheckerTarget::DivByZero => "divByZero",
+            ModelCheckerTarget::ConstantCondition => "constantCondition",
+            ModelCheckerTarget::PopEmptyArray => "popEmptyArray",
+            ModelCheckerTarget::OutOfBounds => "outOfBounds",
+            ModelCheckerTarget::Balance => "balance",
+        };
+        write!(f, "{}", string)
+    }
+}
+
+impl FromStr for ModelCheckerTarget {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "assert" => Ok(ModelCheckerTarget::Assert),
+            "underflow" => Ok(ModelCheckerTarget::Underflow),
+            "overflow" => Ok(ModelCheckerTarget::Overflow),
+            "divByZero" => Ok(ModelCheckerTarget::DivByZero),
+            "constantCondition" => Ok(ModelCheckerTarget::ConstantCondition),
+            "popEmptyArray" => Ok(ModelCheckerTarget::PopEmptyArray),
+            "outOfBounds" => Ok(ModelCheckerTarget::OutOfBounds),
+            "balance" => Ok(ModelCheckerTarget::Balance),
+            s => Err(format!("Unknown model checker target: {}", s)),
+        }
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Compiler {
     pub version: String,
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index 384f9c75..92e3b9a9 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -371,9 +371,21 @@ impl Solc {
     /// Blocking version of `Self::install`
     #[cfg(all(feature = "svm-solc"))]
     pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> {
+        use crate::utils::RuntimeOrHandle;
+
         tracing::trace!("blocking installing solc version \"{}\"", version);
         crate::report::solc_installation_start(version);
-        match svm::blocking_install(version) {
+        // the async version `svm::install` is used instead of `svm::blocking_intsall`
+        // because the underlying `reqwest::blocking::Client` does not behave well
+        // in tokio rt. see https://github.com/seanmonstar/reqwest/issues/1017
+        cfg_if::cfg_if! {
+            if #[cfg(target_arch = "wasm32")] {
+                let installation = svm::blocking_install(version);
+            } else {
+                let installation = RuntimeOrHandle::new().block_on(svm::install(version));
+            }
+        };
+        match installation {
             Ok(path) => {
                 crate::report::solc_installation_success(version);
                 Ok(Solc::new(path))
@@ -723,6 +735,7 @@ mod tests {
         let other = solc().async_compile(&serde_json::json!(input)).await.unwrap();
         assert_eq!(out, other);
     }
+
     #[cfg(feature = "async")]
     #[tokio::test]
     async fn async_solc_compile_works2() {
@@ -799,6 +812,15 @@ mod tests {
         assert_eq!(res.solc, expected);
     }
 
+    #[test]
+    #[cfg(feature = "svm-solc")]
+    fn can_install_solc_in_tokio_rt() {
+        let version = Version::from_str("0.8.6").unwrap();
+        let rt = tokio::runtime::Runtime::new().unwrap();
+        let result = rt.block_on(async { Solc::blocking_install(&version) });
+        assert!(result.is_ok());
+    }
+
     #[test]
     fn does_not_find_not_installed_version() {
         let ver = "1.1.1";
diff --git a/ethers-solc/src/compile/output/contracts.rs b/ethers-solc/src/compile/output/contracts.rs
index f90f931b..2945cd19 100644
--- a/ethers-solc/src/compile/output/contracts.rs
+++ b/ethers-solc/src/compile/output/contracts.rs
@@ -4,7 +4,7 @@ use crate::artifacts::{
 };
 use semver::Version;
 use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, path::Path};
 
 /// file -> [(contract name  -> Contract + solc version)]
 #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
@@ -129,6 +129,36 @@ impl VersionedContracts {
                 .flat_map(|(name, c)| c.into_iter().map(move |c| (name.clone(), c.contract)))
         })
     }
+
+    /// Sets the contract's file paths to `root` adjoined to `self.file`.
+    pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
+        let root = root.as_ref();
+        self.0 = std::mem::take(&mut self.0)
+            .into_iter()
+            .map(|(contract_path, contracts)| {
+                (root.join(contract_path).to_string_lossy().to_string(), contracts)
+            })
+            .collect();
+        self
+    }
+
+    /// Removes `base` from all contract paths
+    pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
+        let base = base.as_ref();
+        self.0 = std::mem::take(&mut self.0)
+            .into_iter()
+            .map(|(contract_path, contracts)| {
+                let p = Path::new(&contract_path);
+                (
+                    p.strip_prefix(base)
+                        .map(|p| p.to_string_lossy().to_string())
+                        .unwrap_or(contract_path),
+                    contracts,
+                )
+            })
+            .collect();
+        self
+    }
 }
 
 impl AsRef<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
diff --git a/ethers-solc/src/compile/output/mod.rs b/ethers-solc/src/compile/output/mod.rs
index 57d121bb..7730df88 100644
--- a/ethers-solc/src/compile/output/mod.rs
+++ b/ethers-solc/src/compile/output/mod.rs
@@ -92,10 +92,9 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
     ///
     /// # Example
     ///
-    /// Make all artifact files relative tot the project's root directory
+    /// Make all artifact files relative to the project's root directory
     ///
     /// ```no_run
-    /// use ethers_solc::artifacts::contract::CompactContractBytecode;
     /// use ethers_solc::Project;
     ///
     /// let project = Project::builder().build().unwrap();
@@ -105,6 +104,7 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
         let base = base.as_ref();
         self.cached_artifacts = self.cached_artifacts.into_stripped_file_prefixes(base);
         self.compiled_artifacts = self.compiled_artifacts.into_stripped_file_prefixes(base);
+        self.compiler_output.strip_prefix_all(base);
         self
     }
 
@@ -360,6 +360,36 @@ impl AggregatedCompilerOutput {
     pub fn split(self) -> (VersionedSourceFiles, VersionedContracts) {
         (self.sources, self.contracts)
     }
+
+    /// Strips the given prefix from all file paths to make them relative to the given
+    /// `base` argument.
+    ///
+    /// Convenience method for [Self::strip_prefix_all()] that consumes the type.
+    ///
+    /// # Example
+    ///
+    /// Make all sources and contracts relative to the project's root directory
+    ///
+    /// ```no_run
+    /// use ethers_solc::Project;
+    ///
+    /// let project = Project::builder().build().unwrap();
+    /// let output = project.compile().unwrap().output().with_stripped_file_prefixes(project.root());
+    /// ```
+    pub fn with_stripped_file_prefixes(mut self, base: impl AsRef<Path>) -> Self {
+        let base = base.as_ref();
+        self.contracts.strip_prefix_all(base);
+        self.sources.strip_prefix_all(base);
+        self
+    }
+
+    /// Removes `base` from all contract paths
+    pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
+        let base = base.as_ref();
+        self.contracts.strip_prefix_all(base);
+        self.sources.strip_prefix_all(base);
+        self
+    }
 }
 
 /// Helper type to implement display for solc errors
diff --git a/ethers-solc/src/compile/output/sources.rs b/ethers-solc/src/compile/output/sources.rs
index 811690fd..4a9a556e 100644
--- a/ethers-solc/src/compile/output/sources.rs
+++ b/ethers-solc/src/compile/output/sources.rs
@@ -1,7 +1,7 @@
 use crate::SourceFile;
 use semver::Version;
 use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, path::Path};
 
 /// (source_file path  -> `SourceFile` + solc version)
 #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
@@ -223,6 +223,36 @@ impl VersionedSourceFiles {
                 .map(move |source| (path.clone(), source.source_file, source.version))
         })
     }
+
+    /// Sets the sources' file paths to `root` adjoined to `self.file`.
+    pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
+        let root = root.as_ref();
+        self.0 = std::mem::take(&mut self.0)
+            .into_iter()
+            .map(|(file_path, sources)| {
+                (root.join(file_path).to_string_lossy().to_string(), sources)
+            })
+            .collect();
+        self
+    }
+
+    /// Removes `base` from all source file paths
+    pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
+        let base = base.as_ref();
+        self.0 = std::mem::take(&mut self.0)
+            .into_iter()
+            .map(|(file_path, sources)| {
+                let p = Path::new(&file_path);
+                (
+                    p.strip_prefix(base)
+                        .map(|p| p.to_string_lossy().to_string())
+                        .unwrap_or(file_path),
+                    sources,
+                )
+            })
+            .collect();
+        self
+    }
 }
 
 impl AsRef<BTreeMap<String, Vec<VersionedSourceFile>>> for VersionedSourceFiles {
diff --git a/ethers-solc/src/resolver/parse.rs b/ethers-solc/src/resolver/parse.rs
index 4bcd8807..642b5104 100644
--- a/ethers-solc/src/resolver/parse.rs
+++ b/ethers-solc/src/resolver/parse.rs
@@ -49,7 +49,7 @@ impl SolData {
             Ok((units, _)) => {
                 for unit in units.0 {
                     match unit {
-                        SourceUnitPart::PragmaDirective(loc, _, pragma, value) => {
+                        SourceUnitPart::PragmaDirective(loc, pragma, value) => {
                             if pragma.name == "solidity" {
                                 // we're only interested in the solidity version pragma
                                 version = Some(SolDataUnit::from_loc(value.string.clone(), loc));
@@ -60,7 +60,7 @@ impl SolData {
                                     Some(SolDataUnit::from_loc(value.string.clone(), loc));
                             }
                         }
-                        SourceUnitPart::ImportDirective(_, import) => {
+                        SourceUnitPart::ImportDirective(import) => {
                             let (import, ids, loc) = match import {
                                 Import::Plain(s, l) => (s, vec![], l),
                                 Import::GlobalSymbol(s, i, l) => (s, vec![(i, None)], l),
diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs
index 48e2cb59..a5bd3e11 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -330,6 +330,40 @@ pub(crate) fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &st
     p
 }
 
+#[cfg(not(target_arch = "wasm32"))]
+use tokio::runtime::{Handle, Runtime};
+
+#[cfg(not(target_arch = "wasm32"))]
+#[derive(Debug)]
+pub enum RuntimeOrHandle {
+    Runtime(Runtime),
+    Handle(Handle),
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl Default for RuntimeOrHandle {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl RuntimeOrHandle {
+    pub fn new() -> RuntimeOrHandle {
+        match Handle::try_current() {
+            Ok(handle) => RuntimeOrHandle::Handle(handle),
+            Err(_) => RuntimeOrHandle::Runtime(Runtime::new().expect("Failed to start runtime")),
+        }
+    }
+
+    pub fn block_on<F: std::future::Future>(&self, f: F) -> F::Output {
+        match &self {
+            RuntimeOrHandle::Runtime(runtime) => runtime.block_on(f),
+            RuntimeOrHandle::Handle(handle) => tokio::task::block_in_place(|| handle.block_on(f)),
+        }
+    }
+}
+
 /// Creates a new named tempdir
 #[cfg(any(test, feature = "project-util"))]
 pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> {
@@ -415,7 +449,7 @@ mod tests {
         let (unit, _) = solang_parser::parse(s, 0).unwrap();
         assert_eq!(unit.0.len(), 1);
         match unit.0[0] {
-            SourceUnitPart::ImportDirective(_, _) => {}
+            SourceUnitPart::ImportDirective(_) => {}
             _ => unreachable!("failed to parse import"),
         }
         let imports: Vec<_> = find_import_paths(s).map(|m| m.as_str()).collect();
diff --git a/examples/ethers-wasm/Cargo.toml b/examples/ethers-wasm/Cargo.toml
index 35a4ab52..ccc36499 100644
--- a/examples/ethers-wasm/Cargo.toml
+++ b/examples/ethers-wasm/Cargo.toml
@@ -19,7 +19,7 @@ crate-type = ["cdylib", "rlib"]
 default = ["console_error_panic_hook"]
 
 [dependencies]
-ethers = { path = "../..", features = ["abigen", "legacy", "ws"] }
+ethers = { path = "../..", version = "0.6.0", features = ["abigen", "legacy", "ws"] }
 serde_derive = "1.0.126"
 wasm-bindgen-futures = "0.4.30"
 serde_json = "1.0.64"
diff --git a/examples/subscribe_logs.rs b/examples/subscribe_logs.rs
new file mode 100644
index 00000000..18216db9
--- /dev/null
+++ b/examples/subscribe_logs.rs
@@ -0,0 +1,34 @@
+use ethers::{abi::AbiDecode, prelude::*, utils::keccak256};
+use eyre::Result;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    let client =
+        Provider::<Ws>::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
+            .await?;
+    let client = Arc::new(client);
+
+    let last_block = client.get_block(BlockNumber::Latest).await?.unwrap().number.unwrap();
+    println!("last_block: {}", last_block);
+
+    let erc20_transfer_filter = Filter::new()
+        .from_block(last_block - 25)
+        .topic0(ValueOrArray::Value(H256::from(keccak256("Transfer(address,address,uint256)"))));
+
+    let mut stream = client.subscribe_logs(&erc20_transfer_filter).await?;
+
+    while let Some(log) = stream.next().await {
+        println!(
+            "block: {:?}, tx: {:?}, token: {:?}, from: {:?}, to: {:?}, amount: {:?}",
+            log.block_number,
+            log.transaction_hash,
+            log.address,
+            Address::from(log.topics[1]),
+            Address::from(log.topics[2]),
+            U256::decode(log.data)
+        );
+    }
+
+    Ok(())
+}
diff --git a/release.toml b/release.toml
new file mode 100644
index 00000000..d55f4e95
--- /dev/null
+++ b/release.toml
@@ -0,0 +1,2 @@
+consolidate-commits = false
+consolidate-pushes = true


Test patch:
diff --git a/ethers-solc/test-data/model-checker-sample/Assert.sol b/ethers-solc/test-data/model-checker-sample/Assert.sol
new file mode 100644
index 00000000..4d6656e0
--- /dev/null
+++ b/ethers-solc/test-data/model-checker-sample/Assert.sol
@@ -0,0 +1,5 @@
+contract Assert {
+	function f(uint x) public pure {
+		assert(x > 0);
+	}
+}
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 40f9aaa4..21698fd7 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -9,7 +9,7 @@ use std::{
 
 use ethers_core::types::Address;
 use ethers_solc::{
-    artifacts::{BytecodeHash, Libraries},
+    artifacts::{BytecodeHash, Libraries, ModelCheckerEngine::CHC, ModelCheckerSettings},
     cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
     project_util::*,
     remappings::Remapping,
@@ -17,6 +17,7 @@ use ethers_solc::{
     ProjectPathsConfig, Solc, TestFileFilter,
 };
 use pretty_assertions::assert_eq;
+use semver::Version;
 
 #[allow(unused)]
 fn init_tracing() {
@@ -1310,3 +1311,72 @@ fn can_compile_std_json_input() {
         assert!(out.sources.contains_key("lib/ds-test/src/test.sol"));
     }
 }
+
+#[test]
+fn can_compile_model_checker_sample() {
+    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/model-checker-sample");
+    let paths = ProjectPathsConfig::builder().sources(root);
+
+    let mut project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
+    project.project_mut().solc_config.settings.model_checker = Some(ModelCheckerSettings {
+        contracts: BTreeMap::new(),
+        engine: Some(CHC),
+        targets: None,
+        timeout: Some(10000),
+    });
+    let compiled = project.compile().unwrap();
+
+    assert!(compiled.find("Assert").is_some());
+    assert!(!compiled.has_compiler_errors());
+    assert!(compiled.has_compiler_warnings());
+}
+
+fn remove_solc_if_exists(version: &Version) {
+    match Solc::find_svm_installed_version(version.to_string()).unwrap() {
+        Some(_) => svm::remove_version(&version).expect("failed to remove version"),
+        None => {}
+    };
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn can_install_solc_and_compile_version() {
+    let project = TempProject::dapptools().unwrap();
+    let version = Version::new(0, 8, 10);
+
+    project
+        .add_source(
+            "Contract",
+            format!(
+                r#"
+pragma solidity {};
+contract Contract {{ }}
+"#,
+                version.to_string()
+            ),
+        )
+        .unwrap();
+
+    remove_solc_if_exists(&version);
+
+    let compiled = project.compile().unwrap();
+    assert!(!compiled.has_compiler_errors());
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn can_install_solc_and_compile_std_json_input_async() {
+    let tmp = TempProject::dapptools_init().unwrap();
+    tmp.assert_no_errors();
+    let source = tmp.list_source_files().into_iter().find(|p| p.ends_with("Dapp.t.sol")).unwrap();
+    let input = tmp.project().standard_json_input(source).unwrap();
+    let solc = &tmp.project().solc;
+
+    assert!(input.settings.remappings.contains(&"ds-test/=lib/ds-test/src/".parse().unwrap()));
+    let input: CompilerInput = input.into();
+    assert!(input.sources.contains_key(Path::new("lib/ds-test/src/test.sol")));
+
+    remove_solc_if_exists(&solc.version().expect("failed to get version"));
+
+    let out = solc.async_compile(&input).await.unwrap();
+    assert!(!out.has_error());
+    assert!(out.sources.contains_key("lib/ds-test/src/test.sol"));
+}