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

difftreelog

source

crates/evm-coder/src/solidity/mod.rs10.9 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::{string::String, 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	id: Cell<usize>,48}49impl TypeCollector {50	pub fn new() -> Self {51		Self::default()52	}53	pub fn collect(&self, item: string) {54		let id = self.next_id();55		self.structs.borrow_mut().insert(item, id);56	}57	pub fn next_id(&self) -> usize {58		let v = self.id.get();59		self.id.set(v + 1);60		v61	}62	pub fn collect_tuple<T: SolidityType>(&self) -> String {63		let names = T::names(self);64		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {65			return format!("Tuple{}", id);66		}67		let id = self.next_id();68		let mut str = String::new();69		writeln!(str, "/// @dev anonymous struct").unwrap();70		writeln!(str, "struct Tuple{} {{", id).unwrap();71		for (i, name) in names.iter().enumerate() {72			writeln!(str, "\t{} field_{};", name, i).unwrap();73		}74		writeln!(str, "}}").unwrap();75		self.collect(str);76		self.anonymous.borrow_mut().insert(names, id);77		format!("Tuple{}", id)78	}79	pub fn collect_struct<T: StructCollect>(&self) -> String {80		self.collect(<T as StructCollect>::declaration());81		<T as StructCollect>::name()82	}83	pub fn finish(self) -> Vec<string> {84		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();85		data.sort_by_key(|(_, id)| Reverse(*id));86		data.into_iter().map(|(code, _)| code).collect()87	}88}89#[derive(Default)]90pub struct UnnamedArgument<T>(PhantomData<*const T>);9192impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {93	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {94		if !T::is_void() {95			T::solidity_name(writer, tc)?;96			if !T::is_simple() {97				write!(writer, " memory")?;98			}99			Ok(())100		} else {101			Ok(())102		}103	}104	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {105		Ok(())106	}107	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {108		T::solidity_default(writer, tc)109	}110	fn len(&self) -> usize {111		if T::is_void() {112			0113		} else {114			1115		}116	}117}118119pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);120121impl<T> NamedArgument<T> {122	pub fn new(name: &'static str) -> Self {123		Self(name, Default::default())124	}125}126127impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {128	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {129		if !T::is_void() {130			T::solidity_name(writer, tc)?;131			if !T::is_simple() {132				write!(writer, " memory")?;133			}134			write!(writer, " {}", self.0)135		} else {136			Ok(())137		}138	}139	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {140		writeln!(writer, "\t{prefix}\t{};", self.0)141	}142	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {143		T::solidity_default(writer, tc)144	}145	fn len(&self) -> usize {146		if T::is_void() {147			0148		} else {149			1150		}151	}152}153154pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);155156impl<T> SolidityEventArgument<T> {157	pub fn new(indexed: bool, name: &'static str) -> Self {158		Self(indexed, name, Default::default())159	}160}161162impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {163	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {164		if !T::is_void() {165			T::solidity_name(writer, tc)?;166			if self.0 {167				write!(writer, " indexed")?;168			}169			write!(writer, " {}", self.1)170		} else {171			Ok(())172		}173	}174	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {175		writeln!(writer, "\t{prefix}\t{};", self.1)176	}177	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {178		T::solidity_default(writer, tc)179	}180	fn len(&self) -> usize {181		if T::is_void() {182			0183		} else {184			1185		}186	}187}188189impl SolidityArguments for () {190	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {191		Ok(())192	}193	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {194		Ok(())195	}196	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {197		Ok(())198	}199	fn len(&self) -> usize {200		0201	}202}203204#[impl_for_tuples(1, 12)]205impl SolidityArguments for Tuple {206	for_tuples!( where #( Tuple: SolidityArguments ),* );207208	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {209		let mut first = true;210		for_tuples!( #(211            if !Tuple.is_empty() {212                if !first {213                    write!(writer, ", ")?;214                }215                first = false;216                Tuple.solidity_name(writer, tc)?;217            }218        )* );219		Ok(())220	}221	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {222		for_tuples!( #(223            Tuple.solidity_get(prefix, writer)?;224        )* );225		Ok(())226	}227	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {228		if self.is_empty() {229			Ok(())230		} else if self.len() == 1 {231			for_tuples!( #(232				Tuple.solidity_default(writer, tc)?;233			)* );234			Ok(())235		} else {236			write!(writer, "(")?;237			let mut first = true;238			for_tuples!( #(239				if !Tuple.is_empty() {240					if !first {241						write!(writer, ", ")?;242					}243					first = false;244					Tuple.solidity_default(writer, tc)?;245				}246			)* );247			write!(writer, ")")?;248			Ok(())249		}250	}251	fn len(&self) -> usize {252		for_tuples!( #( Tuple.len() )+* )253	}254}255256pub enum SolidityMutability {257	Pure,258	View,259	Mutable,260}261pub struct SolidityFunction<A, R> {262	pub docs: &'static [&'static str],263	pub selector: u32,264	pub hide: bool,265	pub custom_signature: SignatureUnit,266	pub name: &'static str,267	pub args: A,268	pub result: R,269	pub mutability: SolidityMutability,270	pub is_payable: bool,271}272impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {273	fn solidity_name(274		&self,275		is_impl: bool,276		writer: &mut impl fmt::Write,277		tc: &TypeCollector,278	) -> fmt::Result {279		let hide_comment = self.hide.then_some("// ").unwrap_or("");280		for doc in self.docs {281			writeln!(writer, "\t{hide_comment}///{}", doc)?;282		}283		writeln!(284			writer,285			"\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",286			self.selector287		)?;288		writeln!(289			writer,290			"\t{hide_comment}///  or in textual repr: {}",291			self.custom_signature.as_str().expect("bad utf-8")292		)?;293		write!(writer, "\t{hide_comment}function {}(", self.name)?;294		self.args.solidity_name(writer, tc)?;295		write!(writer, ")")?;296		if is_impl {297			write!(writer, " public")?;298		} else {299			write!(writer, " external")?;300		}301		match &self.mutability {302			SolidityMutability::Pure => write!(writer, " pure")?,303			SolidityMutability::View => write!(writer, " view")?,304			SolidityMutability::Mutable => {}305		}306		if self.is_payable {307			write!(writer, " payable")?;308		}309		if !self.result.is_empty() {310			write!(writer, " returns (")?;311			self.result.solidity_name(writer, tc)?;312			write!(writer, ")")?;313		}314		if is_impl {315			writeln!(writer, " {{")?;316			writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;317			self.args.solidity_get(hide_comment, writer)?;318			match &self.mutability {319				SolidityMutability::Pure => {}320				SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,321				SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,322			}323			if !self.result.is_empty() {324				write!(writer, "\t{hide_comment}\treturn ")?;325				self.result.solidity_default(writer, tc)?;326				writeln!(writer, ";")?;327			}328			writeln!(writer, "\t{hide_comment}}}")?;329		} else {330			writeln!(writer, ";")?;331		}332		if self.hide {333			writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;334		}335		Ok(())336	}337}338339#[impl_for_tuples(0, 48)]340impl SolidityFunctions for Tuple {341	for_tuples!( where #( Tuple: SolidityFunctions ),* );342343	fn solidity_name(344		&self,345		is_impl: bool,346		writer: &mut impl fmt::Write,347		tc: &TypeCollector,348	) -> fmt::Result {349		let mut first = false;350		for_tuples!( #(351            Tuple.solidity_name(is_impl, writer, tc)?;352        )* );353		Ok(())354	}355}356357pub struct SolidityInterface<F: SolidityFunctions> {358	pub docs: &'static [&'static str],359	pub selector: bytes4,360	pub name: &'static str,361	pub is: &'static [&'static str],362	pub functions: F,363}364365impl<F: SolidityFunctions> SolidityInterface<F> {366	pub fn format(367		&self,368		is_impl: bool,369		out: &mut impl fmt::Write,370		tc: &TypeCollector,371	) -> fmt::Result {372		const ZERO_BYTES: [u8; 4] = [0; 4];373		for doc in self.docs {374			writeln!(out, "///{}", doc)?;375		}376		if self.selector != ZERO_BYTES {377			writeln!(378				out,379				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",380				u32::from_be_bytes(self.selector)381			)?;382		}383		if is_impl {384			write!(out, "contract ")?;385		} else {386			write!(out, "interface ")?;387		}388		write!(out, "{}", self.name)?;389		if !self.is.is_empty() {390			write!(out, " is")?;391			for (i, n) in self.is.iter().enumerate() {392				if i != 0 {393					write!(out, ",")?;394				}395				write!(out, " {}", n)?;396			}397		}398		writeln!(out, " {{")?;399		self.functions.solidity_name(is_impl, out, tc)?;400		writeln!(out, "}}")?;401		Ok(())402	}403}404405pub struct SolidityEvent<A> {406	pub name: &'static str,407	pub args: A,408}409410impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {411	fn solidity_name(412		&self,413		_is_impl: bool,414		writer: &mut impl fmt::Write,415		tc: &TypeCollector,416	) -> fmt::Result {417		write!(writer, "\tevent {}(", self.name)?;418		self.args.solidity_name(writer, tc)?;419		writeln!(writer, ");")420	}421}