feat: debug_traceCall (#1949)

* feat: debug_traceCall

* chore: update changelog

* fix: rebase to master and change tracer from string to enum
This commit is contained in:
Vid Kersic 2022-12-25 12:33:05 +01:00 committed by GitHub
parent 2e32528292
commit b27c7b0773
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 8 deletions

View File

@ -4,8 +4,8 @@
### Unreleased
-
- Fix typo in `RwClient` docs for `write_client` method.
- Add support for Geth `debug_traceCall` [#1949](https://github.com/gakonst/ethers-rs/pull/1949)
- 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.

View File

@ -1,4 +1,7 @@
use crate::types::{Bytes, H256, U256};
use crate::{
types::{Bytes, H256, U256},
utils::from_int_or_hex,
};
use serde::{Deserialize, Serialize, Serializer};
use std::collections::BTreeMap;
@ -6,7 +9,8 @@ use std::collections::BTreeMap;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GethTrace {
pub failed: bool,
pub gas: u64,
#[serde(deserialize_with = "from_int_or_hex")]
pub gas: U256,
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")]
pub return_value: Bytes,
#[serde(rename = "structLogs")]
@ -35,6 +39,16 @@ pub struct StructLog {
pub storage: Option<BTreeMap<H256, H256>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
/// Available built-in tracers
///
/// See <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
pub enum GethDebugTracerType {
/// callTracer (native)
#[serde(rename = "callTracer")]
CallTracer,
}
/// Bindings for additional `debug_traceTransaction` options
///
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracetransaction>
@ -50,11 +64,22 @@ pub struct GethDebugTracingOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enable_return_data: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tracer: Option<String>,
pub tracer: Option<GethDebugTracerType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout: Option<String>,
}
/// Bindings for additional `debug_traceCall` options
///
/// See <https://geth.ethereum.org/docs/rpc/ns-debug#debug_tracecall>
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct GethDebugTracingCallOptions {
#[serde(flatten)]
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,

View File

@ -31,7 +31,7 @@ tracing = { version = "0.1.37", default-features = false }
tracing-futures = { version = "0.2.5", default-features = false }
# for gas oracles
reqwest = { version = "0.11.13", default-features = false, features = ["json"] }
reqwest = { version = "0.11.13", default-features = false, features = ["json", "rustls-tls"] }
url = { version = "2.3.1", default-features = false }
serde_json = { version = "1.0.64", default-features = false }

View File

@ -540,6 +540,7 @@ pub trait Middleware: Sync + Send + Debug {
}
// Geth `trace` support
/// After replaying any previous transactions in the same block,
/// Replays a transaction, returning the traces configured with passed options
async fn debug_trace_transaction(
@ -550,6 +551,16 @@ pub trait Middleware: Sync + Send + Debug {
self.inner().debug_trace_transaction(tx_hash, trace_options).await.map_err(FromErr::from)
}
/// Executes the given call and returns a number of possible traces for it
async fn debug_trace_call<T: Into<TypedTransaction> + Send + Sync>(
&self,
req: T,
block: Option<BlockId>,
trace_options: GethDebugTracingCallOptions,
) -> Result<GethTrace, ProviderError> {
self.inner().debug_trace_call(req, block, trace_options).await.map_err(FromErr::from)
}
// Parity `trace` support
/// Executes the given call and returns a number of possible traces for it

View File

@ -22,9 +22,10 @@ use ethers_core::{
types::{
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, FeeHistory,
Filter, FilterBlockOption, GethDebugTracingOptions, GethTrace, Log, NameOrAddress,
Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt,
TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
Filter, FilterBlockOption, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
Log, NameOrAddress, Selector, Signature, Trace, TraceFilter, TraceType, Transaction,
TransactionReceipt, TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus,
H256, U256, U64,
},
utils,
};
@ -1048,6 +1049,20 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
self.request("debug_traceTransaction", [tx_hash, trace_options]).await
}
/// Executes the given call and returns a number of possible traces for it
async fn debug_trace_call<T: Into<TypedTransaction> + Send + Sync>(
&self,
req: T,
block: Option<BlockId>,
trace_options: GethDebugTracingCallOptions,
) -> Result<GethTrace, ProviderError> {
let req = req.into();
let req = utils::serialize(&req);
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
let trace_options = utils::serialize(&trace_options);
self.request("debug_traceCall", [req, block, trace_options]).await
}
/// Executes the given call and returns a number of possible traces for it
async fn trace_call<T: Into<TypedTransaction> + Send + Sync>(
&self,

View File

@ -0,0 +1,26 @@
use ethers::prelude::*;
use eyre::Result;
use std::str::FromStr;
/// use `debug_traceCall` to fetch traces
/// requires, a valid endpoint in `RPC_URL` env var that supports `debug_traceCall`
#[tokio::main]
async fn main() -> Result<()> {
if let Ok(url) = std::env::var("RPC_URL") {
let client = Provider::<Http>::try_from(url)?;
let tx = TransactionRequest::new().from(Address::from_str("0xdeadbeef29292929192939494959594933929292").unwrap()).to(Address::from_str("0xde929f939d939d393f939393f93939f393929023").unwrap()).gas("0x7a120").data(Bytes::from_str("0xf00d4b5d00000000000000000000000001291230982139282304923482304912923823920000000000000000000000001293123098123928310239129839291010293810").unwrap());
let block = BlockId::from(16213100);
let options: GethDebugTracingCallOptions = GethDebugTracingCallOptions {
tracing_options: GethDebugTracingOptions {
disable_storage: Some(true),
enable_memory: Some(false),
tracer: Some(GethDebugTracerType::CallTracer),
..Default::default()
},
};
let traces = client.debug_trace_call(tx, Some(block), options).await?;
println!("{traces:?}");
}
Ok(())
}