1use crate::evm::Bytes;
19use alloc::{collections::BTreeMap, string::String, vec::Vec};
20use codec::{Decode, Encode};
21use derive_more::From;
22use scale_info::TypeInfo;
23use serde::{
24 ser::{SerializeMap, Serializer},
25 Deserialize, Serialize,
26};
27use sp_core::{H160, H256, U256};
28
29#[derive(TypeInfo, Debug, Clone, Encode, Decode, Serialize, Deserialize, PartialEq)]
32#[serde(tag = "tracer", content = "tracerConfig", rename_all = "camelCase")]
33pub enum TracerType {
34 CallTracer(Option<CallTracerConfig>),
36
37 PrestateTracer(Option<PrestateTracerConfig>),
39}
40
41impl From<CallTracerConfig> for TracerType {
42 fn from(config: CallTracerConfig) -> Self {
43 TracerType::CallTracer(Some(config))
44 }
45}
46
47impl Default for TracerType {
48 fn default() -> Self {
49 TracerType::CallTracer(Some(CallTracerConfig::default()))
50 }
51}
52
53#[derive(TypeInfo, Debug, Clone, Default, PartialEq)]
55#[cfg_attr(feature = "std", derive(Deserialize, Serialize), serde(rename_all = "camelCase"))]
56pub struct TracerConfig {
57 #[cfg_attr(feature = "std", serde(flatten, default))]
59 pub config: TracerType,
60
61 #[cfg_attr(feature = "std", serde(with = "humantime_serde", default))]
63 pub timeout: Option<core::time::Duration>,
64}
65
66#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)]
68#[serde(default, rename_all = "camelCase")]
69pub struct CallTracerConfig {
70 pub with_logs: bool,
72
73 pub only_top_call: bool,
75}
76
77impl Default for CallTracerConfig {
78 fn default() -> Self {
79 Self { with_logs: true, only_top_call: false }
80 }
81}
82
83#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)]
85#[serde(default, rename_all = "camelCase")]
86pub struct PrestateTracerConfig {
87 pub diff_mode: bool,
89
90 pub disable_storage: bool,
92
93 pub disable_code: bool,
95}
96
97impl Default for PrestateTracerConfig {
98 fn default() -> Self {
99 Self { diff_mode: false, disable_storage: false, disable_code: false }
100 }
101}
102
103#[test]
113fn test_tracer_config_serialization() {
114 let tracers = vec![
115 (
116 r#"{"tracer": "callTracer"}"#,
117 TracerConfig { config: TracerType::CallTracer(None), timeout: None },
118 ),
119 (
120 r#"{"tracer": "callTracer", "tracerConfig": { "withLogs": false }}"#,
121 TracerConfig {
122 config: CallTracerConfig { with_logs: false, only_top_call: false }.into(),
123 timeout: None,
124 },
125 ),
126 (
127 r#"{"tracer": "callTracer", "tracerConfig": { "onlyTopCall": true }}"#,
128 TracerConfig {
129 config: CallTracerConfig { with_logs: true, only_top_call: true }.into(),
130 timeout: None,
131 },
132 ),
133 (
134 r#"{"tracer": "callTracer", "tracerConfig": { "onlyTopCall": true }, "timeout": "10ms"}"#,
135 TracerConfig {
136 config: CallTracerConfig { with_logs: true, only_top_call: true }.into(),
137 timeout: Some(core::time::Duration::from_millis(10)),
138 },
139 ),
140 ];
141
142 for (json_data, expected) in tracers {
143 let result: TracerConfig =
144 serde_json::from_str(json_data).expect("Deserialization should succeed");
145 assert_eq!(result, expected);
146 }
147}
148
149#[derive(
151 Default, TypeInfo, Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Clone, Debug,
152)]
153#[serde(rename_all = "UPPERCASE")]
154pub enum CallType {
155 #[default]
157 Call,
158 StaticCall,
160 DelegateCall,
162 Create,
164 Create2,
166}
167
168#[derive(TypeInfo, From, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
170#[serde(untagged)]
171pub enum Trace {
172 Call(CallTrace),
174 Prestate(PrestateTrace),
176}
177
178#[derive(TypeInfo, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
180#[serde(untagged)]
181pub enum PrestateTrace {
182 Prestate(BTreeMap<H160, PrestateTraceInfo>),
184
185 DiffMode {
188 pre: BTreeMap<H160, PrestateTraceInfo>,
193 post: BTreeMap<H160, PrestateTraceInfo>,
196 },
197}
198
199impl PrestateTrace {
200 pub fn state_mut(
202 &mut self,
203 ) -> (&mut BTreeMap<H160, PrestateTraceInfo>, Option<&mut BTreeMap<H160, PrestateTraceInfo>>) {
204 match self {
205 PrestateTrace::Prestate(pre) => (pre, None),
206 PrestateTrace::DiffMode { pre, post } => (pre, Some(post)),
207 }
208 }
209}
210
211#[derive(
213 TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
214)]
215pub struct PrestateTraceInfo {
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub balance: Option<U256>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub nonce: Option<u32>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub code: Option<Bytes>,
225 #[serde(skip_serializing_if = "is_empty", serialize_with = "serialize_map_skip_none")]
227 pub storage: BTreeMap<Bytes, Option<Bytes>>,
228}
229
230pub fn is_empty<K, V>(map: &BTreeMap<K, Option<V>>) -> bool {
232 !map.values().any(|v| v.is_some())
233}
234
235pub fn serialize_map_skip_none<S, K, V>(
237 map: &BTreeMap<K, Option<V>>,
238 serializer: S,
239) -> Result<S::Ok, S::Error>
240where
241 S: Serializer,
242 K: serde::Serialize,
243 V: serde::Serialize,
244{
245 let len = map.values().filter(|v| v.is_some()).count();
246 let mut ser_map = serializer.serialize_map(Some(len))?;
247
248 for (key, opt_val) in map {
249 if let Some(val) = opt_val {
250 ser_map.serialize_entry(key, val)?;
251 }
252 }
253
254 ser_map.end()
255}
256
257#[derive(
259 TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
260)]
261#[serde(rename_all = "camelCase")]
262pub struct CallTrace<Gas = U256> {
263 pub from: H160,
265 pub gas: Gas,
267 pub gas_used: Gas,
269 pub to: H160,
271 pub input: Bytes,
273 #[serde(skip_serializing_if = "Bytes::is_empty")]
275 pub output: Bytes,
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub error: Option<String>,
279 #[serde(skip_serializing_if = "Option::is_none")]
281 pub revert_reason: Option<String>,
282 #[serde(skip_serializing_if = "Vec::is_empty")]
284 pub calls: Vec<CallTrace<Gas>>,
285 #[serde(skip_serializing_if = "Vec::is_empty")]
287 pub logs: Vec<CallLog>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub value: Option<U256>,
291 #[serde(rename = "type")]
293 pub call_type: CallType,
294 #[serde(skip)]
296 pub child_call_count: u32,
297}
298
299#[derive(
301 Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq,
302)]
303pub struct CallLog {
304 pub address: H160,
306 #[serde(default, skip_serializing_if = "Vec::is_empty")]
308 pub topics: Vec<H256>,
309 pub data: Bytes,
311 #[serde(with = "super::hex_serde")]
314 pub position: u32,
315}
316
317#[derive(Serialize, Deserialize, Clone, Debug)]
319#[serde(rename_all = "camelCase")]
320pub struct TransactionTrace {
321 pub tx_hash: H256,
323 #[serde(rename = "result")]
325 pub trace: Trace,
326}