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, Some(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 if let Some(func) = functions.first() {
414 return DecodedCallTrace {
415 label,
416 call_data: Some(self.decode_function_input(trace, func)),
417 return_data,
418 };
419 } else {
420 return 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 (!trace.success).then(|| self.revert_decoder.decode(&trace.output, Some(trace.status)))
671 }
672
673 pub async fn decode_event(&self, log: &LogData) -> DecodedCallLog {
675 let &[t0, ..] = log.topics() else { return DecodedCallLog { name: None, params: None } };
676
677 let mut events = Vec::new();
678 let events = match self.events.get(&(t0, log.topics().len() - 1)) {
679 Some(es) => es,
680 None => {
681 if let Some(identifier) = &self.signature_identifier
682 && let Some(event) = identifier.identify_event(t0).await
683 {
684 events.push(get_indexed_event(event, log));
685 }
686 &events
687 }
688 };
689 for event in events {
690 if let Ok(decoded) = event.decode_log(log) {
691 let params = reconstruct_params(event, &decoded);
692 return DecodedCallLog {
693 name: Some(event.name.clone()),
694 params: Some(
695 params
696 .into_iter()
697 .zip(event.inputs.iter())
698 .map(|(param, input)| {
699 let name = input.name.clone();
701 (name, self.format_value(¶m))
702 })
703 .collect(),
704 ),
705 };
706 }
707 }
708
709 DecodedCallLog { name: None, params: None }
710 }
711
712 pub async fn prefetch_signatures(&self, nodes: &[CallTraceNode]) {
714 let Some(identifier) = &self.signature_identifier else { return };
715 let events = nodes
716 .iter()
717 .flat_map(|node| {
718 node.logs
719 .iter()
720 .map(|log| log.raw_log.topics())
721 .filter(|&topics| {
722 if let Some(&first) = topics.first()
723 && self.events.contains_key(&(first, topics.len() - 1))
724 {
725 return false;
726 }
727 true
728 })
729 .filter_map(|topics| topics.first())
730 })
731 .copied();
732 let functions = nodes
733 .iter()
734 .filter(|&n| {
735 if n.trace.address == DEFAULT_CREATE2_DEPLOYER
737 || n.is_precompile()
738 || precompiles::is_known_precompile(n.trace.address, 1)
739 {
740 return false;
741 }
742 if n.trace.kind.is_any_create() || !is_abi_call_data(&n.trace.data) {
744 return false;
745 }
746 true
747 })
748 .filter_map(|n| n.trace.data.first_chunk().map(Selector::from))
749 .filter(|selector| !self.functions.contains_key(selector));
750 let selectors = events
751 .map(SelectorKind::Event)
752 .chain(functions.map(SelectorKind::Function))
753 .unique()
754 .collect::<Vec<_>>();
755 let _ = identifier.identify(&selectors).await;
756 }
757
758 fn format_value(&self, value: &DynSolValue) -> String {
760 if let DynSolValue::Address(addr) = value
761 && let Some(label) = self.labels.get(addr)
762 {
763 return format!("{label}: [{addr}]");
764 }
765 format_token(value)
766 }
767}
768
769fn is_abi_call_data(data: &[u8]) -> bool {
773 match data.len().cmp(&SELECTOR_LEN) {
774 std::cmp::Ordering::Less => false,
775 std::cmp::Ordering::Equal => true,
776 std::cmp::Ordering::Greater => is_abi_data(&data[SELECTOR_LEN..]),
777 }
778}
779
780fn is_abi_data(data: &[u8]) -> bool {
784 let rem = data.len() % 32;
785 if rem == 0 || data.is_empty() {
786 return true;
787 }
788 data[data.len() - rem..].iter().all(|byte| *byte == 0)
790}
791
792fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue> {
795 let mut indexed = 0;
796 let mut unindexed = 0;
797 let mut inputs = vec![];
798 for input in &event.inputs {
799 if input.indexed && indexed < decoded.indexed.len() {
803 inputs.push(decoded.indexed[indexed].clone());
804 indexed += 1;
805 } else if unindexed < decoded.body.len() {
806 inputs.push(decoded.body[unindexed].clone());
807 unindexed += 1;
808 }
809 }
810
811 inputs
812}
813
814fn indexed_inputs(event: &Event) -> usize {
815 event.inputs.iter().filter(|param| param.indexed).count()
816}
817
818#[cfg(test)]
819mod tests {
820 use super::*;
821 use alloy_primitives::hex;
822
823 #[test]
824 fn test_should_redact() {
825 let decoder = CallTraceDecoder::new();
826
827 let cheatcode_input_test_cases = vec![
829 ("addr(uint256)", vec![], Some(vec!["<pk>".to_string()])),
831 ("createWallet(string)", vec![], Some(vec!["<pk>".to_string()])),
832 ("createWallet(uint256)", vec![], Some(vec!["<pk>".to_string()])),
833 ("deriveKey(string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
834 ("deriveKey(string,string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
835 ("deriveKey(string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
836 ("deriveKey(string,string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
837 ("rememberKey(uint256)", vec![], Some(vec!["<pk>".to_string()])),
838 ("broadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
841 ("broadcast()", vec![], None), ("startBroadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
843 ("startBroadcast()", vec![], None), ("getNonce((address,uint256,uint256,uint256))", vec![], Some(vec!["<pk>".to_string()])),
845 ("getNonce(address)", vec![], None), (
849 "sign(uint256,bytes32)",
850 hex!(
851 "
852 e341eaa4
853 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
854 0000000000000000000000000000000000000000000000000000000000000000
855 "
856 )
857 .to_vec(),
858 Some(vec![
859 "\"<pk>\"".to_string(),
860 "0x0000000000000000000000000000000000000000000000000000000000000000"
861 .to_string(),
862 ]),
863 ),
864 (
865 "signP256(uint256,bytes32)",
866 hex!(
867 "
868 83211b40
869 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
870 0000000000000000000000000000000000000000000000000000000000000000
871 "
872 )
873 .to_vec(),
874 Some(vec![
875 "\"<pk>\"".to_string(),
876 "0x0000000000000000000000000000000000000000000000000000000000000000"
877 .to_string(),
878 ]),
879 ),
880 (
881 "createFork(string)",
883 hex!(
884 "
885 31ba3498
886 0000000000000000000000000000000000000000000000000000000000000020
887 000000000000000000000000000000000000000000000000000000000000002c
888 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
889 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
890 "
891 )
892 .to_vec(),
893 Some(vec!["\"<rpc url>\"".to_string()]),
894 ),
895 (
896 "createFork(string)",
898 hex!(
899 "
900 31ba3498
901 0000000000000000000000000000000000000000000000000000000000000020
902 000000000000000000000000000000000000000000000000000000000000002a
903 7773733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f6d2f
904 76322f6170695f6b657900000000000000000000000000000000000000000000
905 "
906 )
907 .to_vec(),
908 Some(vec!["\"<rpc url>\"".to_string()]),
909 ),
910 (
911 "createFork(string)",
913 hex!(
914 "
915 31ba3498
916 0000000000000000000000000000000000000000000000000000000000000020
917 0000000000000000000000000000000000000000000000000000000000000007
918 6d61696e6e657400000000000000000000000000000000000000000000000000
919 "
920 )
921 .to_vec(),
922 Some(vec!["\"mainnet\"".to_string()]),
923 ),
924 (
925 "createFork(string,uint256)",
927 hex!(
928 "
929 6ba3ba2b
930 0000000000000000000000000000000000000000000000000000000000000040
931 0000000000000000000000000000000000000000000000000000000000000001
932 000000000000000000000000000000000000000000000000000000000000002c
933 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
934 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
935 "
936 )
937 .to_vec(),
938 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
939 ),
940 (
941 "createFork(string,uint256)",
943 hex!(
944 "
945 6ba3ba2b
946 0000000000000000000000000000000000000000000000000000000000000040
947 0000000000000000000000000000000000000000000000000000000000000001
948 0000000000000000000000000000000000000000000000000000000000000007
949 6d61696e6e657400000000000000000000000000000000000000000000000000
950 "
951 )
952 .to_vec(),
953 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
954 ),
955 (
956 "createFork(string,bytes32)",
958 hex!(
959 "
960 7ca29682
961 0000000000000000000000000000000000000000000000000000000000000040
962 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
963 000000000000000000000000000000000000000000000000000000000000002c
964 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
965 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
966 "
967 )
968 .to_vec(),
969 Some(vec![
970 "\"<rpc url>\"".to_string(),
971 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
972 .to_string(),
973 ]),
974 ),
975 (
976 "createFork(string,bytes32)",
979 hex!(
980 "
981 7ca29682
982 0000000000000000000000000000000000000000000000000000000000000040
983 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
984 0000000000000000000000000000000000000000000000000000000000000007
985 6d61696e6e657400000000000000000000000000000000000000000000000000
986 "
987 )
988 .to_vec(),
989 Some(vec![
990 "\"mainnet\"".to_string(),
991 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
992 .to_string(),
993 ]),
994 ),
995 (
996 "createSelectFork(string)",
998 hex!(
999 "
1000 98680034
1001 0000000000000000000000000000000000000000000000000000000000000020
1002 000000000000000000000000000000000000000000000000000000000000002c
1003 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1004 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1005 "
1006 )
1007 .to_vec(),
1008 Some(vec!["\"<rpc url>\"".to_string()]),
1009 ),
1010 (
1011 "createSelectFork(string)",
1013 hex!(
1014 "
1015 98680034
1016 0000000000000000000000000000000000000000000000000000000000000020
1017 0000000000000000000000000000000000000000000000000000000000000007
1018 6d61696e6e657400000000000000000000000000000000000000000000000000
1019 "
1020 )
1021 .to_vec(),
1022 Some(vec!["\"mainnet\"".to_string()]),
1023 ),
1024 (
1025 "createSelectFork(string,uint256)",
1027 hex!(
1028 "
1029 71ee464d
1030 0000000000000000000000000000000000000000000000000000000000000040
1031 0000000000000000000000000000000000000000000000000000000000000001
1032 000000000000000000000000000000000000000000000000000000000000002c
1033 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1034 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1035 "
1036 )
1037 .to_vec(),
1038 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
1039 ),
1040 (
1041 "createSelectFork(string,uint256)",
1043 hex!(
1044 "
1045 71ee464d
1046 0000000000000000000000000000000000000000000000000000000000000040
1047 0000000000000000000000000000000000000000000000000000000000000001
1048 0000000000000000000000000000000000000000000000000000000000000007
1049 6d61696e6e657400000000000000000000000000000000000000000000000000
1050 "
1051 )
1052 .to_vec(),
1053 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
1054 ),
1055 (
1056 "createSelectFork(string,bytes32)",
1058 hex!(
1059 "
1060 84d52b7a
1061 0000000000000000000000000000000000000000000000000000000000000040
1062 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1063 000000000000000000000000000000000000000000000000000000000000002c
1064 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1065 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1066 "
1067 )
1068 .to_vec(),
1069 Some(vec![
1070 "\"<rpc url>\"".to_string(),
1071 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1072 .to_string(),
1073 ]),
1074 ),
1075 (
1076 "createSelectFork(string,bytes32)",
1079 hex!(
1080 "
1081 84d52b7a
1082 0000000000000000000000000000000000000000000000000000000000000040
1083 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1084 0000000000000000000000000000000000000000000000000000000000000007
1085 6d61696e6e657400000000000000000000000000000000000000000000000000
1086 "
1087 )
1088 .to_vec(),
1089 Some(vec![
1090 "\"mainnet\"".to_string(),
1091 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1092 .to_string(),
1093 ]),
1094 ),
1095 (
1096 "rpc(string,string,string)",
1098 hex!(
1099 "
1100 0199a220
1101 0000000000000000000000000000000000000000000000000000000000000060
1102 00000000000000000000000000000000000000000000000000000000000000c0
1103 0000000000000000000000000000000000000000000000000000000000000100
1104 000000000000000000000000000000000000000000000000000000000000002c
1105 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1106 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1107 000000000000000000000000000000000000000000000000000000000000000e
1108 6574685f67657442616c616e6365000000000000000000000000000000000000
1109 0000000000000000000000000000000000000000000000000000000000000034
1110 5b22307835353165373738343737386566386530343865343935646634396632
1111 363134663834613466316463222c22307830225d000000000000000000000000
1112 "
1113 )
1114 .to_vec(),
1115 Some(vec![
1116 "\"<rpc url>\"".to_string(),
1117 "\"eth_getBalance\"".to_string(),
1118 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1119 .to_string(),
1120 ]),
1121 ),
1122 (
1123 "rpc(string,string,string)",
1126 hex!(
1127 "
1128 0199a220
1129 0000000000000000000000000000000000000000000000000000000000000060
1130 00000000000000000000000000000000000000000000000000000000000000a0
1131 00000000000000000000000000000000000000000000000000000000000000e0
1132 0000000000000000000000000000000000000000000000000000000000000007
1133 6d61696e6e657400000000000000000000000000000000000000000000000000
1134 000000000000000000000000000000000000000000000000000000000000000e
1135 6574685f67657442616c616e6365000000000000000000000000000000000000
1136 0000000000000000000000000000000000000000000000000000000000000034
1137 5b22307835353165373738343737386566386530343865343935646634396632
1138 363134663834613466316463222c22307830225d000000000000000000000000
1139 "
1140 )
1141 .to_vec(),
1142 Some(vec![
1143 "\"mainnet\"".to_string(),
1144 "\"eth_getBalance\"".to_string(),
1145 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1146 .to_string(),
1147 ]),
1148 ),
1149 ];
1150
1151 let cheatcode_output_test_cases = vec![
1153 ("createWallet(string)", Some("<pk>".to_string())),
1155 ("deriveKey(string,uint32)", Some("<pk>".to_string())),
1156 ("rpcUrl(string)", Some("<rpc url>".to_string())),
1158 ("rpcUrls()", Some("<rpc url>".to_string())),
1159 ("rpcUrlStructs()", Some("<rpc url>".to_string())),
1160 ];
1161
1162 for (function_signature, data, expected) in cheatcode_input_test_cases {
1163 let function = Function::parse(function_signature).unwrap();
1164 let result = decoder.decode_cheatcode_inputs(&function, &data);
1165 assert_eq!(result, expected, "Input case failed for: {function_signature}");
1166 }
1167
1168 for (function_signature, expected) in cheatcode_output_test_cases {
1169 let function = Function::parse(function_signature).unwrap();
1170 let result = Some(decoder.decode_cheatcode_outputs(&function).unwrap_or_default());
1171 assert_eq!(result, expected, "Output case failed for: {function_signature}");
1172 }
1173 }
1174}