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
before · crates/evm-coder/src/lib.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#![cfg_attr(not(feature = "std"), no_std)]18#[cfg(not(feature = "std"))]19extern crate alloc;2021use abi::{AbiRead, AbiReader, AbiWriter};22pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog};23pub mod abi;24pub mod events;25pub use events::ToLog;26use execution::DispatchInfo;27pub mod execution;28pub mod solidity;2930/// Solidity type definitions31pub mod types {32	#![allow(non_camel_case_types)]3334	#[cfg(not(feature = "std"))]35	use alloc::{vec::Vec};36	use primitive_types::{U256, H160, H256};3738	pub type address = H160;3940	pub type uint8 = u8;41	pub type uint16 = u16;42	pub type uint32 = u32;43	pub type uint64 = u64;44	pub type uint128 = u128;45	pub type uint256 = U256;4647	pub type bytes4 = [u8; 4];4849	pub type topic = H256;5051	#[cfg(not(feature = "std"))]52	pub type string = ::alloc::string::String;53	#[cfg(feature = "std")]54	pub type string = ::std::string::String;55	pub type bytes = Vec<u8>;5657	pub type void = ();5859	//#region Special types60	/// Makes function payable61	pub type value = U256;62	/// Makes function caller-sensitive63	pub type caller = address;64	//#endregion6566	pub struct Msg<C> {67		pub call: C,68		pub caller: H160,69		pub value: U256,70	}71}7273pub trait Call: Sized {74	fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;75}7677pub type Weight = u64;7879pub trait Weighted: Call {80	fn weight(&self) -> DispatchInfo;81}8283pub trait Callable<C: Call> {84	fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;85}8687/// Implementation is implicitly provided for all interfaces88///89/// Note: no Callable implementation is provided90#[derive(Debug)]91pub enum ERC165Call {92	SupportsInterface { interface_id: types::bytes4 },93}9495impl ERC165Call {96	pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);97}9899impl Call for ERC165Call {100	fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>> {101		if selector != Self::INTERFACE_ID {102			return Ok(None);103		}104		Ok(Some(Self::SupportsInterface {105			interface_id: input.abi_read()?,106		}))107	}108}109110/// Generate "tests", which will generate solidity code on execution and print it to stdout111/// Script at .maintain/scripts/generate_api.sh can split this output from test runtime112///113/// This macro receives type usage as second argument, but you can use anything as generics,114/// because no bounds are implied115#[macro_export]116macro_rules! generate_stubgen {117	($name:ident, $decl:ty, $is_impl:literal) => {118		#[test]119		#[ignore]120		fn $name() {121			use evm_coder::solidity::TypeCollector;122			let mut out = TypeCollector::new();123			<$decl>::generate_solidity_interface(&mut out, $is_impl);124			println!("=== SNIP START ===");125			println!("// SPDX-License-Identifier: OTHER");126			println!("// This code is automatically generated");127			println!();128			println!("pragma solidity >=0.8.0 <0.9.0;");129			println!();130			for b in out.finish() {131				println!("{}", b);132			}133			println!("=== SNIP END ===");134		}135	};136}137138#[cfg(test)]139mod tests {140	use super::*;141142	#[test]143	fn function_selector_generation() {144		assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);145	}146147	#[test]148	fn event_topic_generation() {149		assert_eq!(150			hex::encode(&event_topic!(Transfer(address, address, uint256))[..]),151			"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",152		);153	}154}
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -14,15 +14,15 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation
+//! Implementation detail of [`crate::solidity_interface`] macro code-generation.
+//! You should not rely on any public item from this module, as it is only intended to be used
+//! by procedural macro, API and output format may be changed at any time.
+//!
+//! Purpose of this module is to receive solidity contract definition in module-specified
+//! format, and then output string, representing interface of this contract in solidity language
 
 #[cfg(not(feature = "std"))]
-use alloc::{
-	string::String,
-	vec::Vec,
-	collections::BTreeMap,
-	format,
-};
+use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
 #[cfg(feature = "std")]
 use std::collections::BTreeMap;
 use core::{
@@ -81,8 +81,10 @@
 
 pub trait SolidityTypeName: 'static {
 	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+	/// "simple" types are stored inline, no `memory` modifier should be used in solidity
 	fn is_simple() -> bool;
 	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+	/// Specialization
 	fn is_void() -> bool {
 		false
 	}
@@ -133,6 +135,10 @@
 }
 
 mod sealed {
+	/// Not every type should be directly placed in vec.
+	/// Vec encoding is not memory efficient, as every item will be padded
+	/// to 32 bytes.
+	/// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
 	pub trait CanBePlacedInVec {}
 }
 
@@ -427,7 +433,11 @@
 		for doc in self.docs {
 			writeln!(writer, "\t///{}", doc)?;
 		}
-		writeln!(writer, "\t/// @dev EVM selector for this function is: 0x{:0>8x},", self.selector)?;
+		writeln!(
+			writer,
+			"\t/// @dev EVM selector for this function is: 0x{:0>8x},",
+			self.selector
+		)?;
 		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;
 		write!(writer, "\tfunction {}(", self.name)?;
 		self.args.solidity_name(writer, tc)?;