From 4aadc003b1ed69ee3ab251e9749b46f248798121 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 01 Sep 2021 10:55:02 +0000 Subject: [PATCH] feat: support complex types in solidity defs --- --- a/crates/evm-coder-macros/src/solidity_interface.rs +++ b/crates/evm-coder-macros/src/solidity_interface.rs @@ -1,16 +1,16 @@ #![allow(dead_code)] use quote::quote; -use darling::FromMeta; +use darling::{FromMeta, ToTokens}; use inflector::cases; use std::fmt::Write; use syn::{ - FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, - ReturnType, Type, spanned::Spanned, + Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta, + NestedMeta, PatType, Path, PathArguments, ReturnType, Type, spanned::Spanned, }; use crate::{ - fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type, + fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment, parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal, snake_ident_to_screaming, }; @@ -77,14 +77,14 @@ fn expand_generator(&self) -> proc_macro2::TokenStream { let pascal_call_name = &self.pascal_call_name; quote! { - #pascal_call_name::generate_solidity_interface(out_set, is_impl); + #pascal_call_name::generate_solidity_interface(tc, is_impl); } } fn expand_event_generator(&self) -> proc_macro2::TokenStream { let name = &self.name; quote! { - #name::generate_solidity_interface(out_set, is_impl); + #name::generate_solidity_interface(tc, is_impl); } } } @@ -121,10 +121,161 @@ rename_selector: Option, } +enum AbiType { + // type + Plain(Ident), + // (type1,type2) + Tuple(Vec), + // type[] + Vec(Box), + // type[20] + Array(Box, usize), +} +impl AbiType { + fn try_from(value: &Type) -> syn::Result { + let value = Self::try_maybe_special_from(value)?; + if value.is_special() { + return Err(syn::Error::new(value.span(), "unexpected special type")); + } + Ok(value) + } + fn try_maybe_special_from(value: &Type) -> syn::Result { + match value { + Type::Array(arr) => { + let wrapped = AbiType::try_from(&arr.elem)?; + match &arr.len { + Expr::Lit(l) => match &l.lit { + Lit::Int(i) => { + let num = i.base10_parse::()?; + Ok(AbiType::Array(Box::new(wrapped), num as usize)) + } + _ => Err(syn::Error::new(arr.len.span(), "should be int literal")), + }, + _ => Err(syn::Error::new(arr.len.span(), "should be literal")), + } + } + Type::Path(_) => { + let path = parse_path(value)?; + let segment = parse_path_segment(path)?; + if segment.ident == "Vec" { + let args = match &segment.arguments { + PathArguments::AngleBracketed(e) => e, + _ => { + return Err(syn::Error::new( + segment.arguments.span(), + "missing Vec generic", + )) + } + }; + let args = &args.args; + if args.len() != 1 { + return Err(syn::Error::new( + args.span(), + "expected only one generic for vec", + )); + } + let arg = args.first().unwrap(); + + let ty = match arg { + GenericArgument::Type(ty) => ty, + _ => { + return Err(syn::Error::new( + arg.span(), + "expected first generic to be type", + )) + } + }; + + let wrapped = AbiType::try_from(ty)?; + Ok(Self::Vec(Box::new(wrapped))) + } else { + if !segment.arguments.is_empty() { + return Err(syn::Error::new( + segment.arguments.span(), + "unexpected generic arguments for non-vec type", + )); + } + Ok(Self::Plain(segment.ident.clone())) + } + } + Type::Tuple(t) => { + let mut out = Vec::with_capacity(t.elems.len()); + for el in t.elems.iter() { + out.push(AbiType::try_from(el)?) + } + Ok(Self::Tuple(out)) + } + _ => Err(syn::Error::new( + value.span(), + "unexpected type, only arrays, plain types and tuples are supported", + )), + } + } + fn is_value(&self) -> bool { + match self { + Self::Plain(v) if v == "value" => true, + _ => false, + } + } + fn is_caller(&self) -> bool { + match self { + Self::Plain(v) if v == "caller" => true, + _ => false, + } + } + fn is_special(&self) -> bool { + self.is_caller() || self.is_value() + } + fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result { + match self { + AbiType::Plain(t) => { + write!(buf, "{}", t) + } + AbiType::Tuple(t) => { + write!(buf, "(")?; + for (i, t) in t.iter().enumerate() { + if i != 0 { + write!(buf, ",")?; + } + t.selector_ty_buf(buf)?; + } + write!(buf, ")") + } + AbiType::Vec(v) => { + v.selector_ty_buf(buf)?; + write!(buf, "[]") + } + AbiType::Array(v, len) => { + v.selector_ty_buf(buf)?; + write!(buf, "[{}]", len) + } + } + } + fn selector_ty(&self) -> String { + let mut out = String::new(); + self.selector_ty_buf(&mut out).expect("no fmt error"); + out + } +} +impl ToTokens for AbiType { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + AbiType::Plain(t) => tokens.extend(quote! {#t}), + AbiType::Tuple(t) => { + tokens.extend(quote! {( + #(#t),* + )}); + } + AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}), + AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}), + } + } +} + struct MethodArg { name: Ident, camel_name: String, - ty: Ident, + ty: AbiType, } impl MethodArg { fn try_from(value: &PatType) -> syn::Result { @@ -132,21 +283,21 @@ Ok(Self { camel_name: cases::camelcase::to_camel_case(&name.to_string()), name, - ty: parse_ident_from_type(&value.ty, false)?.clone(), + ty: AbiType::try_maybe_special_from(&value.ty)?, }) } fn is_value(&self) -> bool { - self.ty == "value" + self.ty.is_value() } fn is_caller(&self) -> bool { - self.ty == "caller" + self.ty.is_caller() } fn is_special(&self) -> bool { - self.is_value() || self.is_caller() + self.ty.is_special() } - fn selector_ty(&self) -> &Ident { + fn selector_ty(&self) -> String { assert!(!self.is_special()); - &self.ty + self.ty.selector_ty() } fn expand_call_def(&self) -> proc_macro2::TokenStream { @@ -544,7 +695,7 @@ )* ) } - pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet, is_impl: bool) { + pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) { use evm_coder::solidity::*; use core::fmt::Write; let interface = SolidityInterface { @@ -559,9 +710,9 @@ )*), }; if is_impl { - out_set.insert("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into()); + tc.collect("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into()); } else { - out_set.insert("// Common stubs holder\ninterface Dummy {\n}\n".into()); + tc.collect("// Common stubs holder\ninterface Dummy {\n}\n".into()); } #( #solidity_generators @@ -576,8 +727,8 @@ if #solidity_name.starts_with("Inline") { out.push_str("// Inline\n"); } - let _ = interface.format(is_impl, &mut out); - out_set.insert(out); + let _ = interface.format(is_impl, &mut out, tc); + tc.collect(out); } } impl ::evm_coder::Call for #call_name { --- a/crates/evm-coder-macros/src/to_log.rs +++ b/crates/evm-coder-macros/src/to_log.rs @@ -179,7 +179,7 @@ #consts )* - pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet, is_impl: bool) { + pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) { use evm_coder::solidity::*; use core::fmt::Write; let interface = SolidityInterface { @@ -191,8 +191,8 @@ }; let mut out = string::new(); out.push_str("// Inline\n"); - let _ = interface.format(is_impl, &mut out); - out_set.insert(out); + let _ = interface.format(is_impl, &mut out, tc); + tc.collect(out); } } --- a/crates/evm-coder/src/abi.rs +++ b/crates/evm-coder/src/abi.rs @@ -247,6 +247,51 @@ impl_abi_readable!(bool, bool); impl_abi_readable!(string, string); +mod sealed { + /// Not all types can be placed in vec, i.e `Vec` is restricted, `bytes` should be used instead + pub trait CanBePlacedInVec {} +} + +impl sealed::CanBePlacedInVec for U256 {} +impl sealed::CanBePlacedInVec for string {} +impl sealed::CanBePlacedInVec for H160 {} + +impl AbiRead> for AbiReader<'_> +where + Self: AbiRead, +{ + fn abi_read(&mut self) -> Result> { + todo!() + } +} + +macro_rules! impl_tuples { + ($($ident:ident)+) => { + impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {} + impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_> + where + $(Self: AbiRead<$ident>),+ + { + fn abi_read(&mut self) -> Result<($($ident,)+)> { + Ok(( + $(>::abi_read(self)?,)+ + )) + } + } + }; +} + +impl_tuples! {A} +impl_tuples! {A B} +impl_tuples! {A B C} +impl_tuples! {A B C D} +impl_tuples! {A B C D E} +impl_tuples! {A B C D E F} +impl_tuples! {A B C D E F G} +impl_tuples! {A B C D E F G H} +impl_tuples! {A B C D E F G H I} +impl_tuples! {A B C D E F G H I J} + pub trait AbiWrite { fn abi_write(&self, writer: &mut AbiWriter); } --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -67,8 +67,8 @@ #[test] #[ignore] fn $name() { - use sp_std::collections::btree_set::BTreeSet; - let mut out = BTreeSet::new(); + use evm_coder::solidity::TypeCollector; + let mut out = TypeCollector::new(); $decl::generate_solidity_interface(&mut out, $is_impl); println!("=== SNIP START ==="); println!("// SPDX-License-Identifier: OTHER"); @@ -76,7 +76,7 @@ println!(); println!("pragma solidity >=0.8.0 <0.9.0;"); println!(); - for b in out { + for b in out.finish() { println!("{}", b); } println!("=== SNIP END ==="); --- a/crates/evm-coder/src/solidity.rs +++ b/crates/evm-coder/src/solidity.rs @@ -1,25 +1,79 @@ #[cfg(not(feature = "std"))] -use alloc::{string::String}; -use core::{fmt, marker::PhantomData}; +use alloc::{ + string::String, + vec::Vec, + collections::{BTreeSet, BTreeMap}, + format, +}; +#[cfg(feature = "std")] +use std::collections::{BTreeSet, BTreeMap}; +use core::{ + fmt::{self, Write}, + marker::PhantomData, + cell::{Cell, RefCell}, +}; use impl_trait_for_tuples::impl_for_tuples; use crate::types::*; +#[derive(Default)] +pub struct TypeCollector { + structs: RefCell>, + anonymous: RefCell, usize>>, + id: Cell, +} +impl TypeCollector { + pub fn new() -> Self { + Self::default() + } + pub fn collect(&self, item: string) { + self.structs.borrow_mut().insert(item); + } + pub fn next_id(&self) -> usize { + let v = self.id.get(); + self.id.set(v + 1); + v + } + pub fn collect_tuple(&self) -> String { + let names = T::names(self); + if let Some(id) = self.anonymous.borrow().get(&names).cloned() { + return format!("Tuple{}", id); + } + let id = self.next_id(); + let mut str = String::new(); + writeln!(str, "// Anonymous struct").unwrap(); + writeln!(str, "struct Tuple{} {{", id).unwrap(); + for (i, name) in names.iter().enumerate() { + writeln!(str, "\t{} field_{};", name, i).unwrap(); + } + writeln!(str, "}}").unwrap(); + self.collect(str); + self.anonymous.borrow_mut().insert(names, id); + format!("Tuple{}", id) + } + pub fn finish(self) -> BTreeSet { + self.structs.into_inner() + } +} + pub trait SolidityTypeName: 'static { - fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result; - fn solidity_default(writer: &mut impl fmt::Write) -> fmt::Result; + fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; + fn is_simple() -> bool; + fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; fn is_void() -> bool { false } } - macro_rules! solidity_type_name { - ($($ty:ident => $name:literal = $default:literal),* $(,)?) => { + ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => { $( impl SolidityTypeName for $ty { - fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result { + fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result { write!(writer, $name) } - fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result { + fn is_simple() -> bool { + $simple + } + fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result { write!(writer, $default) } } @@ -28,20 +82,23 @@ } solidity_type_name! { - uint8 => "uint8" = "0", - uint32 => "uint32" = "0", - uint128 => "uint128" = "0", - uint256 => "uint256" = "0", - address => "address" = "0x0000000000000000000000000000000000000000", - string => "string memory" = "\"\"", - bytes => "bytes memory" = "hex\"\"", - bool => "bool" = "false", + uint8 => "uint8" true = "0", + uint32 => "uint32" true = "0", + uint128 => "uint128" true = "0", + uint256 => "uint256" true = "0", + address => "address" true = "0x0000000000000000000000000000000000000000", + string => "string" false = "\"\"", + bytes => "bytes" false = "hex\"\"", + bool => "bool" true = "false", } impl SolidityTypeName for void { - fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { Ok(()) } - fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result { + fn is_simple() -> bool { + true + } + fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { Ok(()) } fn is_void() -> bool { @@ -49,10 +106,88 @@ } } +mod sealed { + pub trait CanBePlacedInVec {} +} + +impl sealed::CanBePlacedInVec for uint256 {} +impl sealed::CanBePlacedInVec for string {} +impl sealed::CanBePlacedInVec for address {} + +impl SolidityTypeName for Vec { + fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + T::solidity_name(writer, tc)?; + write!(writer, "[]") + } + fn is_simple() -> bool { + false + } + fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { + write!(writer, "[]") + } +} + +pub trait SolidityTupleType { + fn names(tc: &TypeCollector) -> Vec; + fn len() -> usize; +} + +macro_rules! count { + () => (0usize); + ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*)); +} + +macro_rules! impl_tuples { + ($($ident:ident)+) => { + impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {} + impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) { + fn names(tc: &TypeCollector) -> Vec { + let mut collected = Vec::with_capacity(Self::len()); + $({ + let mut out = string::new(); + $ident::solidity_name(&mut out, tc).expect("no fmt error"); + collected.push(out); + })*; + collected + } + + fn len() -> usize { + count!($($ident)*) + } + } + impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) { + fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + write!(writer, "{}", tc.collect_tuple::()) + } + fn is_simple() -> bool { + false + } + fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + write!(writer, "{}(", tc.collect_tuple::())?; + $( + <$ident>::solidity_default(writer, tc)?; + )* + write!(writer, ")") + } + } + }; +} + +impl_tuples! {A} +impl_tuples! {A B} +impl_tuples! {A B C} +impl_tuples! {A B C D} +impl_tuples! {A B C D E} +impl_tuples! {A B C D E F} +impl_tuples! {A B C D E F G} +impl_tuples! {A B C D E F G H} +impl_tuples! {A B C D E F G H I} +impl_tuples! {A B C D E F G H I J} + pub trait SolidityArguments { - fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result; + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result; - fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result; + fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; fn is_empty(&self) -> bool { self.len() == 0 } @@ -63,9 +198,13 @@ pub struct UnnamedArgument(PhantomData<*const T>); impl SolidityArguments for UnnamedArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { if !T::is_void() { - T::solidity_name(writer) + T::solidity_name(writer, tc)?; + if !T::is_simple() { + write!(writer, " memory")?; + } + Ok(()) } else { Ok(()) } @@ -73,8 +212,8 @@ fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result { Ok(()) } - fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result { - T::solidity_default(writer) + fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + T::solidity_default(writer, tc) } fn len(&self) -> usize { if T::is_void() { @@ -94,9 +233,12 @@ } impl SolidityArguments for NamedArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { if !T::is_void() { - T::solidity_name(writer)?; + T::solidity_name(writer, tc)?; + if !T::is_simple() { + write!(writer, " memory")?; + } write!(writer, " {}", self.0) } else { Ok(()) @@ -105,8 +247,8 @@ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result { writeln!(writer, "\t\t{};", self.0) } - fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result { - T::solidity_default(writer) + fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + T::solidity_default(writer, tc) } fn len(&self) -> usize { if T::is_void() { @@ -126,9 +268,9 @@ } impl SolidityArguments for SolidityEventArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { if !T::is_void() { - T::solidity_name(writer)?; + T::solidity_name(writer, tc)?; if self.0 { write!(writer, " indexed")?; } @@ -140,8 +282,8 @@ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result { writeln!(writer, "\t\t{};", self.1) } - fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result { - T::solidity_default(writer) + fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + T::solidity_default(writer, tc) } fn len(&self) -> usize { if T::is_void() { @@ -153,13 +295,13 @@ } impl SolidityArguments for () { - fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { Ok(()) } fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result { Ok(()) } - fn solidity_default(&self, _writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { Ok(()) } fn len(&self) -> usize { @@ -171,7 +313,7 @@ impl SolidityArguments for Tuple { for_tuples!( where #( Tuple: SolidityArguments ),* ); - fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { let mut first = true; for_tuples!( #( if !Tuple.is_empty() { @@ -179,7 +321,7 @@ write!(writer, ", ")?; } first = false; - Tuple.solidity_name(writer)?; + Tuple.solidity_name(writer, tc)?; } )* ); Ok(()) @@ -190,12 +332,12 @@ )* ); Ok(()) } - fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { if self.is_empty() { Ok(()) } else if self.len() == 1 { for_tuples!( #( - Tuple.solidity_default(writer)?; + Tuple.solidity_default(writer, tc)?; )* ); Ok(()) } else { @@ -207,7 +349,7 @@ write!(writer, ", ")?; } first = false; - Tuple.solidity_name(writer)?; + Tuple.solidity_default(writer, tc)?; } )* ); write!(writer, ")")?; @@ -220,7 +362,12 @@ } pub trait SolidityFunctions { - fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result; + fn solidity_name( + &self, + is_impl: bool, + writer: &mut impl fmt::Write, + tc: &TypeCollector, + ) -> fmt::Result; } pub enum SolidityMutability { @@ -235,9 +382,14 @@ pub mutability: SolidityMutability, } impl SolidityFunctions for SolidityFunction { - fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name( + &self, + is_impl: bool, + writer: &mut impl fmt::Write, + tc: &TypeCollector, + ) -> fmt::Result { write!(writer, "\tfunction {}(", self.name)?; - self.args.solidity_name(writer)?; + self.args.solidity_name(writer, tc)?; write!(writer, ")")?; if is_impl { write!(writer, " public")?; @@ -251,7 +403,7 @@ } if !self.result.is_empty() { write!(writer, " returns (")?; - self.result.solidity_name(writer)?; + self.result.solidity_name(writer, tc)?; write!(writer, ")")?; } if is_impl { @@ -265,7 +417,7 @@ } if !self.result.is_empty() { write!(writer, "\t\treturn ")?; - self.result.solidity_default(writer)?; + self.result.solidity_default(writer, tc)?; writeln!(writer, ";")?; } writeln!(writer, "\t}}")?; @@ -280,10 +432,15 @@ impl SolidityFunctions for Tuple { for_tuples!( where #( Tuple: SolidityFunctions ),* ); - fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name( + &self, + is_impl: bool, + writer: &mut impl fmt::Write, + tc: &TypeCollector, + ) -> fmt::Result { let mut first = false; for_tuples!( #( - Tuple.solidity_name(is_impl, writer)?; + Tuple.solidity_name(is_impl, writer, tc)?; )* ); Ok(()) } @@ -296,7 +453,12 @@ } impl SolidityInterface { - pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result { + pub fn format( + &self, + is_impl: bool, + out: &mut impl fmt::Write, + tc: &TypeCollector, + ) -> fmt::Result { if is_impl { write!(out, "contract ")?; } else { @@ -313,7 +475,7 @@ } } writeln!(out, " {{")?; - self.functions.solidity_name(is_impl, out)?; + self.functions.solidity_name(is_impl, out, tc)?; writeln!(out, "}}")?; Ok(()) } @@ -325,9 +487,14 @@ } impl SolidityFunctions for SolidityEvent { - fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result { + fn solidity_name( + &self, + _is_impl: bool, + writer: &mut impl fmt::Write, + tc: &TypeCollector, + ) -> fmt::Result { write!(writer, "\tevent {}(", self.name)?; - self.args.solidity_name(writer)?; + self.args.solidity_name(writer, tc)?; writeln!(writer, ");") } } -- gitstuff