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:
parent
2e32528292
commit
b27c7b0773
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
-
|
|
||||||
- Fix typo in `RwClient` docs for `write_client` method.
|
- 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)
|
- 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.
|
- `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.
|
- An `Event` builder can be instantiated specifying the event filter type, without the need to instantiate a contract.
|
||||||
|
|
|
@ -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 serde::{Deserialize, Serialize, Serializer};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -6,7 +9,8 @@ use std::collections::BTreeMap;
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct GethTrace {
|
pub struct GethTrace {
|
||||||
pub failed: bool,
|
pub failed: bool,
|
||||||
pub gas: u64,
|
#[serde(deserialize_with = "from_int_or_hex")]
|
||||||
|
pub gas: U256,
|
||||||
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")]
|
#[serde(serialize_with = "serialize_bytes", rename = "returnValue")]
|
||||||
pub return_value: Bytes,
|
pub return_value: Bytes,
|
||||||
#[serde(rename = "structLogs")]
|
#[serde(rename = "structLogs")]
|
||||||
|
@ -35,6 +39,16 @@ pub struct StructLog {
|
||||||
pub storage: Option<BTreeMap<H256, H256>>,
|
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
|
/// 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>
|
||||||
|
@ -50,11 +64,22 @@ pub struct GethDebugTracingOptions {
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub enable_return_data: Option<bool>,
|
pub enable_return_data: Option<bool>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub tracer: Option<String>,
|
pub tracer: Option<GethDebugTracerType>,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub timeout: Option<String>,
|
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>
|
fn serialize_bytes<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
|
|
|
@ -31,7 +31,7 @@ tracing = { version = "0.1.37", default-features = false }
|
||||||
tracing-futures = { version = "0.2.5", default-features = false }
|
tracing-futures = { version = "0.2.5", default-features = false }
|
||||||
|
|
||||||
# for gas oracles
|
# 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 }
|
url = { version = "2.3.1", default-features = false }
|
||||||
|
|
||||||
serde_json = { version = "1.0.64", default-features = false }
|
serde_json = { version = "1.0.64", default-features = false }
|
||||||
|
|
|
@ -540,6 +540,7 @@ pub trait Middleware: Sync + Send + Debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Geth `trace` support
|
// Geth `trace` support
|
||||||
|
|
||||||
/// After replaying any previous transactions in the same block,
|
/// After replaying any previous transactions in the same block,
|
||||||
/// Replays a transaction, returning the traces configured with passed options
|
/// Replays a transaction, returning the traces configured with passed options
|
||||||
async fn debug_trace_transaction(
|
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)
|
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
|
// Parity `trace` support
|
||||||
|
|
||||||
/// Executes the given call and returns a number of possible traces for it
|
/// Executes the given call and returns a number of possible traces for it
|
||||||
|
|
|
@ -22,9 +22,10 @@ use ethers_core::{
|
||||||
types::{
|
types::{
|
||||||
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
|
transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed},
|
||||||
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, FeeHistory,
|
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, FeeHistory,
|
||||||
Filter, FilterBlockOption, GethDebugTracingOptions, GethTrace, Log, NameOrAddress,
|
Filter, FilterBlockOption, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
|
||||||
Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt,
|
Log, NameOrAddress, Selector, Signature, Trace, TraceFilter, TraceType, Transaction,
|
||||||
TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
|
TransactionReceipt, TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus,
|
||||||
|
H256, U256, U64,
|
||||||
},
|
},
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
@ -1048,6 +1049,20 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
self.request("debug_traceTransaction", [tx_hash, trace_options]).await
|
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
|
/// Executes the given call and returns a number of possible traces for it
|
||||||
async fn trace_call<T: Into<TypedTransaction> + Send + Sync>(
|
async fn trace_call<T: Into<TypedTransaction> + Send + Sync>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
Loading…
Reference in New Issue