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

difftreelog

doc(evm-coder): document public api

Yaroslav Bolyukin2022-07-12parent: #f0dde16.patch.diff
in: master

9 files changed

modifiedcrates/evm-coder/Cargo.tomldiffbeforeafterboth
--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -5,15 +5,21 @@
 edition = "2021"
 
 [dependencies]
-evm-coder-macros = { path = "../evm-coder-macros" }
+# evm-coder reexports those proc-macro
+evm-coder-procedural = { path = "./procedural" }
+# Evm uses primitive-types for H160, H256 and others
 primitive-types = { version = "0.11.1", default-features = false }
-hex-literal = "0.3.3"
+# Evm doesn't have reexports for log and others
 ethereum = { version = "0.12.0", default-features = false }
+# Error types for execution
 evm-core = { default-features = false, git = "https://github.com/uniquenetwork/evm", branch = "unique-polkadot-v0.9.27" }
-impl-trait-for-tuples = "0.2.1"
+# We have tuple-heavy code in solidity.rs
+impl-trait-for-tuples = "0.2.2"
 
 [dev-dependencies]
+# We want to assert some large binary blobs equality in tests
 hex = "0.4.3"
+hex-literal = "0.3.4"
 
 [features]
 default = ["std"]
addedcrates/evm-coder/README.mddiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/README.md
@@ -0,0 +1,15 @@
+# evm-coder
+
+Library for seamless call translation between Rust and Solidity code
+
+By encoding solidity definitions in Rust, this library also provides generation of
+solidity interfaces for ethereum developers
+
+## Overview
+
+Most of this library functionality shouldn't be used directly, but via macros
+
+- [`solidity_interface`]
+- [`ToLog`]
+
+<!-- TODO: make links useable on github, by publishing crate to docs.rs, and linking it from here instead -->
\ No newline at end of file
modifiedcrates/evm-coder/procedural/Cargo.tomldiffbeforeafterboth
--- a/crates/evm-coder/procedural/Cargo.toml
+++ b/crates/evm-coder/procedural/Cargo.toml
@@ -8,9 +8,12 @@
 proc-macro = true
 
 [dependencies]
+# Ethereum uses keccak (=sha3) for selectors
 sha3 = "0.10.1"
+# Value formatting
+hex = "0.4.3"
+Inflector = "0.11.4"
+# General proc-macro utilities
 quote = "1.0"
 proc-macro2 = "1.0"
 syn = { version = "1.0", features = ["full"] }
-hex = "0.4.3"
-Inflector = "0.11.4"
modifiedcrates/evm-coder/procedural/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/lib.rs
+++ b/crates/evm-coder/procedural/src/lib.rs
@@ -21,8 +21,8 @@
 use quote::quote;
 use sha3::{Digest, Keccak256};
 use syn::{
-	DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments,
-	PathSegment, Type, parse_macro_input, spanned::Spanned,
+	DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, PathSegment, Type,
+	parse_macro_input, spanned::Spanned,
 };
 
 mod solidity_interface;
@@ -199,58 +199,7 @@
 	Ident::new(&name, ident.span())
 }
 
-/// Derives call enum implementing [`evm_coder::Callable`], [`evm_coder::Weighted`]
-/// and [`evm_coder::Call`] from impl block
-///
-/// ## Macro syntax
-///
-/// `#[solidity_interface(name, is, inline_is, events)]`
-/// - *name*: used in generated code, and for Call enum name
-/// - *is*: used to provide call inheritance, not found methods will be delegated to all contracts
-/// specified in is/inline_is
-/// - *inline_is*: same as is, but selectors for passed contracts will be used by derived ERC165
-/// implementation
-///
-/// `#[weight(value)]`
-/// Can be added to every method of impl block, used for deriving [`evm_coder::Weighted`], which
-/// is used by substrate bridge
-/// - *value*: expression, which evaluates to weight required to call this method.
-/// This expression can use call arguments to calculate non-constant execution time.
-/// This expression should evaluate faster than actual execution does, and may provide worser case
-/// than one is called
-///
-/// `#[solidity_interface(rename_selector)]`
-/// - *rename_selector*: by default, selector name will be generated by transforming method name
-/// from snake_case to camelCase. Use this option, if other naming convention is required.
-/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name
-/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"`
-/// explicitly
-///
-/// Also, any contract method may have doc comments, which will be automatically added to generated
-/// solidity interface definitions
-///
-/// ## Example
-///
-/// ```ignore
-/// struct SuperContract;
-/// struct InlineContract;
-/// struct Contract;
-///
-/// #[derive(ToLog)]
-/// enum ContractEvents {
-///     Event(#[indexed] uint32),
-/// }
-///
-/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
-/// impl Contract {
-///     /// Multiply two numbers
-///     #[weight(200 + a + b)]
-///     #[solidity_interface(rename_selector = "mul")]
-///     fn mul(&mut self, a: uint32, b: uint32) -> Result<uint32> {
-///         Ok(a.checked_mul(b).ok_or("overflow")?)
-///     }
-/// }
-/// ```
+/// See documentation for this proc-macro reexported in `evm-coder` crate
 #[proc_macro_attribute]
 pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {
 	let args = parse_macro_input!(args as solidity_interface::InterfaceInfo);
@@ -282,10 +231,7 @@
 	stream
 }
 
-/// ## Syntax
-///
-/// `#[indexed]`
-/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
+/// See documentation for this proc-macro reexported in `evm-coder` crate
 #[proc_macro_derive(ToLog, attributes(indexed))]
 pub fn to_log(value: TokenStream) -> TokenStream {
 	let input = parse_macro_input!(value as DeriveInput);
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -14,8 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-//! TODO: I misunterstood therminology, abi IS rlp encoded, so
-//! this module should be replaced with rlp crate
+//! Implementation of EVM RLP reader/writer
 
 #![allow(dead_code)]
 
@@ -32,6 +31,7 @@
 
 const ABI_ALIGNMENT: usize = 32;
 
+/// View into RLP data, which provides method to read typed items from it
 #[derive(Clone)]
 pub struct AbiReader<'i> {
 	buf: &'i [u8],
@@ -39,6 +39,7 @@
 	offset: usize,
 }
 impl<'i> AbiReader<'i> {
+	/// Start reading RLP buffer, assuming there is no padding bytes
 	pub fn new(buf: &'i [u8]) -> Self {
 		Self {
 			buf,
@@ -46,6 +47,7 @@
 			offset: 0,
 		}
 	}
+	/// Start reading RLP buffer, parsing first 4 bytes as selector
 	pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {
 		if buf.len() < 4 {
 			return Err(Error::Error(ExitError::OutOfOffset));
@@ -109,10 +111,12 @@
 		)
 	}
 
+	/// Read [`H160`] at current position, then advance
 	pub fn address(&mut self) -> Result<H160> {
 		Ok(H160(self.read_padleft()?))
 	}
 
+	/// Read [`bool`] at current position, then advance
 	pub fn bool(&mut self) -> Result<bool> {
 		let data: [u8; 1] = self.read_padleft()?;
 		match data[0] {
@@ -122,49 +126,61 @@
 		}
 	}
 
+	/// Read [`[u8; 4]`] at current position, then advance
 	pub fn bytes4(&mut self) -> Result<[u8; 4]> {
 		self.read_padright()
 	}
 
+	/// Read [`Vec<u8>`] at current position, then advance
 	pub fn bytes(&mut self) -> Result<Vec<u8>> {
 		let mut subresult = self.subresult()?;
-		let length = subresult.read_usize()?;
+		let length = subresult.uint32()? as usize;
 		if subresult.buf.len() < subresult.offset + length {
 			return Err(Error::Error(ExitError::OutOfOffset));
 		}
 		Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
 	}
+
+	/// Read [`string`] at current position, then advance
 	pub fn string(&mut self) -> Result<string> {
 		string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
 	}
 
+	/// Read [`u8`] at current position, then advance
 	pub fn uint8(&mut self) -> Result<u8> {
 		Ok(self.read_padleft::<1>()?[0])
 	}
 
+	/// Read [`u32`] at current position, then advance
 	pub fn uint32(&mut self) -> Result<u32> {
 		Ok(u32::from_be_bytes(self.read_padleft()?))
 	}
 
+	/// Read [`u128`] at current position, then advance
 	pub fn uint128(&mut self) -> Result<u128> {
 		Ok(u128::from_be_bytes(self.read_padleft()?))
 	}
 
+	/// Read [`U256`] at current position, then advance
 	pub fn uint256(&mut self) -> Result<U256> {
 		let buf: [u8; 32] = self.read_padleft()?;
 		Ok(U256::from_big_endian(&buf))
 	}
 
+	/// Read [`u64`] at current position, then advance
 	pub fn uint64(&mut self) -> Result<u64> {
 		Ok(u64::from_be_bytes(self.read_padleft()?))
 	}
 
+	/// Read [`usize`] at current position, then advance
+	#[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
 	pub fn read_usize(&mut self) -> Result<usize> {
 		Ok(usize::from_be_bytes(self.read_padleft()?))
 	}
 
+	/// Slice recursive buffer, advance one word for buffer offset
 	fn subresult(&mut self) -> Result<AbiReader<'i>> {
-		let offset = self.read_usize()?;
+		let offset = self.uint32()? as usize;
 		if offset + self.subresult_offset > self.buf.len() {
 			return Err(Error::Error(ExitError::InvalidRange));
 		}
@@ -175,11 +191,13 @@
 		})
 	}
 
+	/// Is this parser reached end of buffer?
 	pub fn is_finished(&self) -> bool {
 		self.buf.len() == self.offset
 	}
 }
 
+/// Writer for RLP encoded data
 #[derive(Default)]
 pub struct AbiWriter {
 	static_part: Vec<u8>,
@@ -187,9 +205,11 @@
 	had_call: bool,
 }
 impl AbiWriter {
+	/// Initialize internal buffers for output data, assuming no padding required
 	pub fn new() -> Self {
 		Self::default()
 	}
+	/// Initialize internal buffers, inserting method selector at beginning
 	pub fn new_call(method_id: u32) -> Self {
 		let mut val = Self::new();
 		val.static_part.extend(&method_id.to_be_bytes());
@@ -211,59 +231,71 @@
 			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);
 	}
 
+	/// Write [`H160`] to end of buffer
 	pub fn address(&mut self, address: &H160) {
 		self.write_padleft(&address.0)
 	}
 
+	/// Write [`bool`] to end of buffer
 	pub fn bool(&mut self, value: &bool) {
 		self.write_padleft(&[if *value { 1 } else { 0 }])
 	}
 
+	/// Write [`u8`] to end of buffer
 	pub fn uint8(&mut self, value: &u8) {
 		self.write_padleft(&[*value])
 	}
 
+	/// Write [`u32`] to end of buffer
 	pub fn uint32(&mut self, value: &u32) {
 		self.write_padleft(&u32::to_be_bytes(*value))
 	}
 
+	/// Write [`u128`] to end of buffer
 	pub fn uint128(&mut self, value: &u128) {
 		self.write_padleft(&u128::to_be_bytes(*value))
 	}
 
+	/// Write [`U256`] to end of buffer
 	pub fn uint256(&mut self, value: &U256) {
 		let mut out = [0; 32];
 		value.to_big_endian(&mut out);
 		self.write_padleft(&out)
 	}
 
+	/// Write [`usize`] to end of buffer
+	#[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
 	pub fn write_usize(&mut self, value: &usize) {
 		self.write_padleft(&usize::to_be_bytes(*value))
 	}
 
+	/// Append recursive data, writing pending offset at end of buffer
 	pub fn write_subresult(&mut self, result: Self) {
 		self.dynamic_part.push((self.static_part.len(), result));
 		// Empty block, to be filled later
 		self.write_padleft(&[]);
 	}
 
-	pub fn memory(&mut self, value: &[u8]) {
+	fn memory(&mut self, value: &[u8]) {
 		let mut sub = Self::new();
-		sub.write_usize(&value.len());
+		sub.uint32(&(value.len() as u32));
 		for chunk in value.chunks(ABI_ALIGNMENT) {
 			sub.write_padright(chunk);
 		}
 		self.write_subresult(sub);
 	}
 
+	/// Append recursive [`str`] at end of buffer
 	pub fn string(&mut self, value: &str) {
 		self.memory(value.as_bytes())
 	}
 
+	/// Append recursive [`[u8]`] at end of buffer
 	pub fn bytes(&mut self, value: &[u8]) {
 		self.memory(value)
 	}
 
+	/// Finish writer, concatenating all internal buffers
 	pub fn finish(mut self) -> Vec<u8> {
 		for (static_offset, part) in self.dynamic_part {
 			let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);
@@ -278,7 +310,13 @@
 	}
 }
 
+/// [`AbiReader`] implements reading of many types, but it should
+/// be limited to types defined in spec
+///
+/// As this trait can't be made sealed,
+/// instead of having `impl AbiRead for T`, we have `impl AbiRead<T> for AbiReader`
 pub trait AbiRead<T> {
+	/// Read item from current position, advanding decoder
 	fn abi_read(&mut self) -> Result<T>;
 }
 
@@ -318,7 +356,7 @@
 {
 	fn abi_read(&mut self) -> Result<Vec<R>> {
 		let mut sub = self.subresult()?;
-		let size = sub.read_usize()?;
+		let size = sub.uint32()? as usize;
 		sub.subresult_offset = sub.offset;
 		let mut out = Vec::with_capacity(size);
 		for _ in 0..size {
@@ -366,8 +404,13 @@
 impl_tuples! {A B C D E F G H I}
 impl_tuples! {A B C D E F G H I J}
 
+/// For questions about inability to provide custom implementations,
+/// see [`AbiRead`]
 pub trait AbiWrite {
+	/// Write value to end of specified encoder
 	fn abi_write(&self, writer: &mut AbiWriter);
+	/// Specialization for [`crate::solidity_interface`] implementation,
+	/// see comment in `impl AbiWrite for ResultWithPostInfo`
 	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
 		let mut writer = AbiWriter::new();
 		self.abi_write(&mut writer);
@@ -375,13 +418,11 @@
 	}
 }
 
+/// This particular AbiWrite implementation should be split to another trait,
+/// which only implements `to_result`, but due to lack of specialization feature
+/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,
+/// so here we abusing default trait methods for it
 impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {
-	// this particular AbiWrite implementation should be split to another trait,
-	// which only implements [`to_result`]
-	//
-	// But due to lack of specialization feature in stable Rust, we can't have
-	// blanket impl of this trait `for T where T: AbiWrite`, so here we abusing
-	// default trait methods for it
 	fn abi_write(&self, _writer: &mut AbiWriter) {
 		debug_assert!(false, "shouldn't be called, see comment")
 	}
@@ -432,6 +473,8 @@
 	fn abi_write(&self, _writer: &mut AbiWriter) {}
 }
 
+/// Helper macros to parse reader into variables
+#[deprecated]
 #[macro_export]
 macro_rules! abi_decode {
 	($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {
@@ -440,6 +483,9 @@
 		)+
 	}
 }
+
+/// Helper macros to construct RLP-encoded buffer
+#[deprecated]
 #[macro_export]
 macro_rules! abi_encode {
 	($($typ:ident($value:expr)),* $(,)?) => {{
modifiedcrates/evm-coder/src/events.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/events.rs
+++ b/crates/evm-coder/src/events.rs
@@ -19,11 +19,23 @@
 
 use crate::types::*;
 
+/// Implementation of this trait should not be written manually,
+/// instead use [`crate::ToLog`] proc macros.
+///
+/// See also [`evm_coder_procedural::ToLog`], [solidity docs on events](https://docs.soliditylang.org/en/develop/contracts.html#events)
 pub trait ToLog {
+	/// Convert event to [`ethereum::Log`].
+	/// Because event by itself doesn't contains current contract
+	/// address, it should be specified manually.
 	fn to_log(&self, contract: H160) -> Log;
 }
 
+/// Only items implementing `ToTopic` may be used as `#[indexed]` field
+/// in [`crate::ToLog`] macro usage.
+///
+/// See also (solidity docs on events)[<https://docs.soliditylang.org/en/develop/contracts.html#events>]
 pub trait ToTopic {
+	/// Convert value to topic to be used in [`ethereum::Log`]
 	fn to_topic(&self) -> H256;
 }
 
modifiedcrates/evm-coder/src/execution.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/execution.rs
+++ b/crates/evm-coder/src/execution.rs
@@ -14,6 +14,8 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+//! Contract execution related types
+
 #[cfg(not(feature = "std"))]
 use alloc::string::{String, ToString};
 use evm_core::{ExitError, ExitFatal};
@@ -22,10 +24,14 @@
 
 use crate::Weight;
 
+/// Execution error, should be convertible between EVM and Substrate.
 #[derive(Debug, Clone)]
 pub enum Error {
+	/// Non-fatal contract error occured
 	Revert(String),
+	/// EVM fatal error
 	Fatal(ExitFatal),
+	/// EVM normal error
 	Error(ExitError),
 }
 
@@ -38,9 +44,12 @@
 	}
 }
 
+/// To be used in [`crate::solidity_interface`] implementation.
 pub type Result<T> = core::result::Result<T, Error>;
 
+/// Static information collected from [`crate::weight`].
 pub struct DispatchInfo {
+	/// Statically predicted call weight
 	pub weight: Weight,
 }
 
@@ -55,16 +64,22 @@
 	}
 }
 
+/// Weight information that is only available post dispatch.
+/// Note: This can only be used to reduce the weight or fee, not increase it.
 #[derive(Default, Clone)]
 pub struct PostDispatchInfo {
+	/// Actual weight consumed by call
 	actual_weight: Option<Weight>,
 }
 
 impl PostDispatchInfo {
+	/// Calculate amount to be returned back to user
 	pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
 		info.weight - self.calc_actual_weight(info)
 	}
 
+	/// Calculate actual cansumed weight, saturating to weight reported
+	/// pre-dispatch
 	pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
 		if let Some(actual_weight) = self.actual_weight {
 			actual_weight.min(info.weight)
@@ -74,9 +89,12 @@
 	}
 }
 
+/// Wrapper for PostDispatchInfo and any user-provided data
 #[derive(Clone)]
 pub struct WithPostDispatchInfo<T> {
+	/// User provided data
 	pub data: T,
+	/// Info known after dispatch
 	pub post_info: PostDispatchInfo,
 }
 
@@ -89,5 +107,6 @@
 	}
 }
 
+/// Return type of items in [`crate::solidity_interface`] definition
 pub type ResultWithPostInfo<T> =
 	core::result::Result<WithPostDispatchInfo<T>, WithPostDispatchInfo<Error>>;
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -14,22 +14,102 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+#![doc = include_str!("../README.md")]
+#![deny(missing_docs)]
 #![cfg_attr(not(feature = "std"), no_std)]
 #[cfg(not(feature = "std"))]
 extern crate alloc;
 
 use abi::{AbiRead, AbiReader, AbiWriter};
-pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog};
+pub use evm_coder_procedural::{event_topic, fn_selector};
 pub mod abi;
-pub mod events;
-pub use events::ToLog;
+pub use events::{ToLog, ToTopic};
 use execution::DispatchInfo;
 pub mod execution;
+
+/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`]
+/// and [`crate::Call`] from impl block.
+///
+/// ## Macro syntax
+///
+/// `#[solidity_interface(name, is, inline_is, events)]`
+/// - *name* - used in generated code, and for Call enum name
+/// - *is* - used to provide call inheritance, not found methods will be delegated to all contracts
+/// specified in is/inline_is
+/// - *inline_is* - same as is, but selectors for passed contracts will be used by derived ERC165
+/// implementation
+///
+/// `#[weight(value)]`
+/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which
+/// is used by substrate bridge.
+/// - *value*: expression, which evaluates to weight required to call this method.
+/// This expression can use call arguments to calculate non-constant execution time.
+/// This expression should evaluate faster than actual execution does, and may provide worser case
+/// than one is called.
+///
+/// `#[solidity_interface(rename_selector)]`
+/// - *rename_selector* - by default, selector name will be generated by transforming method name
+/// from snake_case to camelCase. Use this option, if other naming convention is required.
+/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name
+/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"`
+/// explicitly.
+///
+/// Both contract and contract methods may have doccomments, which will end up in a generated
+/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro
+///
+/// ## Example
+///
+/// ```ignore
+/// struct SuperContract;
+/// struct InlineContract;
+/// struct Contract;
+///
+/// #[derive(ToLog)]
+/// enum ContractEvents {
+///     Event(#[indexed] uint32),
+/// }
+///
+/// /// @dev This contract provides function to multiply two numbers
+/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
+/// impl Contract {
+///     /// Multiply two numbers
+/// 	/// @param a First number
+/// 	/// @param b Second number
+/// 	/// @return uint32 Product of two passed numbers
+/// 	/// @dev This function returns error in case of overflow
+///     #[weight(200 + a + b)]
+///     #[solidity_interface(rename_selector = "mul")]
+///     fn mul(&mut self, a: uint32, b: uint32) -> Result<uint32> {
+///         Ok(a.checked_mul(b).ok_or("overflow")?)
+///     }
+/// }
+/// ```
+pub use evm_coder_procedural::solidity_interface;
+/// See [`solidity_interface`]
+pub use evm_coder_procedural::solidity;
+/// See [`solidity_interface`]
+pub use evm_coder_procedural::weight;
+
+/// Derives [`ToLog`] for enum
+///
+/// Selectors will be derived from variant names, there is currently no way to have custom naming
+/// for them
+///
+/// `#[indexed]`
+/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
+pub use evm_coder_procedural::ToLog;
+
+// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros
+#[doc(hidden)]
+pub mod events;
+#[doc(hidden)]
 pub mod solidity;
 
-/// Solidity type definitions
+/// Solidity type definitions (aliases from solidity name to rust type)
+/// To be used in [`solidity_interface`] definitions, to make sure there is no
+/// type conflict between Rust code and generated definitions
 pub mod types {
-	#![allow(non_camel_case_types)]
+	#![allow(non_camel_case_types, missing_docs)]
 
 	#[cfg(not(feature = "std"))]
 	use alloc::{vec::Vec};
@@ -54,6 +134,8 @@
 	pub type string = ::std::string::String;
 	pub type bytes = Vec<u8>;
 
+	/// Solidity doesn't have `void` type, however we have special implementation
+	/// for empty tuple return type
 	pub type void = ();
 
 	//#region Special types
@@ -63,36 +145,64 @@
 	pub type caller = address;
 	//#endregion
 
+	/// Ethereum typed call message, similar to solidity
+	/// `msg` object.
 	pub struct Msg<C> {
 		pub call: C,
+		/// Address of user, which called this contract.
 		pub caller: H160,
+		/// Payment amount to contract.
+		/// Contract should reject payment, if target call is not payable,
+		/// and there is no `receiver()` function defined.
 		pub value: U256,
 	}
 }
 
+/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro
 pub trait Call: Sized {
+	/// Parse call buffer into typed call enum
 	fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
 }
 
+/// Intended to be used as `#[weight]` output type
+/// Should be same between evm-coder and substrate to avoid confusion
+///
+/// Isn't same thing as gas, some mapping is required between those types
 pub type Weight = u64;
 
+/// In substrate, we have benchmarking, which allows
+/// us to not rely on gas metering, but instead predict amount of gas to execute call
 pub trait Weighted: Call {
+	/// Predict weight of this call
 	fn weight(&self) -> DispatchInfo;
 }
 
+/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro
+/// on interface implementation, or for externally-owned real EVM contract
 pub trait Callable<C: Call> {
+	/// Call contract using specified call data
 	fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;
 }
 
-/// Implementation is implicitly provided for all interfaces
+/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`],
+/// this structure holds parsed data for ERC165Call subvariant
+///
+/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every
+/// implementing contract
 ///
-/// Note: no Callable implementation is provided
+/// See <https://eips.ethereum.org/EIPS/eip-165>
 #[derive(Debug)]
 pub enum ERC165Call {
-	SupportsInterface { interface_id: types::bytes4 },
+	/// ERC165 provides single method, which returns true, if contract
+	/// implements specified interface
+	SupportsInterface {
+		/// Requested interface
+		interface_id: types::bytes4,
+	},
 }
 
 impl ERC165Call {
+	/// ERC165 selector is provided by standard
 	pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);
 }
 
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 [`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		writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;431		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;432		write!(writer, "\tfunction {}(", self.name)?;433		self.args.solidity_name(writer, tc)?;434		write!(writer, ")")?;435		if is_impl {436			write!(writer, " public")?;437		} else {438			write!(writer, " external")?;439		}440		match &self.mutability {441			SolidityMutability::Pure => write!(writer, " pure")?,442			SolidityMutability::View => write!(writer, " view")?,443			SolidityMutability::Mutable => {}444		}445		if !self.result.is_empty() {446			write!(writer, " returns (")?;447			self.result.solidity_name(writer, tc)?;448			write!(writer, ")")?;449		}450		if is_impl {451			writeln!(writer, " {{")?;452			writeln!(writer, "\t\trequire(false, stub_error);")?;453			self.args.solidity_get(writer)?;454			match &self.mutability {455				SolidityMutability::Pure => {}456				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,457				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,458			}459			if !self.result.is_empty() {460				write!(writer, "\t\treturn ")?;461				self.result.solidity_default(writer, tc)?;462				writeln!(writer, ";")?;463			}464			writeln!(writer, "\t}}")?;465		} else {466			writeln!(writer, ";")?;467		}468		Ok(())469	}470}471472#[impl_for_tuples(0, 24)]473impl SolidityFunctions for Tuple {474	for_tuples!( where #( Tuple: SolidityFunctions ),* );475476	fn solidity_name(477		&self,478		is_impl: bool,479		writer: &mut impl fmt::Write,480		tc: &TypeCollector,481	) -> fmt::Result {482		let mut first = false;483		for_tuples!( #(484            Tuple.solidity_name(is_impl, writer, tc)?;485        )* );486		Ok(())487	}488}489490pub struct SolidityInterface<F: SolidityFunctions> {491	pub docs: &'static [&'static str],492	pub selector: bytes4,493	pub name: &'static str,494	pub is: &'static [&'static str],495	pub functions: F,496}497498impl<F: SolidityFunctions> SolidityInterface<F> {499	pub fn format(500		&self,501		is_impl: bool,502		out: &mut impl fmt::Write,503		tc: &TypeCollector,504	) -> fmt::Result {505		const ZERO_BYTES: [u8; 4] = [0; 4];506		for doc in self.docs {507			writeln!(out, "///{}", doc)?;508		}509		if self.selector != ZERO_BYTES {510			writeln!(511				out,512				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",513				u32::from_be_bytes(self.selector)514			)?;515		}516		if is_impl {517			write!(out, "contract ")?;518		} else {519			write!(out, "interface ")?;520		}521		write!(out, "{}", self.name)?;522		if !self.is.is_empty() {523			write!(out, " is")?;524			for (i, n) in self.is.iter().enumerate() {525				if i != 0 {526					write!(out, ",")?;527				}528				write!(out, " {}", n)?;529			}530		}531		writeln!(out, " {{")?;532		self.functions.solidity_name(is_impl, out, tc)?;533		writeln!(out, "}}")?;534		Ok(())535	}536}537538pub struct SolidityEvent<A> {539	pub name: &'static str,540	pub args: A,541}542543impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {544	fn solidity_name(545		&self,546		_is_impl: bool,547		writer: &mut impl fmt::Write,548		tc: &TypeCollector,549	) -> fmt::Result {550		write!(writer, "\tevent {}(", self.name)?;551		self.args.solidity_name(writer, tc)?;552		writeln!(writer, ");")553	}554}
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, "[]")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, 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, _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, writer: &mut impl fmt::Write) -> fmt::Result {287		writeln!(writer, "\t\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, writer: &mut impl fmt::Write) -> fmt::Result {322		writeln!(writer, "\t\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, _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, writer: &mut impl fmt::Write) -> fmt::Result {369		for_tuples!( #(370            Tuple.solidity_get(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 name: &'static str,422	pub args: A,423	pub result: R,424	pub mutability: SolidityMutability,425}426impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {427	fn solidity_name(428		&self,429		is_impl: bool,430		writer: &mut impl fmt::Write,431		tc: &TypeCollector,432	) -> fmt::Result {433		for doc in self.docs {434			writeln!(writer, "\t///{}", doc)?;435		}436		writeln!(437			writer,438			"\t/// @dev EVM selector for this function is: 0x{:0>8x},",439			self.selector440		)?;441		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;442		write!(writer, "\tfunction {}(", self.name)?;443		self.args.solidity_name(writer, tc)?;444		write!(writer, ")")?;445		if is_impl {446			write!(writer, " public")?;447		} else {448			write!(writer, " external")?;449		}450		match &self.mutability {451			SolidityMutability::Pure => write!(writer, " pure")?,452			SolidityMutability::View => write!(writer, " view")?,453			SolidityMutability::Mutable => {}454		}455		if !self.result.is_empty() {456			write!(writer, " returns (")?;457			self.result.solidity_name(writer, tc)?;458			write!(writer, ")")?;459		}460		if is_impl {461			writeln!(writer, " {{")?;462			writeln!(writer, "\t\trequire(false, stub_error);")?;463			self.args.solidity_get(writer)?;464			match &self.mutability {465				SolidityMutability::Pure => {}466				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,467				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,468			}469			if !self.result.is_empty() {470				write!(writer, "\t\treturn ")?;471				self.result.solidity_default(writer, tc)?;472				writeln!(writer, ";")?;473			}474			writeln!(writer, "\t}}")?;475		} else {476			writeln!(writer, ";")?;477		}478		Ok(())479	}480}481482#[impl_for_tuples(0, 24)]483impl SolidityFunctions for Tuple {484	for_tuples!( where #( Tuple: SolidityFunctions ),* );485486	fn solidity_name(487		&self,488		is_impl: bool,489		writer: &mut impl fmt::Write,490		tc: &TypeCollector,491	) -> fmt::Result {492		let mut first = false;493		for_tuples!( #(494            Tuple.solidity_name(is_impl, writer, tc)?;495        )* );496		Ok(())497	}498}499500pub struct SolidityInterface<F: SolidityFunctions> {501	pub docs: &'static [&'static str],502	pub selector: bytes4,503	pub name: &'static str,504	pub is: &'static [&'static str],505	pub functions: F,506}507508impl<F: SolidityFunctions> SolidityInterface<F> {509	pub fn format(510		&self,511		is_impl: bool,512		out: &mut impl fmt::Write,513		tc: &TypeCollector,514	) -> fmt::Result {515		const ZERO_BYTES: [u8; 4] = [0; 4];516		for doc in self.docs {517			writeln!(out, "///{}", doc)?;518		}519		if self.selector != ZERO_BYTES {520			writeln!(521				out,522				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",523				u32::from_be_bytes(self.selector)524			)?;525		}526		if is_impl {527			write!(out, "contract ")?;528		} else {529			write!(out, "interface ")?;530		}531		write!(out, "{}", self.name)?;532		if !self.is.is_empty() {533			write!(out, " is")?;534			for (i, n) in self.is.iter().enumerate() {535				if i != 0 {536					write!(out, ",")?;537				}538				write!(out, " {}", n)?;539			}540		}541		writeln!(out, " {{")?;542		self.functions.solidity_name(is_impl, out, tc)?;543		writeln!(out, "}}")?;544		Ok(())545	}546}547548pub struct SolidityEvent<A> {549	pub name: &'static str,550	pub args: A,551}552553impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {554	fn solidity_name(555		&self,556		_is_impl: bool,557		writer: &mut impl fmt::Write,558		tc: &TypeCollector,559	) -> fmt::Result {560		write!(writer, "\tevent {}(", self.name)?;561		self.args.solidity_name(writer, tc)?;562		writeln!(writer, ");")563	}564}