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
before · crates/evm-coder/src/abi.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//! TODO: I misunterstood therminology, abi IS rlp encoded, so18//! this module should be replaced with rlp crate1920#![allow(dead_code)]2122#[cfg(not(feature = "std"))]23use alloc::vec::Vec;24use evm_core::ExitError;25use primitive_types::{H160, U256};2627use crate::{28	execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},29	types::{string, self},30};31use crate::execution::Result;3233const ABI_ALIGNMENT: usize = 32;3435#[derive(Clone)]36pub struct AbiReader<'i> {37	buf: &'i [u8],38	subresult_offset: usize,39	offset: usize,40}41impl<'i> AbiReader<'i> {42	pub fn new(buf: &'i [u8]) -> Self {43		Self {44			buf,45			subresult_offset: 0,46			offset: 0,47		}48	}49	pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {50		if buf.len() < 4 {51			return Err(Error::Error(ExitError::OutOfOffset));52		}53		let mut method_id = [0; 4];54		method_id.copy_from_slice(&buf[0..4]);5556		Ok((57			method_id,58			Self {59				buf,60				subresult_offset: 4,61				offset: 4,62			},63		))64	}6566	fn read_pad<const S: usize>(67		buf: &[u8],68		offset: usize,69		pad_start: usize,70		pad_size: usize,71		block_start: usize,72		block_size: usize,73	) -> Result<[u8; S]> {74		if buf.len() - offset < ABI_ALIGNMENT {75			return Err(Error::Error(ExitError::OutOfOffset));76		}77		let mut block = [0; S];78		// Verify padding is empty79		if !buf[pad_start..pad_size].iter().all(|&v| v == 0) {80			return Err(Error::Error(ExitError::InvalidRange));81		}82		block.copy_from_slice(&buf[block_start..block_size]);83		Ok(block)84	}8586	fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {87		let offset = self.offset;88		self.offset += ABI_ALIGNMENT;89		Self::read_pad(90			self.buf,91			offset,92			offset,93			offset + ABI_ALIGNMENT - S,94			offset + ABI_ALIGNMENT - S,95			offset + ABI_ALIGNMENT,96		)97	}9899	fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {100		let offset = self.offset;101		self.offset += ABI_ALIGNMENT;102		Self::read_pad(103			self.buf,104			offset,105			offset + S,106			offset + ABI_ALIGNMENT,107			offset,108			offset + S,109		)110	}111112	pub fn address(&mut self) -> Result<H160> {113		Ok(H160(self.read_padleft()?))114	}115116	pub fn bool(&mut self) -> Result<bool> {117		let data: [u8; 1] = self.read_padleft()?;118		match data[0] {119			0 => Ok(false),120			1 => Ok(true),121			_ => Err(Error::Error(ExitError::InvalidRange)),122		}123	}124125	pub fn bytes4(&mut self) -> Result<[u8; 4]> {126		self.read_padright()127	}128129	pub fn bytes(&mut self) -> Result<Vec<u8>> {130		let mut subresult = self.subresult()?;131		let length = subresult.read_usize()?;132		if subresult.buf.len() < subresult.offset + length {133			return Err(Error::Error(ExitError::OutOfOffset));134		}135		Ok(subresult.buf[subresult.offset..subresult.offset + length].into())136	}137	pub fn string(&mut self) -> Result<string> {138		string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))139	}140141	pub fn uint8(&mut self) -> Result<u8> {142		Ok(self.read_padleft::<1>()?[0])143	}144145	pub fn uint32(&mut self) -> Result<u32> {146		Ok(u32::from_be_bytes(self.read_padleft()?))147	}148149	pub fn uint128(&mut self) -> Result<u128> {150		Ok(u128::from_be_bytes(self.read_padleft()?))151	}152153	pub fn uint256(&mut self) -> Result<U256> {154		let buf: [u8; 32] = self.read_padleft()?;155		Ok(U256::from_big_endian(&buf))156	}157158	pub fn uint64(&mut self) -> Result<u64> {159		Ok(u64::from_be_bytes(self.read_padleft()?))160	}161162	pub fn read_usize(&mut self) -> Result<usize> {163		Ok(usize::from_be_bytes(self.read_padleft()?))164	}165166	fn subresult(&mut self) -> Result<AbiReader<'i>> {167		let offset = self.read_usize()?;168		if offset + self.subresult_offset > self.buf.len() {169			return Err(Error::Error(ExitError::InvalidRange));170		}171		Ok(AbiReader {172			buf: self.buf,173			subresult_offset: offset + self.subresult_offset,174			offset: offset + self.subresult_offset,175		})176	}177178	pub fn is_finished(&self) -> bool {179		self.buf.len() == self.offset180	}181}182183#[derive(Default)]184pub struct AbiWriter {185	static_part: Vec<u8>,186	dynamic_part: Vec<(usize, AbiWriter)>,187	had_call: bool,188}189impl AbiWriter {190	pub fn new() -> Self {191		Self::default()192	}193	pub fn new_call(method_id: u32) -> Self {194		let mut val = Self::new();195		val.static_part.extend(&method_id.to_be_bytes());196		val.had_call = true;197		val198	}199200	fn write_padleft(&mut self, block: &[u8]) {201		assert!(block.len() <= ABI_ALIGNMENT);202		self.static_part203			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);204		self.static_part.extend(block);205	}206207	fn write_padright(&mut self, bytes: &[u8]) {208		assert!(bytes.len() <= ABI_ALIGNMENT);209		self.static_part.extend(bytes);210		self.static_part211			.extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);212	}213214	pub fn address(&mut self, address: &H160) {215		self.write_padleft(&address.0)216	}217218	pub fn bool(&mut self, value: &bool) {219		self.write_padleft(&[if *value { 1 } else { 0 }])220	}221222	pub fn uint8(&mut self, value: &u8) {223		self.write_padleft(&[*value])224	}225226	pub fn uint32(&mut self, value: &u32) {227		self.write_padleft(&u32::to_be_bytes(*value))228	}229230	pub fn uint128(&mut self, value: &u128) {231		self.write_padleft(&u128::to_be_bytes(*value))232	}233234	pub fn uint256(&mut self, value: &U256) {235		let mut out = [0; 32];236		value.to_big_endian(&mut out);237		self.write_padleft(&out)238	}239240	pub fn write_usize(&mut self, value: &usize) {241		self.write_padleft(&usize::to_be_bytes(*value))242	}243244	pub fn write_subresult(&mut self, result: Self) {245		self.dynamic_part.push((self.static_part.len(), result));246		// Empty block, to be filled later247		self.write_padleft(&[]);248	}249250	pub fn memory(&mut self, value: &[u8]) {251		let mut sub = Self::new();252		sub.write_usize(&value.len());253		for chunk in value.chunks(ABI_ALIGNMENT) {254			sub.write_padright(chunk);255		}256		self.write_subresult(sub);257	}258259	pub fn string(&mut self, value: &str) {260		self.memory(value.as_bytes())261	}262263	pub fn bytes(&mut self, value: &[u8]) {264		self.memory(value)265	}266267	pub fn finish(mut self) -> Vec<u8> {268		for (static_offset, part) in self.dynamic_part {269			let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);270271			let encoded_dynamic_offset = usize::to_be_bytes(part_offset);272			self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()273				..static_offset + ABI_ALIGNMENT]274				.copy_from_slice(&encoded_dynamic_offset);275			self.static_part.extend(part.finish())276		}277		self.static_part278	}279}280281pub trait AbiRead<T> {282	fn abi_read(&mut self) -> Result<T>;283}284285macro_rules! impl_abi_readable {286	($ty:ty, $method:ident) => {287		impl AbiRead<$ty> for AbiReader<'_> {288			fn abi_read(&mut self) -> Result<$ty> {289				self.$method()290			}291		}292	};293}294295impl_abi_readable!(u8, uint8);296impl_abi_readable!(u32, uint32);297impl_abi_readable!(u64, uint64);298impl_abi_readable!(u128, uint128);299impl_abi_readable!(U256, uint256);300impl_abi_readable!([u8; 4], bytes4);301impl_abi_readable!(H160, address);302impl_abi_readable!(Vec<u8>, bytes);303impl_abi_readable!(bool, bool);304impl_abi_readable!(string, string);305306mod sealed {307	/// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead308	pub trait CanBePlacedInVec {}309}310311impl sealed::CanBePlacedInVec for U256 {}312impl sealed::CanBePlacedInVec for string {}313impl sealed::CanBePlacedInVec for H160 {}314315impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>316where317	Self: AbiRead<R>,318{319	fn abi_read(&mut self) -> Result<Vec<R>> {320		let mut sub = self.subresult()?;321		let size = sub.read_usize()?;322		sub.subresult_offset = sub.offset;323		let mut out = Vec::with_capacity(size);324		for _ in 0..size {325			out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);326		}327		Ok(out)328	}329}330331macro_rules! impl_tuples {332	($($ident:ident)+) => {333		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}334		impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>335		where336			$(Self: AbiRead<$ident>),+337		{338			fn abi_read(&mut self) -> Result<($($ident,)+)> {339				let mut subresult = self.subresult()?;340				Ok((341					$(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+342				))343			}344		}345		#[allow(non_snake_case)]346		impl<$($ident),+> AbiWrite for &($($ident,)+)347		where348			$($ident: AbiWrite,)+349		{350			fn abi_write(&self, writer: &mut AbiWriter) {351				let ($($ident,)+) = self;352				$($ident.abi_write(writer);)+353			}354		}355	};356}357358impl_tuples! {A}359impl_tuples! {A B}360impl_tuples! {A B C}361impl_tuples! {A B C D}362impl_tuples! {A B C D E}363impl_tuples! {A B C D E F}364impl_tuples! {A B C D E F G}365impl_tuples! {A B C D E F G H}366impl_tuples! {A B C D E F G H I}367impl_tuples! {A B C D E F G H I J}368369pub trait AbiWrite {370	fn abi_write(&self, writer: &mut AbiWriter);371	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {372		let mut writer = AbiWriter::new();373		self.abi_write(&mut writer);374		Ok(writer.into())375	}376}377378impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {379	// this particular AbiWrite implementation should be split to another trait,380	// which only implements [`to_result`]381	//382	// But due to lack of specialization feature in stable Rust, we can't have383	// blanket impl of this trait `for T where T: AbiWrite`, so here we abusing384	// default trait methods for it385	fn abi_write(&self, _writer: &mut AbiWriter) {386		debug_assert!(false, "shouldn't be called, see comment")387	}388	fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {389		match self {390			Ok(v) => Ok(WithPostDispatchInfo {391				post_info: v.post_info.clone(),392				data: {393					let mut out = AbiWriter::new();394					v.data.abi_write(&mut out);395					out396				},397			}),398			Err(e) => Err(e.clone()),399		}400	}401}402403macro_rules! impl_abi_writeable {404	($ty:ty, $method:ident) => {405		impl AbiWrite for $ty {406			fn abi_write(&self, writer: &mut AbiWriter) {407				writer.$method(&self)408			}409		}410	};411}412413impl_abi_writeable!(u8, uint8);414impl_abi_writeable!(u32, uint32);415impl_abi_writeable!(u128, uint128);416impl_abi_writeable!(U256, uint256);417impl_abi_writeable!(H160, address);418impl_abi_writeable!(bool, bool);419impl_abi_writeable!(&str, string);420impl AbiWrite for &string {421	fn abi_write(&self, writer: &mut AbiWriter) {422		writer.string(self)423	}424}425impl AbiWrite for &Vec<u8> {426	fn abi_write(&self, writer: &mut AbiWriter) {427		writer.bytes(self)428	}429}430431impl AbiWrite for () {432	fn abi_write(&self, _writer: &mut AbiWriter) {}433}434435#[macro_export]436macro_rules! abi_decode {437	($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {438		$(439			let $name = $reader.$typ()?;440		)+441	}442}443#[macro_export]444macro_rules! abi_encode {445	($($typ:ident($value:expr)),* $(,)?) => {{446		#[allow(unused_mut)]447		let mut writer = ::evm_coder::abi::AbiWriter::new();448		$(449			writer.$typ($value);450		)*451		writer452	}};453	(call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{454		#[allow(unused_mut)]455		let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);456		$(457			writer.$typ($value);458		)*459		writer460	}}461}462463#[cfg(test)]464pub mod test {465	use crate::{466		abi::AbiRead,467		types::{string, uint256},468	};469470	use super::{AbiReader, AbiWriter};471	use hex_literal::hex;472473	#[test]474	fn dynamic_after_static() {475		let mut encoder = AbiWriter::new();476		encoder.bool(&true);477		encoder.string("test");478		let encoded = encoder.finish();479480		let mut encoder = AbiWriter::new();481		encoder.bool(&true);482		// Offset to subresult483		encoder.uint32(&(32 * 2));484		// Len of "test"485		encoder.uint32(&4);486		encoder.write_padright(&[b't', b'e', b's', b't']);487		let alternative_encoded = encoder.finish();488489		assert_eq!(encoded, alternative_encoded);490491		let mut decoder = AbiReader::new(&encoded);492		assert_eq!(decoder.bool().unwrap(), true);493		assert_eq!(decoder.string().unwrap(), "test");494	}495496	#[test]497	fn mint_sample() {498		let (call, mut decoder) = AbiReader::new_call(&hex!(499			"500				50bb4e7f501				000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374502				0000000000000000000000000000000000000000000000000000000000000001503				0000000000000000000000000000000000000000000000000000000000000060504				0000000000000000000000000000000000000000000000000000000000000008505				5465737420555249000000000000000000000000000000000000000000000000506			"507		))508		.unwrap();509		assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));510		assert_eq!(511			format!("{:?}", decoder.address().unwrap()),512			"0xad2c0954693c2b5404b7e50967d3481bea432374"513		);514		assert_eq!(decoder.uint32().unwrap(), 1);515		assert_eq!(decoder.string().unwrap(), "Test URI");516	}517518	#[test]519	fn mint_bulk() {520		let (call, mut decoder) = AbiReader::new_call(&hex!(521			"522				36543006523				00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address524				0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]525				0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]526527				0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem528				00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem529				0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem530531				0000000000000000000000000000000000000000000000000000000000000001 // first token id?   					#60532				0000000000000000000000000000000000000000000000000000000000000040 // offset of string533				000000000000000000000000000000000000000000000000000000000000000a // size of string534				5465737420555249203000000000000000000000000000000000000000000000 // string535536				000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11?			#e0537				0000000000000000000000000000000000000000000000000000000000000040 // offset of string538				000000000000000000000000000000000000000000000000000000000000000a // size of string539				5465737420555249203100000000000000000000000000000000000000000000 // string540541				000000000000000000000000000000000000000000000000000000000000000c // third token id?  Why ==12?			#160542				0000000000000000000000000000000000000000000000000000000000000040 // offset of string543				000000000000000000000000000000000000000000000000000000000000000a // size of string544				5465737420555249203200000000000000000000000000000000000000000000 // string545			"546		))547		.unwrap();548		assert_eq!(call, u32::to_be_bytes(0x36543006));549		let _ = decoder.address().unwrap();550		let data =551			<AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();552		assert_eq!(553			data,554			vec![555				(1.into(), "Test URI 0".to_string()),556				(11.into(), "Test URI 1".to_string()),557				(12.into(), "Test URI 2".to_string())558			]559		);560	}561}
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
--- 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)?;