git.delta.rocks / unique-network / refs/commits / 7d5b680cd73d

difftreelog

refactor(evm-coder) toposort generated code

Yaroslav Bolyukin2022-07-12parent: #50d005e.patch.diff
in: master
Previously, all items from evm-coder codegen was sorted lexically, which
caused some problems, where lexically sorted names of contract were not
in the same order as contract inherit chain

Now they are sorted by insertion order, and macro-generated code now
inserts them in order of definition, thus providing topological sort of
output contract items

3 files changed

modifiedcrates/evm-coder-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/lib.rs
+++ b/crates/evm-coder-macros/src/lib.rs
@@ -22,7 +22,7 @@
 use sha3::{Digest, Keccak256};
 use syn::{
 	DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments,
-	PathSegment, Type, parse_macro_input, spanned::Spanned, Attribute, parse::Parse,
+	PathSegment, Type, parse_macro_input, spanned::Spanned,
 };
 
 mod solidity_interface;
@@ -241,7 +241,7 @@
 ///     Event(#[indexed] uint32),
 /// }
 ///
-/// #[solidity_interface(name = "MyContract", is(SuperContract), inline_is(InlineContract))]
+/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
 /// impl Contract {
 ///     /// Multiply two numbers
 ///     #[weight(200 + a + b)]
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
@@ -989,26 +989,24 @@
 							#solidity_functions,
 						)*),
 					};
-					if is_impl {
-						tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
-					} else {
-						tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
-					}
-					#(
-						#solidity_generators
-					)*
-					#(
-						#solidity_event_generators
-					)*
 
 					let mut out = string::new();
-					// In solidity interface usage (is) should be preceeded by interface definition
-					// HACK: this comment helps to sort it in a set
 					if #solidity_name.starts_with("Inline") {
 						out.push_str("/// @dev inlined interface\n");
 					}
 					let _ = interface.format(is_impl, &mut out, tc);
 					tc.collect(out);
+					#(
+						#solidity_event_generators
+					)*
+					#(
+						#solidity_generators
+					)*
+					if is_impl {
+						tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
+					} else {
+						tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
+					}
 				}
 			}
 			impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
before · crates/evm-coder/src/solidity.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#[cfg(not(feature = "std"))]18use alloc::{19	string::String,20	vec::Vec,21	collections::{BTreeSet, BTreeMap},22	format,23};24#[cfg(feature = "std")]25use std::collections::{BTreeSet, BTreeMap};26use core::{27	fmt::{self, Write},28	marker::PhantomData,29	cell::{Cell, RefCell},30};31use impl_trait_for_tuples::impl_for_tuples;32use crate::types::*;3334#[derive(Default)]35pub struct TypeCollector {36	structs: RefCell<BTreeSet<string>>,37	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,38	id: Cell<usize>,39}40impl TypeCollector {41	pub fn new() -> Self {42		Self::default()43	}44	pub fn collect(&self, item: string) {45		self.structs.borrow_mut().insert(item);46	}47	pub fn next_id(&self) -> usize {48		let v = self.id.get();49		self.id.set(v + 1);50		v51	}52	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {53		let names = T::names(self);54		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {55			return format!("Tuple{}", id);56		}57		let id = self.next_id();58		let mut str = String::new();59		writeln!(str, "/// @dev anonymous struct").unwrap();60		writeln!(str, "struct Tuple{} {{", id).unwrap();61		for (i, name) in names.iter().enumerate() {62			writeln!(str, "\t{} field_{};", name, i).unwrap();63		}64		writeln!(str, "}}").unwrap();65		self.collect(str);66		self.anonymous.borrow_mut().insert(names, id);67		format!("Tuple{}", id)68	}69	pub fn finish(self) -> BTreeSet<string> {70		self.structs.into_inner()71	}72}7374pub trait SolidityTypeName: 'static {75	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;76	fn is_simple() -> bool;77	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;78	fn is_void() -> bool {79		false80	}81}82macro_rules! solidity_type_name {83    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {84        $(85            impl SolidityTypeName for $ty {86                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {87                    write!(writer, $name)88                }89				fn is_simple() -> bool {90					$simple91				}92				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {93					write!(writer, $default)94				}95            }96        )*97    };98}99100solidity_type_name! {101	uint8 => "uint8" true = "0",102	uint32 => "uint32" true = "0",103	uint64 => "uint64" true = "0",104	uint128 => "uint128" true = "0",105	uint256 => "uint256" true = "0",106	bytes4 => "bytes4" true = "bytes4(0)",107	address => "address" true = "0x0000000000000000000000000000000000000000",108	string => "string" false = "\"\"",109	bytes => "bytes" false = "hex\"\"",110	bool => "bool" true = "false",111}112impl SolidityTypeName for void {113	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {114		Ok(())115	}116	fn is_simple() -> bool {117		true118	}119	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {120		Ok(())121	}122	fn is_void() -> bool {123		true124	}125}126127mod sealed {128	pub trait CanBePlacedInVec {}129}130131impl sealed::CanBePlacedInVec for uint256 {}132impl sealed::CanBePlacedInVec for string {}133impl sealed::CanBePlacedInVec for address {}134135impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {136	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {137		T::solidity_name(writer, tc)?;138		write!(writer, "[]")139	}140	fn is_simple() -> bool {141		false142	}143	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {144		write!(writer, "[]")145	}146}147148pub trait SolidityTupleType {149	fn names(tc: &TypeCollector) -> Vec<String>;150	fn len() -> usize;151}152153macro_rules! count {154    () => (0usize);155    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));156}157158macro_rules! impl_tuples {159	($($ident:ident)+) => {160		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}161		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {162			fn names(tc: &TypeCollector) -> Vec<string> {163				let mut collected = Vec::with_capacity(Self::len());164				$({165					let mut out = string::new();166					$ident::solidity_name(&mut out, tc).expect("no fmt error");167					collected.push(out);168				})*;169				collected170			}171172			fn len() -> usize {173				count!($($ident)*)174			}175		}176		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {177			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {178				write!(writer, "{}", tc.collect_tuple::<Self>())179			}180			fn is_simple() -> bool {181				false182			}183			#[allow(unused_assignments)]184			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {185				write!(writer, "{}(", tc.collect_tuple::<Self>())?;186				let mut first = true;187				$(188					if !first {189						write!(writer, ",")?;190					} else {191						first = false;192					}193					<$ident>::solidity_default(writer, tc)?;194				)*195				write!(writer, ")")196			}197		}198	};199}200201impl_tuples! {A}202impl_tuples! {A B}203impl_tuples! {A B C}204impl_tuples! {A B C D}205impl_tuples! {A B C D E}206impl_tuples! {A B C D E F}207impl_tuples! {A B C D E F G}208impl_tuples! {A B C D E F G H}209impl_tuples! {A B C D E F G H I}210impl_tuples! {A B C D E F G H I J}211212pub trait SolidityArguments {213	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;214	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;215	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;216	fn is_empty(&self) -> bool {217		self.len() == 0218	}219	fn len(&self) -> usize;220}221222#[derive(Default)]223pub struct UnnamedArgument<T>(PhantomData<*const T>);224225impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {226	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {227		if !T::is_void() {228			T::solidity_name(writer, tc)?;229			if !T::is_simple() {230				write!(writer, " memory")?;231			}232			Ok(())233		} else {234			Ok(())235		}236	}237	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {238		Ok(())239	}240	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241		T::solidity_default(writer, tc)242	}243	fn len(&self) -> usize {244		if T::is_void() {245			0246		} else {247			1248		}249	}250}251252pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);253254impl<T> NamedArgument<T> {255	pub fn new(name: &'static str) -> Self {256		Self(name, Default::default())257	}258}259260impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {261	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {262		if !T::is_void() {263			T::solidity_name(writer, tc)?;264			if !T::is_simple() {265				write!(writer, " memory")?;266			}267			write!(writer, " {}", self.0)268		} else {269			Ok(())270		}271	}272	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {273		writeln!(writer, "\t\t{};", self.0)274	}275	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276		T::solidity_default(writer, tc)277	}278	fn len(&self) -> usize {279		if T::is_void() {280			0281		} else {282			1283		}284	}285}286287pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);288289impl<T> SolidityEventArgument<T> {290	pub fn new(indexed: bool, name: &'static str) -> Self {291		Self(indexed, name, Default::default())292	}293}294295impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {296	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {297		if !T::is_void() {298			T::solidity_name(writer, tc)?;299			if self.0 {300				write!(writer, " indexed")?;301			}302			write!(writer, " {}", self.1)303		} else {304			Ok(())305		}306	}307	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {308		writeln!(writer, "\t\t{};", self.1)309	}310	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311		T::solidity_default(writer, tc)312	}313	fn len(&self) -> usize {314		if T::is_void() {315			0316		} else {317			1318		}319	}320}321322impl SolidityArguments for () {323	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {324		Ok(())325	}326	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {327		Ok(())328	}329	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {330		Ok(())331	}332	fn len(&self) -> usize {333		0334	}335}336337#[impl_for_tuples(1, 12)]338impl SolidityArguments for Tuple {339	for_tuples!( where #( Tuple: SolidityArguments ),* );340341	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {342		let mut first = true;343		for_tuples!( #(344            if !Tuple.is_empty() {345                if !first {346                    write!(writer, ", ")?;347                }348                first = false;349                Tuple.solidity_name(writer, tc)?;350            }351        )* );352		Ok(())353	}354	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {355		for_tuples!( #(356            Tuple.solidity_get(writer)?;357        )* );358		Ok(())359	}360	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {361		if self.is_empty() {362			Ok(())363		} else if self.len() == 1 {364			for_tuples!( #(365				Tuple.solidity_default(writer, tc)?;366			)* );367			Ok(())368		} else {369			write!(writer, "(")?;370			let mut first = true;371			for_tuples!( #(372				if !Tuple.is_empty() {373					if !first {374						write!(writer, ", ")?;375					}376					first = false;377					Tuple.solidity_default(writer, tc)?;378				}379			)* );380			write!(writer, ")")?;381			Ok(())382		}383	}384	fn len(&self) -> usize {385		for_tuples!( #( Tuple.len() )+* )386	}387}388389pub trait SolidityFunctions {390	fn solidity_name(391		&self,392		is_impl: bool,393		writer: &mut impl fmt::Write,394		tc: &TypeCollector,395	) -> fmt::Result;396}397398pub enum SolidityMutability {399	Pure,400	View,401	Mutable,402}403pub struct SolidityFunction<A, R> {404	pub docs: &'static [&'static str],405	pub selector_str: &'static str,406	pub selector: u32,407	pub name: &'static str,408	pub args: A,409	pub result: R,410	pub mutability: SolidityMutability,411}412impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {413	fn solidity_name(414		&self,415		is_impl: bool,416		writer: &mut impl fmt::Write,417		tc: &TypeCollector,418	) -> fmt::Result {419		for doc in self.docs {420			writeln!(writer, "\t///{}", doc)?;421		}422		if !self.docs.is_empty() {423			writeln!(writer, "\t///")?;424		}425		writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;426		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;427		write!(writer, "\tfunction {}(", self.name)?;428		self.args.solidity_name(writer, tc)?;429		write!(writer, ")")?;430		if is_impl {431			write!(writer, " public")?;432		} else {433			write!(writer, " external")?;434		}435		match &self.mutability {436			SolidityMutability::Pure => write!(writer, " pure")?,437			SolidityMutability::View => write!(writer, " view")?,438			SolidityMutability::Mutable => {}439		}440		if !self.result.is_empty() {441			write!(writer, " returns (")?;442			self.result.solidity_name(writer, tc)?;443			write!(writer, ")")?;444		}445		if is_impl {446			writeln!(writer, " {{")?;447			writeln!(writer, "\t\trequire(false, stub_error);")?;448			self.args.solidity_get(writer)?;449			match &self.mutability {450				SolidityMutability::Pure => {}451				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,452				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,453			}454			if !self.result.is_empty() {455				write!(writer, "\t\treturn ")?;456				self.result.solidity_default(writer, tc)?;457				writeln!(writer, ";")?;458			}459			writeln!(writer, "\t}}")?;460		} else {461			writeln!(writer, ";")?;462		}463		Ok(())464	}465}466467#[impl_for_tuples(0, 24)]468impl SolidityFunctions for Tuple {469	for_tuples!( where #( Tuple: SolidityFunctions ),* );470471	fn solidity_name(472		&self,473		is_impl: bool,474		writer: &mut impl fmt::Write,475		tc: &TypeCollector,476	) -> fmt::Result {477		let mut first = false;478		for_tuples!( #(479            Tuple.solidity_name(is_impl, writer, tc)?;480        )* );481		Ok(())482	}483}484485pub struct SolidityInterface<F: SolidityFunctions> {486	pub selector: bytes4,487	pub name: &'static str,488	pub is: &'static [&'static str],489	pub functions: F,490}491492impl<F: SolidityFunctions> SolidityInterface<F> {493	pub fn format(494		&self,495		is_impl: bool,496		out: &mut impl fmt::Write,497		tc: &TypeCollector,498	) -> fmt::Result {499		const ZERO_BYTES: [u8; 4] = [0; 4];500		if self.selector != ZERO_BYTES {501			writeln!(502				out,503				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",504				u32::from_be_bytes(self.selector)505			)?;506		}507		if is_impl {508			write!(out, "contract ")?;509		} else {510			write!(out, "interface ")?;511		}512		write!(out, "{}", self.name)?;513		if !self.is.is_empty() {514			write!(out, " is")?;515			for (i, n) in self.is.iter().enumerate() {516				if i != 0 {517					write!(out, ",")?;518				}519				write!(out, " {}", n)?;520			}521		}522		writeln!(out, " {{")?;523		self.functions.solidity_name(is_impl, out, tc)?;524		writeln!(out, "}}")?;525		Ok(())526	}527}528529pub struct SolidityEvent<A> {530	pub name: &'static str,531	pub args: A,532}533534impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {535	fn solidity_name(536		&self,537		_is_impl: bool,538		writer: &mut impl fmt::Write,539		tc: &TypeCollector,540	) -> fmt::Result {541		write!(writer, "\tevent {}(", self.name)?;542		self.args.solidity_name(writer, tc)?;543		writeln!(writer, ");")544	}545}
after · crates/evm-coder/src/solidity.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation1819#[cfg(not(feature = "std"))]20use alloc::{21	string::String,22	vec::Vec,23	collections::BTreeMap,24	format,25};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29	fmt::{self, Write},30	marker::PhantomData,31	cell::{Cell, RefCell},32	cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39	/// Code => id40	/// id ordering is required to perform topo-sort on the resulting data41	structs: RefCell<BTreeMap<string, usize>>,42	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43	id: Cell<usize>,44}45impl TypeCollector {46	pub fn new() -> Self {47		Self::default()48	}49	pub fn collect(&self, item: string) {50		let id = self.next_id();51		self.structs.borrow_mut().insert(item, id);52	}53	pub fn next_id(&self) -> usize {54		let v = self.id.get();55		self.id.set(v + 1);56		v57	}58	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59		let names = T::names(self);60		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61			return format!("Tuple{}", id);62		}63		let id = self.next_id();64		let mut str = String::new();65		writeln!(str, "/// @dev anonymous struct").unwrap();66		writeln!(str, "struct Tuple{} {{", id).unwrap();67		for (i, name) in names.iter().enumerate() {68			writeln!(str, "\t{} field_{};", name, i).unwrap();69		}70		writeln!(str, "}}").unwrap();71		self.collect(str);72		self.anonymous.borrow_mut().insert(names, id);73		format!("Tuple{}", id)74	}75	pub fn finish(self) -> Vec<string> {76		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77		data.sort_by_key(|(_, id)| Reverse(*id));78		data.into_iter().map(|(code, _)| code).collect()79	}80}8182pub trait SolidityTypeName: 'static {83	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84	fn is_simple() -> bool;85	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;86	fn is_void() -> bool {87		false88	}89}90macro_rules! solidity_type_name {91    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {92        $(93            impl SolidityTypeName for $ty {94                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {95                    write!(writer, $name)96                }97				fn is_simple() -> bool {98					$simple99				}100				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {101					write!(writer, $default)102				}103            }104        )*105    };106}107108solidity_type_name! {109	uint8 => "uint8" true = "0",110	uint32 => "uint32" true = "0",111	uint64 => "uint64" true = "0",112	uint128 => "uint128" true = "0",113	uint256 => "uint256" true = "0",114	bytes4 => "bytes4" true = "bytes4(0)",115	address => "address" true = "0x0000000000000000000000000000000000000000",116	string => "string" false = "\"\"",117	bytes => "bytes" false = "hex\"\"",118	bool => "bool" true = "false",119}120impl SolidityTypeName for void {121	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {122		Ok(())123	}124	fn is_simple() -> bool {125		true126	}127	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {128		Ok(())129	}130	fn is_void() -> bool {131		true132	}133}134135mod sealed {136	pub trait CanBePlacedInVec {}137}138139impl sealed::CanBePlacedInVec for uint256 {}140impl sealed::CanBePlacedInVec for string {}141impl sealed::CanBePlacedInVec for address {}142143impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {144	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {145		T::solidity_name(writer, tc)?;146		write!(writer, "[]")147	}148	fn is_simple() -> bool {149		false150	}151	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {152		write!(writer, "[]")153	}154}155156pub trait SolidityTupleType {157	fn names(tc: &TypeCollector) -> Vec<String>;158	fn len() -> usize;159}160161macro_rules! count {162    () => (0usize);163    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));164}165166macro_rules! impl_tuples {167	($($ident:ident)+) => {168		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}169		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {170			fn names(tc: &TypeCollector) -> Vec<string> {171				let mut collected = Vec::with_capacity(Self::len());172				$({173					let mut out = string::new();174					$ident::solidity_name(&mut out, tc).expect("no fmt error");175					collected.push(out);176				})*;177				collected178			}179180			fn len() -> usize {181				count!($($ident)*)182			}183		}184		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {185			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {186				write!(writer, "{}", tc.collect_tuple::<Self>())187			}188			fn is_simple() -> bool {189				false190			}191			#[allow(unused_assignments)]192			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {193				write!(writer, "{}(", tc.collect_tuple::<Self>())?;194				let mut first = true;195				$(196					if !first {197						write!(writer, ",")?;198					} else {199						first = false;200					}201					<$ident>::solidity_default(writer, tc)?;202				)*203				write!(writer, ")")204			}205		}206	};207}208209impl_tuples! {A}210impl_tuples! {A B}211impl_tuples! {A B C}212impl_tuples! {A B C D}213impl_tuples! {A B C D E}214impl_tuples! {A B C D E F}215impl_tuples! {A B C D E F G}216impl_tuples! {A B C D E F G H}217impl_tuples! {A B C D E F G H I}218impl_tuples! {A B C D E F G H I J}219220pub trait SolidityArguments {221	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;222	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;223	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;224	fn is_empty(&self) -> bool {225		self.len() == 0226	}227	fn len(&self) -> usize;228}229230#[derive(Default)]231pub struct UnnamedArgument<T>(PhantomData<*const T>);232233impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {234	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {235		if !T::is_void() {236			T::solidity_name(writer, tc)?;237			if !T::is_simple() {238				write!(writer, " memory")?;239			}240			Ok(())241		} else {242			Ok(())243		}244	}245	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {246		Ok(())247	}248	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {249		T::solidity_default(writer, tc)250	}251	fn len(&self) -> usize {252		if T::is_void() {253			0254		} else {255			1256		}257	}258}259260pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);261262impl<T> NamedArgument<T> {263	pub fn new(name: &'static str) -> Self {264		Self(name, Default::default())265	}266}267268impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {269	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {270		if !T::is_void() {271			T::solidity_name(writer, tc)?;272			if !T::is_simple() {273				write!(writer, " memory")?;274			}275			write!(writer, " {}", self.0)276		} else {277			Ok(())278		}279	}280	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {281		writeln!(writer, "\t\t{};", self.0)282	}283	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {284		T::solidity_default(writer, tc)285	}286	fn len(&self) -> usize {287		if T::is_void() {288			0289		} else {290			1291		}292	}293}294295pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);296297impl<T> SolidityEventArgument<T> {298	pub fn new(indexed: bool, name: &'static str) -> Self {299		Self(indexed, name, Default::default())300	}301}302303impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {304	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {305		if !T::is_void() {306			T::solidity_name(writer, tc)?;307			if self.0 {308				write!(writer, " indexed")?;309			}310			write!(writer, " {}", self.1)311		} else {312			Ok(())313		}314	}315	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {316		writeln!(writer, "\t\t{};", self.1)317	}318	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {319		T::solidity_default(writer, tc)320	}321	fn len(&self) -> usize {322		if T::is_void() {323			0324		} else {325			1326		}327	}328}329330impl SolidityArguments for () {331	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {332		Ok(())333	}334	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {335		Ok(())336	}337	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338		Ok(())339	}340	fn len(&self) -> usize {341		0342	}343}344345#[impl_for_tuples(1, 12)]346impl SolidityArguments for Tuple {347	for_tuples!( where #( Tuple: SolidityArguments ),* );348349	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {350		let mut first = true;351		for_tuples!( #(352            if !Tuple.is_empty() {353                if !first {354                    write!(writer, ", ")?;355                }356                first = false;357                Tuple.solidity_name(writer, tc)?;358            }359        )* );360		Ok(())361	}362	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {363		for_tuples!( #(364            Tuple.solidity_get(writer)?;365        )* );366		Ok(())367	}368	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {369		if self.is_empty() {370			Ok(())371		} else if self.len() == 1 {372			for_tuples!( #(373				Tuple.solidity_default(writer, tc)?;374			)* );375			Ok(())376		} else {377			write!(writer, "(")?;378			let mut first = true;379			for_tuples!( #(380				if !Tuple.is_empty() {381					if !first {382						write!(writer, ", ")?;383					}384					first = false;385					Tuple.solidity_default(writer, tc)?;386				}387			)* );388			write!(writer, ")")?;389			Ok(())390		}391	}392	fn len(&self) -> usize {393		for_tuples!( #( Tuple.len() )+* )394	}395}396397pub trait SolidityFunctions {398	fn solidity_name(399		&self,400		is_impl: bool,401		writer: &mut impl fmt::Write,402		tc: &TypeCollector,403	) -> fmt::Result;404}405406pub enum SolidityMutability {407	Pure,408	View,409	Mutable,410}411pub struct SolidityFunction<A, R> {412	pub docs: &'static [&'static str],413	pub selector_str: &'static str,414	pub selector: u32,415	pub name: &'static str,416	pub args: A,417	pub result: R,418	pub mutability: SolidityMutability,419}420impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {421	fn solidity_name(422		&self,423		is_impl: bool,424		writer: &mut impl fmt::Write,425		tc: &TypeCollector,426	) -> fmt::Result {427		for doc in self.docs {428			writeln!(writer, "\t///{}", doc)?;429		}430		if !self.docs.is_empty() {431			writeln!(writer, "\t///")?;432		}433		writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;434		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;435		write!(writer, "\tfunction {}(", self.name)?;436		self.args.solidity_name(writer, tc)?;437		write!(writer, ")")?;438		if is_impl {439			write!(writer, " public")?;440		} else {441			write!(writer, " external")?;442		}443		match &self.mutability {444			SolidityMutability::Pure => write!(writer, " pure")?,445			SolidityMutability::View => write!(writer, " view")?,446			SolidityMutability::Mutable => {}447		}448		if !self.result.is_empty() {449			write!(writer, " returns (")?;450			self.result.solidity_name(writer, tc)?;451			write!(writer, ")")?;452		}453		if is_impl {454			writeln!(writer, " {{")?;455			writeln!(writer, "\t\trequire(false, stub_error);")?;456			self.args.solidity_get(writer)?;457			match &self.mutability {458				SolidityMutability::Pure => {}459				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,460				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,461			}462			if !self.result.is_empty() {463				write!(writer, "\t\treturn ")?;464				self.result.solidity_default(writer, tc)?;465				writeln!(writer, ";")?;466			}467			writeln!(writer, "\t}}")?;468		} else {469			writeln!(writer, ";")?;470		}471		Ok(())472	}473}474475#[impl_for_tuples(0, 24)]476impl SolidityFunctions for Tuple {477	for_tuples!( where #( Tuple: SolidityFunctions ),* );478479	fn solidity_name(480		&self,481		is_impl: bool,482		writer: &mut impl fmt::Write,483		tc: &TypeCollector,484	) -> fmt::Result {485		let mut first = false;486		for_tuples!( #(487            Tuple.solidity_name(is_impl, writer, tc)?;488        )* );489		Ok(())490	}491}492493pub struct SolidityInterface<F: SolidityFunctions> {494	pub selector: bytes4,495	pub name: &'static str,496	pub is: &'static [&'static str],497	pub functions: F,498}499500impl<F: SolidityFunctions> SolidityInterface<F> {501	pub fn format(502		&self,503		is_impl: bool,504		out: &mut impl fmt::Write,505		tc: &TypeCollector,506	) -> fmt::Result {507		const ZERO_BYTES: [u8; 4] = [0; 4];508		if self.selector != ZERO_BYTES {509			writeln!(510				out,511				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",512				u32::from_be_bytes(self.selector)513			)?;514		}515		if is_impl {516			write!(out, "contract ")?;517		} else {518			write!(out, "interface ")?;519		}520		write!(out, "{}", self.name)?;521		if !self.is.is_empty() {522			write!(out, " is")?;523			for (i, n) in self.is.iter().enumerate() {524				if i != 0 {525					write!(out, ",")?;526				}527				write!(out, " {}", n)?;528			}529		}530		writeln!(out, " {{")?;531		self.functions.solidity_name(is_impl, out, tc)?;532		writeln!(out, "}}")?;533		Ok(())534	}535}536537pub struct SolidityEvent<A> {538	pub name: &'static str,539	pub args: A,540}541542impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {543	fn solidity_name(544		&self,545		_is_impl: bool,546		writer: &mut impl fmt::Write,547		tc: &TypeCollector,548	) -> fmt::Result {549		write!(writer, "\tevent {}(", self.name)?;550		self.args.solidity_name(writer, tc)?;551		writeln!(writer, ");")552	}553}