Skip to main content

foundry_evm_traces/decoder/
vm.rs

1//! Cheatcode ABIs for trace decoding.
2//!
3//! `Vm` is declared without `#[sol(abi)]`, which would generate a `Function` literal for each
4//! cheatcode at compile time. The functions are built here from the cheatcode spec instead.
5
6use alloy_json_abi::{Function, Param};
7use foundry_cheatcodes_spec::{Cheatcodes, Enum, Struct};
8
9/// Returns the ABI of every cheatcode, in spec order.
10pub(super) fn functions() -> Vec<Function> {
11    let Cheatcodes { structs, enums, cheatcodes, .. } = Cheatcodes::new();
12    cheatcodes
13        .iter()
14        .map(|cheatcode| {
15            let declaration = cheatcode.func.declaration.trim_end_matches(';');
16            let mut func = Function::parse(declaration)
17                .unwrap_or_else(|e| panic!("invalid cheatcode `{declaration}`: {e}"));
18            for param in func.inputs.iter_mut().chain(&mut func.outputs) {
19                resolve(param, &structs, &enums);
20            }
21            func
22        })
23        .collect()
24}
25
26/// Replaces spec struct and enum types in `param` with their ABI types: structs become tuples
27/// and enums become `uint8`.
28fn resolve(param: &mut Param, structs: &[Struct<'_>], enums: &[Enum<'_>]) {
29    let (root, suffix) = param.ty.split_at(param.ty.find('[').unwrap_or(param.ty.len()));
30    if let Some(strukt) = structs.iter().find(|s| s.name == root) {
31        param.components = strukt
32            .fields
33            .iter()
34            .map(|field| {
35                let mut component = Param::parse(&format!("{} {}", field.ty, field.name))
36                    .unwrap_or_else(|e| {
37                        panic!("invalid field `{}.{}`: {e}", strukt.name, field.name)
38                    });
39                resolve(&mut component, structs, enums);
40                component
41            })
42            .collect();
43        param.ty = format!("tuple{suffix}");
44    } else if enums.iter().any(|e| e.name == root) {
45        param.ty = format!("uint8{suffix}");
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn selectors_match_spec() {
55        let cheatcodes = Cheatcodes::new().cheatcodes;
56        let functions = functions();
57        assert_eq!(functions.len(), cheatcodes.len());
58        for (func, cheatcode) in functions.iter().zip(cheatcodes.iter()) {
59            assert_eq!(func.selector().0, cheatcode.func.selector_bytes, "{}", func.signature());
60        }
61    }
62}