Repository: gakonst/ethers-rs
Pull request #1258 (2022-05-13 16:47:49)

Add basic solc model checker options
Foundry's https://github.com/foundry-rs/foundry/pull/1602 depends on this PR.

This PR adds [Solidity's model checker](https://docs.soliditylang.org/en/latest/smtchecker.html#) options to ethers-solc's settings.

## Motivation

Mainly to allow Foundry users to have direct access to the model checker.
The default case of these options do not enable the model checker since it's opt-in, so the PR shouldn't disturb users who are not interested in the model checker.

## Solution

Add data structures that represent a basic subset of the [tool's options](https://docs.soliditylang.org/en/latest/smtchecker.html#smtchecker-options-and-tuning).
The 4 added options are the most basic to enable the tool. Other options such as `divModNoSlacks` and `invariants` can be added later on.

## PR Checklist

- [ ] Added Tests
- [ ] Added Documentation
- [ ] Updated the changelog


leonardoalt: > these are supported by all solc versions, right? otherwise we'd need to update the input based on the targeted version:

Oh, good point! Definitely not supported by all versions. How about making it available only for compiler versions >=0.8.something? I'll find out the earliest version that has all of these options.
leonardoalt: - 0.8.4 is the earliest for the options in this PR
- 0.8.7 adds other options that are not here yet
- 0.8.10 same as /\

I'd suggest enabling it only for >=0.8.10.
mattsse: > * 0.8.4 is the earliest for the options in this PR
> * 0.8.7 adds other options that are not here yet
> * 0.8.10 same as /\
> 
> I'd suggest enabling it only for >=0.8.10.

sounds good! so we can simply reuse the existing `PRE_V0_8_10` 

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/CHANGELOG.md b/CHANGELOG.md
index 853c8d1e..bfe4d21b 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
 
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 9fb231c3..8fdbc201 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,7 +1239,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-core"
-version = "0.6.0"
+version = "0.6.3"
 dependencies = [
  "arrayvec 0.7.2",
  "bincode",
@@ -1268,7 +1268,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-derive-eip712"
-version = "0.2.0"
+version = "0.2.2"
 dependencies = [
  "ethers-contract",
  "ethers-contract-derive",
@@ -1282,7 +1282,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-etherscan"
-version = "0.2.0"
+version = "0.2.2"
 dependencies = [
  "ethers-core",
  "ethers-solc",
@@ -1301,7 +1301,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-middleware"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "ethers-contract",
@@ -1329,7 +1329,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-providers"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "auto_impl",
@@ -1366,7 +1366,7 @@ dependencies = [
 
 [[package]]
 name = "ethers-signers"
-version = "0.6.0"
+version = "0.6.2"
 dependencies = [
  "async-trait",
  "coins-bip32",
@@ -2280,17 +2280,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"
@@ -2301,18 +2290,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"
@@ -3579,15 +3556,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",
 ]
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-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 2d3d61af..1cc5ac7a 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"
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 61c9f16b..cf05c85a 100644
--- a/ethers-core/src/types/block.rs
+++ b/ethers-core/src/types/block.rs
@@ -3,8 +3,13 @@ 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.
@@ -446,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 {
@@ -495,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)
     }
 }
 
@@ -510,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-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..4d2c6adc 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"
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..a184ea32 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -35,7 +35,7 @@ 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" }
+solang-parser = { default-features = false, version = "=0.1.13" }
 rayon = "1.5.2"
 rand = { version = "0.8.5", optional = true }
 path-slash = "0.1.4"
@@ -44,8 +44,8 @@ path-slash = "0.1.4"
 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
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/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..825107ea 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -415,7 +415,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/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..5bc568e7 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,
@@ -1310,3 +1310,22 @@ 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());
+}