git.delta.rocks / unique-network / refs/commits / ab2e6a759d16

difftreelog

fix Generate default vaue for vec with tuple

Trubnikov Sergey2022-10-24parent: #4765f58.patch.diff
in: master

1 file changed

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//! Implementation detail of [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};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	/// "simple" types are stored inline, no `memory` modifier should be used in solidity85	fn is_simple() -> bool;86	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87	/// Specialization88	fn is_void() -> bool {89		false90	}91}92macro_rules! solidity_type_name {93    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94        $(95            impl SolidityTypeName for $ty {96                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97                    write!(writer, $name)98                }99				fn is_simple() -> bool {100					$simple101				}102				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103					write!(writer, $default)104				}105            }106        )*107    };108}109110solidity_type_name! {111	uint8 => "uint8" true = "0",112	uint32 => "uint32" true = "0",113	uint64 => "uint64" true = "0",114	uint128 => "uint128" true = "0",115	uint256 => "uint256" true = "0",116	bytes4 => "bytes4" true = "bytes4(0)",117	address => "address" true = "0x0000000000000000000000000000000000000000",118	string => "string" false = "\"\"",119	bytes => "bytes" false = "hex\"\"",120	bool => "bool" true = "false",121}122impl SolidityTypeName for void {123	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124		Ok(())125	}126	fn is_simple() -> bool {127		true128	}129	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130		Ok(())131	}132	fn is_void() -> bool {133		true134	}135}136137mod sealed {138	/// Not every type should be directly placed in vec.139	/// Vec encoding is not memory efficient, as every item will be padded140	/// to 32 bytes.141	/// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142	pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151		T::solidity_name(writer, tc)?;152		write!(writer, "[]")153	}154	fn is_simple() -> bool {155		false156	}157	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {158		write!(writer, "[]")159	}160}161162pub trait SolidityTupleType {163	fn names(tc: &TypeCollector) -> Vec<String>;164	fn len() -> usize;165}166167macro_rules! count {168    () => (0usize);169    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));170}171172macro_rules! impl_tuples {173	($($ident:ident)+) => {174		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}175		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {176			fn names(tc: &TypeCollector) -> Vec<string> {177				let mut collected = Vec::with_capacity(Self::len());178				$({179					let mut out = string::new();180					$ident::solidity_name(&mut out, tc).expect("no fmt error");181					collected.push(out);182				})*;183				collected184			}185186			fn len() -> usize {187				count!($($ident)*)188			}189		}190		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {191			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {192				write!(writer, "{}", tc.collect_tuple::<Self>())193			}194			fn is_simple() -> bool {195				false196			}197			#[allow(unused_assignments)]198			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {199				write!(writer, "{}(", tc.collect_tuple::<Self>())?;200				let mut first = true;201				$(202					if !first {203						write!(writer, ",")?;204					} else {205						first = false;206					}207					<$ident>::solidity_default(writer, tc)?;208				)*209				write!(writer, ")")210			}211		}212	};213}214215impl_tuples! {A}216impl_tuples! {A B}217impl_tuples! {A B C}218impl_tuples! {A B C D}219impl_tuples! {A B C D E}220impl_tuples! {A B C D E F}221impl_tuples! {A B C D E F G}222impl_tuples! {A B C D E F G H}223impl_tuples! {A B C D E F G H I}224impl_tuples! {A B C D E F G H I J}225226pub trait SolidityArguments {227	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;228	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;229	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230	fn is_empty(&self) -> bool {231		self.len() == 0232	}233	fn len(&self) -> usize;234}235236#[derive(Default)]237pub struct UnnamedArgument<T>(PhantomData<*const T>);238239impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {240	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241		if !T::is_void() {242			T::solidity_name(writer, tc)?;243			if !T::is_simple() {244				write!(writer, " memory")?;245			}246			Ok(())247		} else {248			Ok(())249		}250	}251	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {252		Ok(())253	}254	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {255		T::solidity_default(writer, tc)256	}257	fn len(&self) -> usize {258		if T::is_void() {259			0260		} else {261			1262		}263	}264}265266pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);267268impl<T> NamedArgument<T> {269	pub fn new(name: &'static str) -> Self {270		Self(name, Default::default())271	}272}273274impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {275	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276		if !T::is_void() {277			T::solidity_name(writer, tc)?;278			if !T::is_simple() {279				write!(writer, " memory")?;280			}281			write!(writer, " {}", self.0)282		} else {283			Ok(())284		}285	}286	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {287		writeln!(writer, "\t{prefix}\t{};", self.0)288	}289	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {290		T::solidity_default(writer, tc)291	}292	fn len(&self) -> usize {293		if T::is_void() {294			0295		} else {296			1297		}298	}299}300301pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);302303impl<T> SolidityEventArgument<T> {304	pub fn new(indexed: bool, name: &'static str) -> Self {305		Self(indexed, name, Default::default())306	}307}308309impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {310	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311		if !T::is_void() {312			T::solidity_name(writer, tc)?;313			if self.0 {314				write!(writer, " indexed")?;315			}316			write!(writer, " {}", self.1)317		} else {318			Ok(())319		}320	}321	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {322		writeln!(writer, "\t{prefix}\t{};", self.1)323	}324	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {325		T::solidity_default(writer, tc)326	}327	fn len(&self) -> usize {328		if T::is_void() {329			0330		} else {331			1332		}333	}334}335336impl SolidityArguments for () {337	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338		Ok(())339	}340	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {341		Ok(())342	}343	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {344		Ok(())345	}346	fn len(&self) -> usize {347		0348	}349}350351#[impl_for_tuples(1, 12)]352impl SolidityArguments for Tuple {353	for_tuples!( where #( Tuple: SolidityArguments ),* );354355	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {356		let mut first = true;357		for_tuples!( #(358            if !Tuple.is_empty() {359                if !first {360                    write!(writer, ", ")?;361                }362                first = false;363                Tuple.solidity_name(writer, tc)?;364            }365        )* );366		Ok(())367	}368	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {369		for_tuples!( #(370            Tuple.solidity_get(prefix, writer)?;371        )* );372		Ok(())373	}374	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {375		if self.is_empty() {376			Ok(())377		} else if self.len() == 1 {378			for_tuples!( #(379				Tuple.solidity_default(writer, tc)?;380			)* );381			Ok(())382		} else {383			write!(writer, "(")?;384			let mut first = true;385			for_tuples!( #(386				if !Tuple.is_empty() {387					if !first {388						write!(writer, ", ")?;389					}390					first = false;391					Tuple.solidity_default(writer, tc)?;392				}393			)* );394			write!(writer, ")")?;395			Ok(())396		}397	}398	fn len(&self) -> usize {399		for_tuples!( #( Tuple.len() )+* )400	}401}402403pub trait SolidityFunctions {404	fn solidity_name(405		&self,406		is_impl: bool,407		writer: &mut impl fmt::Write,408		tc: &TypeCollector,409	) -> fmt::Result;410}411412pub enum SolidityMutability {413	Pure,414	View,415	Mutable,416}417pub struct SolidityFunction<A, R> {418	pub docs: &'static [&'static str],419	pub selector_str: &'static str,420	pub selector: u32,421	pub hide: bool,422	pub name: &'static str,423	pub args: A,424	pub result: R,425	pub mutability: SolidityMutability,426	pub is_payable: bool,427}428impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {429	fn solidity_name(430		&self,431		is_impl: bool,432		writer: &mut impl fmt::Write,433		tc: &TypeCollector,434	) -> fmt::Result {435		let hide_comment = self.hide.then(|| "// ").unwrap_or("");436		for doc in self.docs {437			writeln!(writer, "\t{hide_comment}///{}", doc)?;438		}439		writeln!(440			writer,441			"\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",442			self.selector443		)?;444		writeln!(445			writer,446			"\t{hide_comment}///  or in textual repr: {}",447			self.selector_str448		)?;449		write!(writer, "\t{hide_comment}function {}(", self.name)?;450		self.args.solidity_name(writer, tc)?;451		write!(writer, ")")?;452		if is_impl {453			write!(writer, " public")?;454		} else {455			write!(writer, " external")?;456		}457		match &self.mutability {458			SolidityMutability::Pure => write!(writer, " pure")?,459			SolidityMutability::View => write!(writer, " view")?,460			SolidityMutability::Mutable => {}461		}462		if self.is_payable {463			write!(writer, " payable")?;464		}465		if !self.result.is_empty() {466			write!(writer, " returns (")?;467			self.result.solidity_name(writer, tc)?;468			write!(writer, ")")?;469		}470		if is_impl {471			writeln!(writer, " {{")?;472			writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;473			self.args.solidity_get(hide_comment, writer)?;474			match &self.mutability {475				SolidityMutability::Pure => {}476				SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,477				SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,478			}479			if !self.result.is_empty() {480				write!(writer, "\t{hide_comment}\treturn ")?;481				self.result.solidity_default(writer, tc)?;482				writeln!(writer, ";")?;483			}484			writeln!(writer, "\t{hide_comment}}}")?;485		} else {486			writeln!(writer, ";")?;487		}488		if self.hide {489			writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;490		}491		Ok(())492	}493}494495#[impl_for_tuples(0, 48)]496impl SolidityFunctions for Tuple {497	for_tuples!( where #( Tuple: SolidityFunctions ),* );498499	fn solidity_name(500		&self,501		is_impl: bool,502		writer: &mut impl fmt::Write,503		tc: &TypeCollector,504	) -> fmt::Result {505		let mut first = false;506		for_tuples!( #(507            Tuple.solidity_name(is_impl, writer, tc)?;508        )* );509		Ok(())510	}511}512513pub struct SolidityInterface<F: SolidityFunctions> {514	pub docs: &'static [&'static str],515	pub selector: bytes4,516	pub name: &'static str,517	pub is: &'static [&'static str],518	pub functions: F,519}520521impl<F: SolidityFunctions> SolidityInterface<F> {522	pub fn format(523		&self,524		is_impl: bool,525		out: &mut impl fmt::Write,526		tc: &TypeCollector,527	) -> fmt::Result {528		const ZERO_BYTES: [u8; 4] = [0; 4];529		for doc in self.docs {530			writeln!(out, "///{}", doc)?;531		}532		if self.selector != ZERO_BYTES {533			writeln!(534				out,535				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",536				u32::from_be_bytes(self.selector)537			)?;538		}539		if is_impl {540			write!(out, "contract ")?;541		} else {542			write!(out, "interface ")?;543		}544		write!(out, "{}", self.name)?;545		if !self.is.is_empty() {546			write!(out, " is")?;547			for (i, n) in self.is.iter().enumerate() {548				if i != 0 {549					write!(out, ",")?;550				}551				write!(out, " {}", n)?;552			}553		}554		writeln!(out, " {{")?;555		self.functions.solidity_name(is_impl, out, tc)?;556		writeln!(out, "}}")?;557		Ok(())558	}559}560561pub struct SolidityEvent<A> {562	pub name: &'static str,563	pub args: A,564}565566impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {567	fn solidity_name(568		&self,569		_is_impl: bool,570		writer: &mut impl fmt::Write,571		tc: &TypeCollector,572	) -> fmt::Result {573		write!(writer, "\tevent {}(", self.name)?;574		self.args.solidity_name(writer, tc)?;575		writeln!(writer, ");")576	}577}
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 [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};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	/// "simple" types are stored inline, no `memory` modifier should be used in solidity85	fn is_simple() -> bool;86	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87	/// Specialization88	fn is_void() -> bool {89		false90	}91}92macro_rules! solidity_type_name {93    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94        $(95            impl SolidityTypeName for $ty {96                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97                    write!(writer, $name)98                }99				fn is_simple() -> bool {100					$simple101				}102				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103					write!(writer, $default)104				}105            }106        )*107    };108}109110solidity_type_name! {111	uint8 => "uint8" true = "0",112	uint32 => "uint32" true = "0",113	uint64 => "uint64" true = "0",114	uint128 => "uint128" true = "0",115	uint256 => "uint256" true = "0",116	bytes4 => "bytes4" true = "bytes4(0)",117	address => "address" true = "0x0000000000000000000000000000000000000000",118	string => "string" false = "\"\"",119	bytes => "bytes" false = "hex\"\"",120	bool => "bool" true = "false",121}122impl SolidityTypeName for void {123	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124		Ok(())125	}126	fn is_simple() -> bool {127		true128	}129	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130		Ok(())131	}132	fn is_void() -> bool {133		true134	}135}136137mod sealed {138	/// Not every type should be directly placed in vec.139	/// Vec encoding is not memory efficient, as every item will be padded140	/// to 32 bytes.141	/// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142	pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151		T::solidity_name(writer, tc)?;152		write!(writer, "[]")153	}154	fn is_simple() -> bool {155		false156	}157	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {158		write!(writer, "new ")?;159		T::solidity_name(writer, tc)?;160		write!(writer, "[](0)")161	}162}163164pub trait SolidityTupleType {165	fn names(tc: &TypeCollector) -> Vec<String>;166	fn len() -> usize;167}168169macro_rules! count {170    () => (0usize);171    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));172}173174macro_rules! impl_tuples {175	($($ident:ident)+) => {176		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}177		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {178			fn names(tc: &TypeCollector) -> Vec<string> {179				let mut collected = Vec::with_capacity(Self::len());180				$({181					let mut out = string::new();182					$ident::solidity_name(&mut out, tc).expect("no fmt error");183					collected.push(out);184				})*;185				collected186			}187188			fn len() -> usize {189				count!($($ident)*)190			}191		}192		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {193			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {194				write!(writer, "{}", tc.collect_tuple::<Self>())195			}196			fn is_simple() -> bool {197				false198			}199			#[allow(unused_assignments)]200			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {201				write!(writer, "{}(", tc.collect_tuple::<Self>())?;202				let mut first = true;203				$(204					if !first {205						write!(writer, ",")?;206					} else {207						first = false;208					}209					<$ident>::solidity_default(writer, tc)?;210				)*211				write!(writer, ")")212			}213		}214	};215}216217impl_tuples! {A}218impl_tuples! {A B}219impl_tuples! {A B C}220impl_tuples! {A B C D}221impl_tuples! {A B C D E}222impl_tuples! {A B C D E F}223impl_tuples! {A B C D E F G}224impl_tuples! {A B C D E F G H}225impl_tuples! {A B C D E F G H I}226impl_tuples! {A B C D E F G H I J}227228pub trait SolidityArguments {229	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;231	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;232	fn is_empty(&self) -> bool {233		self.len() == 0234	}235	fn len(&self) -> usize;236}237238#[derive(Default)]239pub struct UnnamedArgument<T>(PhantomData<*const T>);240241impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {242	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {243		if !T::is_void() {244			T::solidity_name(writer, tc)?;245			if !T::is_simple() {246				write!(writer, " memory")?;247			}248			Ok(())249		} else {250			Ok(())251		}252	}253	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {254		Ok(())255	}256	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {257		T::solidity_default(writer, tc)258	}259	fn len(&self) -> usize {260		if T::is_void() {261			0262		} else {263			1264		}265	}266}267268pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);269270impl<T> NamedArgument<T> {271	pub fn new(name: &'static str) -> Self {272		Self(name, Default::default())273	}274}275276impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {277	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {278		if !T::is_void() {279			T::solidity_name(writer, tc)?;280			if !T::is_simple() {281				write!(writer, " memory")?;282			}283			write!(writer, " {}", self.0)284		} else {285			Ok(())286		}287	}288	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {289		writeln!(writer, "\t{prefix}\t{};", self.0)290	}291	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {292		T::solidity_default(writer, tc)293	}294	fn len(&self) -> usize {295		if T::is_void() {296			0297		} else {298			1299		}300	}301}302303pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);304305impl<T> SolidityEventArgument<T> {306	pub fn new(indexed: bool, name: &'static str) -> Self {307		Self(indexed, name, Default::default())308	}309}310311impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {312	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {313		if !T::is_void() {314			T::solidity_name(writer, tc)?;315			if self.0 {316				write!(writer, " indexed")?;317			}318			write!(writer, " {}", self.1)319		} else {320			Ok(())321		}322	}323	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {324		writeln!(writer, "\t{prefix}\t{};", self.1)325	}326	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {327		T::solidity_default(writer, tc)328	}329	fn len(&self) -> usize {330		if T::is_void() {331			0332		} else {333			1334		}335	}336}337338impl SolidityArguments for () {339	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {340		Ok(())341	}342	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {343		Ok(())344	}345	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {346		Ok(())347	}348	fn len(&self) -> usize {349		0350	}351}352353#[impl_for_tuples(1, 12)]354impl SolidityArguments for Tuple {355	for_tuples!( where #( Tuple: SolidityArguments ),* );356357	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {358		let mut first = true;359		for_tuples!( #(360            if !Tuple.is_empty() {361                if !first {362                    write!(writer, ", ")?;363                }364                first = false;365                Tuple.solidity_name(writer, tc)?;366            }367        )* );368		Ok(())369	}370	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {371		for_tuples!( #(372            Tuple.solidity_get(prefix, writer)?;373        )* );374		Ok(())375	}376	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {377		if self.is_empty() {378			Ok(())379		} else if self.len() == 1 {380			for_tuples!( #(381				Tuple.solidity_default(writer, tc)?;382			)* );383			Ok(())384		} else {385			write!(writer, "(")?;386			let mut first = true;387			for_tuples!( #(388				if !Tuple.is_empty() {389					if !first {390						write!(writer, ", ")?;391					}392					first = false;393					Tuple.solidity_default(writer, tc)?;394				}395			)* );396			write!(writer, ")")?;397			Ok(())398		}399	}400	fn len(&self) -> usize {401		for_tuples!( #( Tuple.len() )+* )402	}403}404405pub trait SolidityFunctions {406	fn solidity_name(407		&self,408		is_impl: bool,409		writer: &mut impl fmt::Write,410		tc: &TypeCollector,411	) -> fmt::Result;412}413414pub enum SolidityMutability {415	Pure,416	View,417	Mutable,418}419pub struct SolidityFunction<A, R> {420	pub docs: &'static [&'static str],421	pub selector_str: &'static str,422	pub selector: u32,423	pub hide: bool,424	pub name: &'static str,425	pub args: A,426	pub result: R,427	pub mutability: SolidityMutability,428	pub is_payable: bool,429}430impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {431	fn solidity_name(432		&self,433		is_impl: bool,434		writer: &mut impl fmt::Write,435		tc: &TypeCollector,436	) -> fmt::Result {437		let hide_comment = self.hide.then(|| "// ").unwrap_or("");438		for doc in self.docs {439			writeln!(writer, "\t{hide_comment}///{}", doc)?;440		}441		writeln!(442			writer,443			"\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",444			self.selector445		)?;446		writeln!(447			writer,448			"\t{hide_comment}///  or in textual repr: {}",449			self.selector_str450		)?;451		write!(writer, "\t{hide_comment}function {}(", self.name)?;452		self.args.solidity_name(writer, tc)?;453		write!(writer, ")")?;454		if is_impl {455			write!(writer, " public")?;456		} else {457			write!(writer, " external")?;458		}459		match &self.mutability {460			SolidityMutability::Pure => write!(writer, " pure")?,461			SolidityMutability::View => write!(writer, " view")?,462			SolidityMutability::Mutable => {}463		}464		if self.is_payable {465			write!(writer, " payable")?;466		}467		if !self.result.is_empty() {468			write!(writer, " returns (")?;469			self.result.solidity_name(writer, tc)?;470			write!(writer, ")")?;471		}472		if is_impl {473			writeln!(writer, " {{")?;474			writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;475			self.args.solidity_get(hide_comment, writer)?;476			match &self.mutability {477				SolidityMutability::Pure => {}478				SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,479				SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,480			}481			if !self.result.is_empty() {482				write!(writer, "\t{hide_comment}\treturn ")?;483				self.result.solidity_default(writer, tc)?;484				writeln!(writer, ";")?;485			}486			writeln!(writer, "\t{hide_comment}}}")?;487		} else {488			writeln!(writer, ";")?;489		}490		if self.hide {491			writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;492		}493		Ok(())494	}495}496497#[impl_for_tuples(0, 48)]498impl SolidityFunctions for Tuple {499	for_tuples!( where #( Tuple: SolidityFunctions ),* );500501	fn solidity_name(502		&self,503		is_impl: bool,504		writer: &mut impl fmt::Write,505		tc: &TypeCollector,506	) -> fmt::Result {507		let mut first = false;508		for_tuples!( #(509            Tuple.solidity_name(is_impl, writer, tc)?;510        )* );511		Ok(())512	}513}514515pub struct SolidityInterface<F: SolidityFunctions> {516	pub docs: &'static [&'static str],517	pub selector: bytes4,518	pub name: &'static str,519	pub is: &'static [&'static str],520	pub functions: F,521}522523impl<F: SolidityFunctions> SolidityInterface<F> {524	pub fn format(525		&self,526		is_impl: bool,527		out: &mut impl fmt::Write,528		tc: &TypeCollector,529	) -> fmt::Result {530		const ZERO_BYTES: [u8; 4] = [0; 4];531		for doc in self.docs {532			writeln!(out, "///{}", doc)?;533		}534		if self.selector != ZERO_BYTES {535			writeln!(536				out,537				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",538				u32::from_be_bytes(self.selector)539			)?;540		}541		if is_impl {542			write!(out, "contract ")?;543		} else {544			write!(out, "interface ")?;545		}546		write!(out, "{}", self.name)?;547		if !self.is.is_empty() {548			write!(out, " is")?;549			for (i, n) in self.is.iter().enumerate() {550				if i != 0 {551					write!(out, ",")?;552				}553				write!(out, " {}", n)?;554			}555		}556		writeln!(out, " {{")?;557		self.functions.solidity_name(is_impl, out, tc)?;558		writeln!(out, "}}")?;559		Ok(())560	}561}562563pub struct SolidityEvent<A> {564	pub name: &'static str,565	pub args: A,566}567568impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {569	fn solidity_name(570		&self,571		_is_impl: bool,572		writer: &mut impl fmt::Write,573		tc: &TypeCollector,574	) -> fmt::Result {575		write!(writer, "\tevent {}(", self.name)?;576		self.args.solidity_name(writer, tc)?;577		writeln!(writer, ");")578	}579}