Repository: gakonst/ethers-rs
Pull request #1489 (2022-07-20 07:08:27)

chore(deps): bump terser from 4.8.0 to 4.8.1 in /examples/ethers-wasm
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/terser/terser/blob/master/CHANGELOG.md">terser's changelog</a>.</em></p>
<blockquote>
<h2>v4.8.1 (backport)</h2>
<ul>
<li>Security fix for RegExps that should not be evaluated (regexp DDOS)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/terser/terser/commits">compare view</a></li>
</ul>
</details>
<br />


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

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

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

---

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

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/gakonst/ethers-rs/network/alerts).

</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13d57870..8ad96f05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Use correct, new transaction type for `typool_content` RPC endpoint [#1501](https://github.com/gakonst/ethers-rs/pull/1501)
 - Fix the default config for generated `BuildInfo` [#1458](https://github.com/gakonst/ethers-rs/pull/1458)
 - Allow configuration of the output directory of the generated `BuildInfo` [#1433](https://github.com/gakonst/ethers-rs/pull/1433)
 - capture unknown fields in `Block` and `Transaction` type via new `OtherFields` type [#1423](https://github.com/gakonst/ethers-rs/pull/1423)
@@ -90,6 +91,10 @@
   [#1030](https://github.com/gakonst/ethers-rs/pull/1030).
 - Generate correct bindings of struct's field names that are reserved words
   [#989](https://github.com/gakonst/ethers-rs/pull/989).
+- Generate correct binding module names that are reserved words
+  [#1498](https://github.com/gakonst/ethers-rs/pull/1498). Note: this changes
+  generated module names to snake case. For example, `MyContract` is now
+  `my_contract` rather than `mycontract_mod`.
 
 ### 0.6.0
 
diff --git a/README.md b/README.md
index 5e76b95a..5a8232a5 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,45 @@ in the transactions which are fetched over JSON-RPC.
 - [ ] FFI Bindings (see note)
 - [ ] CLI for common operations
 
+### Websockets
+
+Websockets support is turned on via the feature-flag `ws`:
+
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ws"] }
+```
+
+### Interprocess Communication (IPC)
+
+IPC support is turned on via the feature-flag `ipc`:
+
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ipc"] }
+```
+
+### HTTP Secure (HTTPS)
+
+If you are looking to connect to a HTTPS endpoint, then you need to enable the `rustls` or `openssl` feature.
+feature-flags.
+
+To enable `rustls`:
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["rustls"] }
+```
+
+To enable `openssl`:
+```toml
+[dependencies]
+
+ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["openssl"] }
+```
+
 ## Note on WASM and FFI bindings
 
 You should be able to build a wasm app that uses ethers-rs (see the [example](./examples/ethers-wasm) for reference). If ethers fails to
diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml
index d011b1e3..8cf9b72e 100644
--- a/ethers-contract/ethers-contract-abigen/Cargo.toml
+++ b/ethers-contract/ethers-contract-abigen/Cargo.toml
@@ -41,4 +41,4 @@ rustls = ["reqwest/rustls-tls"]
 
 [dev-dependencies]
 tempfile = "3.2.0"
-ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util"] }
+ethers-solc = { version = "^0.13.0", path = "../../ethers-solc", default-features = false, features = ["project-util", "svm-solc"] }
diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs
index 1b33ff86..df31a3ec 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract.rs
@@ -101,8 +101,7 @@ impl Context {
     /// Expands the whole rust contract
     pub fn expand(&self) -> Result<ExpandedContract> {
         let name = &self.contract_ident;
-        let name_mod =
-            util::ident(&format!("{}_mod", self.contract_ident.to_string().to_lowercase()));
+        let name_mod = util::ident(&util::safe_module_name(&self.contract_name));
         let abi_name = self.inline_abi_ident();
 
         // 0. Imports
diff --git a/ethers-contract/ethers-contract-abigen/src/contract/common.rs b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
index a4bfb4b7..0a7335c3 100644
--- a/ethers-contract/ethers-contract-abigen/src/contract/common.rs
+++ b/ethers-contract/ethers-contract-abigen/src/contract/common.rs
@@ -42,7 +42,7 @@ pub(crate) fn struct_declaration(cx: &Context) -> TokenStream {
 
     let abi_parse = if !cx.human_readable {
         quote! {
-            pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| serde_json::from_str(#abi)
+            pub static #abi_name: #ethers_contract::Lazy<#ethers_core::abi::Abi> = #ethers_contract::Lazy::new(|| #ethers_core::utils::__serde_json::from_str(#abi)
                                               .expect("invalid abi"));
         }
     } else {
diff --git a/ethers-contract/ethers-contract-abigen/src/lib.rs b/ethers-contract/ethers-contract-abigen/src/lib.rs
index a9b7e76e..a538df71 100644
--- a/ethers-contract/ethers-contract-abigen/src/lib.rs
+++ b/ethers-contract/ethers-contract-abigen/src/lib.rs
@@ -29,7 +29,6 @@ pub use util::parse_address;
 
 use crate::contract::ExpandedContract;
 use eyre::Result;
-use inflector::Inflector;
 use proc_macro2::TokenStream;
 use std::{collections::HashMap, fs::File, io::Write, path::Path};
 
@@ -233,7 +232,7 @@ impl ContractBindings {
 
     /// Generate the default module name (snake case of the contract name)
     pub fn module_name(&self) -> String {
-        self.name.to_snake_case()
+        util::safe_module_name(&self.name)
     }
 
     /// Generate the default filename of the module
diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs
index 594a5797..0023f583 100644
--- a/ethers-contract/ethers-contract-abigen/src/multi.rs
+++ b/ethers-contract/ethers-contract-abigen/src/multi.rs
@@ -559,7 +559,7 @@ serde_json = "1.0.79"
     /// Append module declarations to the `lib.rs` or `mod.rs`
     fn append_module_names(&self, mut buf: impl Write) -> Result<()> {
         let mut mod_names: BTreeSet<_> =
-            self.bindings.keys().map(|name| name.to_snake_case()).collect();
+            self.bindings.keys().map(|name| util::safe_module_name(name)).collect();
         if let Some(ref shared) = self.shared_types {
             mod_names.insert(shared.name.to_snake_case());
         }
@@ -1093,4 +1093,63 @@ contract Greeter2 {
         assert!(content.contains("pub struct Inner"));
         assert!(content.contains("pub struct Stuff"));
     }
+
+    #[test]
+    fn can_sanitize_reserved_words() {
+        let tmp = TempProject::dapptools().unwrap();
+
+        tmp.add_source(
+            "ReservedWords",
+            r#"
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.0;
+
+contract Mod {
+    function greet() public pure returns (uint256) {
+        return 1;
+    }
+}
+
+// from a gnosis contract
+contract Enum {
+    enum Operation {Call, DelegateCall}
+}
+"#,
+        )
+        .unwrap();
+
+        let _ = tmp.compile().unwrap();
+
+        let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
+        let bindings = gen.build().unwrap();
+        let single_file_dir = tmp.root().join("single_bindings");
+        bindings.write_to_module(&single_file_dir, true).unwrap();
+
+        let single_file_mod = single_file_dir.join("mod.rs");
+        assert!(single_file_mod.exists());
+        let content = fs::read_to_string(&single_file_mod).unwrap();
+        assert!(content.contains("pub mod mod_ {"));
+        assert!(content.contains("pub mod enum_ {"));
+
+        // multiple files
+        let gen = MultiAbigen::from_json_files(tmp.artifacts_path()).unwrap();
+        let bindings = gen.build().unwrap();
+        let multi_file_dir = tmp.root().join("multi_bindings");
+        bindings.write_to_module(&multi_file_dir, false).unwrap();
+        let multi_file_mod = multi_file_dir.join("mod.rs");
+        assert!(multi_file_mod.exists());
+        let content = fs::read_to_string(&multi_file_mod).unwrap();
+        assert!(content.contains("pub mod enum_;"));
+        assert!(content.contains("pub mod mod_;"));
+
+        let enum_ = multi_file_dir.join("enum_.rs");
+        assert!(enum_.exists());
+        let content = fs::read_to_string(&enum_).unwrap();
+        assert!(content.contains("pub mod enum_ {"));
+
+        let mod_ = multi_file_dir.join("mod_.rs");
+        assert!(mod_.exists());
+        let content = fs::read_to_string(&mod_).unwrap();
+        assert!(content.contains("pub mod mod_ {"));
+    }
 }
diff --git a/ethers-contract/ethers-contract-abigen/src/util.rs b/ethers-contract/ethers-contract-abigen/src/util.rs
index d9c812e0..33936e09 100644
--- a/ethers-contract/ethers-contract-abigen/src/util.rs
+++ b/ethers-contract/ethers-contract-abigen/src/util.rs
@@ -40,6 +40,12 @@ fn safe_identifier_name(name: String) -> String {
     }
 }
 
+/// converts invalid rust module names to valid ones
+pub fn safe_module_name(name: &str) -> String {
+    // handle reserve words used in contracts (eg Enum is a gnosis contract)
+    safe_ident(&safe_snake_case(name)).to_string()
+}
+
 /// Expands an identifier as snakecase and preserve any leading or trailing underscores
 pub fn safe_snake_case_ident(name: &str) -> Ident {
     let i = name.to_snake_case();
@@ -228,4 +234,12 @@ mod tests {
             Address::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
         assert_eq!(parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(), expected);
     }
+
+    #[test]
+    fn test_safe_module_name() {
+        assert_eq!(safe_module_name("Valid"), "valid");
+        assert_eq!(safe_module_name("Enum"), "enum_");
+        assert_eq!(safe_module_name("Mod"), "mod_");
+        assert_eq!(safe_module_name("2Two"), "_2_two");
+    }
 }
diff --git a/ethers-core/ethers-derive-eip712/src/lib.rs b/ethers-core/ethers-derive-eip712/src/lib.rs
index 3b3c8fd7..a64bb351 100644
--- a/ethers-core/ethers-derive-eip712/src/lib.rs
+++ b/ethers-core/ethers-derive-eip712/src/lib.rs
@@ -61,7 +61,6 @@
 //! determine if there is a nested eip712 struct. However, this work is not yet complete.
 
 #![deny(rustdoc::broken_intra_doc_links)]
-#![deny(unused_crate_dependencies)]
 
 use std::convert::TryFrom;
 
@@ -121,7 +120,7 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
 
             fn type_hash() -> Result<[u8; 32], Self::Error> {
                 use std::convert::TryFrom;
-                let decoded = hex::decode(#type_hash)?;
+                let decoded = #ethers_core::utils::hex::decode(#type_hash)?;
                 let byte_array: [u8; 32] = <[u8; 32]>::try_from(&decoded[..])?;
                 Ok(byte_array)
             }
@@ -129,13 +128,13 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
             // Return the pre-computed domain separator from compile time;
             fn domain_separator(&self) -> Result<[u8; 32], Self::Error> {
                 use std::convert::TryFrom;
-                let decoded = hex::decode(#domain_separator)?;
+                let decoded = #ethers_core::utils::hex::decode(#domain_separator)?;
                 let byte_array: [u8; 32] = <[u8; 32]>::try_from(&decoded[..])?;
                 Ok(byte_array)
             }
 
             fn domain(&self) -> Result<#ethers_core::types::transaction::eip712::EIP712Domain, Self::Error> {
-                let domain: #ethers_core::types::transaction::eip712::EIP712Domain = serde_json::from_str(#domain_str)?;
+                let domain: #ethers_core::types::transaction::eip712::EIP712Domain = # ethers_core::utils::__serde_json::from_str(#domain_str)?;
 
                 Ok(domain)
             }
diff --git a/ethers-core/src/types/transaction/eip712.rs b/ethers-core/src/types/transaction/eip712.rs
index 61d7edcf..5bb7c978 100644
--- a/ethers-core/src/types/transaction/eip712.rs
+++ b/ethers-core/src/types/transaction/eip712.rs
@@ -9,7 +9,7 @@ use syn::{
 use crate::{
     abi,
     abi::{ParamType, Token},
-    types::{Address, H160, U256},
+    types::{Address, U256},
     utils::keccak256,
 };
 
@@ -196,9 +196,14 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
     type Error = TokenStream;
     fn try_from(input: &syn::DeriveInput) -> Result<EIP712Domain, Self::Error> {
         let mut domain = EIP712Domain::default();
+        let mut domain_name = None;
+        let mut domain_version = None;
+        let mut chain_id = None;
+        let mut verifying_contract = None;
+
         let mut found_eip712_attribute = false;
 
-        for attribute in input.attrs.iter() {
+        'attribute_search: for attribute in input.attrs.iter() {
             if let AttrStyle::Outer = attribute.style {
                 if let Ok(syn::Meta::List(meta)) = attribute.parse_meta() {
                     if meta.path.is_ident("eip712") {
@@ -219,7 +224,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                         match ident.to_string().as_ref() {
                                             "name" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.name != String::default() {
+                                                    if domain_name.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain name already specified",
@@ -227,7 +232,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.name = lit_str.value();
+                                                    domain_name = Some(lit_str.value());
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -239,7 +244,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "version" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.version != String::default() {
+                                                    if domain_version.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain version already specified",
@@ -247,7 +252,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.version = lit_str.value();
+                                                    domain_version = Some(lit_str.value());
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -259,7 +264,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "chain_id" => match meta.lit {
                                                 syn::Lit::Int(ref lit_int) => {
-                                                    if domain.chain_id != U256::default() {
+                                                    if chain_id.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain chain_id already specified",
@@ -267,7 +272,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error())
                                                     }
 
-                                                    domain.chain_id = U256::from(
+                                                    chain_id = Some(U256::from(
                                                         lit_int.base10_parse::<u64>().map_err(
                                                             |_| {
                                                                 Error::new(
@@ -277,7 +282,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                                 .to_compile_error()
                                                             },
                                                         )?,
-                                                    );
+                                                    ));
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -289,8 +294,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "verifying_contract" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.verifying_contract != H160::default()
-                                                    {
+                                                    if verifying_contract.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain verifying_contract already specified",
@@ -298,13 +302,13 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                                         .to_compile_error());
                                                     }
 
-                                                    domain.verifying_contract = lit_str.value().parse().map_err(|_| {
+                                                    verifying_contract = Some(lit_str.value().parse().map_err(|_| {
                                                             Error::new(
                                                                 meta.path.span(),
                                                                 "failed to parse verifying contract into Address",
                                                             )
                                                             .to_compile_error()
-                                                        })?;
+                                                        })?);
                                                 }
                                                 _ => {
                                                     return Err(Error::new(
@@ -316,7 +320,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                                             },
                                             "salt" => match meta.lit {
                                                 syn::Lit::Str(ref lit_str) => {
-                                                    if domain.salt != Option::None {
+                                                    if domain.salt.is_some() {
                                                         return Err(Error::new(
                                                             meta.path.span(),
                                                             "domain salt already specified",
@@ -365,36 +369,41 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
                             }
                         }
 
-                        if domain.name == String::default() {
-                            return Err(Error::new(
+                        domain.name = domain_name.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'name'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.version == String::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.version = domain_version.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'version'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.chain_id == U256::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.chain_id = chain_id.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'chain_id'".to_string(),
                             )
-                            .to_compile_error())
-                        }
-                        if domain.verifying_contract == H160::default() {
-                            return Err(Error::new(
+                            .to_compile_error()
+                        })?;
+
+                        domain.verifying_contract = verifying_contract.ok_or_else(|| {
+                            Error::new(
                                 meta.path.span(),
                                 "missing required domain attribute: 'verifying_contract'"
                                     .to_string(),
                             )
-                            .to_compile_error())
-                        }
+                            .to_compile_error()
+                        })?;
                     }
+
+                    break 'attribute_search
                 }
             }
         }
diff --git a/ethers-core/src/types/txpool.rs b/ethers-core/src/types/txpool.rs
index 0b023cf4..a9462089 100644
--- a/ethers-core/src/types/txpool.rs
+++ b/ethers-core/src/types/txpool.rs
@@ -1,5 +1,4 @@
-use crate::types::{Address, TransactionRequest as TxpoolTransaction, U256, U64};
-
+use crate::types::{Address, Bytes, H256, U256, U64};
 use serde::{
     de::{self, Deserializer, Visitor},
     Deserialize, Serialize,
@@ -108,7 +107,7 @@ impl Serialize for TxpoolInspectSummary {
 /// as the ones that are being scheduled for future execution only.
 ///
 /// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details
-#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct TxpoolContent {
     /// pending tx
     pub pending: BTreeMap<Address, BTreeMap<String, TxpoolTransaction>>,
@@ -116,6 +115,23 @@ pub struct TxpoolContent {
     pub queued: BTreeMap<Address, BTreeMap<String, TxpoolTransaction>>,
 }
 
+/// Represents the Transaction object as returned by `txpool_content`
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TxpoolTransaction {
+    pub block_hash: Option<H256>,
+    pub block_number: Option<U64>,
+    pub from: Option<Address>,
+    pub gas: Option<U256>,
+    pub gas_price: Option<U256>,
+    pub hash: H256,
+    pub input: Bytes,
+    pub nonce: U256,
+    pub to: Option<Address>,
+    pub transaction_index: Option<U64>,
+    pub value: U256,
+}
+
 /// Transaction Pool Inspect
 ///
 /// The inspect inspection property can be queried to list a textual summary
@@ -261,7 +277,11 @@ mod tests {
   }
 }"#;
         let deserialized: TxpoolContent = serde_json::from_str(txpool_content_json).unwrap();
-        let serialized: String = serde_json::to_string(&deserialized).unwrap();
+        let serialized: String = serde_json::to_string_pretty(&deserialized).unwrap();
+
+        let origin: serde_json::Value = serde_json::from_str(txpool_content_json).unwrap();
+        let serialized_value = serde_json::to_value(deserialized.clone()).unwrap();
+        assert_eq!(origin, serialized_value);
         assert_eq!(deserialized, serde_json::from_str::<TxpoolContent>(&serialized).unwrap());
     }
 
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 1703276f..b1cf9ca2 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -38,6 +38,12 @@ use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
 use std::convert::{TryFrom, TryInto};
 use thiserror::Error;
 
+/// Re-export of serde-json
+#[doc(hidden)]
+pub mod __serde_json {
+    pub use serde_json::*;
+}
+
 #[derive(Error, Debug)]
 pub enum ConversionError {
     #[error("Unknown units: {0}")]
diff --git a/ethers-etherscan/src/account.rs b/ethers-etherscan/src/account.rs
index 47cecfd9..cc310bd8 100644
--- a/ethers-etherscan/src/account.rs
+++ b/ethers-etherscan/src/account.rs
@@ -607,7 +607,7 @@ mod tests {
                 .await;
             assert!(balances.is_ok());
             let balances = balances.unwrap();
-            assert!(balances.len() == 1);
+            assert_eq!(balances.len(), 1);
         })
         .await
     }
@@ -624,7 +624,6 @@ mod tests {
                     None,
                 )
                 .await;
-            dbg!(&txs);
             assert!(txs.is_ok());
         })
         .await
@@ -726,4 +725,18 @@ mod tests {
         })
         .await
     }
+
+    #[tokio::test]
+    #[serial]
+    async fn get_avalanche_transactions() {
+        if std::env::var("SNOWTRACE_API_KEY").is_err() {
+            // nothing to do if api key unset
+            return
+        }
+        let client = Client::new_from_env(Chain::Avalanche).unwrap();
+        let txs = client
+            .get_transactions(&"0x1549ea9b546ba9ffb306d78a1e1f304760cc4abf".parse().unwrap(), None)
+            .await;
+        assert!(txs.is_ok());
+    }
 }
diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml
index ca8cdd37..16e37f76 100644
--- a/ethers-middleware/Cargo.toml
+++ b/ethers-middleware/Cargo.toml
@@ -14,7 +14,7 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-ethers-contract = { version = "^0.13.0", path = "../ethers-contract", default-features = false, features = ["abigen"] }
+ethers-contract = { version = "^0.13.0", path = "../ethers-contract", default-features = false }
 ethers-core = { version = "^0.13.0", path = "../ethers-core", default-features = false }
 ethers-etherscan = { version = "^0.13.0", path = "../ethers-etherscan", default-features = false }
 ethers-providers = { version = "^0.13.0", path = "../ethers-providers", default-features = false }
@@ -43,7 +43,7 @@ hex = { version = "0.4.3", default-features = false, features = ["std"] }
 rand = { version = "0.8.5", default-features = false }
 ethers-providers = { version = "^0.13.0", path = "../ethers-providers", default-features = false, features = ["ws", "rustls"] }
 once_cell = "1.13.0"
-ethers-solc = { version = "^0.13.0", path = "../ethers-solc", default-features = false }
+ethers-solc = { version = "^0.13.0", path = "../ethers-solc" }
 serial_test = "0.8.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
diff --git a/ethers-middleware/src/transformer/ds_proxy/factory.rs b/ethers-middleware/src/transformer/ds_proxy/factory.rs
index 8de0c88f..c6f0561d 100644
--- a/ethers-middleware/src/transformer/ds_proxy/factory.rs
+++ b/ethers-middleware/src/transformer/ds_proxy/factory.rs
@@ -37,12 +37,16 @@ mod dsproxyfactory_mod {
         Contract, EthEvent, Lazy,
     };
     use ethers_core::{
-        abi::{parse_abi, Abi, Detokenize, InvalidOutputType, Token, Tokenizable},
+        abi::{
+            parse_abi, Abi, Detokenize, InvalidOutputType, ParamType, Token, Tokenizable,
+            TokenizableItem,
+        },
         types::*,
     };
     use ethers_providers::Middleware;
     #[doc = "DsProxyFactory was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"]
     use std::sync::Arc;
+
     pub static DSPROXYFACTORY_ABI: Lazy<Abi> = Lazy::new(|| {
         serde_json :: from_str ("[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"isProxy\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"proxy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cache\",\"type\":\"address\"}],\"name\":\"Created\",\"type\":\"event\"}]\n") . expect ("invalid abi")
     });
@@ -78,18 +82,13 @@ mod dsproxyfactory_mod {
                 .expect("method not found (this should never happen)")
         }
         ///Calls the contract's `build` (0x8e1a55fc) function
-        pub fn build_with_sender(
-            &self,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::Address> {
+        pub fn build_with_sender(&self) -> ContractCall<M, Address> {
             self.0
                 .method_hash([142, 26, 85, 252], ())
                 .expect("method not found (this should never happen)")
         }
         ///Calls the contract's `build` (0xf3701da2) function
-        pub fn build(
-            &self,
-            owner: ethers_core::types::Address,
-        ) -> ethers_contract::builders::ContractCall<M, ethers_core::types::Address> {
+        pub fn build(&self, owner: Address) -> ContractCall<M, Address> {
             self.0
                 .method_hash([243, 112, 29, 162], owner)
                 .expect("method not found (this should never happen)")
@@ -101,24 +100,107 @@ mod dsproxyfactory_mod {
                 .expect("method not found (this should never happen)")
         }
         ///Gets the contract's `Created` event
-        pub fn created_filter(&self) -> ethers_contract::builders::Event<M, CreatedFilter> {
+        pub fn created_filter(&self) -> Event<M, CreatedFilter> {
             self.0.event()
         }
 
         /// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this
         /// contract
-        pub fn events(&self) -> ethers_contract::builders::Event<M, CreatedFilter> {
+        pub fn events(&self) -> Event<M, CreatedFilter> {
             self.0.event_with_filter(Default::default())
         }
     }
-    #[derive(Clone, Debug, Default, Eq, PartialEq, EthEvent)]
-    #[ethevent(name = "Created")]
+
+    #[derive(Clone, Debug, Default, Eq, PartialEq)]
     pub struct CreatedFilter {
-        #[ethevent(indexed)]
         pub sender: Address,
-        #[ethevent(indexed)]
         pub owner: Address,
         pub proxy: Address,
         pub cache: Address,
     }
+
+    impl ethers_core::abi::AbiType for CreatedFilter {
+        fn param_type() -> ParamType {
+            ParamType::Tuple(::std::vec![
+                ParamType::Address; 4
+            ])
+        }
+    }
+    impl ethers_core::abi::AbiArrayType for CreatedFilter {}
+
+    impl Tokenizable for CreatedFilter {
+        fn from_token(token: Token) -> Result<Self, InvalidOutputType>
+        where
+            Self: Sized,
+        {
+            if let Token::Tuple(tokens) = token {
+                if tokens.len() != 4usize {
+                    return Err(InvalidOutputType(format!(
+                        "Expected {} tokens, got {}: {:?}",
+                        4usize,
+                        tokens.len(),
+                        tokens
+                    )))
+                }
+                let mut iter = tokens.into_iter();
+                Ok(Self {
+                    sender: Tokenizable::from_token(iter.next().unwrap())?,
+                    owner: Tokenizable::from_token(iter.next().unwrap())?,
+                    proxy: Tokenizable::from_token(iter.next().unwrap())?,
+                    cache: Tokenizable::from_token(iter.next().unwrap())?,
+                })
+            } else {
+                Err(InvalidOutputType(format!("Expected Tuple, got {:?}", token)))
+            }
+        }
+        fn into_token(self) -> Token {
+            Token::Tuple(::std::vec![
+                self.sender.into_token(),
+                self.owner.into_token(),
+                self.proxy.into_token(),
+                self.cache.into_token(),
+            ])
+        }
+    }
+    impl TokenizableItem for CreatedFilter {}
+
+    impl ethers_contract::EthEvent for CreatedFilter {
+        fn name() -> std::borrow::Cow<'static, str> {
+            "Created".into()
+        }
+        fn signature() -> H256 {
+            H256([
+                37, 155, 48, 202, 57, 136, 92, 109, 128, 26, 11, 93, 188, 152, 134, 64, 243, 194,
+                94, 47, 55, 83, 31, 225, 56, 197, 197, 175, 137, 85, 212, 27,
+            ])
+        }
+        fn abi_signature() -> std::borrow::Cow<'static, str> {
+            "Created(address,address,address,address)".into()
+        }
+        fn decode_log(log: &ethers_core::abi::RawLog) -> Result<Self, ethers_core::abi::Error>
+        where
+            Self: Sized,
+        {
+            let ethers_core::abi::RawLog { data, topics } = log;
+            let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?;
+            if event_signature != &Self::signature() {
+                return Err(ethers_core::abi::Error::InvalidData)
+            }
+            let topic_types = ::std::vec![ParamType::Address, ParamType::Address];
+            let data_types = [ParamType::Address, ParamType::Address];
+            let flat_topics =
+                topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
+            let topic_tokens = ethers_core::abi::decode(&topic_types, &flat_topics)?;
+            if topic_tokens.len() != topics.len() - 1 {
+                return Err(ethers_core::abi::Error::InvalidData)
+            }
+            let data_tokens = ethers_core::abi::decode(&data_types, data)?;
+            let tokens: Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect();
+            Tokenizable::from_token(Token::Tuple(tokens))
+                .map_err(|_| ethers_core::abi::Error::InvalidData)
+        }
+        fn is_anonymous() -> bool {
+            false
+        }
+    }
 }
diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs
index 7958a211..7d4fb06c 100644
--- a/ethers-solc/src/artifact_output/configurable.rs
+++ b/ethers-solc/src/artifact_output/configurable.rs
@@ -5,8 +5,8 @@
 //! `ConfigurableArtifacts` populates a single `Artifact`, the `ConfigurableArtifact`, by default
 //! with essential entries only, such as `abi`, `bytecode`,..., but may include additional values
 //! based on its `ExtraOutputValues` that maps to various objects in the solc contract output, see
-//! also: [`OutputSelection`](crate::artifacts::OutputSelection). In addition to that some output
-//! values can also be emitted as standalone files.
+//! also: [`OutputSelection`](crate::artifacts::output_selection::OutputSelection). In addition to
+//! that some output values can also be emitted as standalone files.
 
 use crate::{
     artifacts::{
diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs
index e1b6217a..4fe6bf5a 100644
--- a/ethers-solc/src/compile/mod.rs
+++ b/ethers-solc/src/compile/mod.rs
@@ -431,7 +431,7 @@ impl Solc {
     }
 
     /// Verify that the checksum for this version of solc is correct. We check against the SHA256
-    /// checksum from the build information published by binaries.soliditylang
+    /// checksum from the build information published by [binaries.soliditylang.org](https://binaries.soliditylang.org/)
     #[cfg(all(feature = "svm-solc"))]
     pub fn verify_checksum(&self) -> Result<()> {
         let version = self.version_short()?;
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index 8d98713b..55238466 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -1,4 +1,6 @@
 #![deny(rustdoc::broken_intra_doc_links)]
+#![allow(rustdoc::private_intra_doc_links)]
+
 pub mod artifacts;
 pub mod sourcemap;
 
@@ -279,7 +281,7 @@ impl<T: ArtifactOutput> Project<T> {
     }
 
     /// Convenience function to compile a series of solidity files with the project's settings.
-    /// Same as [`Self::svm_compile()`] but with the given `files` as input.
+    /// Same as [`Self::compile()`] but with the given `files` as input.
     ///
     /// # Example
     ///
@@ -310,7 +312,7 @@ impl<T: ArtifactOutput> Project<T> {
     }
 
     /// Convenience function to compile only (re)compile files that match the provided [FileFilter].
-    /// Same as [`Self::svm_compile()`] but with only with those files as input that match
+    /// Same as [`Self::compile()`] but with only with those files as input that match
     /// [FileFilter::is_match()].
     ///
     /// # Example - Only compile Test files
diff --git a/examples/ethers-wasm/yarn.lock b/examples/ethers-wasm/yarn.lock
index d17e98c0..65e0c592 100644
--- a/examples/ethers-wasm/yarn.lock
+++ b/examples/ethers-wasm/yarn.lock
@@ -3696,9 +3696,9 @@ source-map-support@0.5.12:
     source-map "^0.6.0"
 
 source-map-support@~0.5.12:
-  version "0.5.19"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
-  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -3900,9 +3900,9 @@ terser-webpack-plugin@^1.4.3:
     worker-farm "^1.7.0"
 
 terser@^4.1.2:
-  version "4.8.0"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
-  integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
+  version "4.8.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
+  integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
   dependencies:
     commander "^2.20.0"
     source-map "~0.6.1"


Test patch:
diff --git a/ethers-contract/tests/abigen.rs b/ethers-contract/tests/abigen.rs
index 3271c551..ecf805f9 100644
--- a/ethers-contract/tests/abigen.rs
+++ b/ethers-contract/tests/abigen.rs
@@ -366,7 +366,7 @@ async fn can_handle_underscore_functions() {
     // Manual call construction
     use ethers_providers::Middleware;
     // TODO: How do we handle underscores for calls here?
-    let data = simplestorage_mod::HashPuzzleCall.encode();
+    let data = simple_storage::HashPuzzleCall.encode();
     let tx = Eip1559TransactionRequest::new().data(data).to(addr);
     let tx = TypedTransaction::Eip1559(tx);
     let res5 = client.call(&tx, None).await.unwrap();
diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs
index acf63042..4d119048 100644
--- a/ethers-contract/tests/contract.rs
+++ b/ethers-contract/tests/contract.rs
@@ -601,7 +601,7 @@ mod eth_tests {
             out: Address::from([0; 20]),
         };
 
-        let derived_foo_bar = deriveeip712test_mod::FooBar {
+        let derived_foo_bar = derive_eip_712_test::FooBar {
             foo: foo_bar.foo,
             bar: foo_bar.bar,
             fizz: foo_bar.fizz.clone(),