Repository: gakonst/ethers-rs
Pull request #2373 (2023-04-23 10:48:30)

feat(ws): add methods taking in a tungstenite config
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation

Currently the websocket implementation does not allow to pass in a custom Tungestenite config. For example, its not possible to increase the default msg size over the Tungestenite default, which can lead to `Message Too Long` errors if fetching a lot of events with a lot of fields in them, one case where I've seen this failure in practice is when fetching around 8k events like this one:
```solidity
    event TradeRequested(
        address indexed tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        address recipient,
        uint256 indexed tradeIndex,
        address indexed trader
    );
```

## Solution

This PR adds 2 new methods to the `WsClient` and the `WsManager`:
- `connect_with_config` which is the same as `connect`, but allows taking in a Tungstenite config object
- `connect_with_config_and_reconnects` which is the same as `connect_with_reconnects`, but allows taking in a Tungstenite config object.

## PR Checklist

-   [ ] Added Tests
-   [x] Added Documentation
-   [ ] Breaking changes



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7861d3dc..696af0c8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1230,8 +1230,6 @@ dependencies = [
  "ethers-contract-derive",
  "ethers-core",
  "ethers-providers",
- "ethers-signers",
- "ethers-solc",
  "futures-util",
  "hex",
  "once_cell",
@@ -1250,7 +1248,6 @@ dependencies = [
  "dunce",
  "ethers-core",
  "ethers-etherscan",
- "ethers-solc",
  "eyre",
  "getrandom",
  "hex",
diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml
index fe8bcbe9..072ab237 100644
--- a/ethers-contract/Cargo.toml
+++ b/ethers-contract/Cargo.toml
@@ -40,8 +40,6 @@ ethers-contract-abigen = { workspace = true, optional = true }
 ethers-contract-derive = { workspace = true, optional = true }
 
 [dev-dependencies]
-ethers-signers.workspace = true
-ethers-solc.workspace = true
 ethers-providers = { workspace = true, features = ["ws"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
@@ -50,8 +48,8 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
 [features]
 default = ["abigen"]
 
-abigen-offline = ["ethers-contract-abigen", "ethers-contract-derive"]
-abigen = ["abigen-offline", "ethers-contract-abigen/online"]
+abigen = ["ethers-contract-abigen", "ethers-contract-derive"]
+abigen-online = ["abigen", "ethers-contract-abigen/online"]
 
 celo = ["legacy", "ethers-core/celo", "ethers-providers/celo"]
 legacy = []
@@ -60,4 +58,5 @@ rustls = ["ethers-contract-abigen/rustls"]
 openssl = ["ethers-contract-abigen/openssl"]
 
 # Deprecated
+abigen-offline = ["abigen"]
 eip712 = []
diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml
index 072b7a61..120db9db 100644
--- a/ethers-contract/ethers-contract-abigen/Cargo.toml
+++ b/ethers-contract/ethers-contract-abigen/Cargo.toml
@@ -56,9 +56,8 @@ getrandom.workspace = true
 
 [features]
 online = ["reqwest", "ethers-etherscan", "url", "tokio"]
-openssl = ["online", "reqwest/native-tls", "ethers-etherscan/openssl"]
-rustls = ["online", "reqwest/rustls-tls", "ethers-etherscan/rustls"]
+rustls = ["reqwest?/rustls-tls", "ethers-etherscan?/rustls"]
+openssl = ["reqwest?/native-tls", "ethers-etherscan?/openssl"]
 
 [dev-dependencies]
 tempfile.workspace = true
-ethers-solc = { workspace = true, features = ["project-util", "svm-solc"] }
diff --git a/ethers-contract/ethers-contract-abigen/src/lib.rs b/ethers-contract/ethers-contract-abigen/src/lib.rs
index 3867ed46..e0a56c44 100644
--- a/ethers-contract/ethers-contract-abigen/src/lib.rs
+++ b/ethers-contract/ethers-contract-abigen/src/lib.rs
@@ -371,7 +371,6 @@ impl ContractBindings {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use ethers_solc::project_util::TempProject;
 
     #[test]
     fn can_generate_structs() {
@@ -381,77 +380,4 @@ mod tests {
         let out = gen.tokens.to_string();
         assert!(out.contains("pub struct Stuff"));
     }
-
-    #[test]
-    fn can_compile_and_generate() {
-        let tmp = TempProject::dapptools().unwrap();
-
-        tmp.add_source(
-            "Greeter",
-            r#"
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-contract Greeter {
-
-    struct Inner {
-        bool a;
-    }
-
-    struct Stuff {
-        Inner inner;
-    }
-
-    function greet(Stuff calldata stuff) public view returns (Stuff memory) {
-        return stuff;
-    }
-}
-"#,
-        )
-        .unwrap();
-
-        let _ = tmp.compile().unwrap();
-
-        let abigen =
-            Abigen::from_file(tmp.artifacts_path().join("Greeter.sol/Greeter.json")).unwrap();
-        let gen = abigen.generate().unwrap();
-        let out = gen.tokens.to_string();
-        assert!(out.contains("pub struct Stuff"));
-        assert!(out.contains("pub struct Inner"));
-    }
-
-    #[test]
-    fn can_compile_and_generate_with_punctuation() {
-        let tmp = TempProject::dapptools().unwrap();
-
-        tmp.add_source(
-            "Greeter.t.sol",
-            r#"
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-contract Greeter {
-    struct Inner {
-        bool a;
-    }
-    struct Stuff {
-        Inner inner;
-    }
-    function greet(Stuff calldata stuff) public view returns (Stuff memory) {
-        return stuff;
-    }
-}
-"#,
-        )
-        .unwrap();
-
-        let _ = tmp.compile().unwrap();
-
-        let abigen =
-            Abigen::from_file(tmp.artifacts_path().join("Greeter.t.sol/Greeter.json")).unwrap();
-        let gen = abigen.generate().unwrap();
-        let out = gen.tokens.to_string();
-        assert!(out.contains("pub struct Stuff"));
-        assert!(out.contains("pub struct Inner"));
-    }
 }
diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs
index 0f9127b6..08722463 100644
--- a/ethers-contract/ethers-contract-abigen/src/multi.rs
+++ b/ethers-contract/ethers-contract-abigen/src/multi.rs
@@ -94,7 +94,10 @@ impl MultiAbigen {
     /// # }
     /// ```
     pub fn from_json_files(root: impl AsRef<Path>) -> Result<Self> {
-        util::json_files(root.as_ref()).into_iter().map(Abigen::from_file).collect()
+        let root = root.as_ref();
+        let files = util::json_files(root);
+        eyre::ensure!(!files.is_empty(), "No json files found in directory: {}", root.display());
+        files.into_iter().map(Abigen::from_file).collect()
     }
 
     /// See `apply_filter`
@@ -851,10 +854,8 @@ fn check_binding_in_dir(dir: &Path, binding: &ContractBindings) -> Result<()> {
 #[cfg(test)]
 mod tests {
     use super::*;
-
     use crate::{ExcludeContracts, SelectContracts};
-    use ethers_solc::project_util::TempProject;
-    use std::{env, panic, path::PathBuf};
+    use std::{env, path::PathBuf};
 
     struct Context {
         multi_gen: MultiAbigen,
@@ -863,9 +864,9 @@ mod tests {
 
     fn run_test<T>(test: T)
     where
-        T: FnOnce(&Context) + panic::UnwindSafe,
+        T: FnOnce(Context),
     {
-        let crate_root = std::path::Path::new(&env!("CARGO_MANIFEST_DIR")).to_owned();
+        let crate_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
         let console = Abigen::new(
             "Console",
             crate_root.join("../tests/solidity-contracts/console.json").display().to_string(),
@@ -874,10 +875,7 @@ mod tests {
 
         let simple_storage = Abigen::new(
             "SimpleStorage",
-            crate_root
-                .join("../tests/solidity-contracts/simplestorage_abi.json")
-                .display()
-                .to_string(),
+            crate_root.join("../tests/solidity-contracts/SimpleStorage.json").display().to_string(),
         )
         .unwrap();
 
@@ -894,12 +892,11 @@ mod tests {
 
         let multi_gen = MultiAbigen::from_abigens([console, simple_storage, human_readable]);
 
-        let mod_root = tempfile::tempdir().unwrap().path().join("contracts");
+        let tmp = tempfile::tempdir().unwrap();
+        let mod_root = tmp.path().join("contracts");
+        // fs::create_dir(&mod_root).unwrap();
         let context = Context { multi_gen, mod_root };
-
-        let result = panic::catch_unwind(|| test(&context));
-
-        result.unwrap()
+        test(context)
     }
 
     #[test]
@@ -909,9 +906,8 @@ mod tests {
 
             let single_file = false;
 
-            multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap();
+            multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap();
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_module(mod_root, single_file)
@@ -925,7 +921,7 @@ mod tests {
             let Context { multi_gen, mod_root } = context;
 
             let single_file = true;
-            let mut inner = multi_gen.clone().build().unwrap().into_inner(single_file);
+            let mut inner = multi_gen.build().unwrap().into_inner(single_file);
             inner.root = Some(PathBuf::from("this does not exist"));
             inner.write_to_module(mod_root, single_file).unwrap();
         })
@@ -938,9 +934,8 @@ mod tests {
 
             let single_file = true;
 
-            multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap();
+            multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap();
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_module(mod_root, single_file)
@@ -961,10 +956,9 @@ mod tests {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -985,10 +979,9 @@ mod tests {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -999,14 +992,13 @@ mod tests {
     #[test]
     fn can_detect_incosistent_multi_file_module() {
         run_test(|context| {
-            let Context { multi_gen, mod_root } = context;
+            let Context { mut multi_gen, mod_root } = context;
 
             let single_file = false;
 
-            multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap();
+            multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap();
 
-            let mut cloned = multi_gen.clone();
-            cloned.push(
+            multi_gen.push(
                 Abigen::new(
                     "AdditionalContract",
                     r#"[
@@ -1017,7 +1009,7 @@ mod tests {
             );
 
             let result =
-                cloned.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err();
+                multi_gen.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err();
 
             // ensure inconsistent bindings are detected
             assert!(result, "Inconsistent bindings wrongly approved");
@@ -1027,14 +1019,13 @@ mod tests {
     #[test]
     fn can_detect_incosistent_single_file_module() {
         run_test(|context| {
-            let Context { multi_gen, mod_root } = context;
+            let Context { mut multi_gen, mod_root } = context;
 
             let single_file = true;
 
-            multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap();
+            multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap();
 
-            let mut cloned = multi_gen.clone();
-            cloned.push(
+            multi_gen.push(
                 Abigen::new(
                     "AdditionalContract",
                     r#"[
@@ -1045,7 +1036,7 @@ mod tests {
             );
 
             let result =
-                cloned.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err();
+                multi_gen.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err();
 
             // ensure inconsistent bindings are detected
             assert!(result, "Inconsistent bindings wrongly approved");
@@ -1055,7 +1046,7 @@ mod tests {
     #[test]
     fn can_detect_incosistent_multi_file_crate() {
         run_test(|context| {
-            let Context { multi_gen, mod_root } = context;
+            let Context { mut multi_gen, mod_root } = context;
 
             let single_file = false;
             let name = "a-name";
@@ -1065,11 +1056,10 @@ mod tests {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
-            let mut cloned = multi_gen.clone();
-            cloned.push(
+            multi_gen.push(
                 Abigen::new(
                     "AdditionalContract",
                     r#"[
@@ -1079,7 +1069,7 @@ mod tests {
                 .unwrap(),
             );
 
-            let result = cloned
+            let result = multi_gen
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -1093,7 +1083,7 @@ mod tests {
     #[test]
     fn can_detect_inconsistent_single_file_crate() {
         run_test(|context| {
-            let Context { multi_gen, mod_root } = context;
+            let Context { mut multi_gen, mod_root } = context;
 
             let single_file = true;
             let name = "a-name";
@@ -1103,11 +1093,10 @@ mod tests {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
-            let mut cloned = multi_gen.clone();
-            cloned.push(
+            multi_gen.push(
                 Abigen::new(
                     "AdditionalContract",
                     r#"[
@@ -1117,7 +1106,7 @@ mod tests {
                 .unwrap(),
             );
 
-            let result = cloned
+            let result = multi_gen
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -1195,44 +1184,12 @@ mod tests {
 
     #[test]
     fn can_deduplicate_types() {
-        let tmp = TempProject::dapptools().unwrap();
-
-        tmp.add_source(
-            "Greeter",
-            r#"
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-struct Inner {
-    bool a;
-}
-
-struct Stuff {
-    Inner inner;
-}
-
-contract Greeter1 {
-
-    function greet(Stuff calldata stuff) public pure returns (Stuff memory) {
-        return stuff;
-    }
-}
-
-contract Greeter2 {
-
-    function greet(Stuff calldata stuff) public pure returns (Stuff memory) {
-        return stuff;
-    }
-}
-"#,
-        )
-        .unwrap();
+        let root = tempfile::tempdir().unwrap();
+        let json_files = "../tests/solidity-contracts/greeter";
 
-        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");
+        let gen = MultiAbigen::from_json_files(json_files).unwrap();
+        let bindings = gen.clone().build().unwrap();
+        let single_file_dir = root.path().join("single_bindings");
         bindings.write_to_module(&single_file_dir, true).unwrap();
 
         let single_file_mod = single_file_dir.join("mod.rs");
@@ -1243,9 +1200,8 @@ contract Greeter2 {
         assert!(content.contains("pub struct Stuff"));
 
         // 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");
+        let multi_file_dir = root.path().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());
@@ -1273,33 +1229,12 @@ contract Greeter2 {
 
     #[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 root = tempfile::tempdir().unwrap();
+        let json_files = "../tests/solidity-contracts/ReservedWords";
 
-        let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
-        let bindings = gen.build().unwrap();
-        let single_file_dir = tmp.root().join("single_bindings");
+        let gen = MultiAbigen::from_json_files(json_files).unwrap();
+        let bindings = gen.clone().build().unwrap();
+        let single_file_dir = root.path().join("single_bindings");
         bindings.write_to_module(&single_file_dir, true).unwrap();
 
         let single_file_mod = single_file_dir.join("mod.rs");
@@ -1309,9 +1244,8 @@ contract Enum {
         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");
+        let multi_file_dir = root.path().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());
@@ -1335,31 +1269,26 @@ contract Enum {
         // gotta bunch these all together as we are overwriting env vars
         run_test(|context| {
             let Context { multi_gen, mod_root } = context;
-            let tmp = TempProject::dapptools().unwrap();
-
-            tmp.add_source(
-            "Cargo.toml",
-            r#"
- [package]
-        name = "ethers-contract"
-        version = "1.0.0"
-        edition = "2021"
-        rust-version = "1.64"
-        authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
-        license = "MIT OR Apache-2.0"
-        description = "Smart contract bindings for the ethers-rs crate"
-        homepage = "https://docs.rs/ethers"
-        repository = "https://github.com/gakonst/ethers-rs"
-        keywords = ["ethereum", "web3", "celo", "ethers"]
-
-        [dependencies]
-        ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false }
-"#,
-        )
-        .unwrap();
-
-            let _ = tmp.compile().unwrap();
-            env::set_var("CARGO_MANIFEST_DIR", tmp.root());
+            let manifest = r#"
+[package]
+    name = "ethers-contract"
+    version = "1.0.0"
+    edition = "2021"
+    rust-version = "1.64"
+    authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
+    license = "MIT OR Apache-2.0"
+    description = "Smart contract bindings for the ethers-rs crate"
+    homepage = "https://docs.rs/ethers"
+    repository = "https://github.com/gakonst/ethers-rs"
+    keywords = ["ethereum", "web3", "celo", "ethers"]
+
+    [dependencies]
+    ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false }
+"#;
+
+            let root = mod_root.parent().unwrap();
+            fs::write(root.join("../Cargo.toml"), manifest).unwrap();
+            env::set_var("CARGO_MANIFEST_DIR", root);
             let single_file = false;
             let name = "a-name";
             let version = "290.3782.3";
@@ -1368,44 +1297,39 @@ contract Enum {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
-                .ensure_consistent_crate(name, version, mod_root, single_file, true)
+                .ensure_consistent_crate(name, version, &mod_root, single_file, true)
                 .expect("Inconsistent bindings");
         });
 
         run_test(|context| {
             let Context { multi_gen, mod_root } = context;
-            let tmp = TempProject::dapptools().unwrap();
 
-            tmp.add_source(
-                "Cargo.toml",
-                r#"
+            let manifest = r#"
  [package]
-        name = "ethers-contract"
-        version = "1.0.0"
-        edition = "2021"
-        rust-version = "1.64"
-        authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
-        license = "MIT OR Apache-2.0"
-        description = "Smart contract bindings for the ethers-rs crate"
-        homepage = "https://docs.rs/ethers"
-        repository = "https://github.com/gakonst/ethers-rs"
-        keywords = ["ethereum", "web3", "celo", "ethers"]
-
-        [dependencies]
-        ethers-contracts = "0.4.0"
-"#,
-            )
-            .unwrap();
-
-            let _ = tmp.compile().unwrap();
-            env::set_var("CARGO_MANIFEST_DIR", tmp.root());
+    name = "ethers-contract"
+    version = "1.0.0"
+    edition = "2021"
+    rust-version = "1.64"
+    authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
+    license = "MIT OR Apache-2.0"
+    description = "Smart contract bindings for the ethers-rs crate"
+    homepage = "https://docs.rs/ethers"
+    repository = "https://github.com/gakonst/ethers-rs"
+    keywords = ["ethereum", "web3", "celo", "ethers"]
+
+    [dependencies]
+    ethers-contracts = "0.4.0"
+"#;
+
+            let root = mod_root.parent().unwrap();
+            fs::write(root.join("../Cargo.toml"), manifest).unwrap();
+            env::set_var("CARGO_MANIFEST_DIR", root);
 
             let single_file = false;
             let name = "a-name";
@@ -1415,11 +1339,10 @@ contract Enum {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -1428,31 +1351,27 @@ contract Enum {
 
         run_test(|context| {
             let Context { multi_gen, mod_root } = context;
-            let tmp = TempProject::dapptools().unwrap();
-
-            tmp.add_source(
-            "Cargo.toml",
-            r#"
-    [package]
-        name = "ethers-contract"
-        version = "1.0.0"
-        edition = "2021"
-        rust-version = "1.64"
-        authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
-        license = "MIT OR Apache-2.0"
-        description = "Smart contract bindings for the ethers-rs crate"
-        homepage = "https://docs.rs/ethers"
-        repository = "https://github.com/gakonst/ethers-rs"
-        keywords = ["ethereum", "web3", "celo", "ethers"]
-
-        [dependencies]
-        ethers = {git="https://github.com/gakonst/ethers-rs", rev = "fd8ebf5",features = ["ws", "rustls", "ipc"] }
-"#,
-        )
-        .unwrap();
 
-            let _ = tmp.compile().unwrap();
-            env::set_var("CARGO_MANIFEST_DIR", tmp.root());
+            let manifest = r#"
+[package]
+    name = "ethers-contract"
+    version = "1.0.0"
+    edition = "2021"
+    rust-version = "1.64"
+    authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
+    license = "MIT OR Apache-2.0"
+    description = "Smart contract bindings for the ethers-rs crate"
+    homepage = "https://docs.rs/ethers"
+    repository = "https://github.com/gakonst/ethers-rs"
+    keywords = ["ethereum", "web3", "celo", "ethers"]
+
+    [dependencies]
+    ethers = {git="https://github.com/gakonst/ethers-rs", rev = "fd8ebf5",features = ["ws", "rustls", "ipc"] }
+"#;
+
+            let root = mod_root.parent().unwrap();
+            fs::write(root.join("../Cargo.toml"), manifest).unwrap();
+            env::set_var("CARGO_MANIFEST_DIR", root);
 
             let single_file = false;
             let name = "a-name";
@@ -1462,11 +1381,10 @@ contract Enum {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
@@ -1475,31 +1393,27 @@ contract Enum {
 
         run_test(|context| {
             let Context { multi_gen, mod_root } = context;
-            let tmp = TempProject::dapptools().unwrap();
-
-            tmp.add_source(
-                "Cargo.toml",
-                r#"
-    [package]
-        name = "ethers-contract"
-        version = "1.0.0"
-        edition = "2021"
-        rust-version = "1.64"
-        authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
-        license = "MIT OR Apache-2.0"
-        description = "Smart contract bindings for the ethers-rs crate"
-        homepage = "https://docs.rs/ethers"
-        repository = "https://github.com/gakonst/ethers-rs"
-        keywords = ["ethereum", "web3", "celo", "ethers"]
-
-        [dependencies]
-        ethers = {git="https://github.com/gakonst/ethers-rs" ,features = ["ws", "rustls", "ipc"] }
-"#,
-            )
-            .unwrap();
 
-            let _ = tmp.compile().unwrap();
-            env::set_var("CARGO_MANIFEST_DIR", tmp.root());
+            let manifest = r#"
+[package]
+    name = "ethers-contract"
+    version = "1.0.0"
+    edition = "2021"
+    rust-version = "1.64"
+    authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
+    license = "MIT OR Apache-2.0"
+    description = "Smart contract bindings for the ethers-rs crate"
+    homepage = "https://docs.rs/ethers"
+    repository = "https://github.com/gakonst/ethers-rs"
+    keywords = ["ethereum", "web3", "celo", "ethers"]
+
+    [dependencies]
+    ethers = {git = "https://github.com/gakonst/ethers-rs", features = ["ws", "rustls", "ipc"] }
+"#;
+
+            let root = mod_root.parent().unwrap();
+            fs::write(root.join("../Cargo.toml"), manifest).unwrap();
+            env::set_var("CARGO_MANIFEST_DIR", root);
 
             let single_file = false;
             let name = "a-name";
@@ -1509,11 +1423,10 @@ contract Enum {
                 .clone()
                 .build()
                 .unwrap()
-                .write_to_crate(name, version, mod_root, single_file)
+                .write_to_crate(name, version, &mod_root, single_file)
                 .unwrap();
 
             multi_gen
-                .clone()
                 .build()
                 .unwrap()
                 .ensure_consistent_crate(name, version, mod_root, single_file, true)
diff --git a/ethers-contract/ethers-contract-abigen/src/source/mod.rs b/ethers-contract/ethers-contract-abigen/src/source/mod.rs
index 95f05d4d..a4128844 100644
--- a/ethers-contract/ethers-contract-abigen/src/source/mod.rs
+++ b/ethers-contract/ethers-contract-abigen/src/source/mod.rs
@@ -107,7 +107,13 @@ impl Source {
         if let Ok(canonicalized) = dunce::canonicalize(&resolved) {
             resolved = canonicalized;
         } else {
-            return Err(eyre::eyre!("File does not exist: {}", resolved.display()))
+            let path = resolved.display().to_string();
+            let err = if path.contains(':') {
+                eyre::eyre!("File does not exist: {path}\nYou may need to enable the `online` feature to parse this source.")
+            } else {
+                eyre::eyre!("File does not exist: {path}")
+            };
+            return Err(err)
         }
 
         Ok(Source::Local(resolved))
diff --git a/ethers-contract/ethers-contract-abigen/src/source/online.rs b/ethers-contract/ethers-contract-abigen/src/source/online.rs
index 212aff96..948d3975 100644
--- a/ethers-contract/ethers-contract-abigen/src/source/online.rs
+++ b/ethers-contract/ethers-contract-abigen/src/source/online.rs
@@ -196,7 +196,7 @@ mod tests {
             let tests2 = tests2.collect::<Result<Vec<_>>>().unwrap();
 
             for slice in tests2.windows(2) {
-                let (a, b) = (&slice[0], &slice[1]);
+                let [a, b] = slice else { unreachable!() };
                 if a != b {
                     panic!("Expected: {expected:?}; Got: {a:?} | {b:?}");
                 }
diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs
index 1f771f79..4a24987b 100644
--- a/ethers-contract/src/event.rs
+++ b/ethers-contract/src/event.rs
@@ -177,7 +177,6 @@ where
     ///  while let Some(Ok(approval)) = event_stream.next().await {
     ///      let Approval{token_owner,spender,tokens} = approval;
     /// }
-    ///
     /// # }
     /// ```
     pub async fn stream(
diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs
index 711e44a2..f93f4fb1 100644
--- a/ethers-contract/src/factory.rs
+++ b/ethers-contract/src/factory.rs
@@ -320,25 +320,21 @@ where
 /// # Example
 ///
 /// ```no_run
-/// use ethers_solc::Solc;
 /// use ethers_contract::ContractFactory;
+/// use ethers_core::types::Bytes;
 /// use ethers_providers::{Provider, Http};
-/// use std::convert::TryFrom;
 ///
 /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
-/// // first we'll compile the contract (you can alternatively compile it yourself
-/// // and pass the ABI/Bytecode
-/// let compiled = Solc::default().compile_source("./tests/contract.sol").unwrap();
-/// let contract = compiled
-///     .get("./tests/contract.sol", "SimpleStorage")
-///     .expect("could not find contract");
+/// // get the contract ABI and bytecode
+/// let abi = Default::default();
+/// let bytecode = Bytes::from_static(b"...");
 ///
 /// // connect to the network
 /// let client = Provider::<Http>::try_from("http://localhost:8545").unwrap();
 /// let client = std::sync::Arc::new(client);
 ///
 /// // create a factory which will be used to deploy instances of the contract
-/// let factory = ContractFactory::new(contract.abi.unwrap().clone(), contract.bytecode().unwrap().clone(), client);
+/// let factory = ContractFactory::new(abi, bytecode, client);
 ///
 /// // The deployer created by the `deploy` call exposes a builder which gets consumed
 /// // by the async `send` call
diff --git a/ethers-core/README.md b/ethers-core/README.md
index d4131855..d202258b 100644
--- a/ethers-core/README.md
+++ b/ethers-core/README.md
@@ -12,7 +12,7 @@ For more information, please refer to the [book](https://gakonst.com/ethers-rs).
 
 ## Feature flags
 
--   `eip712`: Provides the `Eip712` trait and derive procedural macro for EIP-712 encoding of typed data.
+-   `eip712`: Does nothing.
 
 ## ABI
 
diff --git a/ethers-core/src/types/serde_helpers.rs b/ethers-core/src/types/serde_helpers.rs
index e36a97f4..72c5f733 100644
--- a/ethers-core/src/types/serde_helpers.rs
+++ b/ethers-core/src/types/serde_helpers.rs
@@ -301,7 +301,6 @@ mod tests {
     use crate::types::U256;
 
     #[test]
-    #[cfg(feature = "eip712")]
     fn test_deserialize_string_chain_id() {
         use crate::types::transaction::eip712::EIP712Domain;
 
diff --git a/ethers-core/src/types/signature.rs b/ethers-core/src/types/signature.rs
index 47ac9cfd..6b0959e1 100644
--- a/ethers-core/src/types/signature.rs
+++ b/ethers-core/src/types/signature.rs
@@ -69,22 +69,6 @@ impl fmt::Display for Signature {
     }
 }
 
-#[cfg(feature = "eip712")]
-impl Signature {
-    /// Recovers the ethereum address which was used to sign a given EIP712
-    /// typed data payload.
-    ///
-    /// Recovery signature data uses 'Electrum' notation, this means the `v`
-    /// value is expected to be either `27` or `28`.
-    pub fn recover_typed_data<T>(&self, payload: T) -> Result<Address, SignatureError>
-    where
-        T: super::transaction::eip712::Eip712,
-    {
-        let encoded = payload.encode_eip712().map_err(|_| SignatureError::RecoveryError)?;
-        self.recover(encoded)
-    }
-}
-
 impl Signature {
     /// Verifies that signature on `message` was produced by `address`
     pub fn verify<M, A>(&self, message: M, address: A) -> Result<(), SignatureError>
@@ -130,6 +114,19 @@ impl Signature {
         Ok(Address::from_slice(&hash[12..]))
     }
 
+    /// Recovers the ethereum address which was used to sign a given EIP712
+    /// typed data payload.
+    ///
+    /// Recovery signature data uses 'Electrum' notation, this means the `v`
+    /// value is expected to be either `27` or `28`.
+    pub fn recover_typed_data<T>(&self, payload: T) -> Result<Address, SignatureError>
+    where
+        T: super::transaction::eip712::Eip712,
+    {
+        let encoded = payload.encode_eip712().map_err(|_| SignatureError::RecoveryError)?;
+        self.recover(encoded)
+    }
+
     /// Retrieves the recovery signature.
     fn as_signature(&self) -> Result<(RecoverableSignature, RecoveryId), SignatureError> {
         let recovery_id = self.recovery_id()?;
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index 6e503985..0885c102 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -52,5 +52,9 @@ tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
 
 [features]
 default = ["rustls"]
-openssl = ["reqwest/native-tls"]
 rustls = ["reqwest/rustls-tls"]
+openssl = ["reqwest/native-tls"]
+
+ethers-solc = ["dep:ethers-solc"]
+solc-full = ["ethers-solc?/full"]
+solc-tests = ["ethers-solc?/tests"]
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index a6f2799e..b72432ef 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -65,5 +65,5 @@ tokio = { workspace = true, features = ["rt-multi-thread", "macros", "time"] }
 [features]
 default = ["rustls"]
 celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo", "ethers-contract/celo"]
-openssl = ["reqwest/native-tls"]
 rustls = ["reqwest/rustls-tls"]
+openssl = ["reqwest/native-tls"]
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index 0de58087..e24c82ca 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -85,10 +85,10 @@ ws = ["tokio-tungstenite", "futures-channel"]
 legacy-ws = ["ws"]
 ipc = ["tokio/io-util", "futures-channel", "winapi"]
 
-openssl = ["tokio-tungstenite/native-tls", "reqwest/native-tls"]
 # we use the webpki roots so we can build static binaries w/o any root cert dependencies
 # on the host
 rustls = ["tokio-tungstenite/rustls-tls-webpki-roots", "reqwest/rustls-tls"]
+openssl = ["tokio-tungstenite/native-tls", "reqwest/native-tls"]
 dev-rpc = []
 
 [dev-dependencies]
diff --git a/ethers-providers/src/rpc/transports/retry.rs b/ethers-providers/src/rpc/transports/retry.rs
index ba283b31..454e59be 100644
--- a/ethers-providers/src/rpc/transports/retry.rs
+++ b/ethers-providers/src/rpc/transports/retry.rs
@@ -561,7 +561,7 @@ mod tests {
         let err: JsonRpcError = serde_json::from_str(s).unwrap();
         let err = ClientError::JsonRpcError(err);
 
-        let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err);
+        let should_retry = HttpRateLimitRetryPolicy.should_retry(&err);
         assert!(should_retry);
     }
 
@@ -574,7 +574,7 @@ mod tests {
             text: s.to_string(),
         };
 
-        let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err);
+        let should_retry = HttpRateLimitRetryPolicy.should_retry(&err);
         assert!(should_retry);
     }
 }
diff --git a/ethers-providers/src/rpc/transports/ws/backend.rs b/ethers-providers/src/rpc/transports/ws/backend.rs
index 6a4bef66..6ae3391e 100644
--- a/ethers-providers/src/rpc/transports/ws/backend.rs
+++ b/ethers-providers/src/rpc/transports/ws/backend.rs
@@ -69,6 +69,15 @@ impl WsBackend {
         Ok(Self::new(ws))
     }
 
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn connect_with_config(
+        details: ConnectionDetails,
+        config: WebSocketConfig,
+    ) -> Result<(Self, BackendDriver), WsClientError> {
+        let ws = connect_async_with_config(details, Some(config)).await?.0.fuse();
+        Ok(Self::new(ws))
+    }
+
     pub fn new(server: InternalStream) -> (Self, BackendDriver) {
         let (handler, to_handle) = mpsc::unbounded();
         let (dispatcher, to_dispatch) = mpsc::unbounded();
diff --git a/ethers-providers/src/rpc/transports/ws/manager.rs b/ethers-providers/src/rpc/transports/ws/manager.rs
index 5f25ad17..5ab3720e 100644
--- a/ethers-providers/src/rpc/transports/ws/manager.rs
+++ b/ethers-providers/src/rpc/transports/ws/manager.rs
@@ -1,3 +1,5 @@
+#[cfg(not(target_arch = "wasm32"))]
+use super::WebSocketConfig;
 use super::{
     backend::{BackendDriver, WsBackend},
     ActiveSub, ConnectionDetails, InFlight, Instruction, Notification, PubSubItem, Response, SubId,
@@ -196,6 +198,9 @@ pub struct RequestManager {
     backend: BackendDriver,
     // The URL and optional auth info for the connection
     conn: ConnectionDetails,
+    #[cfg(not(target_arch = "wasm32"))]
+    // An Option wrapping a tungstenite WebsocketConfig. If None, the default config is used.
+    config: Option<WebSocketConfig>,
     // Instructions from the user-facing providers
     instructions: mpsc::UnboundedReceiver<Instruction>,
 }
@@ -209,16 +214,84 @@ impl RequestManager {
         Self::connect_with_reconnects(conn, DEFAULT_RECONNECTS).await
     }
 
+    async fn connect_internal(
+        conn: ConnectionDetails,
+    ) -> Result<
+        (
+            BackendDriver,
+            (mpsc::UnboundedSender<Instruction>, mpsc::UnboundedReceiver<Instruction>),
+            SharedChannelMap,
+        ),
+        WsClientError,
+    > {
+        let (ws, backend) = WsBackend::connect(conn).await?;
+
+        ws.spawn();
+
+        Ok((backend, mpsc::unbounded(), Default::default()))
+    }
+
+    #[cfg(target_arch = "wasm32")]
     pub async fn connect_with_reconnects(
         conn: ConnectionDetails,
         reconnects: usize,
     ) -> Result<(Self, WsClient), WsClientError> {
-        let (ws, backend) = WsBackend::connect(conn.clone()).await?;
+        let (backend, (instructions_tx, instructions_rx), channel_map) =
+            Self::connect_internal(conn.clone()).await?;
+
+        Ok((
+            Self {
+                id: Default::default(),
+                reconnects,
+                subs: SubscriptionManager::new(channel_map.clone()),
+                reqs: Default::default(),
+                backend,
+                conn,
+                instructions: instructions_rx,
+            },
+            WsClient { instructions: instructions_tx, channel_map },
+        ))
+    }
 
-        let (instructions_tx, instructions_rx) = mpsc::unbounded();
-        let channel_map: SharedChannelMap = Default::default();
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn connect_with_reconnects(
+        conn: ConnectionDetails,
+        reconnects: usize,
+    ) -> Result<(Self, WsClient), WsClientError> {
+        let (backend, (instructions_tx, instructions_rx), channel_map) =
+            Self::connect_internal(conn.clone()).await?;
 
-        ws.spawn();
+        Ok((
+            Self {
+                id: Default::default(),
+                reconnects,
+                subs: SubscriptionManager::new(channel_map.clone()),
+                reqs: Default::default(),
+                backend,
+                conn,
+                config: None,
+                instructions: instructions_rx,
+            },
+            WsClient { instructions: instructions_tx, channel_map },
+        ))
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn connect_with_config(
+        conn: ConnectionDetails,
+        config: WebSocketConfig,
+    ) -> Result<(Self, WsClient), WsClientError> {
+        Self::connect_with_config_and_reconnects(conn, config, DEFAULT_RECONNECTS).await
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn connect_with_config_and_reconnects(
+        conn: ConnectionDetails,
+        config: WebSocketConfig,
+        reconnects: usize,
+    ) -> Result<(Self, WsClient), WsClientError> {
+        let (backend, (instructions_tx, instructions_rx), channel_map) =
+            Self::connect_internal(conn.clone()).await?;
 
         Ok((
             Self {
@@ -228,12 +301,27 @@ impl RequestManager {
                 reqs: Default::default(),
                 backend,
                 conn,
+                config: Some(config),
                 instructions: instructions_rx,
             },
             WsClient { instructions: instructions_tx, channel_map },
         ))
     }
 
+    #[cfg(target_arch = "wasm32")]
+    async fn reconnect_backend(&mut self) -> Result<(WsBackend, BackendDriver), WsClientError> {
+        WsBackend::connect(self.conn.clone()).await
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    async fn reconnect_backend(&mut self) -> Result<(WsBackend, BackendDriver), WsClientError> {
+        if let Some(config) = self.config {
+            WsBackend::connect_with_config(self.conn.clone(), config).await
+        } else {
+            WsBackend::connect(self.conn.clone()).await
+        }
+    }
+
     async fn reconnect(&mut self) -> Result<(), WsClientError> {
         if self.reconnects == 0 {
             return Err(WsClientError::TooManyReconnects)
@@ -242,7 +330,7 @@ impl RequestManager {
 
         tracing::info!(remaining = self.reconnects, url = self.conn.url, "Reconnecting to backend");
         // create the new backend
-        let (s, mut backend) = WsBackend::connect(self.conn.clone()).await?;
+        let (s, mut backend) = self.reconnect_backend().await?;
 
         // spawn the new backend
         s.spawn();
diff --git a/ethers-providers/src/rpc/transports/ws/mod.rs b/ethers-providers/src/rpc/transports/ws/mod.rs
index d10ebd7d..a3fdadf3 100644
--- a/ethers-providers/src/rpc/transports/ws/mod.rs
+++ b/ethers-providers/src/rpc/transports/ws/mod.rs
@@ -50,6 +50,36 @@ impl WsClient {
         Ok(this)
     }
 
+    #[cfg(not(target_arch = "wasm32"))]
+    /// Establishes a new websocket connection. This method allows specifying a custom websocket
+    /// configuration, see the [tungstenite docs](https://docs.rs/tungstenite/latest/tungstenite/protocol/struct.WebSocketConfig.html) for all avaible options.
+    pub async fn connect_with_config(
+        conn: impl Into<ConnectionDetails>,
+        config: impl Into<WebSocketConfig>,
+    ) -> Result<Self, WsClientError> {
+        let (man, this) = RequestManager::connect_with_config(conn.into(), config.into()).await?;
+        man.spawn();
+        Ok(this)
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    /// Establishes a new websocket connection with auto-reconnects. This method allows specifying a
+    /// custom websocket configuration, see the [tungstenite docs](https://docs.rs/tungstenite/latest/tungstenite/protocol/struct.WebSocketConfig.html) for all avaible options.
+    pub async fn connect_with_config_and_reconnects(
+        conn: impl Into<ConnectionDetails>,
+        config: impl Into<WebSocketConfig>,
+        reconnects: usize,
+    ) -> Result<Self, WsClientError> {
+        let (man, this) = RequestManager::connect_with_config_and_reconnects(
+            conn.into(),
+            config.into(),
+            reconnects,
+        )
+        .await?;
+        man.spawn();
+        Ok(this)
+    }
+
     #[tracing::instrument(skip(self, params), err)]
     async fn make_request<R>(&self, method: &str, params: Box<RawValue>) -> Result<R, WsClientError>
     where
diff --git a/ethers-providers/src/rpc/transports/ws/types.rs b/ethers-providers/src/rpc/transports/ws/types.rs
index 5dbcc3ab..168493c5 100644
--- a/ethers-providers/src/rpc/transports/ws/types.rs
+++ b/ethers-providers/src/rpc/transports/ws/types.rs
@@ -257,10 +257,11 @@ mod aliases {
 #[cfg(not(target_arch = "wasm32"))]
 mod aliases {
     pub use tokio_tungstenite::{
-        connect_async,
+        connect_async, connect_async_with_config,
         tungstenite::{self, protocol::CloseFrame},
     };
     use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
+    pub type WebSocketConfig = tungstenite::protocol::WebSocketConfig;
     pub type Message = tungstenite::protocol::Message;
     pub type WsError = tungstenite::Error;
     pub type WsStreamItem = Result<Message, WsError>;
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 10138624..a2919fb0 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -117,8 +117,8 @@ svm-solc = ["svm", "svm-builds", "sha2"]
 project-util = ["tempfile", "fs_extra", "rand"]
 
 tests = []
-openssl = ["svm?/openssl"]
 rustls = ["svm?/rustls"]
+openssl = ["svm?/openssl"]
 
 # Deprecated
 asm = []
diff --git a/ethers/Cargo.toml b/ethers/Cargo.toml
index 9e11adb3..0de80236 100644
--- a/ethers/Cargo.toml
+++ b/ethers/Cargo.toml
@@ -26,53 +26,56 @@ all-features = true
 [features]
 default = ["abigen", "rustls"]
 
+# workspace-wide features
+legacy = ["ethers-core/legacy", "ethers-contract/legacy"]
 celo = [
+    "ethers-contract/celo",
     "ethers-core/celo",
+    "ethers-middleware/celo",
     "ethers-providers/celo",
     "ethers-signers/celo",
-    "ethers-contract/celo",
-    "ethers-middleware/celo",
     "legacy",
 ]
 
-legacy = ["ethers-core/legacy", "ethers-contract/legacy"]
-
-# individual features per sub-crate
-## providers
-ws = ["ethers-providers/ws"]
-legacy-ws = ["ethers-providers/legacy-ws"]
-ipc = ["ethers-providers/ipc"]
 rustls = [
+    "ethers-contract/rustls",
+    "ethers-etherscan/rustls",
     "ethers-middleware/rustls",
     "ethers-providers/rustls",
-    "ethers-etherscan/rustls",
-    "ethers-contract/rustls",
-    "ethers-solc/rustls",
+    "ethers-solc?/rustls",
 ]
 openssl = [
+    "ethers-contract/openssl",
+    "ethers-etherscan/openssl",
     "ethers-middleware/openssl",
     "ethers-providers/openssl",
-    "ethers-etherscan/openssl",
-    "ethers-contract/openssl",
-    "ethers-solc/openssl",
+    "ethers-solc?/openssl",
 ]
+
+# ethers-providers
+ws = ["ethers-providers/ws"]
+legacy-ws = ["ethers-providers/legacy-ws"]
+ipc = ["ethers-providers/ipc"]
 dev-rpc = ["ethers-providers/dev-rpc"]
-## signers
+
+# ethers-signers
 ledger = ["ethers-signers/ledger"]
 trezor = ["ethers-signers/trezor"]
 yubi = ["ethers-signers/yubi"]
-## contracts
+
+# ethers-contracts
 abigen = ["ethers-contract/abigen"]
-### abigen without reqwest
-abigen-offline = ["ethers-contract/abigen-offline"]
-## solc
+abigen-online = ["ethers-contract/abigen-online"]
+
+# ethers-solc
 ethers-solc = ["dep:ethers-solc", "ethers-etherscan/ethers-solc"]
 solc-full = ["ethers-solc?/full"]
 solc-tests = ["ethers-solc?/tests"]
 
 # Deprecated
-solc-sha2-asm = []
+abigen-offline = ["abigen"]
 eip712 = []
+solc-sha2-asm = []
 
 [dependencies]
 ethers-addressbook.workspace = true


Test patch:
diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs
index 7a1cd79b..ccaddaea 100644
--- a/ethers-contract/tests/it/abigen.rs
+++ b/ethers-contract/tests/it/abigen.rs
@@ -3,11 +3,10 @@
 use ethers_contract::{abigen, ContractError, EthCall, EthError, EthEvent};
 use ethers_core::{
     abi::{AbiDecode, AbiEncode, Address, Tokenizable},
-    types::{transaction::eip2718::TypedTransaction, Bytes, Eip1559TransactionRequest, U256},
+    types::{Bytes, U256},
     utils::Anvil,
 };
 use ethers_providers::{MockProvider, Provider};
-use ethers_solc::Solc;
 use std::{fmt::Debug, hash::Hash, sync::Arc};
 
 const fn assert_codec<T: AbiDecode + AbiEncode>() {}
@@ -353,62 +352,6 @@ fn can_handle_even_more_overloaded_functions() {
     let _contract_call = ConsoleLogCalls::Log2(call);
 }
 
-#[tokio::test]
-async fn can_handle_underscore_functions() {
-    abigen!(
-        SimpleStorage,
-        r#"[
-            _hashPuzzle() (uint256)
-        ]"#;
-
-        SimpleStorage2,
-        "ethers-contract/tests/solidity-contracts/simplestorage_abi.json",
-    );
-
-    // launch the network & connect to it
-    let anvil = Anvil::new().spawn();
-    let from = anvil.addresses()[0];
-    let provider = Provider::try_from(anvil.endpoint())
-        .unwrap()
-        .with_sender(from)
-        .interval(std::time::Duration::from_millis(10));
-    let client = Arc::new(provider);
-
-    let contract = "SimpleStorage";
-    let path = "./tests/solidity-contracts/SimpleStorage.sol";
-    let compiled = Solc::default().compile_source(path).unwrap();
-    let compiled = compiled.get(path, contract).unwrap();
-    let factory = ethers_contract::ContractFactory::new(
-        compiled.abi.unwrap().clone(),
-        compiled.bytecode().unwrap().clone(),
-        client.clone(),
-    );
-    let addr = factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address();
-
-    // connect to the contract
-    let contract = SimpleStorage::new(addr, client.clone());
-    let contract2 = SimpleStorage2::new(addr, client.clone());
-
-    let res = contract.hash_puzzle().call().await.unwrap();
-    let res2 = contract2.hash_puzzle().call().await.unwrap();
-    let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap();
-    let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap();
-
-    // Manual call construction
-    use ethers_providers::Middleware;
-    // TODO: How do we handle underscores for calls here?
-    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();
-    let res5 = U256::from(res5.as_ref());
-    assert_eq!(res, 100.into());
-    assert_eq!(res, res2);
-    assert_eq!(res, res3);
-    assert_eq!(res, res4);
-    assert_eq!(res, res5);
-}
-
 #[test]
 fn can_handle_unique_underscore_functions() {
     abigen!(
@@ -561,7 +504,8 @@ async fn can_deploy_greeter() {
 
 #[tokio::test]
 async fn can_abiencoderv2_output() {
-    abigen!(AbiEncoderv2Test, "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",);
+    abigen!(AbiEncoderv2Test, "ethers-contract/tests/solidity-contracts/Abiencoderv2Test.json");
+
     let anvil = Anvil::new().spawn();
     let from = anvil.addresses()[0];
     let provider = Provider::try_from(anvil.endpoint())
@@ -570,20 +514,9 @@ async fn can_abiencoderv2_output() {
         .interval(std::time::Duration::from_millis(10));
     let client = Arc::new(provider);
 
-    let contract = "AbiencoderV2Test";
-    let path = "./tests/solidity-contracts/Abiencoderv2Test.sol";
-    let compiled = Solc::default().compile_source(path).unwrap();
-    let compiled = compiled.get(path, contract).unwrap();
-    let factory = ethers_contract::ContractFactory::new(
-        compiled.abi.unwrap().clone(),
-        compiled.bytecode().unwrap().clone(),
-        client.clone(),
-    );
-    let addr = factory.deploy(()).unwrap().legacy().send().await.unwrap().address();
+    let contract = AbiEncoderv2Test::deploy(client, ()).unwrap().legacy().send().await.unwrap();
 
-    let contract = AbiEncoderv2Test::new(addr, client.clone());
     let person = Person { name: "Alice".to_string(), age: 20u64.into() };
-
     let res = contract.default_person().call().await.unwrap();
     assert_eq!(res, person);
 }
@@ -667,7 +600,7 @@ fn can_generate_seaport_1_0() {
     assert_eq!(hex::encode(FulfillAdvancedOrderCall::selector()), "e7acab24");
 
     assert_codec::<SeaportErrors>();
-    let err = SeaportErrors::BadContractSignature(BadContractSignature::default());
+    let err = SeaportErrors::BadContractSignature(BadContractSignature);
 
     let encoded = err.clone().encode();
     assert_eq!(err, SeaportErrors::decode(encoded.clone()).unwrap());
diff --git a/ethers-contract/tests/it/common.rs b/ethers-contract/tests/it/common.rs
index 5ef32aaa..8e91aec1 100644
--- a/ethers-contract/tests/it/common.rs
+++ b/ethers-contract/tests/it/common.rs
@@ -1,12 +1,11 @@
 use ethers_contract::{Contract, ContractFactory, EthEvent};
 use ethers_core::{
-    abi::Abi,
+    abi::{Abi, JsonAbi},
     types::{Address, Bytes},
     utils::AnvilInstance,
 };
 use ethers_providers::{Http, Middleware, Provider};
-use ethers_solc::Solc;
-use std::{convert::TryFrom, sync::Arc, time::Duration};
+use std::{convert::TryFrom, fs, sync::Arc, time::Duration};
 
 // Note: The `EthEvent` derive macro implements the necessary conversion between `Tokens` and
 // the struct
@@ -21,14 +20,17 @@ pub struct ValueChanged {
     pub new_value: String,
 }
 
-/// compiles the given contract and returns the ABI and Bytecode
+/// Gets the contract ABI and bytecode from a JSON file
 #[track_caller]
-pub fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
+pub fn get_contract(filename: &str) -> (Abi, Bytes) {
     let path = format!("./tests/solidity-contracts/{filename}");
-    let compiled = Solc::default().compile_source(&path).unwrap();
-    let contract = compiled.get(&path, name).expect("could not find contract");
-    let (abi, bin, _) = contract.into_parts_or_default();
-    (abi, bin)
+    let contents = fs::read_to_string(path).unwrap();
+    let obj: JsonAbi = serde_json::from_str(&contents).unwrap();
+    let JsonAbi::Object(obj) = obj else { panic!() };
+    (
+        serde_json::from_str(&serde_json::to_string(&obj.abi).unwrap()).unwrap(),
+        obj.bytecode.unwrap(),
+    )
 }
 
 /// connects the private key to http://localhost:8545
diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs
index eb5b0c8b..fc1a98cd 100644
--- a/ethers-contract/tests/it/contract.rs
+++ b/ethers-contract/tests/it/contract.rs
@@ -1,18 +1,14 @@
 use crate::common::*;
 use ethers_contract::{
-    abigen, ContractFactory, ContractInstance, Eip712, EthAbiType, EthEvent, LogMeta, Multicall,
-    MulticallError, MulticallVersion,
+    abigen, ContractFactory, ContractInstance, EthEvent, LogMeta, Multicall, MulticallError,
+    MulticallVersion,
 };
 use ethers_core::{
     abi::{encode, AbiEncode, Token, Tokenizable},
-    types::{
-        transaction::eip712::*, Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, I256,
-        U256,
-    },
+    types::{Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, U256},
     utils::{keccak256, Anvil},
 };
 use ethers_providers::{Http, Middleware, MiddlewareError, Provider, StreamExt, Ws};
-use ethers_signers::{LocalWallet, Signer};
 use std::{sync::Arc, time::Duration};
 
 #[derive(Debug)]
@@ -61,7 +57,7 @@ impl<M: Middleware> Middleware for NonClone<M> {
 // It exists to ensure that trait bounds on contract internal behave as
 // expected. It should not be run
 fn _it_compiles() {
-    let (abi, _bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, _bytecode) = get_contract("SimpleStorage.json");
 
     // launch anvil
     let anvil = Anvil::new().spawn();
@@ -99,7 +95,7 @@ fn _it_compiles() {
 
 #[tokio::test]
 async fn deploy_and_call_contract() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
 
     // launch anvil
     let anvil = Anvil::new().spawn();
@@ -172,7 +168,7 @@ async fn deploy_and_call_contract() {
 #[tokio::test]
 #[cfg(feature = "abigen")]
 async fn get_past_events() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let anvil = Anvil::new().spawn();
     let client = connect(&anvil, 0);
     let address = client.get_accounts().await.unwrap()[0];
@@ -211,7 +207,7 @@ async fn get_past_events() {
 #[tokio::test]
 #[cfg(feature = "abigen")]
 async fn get_events_with_meta() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let anvil = Anvil::new().spawn();
     let client = connect(&anvil, 0);
     let address = anvil.addresses()[0];
@@ -243,7 +239,7 @@ async fn get_events_with_meta() {
 
 #[tokio::test]
 async fn call_past_state() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let anvil = Anvil::new().spawn();
     let client = connect(&anvil, 0);
     let contract = deploy(client.clone(), abi, bytecode).await;
@@ -297,7 +293,7 @@ async fn call_past_state() {
 #[ignore]
 async fn call_past_hash_test() {
     // geth --dev --http --http.api eth,web3
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let provider = Provider::<Http>::try_from("http://localhost:8545").unwrap();
     let deployer = provider.get_accounts().await.unwrap()[0];
 
@@ -331,7 +327,7 @@ async fn call_past_hash_test() {
 
 #[tokio::test]
 async fn watch_events() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let anvil = Anvil::new().spawn();
     let client = connect(&anvil, 0);
     let contract = deploy(client.clone(), abi.clone(), bytecode).await;
@@ -374,7 +370,7 @@ async fn watch_events() {
 
 #[tokio::test]
 async fn watch_subscription_events_multiple_addresses() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     let anvil = Anvil::new().spawn();
     let client = connect(&anvil, 0);
     let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await;
@@ -418,7 +414,7 @@ async fn build_event_of_type() {
 
 #[tokio::test]
 async fn signer_on_node() {
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
     // spawn anvil
     let anvil = Anvil::new().spawn();
 
@@ -450,14 +446,13 @@ async fn signer_on_node() {
 #[tokio::test]
 async fn multicall_aggregate() {
     // get ABI and bytecode for the Multicall contract
-    let (multicall_abi, multicall_bytecode) = compile_contract("Multicall3", "Multicall.sol");
+    let (multicall_abi, multicall_bytecode) = get_contract("Multicall.json");
 
     // get ABI and bytecode for the NotSoSimpleStorage contract
-    let (not_so_simple_abi, not_so_simple_bytecode) =
-        compile_contract("NotSoSimpleStorage", "NotSoSimpleStorage.sol");
+    let (not_so_simple_abi, not_so_simple_bytecode) = get_contract("NotSoSimpleStorage.json");
 
     // get ABI and bytecode for the SimpleStorage contract
-    let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
+    let (abi, bytecode) = get_contract("SimpleStorage.json");
 
     // launch anvil
     let anvil = Anvil::new().spawn();
@@ -638,8 +633,7 @@ async fn multicall_aggregate() {
 
     // deploy contract with reverting methods
     let reverting_contract = {
-        let (abi, bytecode) =
-            compile_contract("SimpleRevertingStorage", "SimpleRevertingStorage.sol");
+        let (abi, bytecode) = get_contract("SimpleRevertingStorage.json");
         let f = ContractFactory::new(abi, bytecode, client.clone());
         f.deploy("This contract can revert".to_string()).unwrap().send().await.unwrap()
     };
@@ -783,129 +777,3 @@ async fn multicall_aggregate() {
     assert_eq!(bytes[..4], keccak256("CustomErrorWithData(string)")[..4]);
     assert_eq!(bytes[4..], encode(&[Token::String("Data".to_string())]));
 }
-
-#[tokio::test]
-async fn test_derive_eip712() {
-    // Generate Contract ABI Bindings
-    mod contract {
-        ethers_contract::abigen!(
-            DeriveEip712Test,
-            "./ethers-contract/tests/solidity-contracts/DeriveEip712Test.json",
-            derives(serde::Deserialize, serde::Serialize)
-        );
-    }
-
-    // Create derived structs
-
-    #[derive(Debug, Clone, Eip712, EthAbiType)]
-    #[eip712(
-        name = "Eip712Test",
-        version = "1",
-        chain_id = 1,
-        verifying_contract = "0x0000000000000000000000000000000000000001",
-        salt = "eip712-test-75F0CCte"
-    )]
-    struct FooBar {
-        foo: I256,
-        bar: U256,
-        fizz: Bytes,
-        buzz: [u8; 32],
-        far: String,
-        out: Address,
-    }
-
-    // launch the network & connect to it
-    let anvil = Anvil::new().spawn();
-    let wallet: LocalWallet = anvil.keys()[0].clone().into();
-    let provider = Provider::try_from(anvil.endpoint())
-        .unwrap()
-        .with_sender(wallet.address())
-        .interval(std::time::Duration::from_millis(10));
-    let client = Arc::new(provider);
-
-    let contract: contract::DeriveEip712Test<_> =
-        contract::DeriveEip712Test::deploy(client.clone(), ()).unwrap().send().await.unwrap();
-
-    let foo_bar = FooBar {
-        foo: I256::from(10u64),
-        bar: U256::from(20u64),
-        fizz: b"fizz".into(),
-        buzz: keccak256("buzz"),
-        far: String::from("space"),
-        out: Address::zero(),
-    };
-
-    let derived_foo_bar = contract::FooBar {
-        foo: foo_bar.foo,
-        bar: foo_bar.bar,
-        fizz: foo_bar.fizz.clone(),
-        buzz: foo_bar.buzz,
-        far: foo_bar.far.clone(),
-        out: foo_bar.out,
-    };
-
-    let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data");
-
-    let mut r = [0; 32];
-    sig.r.to_big_endian(&mut r);
-    let mut s = [0; 32];
-    sig.s.to_big_endian(&mut s);
-    let v = sig.v as u8;
-
-    let domain_separator = contract
-        .domain_separator()
-        .call()
-        .await
-        .expect("failed to retrieve domain_separator from contract");
-    let type_hash =
-        contract.type_hash().call().await.expect("failed to retrieve type_hash from contract");
-    let struct_hash = contract
-        .struct_hash(derived_foo_bar.clone())
-        .call()
-        .await
-        .expect("failed to retrieve struct_hash from contract");
-    let encoded = contract
-        .encode_eip_712(derived_foo_bar.clone())
-        .call()
-        .await
-        .expect("failed to retrieve eip712 encoded hash from contract");
-    let verify = contract
-        .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v)
-        .call()
-        .await
-        .expect("failed to verify signed typed data eip712 payload");
-
-    assert_eq!(
-        domain_separator,
-        foo_bar
-            .domain()
-            .expect("failed to return domain_separator from Eip712 implemented struct")
-            .separator(),
-        "domain separator does not match contract domain separator!"
-    );
-
-    assert_eq!(
-        type_hash,
-        FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"),
-        "type hash does not match contract struct type hash!"
-    );
-
-    assert_eq!(
-        struct_hash,
-        foo_bar
-            .clone()
-            .struct_hash()
-            .expect("failed to return struct_hash from Eip712 implemented struct"),
-        "struct hash does not match contract struct hash!"
-    );
-
-    assert_eq!(
-        encoded,
-        foo_bar
-            .encode_eip712()
-            .expect("failed to return domain_separator from Eip712 implemented struct"),
-        "Encoded value does not match!"
-    );
-
-    assert!(verify, "typed data signature failed!");
-}
diff --git a/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.json b/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.json
new file mode 100644
index 00000000..d28539c2
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[],"name":"defaultPerson","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"age","type":"uint256"}],"internalType":"struct Hello.Person","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"}],"bytecode":"0x608060405234801561001057600080fd5b506101fe806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ca8b722c14610030575b600080fd5b61003861004e565b60405161004591906101a6565b60405180910390f35b6100566100a6565b60405180604001604052806040518060400160405280600581526020017f416c69636500000000000000000000000000000000000000000000000000000081525081526020016014815250905090565b604051806040016040528060608152602001600081525090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100fa5780820151818401526020810190506100df565b60008484015250505050565b6000601f19601f8301169050919050565b6000610122826100c0565b61012c81856100cb565b935061013c8185602086016100dc565b61014581610106565b840191505092915050565b6000819050919050565b61016381610150565b82525050565b600060408301600083015184820360008601526101868282610117565b915050602083015161019b602086018261015a565b508091505092915050565b600060208201905081810360008301526101c08184610169565b90509291505056fea2646970667358221220b4b2e7e5d170378b15eba0a22e58c197891d2bace43f60ff0dcc8e9b9be5878b64736f6c63430008140033"}
diff --git a/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.sol b/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.sol
index a89fcf02..3137abeb 100644
--- a/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.sol
+++ b/ethers-contract/tests/solidity-contracts/Abiencoderv2Test.sol
@@ -1,6 +1,7 @@
 pragma solidity >=0.6.0;
 pragma experimental ABIEncoderV2;
 
+// note that this file is not synced with Abiencoderv2Test.json
 contract AbiencoderV2Test {
     struct Person {
         string name;
@@ -9,4 +10,4 @@ contract AbiencoderV2Test {
     function defaultPerson() public pure returns (Person memory) {
         return Person("Alice", 20);
     }
-}
\ No newline at end of file
+}
diff --git a/ethers-contract/tests/solidity-contracts/Multicall.json b/ethers-contract/tests/solidity-contracts/Multicall.json
new file mode 100644
index 00000000..fe9993ea
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/Multicall.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3[]","name":"calls","type":"tuple[]"}],"name":"aggregate3","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"allowFailure","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call3Value[]","name":"calls","type":"tuple[]"}],"name":"aggregate3Value","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"blockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getBasefee","outputs":[{"internalType":"uint256","name":"basefee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryAggregate","outputs":[{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"requireSuccess","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall3.Call[]","name":"calls","type":"tuple[]"}],"name":"tryBlockAndAggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"components":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"internalType":"struct Multicall3.Result[]","name":"returnData","type":"tuple[]"}],"stateMutability":"payable","type":"function"}],"bytecode":"608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c0033"}
diff --git a/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.json b/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.json
new file mode 100644
index 00000000..6783442a
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValues","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x60806040523480156200001157600080fd5b506040516200099f3803806200099f8339810160408190526200003491620000c8565b60405160009033907f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c3906200006e906001908690620001ea565b60405180910390a36001620000848282620002e6565b5050620003b2565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620000bf578181015183820152602001620000a5565b50506000910152565b600060208284031215620000db57600080fd5b81516001600160401b0380821115620000f357600080fd5b818401915084601f8301126200010857600080fd5b8151818111156200011d576200011d6200008c565b604051601f8201601f19908116603f011681019083821181831017156200014857620001486200008c565b816040528281528760208487010111156200016257600080fd5b62000175836020830160208801620000a2565b979650505050505050565b600181811c908216806200019557607f821691505b602082108103620001b657634e487b7160e01b600052602260045260246000fd5b50919050565b60008151808452620001d6816020860160208601620000a2565b601f01601f19169290920160200192915050565b604081526000808454620001fe8162000180565b80604086015260606001808416600081146200022357600181146200023e5762000271565b60ff1985168884015283151560051b88018301955062000271565b8960005260208060002060005b86811015620002685781548b82018701529084019082016200024b565b8a018501975050505b505050505082810360208401526200028a8185620001bc565b95945050505050565b601f821115620002e157600081815260208120601f850160051c81016020861015620002bc5750805b601f850160051c820191505b81811015620002dd57828155600101620002c8565b5050505b505050565b81516001600160401b038111156200030257620003026200008c565b6200031a8162000313845462000180565b8462000293565b602080601f831160018114620003525760008415620003395750858301515b600019600386901b1c1916600185901b178555620002dd565b600085815260208120601f198616915b82811015620003835788860151825594840194600190910190840162000362565b5085821015620003a25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6105dd80620003c26000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806319eb4a90146100515780632096525514610070578063256fec881461008557806393a09352146100b0575b600080fd5b6100596100c5565b6040516100679291906102b4565b60405180910390f35b61007861016e565b60405161006791906102de565b600054610098906001600160a01b031681565b6040516001600160a01b039091168152602001610067565b6100c36100be36600461030e565b610200565b005b6000805460018054606093926001600160a01b03169082906100e6906103bf565b80601f0160208091040260200160405190810160405280929190818152602001828054610112906103bf565b801561015f5780601f106101345761010080835404028352916020019161015f565b820191906000526020600020905b81548152906001019060200180831161014257829003601f168201915b50505050509150915091509091565b60606001805461017d906103bf565b80601f01602080910402602001604051908101604052809291908181526020018280546101a9906103bf565b80156101f65780601f106101cb576101008083540402835291602001916101f6565b820191906000526020600020905b8154815290600101906020018083116101d957829003601f168201915b5050505050905090565b6000546040516001600160a01b039091169033907f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c3906102449060019086906103f9565b60405180910390a3600161025882826104e7565b5050600080546001600160a01b03191633179055565b6000815180845260005b8181101561029457602081850181015186830182015201610278565b506000602082860101526020601f19601f83011685010191505092915050565b6040815260006102c7604083018561026e565b905060018060a01b03831660208301529392505050565b6020815260006102f1602083018461026e565b9392505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561032057600080fd5b813567ffffffffffffffff8082111561033857600080fd5b818401915084601f83011261034c57600080fd5b81358181111561035e5761035e6102f8565b604051601f8201601f19908116603f01168101908382118183101715610386576103866102f8565b8160405282815287602084870101111561039f57600080fd5b826020860160208301376000928101602001929092525095945050505050565b600181811c908216806103d357607f821691505b6020821081036103f357634e487b7160e01b600052602260045260246000fd5b50919050565b60408152600080845461040b816103bf565b806040860152606060018084166000811461042d576001811461044757610478565b60ff1985168884015283151560051b880183019550610478565b8960005260208060002060005b8681101561046f5781548b8201870152908401908201610454565b8a018501975050505b5050505050828103602084015261048f818561026e565b95945050505050565b601f8211156104e257600081815260208120601f850160051c810160208610156104bf5750805b601f850160051c820191505b818110156104de578281556001016104cb565b5050505b505050565b815167ffffffffffffffff811115610501576105016102f8565b6105158161050f84546103bf565b84610498565b602080601f83116001811461054a57600084156105325750858301515b600019600386901b1c1916600185901b1785556104de565b600085815260208120601f198616915b828110156105795788860151825594840194600190910190840161055a565b50858210156105975787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea264697066735822122068dd0f301c6213e18e05fbcb533d11475457fc3e899f0fe378c4a9a656d27fe164736f6c63430008140033"}
diff --git a/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.sol b/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.sol
index 2e85a0d2..cb8c471e 100644
--- a/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.sol
+++ b/ethers-contract/tests/solidity-contracts/NotSoSimpleStorage.sol
@@ -1,7 +1,7 @@
 pragma solidity >=0.4.24;
 
+// note that this file is not synced with NotSoSimpleStorage.json
 contract NotSoSimpleStorage {
-
     event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue);
 
     address public lastSender;
diff --git a/ethers-contract/tests/solidity-contracts/ReservedWords/Enum.json b/ethers-contract/tests/solidity-contracts/ReservedWords/Enum.json
new file mode 100644
index 00000000..7eb22568
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/ReservedWords/Enum.json
@@ -0,0 +1,1 @@
+{"abi":[],"bytecode":{"object":"0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220cba471e4b893563e9f7eb67581749116a7d7d3f7f3d5bfc0200c995903418db264736f6c63430008140033","sourceMap":"180:57:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052600080fdfea2646970667358221220cba471e4b893563e9f7eb67581749116a7d7d3f7f3d5bfc0200c995903418db264736f6c63430008140033","sourceMap":"180:57:0:-:0;;;;;","linkReferences":{}},"methodIdentifiers":{},"ast":{"absolutePath":"src/ReservedWords.sol","id":15,"exportedSymbols":{"Enum":[14],"Mod":[10]},"nodeType":"SourceUnit","src":"33:205:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"33:24:0","nodes":[],"literals":["solidity",">=","0.8",".0"]},{"id":10,"nodeType":"ContractDefinition","src":"59:93:0","nodes":[{"id":9,"nodeType":"FunctionDefinition","src":"78:72:0","nodes":[],"body":{"id":8,"nodeType":"Block","src":"125:25:0","nodes":[],"statements":[{"expression":{"hexValue":"31","id":6,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"142:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"functionReturnParameters":5,"id":7,"nodeType":"Return","src":"135:8:0"}]},"functionSelector":"cfae3217","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"87:5:0","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"92:2:0"},"returnParameters":{"id":5,"nodeType":"ParameterList","parameters":[{"constant":false,"id":4,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":9,"src":"116:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":3,"name":"uint256","nodeType":"ElementaryTypeName","src":"116:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"115:9:0"},"scope":10,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Mod","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[10],"name":"Mod","nameLocation":"68:3:0","scope":15,"usedErrors":[],"usedEvents":[]},{"id":14,"nodeType":"ContractDefinition","src":"180:57:0","nodes":[{"id":13,"nodeType":"EnumDefinition","src":"200:35:0","nodes":[],"canonicalName":"Enum.Operation","members":[{"id":11,"name":"Call","nameLocation":"216:4:0","nodeType":"EnumValue","src":"216:4:0"},{"id":12,"name":"DelegateCall","nameLocation":"222:12:0","nodeType":"EnumValue","src":"222:12:0"}],"name":"Operation","nameLocation":"205:9:0"}],"abstract":false,"baseContracts":[],"canonicalName":"Enum","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[14],"name":"Enum","nameLocation":"189:4:0","scope":15,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0}
diff --git a/ethers-contract/tests/solidity-contracts/ReservedWords/Mod.json b/ethers-contract/tests/solidity-contracts/ReservedWords/Mod.json
new file mode 100644
index 00000000..61f8ea29
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/ReservedWords/Mod.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[],"name":"greet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063cfae321714602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea26469706673582212208a0268f04466666dced13350f5feb289a634aea9f3224d9dbd26c2b7b86e8dc464736f6c63430008140033","sourceMap":"59:93:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063cfae321714602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea26469706673582212208a0268f04466666dced13350f5feb289a634aea9f3224d9dbd26c2b7b86e8dc464736f6c63430008140033","sourceMap":"59:93:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;78:72;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;116:7;142:1;135:8;;78:72;:::o;7:77:1:-;44:7;73:5;62:16;;7:77;;;:::o;90:118::-;177:24;195:5;177:24;:::i;:::-;172:3;165:37;90:118;;:::o;214:222::-;307:4;345:2;334:9;330:18;322:26;;358:71;426:1;415:9;411:17;402:6;358:71;:::i;:::-;214:222;;;;:::o","linkReferences":{}},"methodIdentifiers":{"greet()":"cfae3217"},"ast":{"absolutePath":"src/ReservedWords.sol","id":15,"exportedSymbols":{"Enum":[14],"Mod":[10]},"nodeType":"SourceUnit","src":"33:205:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"33:24:0","nodes":[],"literals":["solidity",">=","0.8",".0"]},{"id":10,"nodeType":"ContractDefinition","src":"59:93:0","nodes":[{"id":9,"nodeType":"FunctionDefinition","src":"78:72:0","nodes":[],"body":{"id":8,"nodeType":"Block","src":"125:25:0","nodes":[],"statements":[{"expression":{"hexValue":"31","id":6,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"142:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"functionReturnParameters":5,"id":7,"nodeType":"Return","src":"135:8:0"}]},"functionSelector":"cfae3217","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"87:5:0","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"92:2:0"},"returnParameters":{"id":5,"nodeType":"ParameterList","parameters":[{"constant":false,"id":4,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":9,"src":"116:7:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":3,"name":"uint256","nodeType":"ElementaryTypeName","src":"116:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"115:9:0"},"scope":10,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Mod","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[10],"name":"Mod","nameLocation":"68:3:0","scope":15,"usedErrors":[],"usedEvents":[]},{"id":14,"nodeType":"ContractDefinition","src":"180:57:0","nodes":[{"id":13,"nodeType":"EnumDefinition","src":"200:35:0","nodes":[],"canonicalName":"Enum.Operation","members":[{"id":11,"name":"Call","nameLocation":"216:4:0","nodeType":"EnumValue","src":"216:4:0"},{"id":12,"name":"DelegateCall","nameLocation":"222:12:0","nodeType":"EnumValue","src":"222:12:0"}],"name":"Operation","nameLocation":"205:9:0"}],"abstract":false,"baseContracts":[],"canonicalName":"Enum","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[14],"name":"Enum","nameLocation":"189:4:0","scope":15,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0}
diff --git a/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.json b/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.json
new file mode 100644
index 00000000..1fd070e7
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CustomError","type":"error"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"CustomErrorWithData","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"customError","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"data","type":"string"}],"name":"customErrorWithData","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"emptyRevert","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"rev","type":"bool"}],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"},{"internalType":"bool","name":"rev","type":"bool"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"data","type":"string"}],"name":"stringRevert","outputs":[],"stateMutability":"pure","type":"function"}],"bytecode":"0x60806040523480156200001157600080fd5b5060405162000b7638038062000b768339810160408190526200003491620000c8565b60405160009033907f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c3906200006e906001908690620001ea565b60405180910390a36001620000848282620002e6565b5050620003b2565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620000bf578181015183820152602001620000a5565b50506000910152565b600060208284031215620000db57600080fd5b81516001600160401b0380821115620000f357600080fd5b818401915084601f8301126200010857600080fd5b8151818111156200011d576200011d6200008c565b604051601f8201601f19908116603f011681019083821181831017156200014857620001486200008c565b816040528281528760208487010111156200016257600080fd5b62000175836020830160208801620000a2565b979650505050505050565b600181811c908216806200019557607f821691505b602082108103620001b657634e487b7160e01b600052602260045260246000fd5b50919050565b60008151808452620001d6816020860160208601620000a2565b601f01601f19169290920160200192915050565b604081526000808454620001fe8162000180565b80604086015260606001808416600081146200022357600181146200023e5762000271565b60ff1985168884015283151560051b88018301955062000271565b8960005260208060002060005b86811015620002685781548b82018701529084019082016200024b565b8a018501975050505b505050505082810360208401526200028a8185620001bc565b95945050505050565b601f821115620002e157600081815260208120601f850160051c81016020861015620002bc5750805b601f850160051c820191505b81811015620002dd57828155600101620002c8565b5050505b505050565b81516001600160401b038111156200030257620003026200008c565b6200031a8162000313845462000180565b8462000293565b602080601f831160018114620003525760008415620003395750858301515b600019600386901b1c1916600185901b178555620002dd565b600085815260208120601f198616915b82811015620003835788860151825594840194600190910190840162000362565b5085821015620003a25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6107b480620003c26000396000f3fe60806040526004361061007b5760003560e01c8063c6a533a61161004e578063c6a533a614610114578063d0e30db014610141578063d1597ad714610149578063dda3a7bd1461016957600080fd5b8063256fec8814610080578063489e6152146100bd5780636db65619146100df57806376187141146100f4575b600080fd5b34801561008c57600080fd5b506000546100a0906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100c957600080fd5b506100dd6100d83660046103b8565b61017e565b005b3480156100eb57600080fd5b506100dd600080fd5b34801561010057600080fd5b506100dd61010f36600461047a565b610232565b34801561012057600080fd5b5061013461012f3660046104ec565b61024e565b6040516100b49190610554565b6100dd610322565b34801561015557600080fd5b506100dd61016436600461047a565b610357565b34801561017557600080fd5b506100dd610374565b80156101c35760405162461bcd60e51b815260206004820152600f60248201526e1cd95d15985b1d59481c995d995c9d608a1b60448201526064015b60405180910390fd5b6000546040516001600160a01b039091169033907f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c3906102079060019087906105a1565b60405180910390a3600161021b838261068f565b5050600080546001600160a01b0319163317905550565b818160405162461bcd60e51b81526004016101ba92919061074f565b606081156102905760405162461bcd60e51b815260206004820152600f60248201526e19d95d15985b1d59481c995d995c9d608a1b60448201526064016101ba565b6001805461029d90610567565b80601f01602080910402602001604051908101604052809291908181526020018280546102c990610567565b80156103165780601f106102eb57610100808354040283529160200191610316565b820191906000526020600020905b8154815290600101906020018083116102f957829003601f168201915b50505050509050919050565b6040513481527f4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e384269060200160405180910390a1565b8181604051632187247360e01b81526004016101ba92919061074f565b6040516309caebf360e01b815260040160405180910390fd5b634e487b7160e01b600052604160045260246000fd5b803580151581146103b357600080fd5b919050565b600080604083850312156103cb57600080fd5b823567ffffffffffffffff808211156103e357600080fd5b818501915085601f8301126103f757600080fd5b8135818111156104095761040961038d565b604051601f8201601f19908116603f011681019083821181831017156104315761043161038d565b8160405282815288602084870101111561044a57600080fd5b826020860160208301376000602084830101528096505050505050610471602084016103a3565b90509250929050565b6000806020838503121561048d57600080fd5b823567ffffffffffffffff808211156104a557600080fd5b818501915085601f8301126104b957600080fd5b8135818111156104c857600080fd5b8660208285010111156104da57600080fd5b60209290920196919550909350505050565b6000602082840312156104fe57600080fd5b610507826103a3565b9392505050565b6000815180845260005b8181101561053457602081850181015186830182015201610518565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610507602083018461050e565b600181811c9082168061057b57607f821691505b60208210810361059b57634e487b7160e01b600052602260045260246000fd5b50919050565b6040815260008084546105b381610567565b80604086015260606001808416600081146105d557600181146105ef57610620565b60ff1985168884015283151560051b880183019550610620565b8960005260208060002060005b868110156106175781548b82018701529084019082016105fc565b8a018501975050505b50505050508281036020840152610637818561050e565b95945050505050565b601f82111561068a57600081815260208120601f850160051c810160208610156106675750805b601f850160051c820191505b8181101561068657828155600101610673565b5050505b505050565b815167ffffffffffffffff8111156106a9576106a961038d565b6106bd816106b78454610567565b84610640565b602080601f8311600181146106f257600084156106da5750858301515b600019600386901b1c1916600185901b178555610686565b600085815260208120601f198616915b8281101561072157888601518255948401946001909101908401610702565b508582101561073f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60208152816020820152818360408301376000818301604090810191909152601f909201601f1916010191905056fea264697066735822122010f1ba5f274c3bb6ce9508b17b87a8eddf3e49edd8ff9101d8ef1d81de736bdb64736f6c63430008140033"}
diff --git a/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.sol b/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.sol
index 81f5ec44..4e3bc285 100644
--- a/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.sol
+++ b/ethers-contract/tests/solidity-contracts/SimpleRevertingStorage.sol
@@ -1,5 +1,6 @@
 pragma solidity >=0.8.4;
 
+// note that this file is not synced with SimpleRevertingStorage.json
 contract SimpleRevertingStorage {
     event ValueChanged(
         address indexed author,
diff --git a/ethers-contract/tests/solidity-contracts/SimpleStorage.json b/ethers-contract/tests/solidity-contracts/SimpleStorage.json
new file mode 100644
index 00000000..1523f692
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/SimpleStorage.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"_hashPuzzle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"},{"internalType":"string","name":"value2","type":"string"}],"name":"setValues","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x60806040523480156200001157600080fd5b50604051620011053803806200110583398181016040528101906200003791906200024e565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c36001846040516200009a92919062000405565b60405180910390a38060019081620000b3919062000607565b5050620006ee565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200012482620000d9565b810181811067ffffffffffffffff82111715620001465762000145620000ea565b5b80604052505050565b60006200015b620000bb565b905062000169828262000119565b919050565b600067ffffffffffffffff8211156200018c576200018b620000ea565b5b6200019782620000d9565b9050602081019050919050565b60005b83811015620001c4578082015181840152602081019050620001a7565b60008484015250505050565b6000620001e7620001e1846200016e565b6200014f565b905082815260208101848484011115620002065762000205620000d4565b5b62000213848285620001a4565b509392505050565b600082601f830112620002335762000232620000cf565b5b815162000245848260208601620001d0565b91505092915050565b600060208284031215620002675762000266620000c5565b5b600082015167ffffffffffffffff811115620002885762000287620000ca565b5b62000296848285016200021b565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002e757607f821691505b602082108103620002fd57620002fc6200029f565b5b50919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b600081546200033881620002ce565b62000344818662000303565b945060018216600081146200036257600181146200037957620003b0565b60ff198316865281151560200286019350620003b0565b620003848562000314565b60005b83811015620003a85781548189015260018201915060208101905062000387565b808801955050505b50505092915050565b600081519050919050565b6000620003d182620003b9565b620003dd818562000303565b9350620003ef818560208601620001a4565b620003fa81620000d9565b840191505092915050565b6000604082019050818103600083015262000421818562000329565b90508181036020830152620004378184620003c4565b90509392505050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026200048f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000450565b6200049b868362000450565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620004e8620004e2620004dc84620004b3565b620004bd565b620004b3565b9050919050565b6000819050919050565b6200050483620004c7565b6200051c6200051382620004ef565b8484546200045d565b825550505050565b600090565b6200053362000524565b62000540818484620004f9565b505050565b5b8181101562000568576200055c60008262000529565b60018101905062000546565b5050565b601f821115620005b757620005818162000314565b6200058c8462000440565b810160208510156200059c578190505b620005b4620005ab8562000440565b83018262000545565b50505b505050565b600082821c905092915050565b6000620005dc60001984600802620005bc565b1980831691505092915050565b6000620005f78383620005c9565b9150826002028217905092915050565b6200061282620003b9565b67ffffffffffffffff8111156200062e576200062d620000ea565b5b6200063a8254620002ce565b620006478282856200056c565b600060209050601f8311600181146200067f57600084156200066a578287015190505b620006768582620005e9565b865550620006e6565b601f1984166200068f8662000314565b60005b82811015620006b95784890151825560018201915060208501945060208101905062000692565b86831015620006d95784890151620006d5601f891682620005c9565b8355505b6001600288020188555050505b505050505050565b610a0780620006fe6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063018ba9911461005c578063209652551461007a578063256fec88146100985780637ffaa4b6146100b657806393a09352146100d2575b600080fd5b6100646100ee565b6040516100719190610305565b60405180910390f35b6100826100f7565b60405161008f91906103b0565b60405180910390f35b6100a0610189565b6040516100ad9190610413565b60405180910390f35b6100d060048036038101906100cb9190610577565b6101ad565b005b6100ec60048036038101906100e791906105ef565b610211565b005b60006064905090565b60606001805461010690610667565b80601f016020809104026020016040519081016040528092919081815260200182805461013290610667565b801561017f5780601f106101545761010080835404028352916020019161017f565b820191906000526020600020905b81548152906001019060200180831161016257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b81600190816101bc9190610844565b5080600290816101cc9190610844565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c360018460405161029192919061099a565b60405180910390a380600190816102a89190610844565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000819050919050565b6102ff816102ec565b82525050565b600060208201905061031a60008301846102f6565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561035a57808201518184015260208101905061033f565b60008484015250505050565b6000601f19601f8301169050919050565b600061038282610320565b61038c818561032b565b935061039c81856020860161033c565b6103a581610366565b840191505092915050565b600060208201905081810360008301526103ca8184610377565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103fd826103d2565b9050919050565b61040d816103f2565b82525050565b60006020820190506104286000830184610404565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61048482610366565b810181811067ffffffffffffffff821117156104a3576104a261044c565b5b80604052505050565b60006104b661042e565b90506104c2828261047b565b919050565b600067ffffffffffffffff8211156104e2576104e161044c565b5b6104eb82610366565b9050602081019050919050565b82818337600083830152505050565b600061051a610515846104c7565b6104ac565b90508281526020810184848401111561053657610535610447565b5b6105418482856104f8565b509392505050565b600082601f83011261055e5761055d610442565b5b813561056e848260208601610507565b91505092915050565b6000806040838503121561058e5761058d610438565b5b600083013567ffffffffffffffff8111156105ac576105ab61043d565b5b6105b885828601610549565b925050602083013567ffffffffffffffff8111156105d9576105d861043d565b5b6105e585828601610549565b9150509250929050565b60006020828403121561060557610604610438565b5b600082013567ffffffffffffffff8111156106235761062261043d565b5b61062f84828501610549565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061067f57607f821691505b60208210810361069257610691610638565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026106fa7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826106bd565b61070486836106bd565b95508019841693508086168417925050509392505050565b6000819050919050565b600061074161073c610737846102ec565b61071c565b6102ec565b9050919050565b6000819050919050565b61075b83610726565b61076f61076782610748565b8484546106ca565b825550505050565b600090565b610784610777565b61078f818484610752565b505050565b5b818110156107b3576107a860008261077c565b600181019050610795565b5050565b601f8211156107f8576107c981610698565b6107d2846106ad565b810160208510156107e1578190505b6107f56107ed856106ad565b830182610794565b50505b505050565b600082821c905092915050565b600061081b600019846008026107fd565b1980831691505092915050565b6000610834838361080a565b9150826002028217905092915050565b61084d82610320565b67ffffffffffffffff8111156108665761086561044c565b5b6108708254610667565b61087b8282856107b7565b600060209050601f8311600181146108ae576000841561089c578287015190505b6108a68582610828565b86555061090e565b601f1984166108bc86610698565b60005b828110156108e4578489015182556001820191506020850194506020810190506108bf565b8683101561090157848901516108fd601f89168261080a565b8355505b6001600288020188555050505b505050505050565b6000815461092381610667565b61092d818661032b565b94506001821660008114610948576001811461095e57610991565b60ff198316865281151560200286019350610991565b61096785610698565b60005b838110156109895781548189015260018201915060208101905061096a565b808801955050505b50505092915050565b600060408201905081810360008301526109b48185610916565b905081810360208301526109c88184610377565b9050939250505056fea2646970667358221220705fcdb202c042dcc35f1030c1c2bdb6a549b78a7865c71430a889478fefddb964736f6c63430008140033"}
diff --git a/ethers-contract/tests/solidity-contracts/SimpleStorage.sol b/ethers-contract/tests/solidity-contracts/SimpleStorage.sol
index 85f92db6..ee28b2a3 100644
--- a/ethers-contract/tests/solidity-contracts/SimpleStorage.sol
+++ b/ethers-contract/tests/solidity-contracts/SimpleStorage.sol
@@ -1,7 +1,7 @@
 pragma solidity >=0.4.24;
 
+// note that this file is not synced with SimpleStorage.json
 contract SimpleStorage {
-
     event ValueChanged(address indexed author, address indexed oldAuthor, string oldValue, string newValue);
 
     address public lastSender;
diff --git a/ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json b/ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json
deleted file mode 100644
index 3e9f6dc9..00000000
--- a/ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json
+++ /dev/null
@@ -1,1 +0,0 @@
-[{"inputs":[],"name":"defaultPerson","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"age","type":"uint256"}],"internalType":"struct Hello.Person","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"}]
diff --git a/ethers-contract/tests/solidity-contracts/greeter/Greeter1.json b/ethers-contract/tests/solidity-contracts/greeter/Greeter1.json
new file mode 100644
index 00000000..5513a824
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/greeter/Greeter1.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"components":[{"components":[{"internalType":"bool","name":"a","type":"bool"}],"internalType":"struct Inner","name":"inner","type":"tuple"}],"internalType":"struct Stuff","name":"stuff","type":"tuple"}],"name":"greet","outputs":[{"components":[{"components":[{"internalType":"bool","name":"a","type":"bool"}],"internalType":"struct Inner","name":"inner","type":"tuple"}],"internalType":"struct Stuff","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b50610314806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80638ed3bc5614610030575b600080fd5b61004a600480360381019061004591906100e1565b610060565b6040516100579190610161565b60405180910390f35b610068610080565b8180360381019061007991906102b1565b9050919050565b6040518060200160405280610093610099565b81525090565b60405180602001604052806000151581525090565b6000604051905090565b600080fd5b600080fd5b6000602082840312156100d8576100d76100bd565b5b81905092915050565b6000602082840312156100f7576100f66100b8565b5b6000610105848285016100c2565b91505092915050565b60008115159050919050565b6101238161010e565b82525050565b60208201600082015161013f600085018261011a565b50505050565b60208201600082015161015b6000850182610129565b50505050565b60006020820190506101766000830184610145565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ca82610181565b810181811067ffffffffffffffff821117156101e9576101e8610192565b5b80604052505050565b60006101fc6100ae565b905061020882826101c1565b919050565b6102168161010e565b811461022157600080fd5b50565b6000813590506102338161020d565b92915050565b60006020828403121561024f5761024e61017c565b5b61025960206101f2565b9050600061026984828501610224565b60008301525092915050565b60006020828403121561028b5761028a61017c565b5b61029560206101f2565b905060006102a584828501610239565b60008301525092915050565b6000602082840312156102c7576102c66100b8565b5b60006102d584828501610275565b9150509291505056fea26469706673582212201699ffec341a1a10cb17bbc29ad8639618d7ff9191b8f4e7fe0d9ad70336938a64736f6c63430008140033","sourceMap":"124:128:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80638ed3bc5614610030575b600080fd5b61004a600480360381019061004591906100e1565b610060565b6040516100579190610161565b60405180910390f35b610068610080565b8180360381019061007991906102b1565b9050919050565b6040518060200160405280610093610099565b81525090565b60405180602001604052806000151581525090565b6000604051905090565b600080fd5b600080fd5b6000602082840312156100d8576100d76100bd565b5b81905092915050565b6000602082840312156100f7576100f66100b8565b5b6000610105848285016100c2565b91505092915050565b60008115159050919050565b6101238161010e565b82525050565b60208201600082015161013f600085018261011a565b50505050565b60208201600082015161015b6000850182610129565b50505050565b60006020820190506101766000830184610145565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ca82610181565b810181811067ffffffffffffffff821117156101e9576101e8610192565b5b80604052505050565b60006101fc6100ae565b905061020882826101c1565b919050565b6102168161010e565b811461022157600080fd5b50565b6000813590506102338161020d565b92915050565b60006020828403121561024f5761024e61017c565b5b61025960206101f2565b9050600061026984828501610224565b60008301525092915050565b60006020828403121561028b5761028a61017c565b5b61029560206101f2565b905060006102a584828501610239565b60008301525092915050565b6000602082840312156102c7576102c66100b8565b5b60006102d584828501610275565b9150509291505056fea26469706673582212201699ffec341a1a10cb17bbc29ad8639618d7ff9191b8f4e7fe0d9ad70336938a64736f6c63430008140033","sourceMap":"124:128:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;149:101;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;207:12;;:::i;:::-;238:5;231:12;;;;;;;;;;:::i;:::-;;;149:101;;;:::o;-1:-1:-1:-;;;;;;;;;;;:::i;:::-;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;7:75:1:-;40:6;73:2;67:9;57:19;;7:75;:::o;88:117::-;197:1;194;187:12;334:117;443:1;440;433:12;477:226;545:5;586:2;577:6;572:3;568:16;564:25;561:112;;;592:79;;:::i;:::-;561:112;691:6;682:15;;477:226;;;;:::o;709:373::-;790:6;839:2;827:9;818:7;814:23;810:32;807:119;;;845:79;;:::i;:::-;807:119;965:1;990:75;1057:7;1048:6;1037:9;1033:22;990:75;:::i;:::-;980:85;;936:139;709:373;;;;:::o;1088:90::-;1122:7;1165:5;1158:13;1151:21;1140:32;;1088:90;;;:::o;1184:99::-;1255:21;1270:5;1255:21;:::i;:::-;1250:3;1243:34;1184:99;;:::o;1325:304::-;1452:4;1447:3;1443:14;1536:4;1529:5;1525:16;1519:23;1555:57;1606:4;1601:3;1597:14;1583:12;1555:57;:::i;:::-;1467:155;1421:208;1325:304;;:::o;1671:364::-;1808:4;1803:3;1799:14;1896:4;1889:5;1885:16;1879:23;1915:103;2012:4;2007:3;2003:14;1989:12;1915:103;:::i;:::-;1823:205;1777:258;1671:364;;:::o;2041:302::-;2174:4;2212:2;2201:9;2197:18;2189:26;;2225:111;2333:1;2322:9;2318:17;2309:6;2225:111;:::i;:::-;2041:302;;;;:::o;2349:117::-;2458:1;2455;2448:12;2472:102;2513:6;2564:2;2560:7;2555:2;2548:5;2544:14;2540:28;2530:38;;2472:102;;;:::o;2580:180::-;2628:77;2625:1;2618:88;2725:4;2722:1;2715:15;2749:4;2746:1;2739:15;2766:281;2849:27;2871:4;2849:27;:::i;:::-;2841:6;2837:40;2979:6;2967:10;2964:22;2943:18;2931:10;2928:34;2925:62;2922:88;;;2990:18;;:::i;:::-;2922:88;3030:10;3026:2;3019:22;2809:238;2766:281;;:::o;3053:129::-;3087:6;3114:20;;:::i;:::-;3104:30;;3143:33;3171:4;3163:6;3143:33;:::i;:::-;3053:129;;;:::o;3311:116::-;3381:21;3396:5;3381:21;:::i;:::-;3374:5;3371:32;3361:60;;3417:1;3414;3407:12;3361:60;3311:116;:::o;3433:133::-;3476:5;3514:6;3501:20;3492:29;;3530:30;3554:5;3530:30;:::i;:::-;3433:133;;;;:::o;3592:402::-;3661:5;3705:4;3693:9;3688:3;3684:19;3680:30;3677:117;;;3713:79;;:::i;:::-;3677:117;3812:21;3828:4;3812:21;:::i;:::-;3803:30;;3889:1;3929:46;3971:3;3962:6;3951:9;3947:22;3929:46;:::i;:::-;3922:4;3915:5;3911:16;3904:72;3843:144;3592:402;;;;:::o;4020:429::-;4089:5;4133:4;4121:9;4116:3;4112:19;4108:30;4105:117;;;4141:79;;:::i;:::-;4105:117;4240:21;4256:4;4240:21;:::i;:::-;4231:30;;4321:1;4361:69;4426:3;4417:6;4406:9;4402:22;4361:69;:::i;:::-;4354:4;4347:5;4343:16;4336:95;4271:171;4020:429;;;;:::o;4455:369::-;4534:6;4583:2;4571:9;4562:7;4558:23;4554:32;4551:119;;;4589:79;;:::i;:::-;4551:119;4709:1;4734:73;4799:7;4790:6;4779:9;4775:22;4734:73;:::i;:::-;4724:83;;4680:137;4455:369;;;;:::o","linkReferences":{}},"methodIdentifiers":{"greet(((bool)))":"8ed3bc56"},"ast":{"absolutePath":"src/Greeter.sol","id":35,"exportedSymbols":{"Greeter1":[21],"Greeter2":[34],"Inner":[4],"Stuff":[8]},"nodeType":"SourceUnit","src":"33:350:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"33:24:0","nodes":[],"literals":["solidity",">=","0.8",".0"]},{"id":4,"nodeType":"StructDefinition","src":"59:28:0","nodes":[],"canonicalName":"Inner","members":[{"constant":false,"id":3,"mutability":"mutable","name":"a","nameLocation":"83:1:0","nodeType":"VariableDeclaration","scope":4,"src":"78:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":2,"name":"bool","nodeType":"ElementaryTypeName","src":"78:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"name":"Inner","nameLocation":"66:5:0","scope":35,"visibility":"public"},{"id":8,"nodeType":"StructDefinition","src":"89:33:0","nodes":[],"canonicalName":"Stuff","members":[{"constant":false,"id":7,"mutability":"mutable","name":"inner","nameLocation":"114:5:0","nodeType":"VariableDeclaration","scope":8,"src":"108:11:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_struct$_Inner_$4_storage_ptr","typeString":"struct Inner"},"typeName":{"id":6,"nodeType":"UserDefinedTypeName","pathNode":{"id":5,"name":"Inner","nameLocations":["108:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":4,"src":"108:5:0"},"referencedDeclaration":4,"src":"108:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Inner_$4_storage_ptr","typeString":"struct Inner"}},"visibility":"internal"}],"name":"Stuff","nameLocation":"96:5:0","scope":35,"visibility":"public"},{"id":21,"nodeType":"ContractDefinition","src":"124:128:0","nodes":[{"id":20,"nodeType":"FunctionDefinition","src":"149:101:0","nodes":[],"body":{"id":19,"nodeType":"Block","src":"221:29:0","nodes":[],"statements":[{"expression":{"id":17,"name":"stuff","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":11,"src":"238:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff calldata"}},"functionReturnParameters":16,"id":18,"nodeType":"Return","src":"231:12:0"}]},"functionSelector":"8ed3bc56","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"158:5:0","parameters":{"id":12,"nodeType":"ParameterList","parameters":[{"constant":false,"id":11,"mutability":"mutable","name":"stuff","nameLocation":"179:5:0","nodeType":"VariableDeclaration","scope":20,"src":"164:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff"},"typeName":{"id":10,"nodeType":"UserDefinedTypeName","pathNode":{"id":9,"name":"Stuff","nameLocations":["164:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"164:5:0"},"referencedDeclaration":8,"src":"164:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"163:22:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":20,"src":"207:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_memory_ptr","typeString":"struct Stuff"},"typeName":{"id":14,"nodeType":"UserDefinedTypeName","pathNode":{"id":13,"name":"Stuff","nameLocations":["207:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"207:5:0"},"referencedDeclaration":8,"src":"207:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"206:14:0"},"scope":21,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Greeter1","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[21],"name":"Greeter1","nameLocation":"133:8:0","scope":35,"usedErrors":[],"usedEvents":[]},{"id":34,"nodeType":"ContractDefinition","src":"254:128:0","nodes":[{"id":33,"nodeType":"FunctionDefinition","src":"279:101:0","nodes":[],"body":{"id":32,"nodeType":"Block","src":"351:29:0","nodes":[],"statements":[{"expression":{"id":30,"name":"stuff","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":24,"src":"368:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff calldata"}},"functionReturnParameters":29,"id":31,"nodeType":"Return","src":"361:12:0"}]},"functionSelector":"8ed3bc56","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"288:5:0","parameters":{"id":25,"nodeType":"ParameterList","parameters":[{"constant":false,"id":24,"mutability":"mutable","name":"stuff","nameLocation":"309:5:0","nodeType":"VariableDeclaration","scope":33,"src":"294:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff"},"typeName":{"id":23,"nodeType":"UserDefinedTypeName","pathNode":{"id":22,"name":"Stuff","nameLocations":["294:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"294:5:0"},"referencedDeclaration":8,"src":"294:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"293:22:0"},"returnParameters":{"id":29,"nodeType":"ParameterList","parameters":[{"constant":false,"id":28,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":33,"src":"337:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_memory_ptr","typeString":"struct Stuff"},"typeName":{"id":27,"nodeType":"UserDefinedTypeName","pathNode":{"id":26,"name":"Stuff","nameLocations":["337:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"337:5:0"},"referencedDeclaration":8,"src":"337:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"336:14:0"},"scope":34,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Greeter2","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[34],"name":"Greeter2","nameLocation":"263:8:0","scope":35,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0}
diff --git a/ethers-contract/tests/solidity-contracts/greeter/Greeter2.json b/ethers-contract/tests/solidity-contracts/greeter/Greeter2.json
new file mode 100644
index 00000000..d853b459
--- /dev/null
+++ b/ethers-contract/tests/solidity-contracts/greeter/Greeter2.json
@@ -0,0 +1,1 @@
+{"abi":[{"inputs":[{"components":[{"components":[{"internalType":"bool","name":"a","type":"bool"}],"internalType":"struct Inner","name":"inner","type":"tuple"}],"internalType":"struct Stuff","name":"stuff","type":"tuple"}],"name":"greet","outputs":[{"components":[{"components":[{"internalType":"bool","name":"a","type":"bool"}],"internalType":"struct Inner","name":"inner","type":"tuple"}],"internalType":"struct Stuff","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b50610314806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80638ed3bc5614610030575b600080fd5b61004a600480360381019061004591906100e1565b610060565b6040516100579190610161565b60405180910390f35b610068610080565b8180360381019061007991906102b1565b9050919050565b6040518060200160405280610093610099565b81525090565b60405180602001604052806000151581525090565b6000604051905090565b600080fd5b600080fd5b6000602082840312156100d8576100d76100bd565b5b81905092915050565b6000602082840312156100f7576100f66100b8565b5b6000610105848285016100c2565b91505092915050565b60008115159050919050565b6101238161010e565b82525050565b60208201600082015161013f600085018261011a565b50505050565b60208201600082015161015b6000850182610129565b50505050565b60006020820190506101766000830184610145565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ca82610181565b810181811067ffffffffffffffff821117156101e9576101e8610192565b5b80604052505050565b60006101fc6100ae565b905061020882826101c1565b919050565b6102168161010e565b811461022157600080fd5b50565b6000813590506102338161020d565b92915050565b60006020828403121561024f5761024e61017c565b5b61025960206101f2565b9050600061026984828501610224565b60008301525092915050565b60006020828403121561028b5761028a61017c565b5b61029560206101f2565b905060006102a584828501610239565b60008301525092915050565b6000602082840312156102c7576102c66100b8565b5b60006102d584828501610275565b9150509291505056fea2646970667358221220a9832ba1326877877e1381f7e826f71355979c5861cb6a1d319f02d92a90c25064736f6c63430008140033","sourceMap":"254:128:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80638ed3bc5614610030575b600080fd5b61004a600480360381019061004591906100e1565b610060565b6040516100579190610161565b60405180910390f35b610068610080565b8180360381019061007991906102b1565b9050919050565b6040518060200160405280610093610099565b81525090565b60405180602001604052806000151581525090565b6000604051905090565b600080fd5b600080fd5b6000602082840312156100d8576100d76100bd565b5b81905092915050565b6000602082840312156100f7576100f66100b8565b5b6000610105848285016100c2565b91505092915050565b60008115159050919050565b6101238161010e565b82525050565b60208201600082015161013f600085018261011a565b50505050565b60208201600082015161015b6000850182610129565b50505050565b60006020820190506101766000830184610145565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6101ca82610181565b810181811067ffffffffffffffff821117156101e9576101e8610192565b5b80604052505050565b60006101fc6100ae565b905061020882826101c1565b919050565b6102168161010e565b811461022157600080fd5b50565b6000813590506102338161020d565b92915050565b60006020828403121561024f5761024e61017c565b5b61025960206101f2565b9050600061026984828501610224565b60008301525092915050565b60006020828403121561028b5761028a61017c565b5b61029560206101f2565b905060006102a584828501610239565b60008301525092915050565b6000602082840312156102c7576102c66100b8565b5b60006102d584828501610275565b9150509291505056fea2646970667358221220a9832ba1326877877e1381f7e826f71355979c5861cb6a1d319f02d92a90c25064736f6c63430008140033","sourceMap":"254:128:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;279:101;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;337:12;;:::i;:::-;368:5;361:12;;;;;;;;;;:::i;:::-;;;279:101;;;:::o;-1:-1:-1:-;;;;;;;;;;;:::i;:::-;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;7:75:1:-;40:6;73:2;67:9;57:19;;7:75;:::o;88:117::-;197:1;194;187:12;334:117;443:1;440;433:12;477:226;545:5;586:2;577:6;572:3;568:16;564:25;561:112;;;592:79;;:::i;:::-;561:112;691:6;682:15;;477:226;;;;:::o;709:373::-;790:6;839:2;827:9;818:7;814:23;810:32;807:119;;;845:79;;:::i;:::-;807:119;965:1;990:75;1057:7;1048:6;1037:9;1033:22;990:75;:::i;:::-;980:85;;936:139;709:373;;;;:::o;1088:90::-;1122:7;1165:5;1158:13;1151:21;1140:32;;1088:90;;;:::o;1184:99::-;1255:21;1270:5;1255:21;:::i;:::-;1250:3;1243:34;1184:99;;:::o;1325:304::-;1452:4;1447:3;1443:14;1536:4;1529:5;1525:16;1519:23;1555:57;1606:4;1601:3;1597:14;1583:12;1555:57;:::i;:::-;1467:155;1421:208;1325:304;;:::o;1671:364::-;1808:4;1803:3;1799:14;1896:4;1889:5;1885:16;1879:23;1915:103;2012:4;2007:3;2003:14;1989:12;1915:103;:::i;:::-;1823:205;1777:258;1671:364;;:::o;2041:302::-;2174:4;2212:2;2201:9;2197:18;2189:26;;2225:111;2333:1;2322:9;2318:17;2309:6;2225:111;:::i;:::-;2041:302;;;;:::o;2349:117::-;2458:1;2455;2448:12;2472:102;2513:6;2564:2;2560:7;2555:2;2548:5;2544:14;2540:28;2530:38;;2472:102;;;:::o;2580:180::-;2628:77;2625:1;2618:88;2725:4;2722:1;2715:15;2749:4;2746:1;2739:15;2766:281;2849:27;2871:4;2849:27;:::i;:::-;2841:6;2837:40;2979:6;2967:10;2964:22;2943:18;2931:10;2928:34;2925:62;2922:88;;;2990:18;;:::i;:::-;2922:88;3030:10;3026:2;3019:22;2809:238;2766:281;;:::o;3053:129::-;3087:6;3114:20;;:::i;:::-;3104:30;;3143:33;3171:4;3163:6;3143:33;:::i;:::-;3053:129;;;:::o;3311:116::-;3381:21;3396:5;3381:21;:::i;:::-;3374:5;3371:32;3361:60;;3417:1;3414;3407:12;3361:60;3311:116;:::o;3433:133::-;3476:5;3514:6;3501:20;3492:29;;3530:30;3554:5;3530:30;:::i;:::-;3433:133;;;;:::o;3592:402::-;3661:5;3705:4;3693:9;3688:3;3684:19;3680:30;3677:117;;;3713:79;;:::i;:::-;3677:117;3812:21;3828:4;3812:21;:::i;:::-;3803:30;;3889:1;3929:46;3971:3;3962:6;3951:9;3947:22;3929:46;:::i;:::-;3922:4;3915:5;3911:16;3904:72;3843:144;3592:402;;;;:::o;4020:429::-;4089:5;4133:4;4121:9;4116:3;4112:19;4108:30;4105:117;;;4141:79;;:::i;:::-;4105:117;4240:21;4256:4;4240:21;:::i;:::-;4231:30;;4321:1;4361:69;4426:3;4417:6;4406:9;4402:22;4361:69;:::i;:::-;4354:4;4347:5;4343:16;4336:95;4271:171;4020:429;;;;:::o;4455:369::-;4534:6;4583:2;4571:9;4562:7;4558:23;4554:32;4551:119;;;4589:79;;:::i;:::-;4551:119;4709:1;4734:73;4799:7;4790:6;4779:9;4775:22;4734:73;:::i;:::-;4724:83;;4680:137;4455:369;;;;:::o","linkReferences":{}},"methodIdentifiers":{"greet(((bool)))":"8ed3bc56"},"ast":{"absolutePath":"src/Greeter.sol","id":35,"exportedSymbols":{"Greeter1":[21],"Greeter2":[34],"Inner":[4],"Stuff":[8]},"nodeType":"SourceUnit","src":"33:350:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"33:24:0","nodes":[],"literals":["solidity",">=","0.8",".0"]},{"id":4,"nodeType":"StructDefinition","src":"59:28:0","nodes":[],"canonicalName":"Inner","members":[{"constant":false,"id":3,"mutability":"mutable","name":"a","nameLocation":"83:1:0","nodeType":"VariableDeclaration","scope":4,"src":"78:6:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"},"typeName":{"id":2,"name":"bool","nodeType":"ElementaryTypeName","src":"78:4:0","typeDescriptions":{"typeIdentifier":"t_bool","typeString":"bool"}},"visibility":"internal"}],"name":"Inner","nameLocation":"66:5:0","scope":35,"visibility":"public"},{"id":8,"nodeType":"StructDefinition","src":"89:33:0","nodes":[],"canonicalName":"Stuff","members":[{"constant":false,"id":7,"mutability":"mutable","name":"inner","nameLocation":"114:5:0","nodeType":"VariableDeclaration","scope":8,"src":"108:11:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_struct$_Inner_$4_storage_ptr","typeString":"struct Inner"},"typeName":{"id":6,"nodeType":"UserDefinedTypeName","pathNode":{"id":5,"name":"Inner","nameLocations":["108:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":4,"src":"108:5:0"},"referencedDeclaration":4,"src":"108:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Inner_$4_storage_ptr","typeString":"struct Inner"}},"visibility":"internal"}],"name":"Stuff","nameLocation":"96:5:0","scope":35,"visibility":"public"},{"id":21,"nodeType":"ContractDefinition","src":"124:128:0","nodes":[{"id":20,"nodeType":"FunctionDefinition","src":"149:101:0","nodes":[],"body":{"id":19,"nodeType":"Block","src":"221:29:0","nodes":[],"statements":[{"expression":{"id":17,"name":"stuff","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":11,"src":"238:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff calldata"}},"functionReturnParameters":16,"id":18,"nodeType":"Return","src":"231:12:0"}]},"functionSelector":"8ed3bc56","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"158:5:0","parameters":{"id":12,"nodeType":"ParameterList","parameters":[{"constant":false,"id":11,"mutability":"mutable","name":"stuff","nameLocation":"179:5:0","nodeType":"VariableDeclaration","scope":20,"src":"164:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff"},"typeName":{"id":10,"nodeType":"UserDefinedTypeName","pathNode":{"id":9,"name":"Stuff","nameLocations":["164:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"164:5:0"},"referencedDeclaration":8,"src":"164:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"163:22:0"},"returnParameters":{"id":16,"nodeType":"ParameterList","parameters":[{"constant":false,"id":15,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":20,"src":"207:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_memory_ptr","typeString":"struct Stuff"},"typeName":{"id":14,"nodeType":"UserDefinedTypeName","pathNode":{"id":13,"name":"Stuff","nameLocations":["207:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"207:5:0"},"referencedDeclaration":8,"src":"207:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"206:14:0"},"scope":21,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Greeter1","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[21],"name":"Greeter1","nameLocation":"133:8:0","scope":35,"usedErrors":[],"usedEvents":[]},{"id":34,"nodeType":"ContractDefinition","src":"254:128:0","nodes":[{"id":33,"nodeType":"FunctionDefinition","src":"279:101:0","nodes":[],"body":{"id":32,"nodeType":"Block","src":"351:29:0","nodes":[],"statements":[{"expression":{"id":30,"name":"stuff","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":24,"src":"368:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff calldata"}},"functionReturnParameters":29,"id":31,"nodeType":"Return","src":"361:12:0"}]},"functionSelector":"8ed3bc56","implemented":true,"kind":"function","modifiers":[],"name":"greet","nameLocation":"288:5:0","parameters":{"id":25,"nodeType":"ParameterList","parameters":[{"constant":false,"id":24,"mutability":"mutable","name":"stuff","nameLocation":"309:5:0","nodeType":"VariableDeclaration","scope":33,"src":"294:20:0","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_calldata_ptr","typeString":"struct Stuff"},"typeName":{"id":23,"nodeType":"UserDefinedTypeName","pathNode":{"id":22,"name":"Stuff","nameLocations":["294:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"294:5:0"},"referencedDeclaration":8,"src":"294:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"293:22:0"},"returnParameters":{"id":29,"nodeType":"ParameterList","parameters":[{"constant":false,"id":28,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":33,"src":"337:12:0","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_memory_ptr","typeString":"struct Stuff"},"typeName":{"id":27,"nodeType":"UserDefinedTypeName","pathNode":{"id":26,"name":"Stuff","nameLocations":["337:5:0"],"nodeType":"IdentifierPath","referencedDeclaration":8,"src":"337:5:0"},"referencedDeclaration":8,"src":"337:5:0","typeDescriptions":{"typeIdentifier":"t_struct$_Stuff_$8_storage_ptr","typeString":"struct Stuff"}},"visibility":"internal"}],"src":"336:14:0"},"scope":34,"stateMutability":"pure","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Greeter2","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[34],"name":"Greeter2","nameLocation":"263:8:0","scope":35,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0}
diff --git a/ethers-contract/tests/solidity-contracts/simplestorage_abi.json b/ethers-contract/tests/solidity-contracts/simplestorage_abi.json
deleted file mode 100644
index 9b2fd83f..00000000
--- a/ethers-contract/tests/solidity-contracts/simplestorage_abi.json
+++ /dev/null
@@ -1,1 +0,0 @@
-[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"_hashPuzzle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"},{"internalType":"string","name":"value2","type":"string"}],"name":"setValues","outputs":[],"stateMutability":"nonpayable","type":"function"}]
diff --git a/ethers-middleware/tests/it/main.rs b/ethers-middleware/tests/it/main.rs
index 9f5c3e0f..bf2dc441 100644
--- a/ethers-middleware/tests/it/main.rs
+++ b/ethers-middleware/tests/it/main.rs
@@ -47,3 +47,137 @@ pub async fn spawn_anvil_ws() -> (Provider<Ws>, AnvilInstance) {
 pub fn get_wallet(anvil: &AnvilInstance, idx: usize) -> LocalWallet {
     LocalWallet::from(anvil.keys()[idx].clone()).with_chain_id(anvil.chain_id())
 }
+
+// TODO: Move this to `ethers-tests`
+#[tokio::test]
+async fn test_derive_eip712() {
+    use ethers_contract::{Eip712, EthAbiType};
+    use ethers_core::{
+        types::{transaction::eip712::Eip712, Address, Bytes, I256, U256},
+        utils::{keccak256, Anvil},
+    };
+    use std::sync::Arc;
+
+    // Generate Contract ABI Bindings
+    mod contract {
+        ethers_contract::abigen!(
+            DeriveEip712Test,
+            "./ethers-contract/tests/solidity-contracts/DeriveEip712Test.json",
+            derives(serde::Deserialize, serde::Serialize)
+        );
+    }
+
+    // Create derived structs
+
+    #[derive(Debug, Clone, Eip712, EthAbiType)]
+    #[eip712(
+        name = "Eip712Test",
+        version = "1",
+        chain_id = 1,
+        verifying_contract = "0x0000000000000000000000000000000000000001",
+        salt = "eip712-test-75F0CCte"
+    )]
+    struct FooBar {
+        foo: I256,
+        bar: U256,
+        fizz: Bytes,
+        buzz: [u8; 32],
+        far: String,
+        out: Address,
+    }
+
+    // launch the network & connect to it
+    let anvil = Anvil::new().spawn();
+    let wallet: LocalWallet = anvil.keys()[0].clone().into();
+    let provider = Provider::try_from(anvil.endpoint())
+        .unwrap()
+        .with_sender(wallet.address())
+        .interval(std::time::Duration::from_millis(10));
+    let client = Arc::new(provider);
+
+    let contract: contract::DeriveEip712Test<_> =
+        contract::DeriveEip712Test::deploy(client.clone(), ()).unwrap().send().await.unwrap();
+
+    let foo_bar = FooBar {
+        foo: I256::from(10u64),
+        bar: U256::from(20u64),
+        fizz: b"fizz".into(),
+        buzz: keccak256("buzz"),
+        far: String::from("space"),
+        out: Address::zero(),
+    };
+
+    let derived_foo_bar = contract::FooBar {
+        foo: foo_bar.foo,
+        bar: foo_bar.bar,
+        fizz: foo_bar.fizz.clone(),
+        buzz: foo_bar.buzz,
+        far: foo_bar.far.clone(),
+        out: foo_bar.out,
+    };
+
+    let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data");
+
+    let mut r = [0; 32];
+    sig.r.to_big_endian(&mut r);
+    let mut s = [0; 32];
+    sig.s.to_big_endian(&mut s);
+    let v = sig.v as u8;
+
+    let domain_separator = contract
+        .domain_separator()
+        .call()
+        .await
+        .expect("failed to retrieve domain_separator from contract");
+    let type_hash =
+        contract.type_hash().call().await.expect("failed to retrieve type_hash from contract");
+    let struct_hash = contract
+        .struct_hash(derived_foo_bar.clone())
+        .call()
+        .await
+        .expect("failed to retrieve struct_hash from contract");
+    let encoded = contract
+        .encode_eip_712(derived_foo_bar.clone())
+        .call()
+        .await
+        .expect("failed to retrieve eip712 encoded hash from contract");
+    let verify = contract
+        .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v)
+        .call()
+        .await
+        .expect("failed to verify signed typed data eip712 payload");
+
+    assert_eq!(
+        domain_separator,
+        foo_bar
+            .domain()
+            .expect("failed to return domain_separator from Eip712 implemented struct")
+            .separator(),
+        "domain separator does not match contract domain separator!"
+    );
+
+    assert_eq!(
+        type_hash,
+        FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"),
+        "type hash does not match contract struct type hash!"
+    );
+
+    assert_eq!(
+        struct_hash,
+        foo_bar
+            .clone()
+            .struct_hash()
+            .expect("failed to return struct_hash from Eip712 implemented struct"),
+        "struct hash does not match contract struct hash!"
+    );
+
+    assert_eq!(
+        encoded,
+        foo_bar
+            .encode_eip712()
+            .expect("failed to return domain_separator from Eip712 implemented struct"),
+        "Encoded value does not match!"
+    );
+
+    assert!(verify, "typed data signature failed!");
+}