Repository: gakonst/ethers-rs
Pull request #1448 (2022-07-04 03:26:26)

chore(deps): bump tracing-subscriber from 0.3.11 to 0.3.14
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.11 to 0.3.14.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/tokio-rs/tracing/releases">tracing-subscriber's releases</a>.</em></p>
<blockquote>
<h2>tracing-subscriber 0.3.14</h2>
<p>This release fixes multiple filtering bugs in the <code>Layer</code> implementations for
<code>Option&lt;impl Layer&gt;</code> and <code>Vec&lt;impl Layer&gt;</code>.</p>
<h3>Fixed</h3>
<ul>
<li><strong>layer</strong>: <code>Layer::event_enabled</code> implementation for <code>Option&lt;impl Layer&lt;S&gt;&gt;</code>
returning <code>false</code> when the <code>Option</code> is <code>None</code>, disabling all events globally
(<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2193">#2193</a>)</li>
<li><strong>layer</strong>: <code>Layer::max_level_hint</code> implementation for <code>Option&lt;impl Layer&lt;S&gt;&gt;</code>
incorrectly disabling max level filtering when the option is <code>None</code> (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2195">#2195</a>)</li>
<li><strong>layer</strong>: <code>Layer::max_level_hint</code> implementation for <code>Vec&lt;impl Layer&lt;S&gt;&gt;</code>
returning <code>LevelFilter::ERROR</code> rather than <code>LevelFilter::OFF</code> when the <code>Vec</code>
is empty (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2195">#2195</a>)</li>
</ul>
<p>Thanks to <a href="https://github.com/CAD97"><code>@​CAD97</code></a> and <a href="https://github.com/guswynn"><code>@​guswynn</code></a> for contributing to this release!</p>
<p><a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2193">#2193</a>: <a href="https://github-redirect.dependabot.com/tokio-rs/tracing/pull/2193">tokio-rs/tracing#2193</a>
<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2195">#2195</a>: <a href="https://github-redirect.dependabot.com/tokio-rs/tracing/pull/2195">tokio-rs/tracing#2195</a></p>
<h2>tracing-subscriber 0.3.13</h2>
<p>This release of <code>tracing-subscriber</code> fixes a compilation failure due to an
incorrect <code>tracing-core</code> dependency that was introduced in v0.3.12.</p>
<h3>Changed</h3>
<ul>
<li><strong>tracing_core</strong>: Updated minimum dependency version to 0.1.28 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2190">#2190</a>)</li>
</ul>
<p><a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2190">#2190</a>: <a href="https://github-redirect.dependabot.com/tokio-rs/tracing/pull/2190">tokio-rs/tracing#2190</a></p>
<h2>tracing-subscriber 0.3.12</h2>
<p>This release of <code>tracing-subscriber</code> adds a new <code>Layer::event_enabled</code> method,
which allows <code>Layer</code>s to filter events <em>after</em> their field values are recorded;
a <code>Filter</code> implementation for <code>reload::Layer</code>, to make using <code>reload</code> with
per-layer filtering more ergonomic, and additional inherent method downcasting
APIs for the <code>Layered</code> type. In addition, it includes dependency updates, and
minor fixes for documentation and feature flagging.</p>
<h3>Added</h3>
<ul>
<li><strong>layer</strong>: <code>Layer::event_enabled</code> method, which can be implemented to filter
events based on their field values (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2008">#2008</a>)</li>
<li><strong>reload</strong>: <code>Filter</code> implementation for <code>reload::Layer</code> (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2159">#2159</a>)</li>
<li><strong>layer</strong>: <code>Layered::downcast_ref</code> and <code>Layered::is</code> inherent methods
(<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2160">#2160</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li><strong>parking_lot</strong>: Updated dependency on <code>parking_lot</code> to 0.13.0 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2143">#2143</a>)</li>
<li>Replaced <code>lazy_static</code> dependency with <code>once_cell</code> (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2147">#2147</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/tokio-rs/tracing/commit/9638587480b3f3c59a858ec78a9ea94103b746b8"><code>9638587</code></a> subscriber: prepare to release v0.3.14 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2201">#2201</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/2659ccf72fa3b2790279fb15d51b1266e7d21bf5"><code>2659ccf</code></a> opentelemetry: fix exception fields defaults in docs (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2200">#2200</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/4a3f2991bfe8ed65abd7622273b3d3055e4dea3e"><code>4a3f299</code></a> attributes: ensure <code>res</code> and <code>err</code> events inherit <code>target</code> (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2184">#2184</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/e0b3f79ed9bc2bdf6125aa3d57919c26139a5a7e"><code>e0b3f79</code></a> subscriber: fix <code>max_level_hint</code> for empty <code>Option</code>/<code>Vec</code> <code>Layer</code> impls (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2195">#2195</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/0012a4131adb0713ce2cf49b16fd1bb47c9ba22c"><code>0012a41</code></a> subscriber: <code>\&lt;None as Layer&gt;::event_enabled</code> should be <code>true</code> (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2193">#2193</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/2fdbaaa1e8532ee197df2642d810034f1181c8f1"><code>2fdbaaa</code></a> subscriber: prepare to release v0.3.13 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2192">#2192</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/7f62097b42dc745ad0e66f91b0dd3ada22ac91c2"><code>7f62097</code></a> subscriber: fix clippy lints (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2191">#2191</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/b4680b1fd0419d522c853fb06eee6809b07aafbc"><code>b4680b1</code></a> subscriber: update minimum <code>tracing-core</code> dep to 0.1.28 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2190">#2190</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/d86d2a4b7e87dbe1c2efaf39162b68381a085f9b"><code>d86d2a4</code></a> subscriber: prepare to release v0.3.12 (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2187">#2187</a>)</li>
<li><a href="https://github.com/tokio-rs/tracing/commit/ebea0e4cc0838ea1438cbcbb693eb135606169be"><code>ebea0e4</code></a> tracing: reorganize benchmarks for comparability (<a href="https://github-redirect.dependabot.com/tokio-rs/tracing/issues/2178">#2178</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.11...tracing-subscriber-0.3.14">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tracing-subscriber&package-manager=cargo&previous-version=0.3.11&new-version=0.3.14)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 092dfb3b..8ad96f05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Use correct, new transaction type for `typool_content` RPC endpoint [#1501](https://github.com/gakonst/ethers-rs/pull/1501)
 - Fix the default config for generated `BuildInfo` [#1458](https://github.com/gakonst/ethers-rs/pull/1458)
 - Allow configuration of the output directory of the generated `BuildInfo` [#1433](https://github.com/gakonst/ethers-rs/pull/1433)
 - capture unknown fields in `Block` and `Transaction` type via new `OtherFields` type [#1423](https://github.com/gakonst/ethers-rs/pull/1423)
@@ -90,6 +91,10 @@
   [#1030](https://github.com/gakonst/ethers-rs/pull/1030).
 - Generate correct bindings of struct's field names that are reserved words
   [#989](https://github.com/gakonst/ethers-rs/pull/989).
+- Generate correct binding module names that are reserved words
+  [#1498](https://github.com/gakonst/ethers-rs/pull/1498). Note: this changes
+  generated module names to snake case. For example, `MyContract` is now
+  `my_contract` rather than `mycontract_mod`.
 
 ### 0.6.0
 
@@ -106,7 +111,7 @@
 
 - Make `ethers-solc` optional dependency of `ethers`, needs `ethers-solc` feature to activate
   [#1463](https://github.com/gakonst/ethers-rs/pull/1463)
-- Add `rawMetadata:String` field to configurable contract output 
+- Add `rawMetadata:String` field to configurable contract output
   [#1365](https://github.com/gakonst/ethers-rs/pull/1365)
 - Use relative source paths and `solc --base-path`
   [#1317](https://github.com/gakonst/ethers-rs/pull/1317)
@@ -237,6 +242,8 @@
 
 ### Unreleased
 
+- Add `Event::stream_with_meta` and `Event::subscribe_with_meta`
+  [#1483](https://github.com/gakonst/ethers-rs/pull/1483)
 - Added tx builder methods to `ContractFactory`
   [#1289](https://github.com/gakonst/ethers-rs/pull/1289)
 - Relax Clone requirements when Arc<Middleware> is used
diff --git a/Cargo.lock b/Cargo.lock
index f010b507..6e8cff3f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -365,9 +365,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e"
 dependencies = [
  "serde",
 ]
@@ -433,6 +433,12 @@ dependencies = [
  "rustc_version",
 ]
 
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
 [[package]]
 name = "cc"
 version = "1.0.72"
@@ -723,12 +729,12 @@ dependencies = [
 
 [[package]]
 name = "criterion"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
+checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
 dependencies = [
  "atty",
- "cast",
+ "cast 0.3.0",
  "clap 2.34.0",
  "criterion-plot",
  "csv",
@@ -755,7 +761,7 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
 dependencies = [
- "cast",
+ "cast 0.2.7",
  "itertools",
 ]
 
@@ -3736,9 +3742,9 @@ dependencies = [
 
 [[package]]
 name = "svm-rs-builds"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac21094817e231b55e691c925262bb0658894d0760ea1163e981885247b7cd02"
+checksum = "3f01d0d76a6e080f1c6fc1a00f5095fa90704f32797f9189fbcb67a10e675329"
 dependencies = [
  "build_const",
  "hex",
@@ -3971,9 +3977,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-tungstenite"
-version = "0.17.1"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
+checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
 dependencies = [
  "futures-util",
  "log",
@@ -4041,9 +4047,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.27"
+version = "0.1.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
+checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
 dependencies = [
  "once_cell",
  "valuable",
@@ -4072,13 +4078,13 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.11"
+version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
+checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59"
 dependencies = [
  "ansi_term",
- "lazy_static",
  "matchers",
+ "once_cell",
  "regex",
  "sharded-slab",
  "smallvec",
@@ -4111,9 +4117,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
 
 [[package]]
 name = "tungstenite"
-version = "0.17.2"
+version = "0.17.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
+checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
 dependencies = [
  "base64 0.13.0",
  "byteorder",
diff --git a/Cargo.toml b/Cargo.toml
index fa3a71eb..378a6815 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -102,7 +102,7 @@ serde = { version = "1.0.124", features = ["derive"] }
 serde_json = "1.0.64"
 tokio = { version = "1.18", features = ["macros", "rt-multi-thread"] }
 hex = "0.4.3"
-bytes = "1.1.0"
+bytes = "1.2.0"
 
 
 # profile for the wasm example
diff --git a/README.md b/README.md
index 5e76b95a..5a8232a5 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,45 @@ in the transactions which are fetched over JSON-RPC.
 - [ ] FFI Bindings (see note)
 - [ ] CLI for common operations
 
+### Websockets
+
+Websockets support is turned on via the feature-flag `ws`:
+
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ws"] }
+```
+
+### Interprocess Communication (IPC)
+
+IPC support is turned on via the feature-flag `ipc`:
+
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ipc"] }
+```
+
+### HTTP Secure (HTTPS)
+
+If you are looking to connect to a HTTPS endpoint, then you need to enable the `rustls` or `openssl` feature.
+feature-flags.
+
+To enable `rustls`:
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["rustls"] }
+```
+
+To enable `openssl`:
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["openssl"] }
+```
+
 ## Note on WASM and FFI bindings
 
 You should be able to build a wasm app that uses ethers-rs (see the [example](./examples/ethers-wasm) for reference). If ethers fails to
diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml
index d011b1e3..8cf9b72e 100644
--- a/ethers-contract/ethers-contract-abigen/Cargo.toml
+++ b/ethers-contract/ethers-contract-abigen/Cargo.toml
@@ -41,4 +41,4 @@ rustls = ["reqwest/rustls-tls"]
 
 [dev-dependencies]
 tempfile = "3.2.0"
-ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util"] }
+ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util", "svm-solc"] }
diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs
index 1b33ff86..df31a3ec 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract.rs
@@ -101,8 +101,7 @@ impl Context {
     /// Expands the whole rust contract
     pub fn expand(&self) -> Result<ExpandedContract> {
         let name = &self.contract_ident;
-        let name_mod =
-            util::ident(&format!("{}_mod", self.contract_ident.to_string().to_lowercase()));
+        let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
         let abi_name = self.inline_abi_ident();
 
         // 0. Imports
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
index a4bfb4b7..0a7335c3 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
@@ -42,7 +42,7 @@ pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
 
     let abi_parse = if !cx.human_readable {
         quote! {
-            pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| serde_json::from_str(#abi)
+            pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::utils::__serde_json::from_str(#abi)
                                               .expect("invalid abi"));
         }
     } else {
diff --git a/ethers-contract/ethers-contract-abigen/src/lib.rs b/ethers-contract/ethers-contract-abigen/src/lib.rs
index a9b7e76e..a538df71 100644
--- a/ethers-contract/ethers-contract-abigen/src/lib.rs
+++ b/ethers-contract/ethers-contract-abigen/src/lib.rs
@@ -29,7 +29,6 @@ pub use util::parse_address;
 
 use crate::contract::ExpandedContract;
 use eyre::Result;
-use inflector::Inflector;
 use proc_macro2::TokenStream;
 use std::{collections::HashMap, fs::File, io::Write, path::Path};
 
@@ -233,7 +232,7 @@ impl ContractBindings {
 
     /// Generate the default module name (snake case of the contract name)
     pub fn module_name(&self) -> String {
-        self.name.to_snake_case()
+        util::safe_module_name(&self.name)
     }
 
     /// Generate the default filename of the module
diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs
index 594a5797..0023f583 100644
--- a/ethers-contract/ethers-contract-abigen/src/multi.rs
+++ b/ethers-contract/ethers-contract-abigen/src/multi.rs
@@ -559,7 +559,7 @@ serde_json = "1.0.79"
     /// Append module declarations to the `lib.rs` or `mod.rs`
     fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
         let mut mod_names: BTreeSet<_> =
-            self.bindings.keys().map(|name| name.to_snake_case()).collect();
+            self.bindings.keys().map(|name| util::safe_module_name(name)).collect();
         if let Some(ref shared) = self.shared_types {
             mod_names.insert(shared.name.to_snake_case());
         }
@@ -1093,4 +1093,63 @@ contract Greeter2 {
         assert!(content.contains("pub struct Inner"));
         assert!(content.contains("pub struct Stuff"));
     }
+
+    #[test]
+    fn can_sanitize_reserved_words() {
+        let tmp = TempProject::dapptools().unwrap();
+
+        tmp.add_source(
+            "ReservedWords",
+            r#"
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.0;
+
+contract Mod {
+    function greet() public pure returns (uint256) {
+        return 1;
+    }
+}
+
+// from a gnosis contract
+contract Enum {
+    enum Operation {Call, DelegateCall}
+}
+"#,
+        )
+        .unwrap();
+
+        let _ = tmp.compile().unwrap();
+
+        let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
+        let bindings = gen.build().unwrap();
+        let single_file_dir = tmp.root().join("single_bindings");
+        bindings.write_to_module(&single_file_dir, true).unwrap();
+
+        let single_file_mod = single_file_dir.join("mod.rs");
+        assert!(single_file_mod.exists());
+        let content = fs::read_to_string(&single_file_mod).unwrap();
+        assert!(content.contains("pub mod mod_ {"));
+        assert!(content.contains("pub mod enum_ {"));
+
+        // multiple files
+        let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
+        let bindings = gen.build().unwrap();
+        let multi_file_dir = tmp.root().join("multi_bindings");
+        bindings.write_to_module(&multi_file_dir, false).unwrap();
+        let multi_file_mod = multi_file_dir.join("mod.rs");
+        assert!(multi_file_mod.exists());
+        let content = fs::read_to_string(&multi_file_mod).unwrap();
+        assert!(content.contains("pub mod enum_;"));
+        assert!(content.contains("pub mod mod_;"));
+
+        let enum_ = multi_file_dir.join("enum_.rs");
+        assert!(enum_.exists());
+        let content = fs::read_to_string(&enum_).unwrap();
+        assert!(content.contains("pub mod enum_ {"));
+
+        let mod_ = multi_file_dir.join("mod_.rs");
+        assert!(mod_.exists());
+        let content = fs::read_to_string(&mod_).unwrap();
+        assert!(content.contains("pub mod mod_ {"));
+    }
 }
diff --git a/ethers-contract/ethers-contract-abigen/src/util.rs b/ethers-contract/ethers-contract-abigen/src/util.rs
index d9c812e0..33936e09 100644
--- a/ethers-contract/ethers-contract-abigen/src/util.rs
+++ b/ethers-contract/ethers-contract-abigen/src/util.rs
@@ -40,6 +40,12 @@ fn safe_identifier_name(name: String) -> String {
     }
 }
 
+/// converts invalid rust module names to valid ones
+pub fn safe_module_name(name: &str) -> String {
+    // handle reserve words used in contracts (eg Enum is a gnosis contract)
+    safe_ident(&safe_snake_case(name)).to_string()
+}
+
 /// Expands an identifier as snakecase and preserve any leading or trailing underscores
 pub fn safe_snake_case_ident(name: &str) -> Ident {
     let i = name.to_snake_case();
@@ -228,4 +234,12 @@ mod tests {
             Address::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
         assert_eq!(parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(), expected);
     }
+
+    #[test]
+    fn test_safe_module_name() {
+        assert_eq!(safe_module_name("Valid"), "valid");
+        assert_eq!(safe_module_name("Enum"), "enum_");
+        assert_eq!(safe_module_name("Mod"), "mod_");
+        assert_eq!(safe_module_name("2Two"), "_2_two");
+    }
 }
diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs
index bc393e70..01fc23f0 100644
--- a/ethers-contract/src/event.rs
+++ b/ethers-contract/src/event.rs
@@ -164,6 +164,26 @@ where
             self.provider.watch(&self.filter).await.map_err(ContractError::MiddlewareError)?;
         Ok(EventStream::new(filter.id, filter, Box::new(move |log| self.parse_log(log))))
     }
+
+    /// As [`Self::stream`], but does not discard [`Log`] metadata.
+    pub async fn stream_with_meta(
+        &'a self,
+    ) -> Result<
+        // Wraps the FilterWatcher with a mapping to the event
+        EventStream<'a, FilterWatcher<'a, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
+        ContractError<M>,
+    > {
+        let filter =
+            self.provider.watch(&self.filter).await.map_err(ContractError::MiddlewareError)?;
+        Ok(EventStream::new(
+            filter.id,
+            filter,
+            Box::new(move |log| {
+                let meta = LogMeta::from(&log);
+                Ok((self.parse_log(log)?, meta))
+            }),
+        ))
+    }
 }
 
 impl<'a, M, D> Event<'a, M, D>
@@ -189,6 +209,28 @@ where
             .map_err(ContractError::MiddlewareError)?;
         Ok(EventStream::new(filter.id, filter, Box::new(move |log| self.parse_log(log))))
     }
+
+    pub async fn subscribe_with_meta(
+        &'a self,
+    ) -> Result<
+        // Wraps the SubscriptionStream with a mapping to the event
+        EventStream<'a, SubscriptionStream<'a, M::Provider, Log>, (D, LogMeta), ContractError<M>>,
+        ContractError<M>,
+    > {
+        let filter = self
+            .provider
+            .subscribe_logs(&self.filter)
+            .await
+            .map_err(ContractError::MiddlewareError)?;
+        Ok(EventStream::new(
+            filter.id,
+            filter,
+            Box::new(move |log| {
+                let meta = LogMeta::from(&log);
+                Ok((self.parse_log(log)?, meta))
+            }),
+        ))
+    }
 }
 
 impl<M, D> Event<'_, M, D>
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index f174ab76..68abf175 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -28,7 +28,7 @@ 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", default-features = false }
-bytes = { version = "1.1.0", features = ["serde"] }
+bytes = { version = "1.2.0", features = ["serde"] }
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
 once_cell = { version = "1.13.0", optional = true }
 unicode-xid = "0.2.3"
diff --git a/ethers-core/ethers-derive-eip712/src/lib.rs b/ethers-core/ethers-derive-eip712/src/lib.rs
index 3b3c8fd7..a64bb351 100644
--- a/ethers-core/ethers-derive-eip712/src/lib.rs
+++ b/ethers-core/ethers-derive-eip712/src/lib.rs
@@ -61,7 +61,6 @@
 //! determine if there is a nested eip712 struct. However, this work is not yet complete.
 
 #![deny(rustdoc::broken_intra_doc_links)]
-#![deny(unused_crate_dependencies)]
 
 use std::convert::TryFrom;
 
@@ -121,7 +120,7 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
 
             fn type_hash() -> Result<[u8; 32], Self::Error> {
                 use std::convert::TryFrom;
-                let decoded = hex::decode(#type_hash)?;
+                let decoded = #ethers_core::utils::hex::decode(#type_hash)?;
                 let byte_array: [u8; 32] = <[u8; 32]>::try_from(&decoded[..])?;
                 Ok(byte_array)
             }
@@ -129,13 +128,13 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
             // Return the pre-computed domain separator from compile time;
             fn domain_separator(&self) -> Result<[u8; 32], Self::Error> {
                 use std::convert::TryFrom;
-                let decoded = hex::decode(#domain_separator)?;
+                let decoded = #ethers_core::utils::hex::decode(#domain_separator)?;
                 let byte_array: [u8; 32] = <[u8; 32]>::try_from(&decoded[..])?;
                 Ok(byte_array)
             }
 
             fn domain(&self) -> Result<#ethers_core::types::transaction::eip712::EIP712Domain, Self::Error> {
-                let domain: #ethers_core::types::transaction::eip712::EIP712Domain = serde_json::from_str(#domain_str)?;
+                let domain: #ethers_core::types::transaction::eip712::EIP712Domain = # ethers_core::utils::__serde_json::from_str(#domain_str)?;
 
                 Ok(domain)
             }
diff --git a/ethers-core/src/abi/human_readable/lexer.rs b/ethers-core/src/abi/human_readable/lexer.rs
index 5e807fb9..3c9c6f24 100644
--- a/ethers-core/src/abi/human_readable/lexer.rs
+++ b/ethers-core/src/abi/human_readable/lexer.rs
@@ -1,4 +1,4 @@
-use ethabi::{Event, EventParam, Function, Param, ParamType, StateMutability};
+use ethabi::{Constructor, Event, EventParam, Function, Param, ParamType, StateMutability};
 use std::{fmt, iter::Peekable, str::CharIndices};
 use unicode_xid::UnicodeXID;
 
@@ -320,6 +320,18 @@ impl<'input> HumanReadableParser<'input> {
         Self::new(input).take_function()
     }
 
+    /// Parses a [Constructor] from a human readable form
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use ethers_core::abi::HumanReadableParser;
+    /// let mut constructor = HumanReadableParser::parse_constructor("constructor(address author, string oldValue, string newValue)").unwrap();
+    /// ```
+    pub fn parse_constructor(input: &'input str) -> Result<Constructor, LexerError> {
+        Self::new(input).take_constructor()
+    }
+
     /// Parses an [Event] from a human readable form
     ///
     /// # Example
@@ -332,6 +344,15 @@ impl<'input> HumanReadableParser<'input> {
         Self::new(input).take_event()
     }
 
+    /// Returns the next `Constructor` and consumes the underlying tokens
+    pub fn take_constructor(&mut self) -> Result<Constructor, LexerError> {
+        self.take_next_exact(Token::Constructor)?;
+        self.take_open_parenthesis()?;
+        let inputs = self.take_function_params()?;
+        self.take_close_parenthesis()?;
+        Ok(Constructor { inputs })
+    }
+    /// Returns the next `Function` and consumes the underlying tokens
     pub fn take_function(&mut self) -> Result<Function, LexerError> {
         let name = self.take_identifier(Token::Function)?;
 
@@ -812,6 +833,30 @@ pub enum DataLocation {
 mod tests {
     use super::*;
 
+    #[test]
+    fn parse_constructor() {
+        let f = Constructor {
+            inputs: vec![
+                Param { name: "author".to_string(), kind: ParamType::Address, internal_type: None },
+                Param {
+                    name: "oldValue".to_string(),
+                    kind: ParamType::String,
+                    internal_type: None,
+                },
+                Param {
+                    name: "newValue".to_string(),
+                    kind: ParamType::String,
+                    internal_type: None,
+                },
+            ],
+        };
+        let parsed = HumanReadableParser::parse_constructor(
+            "constructor(address author, string oldValue, string newValue)",
+        )
+        .unwrap();
+        assert_eq!(f, parsed);
+    }
+
     #[test]
     fn test_parse_function() {
         #[allow(deprecated)]
diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs
index 3a14caff..1831db54 100644
--- a/ethers-core/src/types/chain.rs
+++ b/ethers-core/src/types/chain.rs
@@ -18,6 +18,7 @@ pub struct ParseChainError(String);
 #[strum(serialize_all = "kebab-case")]
 pub enum Chain {
     Mainnet = 1,
+    Morden = 2,
     Ropsten = 3,
     Rinkeby = 4,
     Goerli = 5,
@@ -61,6 +62,7 @@ impl fmt::Display for Chain {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         let chain = match self {
             Chain::Mainnet => "mainnet",
+            Chain::Morden => "morden",
             Chain::Ropsten => "ropsten",
             Chain::Rinkeby => "rinkeby",
             Chain::Goerli => "goerli",
@@ -125,6 +127,7 @@ impl TryFrom<u64> for Chain {
     fn try_from(chain: u64) -> Result<Chain, Self::Error> {
         Ok(match chain {
             1 => Chain::Mainnet,
+            2 => Chain::Morden,
             3 => Chain::Ropsten,
             4 => Chain::Rinkeby,
             5 => Chain::Goerli,
@@ -180,6 +183,7 @@ impl FromStr for Chain {
     fn from_str(chain: &str) -> Result<Self, Self::Err> {
         Ok(match chain {
             "mainnet" => Chain::Mainnet,
+            "morden" => Chain::Morden,
             "ropsten" => Chain::Ropsten,
             "rinkeby" => Chain::Rinkeby,
             "goerli" => Chain::Goerli,
diff --git a/ethers-core/src/types/transaction/eip712.rs b/ethers-core/src/types/transaction/eip712.rs
index 61d7edcf..5bb7c978 100644
--- a/ethers-core/src/types/transaction/eip712.rs
+++ b/ethers-core/src/types/transaction/eip712.rs
@@ -9,7 +9,7 @@ use syn::{
 use crate::{
     abi,
     abi::{ParamType, Token},
-    types::{Address, H160, U256},
+    types::{Address, U256},
     utils::keccak256,
 };
 
@@ -196,9 +196,14 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
     type Error = TokenStream;
     fn try_from(input: &syn::DeriveInput) -> Result<EIP712Domain, Self::Error> {
         let mut domain = EIP712Domain::default();
+        let mut domain_name = None;
+        let mut domain_version = None;
+        let mut chain_id = None;
+        let mut verifying_contract = None;
+
         let mut found_eip712_attribute = false;
 
-        for attribute in input.attrs.iter() {
+        'attribute_search: for attribute in input.attrs.iter() {
             if let AttrStyle::Outer = attribute.style {
                 if let Ok(syn::Meta::List(meta)) = attribute.parse_meta() {
                     if meta.path.is_ident("eip712") {
@@ -219,7 +224,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                         match ident.to_string().as_ref() {
                                             "name" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.name != String::default() {
+                                                    if domain_name.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain name already specified",
@@ -227,7 +232,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.name = lit_str.value();
+                                                    domain_name = Some(lit_str.value());
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -239,7 +244,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "version" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.version != String::default() {
+                                                    if domain_version.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain version already specified",
@@ -247,7 +252,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.version = lit_str.value();
+                                                    domain_version = Some(lit_str.value());
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -259,7 +264,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "chain_id" => match meta.lit {
                                                 syn::Lit::Int(ref lit_int) => {
-                                                    if domain.chain_id != U256::default() {
+                                                    if chain_id.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain chain_id already specified",
@@ -267,7 +272,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.chain_id = U256::from(
+                                                    chain_id = Some(U256::from(
                                                         lit_int.base10_parse::<u64>().map_err(
                                                             |_| {
                                                                 Error::new(
@@ -277,7 +282,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                                 .to_compile_error()
                                                             },
                                                         )?,
-                                                    );
+                                                    ));
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -289,8 +294,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "verifying_contract" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.verifying_contract != H160::default()
-                                                    {
+                                                    if verifying_contract.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain verifying_contract already specified",
@@ -298,13 +302,13 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error());
                                                     }
 
-                                                    domain.verifying_contract = lit_str.value().parse().map_err(|_| {
+                                                    verifying_contract = Some(lit_str.value().parse().map_err(|_| {
                                                             Error::new(
                                                                 meta.path.span(),
                                                                 "failed to parse verifying contract into Address",
                                                             )
                                                             .to_compile_error()
-                                                        })?;
+                                                        })?);
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -316,7 +320,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "salt" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.salt != Option::None {
+                                                    if domain.salt.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain salt already specified",
@@ -365,36 +369,41 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                             }
                         }
 
-                        if domain.name == String::default() {
-                            return Err(Error::new(
+                        domain.name = domain_name.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'name'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.version == String::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.version = domain_version.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'version'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.chain_id == U256::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.chain_id = chain_id.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'chain_id'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.verifying_contract == H160::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.verifying_contract = verifying_contract.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'verifying_contract'"
                                     .to_string(),
                             )
-                            .to_compile_error())
-                        }
+                            .to_compile_error()
+                        })?;
                     }
+
+                    break 'attribute_search
                 }
             }
         }
diff --git a/ethers-core/src/types/txpool.rs b/ethers-core/src/types/txpool.rs
index 0b023cf4..a9462089 100644
--- a/ethers-core/src/types/txpool.rs
+++ b/ethers-core/src/types/txpool.rs
@@ -1,5 +1,4 @@
-use crate::types::{Address, TransactionRequest as TxpoolTransaction, U256, U64};
-
+use crate::types::{Address, Bytes, H256, U256, U64};
 use serde::{
     de::{self, Deserializer, Visitor},
     Deserialize, Serialize,
@@ -108,7 +107,7 @@ impl Serialize for TxpoolInspectSummary {
 /// as the ones that are being scheduled for future execution only.
 ///
 /// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details
-#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct TxpoolContent {
     /// pending tx
     pub pending: BTreeMap<Address, BTreeMap<String, TxpoolTransaction>>,
@@ -116,6 +115,23 @@ pub struct TxpoolContent {
     pub queued: BTreeMap<Address, BTreeMap<String, TxpoolTransaction>>,
 }
 
+/// Represents the Transaction object as returned by `txpool_content`
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TxpoolTransaction {
+    pub block_hash: Option<H256>,
+    pub block_number: Option<U64>,
+    pub from: Option<Address>,
+    pub gas: Option<U256>,
+    pub gas_price: Option<U256>,
+    pub hash: H256,
+    pub input: Bytes,
+    pub nonce: U256,
+    pub to: Option<Address>,
+    pub transaction_index: Option<U64>,
+    pub value: U256,
+}
+
 /// Transaction Pool Inspect
 ///
 /// The inspect inspection property can be queried to list a textual summary
@@ -261,7 +277,11 @@ mod tests {
   }
 }"#;
         let deserialized: TxpoolContent = serde_json::from_str(txpool_content_json).unwrap();
-        let serialized: String = serde_json::to_string(&deserialized).unwrap();
+        let serialized: String = serde_json::to_string_pretty(&deserialized).unwrap();
+
+        let origin: serde_json::Value = serde_json::from_str(txpool_content_json).unwrap();
+        let serialized_value = serde_json::to_value(deserialized.clone()).unwrap();
+        assert_eq!(origin, serialized_value);
         assert_eq!(deserialized, serde_json::from_str::<TxpoolContent>(&serialized).unwrap());
     }
 
diff --git a/ethers-core/src/utils/anvil.rs b/ethers-core/src/utils/anvil.rs
index b762fbb3..ac941b5b 100644
--- a/ethers-core/src/utils/anvil.rs
+++ b/ethers-core/src/utils/anvil.rs
@@ -5,6 +5,7 @@ use crate::{
 use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey};
 use std::{
     io::{BufRead, BufReader},
+    path::PathBuf,
     process::{Child, Command},
     time::{Duration, Instant},
 };
@@ -78,20 +79,59 @@ impl Drop for AnvilInstance {
 /// ```
 #[derive(Debug, Clone, Default)]
 pub struct Anvil {
+    program: Option<PathBuf>,
     port: Option<u16>,
     block_time: Option<u64>,
     mnemonic: Option<String>,
     fork: Option<String>,
+    fork_block_number: Option<u64>,
     args: Vec<String>,
 }
 
 impl Anvil {
     /// Creates an empty Anvil builder.
     /// The default port is 8545. The mnemonic is chosen randomly.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use ethers_core::utils::Anvil;
+    /// fn a() {
+    ///  let anvil = Anvil::default().spawn();
+    ///
+    ///  println!("Anvil running at `{}`", anvil.endpoint());
+    /// # }
+    /// ```
     pub fn new() -> Self {
         Self::default()
     }
 
+    /// Creates an Anvil builder which will execute `anvil` at the given path.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use ethers_core::utils::Anvil;
+    /// fn a() {
+    ///  let anvil = Anvil::at("~/.foundry/bin/anvil").spawn();
+    ///
+    ///  println!("Anvil running at `{}`", anvil.endpoint());
+    /// # }
+    /// ```
+    pub fn at(path: impl Into<PathBuf>) -> Self {
+        Self::new().path(path)
+    }
+
+    /// Sets the `path` to the `anvil` cli
+    ///
+    /// By default, it's expected that `anvil` is in `$PATH`, see also
+    /// [`std::process::Command::new()`]
+    #[must_use]
+    pub fn path<T: Into<PathBuf>>(mut self, path: T) -> Self {
+        self.program = Some(path.into());
+        self
+    }
+
     /// Sets the port which will be used when the `anvil` instance is launched.
     #[must_use]
     pub fn port<T: Into<u16>>(mut self, port: T) -> Self {
@@ -113,6 +153,15 @@ impl Anvil {
         self
     }
 
+    /// Sets the `fork-block-number` which will be used in addition to [`Self::fork`].
+    ///
+    /// **Note:** if set, then this requires `fork` to be set as well
+    #[must_use]
+    pub fn fork_block_number<T: Into<u64>>(mut self, fork_block_number: T) -> Self {
+        self.fork_block_number = Some(fork_block_number.into());
+        self
+    }
+
     /// Sets the `fork` argument to fork from another currently running Ethereum client
     /// at a given block. Input should be the HTTP location and port of the other client,
     /// e.g. `http://localhost:8545`. You can optionally specify the block to fork from
@@ -146,7 +195,11 @@ impl Anvil {
     /// Consumes the builder and spawns `anvil` with stdout redirected
     /// to /dev/null.
     pub fn spawn(self) -> AnvilInstance {
-        let mut cmd = Command::new("anvil");
+        let mut cmd = if let Some(ref prg) = self.program {
+            Command::new(prg)
+        } else {
+            Command::new("anvil")
+        };
         cmd.stdout(std::process::Stdio::piped());
         let port = if let Some(port) = self.port { port } else { unused_port() };
         cmd.arg("-p").arg(port.to_string());
@@ -163,6 +216,10 @@ impl Anvil {
             cmd.arg("-f").arg(fork);
         }
 
+        if let Some(fork_block_number) = self.fork_block_number {
+            cmd.arg("--fork-block-number").arg(fork_block_number.to_string());
+        }
+
         cmd.args(self.args);
 
         let mut child = cmd.spawn().expect("couldnt start anvil");
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 1703276f..b1cf9ca2 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -38,6 +38,12 @@ use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
 use std::convert::{TryFrom, TryInto};
 use thiserror::Error;
 
+/// Re-export of serde-json
+#[doc(hidden)]
+pub mod __serde_json {
+    pub use serde_json::*;
+}
+
 #[derive(Error, Debug)]
 pub enum ConversionError {
     #[error("Unknown units: {0}")]
diff --git a/ethers-etherscan/src/account.rs b/ethers-etherscan/src/account.rs
index 47cecfd9..cc310bd8 100644
--- a/ethers-etherscan/src/account.rs
+++ b/ethers-etherscan/src/account.rs
@@ -607,7 +607,7 @@ mod tests {
                 .await;
             assert!(balances.is_ok());
             let balances = balances.unwrap();
-            assert!(balances.len() == 1);
+            assert_eq!(balances.len(), 1);
         })
         .await
     }
@@ -624,7 +624,6 @@ mod tests {
                     None,
                 )
                 .await;
-            dbg!(&txs);
             assert!(txs.is_ok());
         })
         .await
@@ -726,4 +725,18 @@ mod tests {
         })
         .await
     }
+
+    #[tokio::test]
+    #[serial]
+    async fn get_avalanche_transactions() {
+        if std::env::var("SNOWTRACE_API_KEY").is_err() {
+            // nothing to do if api key unset
+            return
+        }
+        let client = Client::new_from_env(Chain::Avalanche).unwrap();
+        let txs = client
+            .get_transactions(&"0x1549ea9b546ba9ffb306d78a1e1f304760cc4abf".parse().unwrap(), None)
+            .await;
+        assert!(txs.is_ok());
+    }
 }
diff --git a/ethers-etherscan/src/lib.rs b/ethers-etherscan/src/lib.rs
index 472b1e91..a78483f0 100644
--- a/ethers-etherscan/src/lib.rs
+++ b/ethers-etherscan/src/lib.rs
@@ -79,6 +79,7 @@ impl Client {
             Chain::Avalanche | Chain::AvalancheFuji => std::env::var("SNOWTRACE_API_KEY")?,
             Chain::Polygon | Chain::PolygonMumbai => std::env::var("POLYGONSCAN_API_KEY")?,
             Chain::Mainnet |
+            Chain::Morden |
             Chain::Ropsten |
             Chain::Kovan |
             Chain::Rinkeby |
@@ -231,7 +232,7 @@ impl ClientBuilder {
 
         let (etherscan_api_url, etherscan_url) = match chain {
             Chain::Mainnet => urls("https://api.etherscan.io/api", "https://etherscan.io"),
-            Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli => {
+            Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli | Chain::Sepolia => {
                 let chain_name = chain.to_string().to_lowercase();
                 urls(
                     format!("https://api-{}.etherscan.io/api", chain_name),
@@ -484,10 +485,10 @@ mod tests {
 
     #[test]
     fn chain_not_supported() {
-        let err = Client::new_from_env(Chain::Sepolia).unwrap_err();
+        let err = Client::new_from_env(Chain::Morden).unwrap_err();
 
         assert!(matches!(err, EtherscanError::ChainNotSupported(_)));
-        assert_eq!(err.to_string(), "Chain sepolia not supported");
+        assert_eq!(err.to_string(), "Chain morden not supported");
     }
 
     #[test]
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index ca8cdd37..16e37f76 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -14,7 +14,7 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-ethers-contract = { version = "^0.13.0", path = "../ethers-contract", default-features = false, features = ["abigen"] }
+ethers-contract = { version = "^0.13.0", path = "../ethers-contract", default-features = false }
 ethers-core = { version = "^0.13.0", path = "../ethers-core", default-features = false }
 ethers-etherscan = { version = "^0.13.0", path = "../ethers-etherscan", default-features = false }
 ethers-providers = { version = "^0.13.0", path = "../ethers-providers", default-features = false }
@@ -43,7 +43,7 @@ hex = { version = "0.4.3", default-features = false, features = ["std"] }
 rand = { version = "0.8.5", default-features = false }
 ethers-providers = { version = "^0.13.0", path = "../ethers-providers", default-features = false, features = ["ws", "rustls"] }
 once_cell = "1.13.0"
-ethers-solc = { version = "^0.13.0", path = "../ethers-solc", default-features = false }
+ethers-solc = { version = "^0.13.0", path = "../ethers-solc" }
 serial_test = "0.8.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
diff --git a/ethers-middleware/src/transformer/ds_proxy/factory.rs b/ethers-middleware/src/transformer/ds_proxy/factory.rs
index 8de0c88f..c6f0561d 100644
--- a/ethers-middleware/src/transformer/ds_proxy/factory.rs
+++ b/ethers-middleware/src/transformer/ds_proxy/factory.rs
@@ -37,12 +37,16 @@ mod dsproxyfactory_mod {
         Contract, EthEvent, Lazy,
     };
     use ethers_core::{
-        abi::{parse_abi, Abi, Detokenize, InvalidOutputType, Token, Tokenizable},
+        abi::{
+            parse_abi, Abi, Detokenize, InvalidOutputType, ParamType, Token, Tokenizable,
+            TokenizableItem,
+        },
         types::*,
     };
     use ethers_providers::Middleware;
     #[doc = "DsProxyFactory was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"]
     use std::sync::Arc;
+
     pub static DSPROXYFACTORY_ABI: Lazy<Abi> = Lazy::new(|| {
         serde_json :: from_str ("[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"isProxy\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"proxy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cache\",\"type\":\"address\"}],\"name\":\"Created\",\"type\":\"event\"}]\n") . expect ("invalid abi")
     });
@@ -78,18 +82,13 @@ mod dsproxyfactory_mod {
                 .expect("method not found (this should never happen)")
         }
         ///Calls the contract's `build` (0x8e1a55fc) function
-        pub fn build_with_sender(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::Address> {
+        pub fn build_with_sender(&self) -> ContractCall<M, Address> {
             self.0
                 .method_hash([142, 26, 85, 252], ())
                 .expect("method not found (this should never happen)")
         }
         ///Calls the contract's `build` (0xf3701da2) function
-        pub fn build(
-            &self,
-            owner: ethers_core::types::Address,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::Address> {
+        pub fn build(&self, owner: Address) -> ContractCall<M, Address> {
             self.0
                 .method_hash([243, 112, 29, 162], owner)
                 .expect("method not found (this should never happen)")
@@ -101,24 +100,107 @@ mod dsproxyfactory_mod {
                 .expect("method not found (this should never happen)")
         }
         ///Gets the contract's `Created` event
-        pub fn created_filter(&self) -> ethers_contract::builders::Event<M, CreatedFilter> {
+        pub fn created_filter(&self) -> Event<M, CreatedFilter> {
             self.0.event()
         }
 
         /// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this
         /// contract
-        pub fn events(&self) -> ethers_contract::builders::Event<M, CreatedFilter> {
+        pub fn events(&self) -> Event<M, CreatedFilter> {
             self.0.event_with_filter(Default::default())
         }
     }
-    #[derive(Clone, Debug, Default, Eq, PartialEq, EthEvent)]
-    #[ethevent(name = "Created")]
+
+    #[derive(Clone, Debug, Default, Eq, PartialEq)]
     pub struct CreatedFilter {
-        #[ethevent(indexed)]
         pub sender: Address,
-        #[ethevent(indexed)]
         pub owner: Address,
         pub proxy: Address,
         pub cache: Address,
     }
+
+    impl ethers_core::abi::AbiType for CreatedFilter {
+        fn param_type() -> ParamType {
+            ParamType::Tuple(::std::vec![
+                ParamType::Address; 4
+            ])
+        }
+    }
+    impl ethers_core::abi::AbiArrayType for CreatedFilter {}
+
+    impl Tokenizable for CreatedFilter {
+        fn from_token(token: Token) -> Result<Self, InvalidOutputType>
+        where
+            Self: Sized,
+        {
+            if let Token::Tuple(tokens) = token {
+                if tokens.len() != 4usize {
+                    return Err(InvalidOutputType(format!(
+                        "Expected {} tokens, got {}: {:?}",
+                        4usize,
+                        tokens.len(),
+                        tokens
+                    )))
+                }
+                let mut iter = tokens.into_iter();
+                Ok(Self {
+                    sender: Tokenizable::from_token(iter.next().unwrap())?,
+                    owner: Tokenizable::from_token(iter.next().unwrap())?,
+                    proxy: Tokenizable::from_token(iter.next().unwrap())?,
+                    cache: Tokenizable::from_token(iter.next().unwrap())?,
+                })
+            } else {
+                Err(InvalidOutputType(format!("Expected Tuple, got {:?}", token)))
+            }
+        }
+        fn into_token(self) -> Token {
+            Token::Tuple(::std::vec![
+                self.sender.into_token(),
+                self.owner.into_token(),
+                self.proxy.into_token(),
+                self.cache.into_token(),
+            ])
+        }
+    }
+    impl TokenizableItem for CreatedFilter {}
+
+    impl ethers_contract::EthEvent for CreatedFilter {
+        fn name() -> std::borrow::Cow<'static, str> {
+            "Created".into()
+        }
+        fn signature() -> H256 {
+            H256([
+                37, 155, 48, 202, 57, 136, 92, 109, 128, 26, 11, 93, 188, 152, 134, 64, 243, 194,
+                94, 47, 55, 83, 31, 225, 56, 197, 197, 175, 137, 85, 212, 27,
+            ])
+        }
+        fn abi_signature() -> std::borrow::Cow<'static, str> {
+            "Created(address,address,address,address)".into()
+        }
+        fn decode_log(log: &ethers_core::abi::RawLog) -> Result<Self, ethers_core::abi::Error>
+        where
+            Self: Sized,
+        {
+            let ethers_core::abi::RawLog { data, topics } = log;
+            let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?;
+            if event_signature != &Self::signature() {
+                return Err(ethers_core::abi::Error::InvalidData)
+            }
+            let topic_types = ::std::vec![ParamType::Address, ParamType::Address];
+            let data_types = [ParamType::Address, ParamType::Address];
+            let flat_topics =
+                topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
+            let topic_tokens = ethers_core::abi::decode(&topic_types, &flat_topics)?;
+            if topic_tokens.len() != topics.len() - 1 {
+                return Err(ethers_core::abi::Error::InvalidData)
+            }
+            let data_tokens = ethers_core::abi::decode(&data_types, data)?;
+            let tokens: Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect();
+            Tokenizable::from_token(Token::Tuple(tokens))
+                .map_err(|_| ethers_core::abi::Error::InvalidData)
+        }
+        fn is_anonymous() -> bool {
+            false
+        }
+    }
 }
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index 37239747..19e590f2 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -38,14 +38,14 @@ pin-project = { version = "1.0.11", default-features = false }
 tracing = { version = "0.1.35", default-features = false }
 tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] }
 
-bytes = { version  = "1.1.0", default-features = false, optional = true }
+bytes = { version  = "1.2.0", default-features = false, optional = true }
 once_cell = "1.13.0"
 hashers = "1.0.1"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 # tokio
 tokio = { version = "1.18", features = ["time"] }
-tokio-tungstenite = { version = "0.17.1", default-features = false, features = ["connect"], optional = true }
+tokio-tungstenite = { version = "0.17.2", default-features = false, features = ["connect"], optional = true }
 
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs
index 76e1ffaf..e01d67ce 100644
--- a/ethers-providers/src/provider.rs
+++ b/ethers-providers/src/provider.rs
@@ -7,8 +7,10 @@ use crate::{
     PendingTransaction, QuorumProvider, RwClient, SyncingStatus,
 };
 
+#[cfg(all(not(target_arch = "wasm32"), feature = "ws"))]
+use crate::transports::Authorization;
 #[cfg(not(target_arch = "wasm32"))]
-use crate::transports::{Authorization, HttpRateLimitRetryPolicy, RetryClient};
+use crate::transports::{HttpRateLimitRetryPolicy, RetryClient};
 
 #[cfg(feature = "celo")]
 use crate::CeloMiddleware;
diff --git a/ethers-providers/src/transports/retry.rs b/ethers-providers/src/transports/retry.rs
index 5c496045..72b823de 100644
--- a/ethers-providers/src/transports/retry.rs
+++ b/ethers-providers/src/transports/retry.rs
@@ -5,7 +5,6 @@ use super::{common::JsonRpcError, http::ClientError};
 use crate::{provider::ProviderError, JsonRpcClient};
 
 use std::{
-    clone::Clone,
     fmt::Debug,
     sync::atomic::{AtomicU32, Ordering},
     time::Duration,
@@ -135,10 +134,23 @@ where
         A: std::fmt::Debug + Serialize + Send + Sync,
         R: DeserializeOwned,
     {
-        let ahead_in_queue = self.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64;
+        // Helper type that caches the `params` value across several retries
+        // This is necessary because the wrapper provider is supposed to skip he `params` if it's of
+        // size 0, see `crate::transports::common::Request`
+        enum RetryParams<Params> {
+            Value(Params),
+            Zst(()),
+        }
 
-        let params =
-            serde_json::to_value(params).map_err(|err| RetryClientError::SerdeJson(err))?;
+        let params = if std::mem::size_of::<A>() == 0 {
+            RetryParams::Zst(())
+        } else {
+            let params =
+                serde_json::to_value(params).map_err(|err| RetryClientError::SerdeJson(err))?;
+            RetryParams::Value(params)
+        };
+
+        let ahead_in_queue = self.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64;
 
         let mut retry_number: u32 = 0;
 
@@ -148,7 +160,11 @@ where
             // hack to not hold `R` across an await in the sleep future and prevent requiring
             // R: Send + Sync
             {
-                match self.inner.request(method, params.clone()).await {
+                let resp = match params {
+                    RetryParams::Value(ref params) => self.inner.request(method, params).await,
+                    RetryParams::Zst(unit) => self.inner.request(method, unit).await,
+                };
+                match resp {
                     Ok(ret) => {
                         self.requests_enqueued.fetch_sub(1, Ordering::SeqCst);
                         return Ok(ret)
diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml
index 250ae471..311a08d6 100644
--- a/ethers-signers/Cargo.toml
+++ b/ethers-signers/Cargo.toml
@@ -44,7 +44,7 @@ home = { version = "0.5.3", optional = true }
 ethers-contract = { version = "^0.13.0", path = "../ethers-contract", features = ["eip712", "abigen"]}
 ethers-derive-eip712 = { version = "^0.13.0", path = "../ethers-core/ethers-derive-eip712" }
 serde_json = { version = "1.0.64" }
-tracing-subscriber = "0.3.11"
+tracing-subscriber = "0.3.14"
 yubihsm = { version = "0.41.0-pre", features = ["secp256k1", "usb", "mockhsm"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 31b027c5..057810ec 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -44,7 +44,7 @@ cfg-if = "1.0.0"
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 home = "0.5.3"
 svm = { package = "svm-rs", version = "0.2.13", default-features = false, optional = true, features = ["blocking"] }
-svm-builds = { package = "svm-rs-builds", version = "0.1.3", optional = true}
+svm-builds = { package = "svm-rs-builds", version = "0.1.4", optional = true}
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 # NOTE: this enables wasm compatibility for getrandom indirectly
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index 7958a211..7d4fb06c 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -5,8 +5,8 @@
 //! `ConfigurableArtifacts` populates a single `Artifact`, the `ConfigurableArtifact`, by default
 //! with essential entries only, such as `abi`, `bytecode`,..., but may include additional values
 //! based on its `ExtraOutputValues` that maps to various objects in the solc contract output, see
-//! also: [`OutputSelection`](crate::artifacts::OutputSelection). In addition to that some output
-//! values can also be emitted as standalone files.
+//! also: [`OutputSelection`](crate::artifacts::output_selection::OutputSelection). In addition to
+//! that some output values can also be emitted as standalone files.
 
 use crate::{
     artifacts::{
diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs
index c08b64b2..d9005879 100644
--- a/ethers-solc/src/artifacts/mod.rs
+++ b/ethers-solc/src/artifacts/mod.rs
@@ -898,7 +898,7 @@ impl Serialize for LosslessMetadata {
     where
         S: Serializer,
     {
-        self.raw_metadata.serialize(serializer)
+        serializer.serialize_str(&self.raw_metadata)
     }
 }
 
@@ -920,8 +920,8 @@ impl<'de> Deserialize<'de> for LosslessMetadata {
             where
                 E: serde::de::Error,
             {
-                let raw_metadata = value.to_string();
                 let metadata = serde_json::from_str(value).map_err(serde::de::Error::custom)?;
+                let raw_metadata = value.to_string();
                 Ok(LosslessMetadata { raw_metadata, metadata })
             }
         }
@@ -2097,4 +2097,27 @@ mod tests {
             ])
         );
     }
+
+    #[test]
+    fn test_lossless_metadata() {
+        #[derive(Debug, Serialize, Deserialize)]
+        #[serde(rename_all = "camelCase")]
+        pub struct Contract {
+            #[serde(
+                default,
+                skip_serializing_if = "Option::is_none",
+                with = "serde_helpers::json_string_opt"
+            )]
+            pub metadata: Option<LosslessMetadata>,
+        }
+
+        let s = r#"{"metadata":"{\"compiler\":{\"version\":\"0.4.18+commit.9cf6e910\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{\"transferOwnership(address)\":{\"details\":\"Allows the current owner to transfer control of the contract to a newOwner.\",\"params\":{\"newOwner\":\"The address to transfer ownership to.\"}}},\"title\":\"Ownable\"},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"src/Contract.sol\":\"Ownable\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":src/=src/\"]},\"sources\":{\"src/Contract.sol\":{\"keccak256\":\"0x3e0d611f53491f313ae035797ed7ecfd1dfd8db8fef8f82737e6f0cd86d71de7\",\"urls\":[\"bzzr://9c33025fa9d1b8389e4c7c9534a1d70fad91c6c2ad70eb5e4b7dc3a701a5f892\"]}},\"version\":1}"}"#;
+
+        let value: serde_json::Value = serde_json::from_str(s).unwrap();
+        let c: Contract = serde_json::from_value(value).unwrap();
+        assert_eq!(c.metadata.as_ref().unwrap().raw_metadata, "{\"compiler\":{\"version\":\"0.4.18+commit.9cf6e910\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{\"transferOwnership(address)\":{\"details\":\"Allows the current owner to transfer control of the contract to a newOwner.\",\"params\":{\"newOwner\":\"The address to transfer ownership to.\"}}},\"title\":\"Ownable\"},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"src/Contract.sol\":\"Ownable\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":src/=src/\"]},\"sources\":{\"src/Contract.sol\":{\"keccak256\":\"0x3e0d611f53491f313ae035797ed7ecfd1dfd8db8fef8f82737e6f0cd86d71de7\",\"urls\":[\"bzzr://9c33025fa9d1b8389e4c7c9534a1d70fad91c6c2ad70eb5e4b7dc3a701a5f892\"]}},\"version\":1}");
+
+        let value = serde_json::to_string(&c).unwrap();
+        pretty_assertions::assert_eq!(s, value);
+    }
 }
diff --git a/ethers-solc/src/artifacts/serde_helpers.rs b/ethers-solc/src/artifacts/serde_helpers.rs
index 0e38ee3f..75e87ee5 100644
--- a/ethers-solc/src/artifacts/serde_helpers.rs
+++ b/ethers-solc/src/artifacts/serde_helpers.rs
@@ -41,7 +41,7 @@ where
 pub mod json_string_opt {
     use serde::{
         de::{self, DeserializeOwned},
-        ser, Deserialize, Deserializer, Serialize, Serializer,
+        Deserialize, Deserializer, Serialize, Serializer,
     };
 
     pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
@@ -50,8 +50,7 @@ pub mod json_string_opt {
         T: Serialize,
     {
         if let Some(value) = value {
-            let value = serde_json::to_string(value).map_err(ser::Error::custom)?;
-            serializer.serialize_str(&value)
+            value.serialize(serializer)
         } else {
             serializer.serialize_none()
         }
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index e1b6217a..4fe6bf5a 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -431,7 +431,7 @@ impl Solc {
     }
 
     /// Verify that the checksum for this version of solc is correct. We check against the SHA256
-    /// checksum from the build information published by binaries.soliditylang
+    /// checksum from the build information published by [binaries.soliditylang.org](https://binaries.soliditylang.org/)
     #[cfg(all(feature = "svm-solc"))]
     pub fn verify_checksum(&self) -> Result<()> {
         let version = self.version_short()?;
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index cc4d98f0..75dc4d9a 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -119,13 +119,25 @@ impl ProjectPathsConfig {
         Ok(Source::read_all_from(&self.scripts)?)
     }
 
-    /// Returns the combined set solidity file paths for `Self::sources` and `Self::tests`
+    /// Returns true if the there is at least one solidity file in this config.
+    ///
+    /// See also, `Self::input_files()`
+    pub fn has_input_files(&self) -> bool {
+        self.input_files_iter().next().is_some()
+    }
+
+    /// Returns an iterator that yields all solidity file paths for `Self::sources`, `Self::tests`
+    /// and `Self::scripts`
+    pub fn input_files_iter(&self) -> impl Iterator<Item = PathBuf> + '_ {
+        utils::source_files_iter(&self.sources)
+            .chain(utils::source_files_iter(&self.tests))
+            .chain(utils::source_files_iter(&self.scripts))
+    }
+
+    /// Returns the combined set solidity file paths for `Self::sources`, `Self::tests` and
+    /// `Self::scripts`
     pub fn input_files(&self) -> Vec<PathBuf> {
-        utils::source_files(&self.sources)
-            .into_iter()
-            .chain(utils::source_files(&self.tests))
-            .chain(utils::source_files(&self.scripts))
-            .collect()
+        self.input_files_iter().collect()
     }
 
     /// Returns the combined set of `Self::read_sources` + `Self::read_tests` + `Self::read_scripts`
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index 8d98713b..55238466 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -1,4 +1,6 @@
 #![deny(rustdoc::broken_intra_doc_links)]
+#![allow(rustdoc::private_intra_doc_links)]
+
 pub mod artifacts;
 pub mod sourcemap;
 
@@ -279,7 +281,7 @@ impl<T: ArtifactOutput> Project<T> {
     }
 
     /// Convenience function to compile a series of solidity files with the project's settings.
-    /// Same as [`Self::svm_compile()`] but with the given `files` as input.
+    /// Same as [`Self::compile()`] but with the given `files` as input.
     ///
     /// # Example
     ///
@@ -310,7 +312,7 @@ impl<T: ArtifactOutput> Project<T> {
     }
 
     /// Convenience function to compile only (re)compile files that match the provided [FileFilter].
-    /// Same as [`Self::svm_compile()`] but with only with those files as input that match
+    /// Same as [`Self::compile()`] but with only with those files as input that match
     /// [FileFilter::is_match()].
     ///
     /// # Example - Only compile Test files
diff --git a/ethers-solc/src/report/mod.rs b/ethers-solc/src/report/mod.rs
index 4d33285f..4c7a572d 100644
--- a/ethers-solc/src/report/mod.rs
+++ b/ethers-solc/src/report/mod.rs
@@ -253,7 +253,7 @@ where
     CURRENT_STATE
         .try_with(|state| {
             let scoped = state.scoped.borrow_mut();
-            f(&*scoped)
+            f(&scoped)
         })
         .unwrap_or_else(|_| f(&Report::none()))
 }
diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs
index eedea95f..487055cf 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -72,6 +72,22 @@ pub fn find_version_pragma(contract: &str) -> Option<Match> {
     RE_SOL_PRAGMA_VERSION.captures(contract)?.name("version")
 }
 
+/// Returns an iterator that yields all solidity/yul files funder under the given root path or the
+/// `root` itself, if it is a sol/yul file
+///
+/// This also follows symlinks.
+pub fn source_files_iter(root: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
+    WalkDir::new(root)
+        .follow_links(true)
+        .into_iter()
+        .filter_map(Result::ok)
+        .filter(|e| e.file_type().is_file())
+        .filter(|e| {
+            e.path().extension().map(|ext| (ext == "sol") || (ext == "yul")).unwrap_or_default()
+        })
+        .map(|e| e.path().into())
+}
+
 /// Returns a list of absolute paths to all the solidity files under the root, or the file itself,
 /// if the path is a solidity file.
 ///
@@ -86,16 +102,7 @@ pub fn find_version_pragma(contract: &str) -> Option<Match> {
 /// let sources = utils::source_files("./contracts");
 /// ```
 pub fn source_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
-    WalkDir::new(root)
-        .follow_links(true)
-        .into_iter()
-        .filter_map(Result::ok)
-        .filter(|e| e.file_type().is_file())
-        .filter(|e| {
-            e.path().extension().map(|ext| (ext == "sol") || (ext == "yul")).unwrap_or_default()
-        })
-        .map(|e| e.path().into())
-        .collect()
+    source_files_iter(root).collect()
 }
 
 /// Returns a list of _unique_ paths to all folders under `root` that contain at least one solidity
diff --git a/examples/anvil_fork.rs b/examples/anvil_fork.rs
new file mode 100644
index 00000000..31da8670
--- /dev/null
+++ b/examples/anvil_fork.rs
@@ -0,0 +1,14 @@
+//! Spawn an [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) instance in forking mode
+
+use ethers::utils::Anvil;
+
+#[tokio::main]
+async fn main() -> eyre::Result<()> {
+    // ensure `anvil` is available in $PATH
+    let anvil =
+        Anvil::new().fork("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").spawn();
+
+    println!("Anvil running at `{}`", anvil.endpoint());
+
+    Ok(())
+}
diff --git a/examples/ethers-wasm/yarn.lock b/examples/ethers-wasm/yarn.lock
index d17e98c0..65e0c592 100644
--- a/examples/ethers-wasm/yarn.lock
+++ b/examples/ethers-wasm/yarn.lock
@@ -3696,9 +3696,9 @@ source-map-support@0.5.12:
     source-map "^0.6.0"
 
 source-map-support@~0.5.12:
-  version "0.5.19"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
-  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -3900,9 +3900,9 @@ terser-webpack-plugin@^1.4.3:
     worker-farm "^1.7.0"
 
 terser@^4.1.2:
-  version "4.8.0"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
-  integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
+  version "4.8.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
+  integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
   dependencies:
     commander "^2.20.0"
     source-map "~0.6.1"


Test patch:
diff --git a/ethers-contract/tests/abigen.rs b/ethers-contract/tests/abigen.rs
index 3271c551..ecf805f9 100644
--- a/ethers-contract/tests/abigen.rs
+++ b/ethers-contract/tests/abigen.rs
@@ -366,7 +366,7 @@ async fn can_handle_underscore_functions() {
     // Manual call construction
     use ethers_providers::Middleware;
     // TODO: How do we handle underscores for calls here?
-    let data = simplestorage_mod::HashPuzzleCall.encode();
+    let data = simple_storage::HashPuzzleCall.encode();
     let tx = Eip1559TransactionRequest::new().data(data).to(addr);
     let tx = TypedTransaction::Eip1559(tx);
     let res5 = client.call(&tx, None).await.unwrap();
diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs
index acf63042..4d119048 100644
--- a/ethers-contract/tests/contract.rs
+++ b/ethers-contract/tests/contract.rs
@@ -601,7 +601,7 @@ mod eth_tests {
             out: Address::from([0; 20]),
         };
 
-        let derived_foo_bar = deriveeip712test_mod::FooBar {
+        let derived_foo_bar = derive_eip_712_test::FooBar {
             foo: foo_bar.foo,
             bar: foo_bar.bar,
             fizz: foo_bar.fizz.clone(),
diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs
index 666de8e7..b1da9db7 100644
--- a/ethers-providers/tests/provider.rs
+++ b/ethers-providers/tests/provider.rs
@@ -91,6 +91,28 @@ mod eth_tests {
         let (_max_fee_per_gas, _max_priority_fee_per_gas) =
             provider.estimate_eip1559_fees(None).await.unwrap();
     }
+
+    #[tokio::test]
+    #[ignore]
+    async fn test_hardhat_compatibility() {
+        use ethers_providers::RetryClient;
+
+        async fn send_zst_requests<M: Middleware>(provider: M) {
+            let _ = provider.get_chainid().await.unwrap();
+            let _ = provider.get_block_number().await.unwrap();
+            let _ = provider.get_gas_price().await.unwrap();
+            let _ = provider.get_accounts().await.unwrap();
+            let _ = provider.get_net_version().await.unwrap();
+        }
+
+        let provider = Provider::<Http>::try_from("http://localhost:8545").unwrap();
+        send_zst_requests(provider).await;
+
+        let provider =
+            Provider::<RetryClient<Http>>::new_client("http://localhost:8545", 10, 200).unwrap();
+
+        send_zst_requests(provider).await;
+    }
 }
 
 #[cfg(feature = "celo")]
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 1093036b..bb849a7f 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -1620,10 +1620,9 @@ fn can_compile_model_checker_sample() {
 }
 
 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 => {}
-    };
+    if Solc::find_svm_installed_version(version.to_string()).unwrap().is_some() {
+        svm::remove_version(version).expect("failed to remove version")
+    }
 }
 
 #[tokio::test(flavor = "multi_thread")]