git.delta.rocks / unique-network / refs/commits / 71f76028ba5c

difftreelog

source

crates/evm-coder/src/solidity/mod.rs13.2 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 [`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 language2324mod traits;25pub use traits::*;26mod impls;2728#[cfg(not(feature = "std"))]29use alloc::{vec::Vec, collections::BTreeMap, format};30#[cfg(feature = "std")]31use std::collections::BTreeMap;32use core::{33	fmt::{self, Write},34	marker::PhantomData,35	cell::{Cell, RefCell},36	cmp::Reverse,37};38use impl_trait_for_tuples::impl_for_tuples;39use crate::{types::*, custom_signature::SignatureUnit};4041#[derive(Default)]42pub struct TypeCollector {43	/// Code => id44	/// id ordering is required to perform topo-sort on the resulting data45	structs: RefCell<BTreeMap<String, usize>>,46	anonymous: RefCell<BTreeMap<Vec<String>, usize>>,47	// generic: RefCell<BTreeMap<String, usize>>,48	id: Cell<usize>,49}50impl TypeCollector {51	pub fn new() -> Self {52		Self::default()53	}54	pub fn collect(&self, item: String) {55		let id = self.next_id();56		self.structs.borrow_mut().insert(item, id);57	}58	pub fn next_id(&self) -> usize {59		let v = self.id.get();60		self.id.set(v + 1);61		v62	}63	/// Collect typle, deduplicating it by type, and returning generated name64	pub fn collect_tuple<T: SolidityTupleTy>(&self) -> String {65		let names = T::fields(self);66		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {67			return format!("Tuple{}", id);68		}69		let id = self.next_id();70		let mut str = String::new();71		writeln!(str, "/// @dev anonymous struct").unwrap();72		writeln!(str, "struct Tuple{} {{", id).unwrap();73		for (i, name) in names.iter().enumerate() {74			writeln!(str, "\t{} field_{};", name, i).unwrap();75		}76		writeln!(str, "}}").unwrap();77		self.collect(str);78		self.anonymous.borrow_mut().insert(names, id);79		format!("Tuple{}", id)80	}81	pub fn collect_struct<T: SolidityStructTy>(&self) -> String {82		T::generate_solidity_interface(self)83	}84	pub fn collect_enum<T: SolidityEnumTy>(&self) -> String {85		T::generate_solidity_interface(self)86	}87	pub fn finish(self) -> Vec<String> {88		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();89		data.sort_by_key(|(_, id)| Reverse(*id));90		data.into_iter().map(|(code, _)| code).collect()91	}92}93#[derive(Default)]94pub struct UnnamedArgument<T>(PhantomData<*const T>);9596impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {97	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {98		if !T::is_void() {99			T::solidity_name(writer, tc)?;100			if !T::is_simple() {101				write!(writer, " memory")?;102			}103			Ok(())104		} else {105			Ok(())106		}107	}108	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {109		Ok(())110	}111	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {112		T::solidity_default(writer, tc)113	}114	fn len(&self) -> usize {115		if T::is_void() {116			0117		} else {118			1119		}120	}121}122123pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);124125impl<T> NamedArgument<T> {126	pub fn new(name: &'static str) -> Self {127		Self(name, Default::default())128	}129}130131impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {132	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {133		if !T::is_void() {134			T::solidity_name(writer, tc)?;135			if !T::is_simple() {136				write!(writer, " memory")?;137			}138			write!(writer, " {}", self.0)139		} else {140			Ok(())141		}142	}143	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {144		writeln!(writer, "\t{prefix}\t{};", self.0)145	}146	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {147		T::solidity_default(writer, tc)148	}149	fn len(&self) -> usize {150		if T::is_void() {151			0152		} else {153			1154		}155	}156}157158pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);159160impl<T> SolidityEventArgument<T> {161	pub fn new(indexed: bool, name: &'static str) -> Self {162		Self(indexed, name, Default::default())163	}164}165166impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {167	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {168		if !T::is_void() {169			T::solidity_name(writer, tc)?;170			if self.0 {171				write!(writer, " indexed")?;172			}173			write!(writer, " {}", self.1)174		} else {175			Ok(())176		}177	}178	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {179		writeln!(writer, "\t{prefix}\t{};", self.1)180	}181	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {182		T::solidity_default(writer, tc)183	}184	fn len(&self) -> usize {185		if T::is_void() {186			0187		} else {188			1189		}190	}191}192193impl SolidityArguments for () {194	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {195		Ok(())196	}197	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {198		Ok(())199	}200	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {201		Ok(())202	}203	fn len(&self) -> usize {204		0205	}206}207208#[impl_for_tuples(1, 12)]209impl SolidityArguments for Tuple {210	for_tuples!( where #( Tuple: SolidityArguments ),* );211212	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {213		let mut first = true;214		for_tuples!( #(215            if !Tuple.is_empty() {216                if !first {217                    write!(writer, ", ")?;218                }219                first = false;220                Tuple.solidity_name(writer, tc)?;221            }222        )* );223		Ok(())224	}225	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {226		for_tuples!( #(227            Tuple.solidity_get(prefix, writer)?;228        )* );229		Ok(())230	}231	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {232		if self.is_empty() {233			Ok(())234		} else if self.len() == 1 {235			for_tuples!( #(236				Tuple.solidity_default(writer, tc)?;237			)* );238			Ok(())239		} else {240			write!(writer, "(")?;241			let mut first = true;242			for_tuples!( #(243				if !Tuple.is_empty() {244					if !first {245						write!(writer, ", ")?;246					}247					first = false;248					Tuple.solidity_default(writer, tc)?;249				}250			)* );251			write!(writer, ")")?;252			Ok(())253		}254	}255	fn len(&self) -> usize {256		for_tuples!( #( Tuple.len() )+* )257	}258}259260pub enum SolidityMutability {261	Pure,262	View,263	Mutable,264}265pub struct SolidityFunction<A, R> {266	pub docs: &'static [&'static str],267	pub selector: u32,268	pub hide: bool,269	pub custom_signature: SignatureUnit,270	pub name: &'static str,271	pub args: A,272	pub result: R,273	pub mutability: SolidityMutability,274	pub is_payable: bool,275}276impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {277	fn solidity_name(278		&self,279		is_impl: bool,280		writer: &mut impl fmt::Write,281		tc: &TypeCollector,282	) -> fmt::Result {283		let hide_comment = self.hide.then_some("// ").unwrap_or("");284		for doc in self.docs {285			writeln!(writer, "\t{hide_comment}///{}", doc)?;286		}287		writeln!(288			writer,289			"\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",290			self.selector291		)?;292		writeln!(293			writer,294			"\t{hide_comment}///  or in textual repr: {}",295			self.custom_signature.as_str().expect("bad utf-8")296		)?;297		write!(writer, "\t{hide_comment}function {}(", self.name)?;298		self.args.solidity_name(writer, tc)?;299		write!(writer, ")")?;300		if is_impl {301			write!(writer, " public")?;302		} else {303			write!(writer, " external")?;304		}305		match &self.mutability {306			SolidityMutability::Pure => write!(writer, " pure")?,307			SolidityMutability::View => write!(writer, " view")?,308			SolidityMutability::Mutable => {}309		}310		if self.is_payable {311			write!(writer, " payable")?;312		}313		if !self.result.is_empty() {314			write!(writer, " returns (")?;315			self.result.solidity_name(writer, tc)?;316			write!(writer, ")")?;317		}318		if is_impl {319			writeln!(writer, " {{")?;320			writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;321			self.args.solidity_get(hide_comment, writer)?;322			match &self.mutability {323				SolidityMutability::Pure => {}324				SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,325				SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,326			}327			if !self.result.is_empty() {328				write!(writer, "\t{hide_comment}\treturn ")?;329				self.result.solidity_default(writer, tc)?;330				writeln!(writer, ";")?;331			}332			writeln!(writer, "\t{hide_comment}}}")?;333		} else {334			writeln!(writer, ";")?;335		}336		if self.hide {337			writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;338		}339		Ok(())340	}341}342343#[impl_for_tuples(0, 48)]344impl SolidityFunctions for Tuple {345	for_tuples!( where #( Tuple: SolidityFunctions ),* );346347	fn solidity_name(348		&self,349		is_impl: bool,350		writer: &mut impl fmt::Write,351		tc: &TypeCollector,352	) -> fmt::Result {353		let mut first = false;354		for_tuples!( #(355            Tuple.solidity_name(is_impl, writer, tc)?;356        )* );357		Ok(())358	}359}360361pub struct SolidityInterface<F: SolidityFunctions> {362	pub docs: &'static [&'static str],363	pub selector: Bytes4,364	pub name: &'static str,365	pub is: &'static [&'static str],366	pub functions: F,367}368369impl<F: SolidityFunctions> SolidityInterface<F> {370	pub fn format(371		&self,372		is_impl: bool,373		out: &mut impl fmt::Write,374		tc: &TypeCollector,375	) -> fmt::Result {376		const ZERO_BYTES: [u8; 4] = [0; 4];377		for doc in self.docs {378			writeln!(out, "///{}", doc)?;379		}380		if self.selector != ZERO_BYTES {381			writeln!(382				out,383				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",384				u32::from_be_bytes(self.selector)385			)?;386		}387		if is_impl {388			write!(out, "contract ")?;389		} else {390			write!(out, "interface ")?;391		}392		write!(out, "{}", self.name)?;393		if !self.is.is_empty() {394			write!(out, " is")?;395			for (i, n) in self.is.iter().enumerate() {396				if i != 0 {397					write!(out, ",")?;398				}399				write!(out, " {}", n)?;400			}401		}402		writeln!(out, " {{")?;403		self.functions.solidity_name(is_impl, out, tc)?;404		writeln!(out, "}}")?;405		Ok(())406	}407}408409pub struct SolidityEvent<A> {410	pub name: &'static str,411	pub args: A,412}413414impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {415	fn solidity_name(416		&self,417		_is_impl: bool,418		writer: &mut impl fmt::Write,419		tc: &TypeCollector,420	) -> fmt::Result {421		write!(writer, "\tevent {}(", self.name)?;422		self.args.solidity_name(writer, tc)?;423		writeln!(writer, ");")424	}425}426427#[impl_for_tuples(0, 48)]428impl SolidityItems for Tuple {429	for_tuples!( where #( Tuple: SolidityItems ),* );430431	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {432		for_tuples!( #(433            Tuple.solidity_name(writer, tc)?;434        )* );435		Ok(())436	}437}438439pub struct SolidityStructField<T> {440	pub docs: &'static [&'static str],441	pub name: &'static str,442	pub ty: PhantomData<*const T>,443}444445impl<T> SolidityItems for SolidityStructField<T>446where447	T: SolidityTypeName,448{449	fn solidity_name(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {450		for doc in self.docs {451			writeln!(out, "///{}", doc)?;452		}453		write!(out, "\t")?;454		T::solidity_name(out, tc)?;455		writeln!(out, " {};", self.name)?;456		Ok(())457	}458}459pub struct SolidityStruct<'a, F> {460	pub docs: &'a [&'a str],461	// pub generics:462	pub name: &'a str,463	pub fields: F,464}465impl<F> SolidityStruct<'_, F>466where467	F: SolidityItems,468{469	pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {470		for doc in self.docs {471			writeln!(out, "///{}", doc)?;472		}473		writeln!(out, "struct {} {{", self.name)?;474		self.fields.solidity_name(out, tc)?;475		writeln!(out, "}}")?;476		Ok(())477	}478}479480pub struct SolidityEnumVariant {481	pub docs: &'static [&'static str],482	pub name: &'static str,483}484impl SolidityItems for SolidityEnumVariant {485	fn solidity_name(&self, out: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {486		for doc in self.docs {487			writeln!(out, "///{}", doc)?;488		}489		write!(out, "\t{}", self.name)?;490		Ok(())491	}492}493pub struct SolidityEnum {494	pub docs: &'static [&'static str],495	pub name: &'static str,496	pub fields: &'static [SolidityEnumVariant],497}498impl SolidityEnum {499	pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {500		for doc in self.docs {501			writeln!(out, "///{}", doc)?;502		}503		write!(out, "enum {} {{", self.name)?;504		for (i, field) in self.fields.iter().enumerate() {505			if i != 0 {506				write!(out, ",")?;507			}508			writeln!(out)?;509			field.solidity_name(out, tc)?;510		}511		writeln!(out)?;512		writeln!(out, "}}")?;513		Ok(())514	}515}