ic_web3/api/
traces.rs

1use crate::{
2    api::Namespace,
3    helpers::{self, CallFuture},
4    types::{BlockId, BlockNumber, BlockTrace, Bytes, CallRequest, Index, Trace, TraceFilter, TraceType, H256},
5    Transport,
6};
7
8/// `Trace` namespace
9#[derive(Debug, Clone)]
10pub struct Traces<T> {
11    transport: T,
12}
13
14impl<T: Transport> Namespace<T> for Traces<T> {
15    fn new(transport: T) -> Self
16    where
17        Self: Sized,
18    {
19        Traces { transport }
20    }
21
22    fn transport(&self) -> &T {
23        &self.transport
24    }
25}
26
27impl<T: Transport> Traces<T> {
28    /// Executes the given call and returns a number of possible traces for it
29    pub fn call(
30        &self,
31        req: CallRequest,
32        trace_type: Vec<TraceType>,
33        block: Option<BlockNumber>,
34    ) -> CallFuture<BlockTrace, T::Out> {
35        let req = helpers::serialize(&req);
36        let block = helpers::serialize(&block.unwrap_or(BlockNumber::Latest));
37        let trace_type = helpers::serialize(&trace_type);
38        CallFuture::new(self.transport.execute("trace_call", vec![req, trace_type, block]))
39    }
40
41    /// Performs multiple call traces on top of the same block. Allows to trace dependent transactions.
42    pub fn call_many(
43        &self,
44        reqs_with_trace_types: Vec<(CallRequest, Vec<TraceType>)>,
45        block: Option<BlockId>,
46    ) -> CallFuture<Vec<BlockTrace>, T::Out> {
47        let reqs_with_trace_types = helpers::serialize(&reqs_with_trace_types);
48        let block = helpers::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
49        CallFuture::new(
50            self.transport
51                .execute("trace_callMany", vec![reqs_with_trace_types, block]),
52        )
53    }
54
55    /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces
56    pub fn raw_transaction(&self, data: Bytes, trace_type: Vec<TraceType>) -> CallFuture<BlockTrace, T::Out> {
57        let data = helpers::serialize(&data);
58        let trace_type = helpers::serialize(&trace_type);
59        CallFuture::new(self.transport.execute("trace_rawTransaction", vec![data, trace_type]))
60    }
61
62    /// Replays a transaction, returning the traces
63    pub fn replay_transaction(&self, hash: H256, trace_type: Vec<TraceType>) -> CallFuture<BlockTrace, T::Out> {
64        let hash = helpers::serialize(&hash);
65        let trace_type = helpers::serialize(&trace_type);
66        CallFuture::new(
67            self.transport
68                .execute("trace_replayTransaction", vec![hash, trace_type]),
69        )
70    }
71
72    /// Replays all transactions in a block returning the requested traces for each transaction
73    pub fn replay_block_transactions(
74        &self,
75        block: BlockNumber,
76        trace_type: Vec<TraceType>,
77    ) -> CallFuture<Vec<BlockTrace>, T::Out> {
78        let block = helpers::serialize(&block);
79        let trace_type = helpers::serialize(&trace_type);
80        CallFuture::new(
81            self.transport
82                .execute("trace_replayBlockTransactions", vec![block, trace_type]),
83        )
84    }
85
86    /// Returns traces created at given block
87    pub fn block(&self, block: BlockNumber) -> CallFuture<Vec<Trace>, T::Out> {
88        let block = helpers::serialize(&block);
89        CallFuture::new(self.transport.execute("trace_block", vec![block]))
90    }
91
92    /// Return traces matching the given filter
93    ///
94    /// See [TraceFilterBuilder](../types/struct.TraceFilterBuilder.html)
95    pub fn filter(&self, filter: TraceFilter) -> CallFuture<Vec<Trace>, T::Out> {
96        let filter = helpers::serialize(&filter);
97        CallFuture::new(self.transport.execute("trace_filter", vec![filter]))
98    }
99
100    /// Returns trace at the given position
101    pub fn get(&self, hash: H256, index: Vec<Index>) -> CallFuture<Trace, T::Out> {
102        let hash = helpers::serialize(&hash);
103        let index = helpers::serialize(&index);
104        CallFuture::new(self.transport.execute("trace_get", vec![hash, index]))
105    }
106
107    /// Returns all traces of a given transaction
108    pub fn transaction(&self, hash: H256) -> CallFuture<Vec<Trace>, T::Out> {
109        let hash = helpers::serialize(&hash);
110        CallFuture::new(self.transport.execute("trace_transaction", vec![hash]))
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::Traces;
117    use crate::{
118        api::Namespace,
119        types::{Address, BlockNumber, BlockTrace, CallRequest, Trace, TraceFilterBuilder, TraceType, H256},
120    };
121    use hex_literal::hex;
122
123    const EXAMPLE_BLOCKTRACE: &str = r#"
124    {
125        "output": "0x010203",
126        "stateDiff": null,
127        "trace": [
128            {
129                "action": {
130                    "callType": "call",
131                    "from": "0x0000000000000000000000000000000000000000",
132                    "gas": "0x1dcd12f8",
133                    "input": "0x",
134                    "to": "0x0000000000000000000000000000000000000123",
135                    "value": "0x1"
136                },
137                "result": {
138                    "gasUsed": "0x0",
139                    "output": "0x"
140                },
141                "subtraces": 0,
142                "traceAddress": [],
143                "type": "call"
144            }
145        ],
146        "vmTrace": null
147    }
148    "#;
149
150    const EXAMPLE_BLOCKTRACES: &str = r#"
151	[{
152        "output": "0x",
153        "stateDiff": null,
154        "trace": [
155            {
156                "action": {
157                    "callType": "call",
158                    "from": "0xa1e4380a3b1f749673e270229993ee55f35663b4",
159                    "gas": "0x0",
160                    "input": "0x",
161                    "to": "0x5df9b87991262f6ba471f09758cde1c0fc1de734",
162                    "value": "0x7a69"
163                },
164                "result": {
165                    "gasUsed": "0x0",
166                    "output": "0x"
167                },
168                "subtraces": 0,
169                "traceAddress": [],
170                "type": "call"
171            }
172        ],
173        "transactionHash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
174        "vmTrace": null
175    }]
176	"#;
177
178    const EXAMPLE_TRACE_ARR: &str = r#"
179    [
180        {
181            "action": {
182                "callType": "call",
183                "from": "0xaa7b131dc60b80d3cf5e59b5a21a666aa039c951",
184                "gas": "0x0",
185                "input": "0x",
186                "to": "0xd40aba8166a212d6892125f079c33e6f5ca19814",
187                "value": "0x4768d7effc3fbe"
188            },
189            "blockHash": "0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add",
190            "blockNumber": 3068185,
191            "result": {
192                "gasUsed": "0x0",
193                "output": "0x"
194            },
195            "subtraces": 0,
196            "traceAddress": [],
197            "transactionHash": "0x07da28d752aba3b9dd7060005e554719c6205c8a3aea358599fc9b245c52f1f6",
198            "transactionPosition": 0,
199            "type": "call"
200        }
201    ]
202    "#;
203
204    const EXAMPLE_TRACE: &str = r#"
205      {
206          "action": {
207              "callType": "call",
208              "from": "0xaa7b131dc60b80d3cf5e59b5a21a666aa039c951",
209              "gas": "0x0",
210              "input": "0x",
211              "to": "0xd40aba8166a212d6892125f079c33e6f5ca19814",
212              "value": "0x4768d7effc3fbe"
213          },
214          "blockHash": "0x7eb25504e4c202cf3d62fd585d3e238f592c780cca82dacb2ed3cb5b38883add",
215          "blockNumber": 3068185,
216          "result": {
217              "gasUsed": "0x0",
218              "output": "0x"
219          },
220          "subtraces": 0,
221          "traceAddress": [],
222          "transactionHash": "0x07da28d752aba3b9dd7060005e554719c6205c8a3aea358599fc9b245c52f1f6",
223          "transactionPosition": 0,
224          "type": "call"
225      }
226    "#;
227
228    rpc_test!(
229    Traces:call, CallRequest {
230    from: None, to: Some(Address::from_low_u64_be(0x123)),
231    gas: None, gas_price: None,
232    value: Some(0x1.into()), data: None,
233    transaction_type: None, access_list: None,
234    max_fee_per_gas: None, max_priority_fee_per_gas: None,
235    }, vec![TraceType::Trace], None
236    =>
237    "trace_call", vec![r#"{"to":"0x0000000000000000000000000000000000000123","value":"0x1"}"#, r#"["trace"]"#, r#""latest""#];
238    ::serde_json::from_str(EXAMPLE_BLOCKTRACE).unwrap()
239    => ::serde_json::from_str::<BlockTrace>(EXAMPLE_BLOCKTRACE).unwrap()
240    );
241
242    rpc_test!(
243    Traces:raw_transaction, hex!("01020304"), vec![TraceType::Trace]
244    =>
245    "trace_rawTransaction", vec![r#""0x01020304""#, r#"["trace"]"#];
246    ::serde_json::from_str(EXAMPLE_BLOCKTRACE).unwrap()
247    => ::serde_json::from_str::<BlockTrace>(EXAMPLE_BLOCKTRACE).unwrap()
248    );
249
250    rpc_test!(
251    Traces:replay_transaction, "0000000000000000000000000000000000000000000000000000000000000123".parse::<H256>().unwrap(), vec![TraceType::Trace]
252    =>
253    "trace_replayTransaction", vec![r#""0x0000000000000000000000000000000000000000000000000000000000000123""#,r#"["trace"]"#];
254    ::serde_json::from_str(EXAMPLE_BLOCKTRACE).unwrap()
255    => ::serde_json::from_str::<BlockTrace>(EXAMPLE_BLOCKTRACE).unwrap()
256    );
257
258    rpc_test!(
259    Traces:replay_block_transactions, BlockNumber::Latest, vec![TraceType::Trace]
260    =>
261    "trace_replayBlockTransactions", vec![r#""latest""#, r#"["trace"]"#];
262    ::serde_json::from_str(EXAMPLE_BLOCKTRACES).unwrap()
263    => ::serde_json::from_str::<Vec<BlockTrace>>(EXAMPLE_BLOCKTRACES).unwrap()
264    );
265
266    rpc_test!(
267    Traces:block, BlockNumber::Latest
268    =>
269    "trace_block", vec![r#""latest""#];
270    ::serde_json::from_str(EXAMPLE_TRACE_ARR).unwrap()
271    => ::serde_json::from_str::<Vec<Trace>>(EXAMPLE_TRACE_ARR).unwrap()
272    );
273
274    rpc_test!(
275    Traces:filter, TraceFilterBuilder::default().build() => "trace_filter", vec!["{}"];
276    ::serde_json::from_str(EXAMPLE_TRACE_ARR).unwrap()
277    => ::serde_json::from_str::<Vec<Trace>>(EXAMPLE_TRACE_ARR).unwrap()
278    );
279
280    rpc_test!(
281    Traces:get, "0000000000000000000000000000000000000000000000000000000000000123".parse::<H256>().unwrap(), vec![0.into()]
282    =>
283    "trace_get", vec![r#""0x0000000000000000000000000000000000000000000000000000000000000123""#, r#"["0x0"]"#];
284    ::serde_json::from_str(EXAMPLE_TRACE).unwrap()
285    => ::serde_json::from_str::<Trace>(EXAMPLE_TRACE).unwrap()
286    );
287
288    rpc_test!(
289    Traces:transaction, "0000000000000000000000000000000000000000000000000000000000000123".parse::<H256>().unwrap()
290    =>
291    "trace_transaction", vec![r#""0x0000000000000000000000000000000000000000000000000000000000000123""#];
292    ::serde_json::from_str(EXAMPLE_TRACE_ARR).unwrap()
293    => ::serde_json::from_str::<Vec<Trace>>(EXAMPLE_TRACE_ARR).unwrap()
294    );
295}