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
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17#![doc = include_str!("../README.md")]
18#![deny(missing_docs)]
17#![cfg_attr(not(feature = "std"), no_std)]19#![cfg_attr(not(feature = "std"), no_std)]
18#[cfg(not(feature = "std"))]20#[cfg(not(feature = "std"))]
19extern crate alloc;21extern crate alloc;
2022
21use abi::{AbiRead, AbiReader, AbiWriter};23use abi::{AbiRead, AbiReader, AbiWriter};
22pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog};24pub use evm_coder_procedural::{event_topic, fn_selector};
23pub mod abi;25pub mod abi;
24pub mod events;
25pub use events::ToLog;26pub use events::{ToLog, ToTopic};
26use execution::DispatchInfo;27use execution::DispatchInfo;
27pub mod execution;28pub mod execution;
29
30/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`]
31/// and [`crate::Call`] from impl block.
32///
33/// ## Macro syntax
34///
35/// `#[solidity_interface(name, is, inline_is, events)]`
36/// - *name* - used in generated code, and for Call enum name
37/// - *is* - used to provide inheritance in Solidity
38/// - *inline_is* - same as `is`, but ERC165::SupportsInterface will work differently: For `is` SupportsInterface(A) will return true
39/// 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)`
40/// SupportsInterface(A) will internally create a new interface that combines all methods of A and B, so SupportsInterface(A) will return
41/// false.
42///
43/// `#[weight(value)]`
44/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which
45/// is used by substrate bridge.
46/// - *value*: expression, which evaluates to weight required to call this method.
47/// This expression can use call arguments to calculate non-constant execution time.
48/// This expression should evaluate faster than actual execution does, and may provide worse case
49/// than one is called.
50///
51/// `#[solidity_interface(rename_selector)]`
52/// - *rename_selector* - by default, selector name will be generated by transforming method name
53/// from snake_case to camelCase. Use this option, if other naming convention is required.
54/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name
55/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"`
56/// explicitly.
57///
58/// Both contract and contract methods may have doccomments, which will end up in a generated
59/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro
60///
61/// ## Example
62///
63/// ```ignore
64/// struct SuperContract;
65/// struct InlineContract;
66/// struct Contract;
67///
68/// #[derive(ToLog)]
69/// enum ContractEvents {
70/// Event(#[indexed] uint32),
71/// }
72///
73/// /// @dev This contract provides function to multiply two numbers
74/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
75/// impl Contract {
76/// /// Multiply two numbers
77/// /// @param a First number
78/// /// @param b Second number
79/// /// @return uint32 Product of two passed numbers
80/// /// @dev This function returns error in case of overflow
81/// #[weight(200 + a + b)]
82/// #[solidity_interface(rename_selector = "mul")]
83/// fn mul(&mut self, a: uint32, b: uint32) -> Result<uint32> {
84/// Ok(a.checked_mul(b).ok_or("overflow")?)
85/// }
86/// }
87/// ```
88pub use evm_coder_procedural::solidity_interface;
89/// See [`solidity_interface`]
90pub use evm_coder_procedural::solidity;
91/// See [`solidity_interface`]
92pub use evm_coder_procedural::weight;
93
94/// Derives [`ToLog`] for enum
95///
96/// Selectors will be derived from variant names, there is currently no way to have custom naming
97/// for them
98///
99/// `#[indexed]`
100/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
101pub use evm_coder_procedural::ToLog;
102
103// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros
104#[doc(hidden)]
105pub mod events;
106#[doc(hidden)]
28pub mod solidity;107pub mod solidity;
29108
30/// Solidity type definitions109/// Solidity type definitions (aliases from solidity name to rust type)
110/// To be used in [`solidity_interface`] definitions, to make sure there is no
111/// type conflict between Rust code and generated definitions
31pub mod types {112pub mod types {
32 #![allow(non_camel_case_types)]113 #![allow(non_camel_case_types, missing_docs)]
33114
34 #[cfg(not(feature = "std"))]115 #[cfg(not(feature = "std"))]
35 use alloc::{vec::Vec};116 use alloc::{vec::Vec};
54 pub type string = ::std::string::String;135 pub type string = ::std::string::String;
55 pub type bytes = Vec<u8>;136 pub type bytes = Vec<u8>;
56137
138 /// Solidity doesn't have `void` type, however we have special implementation
139 /// for empty tuple return type
57 pub type void = ();140 pub type void = ();
58141
59 //#region Special types142 //#region Special types
63 pub type caller = address;146 pub type caller = address;
64 //#endregion147 //#endregion
65148
149 /// Ethereum typed call message, similar to solidity
150 /// `msg` object.
66 pub struct Msg<C> {151 pub struct Msg<C> {
67 pub call: C,152 pub call: C,
153 /// Address of user, which called this contract.
68 pub caller: H160,154 pub caller: H160,
155 /// Payment amount to contract.
156 /// Contract should reject payment, if target call is not payable,
157 /// and there is no `receiver()` function defined.
69 pub value: U256,158 pub value: U256,
70 }159 }
71}160}
72161
162/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro
73pub trait Call: Sized {163pub trait Call: Sized {
164 /// Parse call buffer into typed call enum
74 fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;165 fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
75}166}
76167
168/// Intended to be used as `#[weight]` output type
169/// Should be same between evm-coder and substrate to avoid confusion
170///
171/// Isn't same thing as gas, some mapping is required between those types
77pub type Weight = u64;172pub type Weight = u64;
78173
174/// In substrate, we have benchmarking, which allows
175/// us to not rely on gas metering, but instead predict amount of gas to execute call
79pub trait Weighted: Call {176pub trait Weighted: Call {
177 /// Predict weight of this call
80 fn weight(&self) -> DispatchInfo;178 fn weight(&self) -> DispatchInfo;
81}179}
82180
181/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro
182/// on interface implementation, or for externally-owned real EVM contract
83pub trait Callable<C: Call> {183pub trait Callable<C: Call> {
184 /// Call contract using specified call data
84 fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;185 fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;
85}186}
86187
87/// Implementation is implicitly provided for all interfaces188/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`],
189/// this structure holds parsed data for ERC165Call subvariant
88///190///
89/// Note: no Callable implementation is provided191/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every
192/// implementing contract
193///
194/// See <https://eips.ethereum.org/EIPS/eip-165>
90#[derive(Debug)]195#[derive(Debug)]
91pub enum ERC165Call {196pub enum ERC165Call {
197 /// ERC165 provides single method, which returns true, if contract
198 /// implements specified interface
92 SupportsInterface { interface_id: types::bytes4 },199 SupportsInterface {
200 /// Requested interface
201 interface_id: types::bytes4,
202 },
93}203}
94204
95impl ERC165Call {205impl ERC165Call {
206 /// ERC165 selector is provided by standard
96 pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);207 pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);
97}208}
98209
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
--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -3,7 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -12,7 +12,7 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Inline
+/// @dev inlined interface
 interface CollectionHelpersEvents {
 	event CollectionCreated(
 		address indexed owner,
@@ -20,22 +20,24 @@
 	);
 }
 
-// Selector: 675f3074
+/// @title Contract, which allows users to operate with collections
+/// @dev the ERC-165 identifier for this interface is 0x675f3074
 interface 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,
 		string memory tokenPrefix
 	) external returns (address);
 
-	// 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,
@@ -43,14 +45,16 @@
 		string memory baseUri
 	) external returns (address);
 
-	// 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,
 		string memory tokenPrefix
 	) external returns (address);
 
-	// 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,
@@ -58,11 +62,11 @@
 		string memory baseUri
 	) external returns (address);
 
-	// 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)
 		external
 		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": [],