1use super::{types, Context};
4use crate::util;
5use ethers_core::{
6 abi::{
7 struct_def::{FieldDeclaration, FieldType, StructFieldType, StructType},
8 Component, HumanReadableParser, ParamType, RawAbi, SolStruct,
9 },
10 macros::ethers_contract_crate,
11};
12use eyre::{eyre, Result};
13use inflector::Inflector;
14use proc_macro2::TokenStream;
15use quote::quote;
16use std::collections::{HashMap, VecDeque};
17use syn::Ident;
18
19impl Context {
20 pub fn abi_structs(&self) -> Result<TokenStream> {
26 if self.human_readable {
27 self.gen_human_readable_structs()
28 } else {
29 self.gen_internal_structs()
30 }
31 }
32
33 pub fn remove_struct(&mut self, name: &str) {
35 if self.human_readable {
36 self.abi_parser.structs.remove(name);
37 } else {
38 self.internal_structs.structs.remove(name);
39 }
40 }
41
42 pub fn struct_definition(&mut self, name: &str) -> Result<TokenStream> {
44 if self.human_readable {
45 self.generate_human_readable_struct(name)
46 } else {
47 self.generate_internal_struct(name)
48 }
49 }
50
51 fn generate_internal_struct(&self, id: &str) -> Result<TokenStream> {
53 let sol_struct =
54 self.internal_structs.structs.get(id).ok_or_else(|| eyre!("Struct not found"))?;
55 let struct_name = self
56 .internal_structs
57 .rust_type_names
58 .get(id)
59 .ok_or_else(|| eyre!("No types found for {id}"))?;
60 let tuple = self
61 .internal_structs
62 .struct_tuples
63 .get(id)
64 .ok_or_else(|| eyre!("No types found for {id}"))?;
65 let types = if let ParamType::Tuple(types) = tuple { types } else { unreachable!() };
66 self.expand_internal_struct(struct_name, sol_struct, types)
67 }
68
69 fn gen_internal_structs(&self) -> Result<TokenStream> {
71 let mut structs = TokenStream::new();
72 let mut ids: Vec<_> = self.internal_structs.structs.keys().collect();
73 ids.sort();
74
75 for id in ids {
76 structs.extend(self.generate_internal_struct(id)?);
77 }
78 Ok(structs)
79 }
80
81 fn expand_internal_struct(
83 &self,
84 name: &str,
85 sol_struct: &SolStruct,
86 types: &[ParamType],
87 ) -> Result<TokenStream> {
88 let mut fields = Vec::with_capacity(sol_struct.fields().len());
89
90 let is_tuple = sol_struct.has_nameless_field();
92
93 for field in sol_struct.fields() {
94 let ty = match field.r#type() {
95 FieldType::Elementary(ty) => types::expand(ty)?,
96 FieldType::Struct(struct_ty) => types::expand_struct_type(struct_ty),
97 FieldType::Mapping(_) => {
98 eyre::bail!("Mapping types in struct `{name}` are not supported")
99 }
100 };
101
102 let field_name = if is_tuple {
103 TokenStream::new()
104 } else {
105 let field_name = util::safe_ident(&field.name().to_snake_case());
106 quote!(#field_name)
107 };
108 fields.push((field_name, ty));
109 }
110
111 let name = util::ident(name);
112
113 let struct_def = expand_struct(&name, &fields, is_tuple);
114
115 let sig = util::abi_signature_types(types);
116 let doc_str = format!("`{name}({sig})`");
117
118 let mut derives = self.expand_extra_derives();
119 util::derive_builtin_traits_struct(&self.internal_structs, sol_struct, types, &mut derives);
120
121 let ethers_contract = ethers_contract_crate();
122
123 Ok(quote! {
124 #[doc = #doc_str]
125 #[derive(Clone, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
126 pub #struct_def
127 })
128 }
129
130 fn generate_human_readable_struct(&self, name: &str) -> Result<TokenStream> {
131 let sol_struct =
132 self.abi_parser.structs.get(name).ok_or_else(|| eyre!("Struct `{name}` not found"))?;
133 let mut fields = Vec::with_capacity(sol_struct.fields().len());
134 let mut param_types = Vec::with_capacity(sol_struct.fields().len());
135 for field in sol_struct.fields() {
136 let field_name = util::safe_ident(&field.name().to_snake_case());
137 match field.r#type() {
138 FieldType::Elementary(ty) => {
139 param_types.push(ty.clone());
140 let ty = types::expand(ty)?;
141 fields.push(quote! { pub #field_name: #ty });
142 }
143 FieldType::Struct(struct_ty) => {
144 let ty = types::expand_struct_type(struct_ty);
145 fields.push(quote! { pub #field_name: #ty });
146
147 let name = struct_ty.name();
148 let tuple = self
149 .abi_parser
150 .struct_tuples
151 .get(name)
152 .ok_or_else(|| eyre!("No types found for {name}"))?
153 .clone();
154 let tuple = ParamType::Tuple(tuple);
155
156 param_types.push(struct_ty.as_param(tuple));
157 }
158 FieldType::Mapping(_) => {
159 eyre::bail!("Mapping types in struct `{name}` are not supported")
160 }
161 }
162 }
163
164 let abi_signature = util::abi_signature(name, ¶m_types);
165
166 let name = util::ident(name);
167
168 let mut derives = self.expand_extra_derives();
169 util::derive_builtin_traits(¶m_types, &mut derives, true, true);
170
171 let ethers_contract = ethers_contract_crate();
172
173 Ok(quote! {
174 #[doc = #abi_signature]
175 #[derive(Clone, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
176 pub struct #name {
177 #( #fields ),*
178 }
179 })
180 }
181
182 fn gen_human_readable_structs(&self) -> Result<TokenStream> {
184 let mut structs = TokenStream::new();
185 let mut names: Vec<_> = self.abi_parser.structs.keys().collect();
186 names.sort();
187 for name in names {
188 structs.extend(self.generate_human_readable_struct(name)?);
189 }
190 Ok(structs)
191 }
192}
193
194#[derive(Debug, Clone, Default)]
199pub struct InternalStructs {
200 pub(crate) function_params: HashMap<(String, String), String>,
203
204 pub(crate) outputs: HashMap<String, Vec<String>>,
206
207 pub(crate) event_params: HashMap<(String, usize), String>,
212
213 pub(crate) structs: HashMap<String, SolStruct>,
215
216 pub(crate) struct_tuples: HashMap<String, ParamType>,
218
219 pub(crate) rust_type_names: HashMap<String, String>,
221}
222
223impl InternalStructs {
224 pub fn new(abi: RawAbi) -> Self {
226 let mut top_level_internal_types = HashMap::new();
227 let mut function_params = HashMap::new();
228 let mut outputs = HashMap::new();
229 let mut event_params = HashMap::new();
230
231 for item in abi
232 .into_iter()
233 .filter(|item| matches!(item.type_field.as_str(), "constructor" | "function" | "event"))
234 {
235 let is_event = item.type_field == "event";
236
237 let name = match item.name {
238 None if item.type_field == "constructor" => Some("constructor".to_owned()),
239 other => other,
240 };
241
242 if let Some(name) = name {
243 for (idx, input) in item.inputs.into_iter().enumerate() {
244 if let Some(ty) = input
245 .internal_type
246 .as_deref()
247 .filter(|ty| ty.starts_with("struct "))
248 .map(struct_type_identifier)
249 {
250 if is_event {
251 event_params.insert((name.clone(), idx), ty.to_string());
252 } else {
253 function_params
254 .insert((name.clone(), input.name.clone()), ty.to_string());
255 }
256 top_level_internal_types.insert(ty.to_string(), input);
257 }
258 }
259
260 if is_event {
261 continue
263 }
264
265 let mut output_structs = Vec::new();
266 for output in item.outputs {
267 if let Some(ty) = output
268 .internal_type
269 .as_deref()
270 .filter(|ty| ty.starts_with("struct "))
271 .map(struct_type_identifier)
272 {
273 output_structs.push(ty.to_string());
274 top_level_internal_types.insert(ty.to_string(), output);
275 }
276 }
277 outputs.insert(name, output_structs);
278 }
279 }
280
281 let mut structs = HashMap::new();
284 for component in top_level_internal_types.values() {
285 insert_structs(&mut structs, component);
286 }
287
288 let struct_tuples = resolve_struct_tuples(&structs);
290
291 let mut type_names: HashMap<String, (String, Vec<String>)> =
293 HashMap::with_capacity(structs.len());
294 for id in structs.keys() {
295 let name = struct_type_name(id).to_pascal_case();
296 let projections = struct_type_projections(id);
297 insert_rust_type_name(&mut type_names, name, projections, id.clone());
298 }
299
300 Self {
301 function_params,
302 outputs,
303 structs,
304 event_params,
305 struct_tuples,
306 rust_type_names: type_names
307 .into_iter()
308 .map(|(rust_name, (id, _))| (id, rust_name))
309 .collect(),
310 }
311 }
312
313 pub fn get_function_input_struct_type(&self, function: &str, input: &str) -> Option<&str> {
316 self.get_function_input_struct_solidity_id(function, input)
317 .and_then(|id| self.rust_type_names.get(id))
318 .map(String::as_str)
319 }
320
321 pub fn get_function_input_struct_solidity_id(
323 &self,
324 function: &str,
325 input: &str,
326 ) -> Option<&str> {
327 let key = (function.to_string(), input.to_string());
328 self.function_params.get(&key).map(String::as_str)
329 }
330
331 pub fn get_event_input_struct_type(&self, event: &str, idx: usize) -> Option<&str> {
336 self.get_event_input_struct_solidity_id(event, idx)
337 .and_then(|id| self.rust_type_names.get(id))
338 .map(String::as_str)
339 }
340
341 pub fn get_event_input_struct_solidity_id(&self, event: &str, idx: usize) -> Option<&str> {
343 let key = (event.to_string(), idx);
344 self.event_params.get(&key).map(String::as_str)
345 }
346
347 pub fn get_function_output_struct_type(
350 &self,
351 function: &str,
352 internal_type: &str,
353 ) -> Option<&str> {
354 self.get_function_output_struct_solidity_id(function, internal_type)
355 .and_then(|id| self.rust_type_names.get(id))
356 .map(String::as_str)
357 }
358
359 pub fn get_function_output_struct_solidity_id(
362 &self,
363 function: &str,
364 internal_type: &str,
365 ) -> Option<&str> {
366 self.outputs
367 .get(function)
368 .and_then(|outputs| {
369 outputs.iter().find(|s| s.as_str() == struct_type_identifier(internal_type))
370 })
371 .map(String::as_str)
372 }
373
374 pub fn get_struct_type(&self, internal_type: &str) -> Option<&str> {
376 self.rust_type_names.get(struct_type_identifier(internal_type)).map(String::as_str)
377 }
378
379 pub fn rust_type_names(&self) -> &HashMap<String, String> {
381 &self.rust_type_names
382 }
383
384 pub fn structs_types(&self) -> &HashMap<String, SolStruct> {
388 &self.structs
389 }
390}
391
392fn insert_rust_type_name(
396 type_names: &mut HashMap<String, (String, Vec<String>)>,
397 mut name: String,
398 mut projections: Vec<String>,
399 id: String,
400) {
401 if let Some((other_id, mut other_projections)) = type_names.remove(&name) {
402 let mut other_name = name.clone();
403 if !other_projections.is_empty() {
405 other_name = format!("{}{other_name}", other_projections.remove(0).to_pascal_case());
406 }
407 insert_rust_type_name(type_names, other_name, other_projections, other_id);
408
409 if !projections.is_empty() {
410 name = format!("{}{name}", projections.remove(0).to_pascal_case());
411 }
412 insert_rust_type_name(type_names, name, projections, id);
413 } else {
414 type_names.insert(name, (id, projections));
415 }
416}
417
418fn resolve_struct_tuples(all_structs: &HashMap<String, SolStruct>) -> HashMap<String, ParamType> {
425 let mut params = HashMap::new();
426 let mut structs: VecDeque<_> = all_structs.iter().collect();
427
428 let mut sequential_retries = 0;
430 'outer: while let Some((id, ty)) = structs.pop_front() {
431 if sequential_retries > structs.len() {
432 break
433 }
434 if let Some(tuple) = ty.as_tuple() {
435 params.insert(id.to_string(), tuple);
436 } else {
437 let mut struct_params = Vec::with_capacity(ty.fields.len());
439 for field in ty.fields() {
440 match field.ty {
441 FieldType::Elementary(ref param) => {
442 struct_params.push(param.clone());
443 }
444 FieldType::Struct(ref field_ty) => {
445 let ty_id = field_ty.identifier();
447 if let Some(nested) = params.get(&ty_id).cloned() {
448 match field_ty {
449 StructFieldType::Type(_) => struct_params.push(nested),
450 StructFieldType::Array(_) => {
451 struct_params.push(ParamType::Array(Box::new(nested)));
452 }
453 StructFieldType::FixedArray(_, size) => {
454 struct_params
455 .push(ParamType::FixedArray(Box::new(nested), *size));
456 }
457 }
458 } else {
459 structs.push_back((id, ty));
461 sequential_retries += 1;
462 continue 'outer
463 }
464 }
465 _ => {
466 unreachable!("mapping types are unsupported")
467 }
468 }
469 }
470 params.insert(id.to_string(), ParamType::Tuple(struct_params));
471 }
472
473 sequential_retries = 0;
475 }
476 params
477}
478
479fn insert_structs(structs: &mut HashMap<String, SolStruct>, tuple: &Component) {
482 if let Some(internal_ty) = tuple.internal_type.as_ref() {
483 let ident = struct_type_identifier(internal_ty);
484 if structs.contains_key(ident) {
485 return
486 }
487 if let Some(fields) = tuple
488 .components
489 .iter()
490 .map(|f| {
491 HumanReadableParser::parse_type(&f.type_field)
492 .ok()
493 .and_then(|kind| field(structs, f, kind))
494 })
495 .collect::<Option<Vec<_>>>()
496 {
497 let s = SolStruct { name: ident.to_string(), fields };
498 structs.insert(ident.to_string(), s);
499 }
500 }
501}
502
503fn field(
505 structs: &mut HashMap<String, SolStruct>,
506 field_component: &Component,
507 kind: ParamType,
508) -> Option<FieldDeclaration> {
509 match kind {
510 ParamType::Array(ty) => {
511 let FieldDeclaration { ty, .. } = field(structs, field_component, *ty)?;
512 match ty {
513 FieldType::Elementary(kind) => {
514 Some(FieldDeclaration::new(
516 field_component.name.clone(),
517 FieldType::Elementary(ParamType::Array(Box::new(kind))),
518 ))
519 }
520 FieldType::Struct(ty) => Some(FieldDeclaration::new(
521 field_component.name.clone(),
522 FieldType::Struct(StructFieldType::Array(Box::new(ty))),
523 )),
524 _ => {
525 unreachable!("no mappings types support as function inputs or outputs")
526 }
527 }
528 }
529 ParamType::FixedArray(ty, size) => {
530 let FieldDeclaration { ty, .. } = field(structs, field_component, *ty)?;
531 match ty {
532 FieldType::Elementary(kind) => {
533 Some(FieldDeclaration::new(
535 field_component.name.clone(),
536 FieldType::Elementary(ParamType::FixedArray(Box::new(kind), size)),
537 ))
538 }
539 FieldType::Struct(ty) => Some(FieldDeclaration::new(
540 field_component.name.clone(),
541 FieldType::Struct(StructFieldType::FixedArray(Box::new(ty), size)),
542 )),
543 _ => {
544 unreachable!("no mappings types support as function inputs or outputs")
545 }
546 }
547 }
548 ParamType::Tuple(_) => {
549 insert_structs(structs, field_component);
550 let internal_type = field_component.internal_type.as_ref()?;
551 let ty = struct_type_identifier(internal_type);
552 let mut idents = ty.rsplit('.');
555 let name = idents.next().unwrap().to_string();
556 let projections = idents.rev().map(str::to_string).collect();
557 Some(FieldDeclaration::new(
558 field_component.name.clone(),
559 FieldType::Struct(StructFieldType::Type(StructType::new(name, projections))),
560 ))
561 }
562 elementary => Some(FieldDeclaration::new(
563 field_component.name.clone(),
564 FieldType::Elementary(elementary),
565 )),
566 }
567}
568
569fn struct_type_name(name: &str) -> &str {
571 struct_type_identifier(name).rsplit('.').next().unwrap()
572}
573
574fn struct_type_identifier(name: &str) -> &str {
576 name.trim_start_matches("struct ").split('[').next().unwrap()
577}
578
579fn struct_type_projections(name: &str) -> Vec<String> {
581 let id = struct_type_identifier(name);
582 let mut iter = id.rsplit('.');
583 iter.next();
584 iter.rev().map(str::to_string).collect()
585}
586
587pub(crate) fn expand_struct(
588 name: &Ident,
589 fields: &[(TokenStream, TokenStream)],
590 is_tuple: bool,
591) -> TokenStream {
592 _expand_struct(name, fields.iter().map(|(a, b)| (a, b, false)), is_tuple)
593}
594
595pub(crate) fn expand_event_struct(
596 name: &Ident,
597 fields: &[(TokenStream, TokenStream, bool)],
598 is_tuple: bool,
599) -> TokenStream {
600 _expand_struct(name, fields.iter().map(|(a, b, c)| (a, b, *c)), is_tuple)
601}
602
603fn _expand_struct<'a>(
604 name: &Ident,
605 fields: impl Iterator<Item = (&'a TokenStream, &'a TokenStream, bool)>,
606 is_tuple: bool,
607) -> TokenStream {
608 let fields = fields.map(|(field, ty, indexed)| {
609 (field, ty, if indexed { Some(quote!(#[ethevent(indexed)])) } else { None })
610 });
611 let fields = if let Some(0) = fields.size_hint().1 {
612 quote!(;)
614 } else if is_tuple {
615 let fields = fields.map(|(_, ty, indexed)| quote!(#indexed pub #ty));
617 quote!(( #( #fields ),* );)
618 } else {
619 let fields = fields.map(|(field, ty, indexed)| quote!(#indexed pub #field: #ty));
621 quote!({ #( #fields, )* })
622 };
623
624 quote!(struct #name #fields)
625}
626
627#[cfg(test)]
628mod tests {
629 use super::*;
630
631 #[test]
632 fn can_determine_structs() {
633 const VERIFIER_ABI: &str =
634 include_str!("../../../tests/solidity-contracts/verifier_abi.json");
635 let abi = serde_json::from_str::<RawAbi>(VERIFIER_ABI).unwrap();
636
637 let _internal = InternalStructs::new(abi);
638 }
639}