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
|
||||
|
||||
- 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)
|
||||
- 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)
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
use crate::{
|
||||
types::{Bytes, H256, U256},
|
||||
types::{Address, Bytes, NameOrAddress, H256, U256},
|
||||
utils::from_int_or_hex,
|
||||
};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
// https://github.com/ethereum/go-ethereum/blob/a9ef135e2dd53682d106c6a2aede9187026cc1de/eth/tracers/logger/logger.go#L406-L411
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct GethTrace {
|
||||
pub struct DefaultFrame {
|
||||
pub failed: bool,
|
||||
#[serde(deserialize_with = "from_int_or_hex")]
|
||||
pub gas: U256,
|
||||
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")]
|
||||
#[serde(rename = "returnValue")]
|
||||
pub return_value: Bytes,
|
||||
#[serde(rename = "structLogs")]
|
||||
pub struct_logs: Vec<StructLog>,
|
||||
|
@ -21,34 +22,83 @@ pub struct GethTrace {
|
|||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct StructLog {
|
||||
pub depth: u64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub error: Option<String>,
|
||||
pub gas: u64,
|
||||
#[serde(rename = "gasCost")]
|
||||
pub gas_cost: u64,
|
||||
/// 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 op: String,
|
||||
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>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
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>>,
|
||||
}
|
||||
|
||||
// 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),
|
||||
CallTracer(CallFrame),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum GethTrace {
|
||||
Known(GethTraceFrame),
|
||||
Unknown(Value),
|
||||
}
|
||||
|
||||
/// Available built-in tracers
|
||||
///
|
||||
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
|
||||
pub enum GethDebugTracerType {
|
||||
/// callTracer (native)
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
|
||||
pub enum GethDebugBuiltInTracerType {
|
||||
#[serde(rename = "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
|
||||
///
|
||||
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
|
||||
|
@ -79,11 +129,3 @@ pub struct GethDebugTracingCallOptions {
|
|||
pub tracing_options: GethDebugTracingOptions,
|
||||
// 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 std::str::FromStr;
|
||||
|
||||
|
@ -14,7 +21,9 @@ async fn main() -> Result<()> {
|
|||
tracing_options: GethDebugTracingOptions {
|
||||
disable_storage: Some(true),
|
||||
enable_memory: Some(false),
|
||||
tracer: Some(GethDebugTracerType::CallTracer),
|
||||
tracer: Some(GethDebugTracerType::BuiltInTracer(
|
||||
GethDebugBuiltInTracerType::CallTracer,
|
||||
)),
|
||||
..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