feat: add support for js tracer to geth trace, fix different return types (#2064)
* feat: add support for js tracer to geth trace, fix different return types * chore: fix clippy * chore: update geth structs
This commit is contained in:
parent
969c10a406
commit
7e7f9041b3
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- 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)
|
- Add a `Send` bound to the `IntoFuture` implementation of `ContractCall` [#2083](https://github.com/gakonst/ethers-rs/pull/2083)
|
||||||
- Bump [`svm-rs`](https://github.com/roynalnaruto/svm-rs) dependency to fix conflicts with Rust Crytpo packages [#2051](https://github.com/gakonst/ethers-rs/pull/2051)
|
- Bump [`svm-rs`](https://github.com/roynalnaruto/svm-rs) dependency to fix conflicts with Rust Crytpo packages [#2051](https://github.com/gakonst/ethers-rs/pull/2051)
|
||||||
- Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046)
|
- Avoid unnecessary allocations in `utils` [#2046](https://github.com/gakonst/ethers-rs/pull/2046)
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{Bytes, H256, U256},
|
types::{Address, Bytes, NameOrAddress, H256, U256},
|
||||||
utils::from_int_or_hex,
|
utils::from_int_or_hex,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411
|
// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct GethTrace {
|
pub struct DefaultFrame {
|
||||||
pub failed: bool,
|
pub failed: bool,
|
||||||
#[serde(deserialize_with = "from_int_or_hex")]
|
#[serde(deserialize_with = "from_int_or_hex")]
|
||||||
pub gas: U256,
|
pub gas: U256,
|
||||||
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")]
|
#[serde(rename = "returnValue")]
|
||||||
pub return_value: Bytes,
|
pub return_value: Bytes,
|
||||||
#[serde(rename = "structLogs")]
|
#[serde(rename = "structLogs")]
|
||||||
pub struct_logs: Vec<StructLog>,
|
pub struct_logs: Vec<StructLog>,
|
||||||
|
@ -21,34 +22,83 @@ pub struct GethTrace {
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct StructLog {
|
pub struct StructLog {
|
||||||
pub depth: u64,
|
pub depth: u64,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
pub gas: u64,
|
pub gas: u64,
|
||||||
#[serde(rename = "gasCost")]
|
#[serde(rename = "gasCost")]
|
||||||
pub gas_cost: u64,
|
pub gas_cost: u64,
|
||||||
/// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452>
|
/// ref <https://github.com/ethereum/go-ethereum/blob/366d2169fbc0e0f803b68c042b77b6b480836dbc/eth/tracers/logger/logger.go#L450-L452>
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub memory: Option<Vec<String>>,
|
pub memory: Option<Vec<String>>,
|
||||||
pub op: String,
|
pub op: String,
|
||||||
pub pc: u64,
|
pub pc: u64,
|
||||||
#[serde(rename = "refund", skip_serializing_if = "Option::is_none")]
|
#[serde(default, rename = "refund", skip_serializing_if = "Option::is_none")]
|
||||||
pub refund_counter: Option<u64>,
|
pub refund_counter: Option<u64>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub stack: Option<Vec<U256>>,
|
pub stack: Option<Vec<U256>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub storage: Option<BTreeMap<H256, H256>>,
|
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)]
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum GethTraceFrame {
|
||||||
|
Default(DefaultFrame),
|
||||||
|
CallTracer(CallFrame),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum GethTrace {
|
||||||
|
Known(GethTraceFrame),
|
||||||
|
Unknown(Value),
|
||||||
|
}
|
||||||
|
|
||||||
/// Available built-in tracers
|
/// Available built-in tracers
|
||||||
///
|
///
|
||||||
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
|
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
|
||||||
pub enum GethDebugTracerType {
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
/// callTracer (native)
|
pub enum GethDebugBuiltInTracerType {
|
||||||
#[serde(rename = "callTracer")]
|
#[serde(rename = "callTracer")]
|
||||||
CallTracer,
|
CallTracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Available tracers
|
||||||
|
///
|
||||||
|
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers> and <https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer>
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum GethDebugTracerType {
|
||||||
|
/// built-in tracer
|
||||||
|
BuiltInTracer(GethDebugBuiltInTracerType),
|
||||||
|
|
||||||
|
/// custom JS tracer
|
||||||
|
JsTracer(String),
|
||||||
|
}
|
||||||
|
|
||||||
/// Bindings for additional `debug_traceTransaction` options
|
/// Bindings for additional `debug_traceTransaction` options
|
||||||
///
|
///
|
||||||
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
|
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
|
||||||
|
@ -79,11 +129,3 @@ pub struct GethDebugTracingCallOptions {
|
||||||
pub tracing_options: GethDebugTracingOptions,
|
pub tracing_options: GethDebugTracingOptions,
|
||||||
// TODO: Add stateoverrides and blockoverrides options
|
// TODO: Add stateoverrides and blockoverrides options
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_bytes<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
T: AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
s.serialize_str(&hex::encode(x.as_ref()))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
use ethers::{
|
|
||||||
core::types::{GethDebugTracingOptions, H256},
|
|
||||||
providers::{Http, Middleware, Provider},
|
|
||||||
};
|
|
||||||
use eyre::Result;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
/// use `debug_traceTransaction` to fetch traces
|
|
||||||
/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceTransaction`
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<()> {
|
|
||||||
if let Ok(url) = std::env::var("RPC_URL") {
|
|
||||||
let client = Provider::<Http>::try_from(url)?;
|
|
||||||
let tx_hash = "0x97a02abf405d36939e5b232a5d4ef5206980c5a6661845436058f30600c52df7";
|
|
||||||
let h: H256 = H256::from_str(tx_hash)?;
|
|
||||||
let options: GethDebugTracingOptions = GethDebugTracingOptions::default();
|
|
||||||
let traces = client.debug_trace_transaction(h, options).await?;
|
|
||||||
println!("{traces:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,4 +1,11 @@
|
||||||
use ethers::prelude::*;
|
use ethers::{
|
||||||
|
core::types::GethDebugTracingOptions,
|
||||||
|
providers::{Http, Middleware, Provider},
|
||||||
|
types::{
|
||||||
|
Address, BlockId, Bytes, GethDebugBuiltInTracerType, GethDebugTracerType,
|
||||||
|
GethDebugTracingCallOptions, TransactionRequest,
|
||||||
|
},
|
||||||
|
};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -14,7 +21,9 @@ async fn main() -> Result<()> {
|
||||||
tracing_options: GethDebugTracingOptions {
|
tracing_options: GethDebugTracingOptions {
|
||||||
disable_storage: Some(true),
|
disable_storage: Some(true),
|
||||||
enable_memory: Some(false),
|
enable_memory: Some(false),
|
||||||
tracer: Some(GethDebugTracerType::CallTracer),
|
tracer: Some(GethDebugTracerType::BuiltInTracer(
|
||||||
|
GethDebugBuiltInTracerType::CallTracer,
|
||||||
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -0,0 +1,47 @@
|
||||||
|
use ethers::{
|
||||||
|
core::types::{GethDebugTracingOptions, H256},
|
||||||
|
providers::{Http, Middleware, Provider},
|
||||||
|
types::{GethDebugBuiltInTracerType, GethDebugTracerType},
|
||||||
|
};
|
||||||
|
use eyre::Result;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// use `debug_traceTransaction` to fetch traces
|
||||||
|
/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceTransaction`
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
if let Ok(url) = std::env::var("RPC_URL") {
|
||||||
|
let client = Provider::<Http>::try_from(url)?;
|
||||||
|
let tx_hash = "0x97a02abf405d36939e5b232a5d4ef5206980c5a6661845436058f30600c52df7";
|
||||||
|
let h: H256 = H256::from_str(tx_hash)?;
|
||||||
|
|
||||||
|
// default tracer
|
||||||
|
let options = GethDebugTracingOptions::default();
|
||||||
|
let traces = client.debug_trace_transaction(h, options).await?;
|
||||||
|
println!("{traces:?}");
|
||||||
|
|
||||||
|
// call tracer
|
||||||
|
let options = GethDebugTracingOptions {
|
||||||
|
disable_storage: Some(true),
|
||||||
|
enable_memory: Some(false),
|
||||||
|
tracer: Some(GethDebugTracerType::BuiltInTracer(
|
||||||
|
GethDebugBuiltInTracerType::CallTracer,
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let traces = client.debug_trace_transaction(h, options).await?;
|
||||||
|
println!("{traces:?}");
|
||||||
|
|
||||||
|
// js tracer
|
||||||
|
let options = GethDebugTracingOptions {
|
||||||
|
disable_storage: Some(true),
|
||||||
|
enable_memory: Some(false),
|
||||||
|
tracer: Some(GethDebugTracerType::JsTracer(String::from("{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"DELEGATECALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}"))),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let traces = client.debug_trace_transaction(h, options).await?;
|
||||||
|
println!("{traces:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue