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#[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 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 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 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 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 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 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 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 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 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}