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 de::{Error, MapAccess, Visitor},
25 ser::{SerializeMap, Serializer},
26 Deserialize, Serialize,
27};
28use sp_core::{H160, H256, U256};
29
30#[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 Selfdestruct,
168}
169
170#[derive(TypeInfo, Deserialize, Serialize, From, Encode, Decode, Clone, Debug, Eq, PartialEq)]
172#[serde(untagged)]
173pub enum Trace {
174 Call(CallTrace),
176 Prestate(PrestateTrace),
178}
179
180#[derive(TypeInfo, Encode, Serialize, Decode, Clone, Debug, Eq, PartialEq)]
182#[serde(untagged)]
183pub enum PrestateTrace {
184 Prestate(BTreeMap<H160, PrestateTraceInfo>),
186
187 DiffMode {
190 pre: BTreeMap<H160, PrestateTraceInfo>,
195 post: BTreeMap<H160, PrestateTraceInfo>,
198 },
199}
200
201impl<'de> Deserialize<'de> for PrestateTrace {
202 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203 where
204 D: serde::Deserializer<'de>,
205 {
206 struct PrestateTraceVisitor;
207
208 impl<'de> Visitor<'de> for PrestateTraceVisitor {
209 type Value = PrestateTrace;
210
211 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
212 formatter.write_str("a map representing either Prestate or DiffMode")
213 }
214
215 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
216 where
217 A: MapAccess<'de>,
218 {
219 let mut pre_map = None;
220 let mut post_map = None;
221 let mut account_map = BTreeMap::new();
222
223 while let Some(key) = map.next_key::<String>()? {
224 match key.as_str() {
225 "pre" => {
226 if pre_map.is_some() {
227 return Err(Error::duplicate_field("pre"));
228 }
229 pre_map = Some(map.next_value::<BTreeMap<H160, PrestateTraceInfo>>()?);
230 },
231 "post" => {
232 if post_map.is_some() {
233 return Err(Error::duplicate_field("post"));
234 }
235 post_map = Some(map.next_value::<BTreeMap<H160, PrestateTraceInfo>>()?);
236 },
237 _ => {
238 let addr: H160 =
239 key.parse().map_err(|_| Error::custom("Invalid address"))?;
240 let info = map.next_value::<PrestateTraceInfo>()?;
241 account_map.insert(addr, info);
242 },
243 }
244 }
245
246 match (pre_map, post_map) {
247 (Some(pre), Some(post)) => {
248 if !account_map.is_empty() {
249 return Err(Error::custom("Mixed diff and prestate mode"));
250 }
251 Ok(PrestateTrace::DiffMode { pre, post })
252 },
253 (None, None) => Ok(PrestateTrace::Prestate(account_map)),
254 _ => Err(Error::custom("diff mode: must have both 'pre' and 'post'")),
255 }
256 }
257 }
258
259 deserializer.deserialize_map(PrestateTraceVisitor)
260 }
261}
262
263impl PrestateTrace {
264 pub fn state_mut(
266 &mut self,
267 ) -> (&mut BTreeMap<H160, PrestateTraceInfo>, Option<&mut BTreeMap<H160, PrestateTraceInfo>>) {
268 match self {
269 PrestateTrace::Prestate(pre) => (pre, None),
270 PrestateTrace::DiffMode { pre, post } => (pre, Some(post)),
271 }
272 }
273}
274
275#[derive(
277 TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
278)]
279pub struct PrestateTraceInfo {
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub balance: Option<U256>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub nonce: Option<u32>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub code: Option<Bytes>,
289 #[serde(default, skip_serializing_if = "is_empty", serialize_with = "serialize_map_skip_none")]
291 pub storage: BTreeMap<Bytes, Option<Bytes>>,
292}
293
294pub fn is_empty<K, V>(map: &BTreeMap<K, Option<V>>) -> bool {
296 !map.values().any(|v| v.is_some())
297}
298
299pub fn serialize_map_skip_none<S, K, V>(
301 map: &BTreeMap<K, Option<V>>,
302 serializer: S,
303) -> Result<S::Ok, S::Error>
304where
305 S: Serializer,
306 K: serde::Serialize,
307 V: serde::Serialize,
308{
309 let len = map.values().filter(|v| v.is_some()).count();
310 let mut ser_map = serializer.serialize_map(Some(len))?;
311
312 for (key, opt_val) in map {
313 if let Some(val) = opt_val {
314 ser_map.serialize_entry(key, val)?;
315 }
316 }
317
318 ser_map.end()
319}
320
321#[derive(
323 TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
324)]
325#[serde(rename_all = "camelCase")]
326pub struct CallTrace<Gas = U256> {
327 pub from: H160,
329 pub gas: Gas,
331 pub gas_used: Gas,
333 pub to: H160,
335 pub input: Bytes,
337 #[serde(skip_serializing_if = "Bytes::is_empty")]
339 pub output: Bytes,
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub error: Option<String>,
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub revert_reason: Option<String>,
346 #[serde(skip_serializing_if = "Vec::is_empty")]
348 pub calls: Vec<CallTrace<Gas>>,
349 #[serde(skip_serializing_if = "Vec::is_empty")]
351 pub logs: Vec<CallLog>,
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub value: Option<U256>,
355 #[serde(rename = "type")]
357 pub call_type: CallType,
358 #[serde(skip)]
360 pub child_call_count: u32,
361}
362
363#[derive(
365 Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq,
366)]
367pub struct CallLog {
368 pub address: H160,
370 #[serde(default, skip_serializing_if = "Vec::is_empty")]
372 pub topics: Vec<H256>,
373 pub data: Bytes,
375 #[serde(with = "super::hex_serde")]
378 pub position: u32,
379}
380
381#[derive(Serialize, Deserialize, Clone, Debug)]
383#[serde(rename_all = "camelCase")]
384pub struct TransactionTrace {
385 pub tx_hash: H256,
387 #[serde(rename = "result")]
389 pub trace: Trace,
390}