Skip to main content

huff_neo_utils/
sol_interface.rs

1use crate::abi::FunctionParamType;
2use crate::prelude::Artifact;
3use std::{
4    fs,
5    path::{Path, PathBuf},
6    sync::Arc,
7};
8
9/// Generate solidity interfaces from a vector of artifacts.
10///
11/// @param artifacts The vector of artifacts to generate interfaces from.
12/// @return The vector of generated interfaces.
13pub fn gen_sol_interfaces(artifacts: &Vec<Arc<Artifact>>, interface: Option<String>) -> Vec<(PathBuf, String, String)> {
14    let mut interfaces = Vec::new();
15
16    for artifact in artifacts {
17        if let Some(a) = &artifact.abi {
18            let mut defs = Vec::new();
19            let mut structs = Vec::new();
20            a.events.iter().for_each(|(_, f)| {
21                defs.push(format!(
22                    "{}event {}({});",
23                    "\t",
24                    f.name,
25                    f.inputs
26                        .iter()
27                        .map(|i| {
28                            format!(
29                                "{}{}",
30                                sol_type(&i.kind, &mut structs),
31                                if i.indexed { String::from(" indexed") } else { String::default() }
32                            )
33                        })
34                        .collect::<Vec<_>>()
35                        .join(", "),
36                ));
37            });
38            a.errors.iter().for_each(|(_, e)| {
39                defs.push(format!(
40                    "{}error {}({});",
41                    "\t",
42                    e.name,
43                    e.inputs.iter().map(|i| sol_type(&i.kind, &mut structs)).collect::<Vec<_>>().join(", "),
44                ));
45            });
46            a.functions.iter().for_each(|(_, f)| {
47                defs.push(format!(
48                    "{}function {}({}) external{}{};",
49                    "\t",
50                    f.name,
51                    f.inputs.iter().map(|i| param(&i.kind, &mut structs)).collect::<Vec<_>>().join(", "),
52                    f.state_mutability.interface_mutability(),
53                    if f.outputs.is_empty() {
54                        String::default()
55                    } else {
56                        format!(" returns ({})", f.outputs.iter().map(|o| param(&o.kind, &mut structs)).collect::<Vec<_>>().join(", "))
57                    },
58                ));
59            });
60
61            let interface_name = interface
62                .clone()
63                .unwrap_or_else(|| format!("I{}", artifact.file.path.split('/').next_back().unwrap().split('.').next().unwrap()));
64            let struct_defs = structs.into_iter().map(|(_, def)| def);
65            let formatted_str = format!("interface {interface_name} {{\n{}\n}}", struct_defs.chain(defs).collect::<Vec<_>>().join("\n"));
66            interfaces.push((Path::new(&artifact.file.path).parent().unwrap().to_path_buf(), interface_name, formatted_str));
67        }
68    }
69
70    interfaces
71}
72
73/// A function parameter, with the `memory` location its type needs
74fn param(kind: &FunctionParamType, structs: &mut Vec<(FunctionParamType, String)>) -> String {
75    format!("{}{}", sol_type(kind, structs), if kind.is_memory_type() { " memory" } else { "" })
76}
77
78/// The Solidity name of a type. Solidity has no anonymous tuples, so each distinct tuple type is
79/// declared once as a struct `TupleN` with the fields `_0`, `_1`, ..., which ABI-encodes the same.
80fn sol_type(kind: &FunctionParamType, structs: &mut Vec<(FunctionParamType, String)>) -> String {
81    match kind {
82        FunctionParamType::Tuple(components) => {
83            if let Some(index) = structs.iter().position(|(tuple, _)| tuple == kind) {
84                return format!("Tuple{index}");
85            }
86            // Nested tuples are declared before the struct that contains them
87            let fields: Vec<String> =
88                components.iter().enumerate().map(|(i, component)| format!("\t\t{} _{i};", sol_type(component, structs))).collect();
89            let name = format!("Tuple{}", structs.len());
90            structs.push((kind.clone(), format!("\tstruct {name} {{\n{}\n\t}}", fields.join("\n"))));
91            name
92        }
93        FunctionParamType::Array(inner, sizes) => format!(
94            "{}{}",
95            sol_type(inner, structs),
96            sizes.iter().map(|size| if *size == 0 { "[]".to_string() } else { format!("[{size}]") }).collect::<String>()
97        ),
98        _ => kind.to_string(),
99    }
100}
101
102/// Export generated solidity interfaces to a file.
103///
104/// @param interfaces The vector of generated interfaces.
105/// @return Unit type if success, error if failure.
106pub fn export_interfaces(interfaces: &Vec<(PathBuf, String, String)>) -> Result<(), std::io::Error> {
107    for (path, name, interface) in interfaces {
108        // A bare file name has an empty parent, so the interface lands next to it
109        fs::write(path.join(format!("{name}.sol")), interface)?;
110    }
111    Ok(())
112}