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

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}8990#[derive(Default)]91pub struct UnnamedArgument<T>(PhantomData<*const T>);9293impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {94	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {95		if !T::is_void() {96			T::solidity_name(writer, tc)?;97			if !T::is_simple() {98				write!(writer, " memory")?;99			}100			Ok(())101		} else {102			Ok(())103		}104	}105	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {106		Ok(())107	}108	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {109		T::solidity_default(writer, tc)110	}111	fn len(&self) -> usize {112		if T::is_void() {113			0114		} else {115			1116		}117	}118}119120pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);121122impl<T> NamedArgument<T> {123	pub fn new(name: &'static str) -> Self {124		Self(name, Default::default())125	}126}127128impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {129	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {130		if !T::is_void() {131			T::solidity_name(writer, tc)?;132			if !T::is_simple() {133				write!(writer, " memory")?;134			}135			write!(writer, " {}", self.0)136		} else {137			Ok(())138		}139	}140	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {141		writeln!(writer, "\t{prefix}\t{};", self.0)142	}143	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {144		T::solidity_default(writer, tc)145	}146	fn len(&self) -> usize {147		if T::is_void() {148			0149		} else {150			1151		}152	}153}154155pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);156157impl<T> SolidityEventArgument<T> {158	pub fn new(indexed: bool, name: &'static str) -> Self {159		Self(indexed, name, Default::default())160	}161}162163impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {164	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {165		if !T::is_void() {166			T::solidity_name(writer, tc)?;167			if self.0 {168				write!(writer, " indexed")?;169			}170			write!(writer, " {}", self.1)171		} else {172			Ok(())173		}174	}175	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {176		writeln!(writer, "\t{prefix}\t{};", self.1)177	}178	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {179		T::solidity_default(writer, tc)180	}181	fn len(&self) -> usize {182		if T::is_void() {183			0184		} else {185			1186		}187	}188}189190impl SolidityArguments for () {191	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {192		Ok(())193	}194	fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {195		Ok(())196	}197	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {198		Ok(())199	}200	fn len(&self) -> usize {201		0202	}203}204205#[impl_for_tuples(1, 12)]206impl SolidityArguments for Tuple {207	for_tuples!( where #( Tuple: SolidityArguments ),* );208209	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {210		let mut first = true;211		for_tuples!( #(212            if !Tuple.is_empty() {213                if !first {214                    write!(writer, ", ")?;215                }216                first = false;217                Tuple.solidity_name(writer, tc)?;218            }219        )* );220		Ok(())221	}222	fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {223		for_tuples!( #(224            Tuple.solidity_get(prefix, writer)?;225        )* );226		Ok(())227	}228	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {229		if self.is_empty() {230			Ok(())231		} else if self.len() == 1 {232			for_tuples!( #(233				Tuple.solidity_default(writer, tc)?;234			)* );235			Ok(())236		} else {237			write!(writer, "(")?;238			let mut first = true;239			for_tuples!( #(240				if !Tuple.is_empty() {241					if !first {242						write!(writer, ", ")?;243					}244					first = false;245					Tuple.solidity_default(writer, tc)?;246				}247			)* );248			write!(writer, ")")?;249			Ok(())250		}251	}252	fn len(&self) -> usize {253		for_tuples!( #( Tuple.len() )+* )254	}255}256257pub enum SolidityMutability {258	Pure,259	View,260	Mutable,261}262pub struct SolidityFunction<A, R> {263	pub docs: &'static [&'static str],264	pub selector: u32,265	pub hide: bool,266	pub custom_signature: SignatureUnit,267	pub name: &'static str,268	pub args: A,269	pub result: R,270	pub mutability: SolidityMutability,271	pub is_payable: bool,272}273impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {274	fn solidity_name(275		&self,276		is_impl: bool,277		writer: &mut impl fmt::Write,278		tc: &TypeCollector,279	) -> fmt::Result {280		let hide_comment = self.hide.then(|| "// ").unwrap_or("");281		for doc in self.docs {282			writeln!(writer, "\t{hide_comment}///{}", doc)?;283		}284		writeln!(285			writer,286			"\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",287			self.selector288		)?;289		writeln!(290			writer,291			"\t{hide_comment}///  or in textual repr: {}",292			self.custom_signature.as_str().expect("bad utf-8")293		)?;294		write!(writer, "\t{hide_comment}function {}(", self.name)?;295		self.args.solidity_name(writer, tc)?;296		write!(writer, ")")?;297		if is_impl {298			write!(writer, " public")?;299		} else {300			write!(writer, " external")?;301		}302		match &self.mutability {303			SolidityMutability::Pure => write!(writer, " pure")?,304			SolidityMutability::View => write!(writer, " view")?,305			SolidityMutability::Mutable => {}306		}307		if self.is_payable {308			write!(writer, " payable")?;309		}310		if !self.result.is_empty() {311			write!(writer, " returns (")?;312			self.result.solidity_name(writer, tc)?;313			write!(writer, ")")?;314		}315		if is_impl {316			writeln!(writer, " {{")?;317			writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;318			self.args.solidity_get(hide_comment, writer)?;319			match &self.mutability {320				SolidityMutability::Pure => {}321				SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,322				SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,323			}324			if !self.result.is_empty() {325				write!(writer, "\t{hide_comment}\treturn ")?;326				self.result.solidity_default(writer, tc)?;327				writeln!(writer, ";")?;328			}329			writeln!(writer, "\t{hide_comment}}}")?;330		} else {331			writeln!(writer, ";")?;332		}333		if self.hide {334			writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;335		}336		Ok(())337	}338}339340#[impl_for_tuples(0, 48)]341impl SolidityFunctions for Tuple {342	for_tuples!( where #( Tuple: SolidityFunctions ),* );343344	fn solidity_name(345		&self,346		is_impl: bool,347		writer: &mut impl fmt::Write,348		tc: &TypeCollector,349	) -> fmt::Result {350		let mut first = false;351		for_tuples!( #(352            Tuple.solidity_name(is_impl, writer, tc)?;353        )* );354		Ok(())355	}356}357358pub struct SolidityInterface<F: SolidityFunctions> {359	pub docs: &'static [&'static str],360	pub selector: bytes4,361	pub name: &'static str,362	pub is: &'static [&'static str],363	pub functions: F,364}365366impl<F: SolidityFunctions> SolidityInterface<F> {367	pub fn format(368		&self,369		is_impl: bool,370		out: &mut impl fmt::Write,371		tc: &TypeCollector,372	) -> fmt::Result {373		const ZERO_BYTES: [u8; 4] = [0; 4];374		for doc in self.docs {375			writeln!(out, "///{}", doc)?;376		}377		if self.selector != ZERO_BYTES {378			writeln!(379				out,380				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",381				u32::from_be_bytes(self.selector)382			)?;383		}384		if is_impl {385			write!(out, "contract ")?;386		} else {387			write!(out, "interface ")?;388		}389		write!(out, "{}", self.name)?;390		if !self.is.is_empty() {391			write!(out, " is")?;392			for (i, n) in self.is.iter().enumerate() {393				if i != 0 {394					write!(out, ",")?;395				}396				write!(out, " {}", n)?;397			}398		}399		writeln!(out, " {{")?;400		self.functions.solidity_name(is_impl, out, tc)?;401		writeln!(out, "}}")?;402		Ok(())403	}404}405406pub struct SolidityEvent<A> {407	pub name: &'static str,408	pub args: A,409}410411impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {412	fn solidity_name(413		&self,414		_is_impl: bool,415		writer: &mut impl fmt::Write,416		tc: &TypeCollector,417	) -> fmt::Result {418		write!(writer, "\tevent {}(", self.name)?;419		self.args.solidity_name(writer, tc)?;420		writeln!(writer, ");")421	}422}