1use crate::ast;
48use crate::ast::abi::FunctionType;
49use crate::ast::huff::{self};
50use serde::{Deserialize, Serialize};
51use std::{collections::BTreeMap, fmt};
52
53#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
57#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
58#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
59pub struct Abi {
60 pub constructor: Option<Constructor>,
62 pub functions: BTreeMap<String, Function>,
64 pub events: BTreeMap<String, Event>,
66 pub errors: BTreeMap<String, Error>,
68 pub receive: bool,
70 pub fallback: bool,
72}
73
74impl Abi {
75 pub fn new() -> Self {
77 Self::default()
78 }
79}
80
81impl From<huff::Contract> for Abi {
83 fn from(contract: huff::Contract) -> Self {
84 let constructor = contract
87 .functions
88 .iter()
89 .find(|function| function.name.eq_ignore_ascii_case("constructor"))
90 .map(|func| Constructor {
91 inputs: func
92 .inputs
93 .iter()
94 .map(|argument| FunctionParam {
95 name: argument.name.clone().unwrap_or_default(),
96 kind: argument.arg_type.clone().unwrap_or_default().into(),
97 internal_type: None,
98 })
99 .collect(),
100 })
101 .or_else(|| contract.macros.contains_key("CONSTRUCTOR").then(|| Constructor { inputs: vec![] }));
102
103 let mut functions = BTreeMap::new();
105 let mut events = BTreeMap::new();
106 let mut errors = BTreeMap::new();
107
108 functions.extend(
111 contract
112 .functions
113 .iter()
114 .filter(|function: &&ast::abi::FunctionDefinition| !function.name.eq_ignore_ascii_case("constructor"))
115 .map(|function| {
116 (
117 function.name.to_string(),
118 Function {
119 name: function.name.to_string(),
120 inputs: function
121 .inputs
122 .iter()
123 .map(|argument| FunctionParam {
124 name: argument.name.clone().unwrap_or_default(),
125 kind: argument.arg_type.clone().unwrap_or_default().into(),
126 internal_type: None,
127 })
128 .collect(),
129 outputs: function
130 .outputs
131 .iter()
132 .map(|argument| FunctionParam {
133 name: argument.name.clone().unwrap_or_default(),
134 kind: argument.arg_type.clone().unwrap_or_default().into(),
135 internal_type: None,
136 })
137 .collect(),
138 constant: false,
139 state_mutability: function.fn_type.clone(),
140 },
141 )
142 }),
143 );
144
145 events.extend(contract.events.iter().map(|event| {
147 (
148 event.name.to_string(),
149 Event {
150 name: event.name.to_string(),
151 inputs: event
152 .parameters
153 .iter()
154 .map(|argument| EventParam {
155 name: argument.name.clone().unwrap_or_default(),
156 kind: argument.arg_type.clone().unwrap_or_default().into(),
157 indexed: argument.indexed,
158 })
159 .collect(),
160 anonymous: false,
161 },
162 )
163 }));
164
165 errors.extend(contract.errors.iter().map(|error| {
167 (
168 error.name.to_string(),
169 Error {
170 name: error.name.to_string(),
171 inputs: error
172 .parameters
173 .iter()
174 .map(|argument| FunctionParam {
175 name: argument.name.clone().unwrap_or_default(),
176 kind: argument.arg_type.clone().unwrap_or_default().into(),
177 internal_type: None,
178 })
179 .collect(),
180 },
181 )
182 }));
183
184 Self { constructor, functions, events, errors, receive: false, fallback: false }
185 }
186}
187
188#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
192#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
193#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
194pub struct Function {
195 pub name: String,
197 pub inputs: Vec<FunctionParam>,
199 pub outputs: Vec<FunctionParam>,
201 pub constant: bool,
203 pub state_mutability: FunctionType,
205}
206
207#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
211#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
212#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
213pub struct Event {
214 pub name: String,
216 pub inputs: Vec<EventParam>,
218 pub anonymous: bool,
220}
221
222#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
226#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
227#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
228pub struct EventParam {
229 pub name: String,
231 pub kind: FunctionParamType,
233 pub indexed: bool,
235}
236
237#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
241#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
242#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
243pub struct Error {
244 pub name: String,
246 pub inputs: Vec<FunctionParam>,
248}
249
250#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
254#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
255#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
256pub struct Constructor {
257 pub inputs: Vec<FunctionParam>,
259}
260
261#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
265#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
266#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
267pub struct FunctionParam {
268 pub name: String,
270 pub kind: FunctionParamType,
272 pub internal_type: Option<String>,
274}
275
276#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
280#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
281#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
282pub enum FunctionParamType {
283 Address,
285 Bytes,
287 Int(usize),
289 Uint(usize),
291 Bool,
293 String,
295 Array(Box<FunctionParamType>, Vec<usize>),
297 FixedBytes(usize),
299 Tuple(Vec<FunctionParamType>),
301}
302
303impl FunctionParamType {
304 pub fn is_memory_type(&self) -> bool {
307 matches!(self, FunctionParamType::Bytes | FunctionParamType::String | FunctionParamType::Tuple(_) | FunctionParamType::Array(_, _))
308 }
309}
310
311impl fmt::Debug for FunctionParamType {
312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313 self.display(f)
314 }
315}
316
317impl fmt::Display for FunctionParamType {
318 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319 self.display(f)
320 }
321}
322
323impl FunctionParamType {
324 pub fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
326 match &self {
327 FunctionParamType::Address => write!(f, "address"),
328 FunctionParamType::Bytes => write!(f, "bytes"),
329 FunctionParamType::Int(size) => write!(f, "int{size}"),
330 FunctionParamType::Uint(size) => write!(f, "uint{size}"),
331 FunctionParamType::Bool => write!(f, "bool"),
332 FunctionParamType::String => write!(f, "string"),
333 FunctionParamType::Array(fpt, sizes) => write!(
334 f,
335 "{}{}",
336 fpt,
337 sizes.iter().map(|s| { if !s.eq(&0) { format!("[{s}]") } else { "[]".to_string() } }).collect::<Vec<_>>().join("")
338 ),
339 FunctionParamType::FixedBytes(size) => write!(f, "bytes{size}"),
340 FunctionParamType::Tuple(inner) => write!(f, "({})", inner.iter().map(|fpt| fpt.to_string()).collect::<Vec<_>>().join(",")),
341 }
342 }
343
344 pub fn convert_string_to_type(string: &str) -> Result<Self, Box<dyn std::error::Error>> {
346 let input = string.trim().to_lowercase();
347 if input.ends_with(']') {
349 let mut base = input.as_str();
350 let mut sizes = Vec::new();
351 while let Some(rest) = base.strip_suffix(']') {
352 let open = rest.rfind('[').ok_or_else(|| format!("Failed to create FunctionParamType from string: {string}"))?;
353 let size = &rest[open + 1..];
354 sizes.push(if size.is_empty() { 0 } else { size.parse::<usize>()? });
355 base = &rest[..open];
356 }
357 sizes.reverse();
358 return Ok(Self::Array(Box::new(FunctionParamType::convert_string_to_type(base)?), sizes));
359 }
360 if let Some(inner) = input.strip_prefix('(').and_then(|rest| rest.strip_suffix(')')) {
361 return Ok(Self::Tuple(split_tuple_components(inner).into_iter().map(Self::convert_string_to_type).collect::<Result<_, _>>()?));
362 }
363 if input.starts_with("uint") {
364 let size = match input.get(4..input.len()) {
366 Some(s) => match s.is_empty() {
367 false => s.parse::<usize>().unwrap(),
368 true => 256,
369 },
370 None => 256,
371 };
372 return Ok(Self::Uint(size));
373 }
374 if input.starts_with("int") {
375 let size = match input.get(3..input.len()) {
377 Some(s) => match s.is_empty() {
378 false => s.parse::<usize>().unwrap(),
379 true => 256,
380 },
381 None => 256,
382 };
383 return Ok(Self::Int(size));
384 }
385 if input.starts_with("bytes") && input.len() != 5 {
386 let size = input.get(5..input.len()).unwrap().parse::<usize>().unwrap();
387 return Ok(Self::FixedBytes(size));
388 }
389 if input.starts_with("bool") {
390 return Ok(Self::Bool);
391 }
392 if input.starts_with("address") {
393 return Ok(Self::Address);
394 }
395 if input.starts_with("string") {
396 return Ok(Self::String);
397 }
398 if input == "bytes" {
399 Ok(Self::Bytes)
400 } else {
401 tracing::error!("Failed to create FunctionParamType from string: {}", string);
402 Err(format!("Failed to create FunctionParamType from string: {string}"))?
403 }
404 }
405}
406
407fn split_tuple_components(inner: &str) -> Vec<&str> {
409 let mut components = Vec::new();
410 let (mut depth, mut start) = (0usize, 0);
411 for (i, c) in inner.char_indices() {
412 match c {
413 '(' => depth += 1,
414 ')' => depth = depth.saturating_sub(1),
415 ',' if depth == 0 => {
416 components.push(&inner[start..i]);
417 start = i + 1;
418 }
419 _ => {}
420 }
421 }
422 components.push(&inner[start..]);
423 components
424}
425
426impl From<&str> for FunctionParamType {
427 fn from(string: &str) -> Self {
428 FunctionParamType::convert_string_to_type(string).unwrap()
429 }
430}
431
432impl From<String> for FunctionParamType {
433 fn from(string: String) -> Self {
434 FunctionParamType::convert_string_to_type(&string).unwrap()
435 }
436}