ic_web3_rs/api/
traces.rs

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