--- 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}}; --- 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" --- 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" --- 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 . - -#![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 -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 { -/// 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() -} --- 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 . - -#![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 { - 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::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); -impl FromMeta for IsList { - fn from_list(items: &[NestedMeta]) -> darling::Result { - 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, -} - -enum AbiType { - // type - Plain(Ident), - // (type1,type2) - Tuple(Vec), - // type[] - Vec(Box), - // type[20] - Array(Box, usize), -} -impl AbiType { - fn try_from(value: &Type) -> syn::Result { - 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 { - 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::()?; - 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 { - 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! { - >::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 { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - - let weight_content; - syn::parenthesized!(weight_content in content); - Ok(WeightAttr(weight_content.parse::()?)) - } -} - -struct Method { - name: Ident, - camel_name: String, - pascal_name: Ident, - screaming_name: Ident, - selector_str: String, - selector: u32, - args: Vec, - has_normal_args: bool, - mutability: Mutability, - result: Type, - weight: Option, - docs: Vec, -} -impl Method { - fn try_from(value: &ImplItemMethod) -> syn::Result { - 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::(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\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: >::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 = <.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, - info: InterfaceInfo, - methods: Vec, -} -impl SolidityInterface { - pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result { - 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> { - 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!() - } - } - } - } - } -} --- 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 . - -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 { - 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! { - >::new(#indexed, #camel_name) - } - } -} - -struct Event { - name: Ident, - name_screaming: Ident, - fields: Vec, - selector: [u8; 32], - selector_str: String, -} - -impl Event { - fn try_from(variant: &Variant) -> syn::Result { - 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, -} - -impl Events { - pub fn try_from(data: &DeriveInput) -> syn::Result { - 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(), - } - } - } - } - } -} --- 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"] --- /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`] + + \ No newline at end of file --- /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"] } --- /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 . + +#![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 +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() +} --- /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 . + +#![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); +impl Parse for IsList { + fn parse(input: ParseStream) -> syn::Result { + let mut out = vec![]; + loop { + if input.is_empty() { + break; + } + let name = input.parse::()?; + let lookahead = input.lookahead1(); + let via = if lookahead.peek(syn::token::Paren) { + let contents; + parenthesized!(contents in input); + let method = contents.parse::()?; + contents.parse::()?; + let ty = contents.parse::()?; + 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::()?; + continue; + } else { + break; + } + } + Ok(Self(out)) + } +} + +pub struct InterfaceInfo { + name: Ident, + is: IsList, + inline_is: IsList, + events: IsList, + expect_selector: Option, +} +impl Parse for InterfaceInfo { + fn parse(input: ParseStream) -> syn::Result { + 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::()?; + input.parse::()?; + if name.replace(input.parse::()?).is_some() { + return Err(syn::Error::new(k.span(), "name is already set")); + } + } else if lookahead.peek(kw::is) { + let k = input.parse::()?; + let contents; + parenthesized!(contents in input); + if is.replace(contents.parse::()?).is_some() { + return Err(syn::Error::new(k.span(), "is is already set")); + } + } else if lookahead.peek(kw::inline_is) { + let k = input.parse::()?; + let contents; + parenthesized!(contents in input); + if inline_is.replace(contents.parse::()?).is_some() { + return Err(syn::Error::new(k.span(), "inline_is is already set")); + } + } else if lookahead.peek(kw::events) { + let k = input.parse::()?; + let contents; + parenthesized!(contents in input); + if events.replace(contents.parse::()?).is_some() { + return Err(syn::Error::new(k.span(), "events is already set")); + } + } else if lookahead.peek(kw::expect_selector) { + let k = input.parse::()?; + input.parse::()?; + let value = input.parse::()?; + if expect_selector + .replace(value.base10_parse::()?) + .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::()?; + } 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, +} +impl Parse for MethodInfo { + fn parse(input: ParseStream) -> syn::Result { + let mut rename_selector = None; + let lookahead = input.lookahead1(); + if lookahead.peek(kw::rename_selector) { + let k = input.parse::()?; + input.parse::()?; + if rename_selector + .replace(input.parse::()?.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), + // type[] + Vec(Box), + // type[20] + Array(Box, usize), +} +impl AbiType { + fn try_from(value: &Type) -> syn::Result { + 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 { + 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::()?; + 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 { + 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! { + >::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, + has_normal_args: bool, + mutability: Mutability, + result: Type, + weight: Option, + docs: Vec, +} +impl Method { + fn try_from(value: &ImplItemMethod) -> syn::Result { + 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::()?; + } 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::()?); + } + } + 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\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: >::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 = <.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, + info: InterfaceInfo, + methods: Vec, + docs: Vec, +} +impl SolidityInterface { + pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result { + 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> { + 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!() + } + } + } + } + } +} --- /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 . + +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 { + 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! { + >::new(#indexed, #camel_name) + } + } +} + +struct Event { + name: Ident, + name_screaming: Ident, + fields: Vec, + selector: [u8; 32], + selector_str: String, +} + +impl Event { + fn try_from(variant: &Variant) -> syn::Result { + 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, +} + +impl Events { + pub fn try_from(data: &DeriveInput) -> syn::Result { + 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(), + } + } + } + } + } +} --- 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 . -//! 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 { Ok(H160(self.read_padleft()?)) } + /// Read [`bool`] at current position, then advance pub fn bool(&mut self) -> Result { 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`] at current position, then advance pub fn bytes(&mut self) -> Result> { 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::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange)) } + /// Read [`u8`] at current position, then advance pub fn uint8(&mut self) -> Result { Ok(self.read_padleft::<1>()?[0]) } + /// Read [`u32`] at current position, then advance pub fn uint32(&mut self) -> Result { Ok(u32::from_be_bytes(self.read_padleft()?)) } + /// Read [`u128`] at current position, then advance pub fn uint128(&mut self) -> Result { Ok(u128::from_be_bytes(self.read_padleft()?)) } + /// Read [`U256`] at current position, then advance pub fn uint256(&mut self) -> Result { 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 { 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 { Ok(usize::from_be_bytes(self.read_padleft()?)) } + /// Slice recursive buffer, advance one word for buffer offset fn subresult(&mut self) -> Result> { - 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, @@ -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 { 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 for AbiReader` pub trait AbiRead { + /// Read item from current position, advanding decoder fn abi_read(&mut self) -> Result; } @@ -318,7 +356,7 @@ { fn abi_read(&mut self) -> Result> { 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 { 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 AbiWrite for ResultWithPostInfo { - // 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)),* $(,)?) => {{ --- 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)[] pub trait ToTopic { + /// Convert value to topic to be used in [`ethereum::Log`] fn to_topic(&self) -> H256; } --- 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 . +//! 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 = core::result::Result; +/// 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, } 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 { + /// 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 = core::result::Result, WithPostDispatchInfo>; --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -14,22 +14,103 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate alloc; use abi::{AbiRead, AbiReader, AbiWriter}; -pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog}; +pub use evm_coder_procedural::{event_topic, fn_selector}; pub mod abi; -pub mod events; -pub use events::ToLog; +pub use events::{ToLog, ToTopic}; use execution::DispatchInfo; pub mod execution; + +/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`] +/// and [`crate::Call`] from impl block. +/// +/// ## Macro syntax +/// +/// `#[solidity_interface(name, is, inline_is, events)]` +/// - *name* - used in generated code, and for Call enum name +/// - *is* - used to provide inheritance in Solidity +/// - *inline_is* - same as `is`, but ERC165::SupportsInterface will work differently: For `is` SupportsInterface(A) will return true +/// if A is one of the interfaces the contract is inherited from (e.g. B is created as `is(A)`). If B is created as `inline_is(A)` +/// SupportsInterface(A) will internally create a new interface that combines all methods of A and B, so SupportsInterface(A) will return +/// false. +/// +/// `#[weight(value)]` +/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which +/// is used by substrate bridge. +/// - *value*: expression, which evaluates to weight required to call this method. +/// This expression can use call arguments to calculate non-constant execution time. +/// This expression should evaluate faster than actual execution does, and may provide worse case +/// than one is called. +/// +/// `#[solidity_interface(rename_selector)]` +/// - *rename_selector* - by default, selector name will be generated by transforming method name +/// from snake_case to camelCase. Use this option, if other naming convention is required. +/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name +/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"` +/// explicitly. +/// +/// Both contract and contract methods may have doccomments, which will end up in a generated +/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro +/// +/// ## Example +/// +/// ```ignore +/// struct SuperContract; +/// struct InlineContract; +/// struct Contract; +/// +/// #[derive(ToLog)] +/// enum ContractEvents { +/// Event(#[indexed] uint32), +/// } +/// +/// /// @dev This contract provides function to multiply two numbers +/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))] +/// impl Contract { +/// /// Multiply two numbers +/// /// @param a First number +/// /// @param b Second number +/// /// @return uint32 Product of two passed numbers +/// /// @dev This function returns error in case of overflow +/// #[weight(200 + a + b)] +/// #[solidity_interface(rename_selector = "mul")] +/// fn mul(&mut self, a: uint32, b: uint32) -> Result { +/// Ok(a.checked_mul(b).ok_or("overflow")?) +/// } +/// } +/// ``` +pub use evm_coder_procedural::solidity_interface; +/// See [`solidity_interface`] +pub use evm_coder_procedural::solidity; +/// See [`solidity_interface`] +pub use evm_coder_procedural::weight; + +/// Derives [`ToLog`] for enum +/// +/// Selectors will be derived from variant names, there is currently no way to have custom naming +/// for them +/// +/// `#[indexed]` +/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data +pub use evm_coder_procedural::ToLog; + +// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros +#[doc(hidden)] +pub mod events; +#[doc(hidden)] pub mod solidity; -/// Solidity type definitions +/// Solidity type definitions (aliases from solidity name to rust type) +/// To be used in [`solidity_interface`] definitions, to make sure there is no +/// type conflict between Rust code and generated definitions pub mod types { - #![allow(non_camel_case_types)] + #![allow(non_camel_case_types, missing_docs)] #[cfg(not(feature = "std"))] use alloc::{vec::Vec}; @@ -54,6 +135,8 @@ pub type string = ::std::string::String; pub type bytes = Vec; + /// Solidity doesn't have `void` type, however we have special implementation + /// for empty tuple return type pub type void = (); //#region Special types @@ -63,36 +146,64 @@ pub type caller = address; //#endregion + /// Ethereum typed call message, similar to solidity + /// `msg` object. pub struct Msg { pub call: C, + /// Address of user, which called this contract. pub caller: H160, + /// Payment amount to contract. + /// Contract should reject payment, if target call is not payable, + /// and there is no `receiver()` function defined. pub value: U256, } } +/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro pub trait Call: Sized { + /// Parse call buffer into typed call enum fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result>; } +/// Intended to be used as `#[weight]` output type +/// Should be same between evm-coder and substrate to avoid confusion +/// +/// Isn't same thing as gas, some mapping is required between those types pub type Weight = u64; +/// In substrate, we have benchmarking, which allows +/// us to not rely on gas metering, but instead predict amount of gas to execute call pub trait Weighted: Call { + /// Predict weight of this call fn weight(&self) -> DispatchInfo; } +/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro +/// on interface implementation, or for externally-owned real EVM contract pub trait Callable { + /// Call contract using specified call data fn call(&mut self, call: types::Msg) -> execution::ResultWithPostInfo; } -/// Implementation is implicitly provided for all interfaces +/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`], +/// this structure holds parsed data for ERC165Call subvariant +/// +/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every +/// implementing contract /// -/// Note: no Callable implementation is provided +/// See #[derive(Debug)] pub enum ERC165Call { - SupportsInterface { interface_id: types::bytes4 }, + /// ERC165 provides single method, which returns true, if contract + /// implements specified interface + SupportsInterface { + /// Requested interface + interface_id: types::bytes4, + }, } impl ERC165Call { + /// ERC165 selector is provided by standard pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7); } --- 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 . +//! 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>, + /// Code => id + /// id ordering is required to perform topo-sort on the resulting data + structs: RefCell>, anonymous: RefCell, usize>>, id: Cell, } @@ -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 { - self.structs.into_inner() + pub fn finish(self) -> Vec { + let mut data = self.structs.into_inner().into_iter().collect::>(); + 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`) pub trait CanBePlacedInVec {} } @@ -402,7 +416,8 @@ } pub struct SolidityFunction { 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 { + 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) )?; } --- a/crates/evm-coder/tests/generics.rs +++ b/crates/evm-coder/tests/generics.rs @@ -19,14 +19,14 @@ struct Generic(PhantomData); -#[solidity_interface(name = "GenericIs")] +#[solidity_interface(name = GenericIs)] impl Generic { fn test_1(&self) -> Result { unreachable!() } } -#[solidity_interface(name = "Generic", is(GenericIs))] +#[solidity_interface(name = Generic, is(GenericIs))] impl> Generic { fn test_2(&self) -> Result { unreachable!() @@ -35,7 +35,7 @@ generate_stubgen!(gen_iface, GenericCall<()>, false); -#[solidity_interface(name = "GenericWhere")] +#[solidity_interface(name = GenericWhere)] impl Generic where T: core::fmt::Debug, --- 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 { unreachable!() } } -#[solidity_interface(name = "OurInterface1")] +#[solidity_interface(name = OurInterface1)] impl Impls { fn fn_b(&self, _input: uint128) -> Result { 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 {} --- 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 { unreachable!() --- 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 { --- 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 CollectionHandle 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 { 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 { let caller = T::CrossAccountId::from_eth(caller); let admin = T::CrossAccountId::from_eth(admin); --- /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 --- 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 . +//! 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(SubstrateRecorder); +/// See [`ContractHelpersCall`] +pub struct ContractHelpers(SubstrateRecorder); impl WithRecorder for ContractHelpers { fn recorder(&self) -> &SubstrateRecorder { &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 ContractHelpers 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
{ Ok(>::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 { 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 { 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 { 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 { Ok(Pallet::::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 { Ok(match Sponsoring::::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 { self.recorder().consume_sload()?; @@ -175,10 +182,21 @@ Ok(()) } - fn sponsoring_mode(&self, contract_address: address) -> Result { - Ok(>::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 { + Ok(>::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 @@ >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; >::set_sponsoring_rate_limit(contract_address, rate_limit.into()); - Ok(()) } - fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result { - Ok(>::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 { self.0.consume_sload()?; Ok(>::allowed(contract_address, user)) } - fn allowlist_enabled(&self, contract_address: address) -> Result { - Ok(>::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 { self.recorder().consume_sload()?; self.recorder().consume_sstore()?; >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; - >::toggle_allowlist(contract_address, enabled); + >::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 { + Ok(>::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 { self.recorder().consume_sload()?; self.recorder().consume_sstore()?; >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; - >::toggle_allowed(contract_address, user, allowed); - + >::toggle_allowlist(contract_address, enabled); Ok(()) } } +/// Implements [`OnMethodCall`], which delegates call to [`ContractHelpers`] pub struct HelpersOnMethodCall(PhantomData<*const T>); impl OnMethodCall for HelpersOnMethodCall where @@ -283,6 +314,7 @@ } } +/// Hooks into contract creation, storing owner of newly deployed contract pub struct HelpersOnCreate(PhantomData<*const T>); impl OnCreate for HelpersOnCreate { fn on_create(owner: H160, contract: H160) { @@ -290,6 +322,7 @@ } } +/// Bridge to pallet-sponsoring pub struct HelpersContractSponsoring(PhantomData<*const T>); impl SponsorshipHandler)> for HelpersContractSponsoring --- 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 . +#![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; + /// In case of enabled sponsoring, but no sponsoring rate limit set, + /// this value will be used implicitly type DefaultSponsoringRateLimit: Get; } #[pallet::error] pub enum Error { - /// 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 { >::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 { >::remove(contract); @@ -234,22 +241,27 @@ >::remove(contract) } + /// Set duration between two sponsored contract calls pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) { >::insert(contract, rate_limit); } + /// Is user added to allowlist, or he is owner of specified contract pub fn allowed(contract: H160, user: H160) -> bool { >::get(&contract, &user) || >::get(&contract) == user } + /// Toggle contract allowlist access pub fn toggle_allowlist(contract: H160, enabled: bool) { >::insert(contract, enabled) } + /// Toggle user presence in contract's allowlist pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) { >::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!(>::get(&contract) == user, Error::::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 } } --- 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; } --- /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 --- 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 . +#![allow(missing_docs)] + use super::{Call, Config, Pallet}; use frame_benchmarking::benchmarks; use frame_system::RawOrigin; --- 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 . +#![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 { + /// 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 Pallet { + /// Start contract migration, inserts contract stub at target address, + /// and marks account as pending, allowing to insert storage #[pallet::weight(>::begin())] pub fn begin(origin: OriginFor, 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(>::set_data(data.len() as u32))] pub fn set_data( origin: OriginFor, @@ -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(>::finish(code.len() as u32))] pub fn finish(origin: OriginFor, address: H160, code: Vec) -> DispatchResult { ensure_root(origin)?; @@ -97,6 +109,7 @@ } } + /// Implements [`pallet_evm::OnMethodCall`], which reserves accounts with pending migration pub struct OnMethodCall(PhantomData); impl pallet_evm::OnMethodCall for OnMethodCall { fn is_reserved(contract: &H160) -> bool { --- 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}}; --- /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 --- 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 . +#![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)>; - type Currency: Currency; } #[pallet::pallet] @@ -43,6 +44,7 @@ pub struct Pallet(_); } +/// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm pub struct TransactionValidityHack(PhantomData<*const T>); impl fp_evm::TransactionValidityHack for TransactionValidityHack { fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option { --- 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 FungibleHandle { fn name(&self) -> Result { Ok(decode_utf16(self.name.iter().copied()) @@ -129,7 +129,7 @@ } } -#[solidity_interface(name = "ERC20UniqueExtensions")] +#[solidity_interface(name = ERC20UniqueExtensions)] impl FungibleHandle { #[weight(>::burn_from())] fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result { @@ -147,11 +147,11 @@ } #[solidity_interface( - name = "UniqueFungible", + name = UniqueFungible, is( ERC20, ERC20UniqueExtensions, - via("CollectionHandle", common_mut, Collection) + Collection(common_mut, CollectionHandle), ) )] impl FungibleHandle where T::AccountId: From<[u8; 32]> {} --- 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, --- 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 NonfungibleHandle { /// @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 NonfungibleHandle { /// @notice A descriptive name for a collection of NFTs in this contract fn name(&self) -> Result { @@ -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 NonfungibleHandle { /// @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 NonfungibleHandle { /// @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 { // TODO: Not implemetable Err("not implemented".into()) @@ -333,7 +333,6 @@ _from: address, _to: address, _token_id: uint256, - _value: value, ) -> Result { // 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(>::transfer_from())] fn transfer_from( &mut self, @@ -356,7 +354,6 @@ from: address, to: address, token_id: uint256, - _value: value, ) -> Result { 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(>::approve())] - fn approve( - &mut self, - caller: caller, - approved: address, - token_id: uint256, - _value: value, - ) -> Result { + fn approve(&mut self, caller: caller, approved: address, token_id: uint256) -> Result { 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 NonfungibleHandle { /// @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 NonfungibleHandle { fn minting_finished(&self) -> Result { Ok(false) @@ -597,22 +588,15 @@ } /// @title Unique extensions for ERC721. -#[solidity_interface(name = "ERC721UniqueExtensions")] +#[solidity_interface(name = ERC721UniqueExtensions)] impl NonfungibleHandle { /// @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(>::transfer())] - fn transfer( - &mut self, - caller: caller, - to: address, - token_id: uint256, - _value: value, - ) -> Result { + fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result { 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(>::burn_from())] - fn burn_from( - &mut self, - caller: caller, - from: address, - token_id: uint256, - _value: value, - ) -> Result { + fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result { 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", common_mut, Collection), + Collection(common_mut, CollectionHandle), TokenProperties, ) )] --- 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; } } --- 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 RefungibleHandle { /// @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 RefungibleHandle { /// @notice A descriptive name for a collection of RFTs in this contract fn name(&self) -> Result { @@ -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 RefungibleHandle { /// @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 RefungibleHandle { /// @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 { // TODO: Not implemetable Err("not implemented".into()) @@ -334,7 +333,6 @@ _from: address, _to: address, _token_id: uint256, - _value: value, ) -> Result { // 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(>::transfer_from_creating_removing())] fn transfer_from( &mut self, @@ -358,7 +355,6 @@ from: address, to: address, token_id: uint256, - _value: value, ) -> Result { 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 { + fn approve(&mut self, _caller: caller, _approved: address, _token_id: uint256) -> Result { 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 RefungibleHandle { /// @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 RefungibleHandle { fn minting_finished(&self) -> Result { Ok(false) @@ -616,7 +606,7 @@ } /// @title Unique extensions for ERC721. -#[solidity_interface(name = "ERC721UniqueExtensions")] +#[solidity_interface(name = ERC721UniqueExtensions)] impl RefungibleHandle { /// @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(>::transfer_creating_removing())] - fn transfer( - &mut self, - caller: caller, - to: address, - token_id: uint256, - _value: value, - ) -> Result { + fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result { 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(>::burn_from())] - fn burn_from( - &mut self, - caller: caller, - from: address, - token_id: uint256, - _value: value, - ) -> Result { + fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result { 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", common_mut, Collection), + Collection(common_mut, CollectionHandle), TokenProperties, ) )] --- a/pallets/refungible/src/erc_token.rs +++ b/pallets/refungible/src/erc_token.rs @@ -49,7 +49,7 @@ pub struct RefungibleTokenHandle(pub RefungibleHandle, pub TokenId); -#[solidity_interface(name = "ERC1633")] +#[solidity_interface(name = ERC1633)] impl RefungibleTokenHandle { fn parent_token(&self) -> Result
{ self.consume_store_reads(2)?; @@ -87,7 +87,7 @@ } } -#[solidity_interface(name = "ERC1633UniqueExtensions")] +#[solidity_interface(name = ERC1633UniqueExtensions)] impl RefungibleTokenHandle { #[solidity(rename_selector = "setParentNFT")] #[weight(>::token_owner() + >::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 RefungibleTokenHandle { /// @return the name of the token. fn name(&self) -> Result { @@ -246,7 +246,7 @@ } } -#[solidity_interface(name = "ERC20UniqueExtensions")] +#[solidity_interface(name = ERC20UniqueExtensions)] impl RefungibleTokenHandle { /// @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 RefungibleTokenHandle where T::AccountId: From<[u8; 32]> {} --- 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; } } --- 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; } } --- 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 EvmCollectionHelpers 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(>::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 { if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) { --- 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 --- 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; } --- 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 --- 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; } --- 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, --- 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 --- 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 --- 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 --- 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" --- 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[]" } --- 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": [],