git.delta.rocks / unique-network / refs/commits / 4aadc003b1ed

difftreelog

feat support complex types in solidity defs

Yaroslav Bolyukin2021-09-01parent: #07de194.patch.diff
in: master

5 files changed

modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- 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<String>,
 }
 
+enum AbiType {
+	// type
+	Plain(Ident),
+	// (type1,type2)
+	Tuple(Vec<AbiType>),
+	// type[]
+	Vec(Box<AbiType>),
+	// type[20]
+	Array(Box<AbiType>, usize),
+}
+impl AbiType {
+	fn try_from(value: &Type) -> syn::Result<Self> {
+		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<Self> {
+		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::<usize>()?;
+							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<Self> {
@@ -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<string>, 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 {
modifiedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
--- 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<string>, 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);
 				}
 			}
 
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- 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<u8>` 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<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>
+where
+	Self: AbiRead<R>,
+{
+	fn abi_read(&mut self) -> Result<Vec<R>> {
+		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((
+					$(<Self as AbiRead<$ident>>::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);
 }
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- 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 ===");
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
before · crates/evm-coder/src/solidity.rs
1#[cfg(not(feature = "std"))]2use alloc::{string::String};3use core::{fmt, marker::PhantomData};4use impl_trait_for_tuples::impl_for_tuples;5use crate::types::*;67pub trait SolidityTypeName: 'static {8	fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;9	fn solidity_default(writer: &mut impl fmt::Write) -> fmt::Result;10	fn is_void() -> bool {11		false12	}13}1415macro_rules! solidity_type_name {16    ($($ty:ident => $name:literal = $default:literal),* $(,)?) => {17        $(18            impl SolidityTypeName for $ty {19                fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {20                    write!(writer, $name)21                }22				fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result {23					write!(writer, $default)24				}25            }26        )*27    };28}2930solidity_type_name! {31	uint8 => "uint8" = "0",32	uint32 => "uint32" = "0",33	uint128 => "uint128" = "0",34	uint256 => "uint256" = "0",35	address => "address" = "0x0000000000000000000000000000000000000000",36	string => "string memory" = "\"\"",37	bytes => "bytes memory" = "hex\"\"",38	bool => "bool" = "false",39}40impl SolidityTypeName for void {41	fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {42		Ok(())43	}44	fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result {45		Ok(())46	}47	fn is_void() -> bool {48		true49	}50}5152pub trait SolidityArguments {53	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;54	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;55	fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result;56	fn is_empty(&self) -> bool {57		self.len() == 058	}59	fn len(&self) -> usize;60}6162#[derive(Default)]63pub struct UnnamedArgument<T>(PhantomData<*const T>);6465impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {66	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {67		if !T::is_void() {68			T::solidity_name(writer)69		} else {70			Ok(())71		}72	}73	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {74		Ok(())75	}76	fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {77		T::solidity_default(writer)78	}79	fn len(&self) -> usize {80		if T::is_void() {81			082		} else {83			184		}85	}86}8788pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);8990impl<T> NamedArgument<T> {91	pub fn new(name: &'static str) -> Self {92		Self(name, Default::default())93	}94}9596impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {97	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {98		if !T::is_void() {99			T::solidity_name(writer)?;100			write!(writer, " {}", self.0)101		} else {102			Ok(())103		}104	}105	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {106		writeln!(writer, "\t\t{};", self.0)107	}108	fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {109		T::solidity_default(writer)110	}111	fn len(&self) -> usize {112		if T::is_void() {113			0114		} else {115			1116		}117	}118}119120pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);121122impl<T> SolidityEventArgument<T> {123	pub fn new(indexed: bool, name: &'static str) -> Self {124		Self(indexed, name, Default::default())125	}126}127128impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {129	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {130		if !T::is_void() {131			T::solidity_name(writer)?;132			if self.0 {133				write!(writer, " indexed")?;134			}135			write!(writer, " {}", self.1)136		} else {137			Ok(())138		}139	}140	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {141		writeln!(writer, "\t\t{};", self.1)142	}143	fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {144		T::solidity_default(writer)145	}146	fn len(&self) -> usize {147		if T::is_void() {148			0149		} else {150			1151		}152	}153}154155impl SolidityArguments for () {156	fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {157		Ok(())158	}159	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {160		Ok(())161	}162	fn solidity_default(&self, _writer: &mut impl fmt::Write) -> fmt::Result {163		Ok(())164	}165	fn len(&self) -> usize {166		0167	}168}169170#[impl_for_tuples(1, 5)]171impl SolidityArguments for Tuple {172	for_tuples!( where #( Tuple: SolidityArguments ),* );173174	fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {175		let mut first = true;176		for_tuples!( #(177            if !Tuple.is_empty() {178                if !first {179                    write!(writer, ", ")?;180                }181                first = false;182                Tuple.solidity_name(writer)?;183            }184        )* );185		Ok(())186	}187	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {188		for_tuples!( #(189            Tuple.solidity_get(writer)?;190        )* );191		Ok(())192	}193	fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {194		if self.is_empty() {195			Ok(())196		} else if self.len() == 1 {197			for_tuples!( #(198				Tuple.solidity_default(writer)?;199			)* );200			Ok(())201		} else {202			write!(writer, "(")?;203			let mut first = true;204			for_tuples!( #(205				if !Tuple.is_empty() {206					if !first {207						write!(writer, ", ")?;208					}209					first = false;210					Tuple.solidity_name(writer)?;211				}212			)* );213			write!(writer, ")")?;214			Ok(())215		}216	}217	fn len(&self) -> usize {218		for_tuples!( #( Tuple.len() )+* )219	}220}221222pub trait SolidityFunctions {223	fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result;224}225226pub enum SolidityMutability {227	Pure,228	View,229	Mutable,230}231pub struct SolidityFunction<A, R> {232	pub name: &'static str,233	pub args: A,234	pub result: R,235	pub mutability: SolidityMutability,236}237impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {238	fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {239		write!(writer, "\tfunction {}(", self.name)?;240		self.args.solidity_name(writer)?;241		write!(writer, ")")?;242		if is_impl {243			write!(writer, " public")?;244		} else {245			write!(writer, " external")?;246		}247		match &self.mutability {248			SolidityMutability::Pure => write!(writer, " pure")?,249			SolidityMutability::View => write!(writer, " view")?,250			SolidityMutability::Mutable => {}251		}252		if !self.result.is_empty() {253			write!(writer, " returns (")?;254			self.result.solidity_name(writer)?;255			write!(writer, ")")?;256		}257		if is_impl {258			writeln!(writer, " {{")?;259			writeln!(writer, "\t\trequire(false, stub_error);")?;260			self.args.solidity_get(writer)?;261			match &self.mutability {262				SolidityMutability::Pure => {}263				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,264				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,265			}266			if !self.result.is_empty() {267				write!(writer, "\t\treturn ")?;268				self.result.solidity_default(writer)?;269				writeln!(writer, ";")?;270			}271			writeln!(writer, "\t}}")?;272		} else {273			writeln!(writer, ";")?;274		}275		Ok(())276	}277}278279#[impl_for_tuples(0, 12)]280impl SolidityFunctions for Tuple {281	for_tuples!( where #( Tuple: SolidityFunctions ),* );282283	fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {284		let mut first = false;285		for_tuples!( #(286            Tuple.solidity_name(is_impl, writer)?;287        )* );288		Ok(())289	}290}291292pub struct SolidityInterface<F: SolidityFunctions> {293	pub name: &'static str,294	pub is: &'static [&'static str],295	pub functions: F,296}297298impl<F: SolidityFunctions> SolidityInterface<F> {299	pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result {300		if is_impl {301			write!(out, "contract ")?;302		} else {303			write!(out, "interface ")?;304		}305		write!(out, "{}", self.name)?;306		if !self.is.is_empty() {307			write!(out, " is")?;308			for (i, n) in self.is.iter().enumerate() {309				if i != 0 {310					write!(out, ",")?;311				}312				write!(out, " {}", n)?;313			}314		}315		writeln!(out, " {{")?;316		self.functions.solidity_name(is_impl, out)?;317		writeln!(out, "}}")?;318		Ok(())319	}320}321322pub struct SolidityEvent<A> {323	pub name: &'static str,324	pub args: A,325}326327impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {328	fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {329		write!(writer, "\tevent {}(", self.name)?;330		self.args.solidity_name(writer)?;331		writeln!(writer, ");")332	}333}
after · crates/evm-coder/src/solidity.rs
1#[cfg(not(feature = "std"))]2use alloc::{3	string::String,4	vec::Vec,5	collections::{BTreeSet, BTreeMap},6	format,7};8#[cfg(feature = "std")]9use std::collections::{BTreeSet, BTreeMap};10use core::{11	fmt::{self, Write},12	marker::PhantomData,13	cell::{Cell, RefCell},14};15use impl_trait_for_tuples::impl_for_tuples;16use crate::types::*;1718#[derive(Default)]19pub struct TypeCollector {20	structs: RefCell<BTreeSet<string>>,21	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,22	id: Cell<usize>,23}24impl TypeCollector {25	pub fn new() -> Self {26		Self::default()27	}28	pub fn collect(&self, item: string) {29		self.structs.borrow_mut().insert(item);30	}31	pub fn next_id(&self) -> usize {32		let v = self.id.get();33		self.id.set(v + 1);34		v35	}36	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {37		let names = T::names(self);38		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {39			return format!("Tuple{}", id);40		}41		let id = self.next_id();42		let mut str = String::new();43		writeln!(str, "// Anonymous struct").unwrap();44		writeln!(str, "struct Tuple{} {{", id).unwrap();45		for (i, name) in names.iter().enumerate() {46			writeln!(str, "\t{} field_{};", name, i).unwrap();47		}48		writeln!(str, "}}").unwrap();49		self.collect(str);50		self.anonymous.borrow_mut().insert(names, id);51		format!("Tuple{}", id)52	}53	pub fn finish(self) -> BTreeSet<string> {54		self.structs.into_inner()55	}56}5758pub trait SolidityTypeName: 'static {59	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;60	fn is_simple() -> bool;61	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;62	fn is_void() -> bool {63		false64	}65}66macro_rules! solidity_type_name {67    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {68        $(69            impl SolidityTypeName for $ty {70                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {71                    write!(writer, $name)72                }73				fn is_simple() -> bool {74					$simple75				}76				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {77					write!(writer, $default)78				}79            }80        )*81    };82}8384solidity_type_name! {85	uint8 => "uint8" true = "0",86	uint32 => "uint32" true = "0",87	uint128 => "uint128" true = "0",88	uint256 => "uint256" true = "0",89	address => "address" true = "0x0000000000000000000000000000000000000000",90	string => "string" false = "\"\"",91	bytes => "bytes" false = "hex\"\"",92	bool => "bool" true = "false",93}94impl SolidityTypeName for void {95	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {96		Ok(())97	}98	fn is_simple() -> bool {99		true100	}101	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {102		Ok(())103	}104	fn is_void() -> bool {105		true106	}107}108109mod sealed {110	pub trait CanBePlacedInVec {}111}112113impl sealed::CanBePlacedInVec for uint256 {}114impl sealed::CanBePlacedInVec for string {}115impl sealed::CanBePlacedInVec for address {}116117impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {118	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {119		T::solidity_name(writer, tc)?;120		write!(writer, "[]")121	}122	fn is_simple() -> bool {123		false124	}125	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {126		write!(writer, "[]")127	}128}129130pub trait SolidityTupleType {131	fn names(tc: &TypeCollector) -> Vec<String>;132	fn len() -> usize;133}134135macro_rules! count {136    () => (0usize);137    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));138}139140macro_rules! impl_tuples {141	($($ident:ident)+) => {142		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}143		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {144			fn names(tc: &TypeCollector) -> Vec<string> {145				let mut collected = Vec::with_capacity(Self::len());146				$({147					let mut out = string::new();148					$ident::solidity_name(&mut out, tc).expect("no fmt error");149					collected.push(out);150				})*;151				collected152			}153154			fn len() -> usize {155				count!($($ident)*)156			}157		}158		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {159			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {160				write!(writer, "{}", tc.collect_tuple::<Self>())161			}162			fn is_simple() -> bool {163				false164			}165			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {166				write!(writer, "{}(", tc.collect_tuple::<Self>())?;167				$(168					<$ident>::solidity_default(writer, tc)?;169				)*170				write!(writer, ")")171			}172		}173	};174}175176impl_tuples! {A}177impl_tuples! {A B}178impl_tuples! {A B C}179impl_tuples! {A B C D}180impl_tuples! {A B C D E}181impl_tuples! {A B C D E F}182impl_tuples! {A B C D E F G}183impl_tuples! {A B C D E F G H}184impl_tuples! {A B C D E F G H I}185impl_tuples! {A B C D E F G H I J}186187pub trait SolidityArguments {188	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;189	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;190	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;191	fn is_empty(&self) -> bool {192		self.len() == 0193	}194	fn len(&self) -> usize;195}196197#[derive(Default)]198pub struct UnnamedArgument<T>(PhantomData<*const T>);199200impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {201	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {202		if !T::is_void() {203			T::solidity_name(writer, tc)?;204			if !T::is_simple() {205				write!(writer, " memory")?;206			}207			Ok(())208		} else {209			Ok(())210		}211	}212	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {213		Ok(())214	}215	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {216		T::solidity_default(writer, tc)217	}218	fn len(&self) -> usize {219		if T::is_void() {220			0221		} else {222			1223		}224	}225}226227pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);228229impl<T> NamedArgument<T> {230	pub fn new(name: &'static str) -> Self {231		Self(name, Default::default())232	}233}234235impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {236	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {237		if !T::is_void() {238			T::solidity_name(writer, tc)?;239			if !T::is_simple() {240				write!(writer, " memory")?;241			}242			write!(writer, " {}", self.0)243		} else {244			Ok(())245		}246	}247	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {248		writeln!(writer, "\t\t{};", self.0)249	}250	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {251		T::solidity_default(writer, tc)252	}253	fn len(&self) -> usize {254		if T::is_void() {255			0256		} else {257			1258		}259	}260}261262pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);263264impl<T> SolidityEventArgument<T> {265	pub fn new(indexed: bool, name: &'static str) -> Self {266		Self(indexed, name, Default::default())267	}268}269270impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {271	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {272		if !T::is_void() {273			T::solidity_name(writer, tc)?;274			if self.0 {275				write!(writer, " indexed")?;276			}277			write!(writer, " {}", self.1)278		} else {279			Ok(())280		}281	}282	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {283		writeln!(writer, "\t\t{};", self.1)284	}285	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {286		T::solidity_default(writer, tc)287	}288	fn len(&self) -> usize {289		if T::is_void() {290			0291		} else {292			1293		}294	}295}296297impl SolidityArguments for () {298	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {299		Ok(())300	}301	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {302		Ok(())303	}304	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {305		Ok(())306	}307	fn len(&self) -> usize {308		0309	}310}311312#[impl_for_tuples(1, 5)]313impl SolidityArguments for Tuple {314	for_tuples!( where #( Tuple: SolidityArguments ),* );315316	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {317		let mut first = true;318		for_tuples!( #(319            if !Tuple.is_empty() {320                if !first {321                    write!(writer, ", ")?;322                }323                first = false;324                Tuple.solidity_name(writer, tc)?;325            }326        )* );327		Ok(())328	}329	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {330		for_tuples!( #(331            Tuple.solidity_get(writer)?;332        )* );333		Ok(())334	}335	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {336		if self.is_empty() {337			Ok(())338		} else if self.len() == 1 {339			for_tuples!( #(340				Tuple.solidity_default(writer, tc)?;341			)* );342			Ok(())343		} else {344			write!(writer, "(")?;345			let mut first = true;346			for_tuples!( #(347				if !Tuple.is_empty() {348					if !first {349						write!(writer, ", ")?;350					}351					first = false;352					Tuple.solidity_default(writer, tc)?;353				}354			)* );355			write!(writer, ")")?;356			Ok(())357		}358	}359	fn len(&self) -> usize {360		for_tuples!( #( Tuple.len() )+* )361	}362}363364pub trait SolidityFunctions {365	fn solidity_name(366		&self,367		is_impl: bool,368		writer: &mut impl fmt::Write,369		tc: &TypeCollector,370	) -> fmt::Result;371}372373pub enum SolidityMutability {374	Pure,375	View,376	Mutable,377}378pub struct SolidityFunction<A, R> {379	pub name: &'static str,380	pub args: A,381	pub result: R,382	pub mutability: SolidityMutability,383}384impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {385	fn solidity_name(386		&self,387		is_impl: bool,388		writer: &mut impl fmt::Write,389		tc: &TypeCollector,390	) -> fmt::Result {391		write!(writer, "\tfunction {}(", self.name)?;392		self.args.solidity_name(writer, tc)?;393		write!(writer, ")")?;394		if is_impl {395			write!(writer, " public")?;396		} else {397			write!(writer, " external")?;398		}399		match &self.mutability {400			SolidityMutability::Pure => write!(writer, " pure")?,401			SolidityMutability::View => write!(writer, " view")?,402			SolidityMutability::Mutable => {}403		}404		if !self.result.is_empty() {405			write!(writer, " returns (")?;406			self.result.solidity_name(writer, tc)?;407			write!(writer, ")")?;408		}409		if is_impl {410			writeln!(writer, " {{")?;411			writeln!(writer, "\t\trequire(false, stub_error);")?;412			self.args.solidity_get(writer)?;413			match &self.mutability {414				SolidityMutability::Pure => {}415				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,416				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,417			}418			if !self.result.is_empty() {419				write!(writer, "\t\treturn ")?;420				self.result.solidity_default(writer, tc)?;421				writeln!(writer, ";")?;422			}423			writeln!(writer, "\t}}")?;424		} else {425			writeln!(writer, ";")?;426		}427		Ok(())428	}429}430431#[impl_for_tuples(0, 12)]432impl SolidityFunctions for Tuple {433	for_tuples!( where #( Tuple: SolidityFunctions ),* );434435	fn solidity_name(436		&self,437		is_impl: bool,438		writer: &mut impl fmt::Write,439		tc: &TypeCollector,440	) -> fmt::Result {441		let mut first = false;442		for_tuples!( #(443            Tuple.solidity_name(is_impl, writer, tc)?;444        )* );445		Ok(())446	}447}448449pub struct SolidityInterface<F: SolidityFunctions> {450	pub name: &'static str,451	pub is: &'static [&'static str],452	pub functions: F,453}454455impl<F: SolidityFunctions> SolidityInterface<F> {456	pub fn format(457		&self,458		is_impl: bool,459		out: &mut impl fmt::Write,460		tc: &TypeCollector,461	) -> fmt::Result {462		if is_impl {463			write!(out, "contract ")?;464		} else {465			write!(out, "interface ")?;466		}467		write!(out, "{}", self.name)?;468		if !self.is.is_empty() {469			write!(out, " is")?;470			for (i, n) in self.is.iter().enumerate() {471				if i != 0 {472					write!(out, ",")?;473				}474				write!(out, " {}", n)?;475			}476		}477		writeln!(out, " {{")?;478		self.functions.solidity_name(is_impl, out, tc)?;479		writeln!(out, "}}")?;480		Ok(())481	}482}483484pub struct SolidityEvent<A> {485	pub name: &'static str,486	pub args: A,487}488489impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {490	fn solidity_name(491		&self,492		_is_impl: bool,493		writer: &mut impl fmt::Write,494		tc: &TypeCollector,495	) -> fmt::Result {496		write!(writer, "\tevent {}(", self.name)?;497		self.args.solidity_name(writer, tc)?;498		writeln!(writer, ");")499	}500}