git.delta.rocks / unique-network / refs/commits / 78becc5a95dd

difftreelog

Merge pull request #433 from UniqueNetwork/doc/evm-coder

Yaroslav Bolyukin2022-08-25parents: #fb94d1c #625c503.patch.diff
in: master
Document evm related crates

58 files changed

modified.maintain/frame-weight-template.hbsdiffbeforeafterboth
--- a/.maintain/frame-weight-template.hbs
+++ b/.maintain/frame-weight-template.hbs
@@ -14,6 +14,7 @@
 #![cfg_attr(rustfmt, rustfmt_skip)]
 #![allow(unused_parens)]
 #![allow(unused_imports)]
+#![allow(missing_docs)]
 #![allow(clippy::unnecessary_cast)]
 
 use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1772,41 +1772,6 @@
 ]
 
 [[package]]
-name = "darling"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
-dependencies = [
- "darling_core",
- "darling_macro",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn",
-]
-
-[[package]]
-name = "darling_macro"
-version = "0.13.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
-dependencies = [
- "darling_core",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "data-encoding"
 version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2207,7 +2172,7 @@
 version = "0.1.1"
 dependencies = [
  "ethereum",
- "evm-coder-macros",
+ "evm-coder-procedural",
  "evm-core",
  "hex",
  "hex-literal",
@@ -2216,15 +2181,14 @@
 ]
 
 [[package]]
-name = "evm-coder-macros"
-version = "0.1.0"
+name = "evm-coder-procedural"
+version = "0.2.0"
 dependencies = [
  "Inflector",
- "darling",
  "hex",
  "proc-macro2",
  "quote",
- "sha3 0.9.1",
+ "sha3 0.10.2",
  "syn",
 ]
 
@@ -3427,12 +3391,6 @@
  "wasm-bindgen",
  "winapi",
 ]
-
-[[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "idna"
deletedcrates/evm-coder-macros/Cargo.tomldiffbeforeafterboth
--- a/crates/evm-coder-macros/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "evm-coder-macros"
-version = "0.1.0"
-license = "GPLv3"
-edition = "2021"
-
-[lib]
-proc-macro = true
-
-[dependencies]
-sha3 = "0.9.1"
-quote = "1.0"
-proc-macro2 = "1.0"
-syn = { version = "1.0", features = ["full"] }
-hex = "0.4.3"
-Inflector = "0.11.4"
-darling = "0.13.0"
deletedcrates/evm-coder-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/lib.rs
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-#![allow(dead_code)]
-
-use darling::FromMeta;
-use inflector::cases;
-use proc_macro::TokenStream;
-use quote::quote;
-use sha3::{Digest, Keccak256};
-use syn::{
-	AttributeArgs, DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments,
-	PathSegment, Type, parse_macro_input, spanned::Spanned,
-};
-
-mod solidity_interface;
-mod to_log;
-
-fn fn_selector_str(input: &str) -> u32 {
-	let mut hasher = Keccak256::new();
-	hasher.update(input.as_bytes());
-	let result = hasher.finalize();
-
-	let mut selector_bytes = [0; 4];
-	selector_bytes.copy_from_slice(&result[0..4]);
-
-	u32::from_be_bytes(selector_bytes)
-}
-
-/// Returns solidity function selector (first 4 bytes of hash) by its
-/// textual representation
-///
-/// ```ignore
-/// use evm_coder_macros::fn_selector;
-///
-/// assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);
-/// ```
-#[proc_macro]
-pub fn fn_selector(input: TokenStream) -> TokenStream {
-	let input = input.to_string().replace(' ', "");
-	let selector = fn_selector_str(&input);
-
-	(quote! {
-		#selector
-	})
-	.into()
-}
-
-fn event_selector_str(input: &str) -> [u8; 32] {
-	let mut hasher = Keccak256::new();
-	hasher.update(input.as_bytes());
-	let result = hasher.finalize();
-
-	let mut selector_bytes = [0; 32];
-	selector_bytes.copy_from_slice(&result[0..32]);
-	selector_bytes
-}
-
-/// Returns solidity topic (hash) by its textual representation
-///
-/// ```ignore
-/// use evm_coder_macros::event_topic;
-///
-/// assert_eq!(
-///     format!("{:x}", event_topic!(Transfer(address, address, uint256))),
-///     "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
-/// );
-/// ```
-#[proc_macro]
-pub fn event_topic(stream: TokenStream) -> TokenStream {
-	let input = stream.to_string().replace(' ', "");
-	let selector_bytes = event_selector_str(&input);
-
-	(quote! {
-		::primitive_types::H256([#(
-			#selector_bytes,
-		)*])
-	})
-	.into()
-}
-
-fn parse_path(ty: &Type) -> syn::Result<&Path> {
-	match &ty {
-		syn::Type::Path(pat) => {
-			if let Some(qself) = &pat.qself {
-				return Err(syn::Error::new(qself.ty.span(), "no receiver expected"));
-			}
-			Ok(&pat.path)
-		}
-		_ => Err(syn::Error::new(ty.span(), "expected ty to be path")),
-	}
-}
-
-fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> {
-	if path.segments.len() != 1 {
-		return Err(syn::Error::new(
-			path.span(),
-			"expected path to have only segment",
-		));
-	}
-	let last_segment = &path.segments.last().unwrap();
-	Ok(last_segment)
-}
-
-fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> {
-	match pat {
-		Pat::Ident(i) => Ok(&i.ident),
-		_ => Err(syn::Error::new(pat.span(), "expected pat ident")),
-	}
-}
-
-fn parse_ident_from_segment(segment: &PathSegment, allow_generics: bool) -> syn::Result<&Ident> {
-	if segment.arguments != PathArguments::None && !allow_generics {
-		return Err(syn::Error::new(
-			segment.arguments.span(),
-			"unexpected generic type",
-		));
-	}
-	Ok(&segment.ident)
-}
-
-fn parse_ident_from_path(path: &Path, allow_generics: bool) -> syn::Result<&Ident> {
-	let segment = parse_path_segment(path)?;
-	parse_ident_from_segment(segment, allow_generics)
-}
-
-fn parse_ident_from_type(ty: &Type, allow_generics: bool) -> syn::Result<&Ident> {
-	let path = parse_path(ty)?;
-	parse_ident_from_path(path, allow_generics)
-}
-
-// Gets T out of Result<T>
-fn parse_result_ok(ty: &Type) -> syn::Result<&Type> {
-	let path = parse_path(ty)?;
-	let segment = parse_path_segment(path)?;
-
-	if segment.ident != "Result" {
-		return Err(syn::Error::new(
-			ty.span(),
-			"expected Result as return type (no renamed aliases allowed)",
-		));
-	}
-	let args = match &segment.arguments {
-		PathArguments::AngleBracketed(e) => e,
-		_ => {
-			return Err(syn::Error::new(
-				segment.arguments.span(),
-				"missing Result generics",
-			))
-		}
-	};
-
-	let args = &args.args;
-	let arg = args.first().unwrap();
-
-	let ty = match arg {
-		GenericArgument::Type(ty) => ty,
-		_ => {
-			return Err(syn::Error::new(
-				arg.span(),
-				"expected first generic to be type",
-			))
-		}
-	};
-
-	Ok(ty)
-}
-
-fn pascal_ident_to_call(ident: &Ident) -> Ident {
-	let name = format!("{}Call", ident);
-	Ident::new(&name, ident.span())
-}
-fn snake_ident_to_pascal(ident: &Ident) -> Ident {
-	let name = ident.to_string();
-	let name = cases::pascalcase::to_pascal_case(&name);
-	Ident::new(&name, ident.span())
-}
-fn snake_ident_to_screaming(ident: &Ident) -> Ident {
-	let name = ident.to_string();
-	let name = cases::screamingsnakecase::to_screaming_snake_case(&name);
-	Ident::new(&name, ident.span())
-}
-fn pascal_ident_to_snake_call(ident: &Ident) -> Ident {
-	let name = ident.to_string();
-	let name = cases::snakecase::to_snake_case(&name);
-	let name = format!("call_{}", name);
-	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")?)
-///     }
-/// }
-/// ```
-#[proc_macro_attribute]
-pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {
-	let args = parse_macro_input!(args as AttributeArgs);
-	let args = solidity_interface::InterfaceInfo::from_list(&args).unwrap();
-
-	let input: ItemImpl = match syn::parse(stream) {
-		Ok(t) => t,
-		Err(e) => return e.to_compile_error().into(),
-	};
-
-	let expanded = match solidity_interface::SolidityInterface::try_from(args, &input) {
-		Ok(v) => v.expand(),
-		Err(e) => e.to_compile_error(),
-	};
-
-	(quote! {
-		#input
-
-		#expanded
-	})
-	.into()
-}
-
-#[proc_macro_attribute]
-pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {
-	stream
-}
-#[proc_macro_attribute]
-pub fn weight(_args: TokenStream, stream: TokenStream) -> TokenStream {
-	stream
-}
-
-/// ## Syntax
-///
-/// `#[indexed]`
-/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
-#[proc_macro_derive(ToLog, attributes(indexed))]
-pub fn to_log(value: TokenStream) -> TokenStream {
-	let input = parse_macro_input!(value as DeriveInput);
-
-	match to_log::Events::try_from(&input) {
-		Ok(e) => e.expand(),
-		Err(e) => e.to_compile_error(),
-	}
-	.into()
-}
deletedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ /dev/null
@@ -1,1005 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-#![allow(dead_code)]
-
-use quote::quote;
-use darling::{FromMeta, ToTokens};
-use inflector::cases;
-use std::fmt::Write;
-use syn::{
-	Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,
-	MetaNameValue, NestedMeta, PatType, Path, PathArguments, ReturnType, Type, spanned::Spanned,
-	parse_str,
-};
-
-use crate::{
-	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,
-	parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,
-	snake_ident_to_screaming,
-};
-
-struct Is {
-	name: Ident,
-	pascal_call_name: Ident,
-	snake_call_name: Ident,
-	via: Option<(Type, Ident)>,
-}
-impl Is {
-	fn new_via(path: &Path, via: Option<(Type, Ident)>) -> syn::Result<Self> {
-		let name = parse_ident_from_path(path, false)?.clone();
-		Ok(Self {
-			pascal_call_name: pascal_ident_to_call(&name),
-			snake_call_name: pascal_ident_to_snake_call(&name),
-			name,
-			via,
-		})
-	}
-	fn new(path: &Path) -> syn::Result<Self> {
-		Self::new_via(path, None)
-	}
-
-	fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		let pascal_call_name = &self.pascal_call_name;
-		quote! {
-			#name(#pascal_call_name #gen_ref)
-		}
-	}
-
-	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
-		let pascal_call_name = &self.pascal_call_name;
-		quote! {
-			interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());
-		}
-	}
-
-	fn expand_supports_interface(
-		&self,
-		generics: &proc_macro2::TokenStream,
-	) -> proc_macro2::TokenStream {
-		let pascal_call_name = &self.pascal_call_name;
-		quote! {
-			<#pascal_call_name #generics>::supports_interface(interface_id)
-		}
-	}
-
-	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		quote! {
-			Self::#name(call) => call.weight()
-		}
-	}
-
-	fn expand_variant_call(
-		&self,
-		call_name: &proc_macro2::Ident,
-		generics: &proc_macro2::TokenStream,
-	) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		let pascal_call_name = &self.pascal_call_name;
-		let via_typ = self
-			.via
-			.as_ref()
-			.map(|(t, _)| quote! {#t})
-			.unwrap_or_else(|| quote! {Self});
-		let via_map = self
-			.via
-			.as_ref()
-			.map(|(_, i)| quote! {.#i()})
-			.unwrap_or_default();
-		quote! {
-			#call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {
-				call,
-				caller: c.caller,
-				value: c.value,
-			})
-		}
-	}
-
-	fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		let pascal_call_name = &self.pascal_call_name;
-		quote! {
-			if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {
-				return Ok(Some(Self::#name(parsed_call)))
-			}
-		}
-	}
-
-	fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
-		let pascal_call_name = &self.pascal_call_name;
-		quote! {
-			<#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);
-		}
-	}
-
-	fn expand_event_generator(&self) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		quote! {
-			#name::generate_solidity_interface(tc, is_impl);
-		}
-	}
-}
-
-#[derive(Default)]
-struct IsList(Vec<Is>);
-impl FromMeta for IsList {
-	fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {
-		let mut out = Vec::new();
-		for item in items {
-			match item {
-				NestedMeta::Meta(Meta::Path(path)) => out.push(Is::new(path)?),
-				// TODO: replace meta parsing with manual
-				NestedMeta::Meta(Meta::List(list))
-					if list.path.is_ident("via") && list.nested.len() == 3 =>
-				{
-					let mut data = list.nested.iter();
-					let typ = match data.next().expect("len == 3") {
-						NestedMeta::Lit(Lit::Str(s)) => {
-							let v = s.value();
-							let typ: Type = parse_str(&v)?;
-							typ
-						}
-						_ => {
-							return Err(syn::Error::new(
-								item.span(),
-								"via typ should be type in string",
-							)
-							.into())
-						}
-					};
-					let via = match data.next().expect("len == 3") {
-						NestedMeta::Meta(Meta::Path(path)) => path
-							.get_ident()
-							.ok_or_else(|| syn::Error::new(item.span(), "via should be ident"))?,
-						_ => return Err(syn::Error::new(item.span(), "via should be ident").into()),
-					};
-					let path = match data.next().expect("len == 3") {
-						NestedMeta::Meta(Meta::Path(path)) => path,
-						_ => return Err(syn::Error::new(item.span(), "path should be path").into()),
-					};
-
-					out.push(Is::new_via(path, Some((typ, via.clone())))?)
-				}
-				_ => {
-					return Err(syn::Error::new(
-						item.span(),
-						"expected either Name or via(\"Type\", getter, Name)",
-					)
-					.into())
-				}
-			}
-		}
-		Ok(Self(out))
-	}
-}
-
-#[derive(FromMeta)]
-pub struct InterfaceInfo {
-	name: Ident,
-	#[darling(default)]
-	is: IsList,
-	#[darling(default)]
-	inline_is: IsList,
-	#[darling(default)]
-	events: IsList,
-}
-
-#[derive(FromMeta)]
-struct MethodInfo {
-	#[darling(default)]
-	rename_selector: Option<String>,
-}
-
-enum AbiType {
-	// type
-	Plain(Ident),
-	// (type1,type2)
-	Tuple(Vec<AbiType>),
-	// type[]
-	Vec(Box<AbiType>),
-	// type[20]
-	Array(Box<AbiType>, usize),
-}
-impl AbiType {
-	fn try_from(value: &Type) -> syn::Result<Self> {
-		let value = Self::try_maybe_special_from(value)?;
-		if value.is_special() {
-			return Err(syn::Error::new(value.span(), "unexpected special type"));
-		}
-		Ok(value)
-	}
-	fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {
-		match value {
-			Type::Array(arr) => {
-				let wrapped = AbiType::try_from(&arr.elem)?;
-				match &arr.len {
-					Expr::Lit(l) => match &l.lit {
-						Lit::Int(i) => {
-							let num = i.base10_parse::<usize>()?;
-							Ok(AbiType::Array(Box::new(wrapped), num as usize))
-						}
-						_ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
-					},
-					_ => Err(syn::Error::new(arr.len.span(), "should be literal")),
-				}
-			}
-			Type::Path(_) => {
-				let path = parse_path(value)?;
-				let segment = parse_path_segment(path)?;
-				if segment.ident == "Vec" {
-					let args = match &segment.arguments {
-						PathArguments::AngleBracketed(e) => e,
-						_ => {
-							return Err(syn::Error::new(
-								segment.arguments.span(),
-								"missing Vec generic",
-							))
-						}
-					};
-					let args = &args.args;
-					if args.len() != 1 {
-						return Err(syn::Error::new(
-							args.span(),
-							"expected only one generic for vec",
-						));
-					}
-					let arg = args.first().unwrap();
-
-					let ty = match arg {
-						GenericArgument::Type(ty) => ty,
-						_ => {
-							return Err(syn::Error::new(
-								arg.span(),
-								"expected first generic to be type",
-							))
-						}
-					};
-
-					let wrapped = AbiType::try_from(ty)?;
-					Ok(Self::Vec(Box::new(wrapped)))
-				} else {
-					if !segment.arguments.is_empty() {
-						return Err(syn::Error::new(
-							segment.arguments.span(),
-							"unexpected generic arguments for non-vec type",
-						));
-					}
-					Ok(Self::Plain(segment.ident.clone()))
-				}
-			}
-			Type::Tuple(t) => {
-				let mut out = Vec::with_capacity(t.elems.len());
-				for el in t.elems.iter() {
-					out.push(AbiType::try_from(el)?)
-				}
-				Ok(Self::Tuple(out))
-			}
-			_ => Err(syn::Error::new(
-				value.span(),
-				"unexpected type, only arrays, plain types and tuples are supported",
-			)),
-		}
-	}
-	fn is_value(&self) -> bool {
-		matches!(self, Self::Plain(v) if v == "value")
-	}
-	fn is_caller(&self) -> bool {
-		matches!(self, Self::Plain(v) if v == "caller")
-	}
-	fn is_special(&self) -> bool {
-		self.is_caller() || self.is_value()
-	}
-	fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {
-		match self {
-			AbiType::Plain(t) => {
-				write!(buf, "{}", t)
-			}
-			AbiType::Tuple(t) => {
-				write!(buf, "(")?;
-				for (i, t) in t.iter().enumerate() {
-					if i != 0 {
-						write!(buf, ",")?;
-					}
-					t.selector_ty_buf(buf)?;
-				}
-				write!(buf, ")")
-			}
-			AbiType::Vec(v) => {
-				v.selector_ty_buf(buf)?;
-				write!(buf, "[]")
-			}
-			AbiType::Array(v, len) => {
-				v.selector_ty_buf(buf)?;
-				write!(buf, "[{}]", len)
-			}
-		}
-	}
-	fn selector_ty(&self) -> String {
-		let mut out = String::new();
-		self.selector_ty_buf(&mut out).expect("no fmt error");
-		out
-	}
-}
-impl ToTokens for AbiType {
-	fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
-		match self {
-			AbiType::Plain(t) => tokens.extend(quote! {#t}),
-			AbiType::Tuple(t) => {
-				tokens.extend(quote! {(
-					#(#t),*
-				)});
-			}
-			AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),
-			AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),
-		}
-	}
-}
-
-struct MethodArg {
-	name: Ident,
-	camel_name: String,
-	ty: AbiType,
-}
-impl MethodArg {
-	fn try_from(value: &PatType) -> syn::Result<Self> {
-		let name = parse_ident_from_pat(&value.pat)?.clone();
-		Ok(Self {
-			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
-			name,
-			ty: AbiType::try_maybe_special_from(&value.ty)?,
-		})
-	}
-	fn is_value(&self) -> bool {
-		self.ty.is_value()
-	}
-	fn is_caller(&self) -> bool {
-		self.ty.is_caller()
-	}
-	fn is_special(&self) -> bool {
-		self.ty.is_special()
-	}
-	fn selector_ty(&self) -> String {
-		assert!(!self.is_special());
-		self.ty.selector_ty()
-	}
-
-	fn expand_call_def(&self) -> proc_macro2::TokenStream {
-		assert!(!self.is_special());
-		let name = &self.name;
-		let ty = &self.ty;
-
-		quote! {
-			#name: #ty
-		}
-	}
-
-	fn expand_parse(&self) -> proc_macro2::TokenStream {
-		assert!(!self.is_special());
-		let name = &self.name;
-		quote! {
-			#name: reader.abi_read()?
-		}
-	}
-
-	fn expand_call_arg(&self) -> proc_macro2::TokenStream {
-		if self.is_value() {
-			quote! {
-				c.value.clone()
-			}
-		} else if self.is_caller() {
-			quote! {
-				c.caller.clone()
-			}
-		} else {
-			let name = &self.name;
-			quote! {
-				#name
-			}
-		}
-	}
-
-	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
-		let camel_name = &self.camel_name.to_string();
-		let ty = &self.ty;
-		quote! {
-			<NamedArgument<#ty>>::new(#camel_name)
-		}
-	}
-}
-
-#[derive(PartialEq)]
-enum Mutability {
-	Mutable,
-	View,
-	Pure,
-}
-
-pub struct WeightAttr(syn::Expr);
-
-mod keyword {
-	syn::custom_keyword!(weight);
-}
-
-impl syn::parse::Parse for WeightAttr {
-	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-		input.parse::<syn::Token![#]>()?;
-		let content;
-		syn::bracketed!(content in input);
-		content.parse::<keyword::weight>()?;
-
-		let weight_content;
-		syn::parenthesized!(weight_content in content);
-		Ok(WeightAttr(weight_content.parse::<syn::Expr>()?))
-	}
-}
-
-struct Method {
-	name: Ident,
-	camel_name: String,
-	pascal_name: Ident,
-	screaming_name: Ident,
-	selector_str: String,
-	selector: u32,
-	args: Vec<MethodArg>,
-	has_normal_args: bool,
-	mutability: Mutability,
-	result: Type,
-	weight: Option<Expr>,
-	docs: Vec<String>,
-}
-impl Method {
-	fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {
-		let mut info = MethodInfo {
-			rename_selector: None,
-		};
-		let mut docs = Vec::new();
-		let mut weight = None;
-		for attr in &value.attrs {
-			let ident = parse_ident_from_path(&attr.path, false)?;
-			if ident == "solidity" {
-				let args = attr.parse_meta().unwrap();
-				info = MethodInfo::from_meta(&args).unwrap();
-			} else if ident == "doc" {
-				let args = attr.parse_meta().unwrap();
-				let value = match args {
-					Meta::NameValue(MetaNameValue {
-						lit: Lit::Str(str), ..
-					}) => str.value(),
-					_ => unreachable!(),
-				};
-				docs.push(value);
-			} else if ident == "weight" {
-				weight = Some(syn::parse2::<WeightAttr>(attr.to_token_stream())?.0);
-			}
-		}
-		let ident = &value.sig.ident;
-		let ident_str = ident.to_string();
-		if !cases::snakecase::is_snake_case(&ident_str) {
-			return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));
-		}
-
-		let mut mutability = Mutability::Pure;
-
-		if let Some(FnArg::Receiver(receiver)) = value
-			.sig
-			.inputs
-			.iter()
-			.find(|arg| matches!(arg, FnArg::Receiver(_)))
-		{
-			if receiver.reference.is_none() {
-				return Err(syn::Error::new(
-					receiver.span(),
-					"receiver should be by ref",
-				));
-			}
-			if receiver.mutability.is_some() {
-				mutability = Mutability::Mutable;
-			} else {
-				mutability = Mutability::View;
-			}
-		}
-		let mut args = Vec::new();
-		for typ in value
-			.sig
-			.inputs
-			.iter()
-			.filter(|arg| matches!(arg, FnArg::Typed(_)))
-		{
-			let typ = match typ {
-				FnArg::Typed(typ) => typ,
-				_ => unreachable!(),
-			};
-			args.push(MethodArg::try_from(typ)?);
-		}
-
-		if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {
-			return Err(syn::Error::new(
-				args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),
-				"payable function should be mutable",
-			));
-		}
-
-		let result = match &value.sig.output {
-			ReturnType::Type(_, ty) => ty,
-			_ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),
-		};
-		let result = parse_result_ok(result)?;
-
-		let camel_name = info
-			.rename_selector
-			.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));
-		let mut selector_str = camel_name.clone();
-		selector_str.push('(');
-		let mut has_normal_args = false;
-		for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {
-			if i != 0 {
-				selector_str.push(',');
-			}
-			write!(selector_str, "{}", arg.selector_ty()).unwrap();
-			has_normal_args = true;
-		}
-		selector_str.push(')');
-		let selector = fn_selector_str(&selector_str);
-
-		Ok(Self {
-			name: ident.clone(),
-			camel_name,
-			pascal_name: snake_ident_to_pascal(ident),
-			screaming_name: snake_ident_to_screaming(ident),
-			selector_str,
-			selector,
-			args,
-			has_normal_args,
-			mutability,
-			result: result.clone(),
-			weight,
-			docs,
-		})
-	}
-	fn expand_call_def(&self) -> proc_macro2::TokenStream {
-		let defs = self
-			.args
-			.iter()
-			.filter(|a| !a.is_special())
-			.map(|a| a.expand_call_def());
-		let pascal_name = &self.pascal_name;
-
-		if self.has_normal_args {
-			quote! {
-				#pascal_name {
-					#(
-						#defs,
-					)*
-				}
-			}
-		} else {
-			quote! {#pascal_name}
-		}
-	}
-
-	fn expand_const(&self) -> proc_macro2::TokenStream {
-		let screaming_name = &self.screaming_name;
-		let selector = u32::to_be_bytes(self.selector);
-		let selector_str = &self.selector_str;
-		quote! {
-			#[doc = #selector_str]
-			const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];
-		}
-	}
-
-	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
-		let screaming_name = &self.screaming_name;
-		quote! {
-			interface_id ^= u32::from_be_bytes(Self::#screaming_name);
-		}
-	}
-
-	fn expand_parse(&self) -> proc_macro2::TokenStream {
-		let pascal_name = &self.pascal_name;
-		let screaming_name = &self.screaming_name;
-		if self.has_normal_args {
-			let parsers = self
-				.args
-				.iter()
-				.filter(|a| !a.is_special())
-				.map(|a| a.expand_parse());
-			quote! {
-				Self::#screaming_name => return Ok(Some(Self::#pascal_name {
-					#(
-						#parsers,
-					)*
-				}))
-			}
-		} else {
-			quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }
-		}
-	}
-
-	fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {
-		let pascal_name = &self.pascal_name;
-		let name = &self.name;
-
-		let matcher = if self.has_normal_args {
-			let names = self
-				.args
-				.iter()
-				.filter(|a| !a.is_special())
-				.map(|a| &a.name);
-
-			quote! {{
-				#(
-					#names,
-				)*
-			}}
-		} else {
-			quote! {}
-		};
-
-		let receiver = match self.mutability {
-			Mutability::Mutable | Mutability::View => quote! {self.},
-			Mutability::Pure => quote! {Self::},
-		};
-		let args = self.args.iter().map(|a| a.expand_call_arg());
-
-		quote! {
-			#call_name::#pascal_name #matcher => {
-				let result = #receiver #name(
-					#(
-						#args,
-					)*
-				)?;
-				(&result).to_result()
-			}
-		}
-	}
-
-	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
-		let pascal_name = &self.pascal_name;
-		if let Some(weight) = &self.weight {
-			let matcher = if self.has_normal_args {
-				let names = self
-					.args
-					.iter()
-					.filter(|a| !a.is_special())
-					.map(|a| &a.name);
-
-				quote! {{
-					#(
-						#names,
-					)*
-				}}
-			} else {
-				quote! {}
-			};
-			quote! {
-				Self::#pascal_name #matcher => (#weight).into()
-			}
-		} else {
-			let matcher = if self.has_normal_args {
-				quote! {{..}}
-			} else {
-				quote! {}
-			};
-			quote! {
-				Self::#pascal_name #matcher => ().into()
-			}
-		}
-	}
-
-	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
-		let camel_name = &self.camel_name;
-		let mutability = match self.mutability {
-			Mutability::Mutable => quote! {SolidityMutability::Mutable},
-			Mutability::View => quote! { SolidityMutability::View },
-			Mutability::Pure => quote! {SolidityMutability::Pure},
-		};
-		let result = &self.result;
-
-		let args = self
-			.args
-			.iter()
-			.filter(|a| !a.is_special())
-			.map(MethodArg::expand_solidity_argument);
-		let docs = self.docs.iter();
-		let selector = format!("{} {:0>8x}", self.selector_str, self.selector);
-
-		quote! {
-			SolidityFunction {
-				docs: &[#(#docs),*],
-				selector: #selector,
-				name: #camel_name,
-				mutability: #mutability,
-				args: (
-					#(
-						#args,
-					)*
-				),
-				result: <UnnamedArgument<#result>>::default(),
-			}
-		}
-	}
-}
-
-fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {
-	if gen.params.is_empty() {
-		return quote! {};
-	}
-	let params = gen.params.iter().map(|p| match p {
-		syn::GenericParam::Type(id) => {
-			let v = &id.ident;
-			quote! {#v}
-		}
-		syn::GenericParam::Lifetime(lt) => {
-			let v = &lt.lifetime;
-			quote! {#v}
-		}
-		syn::GenericParam::Const(c) => {
-			let i = &c.ident;
-			quote! {#i}
-		}
-	});
-	quote! { #(#params),* }
-}
-fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {
-	if gen.params.is_empty() {
-		return quote! {};
-	}
-	let list = generics_list(gen);
-	quote! { <#list> }
-}
-fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {
-	let list = generics_list(gen);
-	if gen.params.len() == 1 {
-		quote! {#list}
-	} else {
-		quote! { (#list) }
-	}
-}
-
-pub struct SolidityInterface {
-	generics: Generics,
-	name: Box<syn::Type>,
-	info: InterfaceInfo,
-	methods: Vec<Method>,
-}
-impl SolidityInterface {
-	pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {
-		let mut methods = Vec::new();
-
-		for item in &value.items {
-			if let ImplItem::Method(method) = item {
-				methods.push(Method::try_from(method)?)
-			}
-		}
-		Ok(Self {
-			generics: value.generics.clone(),
-			name: value.self_ty.clone(),
-			info,
-			methods,
-		})
-	}
-	pub fn expand(self) -> proc_macro2::TokenStream {
-		let name = self.name;
-
-		let solidity_name = self.info.name.to_string();
-		let call_name = pascal_ident_to_call(&self.info.name);
-		let generics = self.generics;
-		let gen_ref = generics_reference(&generics);
-		let gen_data = generics_data(&generics);
-		let gen_where = &generics.where_clause;
-
-		let call_sub = self
-			.info
-			.inline_is
-			.0
-			.iter()
-			.chain(self.info.is.0.iter())
-			.map(|c| Is::expand_call_def(c, &gen_ref));
-		let call_parse = self
-			.info
-			.inline_is
-			.0
-			.iter()
-			.chain(self.info.is.0.iter())
-			.map(|is| Is::expand_parse(is, &gen_ref));
-		let call_variants = self
-			.info
-			.inline_is
-			.0
-			.iter()
-			.chain(self.info.is.0.iter())
-			.map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));
-		let weight_variants = self
-			.info
-			.inline_is
-			.0
-			.iter()
-			.chain(self.info.is.0.iter())
-			.map(Is::expand_variant_weight);
-
-		let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);
-		let supports_interface = self
-			.info
-			.is
-			.0
-			.iter()
-			.map(|is| Is::expand_supports_interface(is, &gen_ref));
-
-		let calls = self.methods.iter().map(Method::expand_call_def);
-		let consts = self.methods.iter().map(Method::expand_const);
-		let interface_id = self.methods.iter().map(Method::expand_interface_id);
-		let parsers = self.methods.iter().map(Method::expand_parse);
-		let call_variants_this = self
-			.methods
-			.iter()
-			.map(|m| Method::expand_variant_call(m, &call_name));
-		let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);
-		let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
-
-		// TODO: Inline inline_is
-		let solidity_is = self
-			.info
-			.is
-			.0
-			.iter()
-			.chain(self.info.inline_is.0.iter())
-			.map(|is| is.name.to_string());
-		let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());
-		let solidity_generators = self
-			.info
-			.is
-			.0
-			.iter()
-			.chain(self.info.inline_is.0.iter())
-			.map(|is| Is::expand_generator(is, &gen_ref));
-		let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
-
-		// let methods = self.methods.iter().map(Method::solidity_def);
-
-		quote! {
-			#[derive(Debug)]
-			pub enum #call_name #gen_ref {
-				ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),
-				#(
-					#calls,
-				)*
-				#(
-					#call_sub,
-				)*
-			}
-			impl #gen_ref #call_name #gen_ref {
-				#(
-					#consts
-				)*
-				pub fn interface_id() -> ::evm_coder::types::bytes4 {
-					let mut interface_id = 0;
-					#(#interface_id)*
-					#(#inline_interface_id)*
-					u32::to_be_bytes(interface_id)
-				}
-				pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {
-					interface_id != u32::to_be_bytes(0xffffff) && (
-						interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||
-						interface_id == Self::interface_id()
-						#(
-							|| #supports_interface
-						)*
-					)
-				}
-				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
-					use evm_coder::solidity::*;
-					use core::fmt::Write;
-					let interface = SolidityInterface {
-						name: #solidity_name,
-						selector: Self::interface_id(),
-						is: &["Dummy", "ERC165", #(
-							#solidity_is,
-						)* #(
-							#solidity_events_is,
-						)* ],
-						functions: (#(
-							#solidity_functions,
-						)*),
-					};
-					if is_impl {
-						tc.collect("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
-					} else {
-						tc.collect("// Common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
-					}
-					#(
-						#solidity_generators
-					)*
-					#(
-						#solidity_event_generators
-					)*
-
-					let mut out = string::new();
-					// In solidity interface usage (is) should be preceeded by interface definition
-					// This comment helps to sort it in a set
-					if #solidity_name.starts_with("Inline") {
-						out.push_str("// Inline\n");
-					}
-					let _ = interface.format(is_impl, &mut out, tc);
-					tc.collect(out);
-				}
-			}
-			impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
-				fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
-					use ::evm_coder::abi::AbiRead;
-					match method_id {
-						::evm_coder::ERC165Call::INTERFACE_ID => return Ok(
-							::evm_coder::ERC165Call::parse(method_id, reader)?
-							.map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))
-						),
-						#(
-							#parsers,
-						)*
-						_ => {},
-					}
-					#(
-						#call_parse
-					)else*
-					return Ok(None);
-				}
-			}
-			impl #generics ::evm_coder::Weighted for #call_name #gen_ref
-			#gen_where
-			{
-				#[allow(unused_variables)]
-				fn weight(&self) -> ::evm_coder::execution::DispatchInfo {
-					match self {
-						#(
-							#weight_variants,
-						)*
-						// TODO: It should be very cheap, but not free
-						Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),
-						#(
-							#weight_variants_this,
-						)*
-					}
-				}
-			}
-			impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name
-			#gen_where
-			{
-				#[allow(unreachable_code)] // In case of no inner calls
-				fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {
-					use ::evm_coder::abi::AbiWrite;
-					match c.call {
-						#(
-							#call_variants,
-						)*
-						#call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {
-							let mut writer = ::evm_coder::abi::AbiWriter::default();
-							writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));
-							return Ok(writer.into());
-						}
-						_ => {},
-					}
-					let mut writer = ::evm_coder::abi::AbiWriter::default();
-					match c.call {
-						#(
-							#call_variants_this,
-						)*
-						_ => unreachable!()
-					}
-				}
-			}
-		}
-	}
-}
deletedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/to_log.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use inflector::cases;
-use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
-use std::fmt::Write;
-use quote::quote;
-
-use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};
-
-struct EventField {
-	name: Ident,
-	camel_name: String,
-	ty: Ident,
-	indexed: bool,
-}
-
-impl EventField {
-	fn try_from(field: &Field) -> syn::Result<Self> {
-		let name = field.ident.as_ref().unwrap();
-		let ty = parse_ident_from_type(&field.ty, false)?;
-		let mut indexed = false;
-		for attr in &field.attrs {
-			if let Ok(ident) = parse_ident_from_path(&attr.path, false) {
-				if ident == "indexed" {
-					indexed = true;
-				}
-			}
-		}
-		Ok(Self {
-			name: name.to_owned(),
-			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
-			ty: ty.to_owned(),
-			indexed,
-		})
-	}
-	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
-		let camel_name = &self.camel_name;
-		let ty = &self.ty;
-		let indexed = self.indexed;
-		quote! {
-			<SolidityEventArgument<#ty>>::new(#indexed, #camel_name)
-		}
-	}
-}
-
-struct Event {
-	name: Ident,
-	name_screaming: Ident,
-	fields: Vec<EventField>,
-	selector: [u8; 32],
-	selector_str: String,
-}
-
-impl Event {
-	fn try_from(variant: &Variant) -> syn::Result<Self> {
-		let name = &variant.ident;
-		let name_screaming = snake_ident_to_screaming(name);
-
-		let named = match &variant.fields {
-			Fields::Named(named) => named,
-			_ => {
-				return Err(syn::Error::new(
-					variant.fields.span(),
-					"expected named fields",
-				))
-			}
-		};
-		let mut fields = Vec::new();
-		for field in &named.named {
-			fields.push(EventField::try_from(field)?);
-		}
-		if fields.iter().filter(|f| f.indexed).count() > 3 {
-			return Err(syn::Error::new(
-				variant.fields.span(),
-				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"
-			));
-		}
-		let mut selector_str = format!("{}(", name);
-		for (i, arg) in fields.iter().enumerate() {
-			if i != 0 {
-				write!(selector_str, ",").unwrap();
-			}
-			write!(selector_str, "{}", arg.ty).unwrap();
-		}
-		selector_str.push(')');
-		let selector = crate::event_selector_str(&selector_str);
-
-		Ok(Self {
-			name: name.to_owned(),
-			name_screaming,
-			fields,
-			selector,
-			selector_str,
-		})
-	}
-
-	fn expand_serializers(&self) -> proc_macro2::TokenStream {
-		let name = &self.name;
-		let name_screaming = &self.name_screaming;
-		let fields = self.fields.iter().map(|f| &f.name);
-
-		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);
-		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);
-
-		quote! {
-			Self::#name {#(
-				#fields,
-			)*} => {
-				topics.push(topic::from(Self::#name_screaming));
-				#(
-					topics.push(#indexed.to_topic());
-				)*
-				#(
-					#plain.abi_write(&mut writer);
-				)*
-			}
-		}
-	}
-
-	fn expand_consts(&self) -> proc_macro2::TokenStream {
-		let name_screaming = &self.name_screaming;
-		let selector_str = &self.selector_str;
-		let selector = &self.selector;
-
-		quote! {
-			#[doc = #selector_str]
-			const #name_screaming: [u8; 32] = [#(
-				#selector,
-			)*];
-		}
-	}
-
-	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
-		let name = self.name.to_string();
-		let args = self.fields.iter().map(EventField::expand_solidity_argument);
-		quote! {
-			SolidityEvent {
-				name: #name,
-				args: (
-					#(
-						#args,
-					)*
-				),
-			}
-		}
-	}
-}
-
-pub struct Events {
-	name: Ident,
-	events: Vec<Event>,
-}
-
-impl Events {
-	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {
-		let name = &data.ident;
-		let en = match &data.data {
-			Data::Enum(en) => en,
-			_ => return Err(syn::Error::new(data.span(), "expected enum")),
-		};
-		let mut events = Vec::new();
-		for variant in &en.variants {
-			events.push(Event::try_from(variant)?);
-		}
-		Ok(Self {
-			name: name.to_owned(),
-			events,
-		})
-	}
-	pub fn expand(&self) -> proc_macro2::TokenStream {
-		let name = &self.name;
-
-		let consts = self.events.iter().map(Event::expand_consts);
-		let serializers = self.events.iter().map(Event::expand_serializers);
-		let solidity_name = self.name.to_string();
-		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
-
-		quote! {
-			impl #name {
-				#(
-					#consts
-				)*
-
-				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
-					use evm_coder::solidity::*;
-					use core::fmt::Write;
-					let interface = SolidityInterface {
-						selector: [0; 4],
-						name: #solidity_name,
-						is: &[],
-						functions: (#(
-							#solidity_functions,
-						)*),
-					};
-					let mut out = string::new();
-					out.push_str("// Inline\n");
-					let _ = interface.format(is_impl, &mut out, tc);
-					tc.collect(out);
-				}
-			}
-
-			#[automatically_derived]
-			impl ::evm_coder::events::ToLog for #name {
-				fn to_log(&self, contract: address) -> ::ethereum::Log {
-					use ::evm_coder::events::ToTopic;
-					use ::evm_coder::abi::AbiWrite;
-					let mut writer = ::evm_coder::abi::AbiWriter::new();
-					let mut topics = Vec::new();
-					match self {
-						#(
-							#serializers,
-						)*
-					}
-					::ethereum::Log {
-						address: contract,
-						topics,
-						data: writer.finish(),
-					}
-				}
-			}
-		}
-	}
-}
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
addedcrates/evm-coder/procedural/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/procedural/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "evm-coder-procedural"
+version = "0.2.0"
+license = "GPLv3"
+edition = "2021"
+
+[lib]
+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"] }
addedcrates/evm-coder/procedural/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/procedural/src/lib.rs
@@ -0,0 +1,244 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+
+use inflector::cases;
+use proc_macro::TokenStream;
+use quote::quote;
+use sha3::{Digest, Keccak256};
+use syn::{
+	DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, PathSegment, Type,
+	parse_macro_input, spanned::Spanned,
+};
+
+mod solidity_interface;
+mod to_log;
+
+fn fn_selector_str(input: &str) -> u32 {
+	let mut hasher = Keccak256::new();
+	hasher.update(input.as_bytes());
+	let result = hasher.finalize();
+
+	let mut selector_bytes = [0; 4];
+	selector_bytes.copy_from_slice(&result[0..4]);
+
+	u32::from_be_bytes(selector_bytes)
+}
+
+/// Returns solidity function selector (first 4 bytes of hash) by its
+/// textual representation
+///
+/// ```ignore
+/// use evm_coder_macros::fn_selector;
+///
+/// assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);
+/// ```
+#[proc_macro]
+pub fn fn_selector(input: TokenStream) -> TokenStream {
+	let input = input.to_string().replace(' ', "");
+	let selector = fn_selector_str(&input);
+
+	(quote! {
+		#selector
+	})
+	.into()
+}
+
+fn event_selector_str(input: &str) -> [u8; 32] {
+	let mut hasher = Keccak256::new();
+	hasher.update(input.as_bytes());
+	let result = hasher.finalize();
+
+	let mut selector_bytes = [0; 32];
+	selector_bytes.copy_from_slice(&result[0..32]);
+	selector_bytes
+}
+
+/// Returns solidity topic (hash) by its textual representation
+///
+/// ```ignore
+/// use evm_coder_macros::event_topic;
+///
+/// assert_eq!(
+///     format!("{:x}", event_topic!(Transfer(address, address, uint256))),
+///     "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+/// );
+/// ```
+#[proc_macro]
+pub fn event_topic(stream: TokenStream) -> TokenStream {
+	let input = stream.to_string().replace(' ', "");
+	let selector_bytes = event_selector_str(&input);
+
+	(quote! {
+		::primitive_types::H256([#(
+			#selector_bytes,
+		)*])
+	})
+	.into()
+}
+
+fn parse_path(ty: &Type) -> syn::Result<&Path> {
+	match &ty {
+		syn::Type::Path(pat) => {
+			if let Some(qself) = &pat.qself {
+				return Err(syn::Error::new(qself.ty.span(), "no receiver expected"));
+			}
+			Ok(&pat.path)
+		}
+		_ => Err(syn::Error::new(ty.span(), "expected ty to be path")),
+	}
+}
+
+fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> {
+	if path.segments.len() != 1 {
+		return Err(syn::Error::new(
+			path.span(),
+			"expected path to have only segment",
+		));
+	}
+	let last_segment = &path.segments.last().unwrap();
+	Ok(last_segment)
+}
+
+fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> {
+	match pat {
+		Pat::Ident(i) => Ok(&i.ident),
+		_ => Err(syn::Error::new(pat.span(), "expected pat ident")),
+	}
+}
+
+fn parse_ident_from_segment(segment: &PathSegment, allow_generics: bool) -> syn::Result<&Ident> {
+	if segment.arguments != PathArguments::None && !allow_generics {
+		return Err(syn::Error::new(
+			segment.arguments.span(),
+			"unexpected generic type",
+		));
+	}
+	Ok(&segment.ident)
+}
+
+fn parse_ident_from_path(path: &Path, allow_generics: bool) -> syn::Result<&Ident> {
+	let segment = parse_path_segment(path)?;
+	parse_ident_from_segment(segment, allow_generics)
+}
+
+fn parse_ident_from_type(ty: &Type, allow_generics: bool) -> syn::Result<&Ident> {
+	let path = parse_path(ty)?;
+	parse_ident_from_path(path, allow_generics)
+}
+
+// Gets T out of Result<T>
+fn parse_result_ok(ty: &Type) -> syn::Result<&Type> {
+	let path = parse_path(ty)?;
+	let segment = parse_path_segment(path)?;
+
+	if segment.ident != "Result" {
+		return Err(syn::Error::new(
+			ty.span(),
+			"expected Result as return type (no renamed aliases allowed)",
+		));
+	}
+	let args = match &segment.arguments {
+		PathArguments::AngleBracketed(e) => e,
+		_ => {
+			return Err(syn::Error::new(
+				segment.arguments.span(),
+				"missing Result generics",
+			))
+		}
+	};
+
+	let args = &args.args;
+	let arg = args.first().unwrap();
+
+	let ty = match arg {
+		GenericArgument::Type(ty) => ty,
+		_ => {
+			return Err(syn::Error::new(
+				arg.span(),
+				"expected first generic to be type",
+			))
+		}
+	};
+
+	Ok(ty)
+}
+
+fn pascal_ident_to_call(ident: &Ident) -> Ident {
+	let name = format!("{}Call", ident);
+	Ident::new(&name, ident.span())
+}
+fn snake_ident_to_pascal(ident: &Ident) -> Ident {
+	let name = ident.to_string();
+	let name = cases::pascalcase::to_pascal_case(&name);
+	Ident::new(&name, ident.span())
+}
+fn snake_ident_to_screaming(ident: &Ident) -> Ident {
+	let name = ident.to_string();
+	let name = cases::screamingsnakecase::to_screaming_snake_case(&name);
+	Ident::new(&name, ident.span())
+}
+fn pascal_ident_to_snake_call(ident: &Ident) -> Ident {
+	let name = ident.to_string();
+	let name = cases::snakecase::to_snake_case(&name);
+	let name = format!("call_{}", name);
+	Ident::new(&name, ident.span())
+}
+
+/// 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);
+
+	let input: ItemImpl = match syn::parse(stream) {
+		Ok(t) => t,
+		Err(e) => return e.to_compile_error().into(),
+	};
+
+	let expanded = match solidity_interface::SolidityInterface::try_from(args, &input) {
+		Ok(v) => v.expand(),
+		Err(e) => e.to_compile_error(),
+	};
+
+	(quote! {
+		#input
+
+		#expanded
+	})
+	.into()
+}
+
+#[proc_macro_attribute]
+pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {
+	stream
+}
+#[proc_macro_attribute]
+pub fn weight(_args: TokenStream, stream: TokenStream) -> TokenStream {
+	stream
+}
+
+/// 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);
+
+	match to_log::Events::try_from(&input) {
+		Ok(e) => e.expand(),
+		Err(e) => e.to_compile_error(),
+	}
+	.into()
+}
addedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -0,0 +1,1110 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+
+// NOTE: In order to understand this Rust macro better, first read this chapter
+// about Procedural Macros in Rust book:
+// https://doc.rust-lang.org/reference/procedural-macros.html
+
+use quote::{quote, ToTokens};
+use inflector::cases;
+use std::fmt::Write;
+use syn::{
+	Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,
+	MetaNameValue, PatType, PathArguments, ReturnType, Type,
+	spanned::Spanned,
+	parse::{Parse, ParseStream},
+	parenthesized, Token, LitInt, LitStr,
+};
+
+use crate::{
+	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment,
+	parse_result_ok, pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal,
+	snake_ident_to_screaming,
+};
+
+struct Is {
+	name: Ident,
+	pascal_call_name: Ident,
+	snake_call_name: Ident,
+	via: Option<(Type, Ident)>,
+}
+impl Is {
+	fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			#name(#pascal_call_name #gen_ref)
+		}
+	}
+
+	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());
+		}
+	}
+
+	fn expand_supports_interface(
+		&self,
+		generics: &proc_macro2::TokenStream,
+	) -> proc_macro2::TokenStream {
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			<#pascal_call_name #generics>::supports_interface(interface_id)
+		}
+	}
+
+	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		quote! {
+			Self::#name(call) => call.weight()
+		}
+	}
+
+	fn expand_variant_call(
+		&self,
+		call_name: &proc_macro2::Ident,
+		generics: &proc_macro2::TokenStream,
+	) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		let pascal_call_name = &self.pascal_call_name;
+		let via_typ = self
+			.via
+			.as_ref()
+			.map(|(t, _)| quote! {#t})
+			.unwrap_or_else(|| quote! {Self});
+		let via_map = self
+			.via
+			.as_ref()
+			.map(|(_, i)| quote! {.#i()})
+			.unwrap_or_default();
+		quote! {
+			#call_name::#name(call) => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg {
+				call,
+				caller: c.caller,
+				value: c.value,
+			})
+		}
+	}
+
+	fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {
+				return Ok(Some(Self::#name(parsed_call)))
+			}
+		}
+	}
+
+	fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
+		let pascal_call_name = &self.pascal_call_name;
+		quote! {
+			<#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);
+		}
+	}
+
+	fn expand_event_generator(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		quote! {
+			#name::generate_solidity_interface(tc, is_impl);
+		}
+	}
+}
+
+#[derive(Default)]
+struct IsList(Vec<Is>);
+impl Parse for IsList {
+	fn parse(input: ParseStream) -> syn::Result<Self> {
+		let mut out = vec![];
+		loop {
+			if input.is_empty() {
+				break;
+			}
+			let name = input.parse::<Ident>()?;
+			let lookahead = input.lookahead1();
+			let via = if lookahead.peek(syn::token::Paren) {
+				let contents;
+				parenthesized!(contents in input);
+				let method = contents.parse::<Ident>()?;
+				contents.parse::<Token![,]>()?;
+				let ty = contents.parse::<Type>()?;
+				Some((ty, method))
+			} else if lookahead.peek(Token![,]) {
+				None
+			} else if input.is_empty() {
+				None
+			} else {
+				return Err(lookahead.error());
+			};
+			out.push(Is {
+				pascal_call_name: pascal_ident_to_call(&name),
+				snake_call_name: pascal_ident_to_snake_call(&name),
+				name,
+				via,
+			});
+			if input.peek(Token![,]) {
+				input.parse::<Token![,]>()?;
+				continue;
+			} else {
+				break;
+			}
+		}
+		Ok(Self(out))
+	}
+}
+
+pub struct InterfaceInfo {
+	name: Ident,
+	is: IsList,
+	inline_is: IsList,
+	events: IsList,
+	expect_selector: Option<u32>,
+}
+impl Parse for InterfaceInfo {
+	fn parse(input: ParseStream) -> syn::Result<Self> {
+		let mut name = None;
+		let mut is = None;
+		let mut inline_is = None;
+		let mut events = None;
+		let mut expect_selector = None;
+		// TODO: create proc-macro to optimize proc-macro boilerplate? :D
+		loop {
+			let lookahead = input.lookahead1();
+			if lookahead.peek(kw::name) {
+				let k = input.parse::<kw::name>()?;
+				input.parse::<Token![=]>()?;
+				if name.replace(input.parse::<Ident>()?).is_some() {
+					return Err(syn::Error::new(k.span(), "name is already set"));
+				}
+			} else if lookahead.peek(kw::is) {
+				let k = input.parse::<kw::is>()?;
+				let contents;
+				parenthesized!(contents in input);
+				if is.replace(contents.parse::<IsList>()?).is_some() {
+					return Err(syn::Error::new(k.span(), "is is already set"));
+				}
+			} else if lookahead.peek(kw::inline_is) {
+				let k = input.parse::<kw::inline_is>()?;
+				let contents;
+				parenthesized!(contents in input);
+				if inline_is.replace(contents.parse::<IsList>()?).is_some() {
+					return Err(syn::Error::new(k.span(), "inline_is is already set"));
+				}
+			} else if lookahead.peek(kw::events) {
+				let k = input.parse::<kw::events>()?;
+				let contents;
+				parenthesized!(contents in input);
+				if events.replace(contents.parse::<IsList>()?).is_some() {
+					return Err(syn::Error::new(k.span(), "events is already set"));
+				}
+			} else if lookahead.peek(kw::expect_selector) {
+				let k = input.parse::<kw::expect_selector>()?;
+				input.parse::<Token![=]>()?;
+				let value = input.parse::<LitInt>()?;
+				if expect_selector
+					.replace(value.base10_parse::<u32>()?)
+					.is_some()
+				{
+					return Err(syn::Error::new(k.span(), "expect_selector is already set"));
+				}
+			} else if input.is_empty() {
+				break;
+			} else {
+				return Err(lookahead.error());
+			}
+			if input.peek(Token![,]) {
+				input.parse::<Token![,]>()?;
+			} else {
+				break;
+			}
+		}
+		Ok(Self {
+			name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?,
+			is: is.unwrap_or_default(),
+			inline_is: inline_is.unwrap_or_default(),
+			events: events.unwrap_or_default(),
+			expect_selector,
+		})
+	}
+}
+
+struct MethodInfo {
+	rename_selector: Option<String>,
+}
+impl Parse for MethodInfo {
+	fn parse(input: ParseStream) -> syn::Result<Self> {
+		let mut rename_selector = None;
+		let lookahead = input.lookahead1();
+		if lookahead.peek(kw::rename_selector) {
+			let k = input.parse::<kw::rename_selector>()?;
+			input.parse::<Token![=]>()?;
+			if rename_selector
+				.replace(input.parse::<LitStr>()?.value())
+				.is_some()
+			{
+				return Err(syn::Error::new(k.span(), "rename_selector is already set"));
+			}
+		}
+		Ok(Self { rename_selector })
+	}
+}
+
+enum AbiType {
+	// type
+	Plain(Ident),
+	// (type1,type2)
+	Tuple(Vec<AbiType>),
+	// type[]
+	Vec(Box<AbiType>),
+	// type[20]
+	Array(Box<AbiType>, usize),
+}
+impl AbiType {
+	fn try_from(value: &Type) -> syn::Result<Self> {
+		let value = Self::try_maybe_special_from(value)?;
+		if value.is_special() {
+			return Err(syn::Error::new(value.span(), "unexpected special type"));
+		}
+		Ok(value)
+	}
+	fn try_maybe_special_from(value: &Type) -> syn::Result<Self> {
+		match value {
+			Type::Array(arr) => {
+				let wrapped = AbiType::try_from(&arr.elem)?;
+				match &arr.len {
+					Expr::Lit(l) => match &l.lit {
+						Lit::Int(i) => {
+							let num = i.base10_parse::<usize>()?;
+							Ok(AbiType::Array(Box::new(wrapped), num as usize))
+						}
+						_ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
+					},
+					_ => Err(syn::Error::new(arr.len.span(), "should be literal")),
+				}
+			}
+			Type::Path(_) => {
+				let path = parse_path(value)?;
+				let segment = parse_path_segment(path)?;
+				if segment.ident == "Vec" {
+					let args = match &segment.arguments {
+						PathArguments::AngleBracketed(e) => e,
+						_ => {
+							return Err(syn::Error::new(
+								segment.arguments.span(),
+								"missing Vec generic",
+							))
+						}
+					};
+					let args = &args.args;
+					if args.len() != 1 {
+						return Err(syn::Error::new(
+							args.span(),
+							"expected only one generic for vec",
+						));
+					}
+					let arg = args.first().expect("first arg");
+
+					let ty = match arg {
+						GenericArgument::Type(ty) => ty,
+						_ => {
+							return Err(syn::Error::new(
+								arg.span(),
+								"expected first generic to be type",
+							))
+						}
+					};
+
+					let wrapped = AbiType::try_from(ty)?;
+					Ok(Self::Vec(Box::new(wrapped)))
+				} else {
+					if !segment.arguments.is_empty() {
+						return Err(syn::Error::new(
+							segment.arguments.span(),
+							"unexpected generic arguments for non-vec type",
+						));
+					}
+					Ok(Self::Plain(segment.ident.clone()))
+				}
+			}
+			Type::Tuple(t) => {
+				let mut out = Vec::with_capacity(t.elems.len());
+				for el in t.elems.iter() {
+					out.push(AbiType::try_from(el)?)
+				}
+				Ok(Self::Tuple(out))
+			}
+			_ => Err(syn::Error::new(
+				value.span(),
+				"unexpected type, only arrays, plain types and tuples are supported",
+			)),
+		}
+	}
+	fn is_value(&self) -> bool {
+		matches!(self, Self::Plain(v) if v == "value")
+	}
+	fn is_caller(&self) -> bool {
+		matches!(self, Self::Plain(v) if v == "caller")
+	}
+	fn is_special(&self) -> bool {
+		self.is_caller() || self.is_value()
+	}
+	fn selector_ty_buf(&self, buf: &mut String) -> std::fmt::Result {
+		match self {
+			AbiType::Plain(t) => {
+				write!(buf, "{}", t)
+			}
+			AbiType::Tuple(t) => {
+				write!(buf, "(")?;
+				for (i, t) in t.iter().enumerate() {
+					if i != 0 {
+						write!(buf, ",")?;
+					}
+					t.selector_ty_buf(buf)?;
+				}
+				write!(buf, ")")
+			}
+			AbiType::Vec(v) => {
+				v.selector_ty_buf(buf)?;
+				write!(buf, "[]")
+			}
+			AbiType::Array(v, len) => {
+				v.selector_ty_buf(buf)?;
+				write!(buf, "[{}]", len)
+			}
+		}
+	}
+	fn selector_ty(&self) -> String {
+		let mut out = String::new();
+		self.selector_ty_buf(&mut out).expect("no fmt error");
+		out
+	}
+}
+impl ToTokens for AbiType {
+	fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+		match self {
+			AbiType::Plain(t) => tokens.extend(quote! {#t}),
+			AbiType::Tuple(t) => {
+				tokens.extend(quote! {(
+					#(#t),*
+				)});
+			}
+			AbiType::Vec(v) => tokens.extend(quote! {Vec<#v>}),
+			AbiType::Array(v, l) => tokens.extend(quote! {[#v; #l]}),
+		}
+	}
+}
+
+struct MethodArg {
+	name: Ident,
+	camel_name: String,
+	ty: AbiType,
+}
+impl MethodArg {
+	fn try_from(value: &PatType) -> syn::Result<Self> {
+		let name = parse_ident_from_pat(&value.pat)?.clone();
+		Ok(Self {
+			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
+			name,
+			ty: AbiType::try_maybe_special_from(&value.ty)?,
+		})
+	}
+	fn is_value(&self) -> bool {
+		self.ty.is_value()
+	}
+	fn is_caller(&self) -> bool {
+		self.ty.is_caller()
+	}
+	fn is_special(&self) -> bool {
+		self.ty.is_special()
+	}
+	fn selector_ty(&self) -> String {
+		assert!(!self.is_special());
+		self.ty.selector_ty()
+	}
+
+	fn expand_call_def(&self) -> proc_macro2::TokenStream {
+		assert!(!self.is_special());
+		let name = &self.name;
+		let ty = &self.ty;
+
+		quote! {
+			#name: #ty
+		}
+	}
+
+	fn expand_parse(&self) -> proc_macro2::TokenStream {
+		assert!(!self.is_special());
+		let name = &self.name;
+		quote! {
+			#name: reader.abi_read()?
+		}
+	}
+
+	fn expand_call_arg(&self) -> proc_macro2::TokenStream {
+		if self.is_value() {
+			quote! {
+				c.value.clone()
+			}
+		} else if self.is_caller() {
+			quote! {
+				c.caller.clone()
+			}
+		} else {
+			let name = &self.name;
+			quote! {
+				#name
+			}
+		}
+	}
+
+	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
+		let camel_name = &self.camel_name.to_string();
+		let ty = &self.ty;
+		quote! {
+			<NamedArgument<#ty>>::new(#camel_name)
+		}
+	}
+}
+
+#[derive(PartialEq)]
+enum Mutability {
+	Mutable,
+	View,
+	Pure,
+}
+
+/// Group all keywords for this macro. Usage example:
+/// #[solidity_interface(name = "B", inline_is(A))]
+mod kw {
+	syn::custom_keyword!(weight);
+
+	syn::custom_keyword!(via);
+	syn::custom_keyword!(name);
+	syn::custom_keyword!(is);
+	syn::custom_keyword!(inline_is);
+	syn::custom_keyword!(events);
+	syn::custom_keyword!(expect_selector);
+
+	syn::custom_keyword!(rename_selector);
+}
+
+/// Rust methods are parsed into this structure when Solidity code is generated
+struct Method {
+	name: Ident,
+	camel_name: String,
+	pascal_name: Ident,
+	screaming_name: Ident,
+	selector_str: String,
+	selector: u32,
+	args: Vec<MethodArg>,
+	has_normal_args: bool,
+	mutability: Mutability,
+	result: Type,
+	weight: Option<Expr>,
+	docs: Vec<String>,
+}
+impl Method {
+	fn try_from(value: &ImplItemMethod) -> syn::Result<Self> {
+		let mut info = MethodInfo {
+			rename_selector: None,
+		};
+		let mut docs = Vec::new();
+		let mut weight = None;
+		for attr in &value.attrs {
+			let ident = parse_ident_from_path(&attr.path, false)?;
+			if ident == "solidity" {
+				info = attr.parse_args::<MethodInfo>()?;
+			} else if ident == "doc" {
+				let args = attr.parse_meta().unwrap();
+				let value = match args {
+					Meta::NameValue(MetaNameValue {
+						lit: Lit::Str(str), ..
+					}) => str.value(),
+					_ => unreachable!(),
+				};
+				docs.push(value);
+			} else if ident == "weight" {
+				weight = Some(attr.parse_args::<Expr>()?);
+			}
+		}
+		let ident = &value.sig.ident;
+		let ident_str = ident.to_string();
+		if !cases::snakecase::is_snake_case(&ident_str) {
+			return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute"));
+		}
+
+		let mut mutability = Mutability::Pure;
+
+		if let Some(FnArg::Receiver(receiver)) = value
+			.sig
+			.inputs
+			.iter()
+			.find(|arg| matches!(arg, FnArg::Receiver(_)))
+		{
+			if receiver.reference.is_none() {
+				return Err(syn::Error::new(
+					receiver.span(),
+					"receiver should be by ref",
+				));
+			}
+			if receiver.mutability.is_some() {
+				mutability = Mutability::Mutable;
+			} else {
+				mutability = Mutability::View;
+			}
+		}
+		let mut args = Vec::new();
+		for typ in value
+			.sig
+			.inputs
+			.iter()
+			.filter(|arg| matches!(arg, FnArg::Typed(_)))
+		{
+			let typ = match typ {
+				FnArg::Typed(typ) => typ,
+				_ => unreachable!(),
+			};
+			args.push(MethodArg::try_from(typ)?);
+		}
+
+		if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) {
+			return Err(syn::Error::new(
+				args.iter().find(|arg| arg.is_value()).unwrap().ty.span(),
+				"payable function should be mutable",
+			));
+		}
+
+		let result = match &value.sig.output {
+			ReturnType::Type(_, ty) => ty,
+			_ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result<value>\nif there is no value to return - specify void (which is alias to unit)")),
+		};
+		let result = parse_result_ok(result)?;
+
+		let camel_name = info
+			.rename_selector
+			.unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string()));
+		let mut selector_str = camel_name.clone();
+		selector_str.push('(');
+		let mut has_normal_args = false;
+		for (i, arg) in args.iter().filter(|arg| !arg.is_special()).enumerate() {
+			if i != 0 {
+				selector_str.push(',');
+			}
+			write!(selector_str, "{}", arg.selector_ty()).unwrap();
+			has_normal_args = true;
+		}
+		selector_str.push(')');
+		let selector = fn_selector_str(&selector_str);
+
+		Ok(Self {
+			name: ident.clone(),
+			camel_name,
+			pascal_name: snake_ident_to_pascal(ident),
+			screaming_name: snake_ident_to_screaming(ident),
+			selector_str,
+			selector,
+			args,
+			has_normal_args,
+			mutability,
+			result: result.clone(),
+			weight,
+			docs,
+		})
+	}
+	fn expand_call_def(&self) -> proc_macro2::TokenStream {
+		let defs = self
+			.args
+			.iter()
+			.filter(|a| !a.is_special())
+			.map(|a| a.expand_call_def());
+		let pascal_name = &self.pascal_name;
+		let docs = &self.docs;
+
+		if self.has_normal_args {
+			quote! {
+				#(#[doc = #docs])*
+				#[allow(missing_docs)]
+				#pascal_name {
+					#(
+						#defs,
+					)*
+				}
+			}
+		} else {
+			quote! {#pascal_name}
+		}
+	}
+
+	fn expand_const(&self) -> proc_macro2::TokenStream {
+		let screaming_name = &self.screaming_name;
+		let selector = u32::to_be_bytes(self.selector);
+		let selector_str = &self.selector_str;
+		quote! {
+			#[doc = #selector_str]
+			const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];
+		}
+	}
+
+	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
+		let screaming_name = &self.screaming_name;
+		quote! {
+			interface_id ^= u32::from_be_bytes(Self::#screaming_name);
+		}
+	}
+
+	fn expand_parse(&self) -> proc_macro2::TokenStream {
+		let pascal_name = &self.pascal_name;
+		let screaming_name = &self.screaming_name;
+		if self.has_normal_args {
+			let parsers = self
+				.args
+				.iter()
+				.filter(|a| !a.is_special())
+				.map(|a| a.expand_parse());
+			quote! {
+				Self::#screaming_name => return Ok(Some(Self::#pascal_name {
+					#(
+						#parsers,
+					)*
+				}))
+			}
+		} else {
+			quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }
+		}
+	}
+
+	fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {
+		let pascal_name = &self.pascal_name;
+		let name = &self.name;
+
+		let matcher = if self.has_normal_args {
+			let names = self
+				.args
+				.iter()
+				.filter(|a| !a.is_special())
+				.map(|a| &a.name);
+
+			quote! {{
+				#(
+					#names,
+				)*
+			}}
+		} else {
+			quote! {}
+		};
+
+		let receiver = match self.mutability {
+			Mutability::Mutable | Mutability::View => quote! {self.},
+			Mutability::Pure => quote! {Self::},
+		};
+		let args = self.args.iter().map(|a| a.expand_call_arg());
+
+		quote! {
+			#call_name::#pascal_name #matcher => {
+				let result = #receiver #name(
+					#(
+						#args,
+					)*
+				)?;
+				(&result).to_result()
+			}
+		}
+	}
+
+	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
+		let pascal_name = &self.pascal_name;
+		if let Some(weight) = &self.weight {
+			let matcher = if self.has_normal_args {
+				let names = self
+					.args
+					.iter()
+					.filter(|a| !a.is_special())
+					.map(|a| &a.name);
+
+				quote! {{
+					#(
+						#names,
+					)*
+				}}
+			} else {
+				quote! {}
+			};
+			quote! {
+				Self::#pascal_name #matcher => (#weight).into()
+			}
+		} else {
+			let matcher = if self.has_normal_args {
+				quote! {{..}}
+			} else {
+				quote! {}
+			};
+			quote! {
+				Self::#pascal_name #matcher => ().into()
+			}
+		}
+	}
+
+	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
+		let camel_name = &self.camel_name;
+		let mutability = match self.mutability {
+			Mutability::Mutable => quote! {SolidityMutability::Mutable},
+			Mutability::View => quote! { SolidityMutability::View },
+			Mutability::Pure => quote! {SolidityMutability::Pure},
+		};
+		let result = &self.result;
+
+		let args = self
+			.args
+			.iter()
+			.filter(|a| !a.is_special())
+			.map(MethodArg::expand_solidity_argument);
+		let docs = &self.docs;
+		let selector_str = &self.selector_str;
+		let selector = self.selector;
+
+		quote! {
+			SolidityFunction {
+				docs: &[#(#docs),*],
+				selector_str: #selector_str,
+				selector: #selector,
+				name: #camel_name,
+				mutability: #mutability,
+				args: (
+					#(
+						#args,
+					)*
+				),
+				result: <UnnamedArgument<#result>>::default(),
+			}
+		}
+	}
+}
+
+fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {
+	if gen.params.is_empty() {
+		return quote! {};
+	}
+	let params = gen.params.iter().map(|p| match p {
+		syn::GenericParam::Type(id) => {
+			let v = &id.ident;
+			quote! {#v}
+		}
+		syn::GenericParam::Lifetime(lt) => {
+			let v = &lt.lifetime;
+			quote! {#v}
+		}
+		syn::GenericParam::Const(c) => {
+			let i = &c.ident;
+			quote! {#i}
+		}
+	});
+	quote! { #(#params),* }
+}
+fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {
+	if gen.params.is_empty() {
+		return quote! {};
+	}
+	let list = generics_list(gen);
+	quote! { <#list> }
+}
+fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {
+	let list = generics_list(gen);
+	if gen.params.len() == 1 {
+		quote! {#list}
+	} else {
+		quote! { (#list) }
+	}
+}
+
+pub struct SolidityInterface {
+	generics: Generics,
+	name: Box<syn::Type>,
+	info: InterfaceInfo,
+	methods: Vec<Method>,
+	docs: Vec<String>,
+}
+impl SolidityInterface {
+	pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result<Self> {
+		let mut methods = Vec::new();
+
+		for item in &value.items {
+			if let ImplItem::Method(method) = item {
+				methods.push(Method::try_from(method)?)
+			}
+		}
+		let mut docs = vec![];
+		for attr in &value.attrs {
+			let ident = parse_ident_from_path(&attr.path, false)?;
+			if ident == "doc" {
+				let args = attr.parse_meta().unwrap();
+				let value = match args {
+					Meta::NameValue(MetaNameValue {
+						lit: Lit::Str(str), ..
+					}) => str.value(),
+					_ => unreachable!(),
+				};
+				docs.push(value);
+			}
+		}
+		Ok(Self {
+			generics: value.generics.clone(),
+			name: value.self_ty.clone(),
+			info,
+			methods,
+			docs,
+		})
+	}
+	pub fn expand(self) -> proc_macro2::TokenStream {
+		let name = self.name;
+
+		let solidity_name = self.info.name.to_string();
+		let call_name = pascal_ident_to_call(&self.info.name);
+		let generics = self.generics;
+		let gen_ref = generics_reference(&generics);
+		let gen_data = generics_data(&generics);
+		let gen_where = &generics.where_clause;
+
+		let call_sub = self
+			.info
+			.inline_is
+			.0
+			.iter()
+			.chain(self.info.is.0.iter())
+			.map(|c| Is::expand_call_def(c, &gen_ref));
+		let call_parse = self
+			.info
+			.inline_is
+			.0
+			.iter()
+			.chain(self.info.is.0.iter())
+			.map(|is| Is::expand_parse(is, &gen_ref));
+		let call_variants = self
+			.info
+			.inline_is
+			.0
+			.iter()
+			.chain(self.info.is.0.iter())
+			.map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));
+		let weight_variants = self
+			.info
+			.inline_is
+			.0
+			.iter()
+			.chain(self.info.is.0.iter())
+			.map(Is::expand_variant_weight);
+
+		let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);
+		let supports_interface = self
+			.info
+			.is
+			.0
+			.iter()
+			.map(|is| Is::expand_supports_interface(is, &gen_ref));
+
+		let calls = self.methods.iter().map(Method::expand_call_def);
+		let consts = self.methods.iter().map(Method::expand_const);
+		let interface_id = self.methods.iter().map(Method::expand_interface_id);
+		let parsers = self.methods.iter().map(Method::expand_parse);
+		let call_variants_this = self
+			.methods
+			.iter()
+			.map(|m| Method::expand_variant_call(m, &call_name));
+		let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);
+		let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
+
+		// TODO: Inline inline_is
+		let solidity_is = self
+			.info
+			.is
+			.0
+			.iter()
+			.chain(self.info.inline_is.0.iter())
+			.map(|is| is.name.to_string());
+		let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());
+		let solidity_generators = self
+			.info
+			.is
+			.0
+			.iter()
+			.chain(self.info.inline_is.0.iter())
+			.map(|is| Is::expand_generator(is, &gen_ref));
+		let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
+
+		let docs = &self.docs;
+
+		if let Some(expect_selector) = &self.info.expect_selector {
+			if !self.info.inline_is.0.is_empty() {
+				return syn::Error::new(
+					name.span(),
+					"expect_selector is not compatible with inline_is",
+				)
+				.to_compile_error();
+			}
+			let selector = self
+				.methods
+				.iter()
+				.map(|m| m.selector)
+				.fold(0, |a, b| a ^ b);
+
+			if *expect_selector != selector {
+				let mut methods = String::new();
+				for meth in self.methods.iter() {
+					write!(methods, "\n- {}", meth.selector_str).expect("write to string");
+				}
+				return syn::Error::new(name.span(), format!("expected selector mismatch, expected {expect_selector:0>8x}, but implementation has {selector:0>8x}{methods}")).to_compile_error();
+			}
+		}
+		// let methods = self.methods.iter().map(Method::solidity_def);
+
+		quote! {
+			#[derive(Debug)]
+			#(#[doc = #docs])*
+			pub enum #call_name #gen_ref {
+				/// Inherited method
+				ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),
+				#(
+					#calls,
+				)*
+				#(
+					#call_sub,
+				)*
+			}
+			impl #gen_ref #call_name #gen_ref {
+				#(
+					#consts
+				)*
+				/// Return this call ERC165 selector
+				pub fn interface_id() -> ::evm_coder::types::bytes4 {
+					let mut interface_id = 0;
+					#(#interface_id)*
+					#(#inline_interface_id)*
+					u32::to_be_bytes(interface_id)
+				}
+				/// Is this contract implements specified ERC165 selector
+				pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {
+					interface_id != u32::to_be_bytes(0xffffff) && (
+						interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||
+						interface_id == Self::interface_id()
+						#(
+							|| #supports_interface
+						)*
+					)
+				}
+				/// Generate solidity definitions for methods described in this interface
+				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
+					use evm_coder::solidity::*;
+					use core::fmt::Write;
+					let interface = SolidityInterface {
+						docs: &[#(#docs),*],
+						name: #solidity_name,
+						selector: Self::interface_id(),
+						is: &["Dummy", "ERC165", #(
+							#solidity_is,
+						)* #(
+							#solidity_events_is,
+						)* ],
+						functions: (#(
+							#solidity_functions,
+						)*),
+					};
+
+					let mut out = string::new();
+					if #solidity_name.starts_with("Inline") {
+						out.push_str("/// @dev inlined interface\n");
+					}
+					let _ = interface.format(is_impl, &mut out, tc);
+					tc.collect(out);
+					#(
+						#solidity_event_generators
+					)*
+					#(
+						#solidity_generators
+					)*
+					if is_impl {
+						tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into());
+					} else {
+						tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into());
+					}
+				}
+			}
+			impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
+				fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
+					use ::evm_coder::abi::AbiRead;
+					match method_id {
+						::evm_coder::ERC165Call::INTERFACE_ID => return Ok(
+							::evm_coder::ERC165Call::parse(method_id, reader)?
+							.map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))
+						),
+						#(
+							#parsers,
+						)*
+						_ => {},
+					}
+					#(
+						#call_parse
+					)else*
+					return Ok(None);
+				}
+			}
+			impl #generics ::evm_coder::Weighted for #call_name #gen_ref
+			#gen_where
+			{
+				#[allow(unused_variables)]
+				fn weight(&self) -> ::evm_coder::execution::DispatchInfo {
+					match self {
+						#(
+							#weight_variants,
+						)*
+						// TODO: It should be very cheap, but not free
+						Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),
+						#(
+							#weight_variants_this,
+						)*
+					}
+				}
+			}
+			impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name
+			#gen_where
+			{
+				#[allow(unreachable_code)] // In case of no inner calls
+				fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {
+					use ::evm_coder::abi::AbiWrite;
+					match c.call {
+						#(
+							#call_variants,
+						)*
+						#call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {
+							let mut writer = ::evm_coder::abi::AbiWriter::default();
+							writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));
+							return Ok(writer.into());
+						}
+						_ => {},
+					}
+					let mut writer = ::evm_coder::abi::AbiWriter::default();
+					match c.call {
+						#(
+							#call_variants_this,
+						)*
+						_ => unreachable!()
+					}
+				}
+			}
+		}
+	}
+}
addedcrates/evm-coder/procedural/src/to_log.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/procedural/src/to_log.rs
@@ -0,0 +1,238 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use inflector::cases;
+use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
+use std::fmt::Write;
+use quote::quote;
+
+use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};
+
+struct EventField {
+	name: Ident,
+	camel_name: String,
+	ty: Ident,
+	indexed: bool,
+}
+
+impl EventField {
+	fn try_from(field: &Field) -> syn::Result<Self> {
+		let name = field.ident.as_ref().unwrap();
+		let ty = parse_ident_from_type(&field.ty, false)?;
+		let mut indexed = false;
+		for attr in &field.attrs {
+			if let Ok(ident) = parse_ident_from_path(&attr.path, false) {
+				if ident == "indexed" {
+					indexed = true;
+				}
+			}
+		}
+		Ok(Self {
+			name: name.to_owned(),
+			camel_name: cases::camelcase::to_camel_case(&name.to_string()),
+			ty: ty.to_owned(),
+			indexed,
+		})
+	}
+	fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
+		let camel_name = &self.camel_name;
+		let ty = &self.ty;
+		let indexed = self.indexed;
+		quote! {
+			<SolidityEventArgument<#ty>>::new(#indexed, #camel_name)
+		}
+	}
+}
+
+struct Event {
+	name: Ident,
+	name_screaming: Ident,
+	fields: Vec<EventField>,
+	selector: [u8; 32],
+	selector_str: String,
+}
+
+impl Event {
+	fn try_from(variant: &Variant) -> syn::Result<Self> {
+		let name = &variant.ident;
+		let name_screaming = snake_ident_to_screaming(name);
+
+		let named = match &variant.fields {
+			Fields::Named(named) => named,
+			_ => {
+				return Err(syn::Error::new(
+					variant.fields.span(),
+					"expected named fields",
+				))
+			}
+		};
+		let mut fields = Vec::new();
+		for field in &named.named {
+			fields.push(EventField::try_from(field)?);
+		}
+		if fields.iter().filter(|f| f.indexed).count() > 3 {
+			return Err(syn::Error::new(
+				variant.fields.span(),
+				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"
+			));
+		}
+		let mut selector_str = format!("{}(", name);
+		for (i, arg) in fields.iter().enumerate() {
+			if i != 0 {
+				write!(selector_str, ",").unwrap();
+			}
+			write!(selector_str, "{}", arg.ty).unwrap();
+		}
+		selector_str.push(')');
+		let selector = crate::event_selector_str(&selector_str);
+
+		Ok(Self {
+			name: name.to_owned(),
+			name_screaming,
+			fields,
+			selector,
+			selector_str,
+		})
+	}
+
+	fn expand_serializers(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		let name_screaming = &self.name_screaming;
+		let fields = self.fields.iter().map(|f| &f.name);
+
+		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);
+		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);
+
+		quote! {
+			Self::#name {#(
+				#fields,
+			)*} => {
+				topics.push(topic::from(Self::#name_screaming));
+				#(
+					topics.push(#indexed.to_topic());
+				)*
+				#(
+					#plain.abi_write(&mut writer);
+				)*
+			}
+		}
+	}
+
+	fn expand_consts(&self) -> proc_macro2::TokenStream {
+		let name_screaming = &self.name_screaming;
+		let selector_str = &self.selector_str;
+		let selector = &self.selector;
+
+		quote! {
+			#[doc = #selector_str]
+			const #name_screaming: [u8; 32] = [#(
+				#selector,
+			)*];
+		}
+	}
+
+	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
+		let name = self.name.to_string();
+		let args = self.fields.iter().map(EventField::expand_solidity_argument);
+		quote! {
+			SolidityEvent {
+				name: #name,
+				args: (
+					#(
+						#args,
+					)*
+				),
+			}
+		}
+	}
+}
+
+pub struct Events {
+	name: Ident,
+	events: Vec<Event>,
+}
+
+impl Events {
+	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {
+		let name = &data.ident;
+		let en = match &data.data {
+			Data::Enum(en) => en,
+			_ => return Err(syn::Error::new(data.span(), "expected enum")),
+		};
+		let mut events = Vec::new();
+		for variant in &en.variants {
+			events.push(Event::try_from(variant)?);
+		}
+		Ok(Self {
+			name: name.to_owned(),
+			events,
+		})
+	}
+	pub fn expand(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+
+		let consts = self.events.iter().map(Event::expand_consts);
+		let serializers = self.events.iter().map(Event::expand_serializers);
+		let solidity_name = self.name.to_string();
+		let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
+
+		quote! {
+			impl #name {
+				#(
+					#consts
+				)*
+
+				pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
+					use evm_coder::solidity::*;
+					use core::fmt::Write;
+					let interface = SolidityInterface {
+						docs: &[],
+						selector: [0; 4],
+						name: #solidity_name,
+						is: &[],
+						functions: (#(
+							#solidity_functions,
+						)*),
+					};
+					let mut out = string::new();
+					out.push_str("/// @dev inlined interface\n");
+					let _ = interface.format(is_impl, &mut out, tc);
+					tc.collect(out);
+				}
+			}
+
+			#[automatically_derived]
+			impl ::evm_coder::events::ToLog for #name {
+				fn to_log(&self, contract: address) -> ::ethereum::Log {
+					use ::evm_coder::events::ToTopic;
+					use ::evm_coder::abi::AbiWrite;
+					let mut writer = ::evm_coder::abi::AbiWriter::new();
+					let mut topics = Vec::new();
+					match self {
+						#(
+							#serializers,
+						)*
+					}
+					::ethereum::Log {
+						address: contract,
+						topics,
+						data: writer.finish(),
+					}
+				}
+			}
+		}
+	}
+}
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 consumed 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,103 @@
 // 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 inheritance in Solidity
+/// - *inline_is* - same as `is`, but ERC165::SupportsInterface will work differently: For `is` SupportsInterface(A) will return true
+///   if A is one of the interfaces the contract is inherited from (e.g. B is created as `is(A)`). If B is created as `inline_is(A)`
+///   SupportsInterface(A) will internally create a new interface that combines all methods of A and B, so SupportsInterface(A) will return
+///   false.
+///
+/// `#[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 worse 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 +135,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 +146,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,26 +14,31 @@
 // 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 [`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::{BTreeSet, BTreeMap},
-	format,
-};
+use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
 #[cfg(feature = "std")]
-use std::collections::{BTreeSet, BTreeMap};
+use std::collections::BTreeMap;
 use core::{
 	fmt::{self, Write},
 	marker::PhantomData,
 	cell::{Cell, RefCell},
+	cmp::Reverse,
 };
 use impl_trait_for_tuples::impl_for_tuples;
 use crate::types::*;
 
 #[derive(Default)]
 pub struct TypeCollector {
-	structs: RefCell<BTreeSet<string>>,
+	/// Code => id
+	/// id ordering is required to perform topo-sort on the resulting data
+	structs: RefCell<BTreeMap<string, usize>>,
 	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
 	id: Cell<usize>,
 }
@@ -42,7 +47,8 @@
 		Self::default()
 	}
 	pub fn collect(&self, item: string) {
-		self.structs.borrow_mut().insert(item);
+		let id = self.next_id();
+		self.structs.borrow_mut().insert(item, id);
 	}
 	pub fn next_id(&self) -> usize {
 		let v = self.id.get();
@@ -56,7 +62,7 @@
 		}
 		let id = self.next_id();
 		let mut str = String::new();
-		writeln!(str, "// Anonymous struct").unwrap();
+		writeln!(str, "/// @dev anonymous struct").unwrap();
 		writeln!(str, "struct Tuple{} {{", id).unwrap();
 		for (i, name) in names.iter().enumerate() {
 			writeln!(str, "\t{} field_{};", name, i).unwrap();
@@ -66,15 +72,19 @@
 		self.anonymous.borrow_mut().insert(names, id);
 		format!("Tuple{}", id)
 	}
-	pub fn finish(self) -> BTreeSet<string> {
-		self.structs.into_inner()
+	pub fn finish(self) -> Vec<string> {
+		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
+		data.sort_by_key(|(_, id)| Reverse(*id));
+		data.into_iter().map(|(code, _)| code).collect()
 	}
 }
 
 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
 	}
@@ -125,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 {}
 }
 
@@ -402,7 +416,8 @@
 }
 pub struct SolidityFunction<A, R> {
 	pub docs: &'static [&'static str],
-	pub selector: &'static str,
+	pub selector_str: &'static str,
+	pub selector: u32,
 	pub name: &'static str,
 	pub args: A,
 	pub result: R,
@@ -416,12 +431,14 @@
 		tc: &TypeCollector,
 	) -> fmt::Result {
 		for doc in self.docs {
-			writeln!(writer, "\t//{}", doc)?;
-		}
-		if !self.docs.is_empty() {
-			writeln!(writer, "\t//")?;
+			writeln!(writer, "\t///{}", doc)?;
 		}
-		writeln!(writer, "\t// Selector: {}", 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)?;
 		write!(writer, ")")?;
@@ -481,6 +498,7 @@
 }
 
 pub struct SolidityInterface<F: SolidityFunctions> {
+	pub docs: &'static [&'static str],
 	pub selector: bytes4,
 	pub name: &'static str,
 	pub is: &'static [&'static str],
@@ -495,10 +513,13 @@
 		tc: &TypeCollector,
 	) -> fmt::Result {
 		const ZERO_BYTES: [u8; 4] = [0; 4];
+		for doc in self.docs {
+			writeln!(out, "///{}", doc)?;
+		}
 		if self.selector != ZERO_BYTES {
 			writeln!(
 				out,
-				"// Selector: {:0>8x}",
+				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",
 				u32::from_be_bytes(self.selector)
 			)?;
 		}
modifiedcrates/evm-coder/tests/generics.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/generics.rs
+++ b/crates/evm-coder/tests/generics.rs
@@ -19,14 +19,14 @@
 
 struct Generic<T>(PhantomData<T>);
 
-#[solidity_interface(name = "GenericIs")]
+#[solidity_interface(name = GenericIs)]
 impl<T> Generic<T> {
 	fn test_1(&self) -> Result<uint256> {
 		unreachable!()
 	}
 }
 
-#[solidity_interface(name = "Generic", is(GenericIs))]
+#[solidity_interface(name = Generic, is(GenericIs))]
 impl<T: Into<u32>> Generic<T> {
 	fn test_2(&self) -> Result<uint256> {
 		unreachable!()
@@ -35,7 +35,7 @@
 
 generate_stubgen!(gen_iface, GenericCall<()>, false);
 
-#[solidity_interface(name = "GenericWhere")]
+#[solidity_interface(name = GenericWhere)]
 impl<T> Generic<T>
 where
 	T: core::fmt::Debug,
modifiedcrates/evm-coder/tests/random.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/random.rs
+++ b/crates/evm-coder/tests/random.rs
@@ -21,14 +21,14 @@
 
 struct Impls;
 
-#[solidity_interface(name = "OurInterface")]
+#[solidity_interface(name = OurInterface)]
 impl Impls {
 	fn fn_a(&self, _input: uint256) -> Result<bool> {
 		unreachable!()
 	}
 }
 
-#[solidity_interface(name = "OurInterface1")]
+#[solidity_interface(name = OurInterface1)]
 impl Impls {
 	fn fn_b(&self, _input: uint128) -> Result<uint32> {
 		unreachable!()
@@ -48,7 +48,7 @@
 }
 
 #[solidity_interface(
-	name = "OurInterface2",
+	name = OurInterface2,
 	is(OurInterface),
 	inline_is(OurInterface1),
 	events(OurEvents)
@@ -79,3 +79,9 @@
 		unreachable!()
 	}
 }
+
+#[solidity_interface(
+	name = ValidSelector,
+	expect_selector = 0x00000000,
+)]
+impl Impls {}
modifiedcrates/evm-coder/tests/solidity_generation.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/solidity_generation.rs
+++ b/crates/evm-coder/tests/solidity_generation.rs
@@ -18,7 +18,7 @@
 
 struct ERC20;
 
-#[solidity_interface(name = "ERC20")]
+#[solidity_interface(name = ERC20)]
 impl ERC20 {
 	fn decimals(&self) -> Result<uint8> {
 		unreachable!()
modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -115,7 +115,7 @@
 		.public()
 }
 
-/// The extensions for the [`ChainSpec`].
+/// The extensions for the [`DefaultChainSpec`].
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
 #[serde(deny_unknown_fields)]
 pub struct Extensions {
modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -60,7 +60,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-#[solidity_interface(name = "Collection")]
+#[solidity_interface(name = Collection)]
 impl<T: Config> CollectionHandle<T>
 where
 	T::AccountId: From<[u8; 32]>,
@@ -228,7 +228,7 @@
 	}
 
 	/// Add collection admin by substrate address.
-	/// @param new_admin Substrate administrator address.
+	/// @param newAdmin Substrate administrator address.
 	fn add_collection_admin_substrate(
 		&mut self,
 		caller: caller,
@@ -254,7 +254,7 @@
 	}
 
 	/// Add collection admin.
-	/// @param new_admin Address of the added administrator.
+	/// @param newAdmin Address of the added administrator.
 	fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let new_admin = T::CrossAccountId::from_eth(new_admin);
@@ -264,7 +264,7 @@
 
 	/// Remove collection admin.
 	///
-	/// @param new_admin Address of the removed administrator.
+	/// @param admin Address of the removed administrator.
 	fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let admin = T::CrossAccountId::from_eth(admin);
addedpallets/evm-contract-helpers/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-contract-helpers/README.md
@@ -0,0 +1,13 @@
+# EVM Contract Helpers
+
+This pallet extends pallet-evm contracts with several new functions.
+
+## Overview
+
+Evm contract helpers pallet provides ability to
+
+- Tracking and getting of user, which deployed contract
+- Sponsoring EVM contract calls (Make transaction calls to be free for users, instead making them being paid from contract address)
+- Allowlist access mode
+
+As most of those functions are intented to be consumed by ethereum users, only API provided by this pallet is [ContractHelpers magic contract](./src/stubs/ContractHelpers.sol)
\ No newline at end of file
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.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/>.
 
+//! Implementation of magic contract
+
 use core::marker::PhantomData;
 use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};
@@ -31,7 +33,8 @@
 use up_sponsorship::SponsorshipHandler;
 use sp_std::vec::Vec;
 
-struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
+/// See [`ContractHelpersCall`]
+pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
 impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
 	fn recorder(&self) -> &SubstrateRecorder<T> {
 		&self.0
@@ -42,22 +45,25 @@
 	}
 }
 
-#[solidity_interface(name = "ContractHelpers")]
+/// @title Magic contract, which allows users to reconfigure other contracts
+#[solidity_interface(name = ContractHelpers)]
 impl<T: Config> ContractHelpers<T>
 where
 	T::AccountId: AsRef<[u8; 32]>,
 {
-	/// Get contract ovner
-	///
-	/// @param Contract_address contract for which the owner is being determined.
-	/// @return Contract owner.
+	/// Get user, which deployed specified contract
+	/// @dev May return zero address in case if contract is deployed
+	///  using uniquenetwork evm-migration pallet, or using other terms not
+	///  intended by pallet-evm
+	/// @dev Returns zero address if contract does not exists
+	/// @param contractAddress Contract to get owner of
+	/// @return address Owner of contract
 	fn contract_owner(&self, contract_address: address) -> Result<address> {
 		Ok(<Owner<T>>::get(contract_address))
 	}
 
 	/// Set sponsor.
-	///
-	/// @param contract_address Contract for which a sponsor is being established.
+	/// @param contractAddress Contract for which a sponsor is being established.
 	/// @param sponsor User address who set as pending sponsor.
 	fn set_sponsor(
 		&mut self,
@@ -80,7 +86,7 @@
 
 	/// Set contract as self sponsored.
 	///
-	/// @param contract_address Contract for which a self sponsoring is being enabled.
+	/// @param contractAddress Contract for which a self sponsoring is being enabled.
 	fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {
 		self.recorder().consume_sload()?;
 		self.recorder().consume_sstore()?;
@@ -93,7 +99,7 @@
 
 	/// Remove sponsor.
 	///
-	/// @param contract_address Contract for which a sponsorship is being removed.
+	/// @param contractAddress Contract for which a sponsorship is being removed.
 	fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {
 		self.recorder().consume_sload()?;
 		self.recorder().consume_sstore()?;
@@ -106,9 +112,9 @@
 
 	/// Confirm sponsorship.
 	///
-	/// @dev Caller must be same that set via [`set_sponsor`].
+	/// @dev Caller must be same that set via [`setSponsor`].
 	///
-	/// @param contract_address Сontract for which need to confirm sponsorship.
+	/// @param contractAddress Сontract for which need to confirm sponsorship.
 	fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {
 		self.recorder().consume_sload()?;
 		self.recorder().consume_sstore()?;
@@ -121,7 +127,7 @@
 
 	/// Get current sponsor.
 	///
-	/// @param contract_address The contract for which a sponsor is requested.
+	/// @param contractAddress The contract for which a sponsor is requested.
 	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
 	fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {
 		let sponsor =
@@ -138,7 +144,7 @@
 
 	/// Check tat contract has confirmed sponsor.
 	///
-	/// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
+	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
 	/// @return **true** if contract has confirmed sponsor.
 	fn has_sponsor(&self, contract_address: address) -> Result<bool> {
 		Ok(Pallet::<T>::get_sponsor(contract_address).is_some())
@@ -146,7 +152,7 @@
 
 	/// Check tat contract has pending sponsor.
 	///
-	/// @param contract_address The contract for which the presence of a pending sponsor is checked.
+	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
 	/// @return **true** if contract has pending sponsor.
 	fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {
 		Ok(match Sponsoring::<T>::get(contract_address) {
@@ -163,6 +169,7 @@
 		&mut self,
 		caller: caller,
 		contract_address: address,
+		// TODO: implement support for enums in evm-coder
 		mode: uint8,
 	) -> Result<void> {
 		self.recorder().consume_sload()?;
@@ -175,10 +182,21 @@
 		Ok(())
 	}
 
-	fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {
-		Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())
+	/// Get current contract sponsoring rate limit
+	/// @param contractAddress Contract to get sponsoring mode of
+	/// @return uint32 Amount of blocks between two sponsored transactions
+	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
+		Ok(<SponsoringRateLimit<T>>::get(contract_address)
+			.try_into()
+			.map_err(|_| "rate limit > u32::MAX")?)
 	}
 
+	/// Set contract sponsoring rate limit
+	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should
+	///  pass between two sponsored transactions
+	/// @param contractAddress Contract to change sponsoring rate limit of
+	/// @param rateLimit Target rate limit
+	/// @dev Only contract owner can change this setting
 	fn set_sponsoring_rate_limit(
 		&mut self,
 		caller: caller,
@@ -190,57 +208,70 @@
 
 		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
 		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
-
 		Ok(())
 	}
 
-	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
-		Ok(<SponsoringRateLimit<T>>::get(contract_address)
-			.try_into()
-			.map_err(|_| "rate limit > u32::MAX")?)
-	}
-
+	/// Is specified user present in contract allow list
+	/// @dev Contract owner always implicitly included
+	/// @param contractAddress Contract to check allowlist of
+	/// @param user User to check
+	/// @return bool Is specified users exists in contract allowlist
 	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
 		self.0.consume_sload()?;
 		Ok(<Pallet<T>>::allowed(contract_address, user))
 	}
 
-	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
-		Ok(<AllowlistEnabled<T>>::get(contract_address))
-	}
-
-	fn toggle_allowlist(
+	/// Toggle user presence in contract allowlist
+	/// @param contractAddress Contract to change allowlist of
+	/// @param user Which user presence should be toggled
+	/// @param isAllowed `true` if user should be allowed to be sponsored
+	///  or call this contract, `false` otherwise
+	/// @dev Only contract owner can change this setting
+	fn toggle_allowed(
 		&mut self,
 		caller: caller,
 		contract_address: address,
-		enabled: bool,
+		user: address,
+		is_allowed: bool,
 	) -> Result<void> {
 		self.recorder().consume_sload()?;
 		self.recorder().consume_sstore()?;
 
 		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
-		<Pallet<T>>::toggle_allowlist(contract_address, enabled);
+		<Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);
 
 		Ok(())
 	}
 
-	fn toggle_allowed(
+	/// Is this contract has allowlist access enabled
+	/// @dev Allowlist always can have users, and it is used for two purposes:
+	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist
+	///  in case of allowlist access enabled, only users from allowlist may call this contract
+	/// @param contractAddress Contract to get allowlist access of
+	/// @return bool Is specified contract has allowlist access enabled
+	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
+		Ok(<AllowlistEnabled<T>>::get(contract_address))
+	}
+
+	/// Toggle contract allowlist access
+	/// @param contractAddress Contract to change allowlist access of
+	/// @param enabled Should allowlist access to be enabled?
+	fn toggle_allowlist(
 		&mut self,
 		caller: caller,
 		contract_address: address,
-		user: address,
-		allowed: bool,
+		enabled: bool,
 	) -> Result<void> {
 		self.recorder().consume_sload()?;
 		self.recorder().consume_sstore()?;
 
 		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
-		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
-
+		<Pallet<T>>::toggle_allowlist(contract_address, enabled);
 		Ok(())
 	}
 }
 
+/// Implements [`OnMethodCall`], which delegates call to [`ContractHelpers`]
 pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);
 impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>
 where
@@ -283,6 +314,7 @@
 	}
 }
 
+/// Hooks into contract creation, storing owner of newly deployed contract
 pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);
 impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {
 	fn on_create(owner: H160, contract: H160) {
@@ -290,6 +322,7 @@
 	}
 }
 
+/// Bridge to pallet-sponsoring
 pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
 impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
 	for HelpersContractSponsoring<T>
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -14,7 +14,9 @@
 // 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")]
 #![cfg_attr(not(feature = "std"), no_std)]
+#![deny(missing_docs)]
 
 use codec::{Decode, Encode, MaxEncodedLen};
 pub use pallet::*;
@@ -35,13 +37,16 @@
 	pub trait Config:
 		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config
 	{
+		/// Address, under which magic contract will be available
 		type ContractAddress: Get<H160>;
+		/// In case of enabled sponsoring, but no sponsoring rate limit set,
+		/// this value will be used implicitly
 		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
 	}
 
 	#[pallet::error]
 	pub enum Error<T> {
-		/// This method is only executable by owner.
+		/// This method is only executable by contract owner
 		NoPermission,
 
 		/// No pending sponsor for contract.
@@ -217,6 +222,7 @@
 			}
 		}
 
+		/// Get current sponsoring mode, performing lazy migration from legacy storage
 		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {
 			<SponsoringMode<T>>::get(contract)
 				.or_else(|| {
@@ -225,6 +231,7 @@
 				.unwrap_or_default()
 		}
 
+		/// Reconfigure contract sponsoring mode
 		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {
 			if mode == SponsoringModeT::Disabled {
 				<SponsoringMode<T>>::remove(contract);
@@ -234,22 +241,27 @@
 			<SelfSponsoring<T>>::remove(contract)
 		}
 
+		/// Set duration between two sponsored contract calls
 		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {
 			<SponsoringRateLimit<T>>::insert(contract, rate_limit);
 		}
 
+		/// Is user added to allowlist, or he is owner of specified contract
 		pub fn allowed(contract: H160, user: H160) -> bool {
 			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
 		}
 
+		/// Toggle contract allowlist access
 		pub fn toggle_allowlist(contract: H160, enabled: bool) {
 			<AllowlistEnabled<T>>::insert(contract, enabled)
 		}
 
+		/// Toggle user presence in contract's allowlist
 		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {
 			<Allowlist<T>>::insert(contract, user, allowed);
 		}
 
+		/// Throw error if user is not allowed to reconfigure target contract
 		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {
 			ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);
 			Ok(())
@@ -257,10 +269,15 @@
 	}
 }
 
-#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)]
+/// Available contract sponsoring modes
+#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]
 pub enum SponsoringModeT {
+	/// Sponsoring is disabled
+	#[default]
 	Disabled,
+	/// Only users from allowlist will be sponsored
 	Allowlisted,
+	/// All users will be sponsored
 	Generous,
 }
 
@@ -279,11 +296,5 @@
 			SponsoringModeT::Allowlisted => 1,
 			SponsoringModeT::Generous => 2,
 		}
-	}
-}
-
-impl Default for SponsoringModeT {
-	fn default() -> Self {
-		Self::Disabled
 	}
 }
modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	address field_0;
-	uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -27,14 +21,18 @@
 	}
 }
 
-// Selector: 6073d917
+/// @title Magic contract, which allows users to reconfigure other contracts
+/// @dev the ERC-165 identifier for this interface is 0xd77fab70
 contract ContractHelpers is Dummy, ERC165 {
-	// Get contract ovner
-	//
-	// @param Contract_address contract for which the owner is being determined.
-	// @return Contract owner.
-	//
-	// Selector: contractOwner(address) 5152b14c
+	/// Get user, which deployed specified contract
+	/// @dev May return zero address in case if contract is deployed
+	///  using uniquenetwork evm-migration pallet, or using other terms not
+	///  intended by pallet-evm
+	/// @dev Returns zero address if contract does not exists
+	/// @param contractAddress Contract to get owner of
+	/// @return address Owner of contract
+	/// @dev EVM selector for this function is: 0x5152b14c,
+	///  or in textual repr: contractOwner(address)
 	function contractOwner(address contractAddress)
 		public
 		view
@@ -46,12 +44,11 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Set sponsor.
-	//
-	// @param contract_address Contract for which a sponsor is being established.
-	// @param sponsor User address who set as pending sponsor.
-	//
-	// Selector: setSponsor(address,address) f01fba93
+	/// Set sponsor.
+	/// @param contractAddress Contract for which a sponsor is being established.
+	/// @param sponsor User address who set as pending sponsor.
+	/// @dev EVM selector for this function is: 0xf01fba93,
+	///  or in textual repr: setSponsor(address,address)
 	function setSponsor(address contractAddress, address sponsor) public {
 		require(false, stub_error);
 		contractAddress;
@@ -59,47 +56,47 @@
 		dummy = 0;
 	}
 
-	// Set contract as self sponsored.
-	//
-	// @param contract_address Contract for which a self sponsoring is being enabled.
-	//
-	// Selector: selfSponsoredEnable(address) 89f7d9ae
+	/// Set contract as self sponsored.
+	///
+	/// @param contractAddress Contract for which a self sponsoring is being enabled.
+	/// @dev EVM selector for this function is: 0x89f7d9ae,
+	///  or in textual repr: selfSponsoredEnable(address)
 	function selfSponsoredEnable(address contractAddress) public {
 		require(false, stub_error);
 		contractAddress;
 		dummy = 0;
 	}
 
-	// Remove sponsor.
-	//
-	// @param contract_address Contract for which a sponsorship is being removed.
-	//
-	// Selector: removeSponsor(address) ef784250
+	/// Remove sponsor.
+	///
+	/// @param contractAddress Contract for which a sponsorship is being removed.
+	/// @dev EVM selector for this function is: 0xef784250,
+	///  or in textual repr: removeSponsor(address)
 	function removeSponsor(address contractAddress) public {
 		require(false, stub_error);
 		contractAddress;
 		dummy = 0;
 	}
 
-	// Confirm sponsorship.
-	//
-	// @dev Caller must be same that set via [`set_sponsor`].
-	//
-	// @param contract_address Сontract for which need to confirm sponsorship.
-	//
-	// Selector: confirmSponsorship(address) abc00001
+	/// Confirm sponsorship.
+	///
+	/// @dev Caller must be same that set via [`setSponsor`].
+	///
+	/// @param contractAddress Сontract for which need to confirm sponsorship.
+	/// @dev EVM selector for this function is: 0xabc00001,
+	///  or in textual repr: confirmSponsorship(address)
 	function confirmSponsorship(address contractAddress) public {
 		require(false, stub_error);
 		contractAddress;
 		dummy = 0;
 	}
 
-	// Get current sponsor.
-	//
-	// @param contract_address The contract for which a sponsor is requested.
-	// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	//
-	// Selector: getSponsor(address) 743fc745
+	/// Get current sponsor.
+	///
+	/// @param contractAddress The contract for which a sponsor is requested.
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x743fc745,
+	///  or in textual repr: getSponsor(address)
 	function getSponsor(address contractAddress)
 		public
 		view
@@ -111,12 +108,12 @@
 		return Tuple0(0x0000000000000000000000000000000000000000, 0);
 	}
 
-	// Check tat contract has confirmed sponsor.
-	//
-	// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
-	// @return **true** if contract has confirmed sponsor.
-	//
-	// Selector: hasSponsor(address) 97418603
+	/// Check tat contract has confirmed sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
+	/// @return **true** if contract has confirmed sponsor.
+	/// @dev EVM selector for this function is: 0x97418603,
+	///  or in textual repr: hasSponsor(address)
 	function hasSponsor(address contractAddress) public view returns (bool) {
 		require(false, stub_error);
 		contractAddress;
@@ -124,12 +121,12 @@
 		return false;
 	}
 
-	// Check tat contract has pending sponsor.
-	//
-	// @param contract_address The contract for which the presence of a pending sponsor is checked.
-	// @return **true** if contract has pending sponsor.
-	//
-	// Selector: hasPendingSponsor(address) 39b9b242
+	/// Check tat contract has pending sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
+	/// @return **true** if contract has pending sponsor.
+	/// @dev EVM selector for this function is: 0x39b9b242,
+	///  or in textual repr: hasPendingSponsor(address)
 	function hasPendingSponsor(address contractAddress)
 		public
 		view
@@ -141,7 +138,8 @@
 		return false;
 	}
 
-	// Selector: sponsoringEnabled(address) 6027dc61
+	/// @dev EVM selector for this function is: 0x6027dc61,
+	///  or in textual repr: sponsoringEnabled(address)
 	function sponsoringEnabled(address contractAddress)
 		public
 		view
@@ -153,7 +151,8 @@
 		return false;
 	}
 
-	// Selector: setSponsoringMode(address,uint8) fde8a560
+	/// @dev EVM selector for this function is: 0xfde8a560,
+	///  or in textual repr: setSponsoringMode(address,uint8)
 	function setSponsoringMode(address contractAddress, uint8 mode) public {
 		require(false, stub_error);
 		contractAddress;
@@ -161,11 +160,15 @@
 		dummy = 0;
 	}
 
-	// Selector: sponsoringMode(address) b70c7267
-	function sponsoringMode(address contractAddress)
+	/// Get current contract sponsoring rate limit
+	/// @param contractAddress Contract to get sponsoring mode of
+	/// @return uint32 Amount of blocks between two sponsored transactions
+	/// @dev EVM selector for this function is: 0x610cfabd,
+	///  or in textual repr: getSponsoringRateLimit(address)
+	function getSponsoringRateLimit(address contractAddress)
 		public
 		view
-		returns (uint8)
+		returns (uint32)
 	{
 		require(false, stub_error);
 		contractAddress;
@@ -173,7 +176,14 @@
 		return 0;
 	}
 
-	// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
+	/// Set contract sponsoring rate limit
+	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should
+	///  pass between two sponsored transactions
+	/// @param contractAddress Contract to change sponsoring rate limit of
+	/// @param rateLimit Target rate limit
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x77b6c908,
+	///  or in textual repr: setSponsoringRateLimit(address,uint32)
 	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
 		public
 	{
@@ -183,32 +193,53 @@
 		dummy = 0;
 	}
 
-	// Selector: getSponsoringRateLimit(address) 610cfabd
-	function getSponsoringRateLimit(address contractAddress)
+	/// Is specified user present in contract allow list
+	/// @dev Contract owner always implicitly included
+	/// @param contractAddress Contract to check allowlist of
+	/// @param user User to check
+	/// @return bool Is specified users exists in contract allowlist
+	/// @dev EVM selector for this function is: 0x5c658165,
+	///  or in textual repr: allowed(address,address)
+	function allowed(address contractAddress, address user)
 		public
 		view
-		returns (uint32)
+		returns (bool)
 	{
 		require(false, stub_error);
 		contractAddress;
+		user;
 		dummy;
-		return 0;
+		return false;
 	}
 
-	// Selector: allowed(address,address) 5c658165
-	function allowed(address contractAddress, address user)
-		public
-		view
-		returns (bool)
-	{
+	/// Toggle user presence in contract allowlist
+	/// @param contractAddress Contract to change allowlist of
+	/// @param user Which user presence should be toggled
+	/// @param isAllowed `true` if user should be allowed to be sponsored
+	///  or call this contract, `false` otherwise
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x4706cc1c,
+	///  or in textual repr: toggleAllowed(address,address,bool)
+	function toggleAllowed(
+		address contractAddress,
+		address user,
+		bool isAllowed
+	) public {
 		require(false, stub_error);
 		contractAddress;
 		user;
-		dummy;
-		return false;
+		isAllowed;
+		dummy = 0;
 	}
 
-	// Selector: allowlistEnabled(address) c772ef6c
+	/// Is this contract has allowlist access enabled
+	/// @dev Allowlist always can have users, and it is used for two purposes:
+	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist
+	///  in case of allowlist access enabled, only users from allowlist may call this contract
+	/// @param contractAddress Contract to get allowlist access of
+	/// @return bool Is specified contract has allowlist access enabled
+	/// @dev EVM selector for this function is: 0xc772ef6c,
+	///  or in textual repr: allowlistEnabled(address)
 	function allowlistEnabled(address contractAddress)
 		public
 		view
@@ -220,24 +251,21 @@
 		return false;
 	}
 
-	// Selector: toggleAllowlist(address,bool) 36de20f5
+	/// Toggle contract allowlist access
+	/// @param contractAddress Contract to change allowlist access of
+	/// @param enabled Should allowlist access to be enabled?
+	/// @dev EVM selector for this function is: 0x36de20f5,
+	///  or in textual repr: toggleAllowlist(address,bool)
 	function toggleAllowlist(address contractAddress, bool enabled) public {
 		require(false, stub_error);
 		contractAddress;
 		enabled;
 		dummy = 0;
 	}
+}
 
-	// Selector: toggleAllowed(address,address,bool) 4706cc1c
-	function toggleAllowed(
-		address contractAddress,
-		address user,
-		bool allowed
-	) public {
-		require(false, stub_error);
-		contractAddress;
-		user;
-		allowed;
-		dummy = 0;
-	}
+/// @dev anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
 }
addedpallets/evm-migration/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-migration/README.md
@@ -0,0 +1,18 @@
+# EVM contract migration pallet
+
+This pallet is only callable by root, it has functionality to migrate contract
+from other ethereum chain to pallet-evm
+
+Contract data includes contract code, and contract storage,
+where contract storage is a mapping from evm word to evm word (evm word = 32 byte)
+
+To import contract data into pallet-evm admin should call this pallet multiple times:
+1. Start migration via `begin`
+2. Insert all contract data using single or
+   multiple (If data can't be fit into single extrinsic) calls
+   to `set_data`
+3. Finish migration using `finish`, providing contract code
+
+During migration no one can insert code at address of this contract,
+as [`pallet::OnMethodCall`] prevents this, and no one can call this contract,
+as code is only supplied at final stage of contract deployment
\ No newline at end of file
modifiedpallets/evm-migration/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/evm-migration/src/benchmarking.rs
+++ b/pallets/evm-migration/src/benchmarking.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/>.
 
+#![allow(missing_docs)]
+
 use super::{Call, Config, Pallet};
 use frame_benchmarking::benchmarks;
 use frame_system::RawOrigin;
modifiedpallets/evm-migration/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-migration/src/lib.rs
+++ b/pallets/evm-migration/src/lib.rs
@@ -14,7 +14,9 @@
 // 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")]
 #![cfg_attr(not(feature = "std"), no_std)]
+#![deny(missing_docs)]
 
 pub use pallet::*;
 #[cfg(feature = "runtime-benchmarks")]
@@ -32,6 +34,7 @@
 
 	#[pallet::config]
 	pub trait Config: frame_system::Config + pallet_evm::Config {
+		/// Weights
 		type WeightInfo: WeightInfo;
 	}
 
@@ -43,7 +46,9 @@
 
 	#[pallet::error]
 	pub enum Error<T> {
+		/// Can only migrate to empty address.
 		AccountNotEmpty,
+		/// Migration of this account is not yet started, or already finished.
 		AccountIsNotMigrating,
 	}
 
@@ -53,6 +58,8 @@
 
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
+		/// Start contract migration, inserts contract stub at target address,
+		/// and marks account as pending, allowing to insert storage
 		#[pallet::weight(<SelfWeightOf<T>>::begin())]
 		pub fn begin(origin: OriginFor<T>, address: H160) -> DispatchResult {
 			ensure_root(origin)?;
@@ -65,6 +72,8 @@
 			Ok(())
 		}
 
+		/// Insert items into contract storage, this method can be called
+		/// multiple times
 		#[pallet::weight(<SelfWeightOf<T>>::set_data(data.len() as u32))]
 		pub fn set_data(
 			origin: OriginFor<T>,
@@ -83,6 +92,9 @@
 			Ok(())
 		}
 
+		/// Finish contract migration, allows it to be called.
+		/// It is not possible to alter contract storage via [`Self::set_data`]
+		/// after this call.
 		#[pallet::weight(<SelfWeightOf<T>>::finish(code.len() as u32))]
 		pub fn finish(origin: OriginFor<T>, address: H160, code: Vec<u8>) -> DispatchResult {
 			ensure_root(origin)?;
@@ -97,6 +109,7 @@
 		}
 	}
 
+	/// Implements [`pallet_evm::OnMethodCall`], which reserves accounts with pending migration
 	pub struct OnMethodCall<T>(PhantomData<T>);
 	impl<T: Config> pallet_evm::OnMethodCall<T> for OnMethodCall<T> {
 		fn is_reserved(contract: &H160) -> bool {
modifiedpallets/evm-migration/src/weights.rsdiffbeforeafterboth
--- a/pallets/evm-migration/src/weights.rs
+++ b/pallets/evm-migration/src/weights.rs
@@ -26,6 +26,7 @@
 #![cfg_attr(rustfmt, rustfmt_skip)]
 #![allow(unused_parens)]
 #![allow(unused_imports)]
+#![allow(missing_docs)]
 #![allow(clippy::unnecessary_cast)]
 
 use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
addedpallets/evm-transaction-payment/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-transaction-payment/README.md
@@ -0,0 +1,5 @@
+# Evm transaction payment pallet
+
+pallet-evm-transaction-payment is a bridge between pallet-evm substrate calls and pallet-sponsoring.
+It doesn't provide any sponsoring logic by itself, instead all sponsoring handlers
+are loosly coupled via [`Config::EvmSponsorshipHandler`] trait.
\ No newline at end of file
modifiedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-transaction-payment/src/lib.rs
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -14,7 +14,9 @@
 // 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")]
 #![cfg_attr(not(feature = "std"), no_std)]
+#![deny(missing_docs)]
 
 use core::marker::PhantomData;
 use fp_evm::WithdrawReason;
@@ -29,13 +31,12 @@
 pub mod pallet {
 	use super::*;
 
-	use frame_support::traits::Currency;
 	use sp_std::vec::Vec;
 
 	#[pallet::config]
 	pub trait Config: frame_system::Config + pallet_evm::account::Config {
+		/// Loosly-coupled handlers for evm call sponsoring
 		type EvmSponsorshipHandler: SponsorshipHandler<Self::CrossAccountId, (H160, Vec<u8>)>;
-		type Currency: Currency<Self::AccountId>;
 	}
 
 	#[pallet::pallet]
@@ -43,6 +44,7 @@
 	pub struct Pallet<T>(_);
 }
 
+/// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm
 pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
 impl<T: Config> fp_evm::TransactionValidityHack<T::CrossAccountId> for TransactionValidityHack<T> {
 	fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option<T::CrossAccountId> {
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -50,7 +50,7 @@
 	},
 }
 
-#[solidity_interface(name = "ERC20", events(ERC20Events))]
+#[solidity_interface(name = ERC20, events(ERC20Events))]
 impl<T: Config> FungibleHandle<T> {
 	fn name(&self) -> Result<string> {
 		Ok(decode_utf16(self.name.iter().copied())
@@ -129,7 +129,7 @@
 	}
 }
 
-#[solidity_interface(name = "ERC20UniqueExtensions")]
+#[solidity_interface(name = ERC20UniqueExtensions)]
 impl<T: Config> FungibleHandle<T> {
 	#[weight(<SelfWeightOf<T>>::burn_from())]
 	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
@@ -147,11 +147,11 @@
 }
 
 #[solidity_interface(
-	name = "UniqueFungible",
+	name = UniqueFungible,
 	is(
 		ERC20,
 		ERC20UniqueExtensions,
-		via("CollectionHandle<T>", common_mut, Collection)
+		Collection(common_mut, CollectionHandle<T>),
 	)
 )]
 impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -19,122 +19,17 @@
 		interfaceID;
 		return true;
 	}
-}
-
-// Inline
-contract ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
 }
 
-// Selector: 79cc6790
-contract ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		from;
-		amount;
-		dummy = 0;
-		return false;
-	}
-}
-
-// Selector: 942e8b22
-contract ERC20 is Dummy, ERC165, ERC20Events {
-	// Selector: name() 06fdde03
-	function name() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	// Selector: symbol() 95d89b41
-	function symbol() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: decimals() 313ce567
-	function decimals() public view returns (uint8) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
-		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
-	}
-
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) public returns (bool) {
-		require(false, stub_error);
-		from;
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address spender, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		spender;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: allowance(address,address) dd62ed3e
-	function allowance(address owner, address spender)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		spender;
-		dummy;
-		return 0;
-	}
-}
-
-// Selector: ffe4da23
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
 contract Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
 	function setCollectionProperty(string memory key, bytes memory value)
 		public
 	{
@@ -144,25 +39,25 @@
 		dummy = 0;
 	}
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
 	function deleteCollectionProperty(string memory key) public {
 		require(false, stub_error);
 		key;
 		dummy = 0;
 	}
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
 	function collectionProperty(string memory key)
 		public
 		view
@@ -174,41 +69,41 @@
 		return hex"";
 	}
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
 	function setCollectionSponsor(address sponsor) public {
 		require(false, stub_error);
 		sponsor;
 		dummy = 0;
 	}
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
 	function confirmCollectionSponsorship() public {
 		require(false, stub_error);
 		dummy = 0;
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
 	function setCollectionLimit(string memory limit, uint32 value) public {
 		require(false, stub_error);
 		limit;
@@ -216,15 +111,15 @@
 		dummy = 0;
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
 	function setCollectionLimit(string memory limit, bool value) public {
 		require(false, stub_error);
 		limit;
@@ -232,73 +127,73 @@
 		dummy = 0;
 	}
 
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
 	function contractAddress() public view returns (address) {
 		require(false, stub_error);
 		dummy;
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
 	function addCollectionAdminSubstrate(uint256 newAdmin) public {
 		require(false, stub_error);
 		newAdmin;
 		dummy = 0;
 	}
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
 	function removeCollectionAdminSubstrate(uint256 admin) public {
 		require(false, stub_error);
 		admin;
 		dummy = 0;
 	}
 
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
 	function addCollectionAdmin(address newAdmin) public {
 		require(false, stub_error);
 		newAdmin;
 		dummy = 0;
 	}
 
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
 	function removeCollectionAdmin(address admin) public {
 		require(false, stub_error);
 		admin;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
 	function setCollectionNesting(bool enable) public {
 		require(false, stub_error);
 		enable;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
 	function setCollectionNesting(bool enable, address[] memory collections)
 		public
 	{
@@ -308,57 +203,57 @@
 		dummy = 0;
 	}
 
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
 	function setCollectionAccess(uint8 mode) public {
 		require(false, stub_error);
 		mode;
 		dummy = 0;
 	}
 
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
 	function addToCollectionAllowList(address user) public {
 		require(false, stub_error);
 		user;
 		dummy = 0;
 	}
 
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
 	function removeFromCollectionAllowList(address user) public {
 		require(false, stub_error);
 		user;
 		dummy = 0;
 	}
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
 	function setCollectionMintMode(bool mode) public {
 		require(false, stub_error);
 		mode;
 		dummy = 0;
 	}
 
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
 	function isOwnerOrAdmin(address user) public view returns (bool) {
 		require(false, stub_error);
 		user;
@@ -366,12 +261,12 @@
 		return false;
 	}
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
 	function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
 		require(false, stub_error);
 		user;
@@ -379,35 +274,35 @@
 		return false;
 	}
 
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() public returns (string memory) {
 		require(false, stub_error);
 		dummy = 0;
 		return "";
 	}
 
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
 	function setOwner(address newOwner) public {
 		require(false, stub_error);
 		newOwner;
 		dummy = 0;
 	}
 
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
 	function setOwnerSubstrate(uint256 newOwner) public {
 		require(false, stub_error);
 		newOwner;
@@ -415,6 +310,122 @@
 	}
 }
 
+/// @dev the ERC-165 identifier for this interface is 0x79cc6790
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
+	function burnFrom(address from, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		from;
+		amount;
+		dummy = 0;
+		return false;
+	}
+}
+
+/// @dev inlined interface
+contract ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+contract ERC20 is Dummy, ERC165, ERC20Events {
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
+	function decimals() public view returns (uint8) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) public returns (bool) {
+		require(false, stub_error);
+		from;
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address spender, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		spender;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
+	function allowance(address owner, address spender)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		spender;
+		dummy;
+		return 0;
+	}
+}
+
 contract UniqueFungible is
 	Dummy,
 	ERC165,
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -50,14 +50,14 @@
 };
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-#[solidity_interface(name = "TokenProperties")]
+#[solidity_interface(name = TokenProperties)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
 	/// @param key Property key.
-	/// @param is_mutable Permission to mutate property.
-	/// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	/// @param token_owner Permission to mutate property by token owner if property is mutable.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
 	fn set_token_property_permission(
 		&mut self,
 		caller: caller,
@@ -201,7 +201,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 /// @dev See https://eips.ethereum.org/EIPS/eip-721
-#[solidity_interface(name = "ERC721Metadata")]
+#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice A descriptive name for a collection of NFTs in this contract
 	fn name(&self) -> Result<string> {
@@ -262,7 +262,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 /// @dev See https://eips.ethereum.org/EIPS/eip-721
-#[solidity_interface(name = "ERC721Enumerable")]
+#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice Enumerate valid NFTs
 	/// @param index A counter less than `totalSupply()`
@@ -289,7 +289,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-#[solidity_interface(name = "ERC721", events(ERC721Events))]
+#[solidity_interface(name = ERC721, events(ERC721Events), expect_selector = 0x80ac58cd)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice Count all NFTs assigned to an owner
 	/// @dev NFTs assigned to the zero address are considered invalid, and this
@@ -316,13 +316,13 @@
 			.as_eth())
 	}
 	/// @dev Not implemented
+	#[solidity(rename_selector = "safeTransferFrom")]
 	fn safe_transfer_from_with_data(
 		&mut self,
 		_from: address,
 		_to: address,
 		_token_id: uint256,
 		_data: bytes,
-		_value: value,
 	) -> Result<void> {
 		// TODO: Not implemetable
 		Err("not implemented".into())
@@ -333,7 +333,6 @@
 		_from: address,
 		_to: address,
 		_token_id: uint256,
-		_value: value,
 	) -> Result<void> {
 		// TODO: Not implemetable
 		Err("not implemented".into())
@@ -348,7 +347,6 @@
 	/// @param from The current owner of the NFT
 	/// @param to The new owner
 	/// @param tokenId The NFT to transfer
-	/// @param _value Not used for an NFT
 	#[weight(<SelfWeightOf<T>>::transfer_from())]
 	fn transfer_from(
 		&mut self,
@@ -356,7 +354,6 @@
 		from: address,
 		to: address,
 		token_id: uint256,
-		_value: value,
 	) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let from = T::CrossAccountId::from_eth(from);
@@ -378,13 +375,7 @@
 	/// @param approved The new approved NFT controller
 	/// @param tokenId The NFT to approve
 	#[weight(<SelfWeightOf<T>>::approve())]
-	fn approve(
-		&mut self,
-		caller: caller,
-		approved: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn approve(&mut self, caller: caller, approved: address, token_id: uint256) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let approved = T::CrossAccountId::from_eth(approved);
 		let token = token_id.try_into()?;
@@ -419,7 +410,7 @@
 }
 
 /// @title ERC721 Token that can be irreversibly burned (destroyed).
-#[solidity_interface(name = "ERC721Burnable")]
+#[solidity_interface(name = ERC721Burnable)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice Burns a specific ERC721 token.
 	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
@@ -436,7 +427,7 @@
 }
 
 /// @title ERC721 minting logic.
-#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
+#[solidity_interface(name = ERC721Mintable, events(ERC721MintableEvents))]
 impl<T: Config> NonfungibleHandle<T> {
 	fn minting_finished(&self) -> Result<bool> {
 		Ok(false)
@@ -597,22 +588,15 @@
 }
 
 /// @title Unique extensions for ERC721.
-#[solidity_interface(name = "ERC721UniqueExtensions")]
+#[solidity_interface(name = ERC721UniqueExtensions)]
 impl<T: Config> NonfungibleHandle<T> {
 	/// @notice Transfer ownership of an NFT
 	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
 	///  is the zero address. Throws if `tokenId` is not a valid NFT.
 	/// @param to The new owner
 	/// @param tokenId The NFT to transfer
-	/// @param _value Not used for an NFT
 	#[weight(<SelfWeightOf<T>>::transfer())]
-	fn transfer(
-		&mut self,
-		caller: caller,
-		to: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let to = T::CrossAccountId::from_eth(to);
 		let token = token_id.try_into()?;
@@ -630,15 +614,8 @@
 	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
 	/// @param from The current owner of the NFT
 	/// @param tokenId The NFT to transfer
-	/// @param _value Not used for an NFT
 	#[weight(<SelfWeightOf<T>>::burn_from())]
-	fn burn_from(
-		&mut self,
-		caller: caller,
-		from: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let from = T::CrossAccountId::from_eth(from);
 		let token = token_id.try_into()?;
@@ -751,7 +728,7 @@
 }
 
 #[solidity_interface(
-	name = "UniqueNFT",
+	name = UniqueNFT,
 	is(
 		ERC721,
 		ERC721Metadata,
@@ -759,7 +736,7 @@
 		ERC721UniqueExtensions,
 		ERC721Mintable,
 		ERC721Burnable,
-		via("CollectionHandle<T>", common_mut, Collection),
+		Collection(common_mut, CollectionHandle<T>),
 		TokenProperties,
 	)
 )]
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	uint256 field_0;
-	string field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -27,40 +21,17 @@
 	}
 }
 
-// Inline
-contract ERC721Events {
-	event Transfer(
-		address indexed from,
-		address indexed to,
-		uint256 indexed tokenId
-	);
-	event Approval(
-		address indexed owner,
-		address indexed approved,
-		uint256 indexed tokenId
-	);
-	event ApprovalForAll(
-		address indexed owner,
-		address indexed operator,
-		bool approved
-	);
-}
-
-// Inline
-contract ERC721MintableEvents {
-	event MintingFinished();
-}
-
-// Selector: 41369377
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0x41369377
 contract TokenProperties is Dummy, ERC165 {
-	// @notice Set permissions for token property.
-	// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// @param key Property key.
-	// @param is_mutable Permission to mutate property.
-	// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	// @param token_owner Permission to mutate property by token owner if property is mutable.
-	//
-	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param key Property key.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	/// @dev EVM selector for this function is: 0x222d97fa,
+	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
 	function setTokenPropertyPermission(
 		string memory key,
 		bool isMutable,
@@ -75,13 +46,13 @@
 		dummy = 0;
 	}
 
-	// @notice Set token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @param value Property value.
-	//
-	// Selector: setProperty(uint256,string,bytes) 1752d67b
+	/// @notice Set token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @param value Property value.
+	/// @dev EVM selector for this function is: 0x1752d67b,
+	///  or in textual repr: setProperty(uint256,string,bytes)
 	function setProperty(
 		uint256 tokenId,
 		string memory key,
@@ -94,12 +65,12 @@
 		dummy = 0;
 	}
 
-	// @notice Delete token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	//
-	// Selector: deleteProperty(uint256,string) 066111d1
+	/// @notice Delete token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x066111d1,
+	///  or in textual repr: deleteProperty(uint256,string)
 	function deleteProperty(uint256 tokenId, string memory key) public {
 		require(false, stub_error);
 		tokenId;
@@ -107,13 +78,13 @@
 		dummy = 0;
 	}
 
-	// @notice Get token property value.
-	// @dev Throws error if key not found
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @return Property value bytes
-	//
-	// Selector: property(uint256,string) 7228c327
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
 	function property(uint256 tokenId, string memory key)
 		public
 		view
@@ -127,213 +98,334 @@
 	}
 }
 
-// Selector: 42966c68
-contract ERC721Burnable is Dummy, ERC165 {
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	//  operator of the current owner.
-	// @param tokenId The NFT to approve
-	//
-	// Selector: burn(uint256) 42966c68
-	function burn(uint256 tokenId) public {
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
+contract Collection is Dummy, ERC165 {
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
+	function setCollectionProperty(string memory key, bytes memory value)
+		public
+	{
 		require(false, stub_error);
-		tokenId;
+		key;
+		value;
 		dummy = 0;
 	}
-}
 
-// Selector: 58800161
-contract ERC721 is Dummy, ERC165, ERC721Events {
-	// @notice Count all NFTs assigned to an owner
-	// @dev NFTs assigned to the zero address are considered invalid, and this
-	//  function throws for queries about the zero address.
-	// @param owner An address for whom to query the balance
-	// @return The number of NFTs owned by `owner`, possibly zero
-	//
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
+	function deleteCollectionProperty(string memory key) public {
 		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
+		key;
+		dummy = 0;
 	}
 
-	// @notice Find the owner of an NFT
-	// @dev NFTs assigned to zero address are considered invalid, and queries
-	//  about them do throw.
-	// @param tokenId The identifier for an NFT
-	// @return The address of the owner of the NFT
-	//
-	// Selector: ownerOf(uint256) 6352211e
-	function ownerOf(uint256 tokenId) public view returns (address) {
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key)
+		public
+		view
+		returns (bytes memory)
+	{
 		require(false, stub_error);
-		tokenId;
+		key;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return hex"";
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
-	function safeTransferFromWithData(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) public {
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
+	function setCollectionSponsor(address sponsor) public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
-		data;
+		sponsor;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
 		dummy = 0;
 	}
 
-	// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
-	//  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	//  THEY MAY BE PERMANENTLY LOST
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this NFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param from The current owner of the NFT
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
+	function setCollectionLimit(string memory limit, uint32 value) public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
+		limit;
+		value;
 		dummy = 0;
 	}
 
-	// @notice Set or reaffirm the approved address for an NFT
-	// @dev The zero address indicates there is no approved address.
-	// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	//  operator of the current owner.
-	// @param approved The new approved NFT controller
-	// @param tokenId The NFT to approve
-	//
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address approved, uint256 tokenId) public {
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
+	function setCollectionLimit(string memory limit, bool value) public {
 		require(false, stub_error);
-		approved;
-		tokenId;
+		limit;
+		value;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: setApprovalForAll(address,bool) a22cb465
-	function setApprovalForAll(address operator, bool approved) public {
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
+	function addCollectionAdminSubstrate(uint256 newAdmin) public {
+		require(false, stub_error);
+		newAdmin;
+		dummy = 0;
+	}
+
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
+	function removeCollectionAdminSubstrate(uint256 admin) public {
+		require(false, stub_error);
+		admin;
+		dummy = 0;
+	}
+
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
+	function addCollectionAdmin(address newAdmin) public {
+		require(false, stub_error);
+		newAdmin;
+		dummy = 0;
+	}
+
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
+	function removeCollectionAdmin(address admin) public {
 		require(false, stub_error);
-		operator;
-		approved;
+		admin;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: getApproved(uint256) 081812fc
-	function getApproved(uint256 tokenId) public view returns (address) {
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
+	function setCollectionNesting(bool enable) public {
 		require(false, stub_error);
-		tokenId;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		enable;
+		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: isApprovedForAll(address,address) e985e9c5
-	function isApprovedForAll(address owner, address operator)
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
+	function setCollectionNesting(bool enable, address[] memory collections)
 		public
-		view
-		returns (address)
 	{
 		require(false, stub_error);
-		owner;
-		operator;
+		enable;
+		collections;
+		dummy = 0;
+	}
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(uint8 mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
+	function addToCollectionAllowList(address user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
+	function removeFromCollectionAllowList(address user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
+	function isOwnerOrAdmin(address user) public view returns (bool) {
+		require(false, stub_error);
+		user;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return false;
 	}
-}
 
-// Selector: 5b5e139f
-contract ERC721Metadata is Dummy, ERC165 {
-	// @notice A descriptive name for a collection of NFTs in this contract
-	//
-	// Selector: name() 06fdde03
-	function name() public view returns (string memory) {
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
+	function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
 		require(false, stub_error);
+		user;
 		dummy;
+		return false;
+	}
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() public returns (string memory) {
+		require(false, stub_error);
+		dummy = 0;
 		return "";
 	}
 
-	// @notice An abbreviated name for NFTs in this contract
-	//
-	// Selector: symbol() 95d89b41
-	function symbol() public view returns (string memory) {
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
+	function setOwner(address newOwner) public {
+		require(false, stub_error);
+		newOwner;
+		dummy = 0;
+	}
+
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
+	function setOwnerSubstrate(uint256 newOwner) public {
 		require(false, stub_error);
-		dummy;
-		return "";
+		newOwner;
+		dummy = 0;
 	}
+}
 
-	// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	//
-	// @dev If the token has a `url` property and it is not empty, it is returned.
-	//  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	//  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	//  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	//  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	//
-	// @return token's const_metadata
-	//
-	// Selector: tokenURI(uint256) c87b56dd
-	function tokenURI(uint256 tokenId) public view returns (string memory) {
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+contract ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) public {
 		require(false, stub_error);
 		tokenId;
-		dummy;
-		return "";
+		dummy = 0;
 	}
 }
 
-// Selector: 68ccfe89
+/// @dev inlined interface
+contract ERC721MintableEvents {
+	event MintingFinished();
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x68ccfe89
 contract ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
-	// Selector: mintingFinished() 05d2035b
+	/// @dev EVM selector for this function is: 0x05d2035b,
+	///  or in textual repr: mintingFinished()
 	function mintingFinished() public view returns (bool) {
 		require(false, stub_error);
 		dummy;
 		return false;
 	}
 
-	// @notice Function to mint token.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted NFT
-	//
-	// Selector: mint(address,uint256) 40c10f19
+	/// @notice Function to mint token.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted NFT
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
 	function mint(address to, uint256 tokenId) public returns (bool) {
 		require(false, stub_error);
 		to;
@@ -342,14 +434,14 @@
 		return false;
 	}
 
-	// @notice Function to mint token with the given tokenUri.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted NFT
-	// @param tokenUri Token URI that would be stored in the NFT properties
-	//
-	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	/// @notice Function to mint token with the given tokenUri.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted NFT
+	/// @param tokenUri Token URI that would be stored in the NFT properties
+	/// @dev EVM selector for this function is: 0x50bb4e7f,
+	///  or in textual repr: mintWithTokenURI(address,uint256,string)
 	function mintWithTokenURI(
 		address to,
 		uint256 tokenId,
@@ -363,9 +455,9 @@
 		return false;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: finishMinting() 7d64bcb4
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x7d64bcb4,
+	///  or in textual repr: finishMinting()
 	function finishMinting() public returns (bool) {
 		require(false, stub_error);
 		dummy = 0;
@@ -373,58 +465,16 @@
 	}
 }
 
-// Selector: 780e9d63
-contract ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid NFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
-	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @dev Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @notice Count NFTs tracked by this contract
-	// @return A count of valid NFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-}
-
-// Selector: d74d154f
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0xd74d154f
 contract ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an NFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @notice Transfer ownership of an NFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 tokenId) public {
 		require(false, stub_error);
 		to;
@@ -432,15 +482,14 @@
 		dummy = 0;
 	}
 
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this NFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param from The current owner of the NFT
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
 	function burnFrom(address from, uint256 tokenId) public {
 		require(false, stub_error);
 		from;
@@ -448,22 +497,22 @@
 		dummy = 0;
 	}
 
-	// @notice Returns next free NFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
+	/// @notice Returns next free NFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
 	function nextTokenId() public view returns (uint256) {
 		require(false, stub_error);
 		dummy;
 		return 0;
 	}
 
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted NFTs
-	//
-	// Selector: mintBulk(address,uint256[]) 44a9945e
+	/// @notice Function to mint multiple tokens.
+	/// @dev `tokenIds` should be an array of consecutive numbers and first number
+	///  should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokenIds IDs of the minted NFTs
+	/// @dev EVM selector for this function is: 0x44a9945e,
+	///  or in textual repr: mintBulk(address,uint256[])
 	function mintBulk(address to, uint256[] memory tokenIds)
 		public
 		returns (bool)
@@ -475,14 +524,14 @@
 		return false;
 	}
 
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
-	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+	/// @notice Function to mint multiple tokens with the given tokenUris.
+	/// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	///  numbers and first number should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokens array of pairs of token ID and token URI for minted tokens
+	/// @dev EVM selector for this function is: 0x36543006,
+	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens)
 		public
 		returns (bool)
 	{
@@ -494,291 +543,251 @@
 	}
 }
 
-// Selector: ffe4da23
-contract Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		public
-	{
-		require(false, stub_error);
-		key;
-		value;
-		dummy = 0;
-	}
+/// @dev anonymous struct
+struct Tuple8 {
+	uint256 field_0;
+	string field_1;
+}
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) public {
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid NFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) public view returns (uint256) {
 		require(false, stub_error);
-		key;
-		dummy = 0;
+		index;
+		dummy;
+		return 0;
 	}
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index)
 		public
 		view
-		returns (bytes memory)
+		returns (uint256)
 	{
 		require(false, stub_error);
-		key;
+		owner;
+		index;
 		dummy;
-		return hex"";
+		return 0;
 	}
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) public {
+	/// @notice Count NFTs tracked by this contract
+	/// @return A count of valid NFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() public view returns (uint256) {
 		require(false, stub_error);
-		sponsor;
-		dummy = 0;
+		dummy;
+		return 0;
 	}
+}
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() public {
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+contract ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() public view returns (string memory) {
 		require(false, stub_error);
-		dummy = 0;
+		dummy;
+		return "";
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) public {
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() public view returns (string memory) {
 		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
+		dummy;
+		return "";
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) public {
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) public view returns (string memory) {
 		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() public view returns (address) {
-		require(false, stub_error);
+		tokenId;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return "";
 	}
+}
 
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
-	function addCollectionAdminSubstrate(uint256 newAdmin) public {
-		require(false, stub_error);
-		newAdmin;
-		dummy = 0;
-	}
+/// @dev inlined interface
+contract ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
-	function removeCollectionAdminSubstrate(uint256 admin) public {
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+contract ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) public view returns (uint256) {
 		require(false, stub_error);
-		admin;
-		dummy = 0;
+		owner;
+		dummy;
+		return 0;
 	}
 
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) public {
+	/// @notice Find the owner of an NFT
+	/// @dev NFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	/// @param tokenId The identifier for an NFT
+	/// @return The address of the owner of the NFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) public view returns (address) {
 		require(false, stub_error);
-		newAdmin;
-		dummy = 0;
+		tokenId;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) public {
 		require(false, stub_error);
-		admin;
+		from;
+		to;
+		tokenId;
+		data;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
 		require(false, stub_error);
-		enable;
+		from;
+		to;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		public
-	{
+	/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
 		require(false, stub_error);
-		enable;
-		collections;
+		from;
+		to;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) public {
+	/// @notice Set or reaffirm the approved address for an NFT
+	/// @dev The zero address indicates there is no approved address.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param approved The new approved NFT controller
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) public {
 		require(false, stub_error);
-		mode;
+		approved;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) public {
 		require(false, stub_error);
-		user;
-		dummy = 0;
-	}
-
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) public {
-		require(false, stub_error);
-		user;
+		operator;
+		approved;
 		dummy = 0;
 	}
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) public view returns (address) {
 		require(false, stub_error);
-		mode;
-		dummy = 0;
-	}
-
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
-	function isOwnerOrAdmin(address user) public view returns (bool) {
-		require(false, stub_error);
-		user;
+		tokenId;
 		dummy;
-		return false;
+		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
-	function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator)
+		public
+		view
+		returns (address)
+	{
 		require(false, stub_error);
-		user;
+		owner;
+		operator;
 		dummy;
-		return false;
-	}
-
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
-	function uniqueCollectionType() public returns (string memory) {
-		require(false, stub_error);
-		dummy = 0;
-		return "";
-	}
-
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
-	function setOwner(address newOwner) public {
-		require(false, stub_error);
-		newOwner;
-		dummy = 0;
-	}
-
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
-	function setOwnerSubstrate(uint256 newOwner) public {
-		require(false, stub_error);
-		newOwner;
-		dummy = 0;
+		return 0x0000000000000000000000000000000000000000;
 	}
 }
 
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -53,14 +53,14 @@
 pub const ADDRESS_FOR_PARTIALLY_OWNED_TOKENS: H160 = H160::repeat_byte(0xff);
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-#[solidity_interface(name = "TokenProperties")]
+#[solidity_interface(name = TokenProperties)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
 	/// @param key Property key.
-	/// @param is_mutable Permission to mutate property.
-	/// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	/// @param token_owner Permission to mutate property by token owner if property is mutable.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
 	fn set_token_property_permission(
 		&mut self,
 		caller: caller,
@@ -197,7 +197,7 @@
 	MintingFinished {},
 }
 
-#[solidity_interface(name = "ERC721Metadata")]
+#[solidity_interface(name = ERC721Metadata)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice A descriptive name for a collection of RFTs in this contract
 	fn name(&self) -> Result<string> {
@@ -258,7 +258,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 /// @dev See https://eips.ethereum.org/EIPS/eip-721
-#[solidity_interface(name = "ERC721Enumerable")]
+#[solidity_interface(name = ERC721Enumerable)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Enumerate valid RFTs
 	/// @param index A counter less than `totalSupply()`
@@ -285,7 +285,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-#[solidity_interface(name = "ERC721", events(ERC721Events))]
+#[solidity_interface(name = ERC721, events(ERC721Events))]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Count all RFTs assigned to an owner
 	/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -322,7 +322,6 @@
 		_to: address,
 		_token_id: uint256,
 		_data: bytes,
-		_value: value,
 	) -> Result<void> {
 		// TODO: Not implemetable
 		Err("not implemented".into())
@@ -334,7 +333,6 @@
 		_from: address,
 		_to: address,
 		_token_id: uint256,
-		_value: value,
 	) -> Result<void> {
 		// TODO: Not implemetable
 		Err("not implemented".into())
@@ -350,7 +348,6 @@
 	/// @param from The current owner of the NFT
 	/// @param to The new owner
 	/// @param tokenId The NFT to transfer
-	/// @param _value Not used for an NFT
 	#[weight(<SelfWeightOf<T>>::transfer_from_creating_removing())]
 	fn transfer_from(
 		&mut self,
@@ -358,7 +355,6 @@
 		from: address,
 		to: address,
 		token_id: uint256,
-		_value: value,
 	) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let from = T::CrossAccountId::from_eth(from);
@@ -378,13 +374,7 @@
 	}
 
 	/// @dev Not implemented
-	fn approve(
-		&mut self,
-		_caller: caller,
-		_approved: address,
-		_token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn approve(&mut self, _caller: caller, _approved: address, _token_id: uint256) -> Result<void> {
 		Err("not implemented".into())
 	}
 
@@ -438,7 +428,7 @@
 }
 
 /// @title ERC721 Token that can be irreversibly burned (destroyed).
-#[solidity_interface(name = "ERC721Burnable")]
+#[solidity_interface(name = ERC721Burnable)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Burns a specific ERC721 token.
 	/// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
@@ -458,7 +448,7 @@
 }
 
 /// @title ERC721 minting logic.
-#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
+#[solidity_interface(name = ERC721Mintable, events(ERC721MintableEvents))]
 impl<T: Config> RefungibleHandle<T> {
 	fn minting_finished(&self) -> Result<bool> {
 		Ok(false)
@@ -616,7 +606,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-#[solidity_interface(name = "ERC721UniqueExtensions")]
+#[solidity_interface(name = ERC721UniqueExtensions)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Transfer ownership of an RFT
 	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
@@ -624,15 +614,8 @@
 	///  Throws if RFT pieces have multiple owners.
 	/// @param to The new owner
 	/// @param tokenId The RFT to transfer
-	/// @param _value Not used for an RFT
 	#[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
-	fn transfer(
-		&mut self,
-		caller: caller,
-		to: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let to = T::CrossAccountId::from_eth(to);
 		let token = token_id.try_into()?;
@@ -655,15 +638,8 @@
 	///  Throws if RFT pieces have multiple owners.
 	/// @param from The current owner of the RFT
 	/// @param tokenId The RFT to transfer
-	/// @param _value Not used for an RFT
 	#[weight(<SelfWeightOf<T>>::burn_from())]
-	fn burn_from(
-		&mut self,
-		caller: caller,
-		from: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
+	fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let from = T::CrossAccountId::from_eth(from);
 		let token = token_id.try_into()?;
@@ -801,7 +777,7 @@
 }
 
 #[solidity_interface(
-	name = "UniqueRefungible",
+	name = UniqueRefungible,
 	is(
 		ERC721,
 		ERC721Metadata,
@@ -809,7 +785,7 @@
 		ERC721UniqueExtensions,
 		ERC721Mintable,
 		ERC721Burnable,
-		via("CollectionHandle<T>", common_mut, Collection),
+		Collection(common_mut, CollectionHandle<T>),
 		TokenProperties,
 	)
 )]
modifiedpallets/refungible/src/erc_token.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -49,7 +49,7 @@
 
 pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);
 
-#[solidity_interface(name = "ERC1633")]
+#[solidity_interface(name = ERC1633)]
 impl<T: Config> RefungibleTokenHandle<T> {
 	fn parent_token(&self) -> Result<address> {
 		self.consume_store_reads(2)?;
@@ -87,7 +87,7 @@
 	}
 }
 
-#[solidity_interface(name = "ERC1633UniqueExtensions")]
+#[solidity_interface(name = ERC1633UniqueExtensions)]
 impl<T: Config> RefungibleTokenHandle<T> {
 	#[solidity(rename_selector = "setParentNFT")]
 	#[weight(<CommonWeights<T>>::token_owner() + <SelfWeightOf<T>>::set_parent_nft_unchecked())]
@@ -137,7 +137,7 @@
 ///
 /// @dev Implementation of the basic standard token.
 /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
-#[solidity_interface(name = "ERC20", events(ERC20Events))]
+#[solidity_interface(name = ERC20, events(ERC20Events))]
 impl<T: Config> RefungibleTokenHandle<T> {
 	/// @return the name of the token.
 	fn name(&self) -> Result<string> {
@@ -246,7 +246,7 @@
 	}
 }
 
-#[solidity_interface(name = "ERC20UniqueExtensions")]
+#[solidity_interface(name = ERC20UniqueExtensions)]
 impl<T: Config> RefungibleTokenHandle<T> {
 	/// @dev Function that burns an amount of the token of a given account,
 	/// deducting from the sender's allowance for said account.
@@ -306,7 +306,7 @@
 }
 
 #[solidity_interface(
-	name = "UniqueRefungibleToken",
+	name = UniqueRefungibleToken,
 	is(ERC20, ERC20UniqueExtensions, ERC1633, ERC1633UniqueExtensions)
 )]
 impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	uint256 field_0;
-	string field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -27,40 +21,17 @@
 	}
 }
 
-// Inline
-contract ERC721Events {
-	event Transfer(
-		address indexed from,
-		address indexed to,
-		uint256 indexed tokenId
-	);
-	event Approval(
-		address indexed owner,
-		address indexed approved,
-		uint256 indexed tokenId
-	);
-	event ApprovalForAll(
-		address indexed owner,
-		address indexed operator,
-		bool approved
-	);
-}
-
-// Inline
-contract ERC721MintableEvents {
-	event MintingFinished();
-}
-
-// Selector: 41369377
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0x41369377
 contract TokenProperties is Dummy, ERC165 {
-	// @notice Set permissions for token property.
-	// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// @param key Property key.
-	// @param is_mutable Permission to mutate property.
-	// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	// @param token_owner Permission to mutate property by token owner if property is mutable.
-	//
-	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param key Property key.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	/// @dev EVM selector for this function is: 0x222d97fa,
+	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
 	function setTokenPropertyPermission(
 		string memory key,
 		bool isMutable,
@@ -75,13 +46,13 @@
 		dummy = 0;
 	}
 
-	// @notice Set token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @param value Property value.
-	//
-	// Selector: setProperty(uint256,string,bytes) 1752d67b
+	/// @notice Set token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @param value Property value.
+	/// @dev EVM selector for this function is: 0x1752d67b,
+	///  or in textual repr: setProperty(uint256,string,bytes)
 	function setProperty(
 		uint256 tokenId,
 		string memory key,
@@ -94,12 +65,12 @@
 		dummy = 0;
 	}
 
-	// @notice Delete token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	//
-	// Selector: deleteProperty(uint256,string) 066111d1
+	/// @notice Delete token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x066111d1,
+	///  or in textual repr: deleteProperty(uint256,string)
 	function deleteProperty(uint256 tokenId, string memory key) public {
 		require(false, stub_error);
 		tokenId;
@@ -107,13 +78,13 @@
 		dummy = 0;
 	}
 
-	// @notice Get token property value.
-	// @dev Throws error if key not found
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @return Property value bytes
-	//
-	// Selector: property(uint256,string) 7228c327
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
 	function property(uint256 tokenId, string memory key)
 		public
 		view
@@ -127,211 +98,334 @@
 	}
 }
 
-// Selector: 42966c68
-contract ERC721Burnable is Dummy, ERC165 {
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
-	//  operator of the current owner.
-	// @param tokenId The RFT to approve
-	//
-	// Selector: burn(uint256) 42966c68
-	function burn(uint256 tokenId) public {
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
+contract Collection is Dummy, ERC165 {
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
+	function setCollectionProperty(string memory key, bytes memory value)
+		public
+	{
 		require(false, stub_error);
-		tokenId;
+		key;
+		value;
 		dummy = 0;
 	}
-}
 
-// Selector: 58800161
-contract ERC721 is Dummy, ERC165, ERC721Events {
-	// @notice Count all RFTs assigned to an owner
-	// @dev RFTs assigned to the zero address are considered invalid, and this
-	//  function throws for queries about the zero address.
-	// @param owner An address for whom to query the balance
-	// @return The number of RFTs owned by `owner`, possibly zero
-	//
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
+	function deleteCollectionProperty(string memory key) public {
 		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
+		key;
+		dummy = 0;
 	}
 
-	// @notice Find the owner of an RFT
-	// @dev RFTs assigned to zero address are considered invalid, and queries
-	//  about them do throw.
-	//  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
-	//  the tokens that are partially owned.
-	// @param tokenId The identifier for an RFT
-	// @return The address of the owner of the RFT
-	//
-	// Selector: ownerOf(uint256) 6352211e
-	function ownerOf(uint256 tokenId) public view returns (address) {
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key)
+		public
+		view
+		returns (bytes memory)
+	{
 		require(false, stub_error);
-		tokenId;
+		key;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return hex"";
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
-	function safeTransferFromWithData(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) public {
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
+	function setCollectionSponsor(address sponsor) public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
-		data;
+		sponsor;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
 		dummy = 0;
 	}
 
-	// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
-	//  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	//  THEY MAY BE PERMANENTLY LOST
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the NFT
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
+	function setCollectionLimit(string memory limit, uint32 value) public {
 		require(false, stub_error);
-		from;
-		to;
-		tokenId;
+		limit;
+		value;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address approved, uint256 tokenId) public {
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
+	function setCollectionLimit(string memory limit, bool value) public {
 		require(false, stub_error);
-		approved;
-		tokenId;
+		limit;
+		value;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: setApprovalForAll(address,bool) a22cb465
-	function setApprovalForAll(address operator, bool approved) public {
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() public view returns (address) {
 		require(false, stub_error);
-		operator;
-		approved;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
+	function addCollectionAdminSubstrate(uint256 newAdmin) public {
+		require(false, stub_error);
+		newAdmin;
 		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: getApproved(uint256) 081812fc
-	function getApproved(uint256 tokenId) public view returns (address) {
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
+	function removeCollectionAdminSubstrate(uint256 admin) public {
 		require(false, stub_error);
-		tokenId;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		admin;
+		dummy = 0;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: isApprovedForAll(address,address) e985e9c5
-	function isApprovedForAll(address owner, address operator)
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
+	function addCollectionAdmin(address newAdmin) public {
+		require(false, stub_error);
+		newAdmin;
+		dummy = 0;
+	}
+
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
+	function removeCollectionAdmin(address admin) public {
+		require(false, stub_error);
+		admin;
+		dummy = 0;
+	}
+
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
+	function setCollectionNesting(bool enable) public {
+		require(false, stub_error);
+		enable;
+		dummy = 0;
+	}
+
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
+	function setCollectionNesting(bool enable, address[] memory collections)
 		public
-		view
-		returns (address)
 	{
 		require(false, stub_error);
-		owner;
-		operator;
+		enable;
+		collections;
+		dummy = 0;
+	}
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(uint8 mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
+	function addToCollectionAllowList(address user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
+	function removeFromCollectionAllowList(address user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
+	function isOwnerOrAdmin(address user) public view returns (bool) {
+		require(false, stub_error);
+		user;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return false;
 	}
-}
 
-// Selector: 5b5e139f
-contract ERC721Metadata is Dummy, ERC165 {
-	// @notice A descriptive name for a collection of RFTs in this contract
-	//
-	// Selector: name() 06fdde03
-	function name() public view returns (string memory) {
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
+	function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
 		require(false, stub_error);
+		user;
 		dummy;
-		return "";
+		return false;
 	}
 
-	// @notice An abbreviated name for RFTs in this contract
-	//
-	// Selector: symbol() 95d89b41
-	function symbol() public view returns (string memory) {
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() public returns (string memory) {
 		require(false, stub_error);
-		dummy;
+		dummy = 0;
 		return "";
 	}
 
-	// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	//
-	// @dev If the token has a `url` property and it is not empty, it is returned.
-	//  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	//  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	//  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	//  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	//
-	// @return token's const_metadata
-	//
-	// Selector: tokenURI(uint256) c87b56dd
-	function tokenURI(uint256 tokenId) public view returns (string memory) {
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
+	function setOwner(address newOwner) public {
 		require(false, stub_error);
+		newOwner;
+		dummy = 0;
+	}
+
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
+	function setOwnerSubstrate(uint256 newOwner) public {
+		require(false, stub_error);
+		newOwner;
+		dummy = 0;
+	}
+}
+
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+contract ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The RFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) public {
+		require(false, stub_error);
 		tokenId;
-		dummy;
-		return "";
+		dummy = 0;
 	}
 }
 
-// Selector: 68ccfe89
+/// @dev inlined interface
+contract ERC721MintableEvents {
+	event MintingFinished();
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x68ccfe89
 contract ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
-	// Selector: mintingFinished() 05d2035b
+	/// @dev EVM selector for this function is: 0x05d2035b,
+	///  or in textual repr: mintingFinished()
 	function mintingFinished() public view returns (bool) {
 		require(false, stub_error);
 		dummy;
 		return false;
 	}
 
-	// @notice Function to mint token.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted RFT
-	//
-	// Selector: mint(address,uint256) 40c10f19
+	/// @notice Function to mint token.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted RFT
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
 	function mint(address to, uint256 tokenId) public returns (bool) {
 		require(false, stub_error);
 		to;
@@ -340,14 +434,14 @@
 		return false;
 	}
 
-	// @notice Function to mint token with the given tokenUri.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted RFT
-	// @param tokenUri Token URI that would be stored in the RFT properties
-	//
-	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	/// @notice Function to mint token with the given tokenUri.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted RFT
+	/// @param tokenUri Token URI that would be stored in the RFT properties
+	/// @dev EVM selector for this function is: 0x50bb4e7f,
+	///  or in textual repr: mintWithTokenURI(address,uint256,string)
 	function mintWithTokenURI(
 		address to,
 		uint256 tokenId,
@@ -361,9 +455,9 @@
 		return false;
 	}
 
-	// @dev Not implemented
-	//
-	// Selector: finishMinting() 7d64bcb4
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x7d64bcb4,
+	///  or in textual repr: finishMinting()
 	function finishMinting() public returns (bool) {
 		require(false, stub_error);
 		dummy = 0;
@@ -371,59 +465,17 @@
 	}
 }
 
-// Selector: 780e9d63
-contract ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid RFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
-	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @notice Count RFTs tracked by this contract
-	// @return A count of valid RFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-}
-
-// Selector: 7c3bef89
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0x7c3bef89
 contract ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an RFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param to The new owner
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @notice Transfer ownership of an RFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param to The new owner
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 tokenId) public {
 		require(false, stub_error);
 		to;
@@ -431,16 +483,15 @@
 		dummy = 0;
 	}
 
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the RFT
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the RFT
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
 	function burnFrom(address from, uint256 tokenId) public {
 		require(false, stub_error);
 		from;
@@ -448,22 +499,22 @@
 		dummy = 0;
 	}
 
-	// @notice Returns next free RFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
+	/// @notice Returns next free RFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
 	function nextTokenId() public view returns (uint256) {
 		require(false, stub_error);
 		dummy;
 		return 0;
 	}
 
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted RFTs
-	//
-	// Selector: mintBulk(address,uint256[]) 44a9945e
+	/// @notice Function to mint multiple tokens.
+	/// @dev `tokenIds` should be an array of consecutive numbers and first number
+	///  should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokenIds IDs of the minted RFTs
+	/// @dev EVM selector for this function is: 0x44a9945e,
+	///  or in textual repr: mintBulk(address,uint256[])
 	function mintBulk(address to, uint256[] memory tokenIds)
 		public
 		returns (bool)
@@ -475,14 +526,14 @@
 		return false;
 	}
 
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
-	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+	/// @notice Function to mint multiple tokens with the given tokenUris.
+	/// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	///  numbers and first number should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokens array of pairs of token ID and token URI for minted tokens
+	/// @dev EVM selector for this function is: 0x36543006,
+	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens)
 		public
 		returns (bool)
 	{
@@ -493,11 +544,11 @@
 		return false;
 	}
 
-	// Returns EVM address for refungible token
-	//
-	// @param token ID of the token
-	//
-	// Selector: tokenContractAddress(uint256) ab76fac6
+	/// Returns EVM address for refungible token
+	///
+	/// @param token ID of the token
+	/// @dev EVM selector for this function is: 0xab76fac6,
+	///  or in textual repr: tokenContractAddress(uint256)
 	function tokenContractAddress(uint256 token) public view returns (address) {
 		require(false, stub_error);
 		token;
@@ -506,291 +557,247 @@
 	}
 }
 
-// Selector: ffe4da23
-contract Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		public
-	{
-		require(false, stub_error);
-		key;
-		value;
-		dummy = 0;
-	}
+/// @dev anonymous struct
+struct Tuple8 {
+	uint256 field_0;
+	string field_1;
+}
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) public {
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid RFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) public view returns (uint256) {
 		require(false, stub_error);
-		key;
-		dummy = 0;
+		index;
+		dummy;
+		return 0;
 	}
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
+	/// Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index)
 		public
 		view
-		returns (bytes memory)
+		returns (uint256)
 	{
 		require(false, stub_error);
-		key;
+		owner;
+		index;
 		dummy;
-		return hex"";
+		return 0;
 	}
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) public {
+	/// @notice Count RFTs tracked by this contract
+	/// @return A count of valid RFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() public view returns (uint256) {
 		require(false, stub_error);
-		sponsor;
-		dummy = 0;
+		dummy;
+		return 0;
 	}
+}
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() public {
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+contract ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of RFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() public view returns (string memory) {
 		require(false, stub_error);
-		dummy = 0;
+		dummy;
+		return "";
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) public {
+	/// @notice An abbreviated name for RFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() public view returns (string memory) {
 		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
+		dummy;
+		return "";
 	}
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) public {
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) public view returns (string memory) {
 		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() public view returns (address) {
-		require(false, stub_error);
+		tokenId;
 		dummy;
-		return 0x0000000000000000000000000000000000000000;
+		return "";
 	}
+}
 
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
-	function addCollectionAdminSubstrate(uint256 newAdmin) public {
-		require(false, stub_error);
-		newAdmin;
-		dummy = 0;
-	}
+/// @dev inlined interface
+contract ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
-	function removeCollectionAdminSubstrate(uint256 admin) public {
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x58800161
+contract ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) public view returns (uint256) {
 		require(false, stub_error);
-		admin;
-		dummy = 0;
+		owner;
+		dummy;
+		return 0;
 	}
 
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) public {
+	/// @notice Find the owner of an RFT
+	/// @dev RFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	///  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
+	///  the tokens that are partially owned.
+	/// @param tokenId The identifier for an RFT
+	/// @return The address of the owner of the RFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) public view returns (address) {
 		require(false, stub_error);
-		newAdmin;
-		dummy = 0;
+		tokenId;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x60a11672,
+	///  or in textual repr: safeTransferFromWithData(address,address,uint256,bytes)
+	function safeTransferFromWithData(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) public {
 		require(false, stub_error);
-		admin;
+		from;
+		to;
+		tokenId;
+		data;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
 		require(false, stub_error);
-		enable;
+		from;
+		to;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		public
-	{
-		require(false, stub_error);
-		enable;
-		collections;
-		dummy = 0;
-	}
-
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) public {
+	/// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) public {
 		require(false, stub_error);
-		mode;
-		dummy = 0;
-	}
-
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) public {
-		require(false, stub_error);
-		user;
+		from;
+		to;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) public {
 		require(false, stub_error);
-		user;
+		approved;
+		tokenId;
 		dummy = 0;
 	}
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) public {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) public {
 		require(false, stub_error);
-		mode;
+		operator;
+		approved;
 		dummy = 0;
 	}
 
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
-	function isOwnerOrAdmin(address user) public view returns (bool) {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) public view returns (address) {
 		require(false, stub_error);
-		user;
+		tokenId;
 		dummy;
-		return false;
+		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
-	function isOwnerOrAdminSubstrate(uint256 user) public view returns (bool) {
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator)
+		public
+		view
+		returns (address)
+	{
 		require(false, stub_error);
-		user;
+		owner;
+		operator;
 		dummy;
-		return false;
-	}
-
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
-	function uniqueCollectionType() public returns (string memory) {
-		require(false, stub_error);
-		dummy = 0;
-		return "";
-	}
-
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
-	function setOwner(address newOwner) public {
-		require(false, stub_error);
-		newOwner;
-		dummy = 0;
-	}
-
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
-	function setOwnerSubstrate(uint256 newOwner) public {
-		require(false, stub_error);
-		newOwner;
-		dummy = 0;
+		return 0x0000000000000000000000000000000000000000;
 	}
 }
 
modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -21,19 +21,10 @@
 	}
 }
 
-// Inline
-contract ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Selector: 042f1106
+/// @dev the ERC-165 identifier for this interface is 0x042f1106
 contract ERC1633UniqueExtensions is Dummy, ERC165 {
-	// Selector: setParentNFT(address,uint256) 042f1106
+	/// @dev EVM selector for this function is: 0x042f1106,
+	///  or in textual repr: setParentNFT(address,uint256)
 	function setParentNFT(address collection, uint256 nftId)
 		public
 		returns (bool)
@@ -46,16 +37,18 @@
 	}
 }
 
-// Selector: 5755c3f2
+/// @dev the ERC-165 identifier for this interface is 0x5755c3f2
 contract ERC1633 is Dummy, ERC165 {
-	// Selector: parentToken() 80a54001
+	/// @dev EVM selector for this function is: 0x80a54001,
+	///  or in textual repr: parentToken()
 	function parentToken() public view returns (address) {
 		require(false, stub_error);
 		dummy;
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Selector: parentTokenId() d7f083f3
+	/// @dev EVM selector for this function is: 0xd7f083f3,
+	///  or in textual repr: parentTokenId()
 	function parentTokenId() public view returns (uint256) {
 		require(false, stub_error);
 		dummy;
@@ -63,49 +56,92 @@
 	}
 }
 
-// Selector: 942e8b22
+/// @dev the ERC-165 identifier for this interface is 0xab8deb37
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev Function that burns an amount of the token of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
+	function burnFrom(address from, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		from;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	/// @dev Function that changes total amount of the tokens.
+	///  Throws if `msg.sender` doesn't owns all of the tokens.
+	/// @param amount New total amount of the tokens.
+	/// @dev EVM selector for this function is: 0xd2418ca7,
+	///  or in textual repr: repartition(uint256)
+	function repartition(uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		amount;
+		dummy = 0;
+		return false;
+	}
+}
+
+/// @dev inlined interface
+contract ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+/// @title Standard ERC20 token
+///
+/// @dev Implementation of the basic standard token.
+/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
 contract ERC20 is Dummy, ERC165, ERC20Events {
-	// @return the name of the token.
-	//
-	// Selector: name() 06fdde03
+	/// @return the name of the token.
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
 	function name() public view returns (string memory) {
 		require(false, stub_error);
 		dummy;
 		return "";
 	}
 
-	// @return the symbol of the token.
-	//
-	// Selector: symbol() 95d89b41
+	/// @return the symbol of the token.
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
 	function symbol() public view returns (string memory) {
 		require(false, stub_error);
 		dummy;
 		return "";
 	}
 
-	// @dev Total number of tokens in existence
-	//
-	// Selector: totalSupply() 18160ddd
+	/// @dev Total number of tokens in existence
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
 	function totalSupply() public view returns (uint256) {
 		require(false, stub_error);
 		dummy;
 		return 0;
 	}
 
-	// @dev Not supported
-	//
-	// Selector: decimals() 313ce567
+	/// @dev Not supported
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
 	function decimals() public view returns (uint8) {
 		require(false, stub_error);
 		dummy;
 		return 0;
 	}
 
-	// @dev Gets the balance of the specified address.
-	// @param owner The address to query the balance of.
-	// @return An uint256 representing the amount owned by the passed address.
-	//
-	// Selector: balanceOf(address) 70a08231
+	/// @dev Gets the balance of the specified address.
+	/// @param owner The address to query the balance of.
+	/// @return An uint256 representing the amount owned by the passed address.
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
 	function balanceOf(address owner) public view returns (uint256) {
 		require(false, stub_error);
 		owner;
@@ -113,11 +149,11 @@
 		return 0;
 	}
 
-	// @dev Transfer token for a specified address
-	// @param to The address to transfer to.
-	// @param amount The amount to be transferred.
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @dev Transfer token for a specified address
+	/// @param to The address to transfer to.
+	/// @param amount The amount to be transferred.
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 amount) public returns (bool) {
 		require(false, stub_error);
 		to;
@@ -126,12 +162,12 @@
 		return false;
 	}
 
-	// @dev Transfer tokens from one address to another
-	// @param from address The address which you want to send tokens from
-	// @param to address The address which you want to transfer to
-	// @param amount uint256 the amount of tokens to be transferred
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
+	/// @dev Transfer tokens from one address to another
+	/// @param from address The address which you want to send tokens from
+	/// @param to address The address which you want to transfer to
+	/// @param amount uint256 the amount of tokens to be transferred
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
 	function transferFrom(
 		address from,
 		address to,
@@ -145,15 +181,15 @@
 		return false;
 	}
 
-	// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
-	// Beware that changing an allowance with this method brings the risk that someone may use both the old
-	// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
-	// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
-	// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
-	// @param spender The address which will spend the funds.
-	// @param amount The amount of tokens to be spent.
-	//
-	// Selector: approve(address,uint256) 095ea7b3
+	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
+	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+	/// @param spender The address which will spend the funds.
+	/// @param amount The amount of tokens to be spent.
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
 	function approve(address spender, uint256 amount) public returns (bool) {
 		require(false, stub_error);
 		spender;
@@ -162,12 +198,12 @@
 		return false;
 	}
 
-	// @dev Function to check the amount of tokens that an owner allowed to a spender.
-	// @param owner address The address which owns the funds.
-	// @param spender address The address which will spend the funds.
-	// @return A uint256 specifying the amount of tokens still available for the spender.
-	//
-	// Selector: allowance(address,address) dd62ed3e
+	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
+	/// @param owner address The address which owns the funds.
+	/// @param spender address The address which will spend the funds.
+	/// @return A uint256 specifying the amount of tokens still available for the spender.
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
 	function allowance(address owner, address spender)
 		public
 		view
@@ -178,35 +214,6 @@
 		spender;
 		dummy;
 		return 0;
-	}
-}
-
-// Selector: ab8deb37
-contract ERC20UniqueExtensions is Dummy, ERC165 {
-	// @dev Function that burns an amount of the token of a given account,
-	// deducting from the sender's allowance for said account.
-	// @param from The account whose tokens will be burnt.
-	// @param amount The amount that will be burnt.
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		from;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// @dev Function that changes total amount of the tokens.
-	//  Throws if `msg.sender` doesn't owns all of the tokens.
-	// @param amount New total amount of the tokens.
-	//
-	// Selector: repartition(uint256) d2418ca7
-	function repartition(uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		amount;
-		dummy = 0;
-		return false;
 	}
 }
 
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -203,7 +203,7 @@
 }
 
 /// @title Contract, which allows users to operate with collections
-#[solidity_interface(name = "CollectionHelpers", events(CollectionHelpersEvents))]
+#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]
 impl<T> EvmCollectionHelpers<T>
 where
 	T: Config + pallet_nonfungible::Config + pallet_refungible::Config,
@@ -211,7 +211,7 @@
 	/// Create an NFT collection
 	/// @param name Name of the collection
 	/// @param description Informative description of the collection
-	/// @param token_prefix Token prefix to represent the collection tokens in UI and user applications
+	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
 	/// @return address Address of the newly created collection
 	#[weight(<SelfWeightOf<T>>::create_collection())]
 	fn create_nonfungible_collection(
@@ -304,7 +304,7 @@
 	}
 
 	/// Check if a collection exists
-	/// @param collection_address Address of the collection in question
+	/// @param collectionAddress Address of the collection in question
 	/// @return bool Does the collection exist?
 	fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {
 		if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -21,7 +21,7 @@
 	}
 }
 
-// Inline
+/// @dev inlined interface
 contract CollectionHelpersEvents {
 	event CollectionCreated(
 		address indexed owner,
@@ -29,15 +29,16 @@
 	);
 }
 
-// Selector: 675f3074
+/// @title Contract, which allows users to operate with collections
+/// @dev the ERC-165 identifier for this interface is 0x675f3074
 contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
-	// Create an NFT collection
-	// @param name Name of the collection
-	// @param description Informative description of the collection
-	// @param token_prefix Token prefix to represent the collection tokens in UI and user applications
-	// @return address Address of the newly created collection
-	//
-	// Selector: createNonfungibleCollection(string,string,string) e34a6844
+	/// Create an NFT collection
+	/// @param name Name of the collection
+	/// @param description Informative description of the collection
+	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+	/// @return address Address of the newly created collection
+	/// @dev EVM selector for this function is: 0xe34a6844,
+	///  or in textual repr: createNonfungibleCollection(string,string,string)
 	function createNonfungibleCollection(
 		string memory name,
 		string memory description,
@@ -51,7 +52,8 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Selector: createERC721MetadataCompatibleCollection(string,string,string,string) a634a5f9
+	/// @dev EVM selector for this function is: 0xa634a5f9,
+	///  or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)
 	function createERC721MetadataCompatibleCollection(
 		string memory name,
 		string memory description,
@@ -67,7 +69,8 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Selector: createRefungibleCollection(string,string,string) 44a68ad5
+	/// @dev EVM selector for this function is: 0x44a68ad5,
+	///  or in textual repr: createRefungibleCollection(string,string,string)
 	function createRefungibleCollection(
 		string memory name,
 		string memory description,
@@ -81,7 +84,8 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Selector: createERC721MetadataCompatibleRFTCollection(string,string,string,string) a5596388
+	/// @dev EVM selector for this function is: 0xa5596388,
+	///  or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)
 	function createERC721MetadataCompatibleRFTCollection(
 		string memory name,
 		string memory description,
@@ -97,11 +101,11 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
-	// Check if a collection exists
-	// @param collection_address Address of the collection in question
-	// @return bool Does the collection exist?
-	//
-	// Selector: isCollectionExist(address) c3de1494
+	/// Check if a collection exists
+	/// @param collectionAddress Address of the collection in question
+	/// @return bool Does the collection exist?
+	/// @dev EVM selector for this function is: 0xc3de1494,
+	///  or in textual repr: isCollectionExist(address)
 	function isCollectionExist(address collectionAddress)
 		public
 		view
modifiedruntime/common/config/ethereum.rsdiffbeforeafterboth
--- a/runtime/common/config/ethereum.rs
+++ b/runtime/common/config/ethereum.rs
@@ -120,5 +120,4 @@
 
 impl pallet_evm_transaction_payment::Config for Runtime {
 	type EvmSponsorshipHandler = EvmSponsorshipHandler;
-	type Currency = Balances;
 }
modifiedtests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
33
4pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;
55
6// Common stubs holder6/// @dev common stubs holder
7interface Dummy {7interface Dummy {
88
9}9}
12 function supportsInterface(bytes4 interfaceID) external view returns (bool);12 function supportsInterface(bytes4 interfaceID) external view returns (bool);
13}13}
1414
15// Inline15/// @dev inlined interface
16interface CollectionHelpersEvents {16interface CollectionHelpersEvents {
17 event CollectionCreated(17 event CollectionCreated(
18 address indexed owner,18 address indexed owner,
19 address indexed collectionId19 address indexed collectionId
20 );20 );
21}21}
2222
23// Selector: 675f307423/// @title Contract, which allows users to operate with collections
24/// @dev the ERC-165 identifier for this interface is 0x675f3074
24interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {25interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
25 // Create an NFT collection26 /// Create an NFT collection
26 // @param name Name of the collection27 /// @param name Name of the collection
27 // @param description Informative description of the collection28 /// @param description Informative description of the collection
28 // @param token_prefix Token prefix to represent the collection tokens in UI and user applications29 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
29 // @return address Address of the newly created collection30 /// @return address Address of the newly created collection
30 //31 /// @dev EVM selector for this function is: 0xe34a6844,
31 // Selector: createNonfungibleCollection(string,string,string) e34a684432 /// or in textual repr: createNonfungibleCollection(string,string,string)
32 function createNonfungibleCollection(33 function createNonfungibleCollection(
33 string memory name,34 string memory name,
34 string memory description,35 string memory description,
35 string memory tokenPrefix36 string memory tokenPrefix
36 ) external returns (address);37 ) external returns (address);
3738
39 /// @dev EVM selector for this function is: 0xa634a5f9,
38 // Selector: createERC721MetadataCompatibleCollection(string,string,string,string) a634a5f940 /// or in textual repr: createERC721MetadataCompatibleCollection(string,string,string,string)
39 function createERC721MetadataCompatibleCollection(41 function createERC721MetadataCompatibleCollection(
40 string memory name,42 string memory name,
41 string memory description,43 string memory description,
42 string memory tokenPrefix,44 string memory tokenPrefix,
43 string memory baseUri45 string memory baseUri
44 ) external returns (address);46 ) external returns (address);
4547
48 /// @dev EVM selector for this function is: 0x44a68ad5,
46 // Selector: createRefungibleCollection(string,string,string) 44a68ad549 /// or in textual repr: createRefungibleCollection(string,string,string)
47 function createRefungibleCollection(50 function createRefungibleCollection(
48 string memory name,51 string memory name,
49 string memory description,52 string memory description,
50 string memory tokenPrefix53 string memory tokenPrefix
51 ) external returns (address);54 ) external returns (address);
5255
56 /// @dev EVM selector for this function is: 0xa5596388,
53 // Selector: createERC721MetadataCompatibleRFTCollection(string,string,string,string) a559638857 /// or in textual repr: createERC721MetadataCompatibleRFTCollection(string,string,string,string)
54 function createERC721MetadataCompatibleRFTCollection(58 function createERC721MetadataCompatibleRFTCollection(
55 string memory name,59 string memory name,
56 string memory description,60 string memory description,
57 string memory tokenPrefix,61 string memory tokenPrefix,
58 string memory baseUri62 string memory baseUri
59 ) external returns (address);63 ) external returns (address);
6064
61 // Check if a collection exists65 /// Check if a collection exists
62 // @param collection_address Address of the collection in question66 /// @param collectionAddress Address of the collection in question
63 // @return bool Does the collection exist?67 /// @return bool Does the collection exist?
64 //68 /// @dev EVM selector for this function is: 0xc3de1494,
65 // Selector: isCollectionExist(address) c3de149469 /// or in textual repr: isCollectionExist(address)
66 function isCollectionExist(address collectionAddress)70 function isCollectionExist(address collectionAddress)
67 external71 external
68 view72 view
modifiedtests/src/eth/api/ContractHelpers.soldiffbeforeafterboth
--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	address field_0;
-	uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -18,124 +12,164 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Selector: 6073d917
+/// @title Magic contract, which allows users to reconfigure other contracts
+/// @dev the ERC-165 identifier for this interface is 0xd77fab70
 interface ContractHelpers is Dummy, ERC165 {
-	// Get contract ovner
-	//
-	// @param Contract_address contract for which the owner is being determined.
-	// @return Contract owner.
-	//
-	// Selector: contractOwner(address) 5152b14c
+	/// Get user, which deployed specified contract
+	/// @dev May return zero address in case if contract is deployed
+	///  using uniquenetwork evm-migration pallet, or using other terms not
+	///  intended by pallet-evm
+	/// @dev Returns zero address if contract does not exists
+	/// @param contractAddress Contract to get owner of
+	/// @return address Owner of contract
+	/// @dev EVM selector for this function is: 0x5152b14c,
+	///  or in textual repr: contractOwner(address)
 	function contractOwner(address contractAddress)
 		external
 		view
 		returns (address);
 
-	// Set sponsor.
-	//
-	// @param contract_address Contract for which a sponsor is being established.
-	// @param sponsor User address who set as pending sponsor.
-	//
-	// Selector: setSponsor(address,address) f01fba93
+	/// Set sponsor.
+	/// @param contractAddress Contract for which a sponsor is being established.
+	/// @param sponsor User address who set as pending sponsor.
+	/// @dev EVM selector for this function is: 0xf01fba93,
+	///  or in textual repr: setSponsor(address,address)
 	function setSponsor(address contractAddress, address sponsor) external;
 
-	// Set contract as self sponsored.
-	//
-	// @param contract_address Contract for which a self sponsoring is being enabled.
-	//
-	// Selector: selfSponsoredEnable(address) 89f7d9ae
+	/// Set contract as self sponsored.
+	///
+	/// @param contractAddress Contract for which a self sponsoring is being enabled.
+	/// @dev EVM selector for this function is: 0x89f7d9ae,
+	///  or in textual repr: selfSponsoredEnable(address)
 	function selfSponsoredEnable(address contractAddress) external;
 
-	// Remove sponsor.
-	//
-	// @param contract_address Contract for which a sponsorship is being removed.
-	//
-	// Selector: removeSponsor(address) ef784250
+	/// Remove sponsor.
+	///
+	/// @param contractAddress Contract for which a sponsorship is being removed.
+	/// @dev EVM selector for this function is: 0xef784250,
+	///  or in textual repr: removeSponsor(address)
 	function removeSponsor(address contractAddress) external;
 
-	// Confirm sponsorship.
-	//
-	// @dev Caller must be same that set via [`set_sponsor`].
-	//
-	// @param contract_address Сontract for which need to confirm sponsorship.
-	//
-	// Selector: confirmSponsorship(address) abc00001
+	/// Confirm sponsorship.
+	///
+	/// @dev Caller must be same that set via [`setSponsor`].
+	///
+	/// @param contractAddress Сontract for which need to confirm sponsorship.
+	/// @dev EVM selector for this function is: 0xabc00001,
+	///  or in textual repr: confirmSponsorship(address)
 	function confirmSponsorship(address contractAddress) external;
 
-	// Get current sponsor.
-	//
-	// @param contract_address The contract for which a sponsor is requested.
-	// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	//
-	// Selector: getSponsor(address) 743fc745
+	/// Get current sponsor.
+	///
+	/// @param contractAddress The contract for which a sponsor is requested.
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x743fc745,
+	///  or in textual repr: getSponsor(address)
 	function getSponsor(address contractAddress)
 		external
 		view
 		returns (Tuple0 memory);
 
-	// Check tat contract has confirmed sponsor.
-	//
-	// @param contract_address The contract for which the presence of a confirmed sponsor is checked.
-	// @return **true** if contract has confirmed sponsor.
-	//
-	// Selector: hasSponsor(address) 97418603
+	/// Check tat contract has confirmed sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
+	/// @return **true** if contract has confirmed sponsor.
+	/// @dev EVM selector for this function is: 0x97418603,
+	///  or in textual repr: hasSponsor(address)
 	function hasSponsor(address contractAddress) external view returns (bool);
 
-	// Check tat contract has pending sponsor.
-	//
-	// @param contract_address The contract for which the presence of a pending sponsor is checked.
-	// @return **true** if contract has pending sponsor.
-	//
-	// Selector: hasPendingSponsor(address) 39b9b242
+	/// Check tat contract has pending sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
+	/// @return **true** if contract has pending sponsor.
+	/// @dev EVM selector for this function is: 0x39b9b242,
+	///  or in textual repr: hasPendingSponsor(address)
 	function hasPendingSponsor(address contractAddress)
 		external
 		view
 		returns (bool);
 
-	// Selector: sponsoringEnabled(address) 6027dc61
+	/// @dev EVM selector for this function is: 0x6027dc61,
+	///  or in textual repr: sponsoringEnabled(address)
 	function sponsoringEnabled(address contractAddress)
 		external
 		view
 		returns (bool);
 
-	// Selector: setSponsoringMode(address,uint8) fde8a560
+	/// @dev EVM selector for this function is: 0xfde8a560,
+	///  or in textual repr: setSponsoringMode(address,uint8)
 	function setSponsoringMode(address contractAddress, uint8 mode) external;
 
-	// Selector: sponsoringMode(address) b70c7267
-	function sponsoringMode(address contractAddress)
+	/// Get current contract sponsoring rate limit
+	/// @param contractAddress Contract to get sponsoring mode of
+	/// @return uint32 Amount of blocks between two sponsored transactions
+	/// @dev EVM selector for this function is: 0x610cfabd,
+	///  or in textual repr: getSponsoringRateLimit(address)
+	function getSponsoringRateLimit(address contractAddress)
 		external
 		view
-		returns (uint8);
+		returns (uint32);
 
-	// Selector: setSponsoringRateLimit(address,uint32) 77b6c908
+	/// Set contract sponsoring rate limit
+	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should
+	///  pass between two sponsored transactions
+	/// @param contractAddress Contract to change sponsoring rate limit of
+	/// @param rateLimit Target rate limit
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x77b6c908,
+	///  or in textual repr: setSponsoringRateLimit(address,uint32)
 	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit)
 		external;
 
-	// Selector: getSponsoringRateLimit(address) 610cfabd
-	function getSponsoringRateLimit(address contractAddress)
-		external
-		view
-		returns (uint32);
-
-	// Selector: allowed(address,address) 5c658165
+	/// Is specified user present in contract allow list
+	/// @dev Contract owner always implicitly included
+	/// @param contractAddress Contract to check allowlist of
+	/// @param user User to check
+	/// @return bool Is specified users exists in contract allowlist
+	/// @dev EVM selector for this function is: 0x5c658165,
+	///  or in textual repr: allowed(address,address)
 	function allowed(address contractAddress, address user)
 		external
 		view
 		returns (bool);
 
-	// Selector: allowlistEnabled(address) c772ef6c
+	/// Toggle user presence in contract allowlist
+	/// @param contractAddress Contract to change allowlist of
+	/// @param user Which user presence should be toggled
+	/// @param isAllowed `true` if user should be allowed to be sponsored
+	///  or call this contract, `false` otherwise
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x4706cc1c,
+	///  or in textual repr: toggleAllowed(address,address,bool)
+	function toggleAllowed(
+		address contractAddress,
+		address user,
+		bool isAllowed
+	) external;
+
+	/// Is this contract has allowlist access enabled
+	/// @dev Allowlist always can have users, and it is used for two purposes:
+	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist
+	///  in case of allowlist access enabled, only users from allowlist may call this contract
+	/// @param contractAddress Contract to get allowlist access of
+	/// @return bool Is specified contract has allowlist access enabled
+	/// @dev EVM selector for this function is: 0xc772ef6c,
+	///  or in textual repr: allowlistEnabled(address)
 	function allowlistEnabled(address contractAddress)
 		external
 		view
 		returns (bool);
 
-	// Selector: toggleAllowlist(address,bool) 36de20f5
+	/// Toggle contract allowlist access
+	/// @param contractAddress Contract to change allowlist access of
+	/// @param enabled Should allowlist access to be enabled?
+	/// @dev EVM selector for this function is: 0x36de20f5,
+	///  or in textual repr: toggleAllowlist(address,bool)
 	function toggleAllowlist(address contractAddress, bool enabled) external;
+}
 
-	// Selector: toggleAllowed(address,address,bool) 4706cc1c
-	function toggleAllowed(
-		address contractAddress,
-		address user,
-		bool allowed
-	) external;
+/// @dev anonymous struct
+struct Tuple0 {
+	address field_0;
+	uint256 field_1;
 }
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -12,246 +12,257 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Inline
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Selector: 79cc6790
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) external returns (bool);
-}
-
-// Selector: 942e8b22
-interface ERC20 is Dummy, ERC165, ERC20Events {
-	// Selector: name() 06fdde03
-	function name() external view returns (string memory);
-
-	// Selector: symbol() 95d89b41
-	function symbol() external view returns (string memory);
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
-
-	// Selector: decimals() 313ce567
-	function decimals() external view returns (uint8);
-
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) external view returns (uint256);
-
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 amount) external returns (bool);
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) external returns (bool);
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address spender, uint256 amount) external returns (bool);
-
-	// Selector: allowance(address,address) dd62ed3e
-	function allowance(address owner, address spender)
-		external
-		view
-		returns (uint256);
-}
-
-// Selector: ffe4da23
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
 interface Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
 	function setCollectionProperty(string memory key, bytes memory value)
 		external;
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
 	function deleteCollectionProperty(string memory key) external;
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
 	function collectionProperty(string memory key)
 		external
 		view
 		returns (bytes memory);
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
 	function setCollectionSponsor(address sponsor) external;
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
 	function confirmCollectionSponsorship() external;
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
 	function setCollectionLimit(string memory limit, uint32 value) external;
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
 	function setCollectionLimit(string memory limit, bool value) external;
 
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
 	function contractAddress() external view returns (address);
 
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
 	function addCollectionAdminSubstrate(uint256 newAdmin) external;
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
 	function removeCollectionAdminSubstrate(uint256 admin) external;
 
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
 	function addCollectionAdmin(address newAdmin) external;
 
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
 	function removeCollectionAdmin(address admin) external;
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
 	function setCollectionNesting(bool enable) external;
 
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
 	function setCollectionNesting(bool enable, address[] memory collections)
 		external;
 
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
 	function setCollectionAccess(uint8 mode) external;
 
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
 	function addToCollectionAllowList(address user) external;
 
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
 	function removeFromCollectionAllowList(address user) external;
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
 	function setCollectionMintMode(bool mode) external;
 
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
 	function isOwnerOrAdmin(address user) external view returns (bool);
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
 	function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
 
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
 
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
 	function setOwner(address newOwner) external;
 
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
 	function setOwnerSubstrate(uint256 newOwner) external;
 }
 
+/// @dev the ERC-165 identifier for this interface is 0x79cc6790
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
+	function burnFrom(address from, uint256 amount) external returns (bool);
+}
+
+/// @dev inlined interface
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+interface ERC20 is Dummy, ERC165, ERC20Events {
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
+	function decimals() external view returns (uint8);
+
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
+	function allowance(address owner, address spender)
+		external
+		view
+		returns (uint256);
+}
+
 interface UniqueFungible is
 	Dummy,
 	ERC165,
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -3,55 +3,26 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	uint256 field_0;
-	string field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
 
 interface ERC165 is Dummy {
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-// Inline
-interface ERC721Events {
-	event Transfer(
-		address indexed from,
-		address indexed to,
-		uint256 indexed tokenId
-	);
-	event Approval(
-		address indexed owner,
-		address indexed approved,
-		uint256 indexed tokenId
-	);
-	event ApprovalForAll(
-		address indexed owner,
-		address indexed operator,
-		bool approved
-	);
 }
 
-// Inline
-interface ERC721MintableEvents {
-	event MintingFinished();
-}
-
-// Selector: 41369377
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0x41369377
 interface TokenProperties is Dummy, ERC165 {
-	// @notice Set permissions for token property.
-	// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// @param key Property key.
-	// @param is_mutable Permission to mutate property.
-	// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	// @param token_owner Permission to mutate property by token owner if property is mutable.
-	//
-	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param key Property key.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	/// @dev EVM selector for this function is: 0x222d97fa,
+	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
 	function setTokenPropertyPermission(
 		string memory key,
 		bool isMutable,
@@ -59,459 +30,497 @@
 		bool tokenOwner
 	) external;
 
-	// @notice Set token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @param value Property value.
-	//
-	// Selector: setProperty(uint256,string,bytes) 1752d67b
+	/// @notice Set token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @param value Property value.
+	/// @dev EVM selector for this function is: 0x1752d67b,
+	///  or in textual repr: setProperty(uint256,string,bytes)
 	function setProperty(
 		uint256 tokenId,
 		string memory key,
 		bytes memory value
 	) external;
 
-	// @notice Delete token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	//
-	// Selector: deleteProperty(uint256,string) 066111d1
+	/// @notice Delete token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x066111d1,
+	///  or in textual repr: deleteProperty(uint256,string)
 	function deleteProperty(uint256 tokenId, string memory key) external;
 
-	// @notice Get token property value.
-	// @dev Throws error if key not found
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @return Property value bytes
-	//
-	// Selector: property(uint256,string) 7228c327
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
 	function property(uint256 tokenId, string memory key)
 		external
 		view
 		returns (bytes memory);
 }
 
-// Selector: 42966c68
-interface ERC721Burnable is Dummy, ERC165 {
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	//  operator of the current owner.
-	// @param tokenId The NFT to approve
-	//
-	// Selector: burn(uint256) 42966c68
-	function burn(uint256 tokenId) external;
-}
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
+interface Collection is Dummy, ERC165 {
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
+	function setCollectionProperty(string memory key, bytes memory value)
+		external;
 
-// Selector: 58800161
-interface ERC721 is Dummy, ERC165, ERC721Events {
-	// @notice Count all NFTs assigned to an owner
-	// @dev NFTs assigned to the zero address are considered invalid, and this
-	//  function throws for queries about the zero address.
-	// @param owner An address for whom to query the balance
-	// @return The number of NFTs owned by `owner`, possibly zero
-	//
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) external view returns (uint256);
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
+	function deleteCollectionProperty(string memory key) external;
 
-	// @notice Find the owner of an NFT
-	// @dev NFTs assigned to zero address are considered invalid, and queries
-	//  about them do throw.
-	// @param tokenId The identifier for an NFT
-	// @return The address of the owner of the NFT
-	//
-	// Selector: ownerOf(uint256) 6352211e
-	function ownerOf(uint256 tokenId) external view returns (address);
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key)
+		external
+		view
+		returns (bytes memory);
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
-	function safeTransferFromWithData(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) external;
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
+	function setCollectionSponsor(address sponsor) external;
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() external;
 
-	// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
-	//  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	//  THEY MAY BE PERMANENTLY LOST
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this NFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param from The current owner of the NFT
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
+	function setCollectionLimit(string memory limit, uint32 value) external;
 
-	// @notice Set or reaffirm the approved address for an NFT
-	// @dev The zero address indicates there is no approved address.
-	// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	//  operator of the current owner.
-	// @param approved The new approved NFT controller
-	// @param tokenId The NFT to approve
-	//
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address approved, uint256 tokenId) external;
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
+	function setCollectionLimit(string memory limit, bool value) external;
 
-	// @dev Not implemented
-	//
-	// Selector: setApprovalForAll(address,bool) a22cb465
-	function setApprovalForAll(address operator, bool approved) external;
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() external view returns (address);
 
-	// @dev Not implemented
-	//
-	// Selector: getApproved(uint256) 081812fc
-	function getApproved(uint256 tokenId) external view returns (address);
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
+	function addCollectionAdminSubstrate(uint256 newAdmin) external;
+
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
+	function removeCollectionAdminSubstrate(uint256 admin) external;
+
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
+	function addCollectionAdmin(address newAdmin) external;
 
-	// @dev Not implemented
-	//
-	// Selector: isApprovedForAll(address,address) e985e9c5
-	function isApprovedForAll(address owner, address operator)
-		external
-		view
-		returns (address);
-}
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
+	function removeCollectionAdmin(address admin) external;
+
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
+	function setCollectionNesting(bool enable) external;
 
-// Selector: 5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	// @notice A descriptive name for a collection of NFTs in this contract
-	//
-	// Selector: name() 06fdde03
-	function name() external view returns (string memory);
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
+	function setCollectionNesting(bool enable, address[] memory collections)
+		external;
 
-	// @notice An abbreviated name for NFTs in this contract
-	//
-	// Selector: symbol() 95d89b41
-	function symbol() external view returns (string memory);
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(uint8 mode) external;
 
-	// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	//
-	// @dev If the token has a `url` property and it is not empty, it is returned.
-	//  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	//  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	//  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	//  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	//
-	// @return token's const_metadata
-	//
-	// Selector: tokenURI(uint256) c87b56dd
-	function tokenURI(uint256 tokenId) external view returns (string memory);
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
+	function addToCollectionAllowList(address user) external;
+
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
+	function removeFromCollectionAllowList(address user) external;
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) external;
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
+	function isOwnerOrAdmin(address user) external view returns (bool);
+
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
+	function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() external returns (string memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
+	function setOwner(address newOwner) external;
+
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
+	function setOwnerSubstrate(uint256 newOwner) external;
+}
+
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+interface ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) external;
 }
 
-// Selector: 68ccfe89
+/// @dev inlined interface
+interface ERC721MintableEvents {
+	event MintingFinished();
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x68ccfe89
 interface ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
-	// Selector: mintingFinished() 05d2035b
+	/// @dev EVM selector for this function is: 0x05d2035b,
+	///  or in textual repr: mintingFinished()
 	function mintingFinished() external view returns (bool);
 
-	// @notice Function to mint token.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted NFT
-	//
-	// Selector: mint(address,uint256) 40c10f19
+	/// @notice Function to mint token.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted NFT
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
 	function mint(address to, uint256 tokenId) external returns (bool);
 
-	// @notice Function to mint token with the given tokenUri.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted NFT
-	// @param tokenUri Token URI that would be stored in the NFT properties
-	//
-	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	/// @notice Function to mint token with the given tokenUri.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted NFT
+	/// @param tokenUri Token URI that would be stored in the NFT properties
+	/// @dev EVM selector for this function is: 0x50bb4e7f,
+	///  or in textual repr: mintWithTokenURI(address,uint256,string)
 	function mintWithTokenURI(
 		address to,
 		uint256 tokenId,
 		string memory tokenUri
 	) external returns (bool);
 
-	// @dev Not implemented
-	//
-	// Selector: finishMinting() 7d64bcb4
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x7d64bcb4,
+	///  or in textual repr: finishMinting()
 	function finishMinting() external returns (bool);
 }
 
-// Selector: 780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid NFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
-	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	// @dev Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		external
-		view
-		returns (uint256);
-
-	// @notice Count NFTs tracked by this contract
-	// @return A count of valid NFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
-}
-
-// Selector: d74d154f
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0xd74d154f
 interface ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an NFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @notice Transfer ownership of an NFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 tokenId) external;
 
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this NFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	// @param from The current owner of the NFT
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
 	function burnFrom(address from, uint256 tokenId) external;
 
-	// @notice Returns next free NFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
+	/// @notice Returns next free NFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
 	function nextTokenId() external view returns (uint256);
 
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted NFTs
-	//
-	// Selector: mintBulk(address,uint256[]) 44a9945e
+	/// @notice Function to mint multiple tokens.
+	/// @dev `tokenIds` should be an array of consecutive numbers and first number
+	///  should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokenIds IDs of the minted NFTs
+	/// @dev EVM selector for this function is: 0x44a9945e,
+	///  or in textual repr: mintBulk(address,uint256[])
 	function mintBulk(address to, uint256[] memory tokenIds)
 		external
 		returns (bool);
 
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
-	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+	/// @notice Function to mint multiple tokens with the given tokenUris.
+	/// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	///  numbers and first number should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokens array of pairs of token ID and token URI for minted tokens
+	/// @dev EVM selector for this function is: 0x36543006,
+	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens)
 		external
 		returns (bool);
 }
 
-// Selector: ffe4da23
-interface Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		external;
+/// @dev anonymous struct
+struct Tuple8 {
+	uint256 field_0;
+	string field_1;
+}
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) external;
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid NFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) external view returns (uint256);
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index)
 		external
 		view
-		returns (bytes memory);
+		returns (uint256);
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) external;
+	/// @notice Count NFTs tracked by this contract
+	/// @return A count of valid NFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+}
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() external;
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) external;
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) external;
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
 
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() external view returns (address);
+/// @dev inlined interface
+interface ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
 
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
-	function addCollectionAdminSubstrate(uint256 newAdmin) external;
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+interface ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
-	function removeCollectionAdminSubstrate(uint256 admin) external;
-
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) external;
-
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) external;
-
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) external;
-
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		external;
-
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) external;
-
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) external;
+	/// @notice Find the owner of an NFT
+	/// @dev NFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	/// @param tokenId The identifier for an NFT
+	/// @return The address of the owner of the NFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) external view returns (address);
 
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) external;
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
 
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
-	function isOwnerOrAdmin(address user) external view returns (bool);
+	/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
-	function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
+	/// @notice Set or reaffirm the approved address for an NFT
+	/// @dev The zero address indicates there is no approved address.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param approved The new approved NFT controller
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) external;
 
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
-	function uniqueCollectionType() external returns (string memory);
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) external;
 
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
-	function setOwner(address newOwner) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) external view returns (address);
 
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
-	function setOwnerSubstrate(uint256 newOwner) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator)
+		external
+		view
+		returns (address);
 }
 
 interface UniqueNFT is
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	uint256 field_0;
-	string field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -18,40 +12,17 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Inline
-interface ERC721Events {
-	event Transfer(
-		address indexed from,
-		address indexed to,
-		uint256 indexed tokenId
-	);
-	event Approval(
-		address indexed owner,
-		address indexed approved,
-		uint256 indexed tokenId
-	);
-	event ApprovalForAll(
-		address indexed owner,
-		address indexed operator,
-		bool approved
-	);
-}
-
-// Inline
-interface ERC721MintableEvents {
-	event MintingFinished();
-}
-
-// Selector: 41369377
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0x41369377
 interface TokenProperties is Dummy, ERC165 {
-	// @notice Set permissions for token property.
-	// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// @param key Property key.
-	// @param is_mutable Permission to mutate property.
-	// @param collection_admin Permission to mutate property by collection admin if property is mutable.
-	// @param token_owner Permission to mutate property by token owner if property is mutable.
-	//
-	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param key Property key.
+	/// @param isMutable Permission to mutate property.
+	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	/// @dev EVM selector for this function is: 0x222d97fa,
+	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
 	function setTokenPropertyPermission(
 		string memory key,
 		bool isMutable,
@@ -59,469 +30,505 @@
 		bool tokenOwner
 	) external;
 
-	// @notice Set token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @param value Property value.
-	//
-	// Selector: setProperty(uint256,string,bytes) 1752d67b
+	/// @notice Set token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @param value Property value.
+	/// @dev EVM selector for this function is: 0x1752d67b,
+	///  or in textual repr: setProperty(uint256,string,bytes)
 	function setProperty(
 		uint256 tokenId,
 		string memory key,
 		bytes memory value
 	) external;
 
-	// @notice Delete token property value.
-	// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	//
-	// Selector: deleteProperty(uint256,string) 066111d1
+	/// @notice Delete token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x066111d1,
+	///  or in textual repr: deleteProperty(uint256,string)
 	function deleteProperty(uint256 tokenId, string memory key) external;
 
-	// @notice Get token property value.
-	// @dev Throws error if key not found
-	// @param tokenId ID of the token.
-	// @param key Property key.
-	// @return Property value bytes
-	//
-	// Selector: property(uint256,string) 7228c327
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
 	function property(uint256 tokenId, string memory key)
 		external
 		view
 		returns (bytes memory);
 }
 
-// Selector: 42966c68
-interface ERC721Burnable is Dummy, ERC165 {
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
-	//  operator of the current owner.
-	// @param tokenId The RFT to approve
-	//
-	// Selector: burn(uint256) 42966c68
-	function burn(uint256 tokenId) external;
-}
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xffe4da23
+interface Collection is Dummy, ERC165 {
+	/// Set collection property.
+	///
+	/// @param key Property key.
+	/// @param value Propery value.
+	/// @dev EVM selector for this function is: 0x2f073f66,
+	///  or in textual repr: setCollectionProperty(string,bytes)
+	function setCollectionProperty(string memory key, bytes memory value)
+		external;
 
-// Selector: 58800161
-interface ERC721 is Dummy, ERC165, ERC721Events {
-	// @notice Count all RFTs assigned to an owner
-	// @dev RFTs assigned to the zero address are considered invalid, and this
-	//  function throws for queries about the zero address.
-	// @param owner An address for whom to query the balance
-	// @return The number of RFTs owned by `owner`, possibly zero
-	//
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) external view returns (uint256);
+	/// Delete collection property.
+	///
+	/// @param key Property key.
+	/// @dev EVM selector for this function is: 0x7b7debce,
+	///  or in textual repr: deleteCollectionProperty(string)
+	function deleteCollectionProperty(string memory key) external;
 
-	// @notice Find the owner of an RFT
-	// @dev RFTs assigned to zero address are considered invalid, and queries
-	//  about them do throw.
-	//  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
-	//  the tokens that are partially owned.
-	// @param tokenId The identifier for an RFT
-	// @return The address of the owner of the RFT
-	//
-	// Selector: ownerOf(uint256) 6352211e
-	function ownerOf(uint256 tokenId) external view returns (address);
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key)
+		external
+		view
+		returns (bytes memory);
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
-	function safeTransferFromWithData(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) external;
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x7623402e,
+	///  or in textual repr: setCollectionSponsor(address)
+	function setCollectionSponsor(address sponsor) external;
 
-	// @dev Not implemented
-	//
-	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() external;
 
-	// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
-	//  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	//  THEY MAY BE PERMANENTLY LOST
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the NFT
-	// @param to The new owner
-	// @param tokenId The NFT to transfer
-	// @param _value Not used for an NFT
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"accountTokenOwnershipLimit",
+	/// 	"sponsoredDataSize",
+	/// 	"sponsoredDataRateLimit",
+	/// 	"tokenLimit",
+	/// 	"sponsorTransferTimeout",
+	/// 	"sponsorApproveTimeout"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x6a3841db,
+	///  or in textual repr: setCollectionLimit(string,uint32)
+	function setCollectionLimit(string memory limit, uint32 value) external;
 
-	// @dev Not implemented
-	//
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address approved, uint256 tokenId) external;
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Name of the limit. Valid names:
+	/// 	"ownerCanTransfer",
+	/// 	"ownerCanDestroy",
+	/// 	"transfersEnabled"
+	/// @param value Value of the limit.
+	/// @dev EVM selector for this function is: 0x993b7fba,
+	///  or in textual repr: setCollectionLimit(string,bool)
+	function setCollectionLimit(string memory limit, bool value) external;
 
-	// @dev Not implemented
-	//
-	// Selector: setApprovalForAll(address,bool) a22cb465
-	function setApprovalForAll(address operator, bool approved) external;
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() external view returns (address);
 
-	// @dev Not implemented
-	//
-	// Selector: getApproved(uint256) 081812fc
-	function getApproved(uint256 tokenId) external view returns (address);
+	/// Add collection admin by substrate address.
+	/// @param newAdmin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x5730062b,
+	///  or in textual repr: addCollectionAdminSubstrate(uint256)
+	function addCollectionAdminSubstrate(uint256 newAdmin) external;
 
-	// @dev Not implemented
-	//
-	// Selector: isApprovedForAll(address,address) e985e9c5
-	function isApprovedForAll(address owner, address operator)
-		external
-		view
-		returns (address);
-}
+	/// Remove collection admin by substrate address.
+	/// @param admin Substrate administrator address.
+	/// @dev EVM selector for this function is: 0x4048fcf9,
+	///  or in textual repr: removeCollectionAdminSubstrate(uint256)
+	function removeCollectionAdminSubstrate(uint256 admin) external;
 
-// Selector: 5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	// @notice A descriptive name for a collection of RFTs in this contract
-	//
-	// Selector: name() 06fdde03
-	function name() external view returns (string memory);
+	/// Add collection admin.
+	/// @param newAdmin Address of the added administrator.
+	/// @dev EVM selector for this function is: 0x92e462c7,
+	///  or in textual repr: addCollectionAdmin(address)
+	function addCollectionAdmin(address newAdmin) external;
 
-	// @notice An abbreviated name for RFTs in this contract
-	//
-	// Selector: symbol() 95d89b41
-	function symbol() external view returns (string memory);
+	/// Remove collection admin.
+	///
+	/// @param admin Address of the removed administrator.
+	/// @dev EVM selector for this function is: 0xfafd7b42,
+	///  or in textual repr: removeCollectionAdmin(address)
+	function removeCollectionAdmin(address admin) external;
 
-	// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	//
-	// @dev If the token has a `url` property and it is not empty, it is returned.
-	//  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	//  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	//  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	//  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	//
-	// @return token's const_metadata
-	//
-	// Selector: tokenURI(uint256) c87b56dd
-	function tokenURI(uint256 tokenId) external view returns (string memory);
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	/// @dev EVM selector for this function is: 0x112d4586,
+	///  or in textual repr: setCollectionNesting(bool)
+	function setCollectionNesting(bool enable) external;
+
+	/// Toggle accessibility of collection nesting.
+	///
+	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	/// @param collections Addresses of collections that will be available for nesting.
+	/// @dev EVM selector for this function is: 0x64872396,
+	///  or in textual repr: setCollectionNesting(bool,address[])
+	function setCollectionNesting(bool enable, address[] memory collections)
+		external;
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// 	0 for Normal
+	/// 	1 for AllowList
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(uint8 mode) external;
+
+	/// Add the user to the allowed list.
+	///
+	/// @param user Address of a trusted user.
+	/// @dev EVM selector for this function is: 0x67844fe6,
+	///  or in textual repr: addToCollectionAllowList(address)
+	function addToCollectionAllowList(address user) external;
+
+	/// Remove the user from the allowed list.
+	///
+	/// @param user Address of a removed user.
+	/// @dev EVM selector for this function is: 0x85c51acb,
+	///  or in textual repr: removeFromCollectionAllowList(address)
+	function removeFromCollectionAllowList(address user) external;
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) external;
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x9811b0c7,
+	///  or in textual repr: isOwnerOrAdmin(address)
+	function isOwnerOrAdmin(address user) external view returns (bool);
+
+	/// Check that substrate account is the owner or admin of the collection
+	///
+	/// @param user account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x68910e00,
+	///  or in textual repr: isOwnerOrAdminSubstrate(uint256)
+	function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() external returns (string memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
+	function setOwner(address newOwner) external;
+
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
+	function setOwnerSubstrate(uint256 newOwner) external;
+}
+
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+interface ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The RFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) external;
 }
 
-// Selector: 68ccfe89
+/// @dev inlined interface
+interface ERC721MintableEvents {
+	event MintingFinished();
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x68ccfe89
 interface ERC721Mintable is Dummy, ERC165, ERC721MintableEvents {
-	// Selector: mintingFinished() 05d2035b
+	/// @dev EVM selector for this function is: 0x05d2035b,
+	///  or in textual repr: mintingFinished()
 	function mintingFinished() external view returns (bool);
 
-	// @notice Function to mint token.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted RFT
-	//
-	// Selector: mint(address,uint256) 40c10f19
+	/// @notice Function to mint token.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted RFT
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
 	function mint(address to, uint256 tokenId) external returns (bool);
 
-	// @notice Function to mint token with the given tokenUri.
-	// @dev `tokenId` should be obtained with `nextTokenId` method,
-	//  unlike standard, you can't specify it manually
-	// @param to The new owner
-	// @param tokenId ID of the minted RFT
-	// @param tokenUri Token URI that would be stored in the RFT properties
-	//
-	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
+	/// @notice Function to mint token with the given tokenUri.
+	/// @dev `tokenId` should be obtained with `nextTokenId` method,
+	///  unlike standard, you can't specify it manually
+	/// @param to The new owner
+	/// @param tokenId ID of the minted RFT
+	/// @param tokenUri Token URI that would be stored in the RFT properties
+	/// @dev EVM selector for this function is: 0x50bb4e7f,
+	///  or in textual repr: mintWithTokenURI(address,uint256,string)
 	function mintWithTokenURI(
 		address to,
 		uint256 tokenId,
 		string memory tokenUri
 	) external returns (bool);
 
-	// @dev Not implemented
-	//
-	// Selector: finishMinting() 7d64bcb4
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x7d64bcb4,
+	///  or in textual repr: finishMinting()
 	function finishMinting() external returns (bool);
 }
-
-// Selector: 780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid RFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
-	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) external view returns (uint256);
 
-	// Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		external
-		view
-		returns (uint256);
-
-	// @notice Count RFTs tracked by this contract
-	// @return A count of valid RFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
-}
-
-// Selector: 7c3bef89
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0x7c3bef89
 interface ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an RFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param to The new owner
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @notice Transfer ownership of an RFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param to The new owner
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 tokenId) external;
 
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the RFT
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the RFT
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
 	function burnFrom(address from, uint256 tokenId) external;
 
-	// @notice Returns next free RFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
+	/// @notice Returns next free RFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
 	function nextTokenId() external view returns (uint256);
 
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted RFTs
-	//
-	// Selector: mintBulk(address,uint256[]) 44a9945e
+	/// @notice Function to mint multiple tokens.
+	/// @dev `tokenIds` should be an array of consecutive numbers and first number
+	///  should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokenIds IDs of the minted RFTs
+	/// @dev EVM selector for this function is: 0x44a9945e,
+	///  or in textual repr: mintBulk(address,uint256[])
 	function mintBulk(address to, uint256[] memory tokenIds)
 		external
 		returns (bool);
 
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
-	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+	/// @notice Function to mint multiple tokens with the given tokenUris.
+	/// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	///  numbers and first number should be obtained with `nextTokenId` method
+	/// @param to The new owner
+	/// @param tokens array of pairs of token ID and token URI for minted tokens
+	/// @dev EVM selector for this function is: 0x36543006,
+	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens)
 		external
 		returns (bool);
 
-	// Returns EVM address for refungible token
-	//
-	// @param token ID of the token
-	//
-	// Selector: tokenContractAddress(uint256) ab76fac6
+	/// Returns EVM address for refungible token
+	///
+	/// @param token ID of the token
+	/// @dev EVM selector for this function is: 0xab76fac6,
+	///  or in textual repr: tokenContractAddress(uint256)
 	function tokenContractAddress(uint256 token)
 		external
 		view
 		returns (address);
 }
 
-// Selector: ffe4da23
-interface Collection is Dummy, ERC165 {
-	// Set collection property.
-	//
-	// @param key Property key.
-	// @param value Propery value.
-	//
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		external;
+/// @dev anonymous struct
+struct Tuple8 {
+	uint256 field_0;
+	string field_1;
+}
 
-	// Delete collection property.
-	//
-	// @param key Property key.
-	//
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) external;
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid RFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) external view returns (uint256);
 
-	// Get collection property.
-	//
-	// @dev Throws error if key not found.
-	//
-	// @param key Property key.
-	// @return bytes The property corresponding to the key.
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
+	/// Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index)
 		external
 		view
-		returns (bytes memory);
+		returns (uint256);
 
-	// Set the sponsor of the collection.
-	//
-	// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	//
-	// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	//
-	// Selector: setCollectionSponsor(address) 7623402e
-	function setCollectionSponsor(address sponsor) external;
+	/// @notice Count RFTs tracked by this contract
+	/// @return A count of valid RFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+}
 
-	// Collection sponsorship confirmation.
-	//
-	// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	//
-	// Selector: confirmCollectionSponsorship() 3c50e97a
-	function confirmCollectionSponsorship() external;
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of RFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"accountTokenOwnershipLimit",
-	// 	"sponsoredDataSize",
-	// 	"sponsoredDataRateLimit",
-	// 	"tokenLimit",
-	// 	"sponsorTransferTimeout",
-	// 	"sponsorApproveTimeout"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,uint32) 6a3841db
-	function setCollectionLimit(string memory limit, uint32 value) external;
+	/// @notice An abbreviated name for RFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
 
-	// Set limits for the collection.
-	// @dev Throws error if limit not found.
-	// @param limit Name of the limit. Valid names:
-	// 	"ownerCanTransfer",
-	// 	"ownerCanDestroy",
-	// 	"transfersEnabled"
-	// @param value Value of the limit.
-	//
-	// Selector: setCollectionLimit(string,bool) 993b7fba
-	function setCollectionLimit(string memory limit, bool value) external;
-
-	// Get contract address.
-	//
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() external view returns (address);
-
-	// Add collection admin by substrate address.
-	// @param new_admin Substrate administrator address.
-	//
-	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
-	function addCollectionAdminSubstrate(uint256 newAdmin) external;
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
 
-	// Remove collection admin by substrate address.
-	// @param admin Substrate administrator address.
-	//
-	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
-	function removeCollectionAdminSubstrate(uint256 admin) external;
-
-	// Add collection admin.
-	// @param new_admin Address of the added administrator.
-	//
-	// Selector: addCollectionAdmin(address) 92e462c7
-	function addCollectionAdmin(address newAdmin) external;
-
-	// Remove collection admin.
-	//
-	// @param new_admin Address of the removed administrator.
-	//
-	// Selector: removeCollectionAdmin(address) fafd7b42
-	function removeCollectionAdmin(address admin) external;
-
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	//
-	// Selector: setCollectionNesting(bool) 112d4586
-	function setCollectionNesting(bool enable) external;
-
-	// Toggle accessibility of collection nesting.
-	//
-	// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// @param collections Addresses of collections that will be available for nesting.
-	//
-	// Selector: setCollectionNesting(bool,address[]) 64872396
-	function setCollectionNesting(bool enable, address[] memory collections)
-		external;
+/// @dev inlined interface
+interface ERC721Events {
+	event Transfer(
+		address indexed from,
+		address indexed to,
+		uint256 indexed tokenId
+	);
+	event Approval(
+		address indexed owner,
+		address indexed approved,
+		uint256 indexed tokenId
+	);
+	event ApprovalForAll(
+		address indexed owner,
+		address indexed operator,
+		bool approved
+	);
+}
 
-	// Set the collection access method.
-	// @param mode Access mode
-	// 	0 for Normal
-	// 	1 for AllowList
-	//
-	// Selector: setCollectionAccess(uint8) 41835d4c
-	function setCollectionAccess(uint8 mode) external;
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x58800161
+interface ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
 
-	// Add the user to the allowed list.
-	//
-	// @param user Address of a trusted user.
-	//
-	// Selector: addToCollectionAllowList(address) 67844fe6
-	function addToCollectionAllowList(address user) external;
+	/// @notice Find the owner of an RFT
+	/// @dev RFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	///  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
+	///  the tokens that are partially owned.
+	/// @param tokenId The identifier for an RFT
+	/// @return The address of the owner of the RFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) external view returns (address);
 
-	// Remove the user from the allowed list.
-	//
-	// @param user Address of a removed user.
-	//
-	// Selector: removeFromCollectionAllowList(address) 85c51acb
-	function removeFromCollectionAllowList(address user) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x60a11672,
+	///  or in textual repr: safeTransferFromWithData(address,address,uint256,bytes)
+	function safeTransferFromWithData(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) external;
 
-	// Switch permission for minting.
-	//
-	// @param mode Enable if "true".
-	//
-	// Selector: setCollectionMintMode(bool) 00018e84
-	function setCollectionMintMode(bool mode) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
 
-	// Check that account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdmin(address) 9811b0c7
-	function isOwnerOrAdmin(address user) external view returns (bool);
+	/// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
 
-	// Check that substrate account is the owner or admin of the collection
-	//
-	// @param user account to verify
-	// @return "true" if account is the owner or admin
-	//
-	// Selector: isOwnerOrAdminSubstrate(uint256) 68910e00
-	function isOwnerOrAdminSubstrate(uint256 user) external view returns (bool);
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) external;
 
-	// Returns collection type
-	//
-	// @return `Fungible` or `NFT` or `ReFungible`
-	//
-	// Selector: uniqueCollectionType() d34b55b8
-	function uniqueCollectionType() external returns (string memory);
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) external;
 
-	// Changes collection owner to another account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner account
-	//
-	// Selector: setOwner(address) 13af4035
-	function setOwner(address newOwner) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) external view returns (address);
 
-	// Changes collection owner to another substrate account
-	//
-	// @dev Owner can be changed only by current owner
-	// @param newOwner new owner substrate account
-	//
-	// Selector: setOwnerSubstrate(uint256) b212138f
-	function setOwnerSubstrate(uint256 newOwner) external;
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator)
+		external
+		view
+		returns (address);
 }
 
 interface UniqueRefungible is
modifiedtests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungibleToken.sol
+++ b/tests/src/eth/api/UniqueRefungibleToken.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -12,120 +12,127 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Inline
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Selector: 042f1106
+/// @dev the ERC-165 identifier for this interface is 0x042f1106
 interface ERC1633UniqueExtensions is Dummy, ERC165 {
-	// Selector: setParentNFT(address,uint256) 042f1106
+	/// @dev EVM selector for this function is: 0x042f1106,
+	///  or in textual repr: setParentNFT(address,uint256)
 	function setParentNFT(address collection, uint256 nftId)
 		external
 		returns (bool);
 }
 
-// Selector: 5755c3f2
+/// @dev the ERC-165 identifier for this interface is 0x5755c3f2
 interface ERC1633 is Dummy, ERC165 {
-	// Selector: parentToken() 80a54001
+	/// @dev EVM selector for this function is: 0x80a54001,
+	///  or in textual repr: parentToken()
 	function parentToken() external view returns (address);
 
-	// Selector: parentTokenId() d7f083f3
+	/// @dev EVM selector for this function is: 0xd7f083f3,
+	///  or in textual repr: parentTokenId()
 	function parentTokenId() external view returns (uint256);
 }
 
-// Selector: 942e8b22
+/// @dev the ERC-165 identifier for this interface is 0xab8deb37
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev Function that burns an amount of the token of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
+	function burnFrom(address from, uint256 amount) external returns (bool);
+
+	/// @dev Function that changes total amount of the tokens.
+	///  Throws if `msg.sender` doesn't owns all of the tokens.
+	/// @param amount New total amount of the tokens.
+	/// @dev EVM selector for this function is: 0xd2418ca7,
+	///  or in textual repr: repartition(uint256)
+	function repartition(uint256 amount) external returns (bool);
+}
+
+/// @dev inlined interface
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(
+		address indexed owner,
+		address indexed spender,
+		uint256 value
+	);
+}
+
+/// @title Standard ERC20 token
+///
+/// @dev Implementation of the basic standard token.
+/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
 interface ERC20 is Dummy, ERC165, ERC20Events {
-	// @return the name of the token.
-	//
-	// Selector: name() 06fdde03
+	/// @return the name of the token.
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
 	function name() external view returns (string memory);
 
-	// @return the symbol of the token.
-	//
-	// Selector: symbol() 95d89b41
+	/// @return the symbol of the token.
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
 	function symbol() external view returns (string memory);
 
-	// @dev Total number of tokens in existence
-	//
-	// Selector: totalSupply() 18160ddd
+	/// @dev Total number of tokens in existence
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
 	function totalSupply() external view returns (uint256);
 
-	// @dev Not supported
-	//
-	// Selector: decimals() 313ce567
+	/// @dev Not supported
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
 	function decimals() external view returns (uint8);
 
-	// @dev Gets the balance of the specified address.
-	// @param owner The address to query the balance of.
-	// @return An uint256 representing the amount owned by the passed address.
-	//
-	// Selector: balanceOf(address) 70a08231
+	/// @dev Gets the balance of the specified address.
+	/// @param owner The address to query the balance of.
+	/// @return An uint256 representing the amount owned by the passed address.
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
 	function balanceOf(address owner) external view returns (uint256);
 
-	// @dev Transfer token for a specified address
-	// @param to The address to transfer to.
-	// @param amount The amount to be transferred.
-	//
-	// Selector: transfer(address,uint256) a9059cbb
+	/// @dev Transfer token for a specified address
+	/// @param to The address to transfer to.
+	/// @param amount The amount to be transferred.
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
 	function transfer(address to, uint256 amount) external returns (bool);
 
-	// @dev Transfer tokens from one address to another
-	// @param from address The address which you want to send tokens from
-	// @param to address The address which you want to transfer to
-	// @param amount uint256 the amount of tokens to be transferred
-	//
-	// Selector: transferFrom(address,address,uint256) 23b872dd
+	/// @dev Transfer tokens from one address to another
+	/// @param from address The address which you want to send tokens from
+	/// @param to address The address which you want to transfer to
+	/// @param amount uint256 the amount of tokens to be transferred
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
 	function transferFrom(
 		address from,
 		address to,
 		uint256 amount
 	) external returns (bool);
 
-	// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
-	// Beware that changing an allowance with this method brings the risk that someone may use both the old
-	// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
-	// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
-	// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
-	// @param spender The address which will spend the funds.
-	// @param amount The amount of tokens to be spent.
-	//
-	// Selector: approve(address,uint256) 095ea7b3
+	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
+	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+	/// @param spender The address which will spend the funds.
+	/// @param amount The amount of tokens to be spent.
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
 	function approve(address spender, uint256 amount) external returns (bool);
 
-	// @dev Function to check the amount of tokens that an owner allowed to a spender.
-	// @param owner address The address which owns the funds.
-	// @param spender address The address which will spend the funds.
-	// @return A uint256 specifying the amount of tokens still available for the spender.
-	//
-	// Selector: allowance(address,address) dd62ed3e
+	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
+	/// @param owner address The address which owns the funds.
+	/// @param spender address The address which will spend the funds.
+	/// @return A uint256 specifying the amount of tokens still available for the spender.
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
 	function allowance(address owner, address spender)
 		external
 		view
 		returns (uint256);
-}
-
-// Selector: ab8deb37
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	// @dev Function that burns an amount of the token of a given account,
-	// deducting from the sender's allowance for said account.
-	// @param from The account whose tokens will be burnt.
-	// @param amount The amount that will be burnt.
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) external returns (bool);
-
-	// @dev Function that changes total amount of the tokens.
-	//  Throws if `msg.sender` doesn't owns all of the tokens.
-	// @param amount New total amount of the tokens.
-	//
-	// Selector: repartition(uint256) d2418ca7
-	function repartition(uint256 amount) external returns (bool);
 }
 
 interface UniqueRefungibleToken is
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -255,7 +255,7 @@
           { "internalType": "uint256", "name": "field_0", "type": "uint256" },
           { "internalType": "string", "name": "field_1", "type": "string" }
         ],
-        "internalType": "struct Tuple0[]",
+        "internalType": "struct Tuple8[]",
         "name": "tokens",
         "type": "tuple[]"
       }
@@ -361,7 +361,7 @@
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
       { "internalType": "bytes", "name": "data", "type": "bytes" }
     ],
-    "name": "safeTransferFromWithData",
+    "name": "safeTransferFrom",
     "outputs": [],
     "stateMutability": "nonpayable",
     "type": "function"
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -255,7 +255,7 @@
           { "internalType": "uint256", "name": "field_0", "type": "uint256" },
           { "internalType": "string", "name": "field_1", "type": "string" }
         ],
-        "internalType": "struct Tuple0[]",
+        "internalType": "struct Tuple8[]",
         "name": "tokens",
         "type": "tuple[]"
       }
modifiedtests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/util/contractHelpersAbi.json
+++ b/tests/src/eth/util/contractHelpersAbi.json
@@ -197,19 +197,6 @@
   },
   {
     "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "sponsoringMode",
-    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
       { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
     ],
     "name": "supportsInterface",
@@ -225,7 +212,7 @@
         "type": "address"
       },
       { "internalType": "address", "name": "user", "type": "address" },
-      { "internalType": "bool", "name": "allowed", "type": "bool" }
+      { "internalType": "bool", "name": "isAllowed", "type": "bool" }
     ],
     "name": "toggleAllowed",
     "outputs": [],