sway_core/abi_generation/
evm_abi.rs

1use sway_types::{integer_bits::IntegerBits, Named};
2
3use crate::{
4    asm_generation::EvmAbiResult,
5    decl_engine::DeclId,
6    language::ty::{TyFunctionDecl, TyProgram, TyProgramKind},
7    Engines, GenericArgument, TypeId, TypeInfo,
8};
9
10pub fn generate_abi_program(program: &TyProgram, engines: &Engines) -> EvmAbiResult {
11    match &program.kind {
12        TyProgramKind::Contract { abi_entries, .. } => abi_entries
13            .iter()
14            .map(|x| generate_abi_function(x, engines))
15            .collect(),
16        TyProgramKind::Script { entry_function, .. }
17        | TyProgramKind::Predicate { entry_function, .. } => {
18            vec![generate_abi_function(entry_function, engines)]
19        }
20        _ => vec![],
21    }
22}
23
24/// Gives back a string that represents the type, considering what it resolves to
25fn get_type_str(type_id: &TypeId, engines: &Engines, resolved_type_id: TypeId) -> String {
26    let type_engine = engines.te();
27    if type_id.is_generic_parameter(engines, resolved_type_id) {
28        format!("generic {}", abi_str(&type_engine.get(*type_id), engines))
29    } else {
30        match (
31            &*type_engine.get(*type_id),
32            &*type_engine.get(resolved_type_id),
33        ) {
34            (TypeInfo::Custom { .. }, TypeInfo::Struct { .. }) => {
35                format!("struct {}", abi_str(&type_engine.get(*type_id), engines))
36            }
37            (TypeInfo::Custom { .. }, TypeInfo::Enum { .. }) => {
38                format!("enum {}", abi_str(&type_engine.get(*type_id), engines))
39            }
40            (TypeInfo::Tuple(fields), TypeInfo::Tuple(resolved_fields)) => {
41                assert_eq!(fields.len(), resolved_fields.len());
42                let field_strs = fields
43                    .iter()
44                    .map(|_| "_".to_string())
45                    .collect::<Vec<String>>();
46                format!("({})", field_strs.join(", "))
47            }
48            (TypeInfo::Array(_, length), TypeInfo::Array(_, resolved_length)) => {
49                assert_eq!(
50                    length.expr().as_literal_val().unwrap(),
51                    resolved_length.expr().as_literal_val().unwrap()
52                );
53                format!("[_; {:?}]", engines.help_out(length.expr()))
54            }
55            (TypeInfo::Slice(_), TypeInfo::Slice(_)) => "__slice[_]".into(),
56            (TypeInfo::Custom { .. }, _) => {
57                format!("generic {}", abi_str(&type_engine.get(*type_id), engines))
58            }
59            _ => abi_str(&type_engine.get(*type_id), engines),
60        }
61    }
62}
63
64pub fn abi_str(type_info: &TypeInfo, engines: &Engines) -> String {
65    use TypeInfo::*;
66    let decl_engine = engines.de();
67    match type_info {
68        Unknown => "unknown".into(),
69        Never => "never".into(),
70        UnknownGeneric { name, .. } => name.to_string(),
71        Placeholder(_) => "_".to_string(),
72        TypeParam(param) => format!("typeparam({})", param.name()),
73        StringSlice => "str".into(),
74        StringArray(length) => format!("str[{:?}]", engines.help_out(length.expr())),
75        UnsignedInteger(x) => match x {
76            IntegerBits::Eight => "uint8",
77            IntegerBits::Sixteen => "uint16",
78            IntegerBits::ThirtyTwo => "uint32",
79            IntegerBits::SixtyFour => "uint64",
80            IntegerBits::V256 => "uint256",
81        }
82        .into(),
83        Boolean => "bool".into(),
84        Custom {
85            qualified_call_path: call_path,
86            ..
87        } => call_path.call_path.suffix.to_string(),
88        Tuple(fields) => {
89            let field_strs = fields
90                .iter()
91                .map(|field| abi_str_type_arg(field, engines))
92                .collect::<Vec<String>>();
93            format!("({})", field_strs.join(", "))
94        }
95        B256 => "uint256".into(),
96        Numeric => "u64".into(), // u64 is the default
97        Contract => "contract".into(),
98        ErrorRecovery(_) => "unknown due to error".into(),
99        UntypedEnum(decl_id) => {
100            let decl = engines.pe().get_enum(decl_id);
101            format!("untyped enum {}", decl.name)
102        }
103        UntypedStruct(decl_id) => {
104            let decl = engines.pe().get_struct(decl_id);
105            format!("untyped struct {}", decl.name)
106        }
107        Enum(decl_ref) => {
108            let decl = decl_engine.get_enum(decl_ref);
109            format!("enum {}", decl.call_path.suffix)
110        }
111        Struct(decl_ref) => {
112            let decl = decl_engine.get_struct(decl_ref);
113            format!("struct {}", decl.call_path.suffix)
114        }
115        ContractCaller { abi_name, .. } => {
116            format!("contract caller {abi_name}")
117        }
118        Array(elem_ty, length) => {
119            format!(
120                "{}[{:?}]",
121                abi_str_type_arg(elem_ty, engines),
122                engines.help_out(length.expr()),
123            )
124        }
125        RawUntypedPtr => "raw untyped ptr".into(),
126        RawUntypedSlice => "raw untyped slice".into(),
127        Ptr(ty) => {
128            format!("__ptr {}", abi_str_type_arg(ty, engines))
129        }
130        Slice(ty) => {
131            format!("__slice {}", abi_str_type_arg(ty, engines))
132        }
133        Alias { ty, .. } => abi_str_type_arg(ty, engines),
134        TraitType {
135            name,
136            implemented_in: _,
137        } => format!("trait type {name}"),
138        Ref {
139            to_mutable_value,
140            referenced_type,
141        } => {
142            format!(
143                "__ref {}{}", // TODO: (REFERENCES) No references in ABIs according to the RFC. Or we want to have them?
144                if *to_mutable_value { "mut " } else { "" },
145                abi_str_type_arg(referenced_type, engines)
146            )
147        }
148    }
149}
150
151pub fn abi_param_type(type_info: &TypeInfo, engines: &Engines) -> ethabi::ParamType {
152    use TypeInfo::*;
153    let type_engine = engines.te();
154    let decl_engine = engines.de();
155    match type_info {
156        StringArray(length) => ethabi::ParamType::FixedArray(
157            Box::new(ethabi::ParamType::String),
158            length.expr().as_literal_val().unwrap(),
159        ),
160        UnsignedInteger(x) => match x {
161            IntegerBits::Eight => ethabi::ParamType::Uint(8),
162            IntegerBits::Sixteen => ethabi::ParamType::Uint(16),
163            IntegerBits::ThirtyTwo => ethabi::ParamType::Uint(32),
164            IntegerBits::SixtyFour => ethabi::ParamType::Uint(64),
165            IntegerBits::V256 => ethabi::ParamType::Uint(256),
166        },
167        Boolean => ethabi::ParamType::Bool,
168        B256 => ethabi::ParamType::Uint(256),
169        Contract => ethabi::ParamType::Address,
170        Enum { .. } => ethabi::ParamType::Uint(8),
171        Tuple(fields) => ethabi::ParamType::Tuple(
172            fields
173                .iter()
174                .map(|f| abi_param_type(&type_engine.get(f.type_id()), engines))
175                .collect::<Vec<ethabi::ParamType>>(),
176        ),
177        Struct(decl_ref) => {
178            let decl = decl_engine.get_struct(decl_ref);
179            ethabi::ParamType::Tuple(
180                decl.fields
181                    .iter()
182                    .map(|f| abi_param_type(&type_engine.get(f.type_argument.type_id()), engines))
183                    .collect::<Vec<ethabi::ParamType>>(),
184            )
185        }
186        Array(elem_ty, ..) => ethabi::ParamType::Array(Box::new(abi_param_type(
187            &type_engine.get(elem_ty.type_id()),
188            engines,
189        ))),
190        _ => panic!("cannot convert type to Solidity ABI param type: {type_info:?}",),
191    }
192}
193
194fn generate_abi_function(
195    fn_decl_id: &DeclId<TyFunctionDecl>,
196    engines: &Engines,
197) -> ethabi::operation::Operation {
198    let decl_engine = engines.de();
199    let fn_decl = decl_engine.get_function(fn_decl_id);
200    // A list of all `ethabi::Param`s needed for inputs
201    let input_types = fn_decl
202        .parameters
203        .iter()
204        .map(|x| ethabi::Param {
205            name: x.name.to_string(),
206            kind: ethabi::ParamType::Address,
207            internal_type: Some(get_type_str(
208                &x.type_argument.type_id(),
209                engines,
210                x.type_argument.type_id(),
211            )),
212        })
213        .collect::<Vec<_>>();
214
215    // The single `ethabi::Param` needed for the output
216    let output_type = ethabi::Param {
217        name: String::default(),
218        kind: ethabi::ParamType::Address,
219        internal_type: Some(get_type_str(
220            &fn_decl.return_type.type_id(),
221            engines,
222            fn_decl.return_type.type_id(),
223        )),
224    };
225
226    // Generate the ABI data for the function
227    #[allow(deprecated)]
228    ethabi::operation::Operation::Function(ethabi::Function {
229        name: fn_decl.name.as_str().to_string(),
230        inputs: input_types,
231        outputs: vec![output_type],
232        constant: None,
233        state_mutability: ethabi::StateMutability::Payable,
234    })
235}
236
237fn abi_str_type_arg(type_arg: &GenericArgument, engines: &Engines) -> String {
238    abi_str(&engines.te().get(type_arg.type_id()), engines)
239}