1use crate::{
2 CallTrace, CallTraceArena, CallTraceNode, DecodedCallData,
3 debug::DebugTraceIdentifier,
4 identifier::{IdentifiedAddress, LocalTraceIdentifier, SignaturesIdentifier, TraceIdentifier},
5};
6use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt};
7use alloy_json_abi::{Error, Event, Function, JsonAbi};
8use alloy_primitives::{
9 Address, B256, LogData, Selector,
10 map::{HashMap, HashSet, hash_map::Entry},
11};
12use foundry_common::{
13 ContractsByArtifact, SELECTOR_LEN, abi::get_indexed_event, fmt::format_token,
14 get_contract_name, selectors::SelectorKind,
15};
16use foundry_evm_core::{
17 abi::{Vm, console},
18 constants::{
19 CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS,
20 TEST_CONTRACT_ADDRESS,
21 },
22 decode::RevertDecoder,
23 precompiles::{
24 BLAKE_2F, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY, MOD_EXP, POINT_EVALUATION,
25 RIPEMD_160, SHA_256,
26 },
27};
28use itertools::Itertools;
29use revm_inspectors::tracing::types::{DecodedCallLog, DecodedCallTrace};
30use std::{collections::BTreeMap, sync::OnceLock};
31
32mod precompiles;
33
34#[derive(Default)]
36#[must_use = "builders do nothing unless you call `build` on them"]
37pub struct CallTraceDecoderBuilder {
38 decoder: CallTraceDecoder,
39}
40
41impl CallTraceDecoderBuilder {
42 #[inline]
44 pub fn new() -> Self {
45 Self { decoder: CallTraceDecoder::new().clone() }
46 }
47
48 #[inline]
50 pub fn with_labels(mut self, labels: impl IntoIterator<Item = (Address, String)>) -> Self {
51 self.decoder.labels.extend(labels);
52 self
53 }
54
55 #[inline]
57 pub fn with_abi(mut self, abi: &JsonAbi) -> Self {
58 self.decoder.collect_abi(abi, None);
59 self
60 }
61
62 #[inline]
64 pub fn with_known_contracts(mut self, contracts: &ContractsByArtifact) -> Self {
65 trace!(target: "evm::traces", len=contracts.len(), "collecting known contract ABIs");
66 for contract in contracts.values() {
67 self.decoder.collect_abi(&contract.abi, None);
68 }
69 self
70 }
71
72 #[inline]
74 pub fn with_local_identifier_abis(self, identifier: &LocalTraceIdentifier<'_>) -> Self {
75 self.with_known_contracts(identifier.contracts())
76 }
77
78 #[inline]
80 pub fn with_verbosity(mut self, level: u8) -> Self {
81 self.decoder.verbosity = level;
82 self
83 }
84
85 #[inline]
87 pub fn with_signature_identifier(mut self, identifier: SignaturesIdentifier) -> Self {
88 self.decoder.signature_identifier = Some(identifier);
89 self
90 }
91
92 #[inline]
94 pub fn with_label_disabled(mut self, disable_alias: bool) -> Self {
95 self.decoder.disable_labels = disable_alias;
96 self
97 }
98
99 #[inline]
101 pub fn with_debug_identifier(mut self, identifier: DebugTraceIdentifier) -> Self {
102 self.decoder.debug_identifier = Some(identifier);
103 self
104 }
105
106 #[inline]
108 pub fn build(self) -> CallTraceDecoder {
109 self.decoder
110 }
111}
112
113#[derive(Clone, Debug, Default)]
121pub struct CallTraceDecoder {
122 pub contracts: HashMap<Address, String>,
126 pub labels: HashMap<Address, String>,
128 pub receive_contracts: HashSet<Address>,
130 pub fallback_contracts: HashMap<Address, HashSet<Selector>>,
133 pub non_fallback_contracts: HashMap<Address, HashSet<Selector>>,
136
137 pub functions: HashMap<Selector, Vec<Function>>,
139 pub events: BTreeMap<(B256, usize), Vec<Event>>,
143 pub revert_decoder: RevertDecoder,
145
146 pub signature_identifier: Option<SignaturesIdentifier>,
148 pub verbosity: u8,
150
151 pub debug_identifier: Option<DebugTraceIdentifier>,
153
154 pub disable_labels: bool,
156}
157
158impl CallTraceDecoder {
159 pub fn new() -> &'static Self {
164 static INIT: OnceLock<CallTraceDecoder> = OnceLock::new();
167 INIT.get_or_init(Self::init)
168 }
169
170 fn init() -> Self {
171 Self {
172 contracts: Default::default(),
173 labels: HashMap::from_iter([
174 (CHEATCODE_ADDRESS, "VM".to_string()),
175 (HARDHAT_CONSOLE_ADDRESS, "console".to_string()),
176 (DEFAULT_CREATE2_DEPLOYER, "Create2Deployer".to_string()),
177 (CALLER, "DefaultSender".to_string()),
178 (TEST_CONTRACT_ADDRESS, "DefaultTestContract".to_string()),
179 (EC_RECOVER, "ECRecover".to_string()),
180 (SHA_256, "SHA-256".to_string()),
181 (RIPEMD_160, "RIPEMD-160".to_string()),
182 (IDENTITY, "Identity".to_string()),
183 (MOD_EXP, "ModExp".to_string()),
184 (EC_ADD, "ECAdd".to_string()),
185 (EC_MUL, "ECMul".to_string()),
186 (EC_PAIRING, "ECPairing".to_string()),
187 (BLAKE_2F, "Blake2F".to_string()),
188 (POINT_EVALUATION, "PointEvaluation".to_string()),
189 ]),
190 receive_contracts: Default::default(),
191 fallback_contracts: Default::default(),
192 non_fallback_contracts: Default::default(),
193
194 functions: console::hh::abi::functions()
195 .into_values()
196 .chain(Vm::abi::functions().into_values())
197 .flatten()
198 .map(|func| (func.selector(), vec![func]))
199 .collect(),
200 events: console::ds::abi::events()
201 .into_values()
202 .flatten()
203 .map(|event| ((event.selector(), indexed_inputs(&event)), vec![event]))
204 .collect(),
205 revert_decoder: Default::default(),
206
207 signature_identifier: None,
208 verbosity: 0,
209
210 debug_identifier: None,
211
212 disable_labels: false,
213 }
214 }
215
216 pub fn clear_addresses(&mut self) {
218 self.contracts.clear();
219
220 let default_labels = &Self::new().labels;
221 if self.labels.len() > default_labels.len() {
222 self.labels.clone_from(default_labels);
223 }
224
225 self.receive_contracts.clear();
226 self.fallback_contracts.clear();
227 }
228
229 pub fn identify(&mut self, arena: &CallTraceArena, identifier: &mut impl TraceIdentifier) {
233 self.collect_identified_addresses(self.identify_addresses(arena, identifier));
234 }
235
236 pub fn identify_addresses<'a>(
240 &self,
241 arena: &CallTraceArena,
242 identifier: &'a mut impl TraceIdentifier,
243 ) -> Vec<IdentifiedAddress<'a>> {
244 let nodes = arena.nodes().iter().filter(|node| {
245 let address = &node.trace.address;
246 !self.labels.contains_key(address) || !self.contracts.contains_key(address)
247 });
248 identifier.identify_addresses(&nodes.collect::<Vec<_>>())
249 }
250
251 pub fn push_event(&mut self, event: Event) {
253 self.events.entry((event.selector(), indexed_inputs(&event))).or_default().push(event);
254 }
255
256 pub fn push_function(&mut self, function: Function) {
258 match self.functions.entry(function.selector()) {
259 Entry::Occupied(entry) => {
260 if entry.get().contains(&function) {
262 return;
263 }
264 trace!(target: "evm::traces", selector=%entry.key(), new=%function.signature(), "duplicate function selector");
265 entry.into_mut().push(function);
266 }
267 Entry::Vacant(entry) => {
268 entry.insert(vec![function]);
269 }
270 }
271 }
272
273 pub fn push_error(&mut self, error: Error) {
275 self.revert_decoder.push_error(error);
276 }
277
278 pub fn without_label(&mut self, disable: bool) {
279 self.disable_labels = disable;
280 }
281
282 fn collect_identified_addresses(&mut self, mut addrs: Vec<IdentifiedAddress<'_>>) {
283 addrs.sort_by_key(|identity| identity.address);
284 addrs.dedup_by_key(|identity| identity.address);
285 if addrs.is_empty() {
286 return;
287 }
288
289 trace!(target: "evm::traces", len=addrs.len(), "collecting address identities");
290 for IdentifiedAddress { address, label, contract, abi, artifact_id: _ } in addrs {
291 let _span = trace_span!(target: "evm::traces", "identity", ?contract, ?label).entered();
292
293 if let Some(contract) = contract {
294 self.contracts.entry(address).or_insert(contract);
295 }
296
297 if let Some(label) = label {
298 self.labels.entry(address).or_insert(label);
299 }
300
301 if let Some(abi) = abi {
302 self.collect_abi(&abi, Some(address));
303 }
304 }
305 }
306
307 fn collect_abi(&mut self, abi: &JsonAbi, address: Option<Address>) {
308 let len = abi.len();
309 if len == 0 {
310 return;
311 }
312 trace!(target: "evm::traces", len, ?address, "collecting ABI");
313 for function in abi.functions() {
314 self.push_function(function.clone());
315 }
316 for event in abi.events() {
317 self.push_event(event.clone());
318 }
319 for error in abi.errors() {
320 self.push_error(error.clone());
321 }
322 if let Some(address) = address {
323 if abi.receive.is_some() {
324 self.receive_contracts.insert(address);
325 }
326
327 if abi.fallback.is_some() {
328 self.fallback_contracts
329 .insert(address, abi.functions().map(|f| f.selector()).collect());
330 } else {
331 self.non_fallback_contracts
332 .insert(address, abi.functions().map(|f| f.selector()).collect());
333 }
334 }
335 }
336
337 pub async fn populate_traces(&self, traces: &mut Vec<CallTraceNode>) {
341 for node in traces {
342 node.trace.decoded = self.decode_function(&node.trace).await;
343 for log in &mut node.logs {
344 log.decoded = self.decode_event(&log.raw_log).await;
345 }
346
347 if let Some(debug) = self.debug_identifier.as_ref()
348 && let Some(identified) = self.contracts.get(&node.trace.address)
349 {
350 debug.identify_node_steps(node, get_contract_name(identified))
351 }
352 }
353 }
354
355 pub async fn decode_function(&self, trace: &CallTrace) -> DecodedCallTrace {
357 let label =
358 if self.disable_labels { None } else { self.labels.get(&trace.address).cloned() };
359
360 if trace.kind.is_any_create() {
361 return DecodedCallTrace { label, ..Default::default() };
362 }
363
364 if let Some(trace) = precompiles::decode(trace, 1) {
365 return trace;
366 }
367
368 let cdata = &trace.data;
369 if trace.address == DEFAULT_CREATE2_DEPLOYER {
370 return DecodedCallTrace {
371 label,
372 call_data: Some(DecodedCallData { signature: "create2".to_string(), args: vec![] }),
373 return_data: self.default_return_data(trace),
374 };
375 }
376
377 if is_abi_call_data(cdata) {
378 let selector = Selector::try_from(&cdata[..SELECTOR_LEN]).unwrap();
379 let mut functions = Vec::new();
380 let functions = match self.functions.get(&selector) {
381 Some(fs) => fs,
382 None => {
383 if let Some(identifier) = &self.signature_identifier
384 && let Some(function) = identifier.identify_function(selector).await
385 {
386 functions.push(function);
387 }
388 &functions
389 }
390 };
391
392 if let Some(contract_selectors) = self.non_fallback_contracts.get(&trace.address)
395 && !contract_selectors.contains(&selector)
396 && (!cdata.is_empty() || !self.receive_contracts.contains(&trace.address))
397 {
398 let return_data = if !trace.success {
399 let revert_msg = self.revert_decoder.decode(&trace.output, trace.status);
400
401 if trace.output.is_empty() || revert_msg.contains("EvmError: Revert") {
402 Some(format!(
403 "unrecognized function selector {} for contract {}, which has no fallback function.",
404 selector, trace.address
405 ))
406 } else {
407 Some(revert_msg)
408 }
409 } else {
410 None
411 };
412
413 return if let Some(func) = functions.first() {
414 DecodedCallTrace {
415 label,
416 call_data: Some(self.decode_function_input(trace, func)),
417 return_data,
418 }
419 } else {
420 DecodedCallTrace {
421 label,
422 call_data: self.fallback_call_data(trace),
423 return_data,
424 }
425 };
426 }
427
428 let [func, ..] = &functions[..] else {
429 return DecodedCallTrace {
430 label,
431 call_data: self.fallback_call_data(trace),
432 return_data: self.default_return_data(trace),
433 };
434 };
435
436 let mut call_data = self.decode_function_input(trace, func);
439 if let Some(fallback_functions) = self.fallback_contracts.get(&trace.address)
440 && !fallback_functions.contains(&selector)
441 && let Some(cd) = self.fallback_call_data(trace)
442 {
443 call_data.signature = cd.signature;
444 }
445
446 DecodedCallTrace {
447 label,
448 call_data: Some(call_data),
449 return_data: self.decode_function_output(trace, functions),
450 }
451 } else {
452 DecodedCallTrace {
453 label,
454 call_data: self.fallback_call_data(trace),
455 return_data: self.default_return_data(trace),
456 }
457 }
458 }
459
460 fn decode_function_input(&self, trace: &CallTrace, func: &Function) -> DecodedCallData {
462 let mut args = None;
463 if trace.data.len() >= SELECTOR_LEN {
464 if trace.address == CHEATCODE_ADDRESS {
465 if let Some(v) = self.decode_cheatcode_inputs(func, &trace.data) {
467 args = Some(v);
468 }
469 }
470
471 if args.is_none()
472 && let Ok(v) = func.abi_decode_input(&trace.data[SELECTOR_LEN..])
473 {
474 args = Some(v.iter().map(|value| self.format_value(value)).collect());
475 }
476 }
477
478 DecodedCallData { signature: func.signature(), args: args.unwrap_or_default() }
479 }
480
481 fn decode_cheatcode_inputs(&self, func: &Function, data: &[u8]) -> Option<Vec<String>> {
483 match func.name.as_str() {
484 "expectRevert" => Some(vec![self.revert_decoder.decode(data, None)]),
485 "addr" | "createWallet" | "deriveKey" | "rememberKey" => {
486 Some(vec!["<pk>".to_string()])
488 }
489 "broadcast" | "startBroadcast" => {
490 if !func.inputs.is_empty() && func.inputs[0].ty == "uint256" {
493 Some(vec!["<pk>".to_string()])
494 } else {
495 None
496 }
497 }
498 "getNonce" => {
499 if !func.inputs.is_empty() && func.inputs[0].ty == "tuple" {
502 Some(vec!["<pk>".to_string()])
503 } else {
504 None
505 }
506 }
507 "sign" | "signP256" => {
508 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
509
510 if !decoded.is_empty() &&
513 (func.inputs[0].ty == "uint256" || func.inputs[0].ty == "tuple")
514 {
515 decoded[0] = DynSolValue::String("<pk>".to_string());
516 }
517
518 Some(decoded.iter().map(format_token).collect())
519 }
520 "signDelegation" | "signAndAttachDelegation" => {
521 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
522 decoded[1] = DynSolValue::String("<pk>".to_string());
526 Some(decoded.iter().map(format_token).collect())
527 }
528 "parseJson" |
529 "parseJsonUint" |
530 "parseJsonUintArray" |
531 "parseJsonInt" |
532 "parseJsonIntArray" |
533 "parseJsonString" |
534 "parseJsonStringArray" |
535 "parseJsonAddress" |
536 "parseJsonAddressArray" |
537 "parseJsonBool" |
538 "parseJsonBoolArray" |
539 "parseJsonBytes" |
540 "parseJsonBytesArray" |
541 "parseJsonBytes32" |
542 "parseJsonBytes32Array" |
543 "writeJson" |
544 "keyExists" |
546 "keyExistsJson" |
547 "serializeBool" |
548 "serializeUint" |
549 "serializeUintToHex" |
550 "serializeInt" |
551 "serializeAddress" |
552 "serializeBytes32" |
553 "serializeString" |
554 "serializeBytes" => {
555 if self.verbosity >= 5 {
556 None
557 } else {
558 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
559 let token = if func.name.as_str() == "parseJson" ||
560 func.name.as_str() == "keyExists" ||
562 func.name.as_str() == "keyExistsJson"
563 {
564 "<JSON file>"
565 } else {
566 "<stringified JSON>"
567 };
568 decoded[0] = DynSolValue::String(token.to_string());
569 Some(decoded.iter().map(format_token).collect())
570 }
571 }
572 s if s.contains("Toml") => {
573 if self.verbosity >= 5 {
574 None
575 } else {
576 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
577 let token = if func.name.as_str() == "parseToml" ||
578 func.name.as_str() == "keyExistsToml"
579 {
580 "<TOML file>"
581 } else {
582 "<stringified TOML>"
583 };
584 decoded[0] = DynSolValue::String(token.to_string());
585 Some(decoded.iter().map(format_token).collect())
586 }
587 }
588 "createFork" |
589 "createSelectFork" |
590 "rpc" => {
591 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
592
593 if !decoded.is_empty() && func.inputs[0].ty == "string" {
595 let url_or_alias = decoded[0].as_str().unwrap_or_default();
596
597 if url_or_alias.starts_with("http") || url_or_alias.starts_with("ws") {
598 decoded[0] = DynSolValue::String("<rpc url>".to_string());
599 }
600 } else {
601 return None;
602 }
603
604 Some(decoded.iter().map(format_token).collect())
605 }
606 _ => None,
607 }
608 }
609
610 fn decode_function_output(&self, trace: &CallTrace, funcs: &[Function]) -> Option<String> {
612 if !trace.success {
613 return self.default_return_data(trace);
614 }
615
616 if trace.address == CHEATCODE_ADDRESS
617 && let Some(decoded) = funcs.iter().find_map(|func| self.decode_cheatcode_outputs(func))
618 {
619 return Some(decoded);
620 }
621
622 if let Some(values) =
623 funcs.iter().find_map(|func| func.abi_decode_output(&trace.output).ok())
624 {
625 if values.is_empty() {
628 return None;
629 }
630
631 return Some(
632 values.iter().map(|value| self.format_value(value)).format(", ").to_string(),
633 );
634 }
635
636 None
637 }
638
639 fn decode_cheatcode_outputs(&self, func: &Function) -> Option<String> {
641 match func.name.as_str() {
642 s if s.starts_with("env") => Some("<env var value>"),
643 "createWallet" | "deriveKey" => Some("<pk>"),
644 "promptSecret" | "promptSecretUint" => Some("<secret>"),
645 "parseJson" if self.verbosity < 5 => Some("<encoded JSON value>"),
646 "readFile" if self.verbosity < 5 => Some("<file>"),
647 "rpcUrl" | "rpcUrls" | "rpcUrlStructs" => Some("<rpc url>"),
648 _ => None,
649 }
650 .map(Into::into)
651 }
652
653 #[track_caller]
654 fn fallback_call_data(&self, trace: &CallTrace) -> Option<DecodedCallData> {
655 let cdata = &trace.data;
656 let signature = if cdata.is_empty() && self.receive_contracts.contains(&trace.address) {
657 "receive()"
658 } else if self.fallback_contracts.contains_key(&trace.address) {
659 "fallback()"
660 } else {
661 return None;
662 }
663 .to_string();
664 let args = if cdata.is_empty() { Vec::new() } else { vec![cdata.to_string()] };
665 Some(DecodedCallData { signature, args })
666 }
667
668 fn default_return_data(&self, trace: &CallTrace) -> Option<String> {
670 if trace.status.is_none() || trace.status.is_some_and(|s| s.is_ok()) {
675 return None;
676 }
677 (!trace.success).then(|| self.revert_decoder.decode(&trace.output, trace.status))
678 }
679
680 pub async fn decode_event(&self, log: &LogData) -> DecodedCallLog {
682 let &[t0, ..] = log.topics() else { return DecodedCallLog { name: None, params: None } };
683
684 let mut events = Vec::new();
685 let events = match self.events.get(&(t0, log.topics().len() - 1)) {
686 Some(es) => es,
687 None => {
688 if let Some(identifier) = &self.signature_identifier
689 && let Some(event) = identifier.identify_event(t0).await
690 {
691 events.push(get_indexed_event(event, log));
692 }
693 &events
694 }
695 };
696 for event in events {
697 if let Ok(decoded) = event.decode_log(log) {
698 let params = reconstruct_params(event, &decoded);
699 return DecodedCallLog {
700 name: Some(event.name.clone()),
701 params: Some(
702 params
703 .into_iter()
704 .zip(event.inputs.iter())
705 .map(|(param, input)| {
706 let name = input.name.clone();
708 (name, self.format_value(¶m))
709 })
710 .collect(),
711 ),
712 };
713 }
714 }
715
716 DecodedCallLog { name: None, params: None }
717 }
718
719 pub async fn prefetch_signatures(&self, nodes: &[CallTraceNode]) {
721 let Some(identifier) = &self.signature_identifier else { return };
722 let events = nodes
723 .iter()
724 .flat_map(|node| {
725 node.logs
726 .iter()
727 .map(|log| log.raw_log.topics())
728 .filter(|&topics| {
729 if let Some(&first) = topics.first()
730 && self.events.contains_key(&(first, topics.len() - 1))
731 {
732 return false;
733 }
734 true
735 })
736 .filter_map(|topics| topics.first())
737 })
738 .copied();
739 let functions = nodes
740 .iter()
741 .filter(|&n| {
742 if n.trace.address == DEFAULT_CREATE2_DEPLOYER
744 || n.is_precompile()
745 || precompiles::is_known_precompile(n.trace.address, 1)
746 {
747 return false;
748 }
749 if n.trace.kind.is_any_create() || !is_abi_call_data(&n.trace.data) {
751 return false;
752 }
753 true
754 })
755 .filter_map(|n| n.trace.data.first_chunk().map(Selector::from))
756 .filter(|selector| !self.functions.contains_key(selector));
757 let selectors = events
758 .map(SelectorKind::Event)
759 .chain(functions.map(SelectorKind::Function))
760 .unique()
761 .collect::<Vec<_>>();
762 let _ = identifier.identify(&selectors).await;
763 }
764
765 fn format_value(&self, value: &DynSolValue) -> String {
767 if let DynSolValue::Address(addr) = value
768 && let Some(label) = self.labels.get(addr)
769 {
770 return format!("{label}: [{addr}]");
771 }
772 format_token(value)
773 }
774}
775
776fn is_abi_call_data(data: &[u8]) -> bool {
780 match data.len().cmp(&SELECTOR_LEN) {
781 std::cmp::Ordering::Less => false,
782 std::cmp::Ordering::Equal => true,
783 std::cmp::Ordering::Greater => is_abi_data(&data[SELECTOR_LEN..]),
784 }
785}
786
787fn is_abi_data(data: &[u8]) -> bool {
791 let rem = data.len() % 32;
792 if rem == 0 || data.is_empty() {
793 return true;
794 }
795 data[data.len() - rem..].iter().all(|byte| *byte == 0)
797}
798
799fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue> {
802 let mut indexed = 0;
803 let mut unindexed = 0;
804 let mut inputs = vec![];
805 for input in &event.inputs {
806 if input.indexed && indexed < decoded.indexed.len() {
810 inputs.push(decoded.indexed[indexed].clone());
811 indexed += 1;
812 } else if unindexed < decoded.body.len() {
813 inputs.push(decoded.body[unindexed].clone());
814 unindexed += 1;
815 }
816 }
817
818 inputs
819}
820
821fn indexed_inputs(event: &Event) -> usize {
822 event.inputs.iter().filter(|param| param.indexed).count()
823}
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828 use alloy_primitives::hex;
829
830 #[test]
831 fn test_should_redact() {
832 let decoder = CallTraceDecoder::new();
833
834 let cheatcode_input_test_cases = vec![
836 ("addr(uint256)", vec![], Some(vec!["<pk>".to_string()])),
838 ("createWallet(string)", vec![], Some(vec!["<pk>".to_string()])),
839 ("createWallet(uint256)", vec![], Some(vec!["<pk>".to_string()])),
840 ("deriveKey(string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
841 ("deriveKey(string,string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
842 ("deriveKey(string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
843 ("deriveKey(string,string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
844 ("rememberKey(uint256)", vec![], Some(vec!["<pk>".to_string()])),
845 ("broadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
848 ("broadcast()", vec![], None), ("startBroadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
850 ("startBroadcast()", vec![], None), ("getNonce((address,uint256,uint256,uint256))", vec![], Some(vec!["<pk>".to_string()])),
852 ("getNonce(address)", vec![], None), (
856 "sign(uint256,bytes32)",
857 hex!(
858 "
859 e341eaa4
860 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
861 0000000000000000000000000000000000000000000000000000000000000000
862 "
863 )
864 .to_vec(),
865 Some(vec![
866 "\"<pk>\"".to_string(),
867 "0x0000000000000000000000000000000000000000000000000000000000000000"
868 .to_string(),
869 ]),
870 ),
871 (
872 "signP256(uint256,bytes32)",
873 hex!(
874 "
875 83211b40
876 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
877 0000000000000000000000000000000000000000000000000000000000000000
878 "
879 )
880 .to_vec(),
881 Some(vec![
882 "\"<pk>\"".to_string(),
883 "0x0000000000000000000000000000000000000000000000000000000000000000"
884 .to_string(),
885 ]),
886 ),
887 (
888 "createFork(string)",
890 hex!(
891 "
892 31ba3498
893 0000000000000000000000000000000000000000000000000000000000000020
894 000000000000000000000000000000000000000000000000000000000000002c
895 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
896 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
897 "
898 )
899 .to_vec(),
900 Some(vec!["\"<rpc url>\"".to_string()]),
901 ),
902 (
903 "createFork(string)",
905 hex!(
906 "
907 31ba3498
908 0000000000000000000000000000000000000000000000000000000000000020
909 000000000000000000000000000000000000000000000000000000000000002a
910 7773733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f6d2f
911 76322f6170695f6b657900000000000000000000000000000000000000000000
912 "
913 )
914 .to_vec(),
915 Some(vec!["\"<rpc url>\"".to_string()]),
916 ),
917 (
918 "createFork(string)",
920 hex!(
921 "
922 31ba3498
923 0000000000000000000000000000000000000000000000000000000000000020
924 0000000000000000000000000000000000000000000000000000000000000007
925 6d61696e6e657400000000000000000000000000000000000000000000000000
926 "
927 )
928 .to_vec(),
929 Some(vec!["\"mainnet\"".to_string()]),
930 ),
931 (
932 "createFork(string,uint256)",
934 hex!(
935 "
936 6ba3ba2b
937 0000000000000000000000000000000000000000000000000000000000000040
938 0000000000000000000000000000000000000000000000000000000000000001
939 000000000000000000000000000000000000000000000000000000000000002c
940 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
941 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
942 "
943 )
944 .to_vec(),
945 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
946 ),
947 (
948 "createFork(string,uint256)",
950 hex!(
951 "
952 6ba3ba2b
953 0000000000000000000000000000000000000000000000000000000000000040
954 0000000000000000000000000000000000000000000000000000000000000001
955 0000000000000000000000000000000000000000000000000000000000000007
956 6d61696e6e657400000000000000000000000000000000000000000000000000
957 "
958 )
959 .to_vec(),
960 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
961 ),
962 (
963 "createFork(string,bytes32)",
965 hex!(
966 "
967 7ca29682
968 0000000000000000000000000000000000000000000000000000000000000040
969 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
970 000000000000000000000000000000000000000000000000000000000000002c
971 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
972 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
973 "
974 )
975 .to_vec(),
976 Some(vec![
977 "\"<rpc url>\"".to_string(),
978 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
979 .to_string(),
980 ]),
981 ),
982 (
983 "createFork(string,bytes32)",
986 hex!(
987 "
988 7ca29682
989 0000000000000000000000000000000000000000000000000000000000000040
990 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
991 0000000000000000000000000000000000000000000000000000000000000007
992 6d61696e6e657400000000000000000000000000000000000000000000000000
993 "
994 )
995 .to_vec(),
996 Some(vec![
997 "\"mainnet\"".to_string(),
998 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
999 .to_string(),
1000 ]),
1001 ),
1002 (
1003 "createSelectFork(string)",
1005 hex!(
1006 "
1007 98680034
1008 0000000000000000000000000000000000000000000000000000000000000020
1009 000000000000000000000000000000000000000000000000000000000000002c
1010 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1011 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1012 "
1013 )
1014 .to_vec(),
1015 Some(vec!["\"<rpc url>\"".to_string()]),
1016 ),
1017 (
1018 "createSelectFork(string)",
1020 hex!(
1021 "
1022 98680034
1023 0000000000000000000000000000000000000000000000000000000000000020
1024 0000000000000000000000000000000000000000000000000000000000000007
1025 6d61696e6e657400000000000000000000000000000000000000000000000000
1026 "
1027 )
1028 .to_vec(),
1029 Some(vec!["\"mainnet\"".to_string()]),
1030 ),
1031 (
1032 "createSelectFork(string,uint256)",
1034 hex!(
1035 "
1036 71ee464d
1037 0000000000000000000000000000000000000000000000000000000000000040
1038 0000000000000000000000000000000000000000000000000000000000000001
1039 000000000000000000000000000000000000000000000000000000000000002c
1040 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1041 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1042 "
1043 )
1044 .to_vec(),
1045 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
1046 ),
1047 (
1048 "createSelectFork(string,uint256)",
1050 hex!(
1051 "
1052 71ee464d
1053 0000000000000000000000000000000000000000000000000000000000000040
1054 0000000000000000000000000000000000000000000000000000000000000001
1055 0000000000000000000000000000000000000000000000000000000000000007
1056 6d61696e6e657400000000000000000000000000000000000000000000000000
1057 "
1058 )
1059 .to_vec(),
1060 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
1061 ),
1062 (
1063 "createSelectFork(string,bytes32)",
1065 hex!(
1066 "
1067 84d52b7a
1068 0000000000000000000000000000000000000000000000000000000000000040
1069 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1070 000000000000000000000000000000000000000000000000000000000000002c
1071 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1072 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1073 "
1074 )
1075 .to_vec(),
1076 Some(vec![
1077 "\"<rpc url>\"".to_string(),
1078 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1079 .to_string(),
1080 ]),
1081 ),
1082 (
1083 "createSelectFork(string,bytes32)",
1086 hex!(
1087 "
1088 84d52b7a
1089 0000000000000000000000000000000000000000000000000000000000000040
1090 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1091 0000000000000000000000000000000000000000000000000000000000000007
1092 6d61696e6e657400000000000000000000000000000000000000000000000000
1093 "
1094 )
1095 .to_vec(),
1096 Some(vec![
1097 "\"mainnet\"".to_string(),
1098 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1099 .to_string(),
1100 ]),
1101 ),
1102 (
1103 "rpc(string,string,string)",
1105 hex!(
1106 "
1107 0199a220
1108 0000000000000000000000000000000000000000000000000000000000000060
1109 00000000000000000000000000000000000000000000000000000000000000c0
1110 0000000000000000000000000000000000000000000000000000000000000100
1111 000000000000000000000000000000000000000000000000000000000000002c
1112 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1113 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1114 000000000000000000000000000000000000000000000000000000000000000e
1115 6574685f67657442616c616e6365000000000000000000000000000000000000
1116 0000000000000000000000000000000000000000000000000000000000000034
1117 5b22307835353165373738343737386566386530343865343935646634396632
1118 363134663834613466316463222c22307830225d000000000000000000000000
1119 "
1120 )
1121 .to_vec(),
1122 Some(vec![
1123 "\"<rpc url>\"".to_string(),
1124 "\"eth_getBalance\"".to_string(),
1125 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1126 .to_string(),
1127 ]),
1128 ),
1129 (
1130 "rpc(string,string,string)",
1133 hex!(
1134 "
1135 0199a220
1136 0000000000000000000000000000000000000000000000000000000000000060
1137 00000000000000000000000000000000000000000000000000000000000000a0
1138 00000000000000000000000000000000000000000000000000000000000000e0
1139 0000000000000000000000000000000000000000000000000000000000000007
1140 6d61696e6e657400000000000000000000000000000000000000000000000000
1141 000000000000000000000000000000000000000000000000000000000000000e
1142 6574685f67657442616c616e6365000000000000000000000000000000000000
1143 0000000000000000000000000000000000000000000000000000000000000034
1144 5b22307835353165373738343737386566386530343865343935646634396632
1145 363134663834613466316463222c22307830225d000000000000000000000000
1146 "
1147 )
1148 .to_vec(),
1149 Some(vec![
1150 "\"mainnet\"".to_string(),
1151 "\"eth_getBalance\"".to_string(),
1152 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1153 .to_string(),
1154 ]),
1155 ),
1156 ];
1157
1158 let cheatcode_output_test_cases = vec![
1160 ("createWallet(string)", Some("<pk>".to_string())),
1162 ("deriveKey(string,uint32)", Some("<pk>".to_string())),
1163 ("rpcUrl(string)", Some("<rpc url>".to_string())),
1165 ("rpcUrls()", Some("<rpc url>".to_string())),
1166 ("rpcUrlStructs()", Some("<rpc url>".to_string())),
1167 ];
1168
1169 for (function_signature, data, expected) in cheatcode_input_test_cases {
1170 let function = Function::parse(function_signature).unwrap();
1171 let result = decoder.decode_cheatcode_inputs(&function, &data);
1172 assert_eq!(result, expected, "Input case failed for: {function_signature}");
1173 }
1174
1175 for (function_signature, expected) in cheatcode_output_test_cases {
1176 let function = Function::parse(function_signature).unwrap();
1177 let result = Some(decoder.decode_cheatcode_outputs(&function).unwrap_or_default());
1178 assert_eq!(result, expected, "Output case failed for: {function_signature}");
1179 }
1180 }
1181}