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

difftreelog

source

crates/evm-coder/src/solidity.rs14.0 KiBsourcehistory
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}