1use crate::{
2 debug::DebugTraceIdentifier,
3 identifier::{IdentifiedAddress, LocalTraceIdentifier, SignaturesIdentifier, TraceIdentifier},
4 CallTrace, CallTraceArena, CallTraceNode, DecodedCallData,
5};
6use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt};
7use alloy_json_abi::{Error, Event, Function, JsonAbi};
8use alloy_primitives::{
9 map::{hash_map::Entry, HashMap, HashSet},
10 Address, LogData, Selector, B256,
11};
12use foundry_common::{
13 abi::get_indexed_event, fmt::format_token, get_contract_name, selectors::SelectorKind,
14 ContractsByArtifact, SELECTOR_LEN,
15};
16use foundry_evm_core::{
17 abi::{console, Vm},
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_debug_identifier(mut self, identifier: DebugTraceIdentifier) -> Self {
95 self.decoder.debug_identifier = Some(identifier);
96 self
97 }
98
99 #[inline]
101 pub fn build(self) -> CallTraceDecoder {
102 self.decoder
103 }
104}
105
106#[derive(Clone, Debug, Default)]
114pub struct CallTraceDecoder {
115 pub contracts: HashMap<Address, String>,
119 pub labels: HashMap<Address, String>,
121 pub receive_contracts: HashSet<Address>,
123 pub fallback_contracts: HashMap<Address, HashSet<Selector>>,
126
127 pub functions: HashMap<Selector, Vec<Function>>,
129 pub events: BTreeMap<(B256, usize), Vec<Event>>,
133 pub revert_decoder: RevertDecoder,
135
136 pub signature_identifier: Option<SignaturesIdentifier>,
138 pub verbosity: u8,
140
141 pub debug_identifier: Option<DebugTraceIdentifier>,
143}
144
145impl CallTraceDecoder {
146 pub fn new() -> &'static Self {
151 static INIT: OnceLock<CallTraceDecoder> = OnceLock::new();
154 INIT.get_or_init(Self::init)
155 }
156
157 fn init() -> Self {
158 Self {
159 contracts: Default::default(),
160 labels: HashMap::from_iter([
161 (CHEATCODE_ADDRESS, "VM".to_string()),
162 (HARDHAT_CONSOLE_ADDRESS, "console".to_string()),
163 (DEFAULT_CREATE2_DEPLOYER, "Create2Deployer".to_string()),
164 (CALLER, "DefaultSender".to_string()),
165 (TEST_CONTRACT_ADDRESS, "DefaultTestContract".to_string()),
166 (EC_RECOVER, "ECRecover".to_string()),
167 (SHA_256, "SHA-256".to_string()),
168 (RIPEMD_160, "RIPEMD-160".to_string()),
169 (IDENTITY, "Identity".to_string()),
170 (MOD_EXP, "ModExp".to_string()),
171 (EC_ADD, "ECAdd".to_string()),
172 (EC_MUL, "ECMul".to_string()),
173 (EC_PAIRING, "ECPairing".to_string()),
174 (BLAKE_2F, "Blake2F".to_string()),
175 (POINT_EVALUATION, "PointEvaluation".to_string()),
176 ]),
177 receive_contracts: Default::default(),
178 fallback_contracts: Default::default(),
179
180 functions: console::hh::abi::functions()
181 .into_values()
182 .chain(Vm::abi::functions().into_values())
183 .flatten()
184 .map(|func| (func.selector(), vec![func]))
185 .collect(),
186 events: console::ds::abi::events()
187 .into_values()
188 .flatten()
189 .map(|event| ((event.selector(), indexed_inputs(&event)), vec![event]))
190 .collect(),
191 revert_decoder: Default::default(),
192
193 signature_identifier: None,
194 verbosity: 0,
195
196 debug_identifier: None,
197 }
198 }
199
200 pub fn clear_addresses(&mut self) {
202 self.contracts.clear();
203
204 let default_labels = &Self::new().labels;
205 if self.labels.len() > default_labels.len() {
206 self.labels.clone_from(default_labels);
207 }
208
209 self.receive_contracts.clear();
210 self.fallback_contracts.clear();
211 }
212
213 pub fn identify(&mut self, arena: &CallTraceArena, identifier: &mut impl TraceIdentifier) {
217 self.collect_identified_addresses(self.identify_addresses(arena, identifier));
218 }
219
220 pub fn identify_addresses<'a>(
224 &self,
225 arena: &CallTraceArena,
226 identifier: &'a mut impl TraceIdentifier,
227 ) -> Vec<IdentifiedAddress<'a>> {
228 let nodes = arena.nodes().iter().filter(|node| {
229 let address = &node.trace.address;
230 !self.labels.contains_key(address) || !self.contracts.contains_key(address)
231 });
232 identifier.identify_addresses(&nodes.collect::<Vec<_>>())
233 }
234
235 pub fn push_event(&mut self, event: Event) {
237 self.events.entry((event.selector(), indexed_inputs(&event))).or_default().push(event);
238 }
239
240 pub fn push_function(&mut self, function: Function) {
242 match self.functions.entry(function.selector()) {
243 Entry::Occupied(entry) => {
244 if entry.get().contains(&function) {
246 return;
247 }
248 trace!(target: "evm::traces", selector=%entry.key(), new=%function.signature(), "duplicate function selector");
249 entry.into_mut().push(function);
250 }
251 Entry::Vacant(entry) => {
252 entry.insert(vec![function]);
253 }
254 }
255 }
256
257 pub fn push_error(&mut self, error: Error) {
259 self.revert_decoder.push_error(error);
260 }
261
262 fn collect_identified_addresses(&mut self, mut addrs: Vec<IdentifiedAddress<'_>>) {
263 addrs.sort_by_key(|identity| identity.address);
264 addrs.dedup_by_key(|identity| identity.address);
265 if addrs.is_empty() {
266 return;
267 }
268
269 trace!(target: "evm::traces", len=addrs.len(), "collecting address identities");
270 for IdentifiedAddress { address, label, contract, abi, artifact_id: _ } in addrs {
271 let _span = trace_span!(target: "evm::traces", "identity", ?contract, ?label).entered();
272
273 if let Some(contract) = contract {
274 self.contracts.entry(address).or_insert(contract);
275 }
276
277 if let Some(label) = label {
278 self.labels.entry(address).or_insert(label);
279 }
280
281 if let Some(abi) = abi {
282 self.collect_abi(&abi, Some(address));
283 }
284 }
285 }
286
287 fn collect_abi(&mut self, abi: &JsonAbi, address: Option<Address>) {
288 let len = abi.len();
289 if len == 0 {
290 return;
291 }
292 trace!(target: "evm::traces", len, ?address, "collecting ABI");
293 for function in abi.functions() {
294 self.push_function(function.clone());
295 }
296 for event in abi.events() {
297 self.push_event(event.clone());
298 }
299 for error in abi.errors() {
300 self.push_error(error.clone());
301 }
302 if let Some(address) = address {
303 if abi.receive.is_some() {
304 self.receive_contracts.insert(address);
305 }
306
307 if abi.fallback.is_some() {
308 self.fallback_contracts
309 .insert(address, abi.functions().map(|f| f.selector()).collect());
310 }
311 }
312 }
313
314 pub async fn populate_traces(&self, traces: &mut Vec<CallTraceNode>) {
318 for node in traces {
319 node.trace.decoded = self.decode_function(&node.trace).await;
320 for log in &mut node.logs {
321 log.decoded = self.decode_event(&log.raw_log).await;
322 }
323
324 if let Some(debug) = self.debug_identifier.as_ref() {
325 if let Some(identified) = self.contracts.get(&node.trace.address) {
326 debug.identify_node_steps(node, get_contract_name(identified))
327 }
328 }
329 }
330 }
331
332 pub async fn decode_function(&self, trace: &CallTrace) -> DecodedCallTrace {
334 let label = self.labels.get(&trace.address).cloned();
335
336 if trace.kind.is_any_create() {
337 return DecodedCallTrace { label, ..Default::default() };
338 }
339
340 if let Some(trace) = precompiles::decode(trace, 1) {
341 return trace;
342 }
343
344 let cdata = &trace.data;
345 if trace.address == DEFAULT_CREATE2_DEPLOYER {
346 return DecodedCallTrace {
347 label,
348 call_data: Some(DecodedCallData { signature: "create2".to_string(), args: vec![] }),
349 return_data: self.default_return_data(trace),
350 };
351 }
352
353 if is_abi_call_data(cdata) {
354 let selector = Selector::try_from(&cdata[..SELECTOR_LEN]).unwrap();
355 let mut functions = Vec::new();
356 let functions = match self.functions.get(&selector) {
357 Some(fs) => fs,
358 None => {
359 if let Some(identifier) = &self.signature_identifier {
360 if let Some(function) = identifier.identify_function(selector).await {
361 functions.push(function);
362 }
363 }
364 &functions
365 }
366 };
367 let [func, ..] = &functions[..] else {
368 return DecodedCallTrace {
369 label,
370 call_data: self.fallback_call_data(trace),
371 return_data: self.default_return_data(trace),
372 };
373 };
374
375 let mut call_data = self.decode_function_input(trace, func);
378 if let Some(fallback_functions) = self.fallback_contracts.get(&trace.address) {
379 if !fallback_functions.contains(&selector) {
380 if let Some(cd) = self.fallback_call_data(trace) {
381 call_data.signature = cd.signature;
382 }
383 }
384 }
385
386 DecodedCallTrace {
387 label,
388 call_data: Some(call_data),
389 return_data: self.decode_function_output(trace, functions),
390 }
391 } else {
392 DecodedCallTrace {
393 label,
394 call_data: self.fallback_call_data(trace),
395 return_data: self.default_return_data(trace),
396 }
397 }
398 }
399
400 fn decode_function_input(&self, trace: &CallTrace, func: &Function) -> DecodedCallData {
402 let mut args = None;
403 if trace.data.len() >= SELECTOR_LEN {
404 if trace.address == CHEATCODE_ADDRESS {
405 if let Some(v) = self.decode_cheatcode_inputs(func, &trace.data) {
407 args = Some(v);
408 }
409 }
410
411 if args.is_none() {
412 if let Ok(v) = func.abi_decode_input(&trace.data[SELECTOR_LEN..], false) {
413 args = Some(v.iter().map(|value| self.format_value(value)).collect());
414 }
415 }
416 }
417
418 DecodedCallData { signature: func.signature(), args: args.unwrap_or_default() }
419 }
420
421 fn decode_cheatcode_inputs(&self, func: &Function, data: &[u8]) -> Option<Vec<String>> {
423 match func.name.as_str() {
424 "expectRevert" => Some(vec![self.revert_decoder.decode(data, None)]),
425 "addr" | "createWallet" | "deriveKey" | "rememberKey" => {
426 Some(vec!["<pk>".to_string()])
428 }
429 "broadcast" | "startBroadcast" => {
430 if !func.inputs.is_empty() && func.inputs[0].ty == "uint256" {
433 Some(vec!["<pk>".to_string()])
434 } else {
435 None
436 }
437 }
438 "getNonce" => {
439 if !func.inputs.is_empty() && func.inputs[0].ty == "tuple" {
442 Some(vec!["<pk>".to_string()])
443 } else {
444 None
445 }
446 }
447 "sign" | "signP256" => {
448 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?;
449
450 if !decoded.is_empty() &&
453 (func.inputs[0].ty == "uint256" || func.inputs[0].ty == "tuple")
454 {
455 decoded[0] = DynSolValue::String("<pk>".to_string());
456 }
457
458 Some(decoded.iter().map(format_token).collect())
459 }
460 "signDelegation" | "signAndAttachDelegation" => {
461 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?;
462 decoded[1] = DynSolValue::String("<pk>".to_string());
466 Some(decoded.iter().map(format_token).collect())
467 }
468 "parseJson" |
469 "parseJsonUint" |
470 "parseJsonUintArray" |
471 "parseJsonInt" |
472 "parseJsonIntArray" |
473 "parseJsonString" |
474 "parseJsonStringArray" |
475 "parseJsonAddress" |
476 "parseJsonAddressArray" |
477 "parseJsonBool" |
478 "parseJsonBoolArray" |
479 "parseJsonBytes" |
480 "parseJsonBytesArray" |
481 "parseJsonBytes32" |
482 "parseJsonBytes32Array" |
483 "writeJson" |
484 "keyExists" |
486 "keyExistsJson" |
487 "serializeBool" |
488 "serializeUint" |
489 "serializeUintToHex" |
490 "serializeInt" |
491 "serializeAddress" |
492 "serializeBytes32" |
493 "serializeString" |
494 "serializeBytes" => {
495 if self.verbosity >= 5 {
496 None
497 } else {
498 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?;
499 let token = if func.name.as_str() == "parseJson" ||
500 func.name.as_str() == "keyExists" ||
502 func.name.as_str() == "keyExistsJson"
503 {
504 "<JSON file>"
505 } else {
506 "<stringified JSON>"
507 };
508 decoded[0] = DynSolValue::String(token.to_string());
509 Some(decoded.iter().map(format_token).collect())
510 }
511 }
512 s if s.contains("Toml") => {
513 if self.verbosity >= 5 {
514 None
515 } else {
516 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?;
517 let token = if func.name.as_str() == "parseToml" ||
518 func.name.as_str() == "keyExistsToml"
519 {
520 "<TOML file>"
521 } else {
522 "<stringified TOML>"
523 };
524 decoded[0] = DynSolValue::String(token.to_string());
525 Some(decoded.iter().map(format_token).collect())
526 }
527 }
528 "createFork" |
529 "createSelectFork" |
530 "rpc" => {
531 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?;
532
533 if !decoded.is_empty() && func.inputs[0].ty == "string" {
535 let url_or_alias = decoded[0].as_str().unwrap_or_default();
536
537 if url_or_alias.starts_with("http") || url_or_alias.starts_with("ws") {
538 decoded[0] = DynSolValue::String("<rpc url>".to_string());
539 }
540 } else {
541 return None;
542 }
543
544 Some(decoded.iter().map(format_token).collect())
545 }
546 _ => None,
547 }
548 }
549
550 fn decode_function_output(&self, trace: &CallTrace, funcs: &[Function]) -> Option<String> {
552 if !trace.success {
553 return self.default_return_data(trace);
554 }
555
556 if trace.address == CHEATCODE_ADDRESS {
557 if let Some(decoded) = funcs.iter().find_map(|func| self.decode_cheatcode_outputs(func))
558 {
559 return Some(decoded);
560 }
561 }
562
563 if let Some(values) =
564 funcs.iter().find_map(|func| func.abi_decode_output(&trace.output, false).ok())
565 {
566 if values.is_empty() {
569 return None;
570 }
571
572 return Some(
573 values.iter().map(|value| self.format_value(value)).format(", ").to_string(),
574 );
575 }
576
577 None
578 }
579
580 fn decode_cheatcode_outputs(&self, func: &Function) -> Option<String> {
582 match func.name.as_str() {
583 s if s.starts_with("env") => Some("<env var value>"),
584 "createWallet" | "deriveKey" => Some("<pk>"),
585 "promptSecret" | "promptSecretUint" => Some("<secret>"),
586 "parseJson" if self.verbosity < 5 => Some("<encoded JSON value>"),
587 "readFile" if self.verbosity < 5 => Some("<file>"),
588 "rpcUrl" | "rpcUrls" | "rpcUrlStructs" => Some("<rpc url>"),
589 _ => None,
590 }
591 .map(Into::into)
592 }
593
594 #[track_caller]
595 fn fallback_call_data(&self, trace: &CallTrace) -> Option<DecodedCallData> {
596 let cdata = &trace.data;
597 let signature = if cdata.is_empty() && self.receive_contracts.contains(&trace.address) {
598 "receive()"
599 } else if self.fallback_contracts.contains_key(&trace.address) {
600 "fallback()"
601 } else {
602 return None;
603 }
604 .to_string();
605 let args = if cdata.is_empty() { Vec::new() } else { vec![cdata.to_string()] };
606 Some(DecodedCallData { signature, args })
607 }
608
609 fn default_return_data(&self, trace: &CallTrace) -> Option<String> {
611 (!trace.success).then(|| self.revert_decoder.decode(&trace.output, Some(trace.status)))
612 }
613
614 pub async fn decode_event(&self, log: &LogData) -> DecodedCallLog {
616 let &[t0, ..] = log.topics() else { return DecodedCallLog { name: None, params: None } };
617
618 let mut events = Vec::new();
619 let events = match self.events.get(&(t0, log.topics().len() - 1)) {
620 Some(es) => es,
621 None => {
622 if let Some(identifier) = &self.signature_identifier {
623 if let Some(event) = identifier.identify_event(t0).await {
624 events.push(get_indexed_event(event, log));
625 }
626 }
627 &events
628 }
629 };
630 for event in events {
631 if let Ok(decoded) = event.decode_log(log, false) {
632 let params = reconstruct_params(event, &decoded);
633 return DecodedCallLog {
634 name: Some(event.name.clone()),
635 params: Some(
636 params
637 .into_iter()
638 .zip(event.inputs.iter())
639 .map(|(param, input)| {
640 let name = input.name.clone();
642 (name, self.format_value(¶m))
643 })
644 .collect(),
645 ),
646 };
647 }
648 }
649
650 DecodedCallLog { name: None, params: None }
651 }
652
653 pub async fn prefetch_signatures(&self, nodes: &[CallTraceNode]) {
655 let Some(identifier) = &self.signature_identifier else { return };
656 let events = nodes
657 .iter()
658 .flat_map(|node| {
659 node.logs
660 .iter()
661 .map(|log| log.raw_log.topics())
662 .filter(|&topics| {
663 if let Some(&first) = topics.first() {
664 if self.events.contains_key(&(first, topics.len() - 1)) {
665 return false;
666 }
667 }
668 true
669 })
670 .filter_map(|topics| topics.first())
671 })
672 .copied();
673 let functions = nodes
674 .iter()
675 .filter(|&n| {
676 if n.trace.address == DEFAULT_CREATE2_DEPLOYER ||
678 n.is_precompile() ||
679 precompiles::is_known_precompile(n.trace.address, 1)
680 {
681 return false;
682 }
683 if n.trace.kind.is_any_create() || !is_abi_call_data(&n.trace.data) {
685 return false;
686 }
687 true
688 })
689 .filter_map(|n| n.trace.data.first_chunk().map(Selector::from))
690 .filter(|selector| !self.functions.contains_key(selector));
691 let selectors = events
692 .map(SelectorKind::Event)
693 .chain(functions.map(SelectorKind::Function))
694 .unique()
695 .collect::<Vec<_>>();
696 let _ = identifier.identify(&selectors).await;
697 }
698
699 fn format_value(&self, value: &DynSolValue) -> String {
701 if let DynSolValue::Address(addr) = value {
702 if let Some(label) = self.labels.get(addr) {
703 return format!("{label}: [{addr}]");
704 }
705 }
706 format_token(value)
707 }
708}
709
710fn is_abi_call_data(data: &[u8]) -> bool {
714 match data.len().cmp(&SELECTOR_LEN) {
715 std::cmp::Ordering::Less => false,
716 std::cmp::Ordering::Equal => true,
717 std::cmp::Ordering::Greater => is_abi_data(&data[SELECTOR_LEN..]),
718 }
719}
720
721fn is_abi_data(data: &[u8]) -> bool {
725 let rem = data.len() % 32;
726 if rem == 0 || data.is_empty() {
727 return true;
728 }
729 data[data.len() - rem..].iter().all(|byte| *byte == 0)
731}
732
733fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue> {
736 let mut indexed = 0;
737 let mut unindexed = 0;
738 let mut inputs = vec![];
739 for input in &event.inputs {
740 if input.indexed && indexed < decoded.indexed.len() {
744 inputs.push(decoded.indexed[indexed].clone());
745 indexed += 1;
746 } else if unindexed < decoded.body.len() {
747 inputs.push(decoded.body[unindexed].clone());
748 unindexed += 1;
749 }
750 }
751
752 inputs
753}
754
755fn indexed_inputs(event: &Event) -> usize {
756 event.inputs.iter().filter(|param| param.indexed).count()
757}
758
759#[cfg(test)]
760mod tests {
761 use super::*;
762 use alloy_primitives::hex;
763
764 #[test]
765 fn test_should_redact() {
766 let decoder = CallTraceDecoder::new();
767
768 let cheatcode_input_test_cases = vec![
770 ("addr(uint256)", vec![], Some(vec!["<pk>".to_string()])),
772 ("createWallet(string)", vec![], Some(vec!["<pk>".to_string()])),
773 ("createWallet(uint256)", vec![], Some(vec!["<pk>".to_string()])),
774 ("deriveKey(string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
775 ("deriveKey(string,string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
776 ("deriveKey(string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
777 ("deriveKey(string,string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
778 ("rememberKey(uint256)", vec![], Some(vec!["<pk>".to_string()])),
779 ("broadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
782 ("broadcast()", vec![], None), ("startBroadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
784 ("startBroadcast()", vec![], None), ("getNonce((address,uint256,uint256,uint256))", vec![], Some(vec!["<pk>".to_string()])),
786 ("getNonce(address)", vec![], None), (
790 "sign(uint256,bytes32)",
791 hex!(
792 "
793 e341eaa4
794 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
795 0000000000000000000000000000000000000000000000000000000000000000
796 "
797 )
798 .to_vec(),
799 Some(vec![
800 "\"<pk>\"".to_string(),
801 "0x0000000000000000000000000000000000000000000000000000000000000000"
802 .to_string(),
803 ]),
804 ),
805 (
806 "signP256(uint256,bytes32)",
807 hex!(
808 "
809 83211b40
810 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
811 0000000000000000000000000000000000000000000000000000000000000000
812 "
813 )
814 .to_vec(),
815 Some(vec![
816 "\"<pk>\"".to_string(),
817 "0x0000000000000000000000000000000000000000000000000000000000000000"
818 .to_string(),
819 ]),
820 ),
821 (
822 "createFork(string)",
824 hex!(
825 "
826 31ba3498
827 0000000000000000000000000000000000000000000000000000000000000020
828 000000000000000000000000000000000000000000000000000000000000002c
829 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
830 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
831 "
832 )
833 .to_vec(),
834 Some(vec!["\"<rpc url>\"".to_string()]),
835 ),
836 (
837 "createFork(string)",
839 hex!(
840 "
841 31ba3498
842 0000000000000000000000000000000000000000000000000000000000000020
843 000000000000000000000000000000000000000000000000000000000000002a
844 7773733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f6d2f
845 76322f6170695f6b657900000000000000000000000000000000000000000000
846 "
847 )
848 .to_vec(),
849 Some(vec!["\"<rpc url>\"".to_string()]),
850 ),
851 (
852 "createFork(string)",
854 hex!(
855 "
856 31ba3498
857 0000000000000000000000000000000000000000000000000000000000000020
858 0000000000000000000000000000000000000000000000000000000000000007
859 6d61696e6e657400000000000000000000000000000000000000000000000000
860 "
861 )
862 .to_vec(),
863 Some(vec!["\"mainnet\"".to_string()]),
864 ),
865 (
866 "createFork(string,uint256)",
868 hex!(
869 "
870 6ba3ba2b
871 0000000000000000000000000000000000000000000000000000000000000040
872 0000000000000000000000000000000000000000000000000000000000000001
873 000000000000000000000000000000000000000000000000000000000000002c
874 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
875 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
876 "
877 )
878 .to_vec(),
879 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
880 ),
881 (
882 "createFork(string,uint256)",
884 hex!(
885 "
886 6ba3ba2b
887 0000000000000000000000000000000000000000000000000000000000000040
888 0000000000000000000000000000000000000000000000000000000000000001
889 0000000000000000000000000000000000000000000000000000000000000007
890 6d61696e6e657400000000000000000000000000000000000000000000000000
891 "
892 )
893 .to_vec(),
894 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
895 ),
896 (
897 "createFork(string,bytes32)",
899 hex!(
900 "
901 7ca29682
902 0000000000000000000000000000000000000000000000000000000000000040
903 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
904 000000000000000000000000000000000000000000000000000000000000002c
905 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
906 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
907 "
908 )
909 .to_vec(),
910 Some(vec![
911 "\"<rpc url>\"".to_string(),
912 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
913 .to_string(),
914 ]),
915 ),
916 (
917 "createFork(string,bytes32)",
920 hex!(
921 "
922 7ca29682
923 0000000000000000000000000000000000000000000000000000000000000040
924 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
925 0000000000000000000000000000000000000000000000000000000000000007
926 6d61696e6e657400000000000000000000000000000000000000000000000000
927 "
928 )
929 .to_vec(),
930 Some(vec![
931 "\"mainnet\"".to_string(),
932 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
933 .to_string(),
934 ]),
935 ),
936 (
937 "createSelectFork(string)",
939 hex!(
940 "
941 98680034
942 0000000000000000000000000000000000000000000000000000000000000020
943 000000000000000000000000000000000000000000000000000000000000002c
944 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
945 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
946 "
947 )
948 .to_vec(),
949 Some(vec!["\"<rpc url>\"".to_string()]),
950 ),
951 (
952 "createSelectFork(string)",
954 hex!(
955 "
956 98680034
957 0000000000000000000000000000000000000000000000000000000000000020
958 0000000000000000000000000000000000000000000000000000000000000007
959 6d61696e6e657400000000000000000000000000000000000000000000000000
960 "
961 )
962 .to_vec(),
963 Some(vec!["\"mainnet\"".to_string()]),
964 ),
965 (
966 "createSelectFork(string,uint256)",
968 hex!(
969 "
970 71ee464d
971 0000000000000000000000000000000000000000000000000000000000000040
972 0000000000000000000000000000000000000000000000000000000000000001
973 000000000000000000000000000000000000000000000000000000000000002c
974 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
975 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
976 "
977 )
978 .to_vec(),
979 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
980 ),
981 (
982 "createSelectFork(string,uint256)",
984 hex!(
985 "
986 71ee464d
987 0000000000000000000000000000000000000000000000000000000000000040
988 0000000000000000000000000000000000000000000000000000000000000001
989 0000000000000000000000000000000000000000000000000000000000000007
990 6d61696e6e657400000000000000000000000000000000000000000000000000
991 "
992 )
993 .to_vec(),
994 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
995 ),
996 (
997 "createSelectFork(string,bytes32)",
999 hex!(
1000 "
1001 84d52b7a
1002 0000000000000000000000000000000000000000000000000000000000000040
1003 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1004 000000000000000000000000000000000000000000000000000000000000002c
1005 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1006 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1007 "
1008 )
1009 .to_vec(),
1010 Some(vec![
1011 "\"<rpc url>\"".to_string(),
1012 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1013 .to_string(),
1014 ]),
1015 ),
1016 (
1017 "createSelectFork(string,bytes32)",
1020 hex!(
1021 "
1022 84d52b7a
1023 0000000000000000000000000000000000000000000000000000000000000040
1024 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1025 0000000000000000000000000000000000000000000000000000000000000007
1026 6d61696e6e657400000000000000000000000000000000000000000000000000
1027 "
1028 )
1029 .to_vec(),
1030 Some(vec![
1031 "\"mainnet\"".to_string(),
1032 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1033 .to_string(),
1034 ]),
1035 ),
1036 (
1037 "rpc(string,string,string)",
1039 hex!(
1040 "
1041 0199a220
1042 0000000000000000000000000000000000000000000000000000000000000060
1043 00000000000000000000000000000000000000000000000000000000000000c0
1044 0000000000000000000000000000000000000000000000000000000000000100
1045 000000000000000000000000000000000000000000000000000000000000002c
1046 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1047 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1048 000000000000000000000000000000000000000000000000000000000000000e
1049 6574685f67657442616c616e6365000000000000000000000000000000000000
1050 0000000000000000000000000000000000000000000000000000000000000034
1051 5b22307835353165373738343737386566386530343865343935646634396632
1052 363134663834613466316463222c22307830225d000000000000000000000000
1053 "
1054 )
1055 .to_vec(),
1056 Some(vec![
1057 "\"<rpc url>\"".to_string(),
1058 "\"eth_getBalance\"".to_string(),
1059 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1060 .to_string(),
1061 ]),
1062 ),
1063 (
1064 "rpc(string,string,string)",
1067 hex!(
1068 "
1069 0199a220
1070 0000000000000000000000000000000000000000000000000000000000000060
1071 00000000000000000000000000000000000000000000000000000000000000a0
1072 00000000000000000000000000000000000000000000000000000000000000e0
1073 0000000000000000000000000000000000000000000000000000000000000007
1074 6d61696e6e657400000000000000000000000000000000000000000000000000
1075 000000000000000000000000000000000000000000000000000000000000000e
1076 6574685f67657442616c616e6365000000000000000000000000000000000000
1077 0000000000000000000000000000000000000000000000000000000000000034
1078 5b22307835353165373738343737386566386530343865343935646634396632
1079 363134663834613466316463222c22307830225d000000000000000000000000
1080 "
1081 )
1082 .to_vec(),
1083 Some(vec![
1084 "\"mainnet\"".to_string(),
1085 "\"eth_getBalance\"".to_string(),
1086 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1087 .to_string(),
1088 ]),
1089 ),
1090 ];
1091
1092 let cheatcode_output_test_cases = vec![
1094 ("createWallet(string)", Some("<pk>".to_string())),
1096 ("deriveKey(string,uint32)", Some("<pk>".to_string())),
1097 ("rpcUrl(string)", Some("<rpc url>".to_string())),
1099 ("rpcUrls()", Some("<rpc url>".to_string())),
1100 ("rpcUrlStructs()", Some("<rpc url>".to_string())),
1101 ];
1102
1103 for (function_signature, data, expected) in cheatcode_input_test_cases {
1104 let function = Function::parse(function_signature).unwrap();
1105 let result = decoder.decode_cheatcode_inputs(&function, &data);
1106 assert_eq!(result, expected, "Input case failed for: {function_signature}");
1107 }
1108
1109 for (function_signature, expected) in cheatcode_output_test_cases {
1110 let function = Function::parse(function_signature).unwrap();
1111 let result = Some(decoder.decode_cheatcode_outputs(&function).unwrap_or_default());
1112 assert_eq!(result, expected, "Output case failed for: {function_signature}");
1113 }
1114 }
1115}