Repository: gakonst/ethers-rs
Pull request #2118 (2023-02-06 03:06:03)

chore(deps): bump fs_extra from 1.2.0 to 1.3.0
Bumps [fs_extra](https://github.com/webdesus/fs_extra) from 1.2.0 to 1.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/webdesus/fs_extra/releases">fs_extra's releases</a>.</em></p>
<blockquote>
<h2>Version 1.3.0</h2>
<p>Changes</p>
<ul>
<li>aaa4bffc26cd0b2ae0c70fcf5ceed015e910192a Translation of the code base on 2018 edition</li>
<li><a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/38">#38</a> Remove trailing semicolon from err! macro</li>
<li><a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/51">#51</a> Add builder style methods for CopyOptions</li>
<li><a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/59">#59</a>  dir::get_size returns wrong size for symlinks</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/webdesus/fs_extra/commit/1754296075e7cc4a25feaa876a3f4b9daccc0b98"><code>1754296</code></a> version 1.3.0</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/bb12c0d7cc286614fad8b59b8d9961fac8b7bde2"><code>bb12c0d</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/60">#60</a> from edmorley/fix-dir-get_size</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/c35e984cec3e3c618ad3de78f1071fee491b6eae"><code>c35e984</code></a> Disregarding the size of directories</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/d6ad379094eef164477a9b1dffab9225db2cfa13"><code>d6ad379</code></a> init  variable size_in_bytes a little bit later</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/1bebf1ebe44732f2e04a1176047dd323bb819c7a"><code>1bebf1e</code></a> Fix <code>dir::get_size</code>'s handling of symlinks</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/5fccd175d87e76fba06a8e6f3381d9dc18ef1ed7"><code>5fccd17</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/54">#54</a> from puppetmaster-/patch-1</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/d4529d785fe9e4afe90943b5ec4a274faaabbbff"><code>d4529d7</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/51">#51</a> from jose-acevedoflores/add-builder-methods-for-copy-o...</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/488d8a77ca1d1a783d5ebe030cdbebd4ad10c4c1"><code>488d8a7</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/webdesus/fs_extra/issues/46">#46</a> from kissen/fix/isexist</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/6b90de001c09709ec1e9b407c27e6a7be4efd8ee"><code>6b90de0</code></a> Create CONTRIBUTING.md</li>
<li><a href="https://github.com/webdesus/fs_extra/commit/6a786a01637a9ec160a02d1a1bc17652d933d3ad"><code>6a786a0</code></a> fix get_dir_content2 example</li>
<li>Additional commits viewable in <a href="https://github.com/webdesus/fs_extra/compare/1.2.0...1.3.0">compare view</a></li>
</ul>
</details>
<br />


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

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

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

---

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

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


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d434c479..5510068b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+- Add `Signature::recover_typed_data` [#2120](https://github.com/gakonst/ethers-rs/pull/2120)
 - Add `abi::encode_packed` [#2104](https://github.com/gakonst/ethers-rs/pull/2104)
 - Add support for custom JavaScript tracer to `debug_traceCall` and `debug_traceTransaction` [#2064](https://github.com/gakonst/ethers-rs/pull/2064)
 - Add a `Send` bound to the `IntoFuture` implementation of `ContractCall` [#2083](https://github.com/gakonst/ethers-rs/pull/2083)
@@ -12,6 +13,7 @@
 - Add abigen support for hardhat generated bytecode json format [#2012](https://github.com/gakonst/ethers-rs/pull/2012)
 - Fix typo in `RwClient` docs for `write_client` method.
 - Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949)
+- Add support for Geth built-in tracer and config [#2121](https://github.com/gakonst/ethers-rs/pull/2121)
 - Graceful handling of WebSocket transport errors [#1889](https://github.com/gakonst/ethers-rs/issues/1889) [#1815](https://github.com/gakonst/ethers-rs/issues/1815)
 - `MiddlewareBuilder` trait to instantiate a `Provider` as `Middleware` layers.
 - An `Event` builder can be instantiated specifying the event filter type, without the need to instantiate a contract.
@@ -297,6 +299,10 @@
 
 ### Unreleased
 
+- Make `Factory` objects generic over the borrow trait, to allow non-arc mware
+  [#2103](https://github.com/gakonst/ethers-rs/pull/2103)
+- Make `Contract` objects generic over the borrow trait, to allow non-arc mware
+  [#2082](https://github.com/gakonst/ethers-rs/pull/2082)
 - Return pending transaction from `Multicall::send`
   [#2044](https://github.com/gakonst/ethers-rs/pull/2044)
 - Add abigen to default features
diff --git a/Cargo.lock b/Cargo.lock
index 064fa836..2fee682e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1835,9 +1835,9 @@ dependencies = [
 
 [[package]]
 name = "fs_extra"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
 
 [[package]]
 name = "funty"
@@ -3192,9 +3192,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.50"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
 dependencies = [
  "unicode-ident",
 ]
diff --git a/ethers-contract/src/base.rs b/ethers-contract/src/base.rs
index 56792137..6f0c4395 100644
--- a/ethers-contract/src/base.rs
+++ b/ethers-contract/src/base.rs
@@ -1,4 +1,4 @@
-use crate::Contract;
+use crate::contract::ContractInstance;
 
 pub use ethers_core::abi::AbiError;
 use ethers_core::{
@@ -8,10 +8,10 @@ use ethers_core::{
 use ethers_providers::Middleware;
 
 use std::{
+    borrow::Borrow,
     collections::{BTreeMap, HashMap},
     fmt::Debug,
     hash::Hash,
-    sync::Arc,
 };
 
 /// A reduced form of `Contract` which just takes the `abi` and produces
@@ -195,12 +195,12 @@ impl BaseContract {
     }
 
     /// Upgrades a `BaseContract` into a full fledged contract with an address and middleware.
-    pub fn into_contract<M: Middleware>(
-        self,
-        address: Address,
-        client: impl Into<Arc<M>>,
-    ) -> Contract<M> {
-        Contract::new(address, self, client)
+    pub fn into_contract<B, M>(self, address: Address, client: B) -> ContractInstance<B, M>
+    where
+        B: Borrow<M>,
+        M: Middleware,
+    {
+        ContractInstance::new(address, self, client)
     }
 }
 
diff --git a/ethers-contract/src/call.rs b/ethers-contract/src/call.rs
index 676bd8d8..97923ebd 100644
--- a/ethers-contract/src/call.rs
+++ b/ethers-contract/src/call.rs
@@ -15,12 +15,11 @@ use ethers_providers::{
 };
 
 use std::{
-    borrow::Cow,
+    borrow::{Borrow, Cow},
     fmt::Debug,
     future::{Future, IntoFuture},
     marker::PhantomData,
     pin::Pin,
-    sync::Arc,
 };
 
 use thiserror::Error as ThisError;
@@ -73,33 +72,49 @@ pub enum ContractError<M: Middleware> {
     ContractNotDeployed,
 }
 
+/// `ContractCall` is a [`FunctionCall`] object with an [`std::sync::Arc`] middleware.
+/// This type alias exists to preserve backwards compatibility with
+/// less-abstract Contracts.
+///
+/// For full usage docs, see [`FunctionCall`].
+pub type ContractCall<M, D> = FunctionCall<std::sync::Arc<M>, M, D>;
+
 #[derive(Debug)]
 #[must_use = "contract calls do nothing unless you `send` or `call` them"]
 /// Helper for managing a transaction before submitting it to a node
-pub struct ContractCall<M, D> {
+pub struct FunctionCall<B, M, D> {
     /// The raw transaction object
     pub tx: TypedTransaction,
     /// The ABI of the function being called
     pub function: Function,
     /// Optional block number to be used when calculating the transaction's gas and nonce
     pub block: Option<BlockId>,
-    pub(crate) client: Arc<M>,
+    pub(crate) client: B,
     pub(crate) datatype: PhantomData<D>,
+    pub(crate) _m: PhantomData<M>,
 }
 
-impl<M, D> Clone for ContractCall<M, D> {
+impl<B, M, D> Clone for FunctionCall<B, M, D>
+where
+    B: Clone,
+{
     fn clone(&self) -> Self {
-        ContractCall {
+        FunctionCall {
             tx: self.tx.clone(),
             function: self.function.clone(),
             block: self.block,
             client: self.client.clone(),
             datatype: self.datatype,
+            _m: self._m,
         }
     }
 }
 
-impl<M, D: Detokenize> ContractCall<M, D> {
+impl<B, M, D> FunctionCall<B, M, D>
+where
+    B: Borrow<M>,
+    D: Detokenize,
+{
     /// Sets the `from` field in the transaction to the provided value
     pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
         self.tx.set_from(from.into());
@@ -145,8 +160,9 @@ impl<M, D: Detokenize> ContractCall<M, D> {
     }
 }
 
-impl<M, D> ContractCall<M, D>
+impl<B, M, D> FunctionCall<B, M, D>
 where
+    B: Borrow<M>,
     M: Middleware,
     D: Detokenize,
 {
@@ -157,7 +173,11 @@ where
 
     /// Returns the estimated gas cost for the underlying transaction to be executed
     pub async fn estimate_gas(&self) -> Result<U256, ContractError<M>> {
-        self.client.estimate_gas(&self.tx, self.block).await.map_err(ContractError::MiddlewareError)
+        self.client
+            .borrow()
+            .estimate_gas(&self.tx, self.block)
+            .await
+            .map_err(ContractError::MiddlewareError)
     }
 
     /// Queries the blockchain via an `eth_call` for the provided transaction.
@@ -170,8 +190,12 @@ where
     ///
     /// Note: this function _does not_ send a transaction from your account
     pub async fn call(&self) -> Result<D, ContractError<M>> {
-        let bytes =
-            self.client.call(&self.tx, self.block).await.map_err(ContractError::MiddlewareError)?;
+        let bytes = self
+            .client
+            .borrow()
+            .call(&self.tx, self.block)
+            .await
+            .map_err(ContractError::MiddlewareError)?;
 
         // decode output
         let data = decode_function_data(&self.function, &bytes, false)?;
@@ -202,7 +226,7 @@ where
     ///
     /// Note: this function _does not_ send a transaction from your account
     pub fn call_raw_bytes(&self) -> CallBuilder<'_, M::Provider> {
-        let call = self.client.provider().call_raw(&self.tx);
+        let call = self.client.borrow().provider().call_raw(&self.tx);
         if let Some(block) = self.block {
             call.block(block)
         } else {
@@ -213,17 +237,19 @@ where
     /// Signs and broadcasts the provided transaction
     pub async fn send(&self) -> Result<PendingTransaction<'_, M::Provider>, ContractError<M>> {
         self.client
+            .borrow()
             .send_transaction(self.tx.clone(), self.block)
             .await
             .map_err(ContractError::MiddlewareError)
     }
 }
 
-/// [`ContractCall`] can be turned into [`Future`] automatically with `.await`.
-/// Defaults to calling [`ContractCall::call`].
-impl<M, D> IntoFuture for ContractCall<M, D>
+/// [`FunctionCall`] can be turned into [`Future`] automatically with `.await`.
+/// Defaults to calling [`FunctionCall::call`].
+impl<B, M, D> IntoFuture for FunctionCall<B, M, D>
 where
     Self: 'static,
+    B: Borrow<M> + Send + Sync,
     M: Middleware,
     D: Detokenize + Send + Sync,
 {
diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs
index 09a2d713..b842b120 100644
--- a/ethers-contract/src/contract.rs
+++ b/ethers-contract/src/contract.rs
@@ -1,6 +1,6 @@
 use crate::{
     base::{encode_function_data, AbiError, BaseContract},
-    call::ContractCall,
+    call::FunctionCall,
     event::{EthEvent, Event},
     EthLogDecode,
 };
@@ -9,13 +9,20 @@ use ethers_core::{
     types::{Address, Filter, Selector, ValueOrArray},
 };
 use ethers_providers::Middleware;
-use std::{marker::PhantomData, sync::Arc};
+use std::{borrow::Borrow, fmt::Debug, marker::PhantomData, sync::Arc};
 
 #[cfg(not(feature = "legacy"))]
 use ethers_core::types::Eip1559TransactionRequest;
 #[cfg(feature = "legacy")]
 use ethers_core::types::TransactionRequest;
 
+/// `Contract` is a [`ContractInstance`] object with an `Arc` middleware.
+/// This type alias exists to preserve backwards compatibility with
+/// less-abstract Contracts.
+///
+/// For full usage docs, see [`ContractInstance`].
+pub type Contract<M> = ContractInstance<std::sync::Arc<M>, M>;
+
 /// A Contract is an abstraction of an executable program on the Ethereum Blockchain.
 /// It has code (called byte code) as well as allocated long-term memory
 /// (called storage). Every deployed Contract has an address, which is used to connect
@@ -69,7 +76,7 @@ use ethers_core::types::TransactionRequest;
 /// use ethers_contract::Contract;
 /// use ethers_providers::{Provider, Http};
 /// use ethers_signers::Wallet;
-/// use std::convert::TryFrom;
+/// use std::{convert::TryFrom, sync::Arc};
 ///
 /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
 /// // this is a fake address used just for this example
@@ -82,7 +89,7 @@ use ethers_core::types::TransactionRequest;
 /// let client = Provider::<Http>::try_from("http://localhost:8545").unwrap();
 ///
 /// // create the contract object at the address
-/// let contract = Contract::new(address, abi, client);
+/// let contract = Contract::new(address, abi, Arc::new(client));
 ///
 /// // Calling constant methods is done by calling `call()` on the method builder.
 /// // (if the function takes no arguments, then you must use `()` as the argument)
@@ -116,13 +123,13 @@ use ethers_core::types::TransactionRequest;
 /// use ethers_contract::{Contract, EthEvent};
 /// use ethers_providers::{Provider, Http, Middleware};
 /// use ethers_signers::Wallet;
-/// use std::convert::TryFrom;
+/// use std::{convert::TryFrom, sync::Arc};
 /// use ethers_core::abi::{Detokenize, Token, InvalidOutputType};
 /// # // this is a fake address used just for this example
 /// # let address = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse::<Address>()?;
 /// # let abi: Abi = serde_json::from_str(r#"[]"#)?;
 /// # let client = Provider::<Http>::try_from("http://localhost:8545").unwrap();
-/// # let contract = Contract::new(address, abi, client);
+/// # let contract = Contract::new(address, abi, Arc::new(client));
 ///
 /// #[derive(Clone, Debug, EthEvent)]
 /// struct ValueChanged {
@@ -145,18 +152,40 @@ use ethers_core::types::TransactionRequest;
 ///
 /// _Disclaimer: these above docs have been adapted from the corresponding [ethers.js page](https://docs.ethers.io/ethers.js/html/api-contract.html)_
 ///
+/// # Usage Note
+///
+/// `ContractInternal` accepts any client that implements `B: Borrow<M>` where
+/// `M :Middleware`. Previous `Contract` versions used only arcs, and relied
+/// heavily on [`Arc`]. Due to constraints on the [`FunctionCall`] type,
+/// calling contracts requires a `B: Borrow<M> + Clone`. This is fine for most
+/// middlware. However, when `B` is an owned middleware that is not Clone, we
+/// cannot issue contract calls. Some notable exceptions:
+///
+/// - `NonceManagerMiddleware`
+/// - `SignerMiddleware` (when using a non-Clone Signer)
+///
+/// When using non-Clone middlewares, instead of instantiating a contract that
+/// OWNS the middlware, pass the contract a REFERENCE to the middleware. This
+/// will fix the trait bounds issue (as `&M` is always `Clone`).
+///
+/// We expect to fix this fully in a future version
+///
 /// [`abigen`]: macro.abigen.html
 /// [`Abigen` builder]: struct.Abigen.html
-/// [`event`]: method@crate::Contract::event
-/// [`method`]: method@crate::Contract::method
+/// [`event`]: method@crate::ContractInstance::event
+/// [`method`]: method@crate::ContractInstance::method
 #[derive(Debug)]
-pub struct Contract<M> {
+pub struct ContractInstance<B, M> {
     address: Address,
     base_contract: BaseContract,
-    client: Arc<M>,
+    client: B,
+    _m: PhantomData<M>,
 }
 
-impl<M> std::ops::Deref for Contract<M> {
+impl<B, M> std::ops::Deref for ContractInstance<B, M>
+where
+    B: Borrow<M>,
+{
     type Target = BaseContract;
 
     fn deref(&self) -> &Self::Target {
@@ -164,18 +193,25 @@ impl<M> std::ops::Deref for Contract<M> {
     }
 }
 
-impl<M> Clone for Contract<M> {
+impl<B, M> Clone for ContractInstance<B, M>
+where
+    B: Clone + Borrow<M>,
+{
     fn clone(&self) -> Self {
-        Contract {
+        ContractInstance {
             base_contract: self.base_contract.clone(),
             client: self.client.clone(),
             address: self.address,
+            _m: self._m,
         }
     }
 }
 
-impl<M> Contract<M> {
-    /// Returns the contract's address.
+impl<B, M> ContractInstance<B, M>
+where
+    B: Borrow<M>,
+{
+    /// Returns the contract's address
     pub fn address(&self) -> Address {
         self.address
     }
@@ -186,21 +222,28 @@ impl<M> Contract<M> {
     }
 
     /// Returns a pointer to the contract's client.
-    pub fn client(&self) -> Arc<M> {
-        Arc::clone(&self.client)
+    pub fn client(&self) -> B
+    where
+        B: Clone,
+    {
+        self.client.clone()
     }
 
     /// Returns a reference to the contract's client.
     pub fn client_ref(&self) -> &M {
-        Arc::as_ref(&self.client)
+        self.client.borrow()
     }
 }
 
-impl<M: Middleware> Contract<M> {
+impl<B, M> ContractInstance<B, M>
+where
+    B: Borrow<M>,
+    M: Middleware,
+{
     /// Returns an [`Event`](crate::builders::Event) builder for the provided event.
     /// This function operates in a static context, then it does not require a `self`
     /// to reference to instantiate an [`Event`](crate::builders::Event) builder.
-    pub fn event_of_type<D: EthEvent>(client: &Arc<M>) -> Event<M, D> {
+    pub fn event_of_type<D: EthEvent>(client: &M) -> Event<M, D> {
         Event {
             provider: client,
             filter: Filter::new().event(&D::abi_signature()),
@@ -209,14 +252,14 @@ impl<M: Middleware> Contract<M> {
     }
 }
 
-impl<M: Middleware> Contract<M> {
+impl<B, M> ContractInstance<B, M>
+where
+    B: Borrow<M>,
+    M: Middleware,
+{
     /// Creates a new contract from the provided client, abi and address
-    pub fn new(
-        address: impl Into<Address>,
-        abi: impl Into<BaseContract>,
-        client: impl Into<Arc<M>>,
-    ) -> Self {
-        Self { base_contract: abi.into(), client: client.into(), address: address.into() }
+    pub fn new(address: impl Into<Address>, abi: impl Into<BaseContract>, client: B) -> Self {
+        Self { base_contract: abi.into(), client, address: address.into(), _m: PhantomData }
     }
 
     /// Returns an [`Event`](crate::builders::Event) builder for the provided event.
@@ -227,7 +270,7 @@ impl<M: Middleware> Contract<M> {
     /// Returns an [`Event`](crate::builders::Event) builder with the provided filter.
     pub fn event_with_filter<D: EthLogDecode>(&self, filter: Filter) -> Event<M, D> {
         Event {
-            provider: &self.client,
+            provider: self.client.borrow(),
             filter: filter.address(ValueOrArray::Value(self.address)),
             datatype: PhantomData,
         }
@@ -240,40 +283,49 @@ impl<M: Middleware> Contract<M> {
         Ok(self.event_with_filter(Filter::new().event(&event.abi_signature())))
     }
 
-    /// Returns a transaction builder for the provided function name. If there are
-    /// multiple functions with the same name due to overloading, consider using
-    /// the `method_hash` method instead, since this will use the first match.
-    pub fn method<T: Tokenize, D: Detokenize>(
-        &self,
-        name: &str,
-        args: T,
-    ) -> Result<ContractCall<M, D>, AbiError> {
-        // get the function
-        let function = self.base_contract.abi.function(name)?;
-        self.method_func(function, args)
+    /// Returns a new contract instance using the provided client
+    ///
+    /// Clones `self` internally
+    #[must_use]
+    pub fn connect<N>(&self, client: Arc<N>) -> ContractInstance<Arc<N>, N>
+    where
+        N: Middleware,
+    {
+        ContractInstance {
+            base_contract: self.base_contract.clone(),
+            client,
+            address: self.address,
+            _m: PhantomData,
+        }
     }
 
-    /// Returns a transaction builder for the selected function signature. This should be
-    /// preferred if there are overloaded functions in your smart contract
-    pub fn method_hash<T: Tokenize, D: Detokenize>(
-        &self,
-        signature: Selector,
-        args: T,
-    ) -> Result<ContractCall<M, D>, AbiError> {
-        let function = self
-            .base_contract
-            .methods
-            .get(&signature)
-            .map(|(name, index)| &self.base_contract.abi.functions[name][*index])
-            .ok_or_else(|| Error::InvalidName(hex::encode(signature)))?;
-        self.method_func(function, args)
+    /// Returns a new contract instance using the provided client
+    ///
+    /// Clones `self` internally
+    #[must_use]
+    pub fn connect_with<C, N>(&self, client: C) -> ContractInstance<C, N>
+    where
+        C: Borrow<N>,
+    {
+        ContractInstance {
+            base_contract: self.base_contract.clone(),
+            client,
+            address: self.address,
+            _m: PhantomData,
+        }
     }
+}
 
+impl<B, M> ContractInstance<B, M>
+where
+    B: Clone + Borrow<M>,
+    M: Middleware,
+{
     fn method_func<T: Tokenize, D: Detokenize>(
         &self,
         function: &Function,
         args: T,
-    ) -> Result<ContractCall<M, D>, AbiError> {
+    ) -> Result<FunctionCall<B, M, D>, AbiError> {
         let data = encode_function_data(function, args)?;
 
         #[cfg(feature = "legacy")]
@@ -291,15 +343,45 @@ impl<M: Middleware> Contract<M> {
 
         let tx = tx.into();
 
-        Ok(ContractCall {
+        Ok(FunctionCall {
             tx,
-            client: Arc::clone(&self.client), // cheap clone behind the Arc
+            client: self.client.clone(),
             block: None,
             function: function.to_owned(),
             datatype: PhantomData,
+            _m: self._m,
         })
     }
 
+    /// Returns a transaction builder for the selected function signature. This should be
+    /// preferred if there are overloaded functions in your smart contract
+    pub fn method_hash<T: Tokenize, D: Detokenize>(
+        &self,
+        signature: Selector,
+        args: T,
+    ) -> Result<FunctionCall<B, M, D>, AbiError> {
+        let function = self
+            .base_contract
+            .methods
+            .get(&signature)
+            .map(|(name, index)| &self.base_contract.abi.functions[name][*index])
+            .ok_or_else(|| Error::InvalidName(hex::encode(signature)))?;
+        self.method_func(function, args)
+    }
+
+    /// Returns a transaction builder for the provided function name. If there are
+    /// multiple functions with the same name due to overloading, consider using
+    /// the `method_hash` method instead, since this will use the first match.
+    pub fn method<T: Tokenize, D: Detokenize>(
+        &self,
+        name: &str,
+        args: T,
+    ) -> Result<FunctionCall<B, M, D>, AbiError> {
+        // get the function
+        let function = self.base_contract.abi.function(name)?;
+        self.method_func(function, args)
+    }
+
     /// Returns a new contract instance at `address`.
     ///
     /// Clones `self` internally
@@ -309,12 +391,4 @@ impl<M: Middleware> Contract<M> {
         this.address = address.into();
         this
     }
-
-    /// Returns a new contract instance using the provided client
-    ///
-    /// Clones `self` internally
-    #[must_use]
-    pub fn connect<N>(&self, client: Arc<N>) -> Contract<N> {
-        Contract { base_contract: self.base_contract.clone(), client, address: self.address }
-    }
 }
diff --git a/ethers-contract/src/factory.rs b/ethers-contract/src/factory.rs
index bffb9045..d3248150 100644
--- a/ethers-contract/src/factory.rs
+++ b/ethers-contract/src/factory.rs
@@ -1,4 +1,4 @@
-use crate::{Contract, ContractError};
+use crate::{ContractError, ContractInstance};
 
 use ethers_core::{
     abi::{Abi, Token, Tokenize},
@@ -15,33 +15,62 @@ use ethers_providers::{
 #[cfg(not(feature = "legacy"))]
 use ethers_core::types::Eip1559TransactionRequest;
 
-use std::{marker::PhantomData, sync::Arc};
+use std::{borrow::Borrow, marker::PhantomData, sync::Arc};
 
-/// Helper which manages the deployment transaction of a smart contract.
+/// `ContractDeployer` is a [`ContractDeploymentTx`] object with an
+/// [`Arc`] middleware. This type alias exists to preserve backwards
+/// compatibility with less-abstract Contracts.
 ///
-/// This is just a wrapper type for [Deployer] with an additional type to convert the [Contract]
-/// that the deployer returns when sending the transaction.
+/// For full usage docs, see [`ContractDeploymentTx`].
+pub type ContractDeployer<M, C> = ContractDeploymentTx<Arc<M>, M, C>;
+
+/// `ContractFactory` is a [`DeploymentTxFactory`] object with an
+/// [`Arc`] middleware. This type alias exists to preserve backwards
+/// compatibility with less-abstract Contracts.
+///
+/// For full usage docs, see [`DeploymentTxFactory`].
+pub type ContractFactory<M> = DeploymentTxFactory<Arc<M>, M>;
+
+/// Helper which manages the deployment transaction of a smart contract. It
+/// wraps a deployment transaction, and retrieves the contract address output
+/// by it.
+///
+/// Currently, we recommend using the [`ContractDeployer`] type alias.
 #[derive(Debug)]
-#[must_use = "Deployer does nothing unless you `send` it"]
-pub struct ContractDeployer<M, C> {
+#[must_use = "DeploymentTx does nothing unless you `send` it"]
+pub struct ContractDeploymentTx<B, M, C> {
     /// the actual deployer, exposed for overriding the defaults
-    pub deployer: Deployer<M>,
+    pub deployer: Deployer<B, M>,
     /// marker for the `Contract` type to create afterwards
     ///
     /// this type will be used to construct it via `From::from(Contract)`
     _contract: PhantomData<C>,
 }
 
-impl<M, C> Clone for ContractDeployer<M, C> {
+impl<B, M, C> Clone for ContractDeploymentTx<B, M, C>
+where
+    B: Clone,
+{
     fn clone(&self) -> Self {
-        ContractDeployer { deployer: self.deployer.clone(), _contract: self._contract }
+        ContractDeploymentTx { deployer: self.deployer.clone(), _contract: self._contract }
     }
 }
 
-impl<M: Middleware, C: From<Contract<M>>> ContractDeployer<M, C> {
-    /// Create a new instance of this [ContractDeployer]
-    pub fn new(deployer: Deployer<M>) -> Self {
-        Self { deployer, _contract: Default::default() }
+impl<B, M, C> From<Deployer<B, M>> for ContractDeploymentTx<B, M, C> {
+    fn from(deployer: Deployer<B, M>) -> Self {
+        Self { deployer, _contract: PhantomData }
+    }
+}
+
+impl<B, M, C> ContractDeploymentTx<B, M, C>
+where
+    B: Borrow<M> + Clone,
+    M: Middleware,
+    C: From<ContractInstance<B, M>>,
+{
+    /// Create a new instance of this from a deployer.
+    pub fn new(deployer: Deployer<B, M>) -> Self {
+        Self { deployer, _contract: PhantomData }
     }
 
     /// Sets the number of confirmations to wait for the contract deployment transaction
@@ -148,7 +177,7 @@ impl<M: Middleware, C: From<Contract<M>>> ContractDeployer<M, C> {
     }
 
     /// Returns a pointer to the deployer's client
-    pub fn client(&self) -> Arc<M> {
+    pub fn client(&self) -> &M {
         self.deployer.client()
     }
 }
@@ -156,16 +185,20 @@ impl<M: Middleware, C: From<Contract<M>>> ContractDeployer<M, C> {
 /// Helper which manages the deployment transaction of a smart contract
 #[derive(Debug)]
 #[must_use = "Deployer does nothing unless you `send` it"]
-pub struct Deployer<M> {
+pub struct Deployer<B, M> {
     /// The deployer's transaction, exposed for overriding the defaults
     pub tx: TypedTransaction,
     abi: Abi,
-    client: Arc<M>,
+    client: B,
     confs: usize,
     block: BlockNumber,
+    _m: PhantomData<M>,
 }
 
-impl<M> Clone for Deployer<M> {
+impl<B, M> Clone for Deployer<B, M>
+where
+    B: Clone,
+{
     fn clone(&self) -> Self {
         Deployer {
             tx: self.tx.clone(),
@@ -173,11 +206,16 @@ impl<M> Clone for Deployer<M> {
             client: self.client.clone(),
             confs: self.confs,
             block: self.block,
+            _m: PhantomData,
         }
     }
 }
 
-impl<M: Middleware> Deployer<M> {
+impl<B, M> Deployer<B, M>
+where
+    B: Borrow<M> + Clone,
+    M: Middleware,
+{
     /// Sets the number of confirmations to wait for the contract deployment transaction
     pub fn confirmations<T: Into<usize>>(mut self, confirmations: T) -> Self {
         self.confs = confirmations.into();
@@ -206,6 +244,7 @@ impl<M: Middleware> Deployer<M> {
     /// Note: this function _does not_ send a transaction from your account
     pub async fn call(&self) -> Result<(), ContractError<M>> {
         self.client
+            .borrow()
             .call(&self.tx, Some(self.block.into()))
             .await
             .map_err(ContractError::MiddlewareError)?;
@@ -220,13 +259,13 @@ impl<M: Middleware> Deployer<M> {
     ///
     /// Note: this function _does not_ send a transaction from your account
     pub fn call_raw(&self) -> CallBuilder<'_, M::Provider> {
-        self.client.provider().call_raw(&self.tx).block(self.block.into())
+        self.client.borrow().provider().call_raw(&self.tx).block(self.block.into())
     }
 
     /// Broadcasts the contract deployment transaction and after waiting for it to
     /// be sufficiently confirmed (default: 1), it returns a [`Contract`](crate::Contract)
     /// struct at the deployed contract's address.
-    pub async fn send(self) -> Result<Contract<M>, ContractError<M>> {
+    pub async fn send(self) -> Result<ContractInstance<B, M>, ContractError<M>> {
         let (contract, _) = self.send_with_receipt().await?;
         Ok(contract)
     }
@@ -237,9 +276,10 @@ impl<M: Middleware> Deployer<M> {
     /// and the corresponding [`TransactionReceipt`](ethers_core::types::TransactionReceipt).
     pub async fn send_with_receipt(
         self,
-    ) -> Result<(Contract<M>, TransactionReceipt), ContractError<M>> {
+    ) -> Result<(ContractInstance<B, M>, TransactionReceipt), ContractError<M>> {
         let pending_tx = self
             .client
+            .borrow()
             .send_transaction(self.tx, Some(self.block.into()))
             .await
             .map_err(ContractError::MiddlewareError)?;
@@ -252,7 +292,7 @@ impl<M: Middleware> Deployer<M> {
             .ok_or(ContractError::ContractNotDeployed)?;
         let address = receipt.contract_address.ok_or(ContractError::ContractNotDeployed)?;
 
-        let contract = Contract::new(address, self.abi.clone(), self.client);
+        let contract = ContractInstance::new(address, self.abi.clone(), self.client.clone());
         Ok((contract, receipt))
     }
 
@@ -262,8 +302,8 @@ impl<M: Middleware> Deployer<M> {
     }
 
     /// Returns a pointer to the deployer's client
-    pub fn client(&self) -> Arc<M> {
-        self.client.clone()
+    pub fn client(&self) -> &M {
+        self.client.borrow()
     }
 }
 
@@ -309,31 +349,43 @@ impl<M: Middleware> Deployer<M> {
 /// # Ok(())
 /// # }
 #[derive(Debug)]
-pub struct ContractFactory<M> {
-    client: Arc<M>,
+pub struct DeploymentTxFactory<B, M> {
+    client: B,
     abi: Abi,
     bytecode: Bytes,
+    _m: PhantomData<M>,
 }
 
-impl<M> Clone for ContractFactory<M> {
+impl<B, M> Clone for DeploymentTxFactory<B, M>
+where
+    B: Clone,
+{
     fn clone(&self) -> Self {
-        ContractFactory {
+        DeploymentTxFactory {
             client: self.client.clone(),
             abi: self.abi.clone(),
             bytecode: self.bytecode.clone(),
+            _m: PhantomData,
         }
     }
 }
 
-impl<M: Middleware> ContractFactory<M> {
+impl<B, M> DeploymentTxFactory<B, M>
+where
+    B: Borrow<M> + Clone,
+    M: Middleware,
+{
     /// Creates a factory for deployment of the Contract with bytecode, and the
     /// constructor defined in the abi. The client will be used to send any deployment
     /// transaction.
-    pub fn new(abi: Abi, bytecode: Bytes, client: Arc<M>) -> Self {
-        Self { client, abi, bytecode }
+    pub fn new(abi: Abi, bytecode: Bytes, client: B) -> Self {
+        Self { client, abi, bytecode, _m: PhantomData }
     }
 
-    pub fn deploy_tokens(self, params: Vec<Token>) -> Result<Deployer<M>, ContractError<M>> {
+    pub fn deploy_tokens(self, params: Vec<Token>) -> Result<Deployer<B, M>, ContractError<M>>
+    where
+        B: Clone,
+    {
         // Encode the constructor args & concatenate with the bytecode if necessary
         let data: Bytes = match (self.abi.constructor(), params.is_empty()) {
             (None, false) => return Err(ContractError::ConstructorError),
@@ -353,11 +405,12 @@ impl<M: Middleware> ContractFactory<M> {
         let tx = tx.into();
 
         Ok(Deployer {
-            client: Arc::clone(&self.client), // cheap clone behind the arc
+            client: self.client.clone(),
             abi: self.abi,
             tx,
             confs: 1,
             block: BlockNumber::Latest,
+            _m: PhantomData,
         })
     }
 
@@ -369,7 +422,10 @@ impl<M: Middleware> ContractFactory<M> {
     /// 1. If there are no constructor arguments, you should pass `()` as the argument.
     /// 1. The default poll duration is 7 seconds.
     /// 1. The default number of confirmations is 1 block.
-    pub fn deploy<T: Tokenize>(self, constructor_args: T) -> Result<Deployer<M>, ContractError<M>> {
+    pub fn deploy<T: Tokenize>(
+        self,
+        constructor_args: T,
+    ) -> Result<Deployer<B, M>, ContractError<M>> {
         self.deploy_tokens(constructor_args.into_tokens())
     }
 }
diff --git a/ethers-contract/src/lib.rs b/ethers-contract/src/lib.rs
index f764616a..4a14760b 100644
--- a/ethers-contract/src/lib.rs
+++ b/ethers-contract/src/lib.rs
@@ -3,19 +3,19 @@
 #![deny(unsafe_code)]
 
 mod contract;
-pub use contract::Contract;
+pub use contract::{Contract, ContractInstance};
 
 mod base;
 pub use base::{decode_function_data, encode_function_data, AbiError, BaseContract};
 
 mod call;
-pub use call::{ContractError, EthCall};
+pub use call::{ContractCall, ContractError, EthCall, FunctionCall};
 
 mod error;
 pub use error::EthError;
 
 mod factory;
-pub use factory::{ContractDeployer, ContractFactory};
+pub use factory::{ContractDeployer, ContractDeploymentTx, ContractFactory, DeploymentTxFactory};
 
 mod event;
 pub use event::{EthEvent, Event};
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index 38aaf4ff..d6978a42 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -41,7 +41,7 @@ cargo_metadata = { version = "0.15.3", optional = true }
 # eip712 feature enabled dependencies
 convert_case = { version = "0.6.0", optional = true }
 syn = { version = "1.0.107", optional = true }
-proc-macro2 = { version = "1.0.50", optional = true }
+proc-macro2 = { version = "1.0.51", optional = true }
 num_enum = "0.5.9"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
diff --git a/ethers-core/src/types/signature.rs b/ethers-core/src/types/signature.rs
index 68e60ecb..886f2b77 100644
--- a/ethers-core/src/types/signature.rs
+++ b/ethers-core/src/types/signature.rs
@@ -69,6 +69,22 @@ 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>
diff --git a/ethers-core/src/types/trace/geth.rs b/ethers-core/src/types/trace/geth.rs
index 6c4ca3ac..630810db 100644
--- a/ethers-core/src/types/trace/geth.rs
+++ b/ethers-core/src/types/trace/geth.rs
@@ -1,5 +1,16 @@
+mod call;
+mod four_byte;
+mod noop;
+mod pre_state;
+
+pub use self::{
+    call::{CallConfig, CallFrame},
+    four_byte::FourByteFrame,
+    noop::NoopFrame,
+    pre_state::{PreStateConfig, PreStateFrame},
+};
 use crate::{
-    types::{Address, Bytes, NameOrAddress, H256, U256},
+    types::{Bytes, H256, U256},
     utils::from_int_or_hex,
 };
 use serde::{Deserialize, Serialize};
@@ -40,34 +51,14 @@ pub struct StructLog {
     pub storage: Option<BTreeMap<H256, H256>>,
 }
 
-// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/native/call.go#L37
-#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CallFrame {
-    #[serde(rename = "type")]
-    pub typ: String,
-    pub from: Address,
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub to: Option<NameOrAddress>,
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub value: Option<U256>,
-    #[serde(deserialize_with = "from_int_or_hex")]
-    pub gas: U256,
-    #[serde(deserialize_with = "from_int_or_hex", rename = "gasUsed")]
-    pub gas_used: U256,
-    pub input: Bytes,
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub output: Option<Bytes>,
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub error: Option<String>,
-    #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub calls: Option<Vec<CallFrame>>,
-}
-
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
 #[serde(untagged)]
 pub enum GethTraceFrame {
     Default(DefaultFrame),
+    NoopTracer(NoopFrame),
+    FourByteTracer(FourByteFrame),
     CallTracer(CallFrame),
+    PreStateTracer(PreStateFrame),
 }
 
 impl From<DefaultFrame> for GethTraceFrame {
@@ -76,12 +67,30 @@ impl From<DefaultFrame> for GethTraceFrame {
     }
 }
 
+impl From<FourByteFrame> for GethTraceFrame {
+    fn from(value: FourByteFrame) -> Self {
+        GethTraceFrame::FourByteTracer(value)
+    }
+}
+
 impl From<CallFrame> for GethTraceFrame {
     fn from(value: CallFrame) -> Self {
         GethTraceFrame::CallTracer(value)
     }
 }
 
+impl From<PreStateFrame> for GethTraceFrame {
+    fn from(value: PreStateFrame) -> Self {
+        GethTraceFrame::PreStateTracer(value)
+    }
+}
+
+impl From<NoopFrame> for GethTraceFrame {
+    fn from(value: NoopFrame) -> Self {
+        GethTraceFrame::NoopTracer(value)
+    }
+}
+
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
 #[serde(untagged)]
 pub enum GethTrace {
@@ -106,8 +115,21 @@ impl From<Value> for GethTrace {
 /// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
 pub enum GethDebugBuiltInTracerType {
+    #[serde(rename = "4byteTracer")]
+    FourByteTracer,
     #[serde(rename = "callTracer")]
     CallTracer,
+    #[serde(rename = "prestateTracer")]
+    PreStateTracer,
+    #[serde(rename = "noopTracer")]
+    NoopTracer,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum GethDebugBuiltInTracerConfig {
+    CallTracer(CallConfig),
+    PreStateTracer(PreStateConfig),
 }
 
 /// Available tracers
@@ -123,6 +145,16 @@ pub enum GethDebugTracerType {
     JsTracer(String),
 }
 
+#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum GethDebugTracerConfig {
+    /// built-in tracer
+    BuiltInTracer(GethDebugBuiltInTracerConfig),
+
+    /// custom JS tracer
+    JsTracer(Value),
+}
+
 /// Bindings for additional `debug_traceTransaction` options
 ///
 /// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
@@ -139,6 +171,10 @@ pub struct GethDebugTracingOptions {
     pub enable_return_data: Option<bool>,
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub tracer: Option<GethDebugTracerType>,
+    /// tracerConfig is slated for Geth v1.11.0
+    /// See <https://github.com/ethereum/go-ethereum/issues/26513>
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub tracer_config: Option<GethDebugTracerConfig>,
     #[serde(default, skip_serializing_if = "Option::is_none")]
     pub timeout: Option<String>,
 }
diff --git a/ethers-core/src/types/trace/geth/call.rs b/ethers-core/src/types/trace/geth/call.rs
new file mode 100644
index 00000000..f6c97b89
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/call.rs
@@ -0,0 +1,87 @@
+use crate::{
+    types::{Address, Bytes, NameOrAddress, H256, U256},
+    utils::from_int_or_hex,
+};
+use serde::{Deserialize, Serialize};
+
+// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/call.go#L44
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CallFrame {
+    #[serde(rename = "type")]
+    pub typ: String,
+    pub from: Address,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub to: Option<NameOrAddress>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub value: Option<U256>,
+    #[serde(default, deserialize_with = "from_int_or_hex")]
+    pub gas: U256,
+    #[serde(default, deserialize_with = "from_int_or_hex", rename = "gasUsed")]
+    pub gas_used: U256,
+    pub input: Bytes,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub output: Option<Bytes>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub error: Option<String>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub calls: Option<Vec<CallFrame>>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub logs: Option<Vec<CallLogFrame>>,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct CallLogFrame {
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    address: Option<Address>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    topics: Option<Vec<H256>>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    data: Option<Bytes>,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CallConfig {
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub only_top_call: Option<bool>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub with_log: Option<bool>,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::types::*;
+
+    // See <https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/internal/tracetest/testdata>
+    const DEFAULT: &str = include_str!("./test_data/call_tracer/default.json");
+    const LEGACY: &str = include_str!("./test_data/call_tracer/legacy.json");
+    const ONLY_TOP_CALL: &str = include_str!("./test_data/call_tracer/only_top_call.json");
+    const WITH_LOG: &str = include_str!("./test_data/call_tracer/with_log.json");
+
+    #[test]
+    fn test_serialize_call_trace() {
+        let mut opts = GethDebugTracingCallOptions::default();
+        opts.tracing_options.disable_storage = Some(false);
+        opts.tracing_options.tracer =
+            Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer));
+        opts.tracing_options.tracer_config =
+            Some(GethDebugTracerConfig::BuiltInTracer(GethDebugBuiltInTracerConfig::CallTracer(
+                CallConfig { only_top_call: Some(true), with_log: Some(true) },
+            )));
+
+        assert_eq!(
+            serde_json::to_string(&opts).unwrap(),
+            r#"{"disableStorage":false,"tracer":"callTracer","tracerConfig":{"onlyTopCall":true,"withLog":true}}"#
+        );
+    }
+
+    #[test]
+    fn test_deserialize_call_trace() {
+        let _trace: CallFrame = serde_json::from_str(DEFAULT).unwrap();
+        let _trace: CallFrame = serde_json::from_str(LEGACY).unwrap();
+        let _trace: CallFrame = serde_json::from_str(ONLY_TOP_CALL).unwrap();
+        let trace: CallFrame = serde_json::from_str(WITH_LOG).unwrap();
+        let _logs = trace.logs.unwrap();
+    }
+}
diff --git a/ethers-core/src/types/trace/geth/four_byte.rs b/ethers-core/src/types/trace/geth/four_byte.rs
new file mode 100644
index 00000000..10f3ef38
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/four_byte.rs
@@ -0,0 +1,34 @@
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+
+// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/4byte.go#L48
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct FourByteFrame(pub BTreeMap<String, u64>);
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::types::*;
+
+    const DEFAULT: &str = r#"{
+        "0x27dc297e-128": 1,
+        "0x38cc4831-0": 2,
+        "0x524f3889-96": 1,
+        "0xadf59f99-288": 1,
+        "0xc281d19e-0": 1
+    }"#;
+
+    #[test]
+    fn test_serialize_four_byte_trace() {
+        let mut opts = GethDebugTracingCallOptions::default();
+        opts.tracing_options.tracer =
+            Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::FourByteTracer));
+
+        assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"4byteTracer"}"#);
+    }
+
+    #[test]
+    fn test_deserialize_four_byte_trace() {
+        let _trace: FourByteFrame = serde_json::from_str(DEFAULT).unwrap();
+    }
+}
diff --git a/ethers-core/src/types/trace/geth/noop.rs b/ethers-core/src/types/trace/geth/noop.rs
new file mode 100644
index 00000000..6c7554fd
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/noop.rs
@@ -0,0 +1,30 @@
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+
+// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/noop.go#L34
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct NoopFrame(BTreeMap<Null, Null>);
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
+struct Null;
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::types::*;
+
+    const DEFAULT: &str = r#"{}"#;
+
+    #[test]
+    fn test_serialize_noop_trace() {
+        let mut opts = GethDebugTracingCallOptions::default();
+        opts.tracing_options.tracer =
+            Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::NoopTracer));
+
+        assert_eq!(serde_json::to_string(&opts).unwrap(), r#"{"tracer":"noopTracer"}"#);
+    }
+
+    #[test]
+    fn test_deserialize_noop_trace() {
+        let _trace: NoopFrame = serde_json::from_str(DEFAULT).unwrap();
+    }
+}
diff --git a/ethers-core/src/types/trace/geth/pre_state.rs b/ethers-core/src/types/trace/geth/pre_state.rs
new file mode 100644
index 00000000..b87dbbd0
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/pre_state.rs
@@ -0,0 +1,92 @@
+use crate::{
+    types::{Address, H256, U256},
+    utils::from_int_or_hex_opt,
+};
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+
+// https://github.com/ethereum/go-ethereum/blob/91cb6f863a965481e51d5d9c0e5ccd54796fd967/eth/tracers/native/prestate.go#L38
+#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[serde(untagged)]
+pub enum PreStateFrame {
+    Default(PreStateMode),
+    Diff(DiffMode),
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct PreStateMode(pub BTreeMap<Address, AccountState>);
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct DiffMode {
+    pub pre: BTreeMap<Address, AccountState>,
+    pub post: BTreeMap<Address, AccountState>,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub struct AccountState {
+    #[serde(
+        default,
+        deserialize_with = "from_int_or_hex_opt",
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub balance: Option<U256>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub code: Option<String>,
+    #[serde(
+        default,
+        deserialize_with = "from_int_or_hex_opt",
+        skip_serializing_if = "Option::is_none"
+    )]
+    pub nonce: Option<U256>,
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub storage: Option<BTreeMap<H256, H256>>,
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PreStateConfig {
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    pub diff_mode: Option<bool>,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::types::*;
+
+    // See <https://github.com/ethereum/go-ethereum/tree/master/eth/tracers/internal/tracetest/testdata>
+    const DEFAULT: &str = include_str!("./test_data/pre_state_tracer/default.json");
+    const LEGACY: &str = include_str!("./test_data/pre_state_tracer/legacy.json");
+    const DIFF_MODE: &str = include_str!("./test_data/pre_state_tracer/diff_mode.json");
+
+    #[test]
+    fn test_serialize_pre_state_trace() {
+        let mut opts = GethDebugTracingCallOptions::default();
+        opts.tracing_options.disable_storage = Some(false);
+        opts.tracing_options.tracer =
+            Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::PreStateTracer));
+        opts.tracing_options.tracer_config = Some(GethDebugTracerConfig::BuiltInTracer(
+            GethDebugBuiltInTracerConfig::PreStateTracer(PreStateConfig { diff_mode: Some(true) }),
+        ));
+
+        assert_eq!(
+            serde_json::to_string(&opts).unwrap(),
+            r#"{"disableStorage":false,"tracer":"prestateTracer","tracerConfig":{"diffMode":true}}"#
+        );
+    }
+
+    #[test]
+    fn test_deserialize_pre_state_trace() {
+        let trace: PreStateFrame = serde_json::from_str(DEFAULT).unwrap();
+        match trace {
+            PreStateFrame::Default(PreStateMode(_)) => {}
+            _ => unreachable!(),
+        }
+        let _trace: PreStateFrame = serde_json::from_str(LEGACY).unwrap();
+        let trace: PreStateFrame = serde_json::from_str(DIFF_MODE).unwrap();
+        match trace {
+            PreStateFrame::Diff(DiffMode { pre: _pre, post: _post }) => {}
+            _ => unreachable!(),
+        }
+    }
+}
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 96e95159..7b432680 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -34,7 +34,7 @@ tracing = "0.1.37"
 num_cpus = "1.15.0"
 tiny-keccak = { version = "2.0.2", default-features = false }
 tempfile = { version = "3.3.0", optional = true }
-fs_extra = { version = "1.2.0", optional = true }
+fs_extra = { version = "1.3.0", optional = true }
 sha2 = { version = "0.10.6", default-features = false, optional = true }
 dunce = "1.0.3"
 solang-parser = { default-features = false, version = "=0.2.1" }


Test patch:
diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs
index c4f58fba..0d3b7a64 100644
--- a/ethers-contract/tests/it/contract.rs
+++ b/ethers-contract/tests/it/contract.rs
@@ -6,10 +6,10 @@ use ethers_core::types::{Filter, ValueOrArray, H256};
 #[cfg(not(feature = "celo"))]
 mod eth_tests {
     use super::*;
-    use ethers_contract::{EthEvent, LogMeta, Multicall, MulticallVersion};
+    use ethers_contract::{ContractInstance, EthEvent, LogMeta, Multicall, MulticallVersion};
     use ethers_core::{
         abi::{encode, Detokenize, Token, Tokenizable},
-        types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256},
+        types::{transaction::eip712::Eip712, Address, BlockId, Bytes, H160, I256, U256},
         utils::{keccak256, Anvil},
     };
     use ethers_derive_eip712::*;
@@ -17,6 +17,81 @@ mod eth_tests {
     use ethers_signers::{LocalWallet, Signer};
     use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration};
 
+    #[derive(Debug)]
+    pub struct NonClone<M> {
+        m: M,
+    }
+
+    #[derive(Debug)]
+    pub struct MwErr<M: Middleware>(M::Error);
+    impl<M> ethers_providers::FromErr<M::Error> for MwErr<M>
+    where
+        M: Middleware,
+    {
+        fn from(src: M::Error) -> Self {
+            Self(src)
+        }
+    }
+
+    impl<M: Middleware> std::fmt::Display for MwErr<M> {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            Ok(())
+        }
+    }
+    impl<M: Middleware> std::error::Error for MwErr<M> {}
+
+    impl<M: Middleware> Middleware for NonClone<M> {
+        type Error = MwErr<M>;
+
+        type Provider = M::Provider;
+
+        type Inner = M;
+
+        fn inner(&self) -> &Self::Inner {
+            &self.m
+        }
+    }
+
+    // this is not a test. It is a compile check. :)
+    // 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");
+
+        // launch anvil
+        let anvil = Anvil::new().spawn();
+
+        let client = Provider::<Http>::try_from(anvil.endpoint())
+            .unwrap()
+            .interval(Duration::from_millis(10u64));
+
+        // Works (B == M, M: Clone)
+        let c: ContractInstance<&Provider<Http>, Provider<Http>> =
+            ContractInstance::new(H160::default(), abi.clone(), &client);
+
+        let _ = c.method::<(), ()>("notARealMethod", ());
+
+        // Works (B == &M, M: Clone)
+        let c: ContractInstance<Provider<Http>, Provider<Http>> =
+            ContractInstance::new(H160::default(), abi.clone(), client.clone());
+
+        let _ = c.method::<(), ()>("notARealMethod", ());
+
+        let non_clone_mware = NonClone { m: client };
+
+        // Works (B == &M, M: !Clone)
+        let c: ContractInstance<&NonClone<Provider<Http>>, NonClone<Provider<Http>>> =
+            ContractInstance::new(H160::default(), abi, &non_clone_mware);
+
+        let _ = c.method::<(), ()>("notARealMethod", ());
+
+        // // Fails (B == M, M: !Clone)
+        // let c: ContractInternal<NonClone<Provider<Http>>, NonClone<Provider<Http>>> =
+        //     ContractInternal::new(H160::default(), abi, non_clone_mware);
+
+        // let _ = c.method::<(), ()>("notARealMethod", ());
+    }
+
     #[tokio::test]
     async fn deploy_and_call_contract() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");
@@ -265,7 +340,7 @@ mod eth_tests {
 
         // Also set up a subscription for the same thing
         let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap();
-        let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws);
+        let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws.into());
         let event2 = contract2.event::<ValueChanged>();
         let mut subscription = event2.subscribe().await.unwrap();
 
diff --git a/ethers-core/src/types/trace/geth/test_data/call_tracer/default.json b/ethers-core/src/types/trace/geth/test_data/call_tracer/default.json
new file mode 100644
index 00000000..553b2a39
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/call_tracer/default.json
@@ -0,0 +1,21 @@
+{
+    "calls": [
+        {
+            "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
+            "gas": "0x6d05",
+            "gasUsed": "0x0",
+            "input": "0x",
+            "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
+            "type": "CALL",
+            "value": "0x6f05b59d3b20000"
+        }
+    ],
+    "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
+    "gas": "0x10738",
+    "gasUsed": "0x9751",
+    "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
+    "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+    "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
+    "type": "CALL",
+    "value": "0x0"
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/call_tracer/legacy.json b/ethers-core/src/types/trace/geth/test_data/call_tracer/legacy.json
new file mode 100644
index 00000000..b89e7ae8
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/call_tracer/legacy.json
@@ -0,0 +1,19 @@
+{
+    "calls": [
+        {
+            "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
+            "input": "0x",
+            "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
+            "type": "CALL",
+            "value": "0x6f05b59d3b20000"
+        }
+    ],
+    "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
+    "gas": "0x10738",
+    "gasUsed": "0x9751",
+    "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
+    "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
+    "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
+    "type": "CALL",
+    "value": "0x0"
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/call_tracer/only_top_call.json b/ethers-core/src/types/trace/geth/test_data/call_tracer/only_top_call.json
new file mode 100644
index 00000000..327bb427
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/call_tracer/only_top_call.json
@@ -0,0 +1,10 @@
+{
+    "from": "0x4f5777744b500616697cb655dcb02ee6cd51deb5",
+    "gas": "0x2dced",
+    "gasUsed": "0x1a9e5",
+    "to": "0x200edd17f30485a8735878661960cd7a9a95733f",
+    "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000",
+    "output": "0xba51a6df00000000000000000000000000000000000000000000000000000000",
+    "value": "0x8ac7230489e80000",
+    "type": "CALL"
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/call_tracer/with_log.json b/ethers-core/src/types/trace/geth/test_data/call_tracer/with_log.json
new file mode 100644
index 00000000..2528bbc0
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/call_tracer/with_log.json
@@ -0,0 +1,20 @@
+{
+    "from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
+    "gas": "0x1f36d",
+    "gasUsed": "0xc6a5",
+    "to": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd",
+    "input": "0xa9059cbb000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb0000000000000000000000000000000000000000000000000000000000989680",
+    "logs": [
+        {
+            "address": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd",
+            "topics": [
+            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+            "0x000000000000000000000000d1220a0cf47c7b9be7a2e6ba89f429762e7b9adb",
+            "0x000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb"
+            ],
+            "data": "0x0000000000000000000000000000000000000000000000000000000000989680"
+        }
+    ],
+    "value": "0x0",
+    "type": "CALL"
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/default.json b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/default.json
new file mode 100644
index 00000000..43e69b11
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/default.json
@@ -0,0 +1,20 @@
+{
+    "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": {
+        "balance": "0xc820f93200f4000",
+        "nonce": 94
+    },
+    "0x332b656504f4eabb44c8617a42af37461a34e9dc": {
+        "balance": "0x11faea4f35e5af80000",
+        "storage": {
+            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000"
+        }
+    },
+    "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": {
+        "balance": "0xbf681825be002ac452",
+        "nonce": 28922
+    },
+    "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": {
+        "balance": "0xb3d0ac5cb94df6f6b0",
+        "nonce": 1
+    }
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/diff_mode.json b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/diff_mode.json
new file mode 100644
index 00000000..0654d26f
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/diff_mode.json
@@ -0,0 +1,41 @@
+{
+    "pre": {
+        "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+            "balance": "0x0",
+            "nonce": 22
+        },
+        "0x1585936b53834b021f68cc13eeefdec2efc8e724": {
+            "balance": "0x0"
+        },
+        "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+            "balance": "0x4d87094125a369d9bd5",
+            "nonce": 1,
+            "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+            "storage": {
+                "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+            }
+        },
+        "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+            "balance": "0x1780d77678137ac1b775",
+            "nonce": 29072
+        }
+    },
+    "post": {
+        "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+            "balance": "0x6f05b59d3b20000"
+        },
+        "0x1585936b53834b021f68cc13eeefdec2efc8e724": {
+            "balance": "0x420eed1bd6c00"
+        },
+        "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+            "balance": "0x4d869a3b70062eb9bd5",
+            "storage": {
+                "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b95e"
+            }
+        },
+        "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+            "balance": "0x1780d7725724a9044b75",
+            "nonce": 29073
+        }
+    }
+}
diff --git a/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/legacy.json b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/legacy.json
new file mode 100644
index 00000000..dbefb198
--- /dev/null
+++ b/ethers-core/src/types/trace/geth/test_data/pre_state_tracer/legacy.json
@@ -0,0 +1,25 @@
+{
+    "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
+        "balance": "0x0",
+        "code": "0x",
+        "nonce": 22,
+        "storage": {}
+    },
+    "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
+        "balance": "0x4d87094125a369d9bd5",
+        "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
+        "nonce": 1,
+        "storage": {
+            "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
+            "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
+            "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
+            "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
+        }
+    },
+    "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
+        "balance": "0x1780d77678137ac1b775",
+        "code": "0x",
+        "nonce": 29072,
+        "storage": {}
+    }
+}