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

feat: add support for Geth built-in tracer and config
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
Now only `callTracer` is supported, add more `build-in` tracer and [config](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#config) support.

<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

- feat: support other geth build-in tracers
- feat: support geth tracer config
- refactor: reconstructs the layout of geth tracer
- test: add unit tests for default, legacy, and tracer config

Refs:  #2064

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

-   [x] Added Tests
-   [ ] Added Documentation
-   [x] Updated the changelog
-   [ ] Breaking changes



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f945ae34..d794156d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,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.
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!(),
+        }
+    }
+}


Test patch:
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": {}
+    }
+}