--- a/crates/evm-coder/procedural/src/abi_derive/derive_enum.rs +++ b/crates/evm-coder/procedural/src/abi_derive/derive_enum.rs @@ -1,20 +1,52 @@ use quote::quote; +use super::extract_docs; + pub fn impl_solidity_option<'a>( + docs: Vec, name: &proc_macro2::Ident, - enum_options: impl Iterator, + enum_options: impl Iterator + Clone, ) -> proc_macro2::TokenStream { - let enum_options = enum_options.map(|opt| { + let variant_names = enum_options.clone().map(|opt| { + let opt = &opt.ident; let s = name.to_string() + "." + opt.to_string().as_str(); let as_string = proc_macro2::Literal::string(s.as_str()); quote!(#name::#opt => #as_string,) }); + let solidity_name = name.to_string(); + + let solidity_fields = enum_options.map(|v| { + let docs = extract_docs(&v.attrs).expect("TODO: handle bad docs"); + let name = v.ident.to_string(); + quote! { + SolidityEnumVariant { + docs: &[#(#docs),*], + name: #name, + } + } + }); + quote!( #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityEnum for #name { + impl ::evm_coder::solidity::SolidityEnumTy for #name { + fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String { + use evm_coder::solidity::*; + use core::fmt::Write; + let interface = SolidityEnum { + docs: &[#(#docs),*], + name: #solidity_name, + fields: &[#( + #solidity_fields, + )*], + }; + let mut out = String::new(); + let _ = interface.format(&mut out, tc); + tc.collect(out); + #solidity_name.to_string() + } fn solidity_option(&self) -> &str { match self { - #(#enum_options)* + #(#variant_names)* } } } @@ -23,11 +55,12 @@ pub fn impl_enum_from_u8<'a>( name: &proc_macro2::Ident, - enum_options: impl Iterator, + enum_options: impl Iterator, ) -> proc_macro2::TokenStream { let error_str = format!("Value not convertible into enum \"{name}\""); let error_str = proc_macro2::Literal::string(&error_str); let enum_options = enum_options.enumerate().map(|(i, opt)| { + let opt = &opt.ident; let n = proc_macro2::Literal::u8_suffixed(i as u8); quote! {#n => Ok(#name::#opt),} }); @@ -93,7 +126,7 @@ writer: &mut impl ::core::fmt::Write, tc: &::evm_coder::solidity::TypeCollector, ) -> ::core::fmt::Result { - write!(writer, "{}", tc.collect_struct::()) + write!(writer, "{}", tc.collect_enum::()) } fn is_simple() -> bool { @@ -104,49 +137,7 @@ writer: &mut impl ::core::fmt::Write, tc: &::evm_coder::solidity::TypeCollector, ) -> ::core::fmt::Result { - write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnum>::solidity_option(&<#name>::default())) - } - } - ) -} - -pub fn impl_enum_solidity_struct_collect<'a>( - name: &syn::Ident, - enum_options: impl Iterator, - option_count: usize, - enum_options_docs: impl Iterator>>, - docs: &[proc_macro2::TokenStream], -) -> proc_macro2::TokenStream { - let string_name = name.to_string(); - let enum_options = enum_options - .zip(enum_options_docs) - .enumerate() - .map(|(i, (opt, doc))| { - let opt = proc_macro2::Literal::string(opt.to_string().as_str()); - let doc = doc.expect("Doc parsing error"); - let comma = if i != option_count - 1 { "," } else { "" }; - quote! { - #(#doc)* - writeln!(str, "\t{}{}", #opt, #comma).expect("Enum format option"); - } - }); - - quote!( - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::StructCollect for #name { - fn name() -> String { - #string_name.into() - } - - fn declaration() -> String { - use std::fmt::Write; - - let mut str = String::new(); - #(#docs)* - writeln!(str, "enum {} {{", ::name()).unwrap(); - #(#enum_options)* - writeln!(str, "}}").unwrap(); - str + write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnumTy>::solidity_option(&<#name>::default())) } } ) --- a/crates/evm-coder/procedural/src/abi_derive/derive_struct.rs +++ b/crates/evm-coder/procedural/src/abi_derive/derive_struct.rs @@ -1,5 +1,6 @@ use super::extract_docs; use quote::quote; +use syn::Field; pub fn tuple_type<'a>( field_types: impl Iterator + Clone, @@ -87,10 +88,6 @@ &field.ty } -pub fn map_field_to_doc(field: &syn::Field) -> syn::Result> { - extract_docs(&field.attrs, true) -} - pub fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream { quote! { impl ::evm_coder::sealed::CanBePlacedInVec for #ident {} @@ -147,27 +144,44 @@ pub fn impl_struct_solidity_type<'a>( name: &syn::Ident, - field_types: impl Iterator + Clone, - params_count: usize, + docs: Vec, + fields: impl Iterator + Clone, ) -> proc_macro2::TokenStream { - let len = proc_macro2::Literal::usize_suffixed(params_count); + let solidity_name = name.to_string(); + let solidity_fields = fields.enumerate().map(|(i, f)| { + let name = f + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| format!("field_{i}")); + let ty = &f.ty; + let docs = extract_docs(&f.attrs).expect("TODO: handle bad docs"); + quote! { + SolidityStructField::<#ty> { + docs: &[#(#docs),*], + name: #name, + ty: ::core::marker::PhantomData, + } + } + }); quote! { #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityType for #name { - fn names(tc: &::evm_coder::solidity::TypeCollector) -> Vec { - let mut collected = - Vec::with_capacity(::len()); - #({ - let mut out = String::new(); - <#field_types as ::evm_coder::solidity::SolidityTypeName>::solidity_name(&mut out, tc) - .expect("no fmt error"); - collected.push(out); - })* - collected - } - - fn len() -> usize { - #len + impl ::evm_coder::solidity::SolidityStructTy for #name { + /// Generate solidity definitions for methods described in this struct + fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String { + use evm_coder::solidity::*; + use core::fmt::Write; + let interface = SolidityStruct { + docs: &[#(#docs),*], + name: #solidity_name, + fields: (#( + #solidity_fields, + )*), + }; + let mut out = String::new(); + let _ = interface.format(&mut out, tc); + tc.collect(out); + #solidity_name.to_string() } } } @@ -214,47 +228,4 @@ } } } -} - -pub fn impl_struct_solidity_struct_collect<'a>( - name: &syn::Ident, - field_names: impl Iterator + Clone, - field_types: impl Iterator + Clone, - field_docs: impl Iterator>> + Clone, - docs: &[proc_macro2::TokenStream], -) -> syn::Result { - let string_name = name.to_string(); - let name_type = field_names - .into_iter() - .zip(field_types) - .zip(field_docs) - .map(|((name, ty), doc)| { - let field_docs = doc.expect("Doc parse error"); - let name = format!("{}", name); - quote!( - #(#field_docs)* - write!(str, "\t{} ", <#ty as ::evm_coder::solidity::StructCollect>::name()).unwrap(); - writeln!(str, "{};", #name).unwrap(); - ) - }); - - Ok(quote! { - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::StructCollect for #name { - fn name() -> String { - #string_name.into() - } - - fn declaration() -> String { - use std::fmt::Write; - - let mut str = String::new(); - #(#docs)* - writeln!(str, "struct {} {{", Self::name()).unwrap(); - #(#name_type)* - writeln!(str, "}}").unwrap(); - str - } - } - }) } --- a/crates/evm-coder/procedural/src/abi_derive/mod.rs +++ b/crates/evm-coder/procedural/src/abi_derive/mod.rs @@ -19,20 +19,18 @@ ast: &syn::DeriveInput, ) -> syn::Result { let name = &ast.ident; - let docs = extract_docs(&ast.attrs, false)?; - let (is_named_fields, field_names, field_types, field_docs, params_count) = match ds.fields { + let docs = extract_docs(&ast.attrs)?; + let (is_named_fields, field_names, field_types, params_count) = match ds.fields { syn::Fields::Named(ref fields) => Ok(( true, fields.named.iter().enumerate().map(map_field_to_name), fields.named.iter().map(map_field_to_type), - fields.named.iter().map(map_field_to_doc), fields.named.len(), )), syn::Fields::Unnamed(ref fields) => Ok(( false, fields.unnamed.iter().enumerate().map(map_field_to_name), fields.unnamed.iter().map(map_field_to_type), - fields.unnamed.iter().map(map_field_to_doc), fields.unnamed.len(), )), syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")), @@ -52,11 +50,9 @@ let abi_type = impl_struct_abi_type(name, tuple_type.clone()); let abi_read = impl_struct_abi_read(name, tuple_type, tuple_names, struct_from_tuple); let abi_write = impl_struct_abi_write(name, is_named_fields, tuple_ref_type, tuple_data); - let solidity_type = impl_struct_solidity_type(name, field_types.clone(), params_count); + let solidity_type = impl_struct_solidity_type(name, docs, ds.fields.iter()); let solidity_type_name = impl_struct_solidity_type_name(name, field_types.clone(), params_count); - let solidity_struct_collect = - impl_struct_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?; Ok(quote! { #can_be_plcaed_in_vec @@ -65,7 +61,6 @@ #abi_write #solidity_type #solidity_type_name - #solidity_struct_collect }) } @@ -75,25 +70,16 @@ ) -> syn::Result { let name = &ast.ident; check_repr_u8(name, &ast.attrs)?; - let docs = extract_docs(&ast.attrs, false)?; - let option_count = check_and_count_options(de)?; - let enum_options = de.variants.iter().map(|v| &v.ident); - let enum_options_docs = de.variants.iter().map(|v| extract_docs(&v.attrs, true)); + let docs = extract_docs(&ast.attrs)?; + let enum_options = de.variants.iter(); let from = impl_enum_from_u8(name, enum_options.clone()); - let solidity_option = impl_solidity_option(name, enum_options.clone()); + let solidity_option = impl_solidity_option(docs, name, enum_options.clone()); let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name); let abi_type = impl_enum_abi_type(name); let abi_read = impl_enum_abi_read(name); let abi_write = impl_enum_abi_write(name); let solidity_type_name = impl_enum_solidity_type_name(name); - let solidity_struct_collect = impl_enum_solidity_struct_collect( - name, - enum_options, - option_count, - enum_options_docs, - &docs, - ); Ok(quote! { #from @@ -103,14 +89,10 @@ #abi_read #abi_write #solidity_type_name - #solidity_struct_collect }) } -fn extract_docs( - attrs: &[syn::Attribute], - is_field_doc: bool, -) -> syn::Result> { +fn extract_docs(attrs: &[syn::Attribute]) -> syn::Result> { attrs .iter() .filter_map(|attr| { @@ -130,16 +112,6 @@ } } None - }) - .enumerate() - .map(|(i, doc)| { - let doc = doc?; - let doc = doc.trim(); - let dev = if i == 0 { " @dev" } else { "" }; - let tab = if is_field_doc { "\t" } else { "" }; - Ok(quote! { - writeln!(str, "{}///{} {}", #tab, #dev, #doc).unwrap(); - }) }) .collect() } --- a/crates/evm-coder/src/abi/impls.rs +++ b/crates/evm-coder/src/abi/impls.rs @@ -135,41 +135,6 @@ } } -impl sealed::CanBePlacedInVec for Property {} - -impl AbiType for Property { - const SIGNATURE: SignatureUnit = make_signature!(new fixed("(string,bytes)")); - - fn is_dynamic() -> bool { - string::is_dynamic() || bytes::is_dynamic() - } - - fn size() -> usize { - ::size() + ::size() - } -} - -impl AbiRead for Property { - fn abi_read(reader: &mut AbiReader) -> Result { - let size = if !Property::is_dynamic() { - Some(::size()) - } else { - None - }; - let mut subresult = reader.subresult(size)?; - let key = ::abi_read(&mut subresult)?; - let value = ::abi_read(&mut subresult)?; - - Ok(Property { key, value }) - } -} - -impl AbiWrite for Property { - fn abi_write(&self, writer: &mut AbiWriter) { - (&self.key, &self.value).abi_write(writer); - } -} - impl AbiWrite for Vec { fn abi_write(&self, writer: &mut AbiWriter) { let is_dynamic = T::is_dynamic(); --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -196,12 +196,6 @@ self.len() == 0 } } - - #[derive(Debug, Default)] - pub struct Property { - pub key: string, - pub value: bytes, - } } /// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro --- a/crates/evm-coder/src/solidity/impls.rs +++ b/crates/evm-coder/src/solidity/impls.rs @@ -1,4 +1,4 @@ -use super::{TypeCollector, SolidityTypeName, SolidityType, StructCollect}; +use super::{TypeCollector, SolidityTypeName, SolidityTupleTy}; use crate::{sealed, types::*}; use core::fmt; use primitive_types::{U256, H160}; @@ -17,16 +17,6 @@ write!(writer, $default) } } - - impl StructCollect for $ty { - fn name() -> String { - $name.to_string() - } - - fn declaration() -> String { - String::default() - } - } )* }; } @@ -81,8 +71,8 @@ macro_rules! impl_tuples { ($($ident:ident)+) => { - impl<$($ident: SolidityTypeName + 'static),+> SolidityType for ($($ident,)+) { - fn names(tc: &TypeCollector) -> Vec { + impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleTy for ($($ident,)+) { + fn fields(tc: &TypeCollector) -> Vec { let mut collected = Vec::with_capacity(Self::len()); $({ let mut out = string::new(); @@ -131,60 +121,3 @@ impl_tuples! {A B C D E F G H} impl_tuples! {A B C D E F G H I} impl_tuples! {A B C D E F G H I J} - -impl StructCollect for Property { - fn name() -> String { - "Property".into() - } - - fn declaration() -> String { - use std::fmt::Write; - - let mut str = String::new(); - writeln!(str, "/// @dev Property struct").unwrap(); - writeln!(str, "struct {} {{", Self::name()).unwrap(); - writeln!(str, "\tstring key;").unwrap(); - writeln!(str, "\tbytes value;").unwrap(); - writeln!(str, "}}").unwrap(); - str - } -} - -impl SolidityTypeName for Property { - fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}", tc.collect_struct::()) - } - - fn is_simple() -> bool { - false - } - - fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}(", tc.collect_struct::())?; - address::solidity_default(writer, tc)?; - write!(writer, ",")?; - uint256::solidity_default(writer, tc)?; - write!(writer, ")") - } -} - -impl SolidityType for Property { - fn names(tc: &TypeCollector) -> Vec { - let mut collected = Vec::with_capacity(Self::len()); - { - let mut out = string::new(); - string::solidity_name(&mut out, tc).expect("no fmt error"); - collected.push(out); - } - { - let mut out = string::new(); - bytes::solidity_name(&mut out, tc).expect("no fmt error"); - collected.push(out); - } - collected - } - - fn len() -> usize { - 2 - } -} --- a/crates/evm-coder/src/solidity/mod.rs +++ b/crates/evm-coder/src/solidity/mod.rs @@ -44,6 +44,7 @@ /// id ordering is required to perform topo-sort on the resulting data structs: RefCell>, anonymous: RefCell, usize>>, + // generic: RefCell>, id: Cell, } impl TypeCollector { @@ -59,8 +60,9 @@ self.id.set(v + 1); v } - pub fn collect_tuple(&self) -> String { - let names = T::names(self); + /// Collect typle, deduplicating it by type, and returning generated name + pub fn collect_tuple(&self) -> String { + let names = T::fields(self); if let Some(id) = self.anonymous.borrow().get(&names).cloned() { return format!("Tuple{}", id); } @@ -76,9 +78,11 @@ self.anonymous.borrow_mut().insert(names, id); format!("Tuple{}", id) } - pub fn collect_struct(&self) -> String { - self.collect(::declaration()); - ::name() + pub fn collect_struct(&self) -> String { + T::generate_solidity_interface(self) + } + pub fn collect_enum(&self) -> String { + T::generate_solidity_interface(self) } pub fn finish(self) -> Vec { let mut data = self.structs.into_inner().into_iter().collect::>(); @@ -419,3 +423,93 @@ writeln!(writer, ");") } } + +#[impl_for_tuples(0, 48)] +impl SolidityItems for Tuple { + for_tuples!( where #( Tuple: SolidityItems ),* ); + + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + for_tuples!( #( + Tuple.solidity_name(writer, tc)?; + )* ); + Ok(()) + } +} + +pub struct SolidityStructField { + pub docs: &'static [&'static str], + pub name: &'static str, + pub ty: PhantomData<*const T>, +} + +impl SolidityItems for SolidityStructField +where + T: SolidityTypeName, +{ + fn solidity_name(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + for doc in self.docs { + writeln!(out, "///{}", doc)?; + } + write!(out, "\t")?; + T::solidity_name(out, tc)?; + writeln!(out, " {};", self.name)?; + Ok(()) + } +} +pub struct SolidityStruct { + pub docs: &'static [&'static str], + // pub generics: + pub name: &'static str, + pub fields: F, +} +impl SolidityStruct +where + F: SolidityItems, +{ + pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + for doc in self.docs { + writeln!(out, "///{}", doc)?; + } + writeln!(out, "struct {} {{", self.name)?; + self.fields.solidity_name(out, tc)?; + writeln!(out, "}}")?; + Ok(()) + } +} + +pub struct SolidityEnumVariant { + pub docs: &'static [&'static str], + pub name: &'static str, +} +impl SolidityItems for SolidityEnumVariant { + fn solidity_name(&self, out: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { + for doc in self.docs { + writeln!(out, "///{}", doc)?; + } + write!(out, "\t{}", self.name)?; + Ok(()) + } +} +pub struct SolidityEnum { + pub docs: &'static [&'static str], + pub name: &'static str, + pub fields: &'static [SolidityEnumVariant], +} +impl SolidityEnum { + pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { + for doc in self.docs { + writeln!(out, "///{}", doc)?; + } + write!(out, "enum {} {{", self.name)?; + for (i, field) in self.fields.iter().enumerate() { + if i != 0 { + write!(out, ",")?; + } + writeln!(out)?; + field.solidity_name(out, tc)?; + } + writeln!(out)?; + writeln!(out, "}}")?; + Ok(()) + } +} --- a/crates/evm-coder/src/solidity/traits.rs +++ b/crates/evm-coder/src/solidity/traits.rs @@ -1,17 +1,6 @@ use super::TypeCollector; use core::fmt; -pub trait StructCollect: 'static { - /// Structure name. - fn name() -> String; - /// Structure declaration. - fn declaration() -> String; -} - -pub trait SolidityEnum: 'static { - fn solidity_option(&self) -> &str; -} - 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 @@ -23,10 +12,17 @@ } } -pub trait SolidityType { - fn names(tc: &TypeCollector) -> Vec; +pub trait SolidityTupleTy: 'static { + fn fields(tc: &TypeCollector) -> Vec; fn len() -> usize; } +pub trait SolidityStructTy: 'static { + fn generate_solidity_interface(tc: &TypeCollector) -> String; +} +pub trait SolidityEnumTy: 'static { + fn generate_solidity_interface(tc: &TypeCollector) -> String; + fn solidity_option(&self) -> &str; +} pub trait SolidityArguments { fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; @@ -46,3 +42,9 @@ tc: &TypeCollector, ) -> fmt::Result; } + +pub trait SolidityItems { + fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; + // For PhantomData fields + // fn is_void() +} --- a/crates/evm-coder/tests/abi_derive_generation.rs +++ b/crates/evm-coder/tests/abi_derive_generation.rs @@ -74,32 +74,6 @@ } #[test] - #[cfg(feature = "stubgen")] - fn struct_collect_type_struct3_derived_mixed_param() { - assert_eq!( - ::name(), - "TypeStruct3DerivedMixedParam" - ); - similar_asserts::assert_eq!( - ::declaration(), - r#"/// @dev Some docs -/// At multi -/// line -struct TypeStruct3DerivedMixedParam { - /// @dev Docs for A - /// multi - /// line - TypeStruct1SimpleParam _a; - /// @dev Docs for B - TypeStruct2DynamicParam _b; - /// @dev Docs for C - TypeStruct2MixedParam _c; -} -"# - ); - } - - #[test] fn impl_abi_type_signature() { assert_eq!( ::SIGNATURE @@ -292,31 +266,6 @@ /// Docs for C TupleStruct2MixedParam, ); - - #[test] - #[cfg(feature = "stubgen")] - fn struct_collect_tuple_struct3_derived_mixed_param() { - assert_eq!( - ::name(), - "TupleStruct3DerivedMixedParam" - ); - similar_asserts::assert_eq!( - ::declaration(), - r#"/// @dev Some docs -/// At multi -/// line -struct TupleStruct3DerivedMixedParam { - /// @dev Docs for A - /// multi - /// line - TupleStruct1SimpleParam field0; - TupleStruct2DynamicParam field1; - /// @dev Docs for C - TupleStruct2MixedParam field2; -} -"# - ); - } #[test] fn impl_abi_type_signature_same_for_structs() { @@ -812,30 +761,5 @@ ::abi_read(&mut decoder).unwrap(); assert_eq!(restored_enum_data, Color::Green); } - } - - #[test] - #[cfg(feature = "stubgen")] - fn struct_collect_enum() { - assert_eq!( - ::name(), - "Color" - ); - similar_asserts::assert_eq!( - ::declaration(), - r#"/// @dev Some docs -/// At multi -/// line -enum Color { - /// @dev Docs for Red - /// multi - /// line - Red, - Green, - /// @dev Docs for Blue - Blue -} -"# - ); } } --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -21,7 +21,6 @@ abi::AbiType, solidity_interface, solidity, ToLog, types::*, - types::Property as PropertyStruct, execution::{Result, Error}, weight, }; @@ -31,15 +30,9 @@ AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property, SponsoringRateLimit, SponsorshipState, }; -use alloc::format; use crate::{ - Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, - eth::{ - EthCrossAccount, CollectionPermissions as EvmPermissions, - CollectionLimits as EvmCollectionLimits, - }, - weights::WeightInfo, + Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, eth, weights::WeightInfo, }; /// Events for ethereum collection helper. @@ -123,21 +116,13 @@ fn set_collection_properties( &mut self, caller: caller, - properties: Vec, + properties: Vec, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); let properties = properties .into_iter() - .map(|PropertyStruct { key, value }| { - let key = >::from(key) - .try_into() - .map_err(|_| "key too large")?; - - let value = value.0.try_into().map_err(|_| "value too large")?; - - Ok(Property { key, value }) - }) + .map(eth::Property::try_into) .collect::>>()?; >::set_collection_properties(self, &caller, properties) @@ -197,7 +182,7 @@ /// /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. - fn collection_properties(&self, keys: Vec) -> Result> { + fn collection_properties(&self, keys: Vec) -> Result> { let keys = keys .into_iter() .map(|key| { @@ -215,12 +200,7 @@ let properties = properties .into_iter() - .map(|p| { - let key = - string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?; - let value = bytes(p.value.to_vec()); - Ok(PropertyStruct { key, value }) - }) + .map(Property::try_into) .collect::>>()?; Ok(properties) } @@ -249,7 +229,7 @@ fn set_collection_sponsor_cross( &mut self, caller: caller, - sponsor: EthCrossAccount, + sponsor: eth::CrossAddress, ) -> Result { self.consume_store_reads_and_writes(1, 1)?; @@ -289,103 +269,65 @@ /// Get current sponsor. /// /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw. - fn collection_sponsor(&self) -> Result { + fn collection_sponsor(&self) -> Result { let sponsor = match self.collection.sponsorship.sponsor() { Some(sponsor) => sponsor, None => return Ok(Default::default()), }; - Ok(EthCrossAccount::from_sub::(&sponsor)) + Ok(eth::CrossAddress::from_sub::(&sponsor)) } /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. - fn collection_limits(&self) -> Result> { - let convert_value_limit = |limit: EvmCollectionLimits, - value: Option| - -> (EvmCollectionLimits, bool, uint256) { - value - .map(|v| (limit, true, v.into())) - .unwrap_or((limit, false, Default::default())) - }; - - let convert_bool_limit = |limit: EvmCollectionLimits, - value: Option| - -> (EvmCollectionLimits, bool, uint256) { - value - .map(|v| { - ( - limit, - true, - if v { - uint256::from(1) - } else { - Default::default() - }, - ) - }) - .unwrap_or((limit, false, Default::default())) - }; - + /// @return Array of collection limits + fn collection_limits(&self) -> Result> { let limits = &self.collection.limits; Ok(vec![ - convert_value_limit( - EvmCollectionLimits::AccountTokenOwnership, + eth::CollectionLimit::new( + eth::CollectionLimitField::AccountTokenOwnership, limits.account_token_ownership_limit, ), - convert_value_limit( - EvmCollectionLimits::SponsoredDataSize, + eth::CollectionLimit::new( + eth::CollectionLimitField::SponsoredDataSize, limits.sponsored_data_size, ), limits .sponsored_data_rate_limit .and_then(|limit| { if let SponsoringRateLimit::Blocks(blocks) = limit { - Some(( - EvmCollectionLimits::SponsoredDataRateLimit, - true, - blocks.into(), + Some(eth::CollectionLimit::new::( + eth::CollectionLimitField::SponsoredDataRateLimit, + blocks, )) } else { None } }) - .unwrap_or(( - EvmCollectionLimits::SponsoredDataRateLimit, - false, + .unwrap_or(eth::CollectionLimit::new::( + eth::CollectionLimitField::SponsoredDataRateLimit, Default::default(), )), - convert_value_limit(EvmCollectionLimits::TokenLimit, limits.token_limit), - convert_value_limit( - EvmCollectionLimits::SponsorTransferTimeout, + eth::CollectionLimit::new(eth::CollectionLimitField::TokenLimit, limits.token_limit), + eth::CollectionLimit::new( + eth::CollectionLimitField::SponsorTransferTimeout, limits.sponsor_transfer_timeout, ), - convert_value_limit( - EvmCollectionLimits::SponsorApproveTimeout, + eth::CollectionLimit::new( + eth::CollectionLimitField::SponsorApproveTimeout, limits.sponsor_approve_timeout, ), - convert_bool_limit( - EvmCollectionLimits::OwnerCanTransfer, + eth::CollectionLimit::new( + eth::CollectionLimitField::OwnerCanTransfer, limits.owner_can_transfer, ), - convert_bool_limit( - EvmCollectionLimits::OwnerCanDestroy, + eth::CollectionLimit::new( + eth::CollectionLimitField::OwnerCanDestroy, limits.owner_can_destroy, ), - convert_bool_limit( - EvmCollectionLimits::TransferEnabled, + eth::CollectionLimit::new( + eth::CollectionLimitField::TransferEnabled, limits.transfers_enabled, ), ]) @@ -393,82 +335,21 @@ /// 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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. + /// @param limit Some limit. #[solidity(rename_selector = "setCollectionLimit")] fn set_collection_limit( &mut self, caller: caller, - limit: EvmCollectionLimits, - status: bool, - value: uint256, + limit: eth::CollectionLimit, ) -> Result { self.consume_store_reads_and_writes(1, 1)?; - if !status { + if !limit.has_value() { return Err(Error::Revert("user can't disable limits".into())); - } - - let value = value - .try_into() - .map_err(|_| Error::Revert(format!("can't convert value to u32 \"{}\"", value)))?; - - let convert_value_to_bool = || match value { - 0 => Ok(false), - 1 => Ok(true), - _ => { - return Err(Error::Revert(format!( - "can't convert value to boolean \"{}\"", - value - ))) - } - }; - - let mut limits = self.limits.clone(); - - match limit { - EvmCollectionLimits::AccountTokenOwnership => { - limits.account_token_ownership_limit = Some(value); - } - EvmCollectionLimits::SponsoredDataSize => { - limits.sponsored_data_size = Some(value); - } - EvmCollectionLimits::SponsoredDataRateLimit => { - limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value)); - } - EvmCollectionLimits::TokenLimit => { - limits.token_limit = Some(value); - } - EvmCollectionLimits::SponsorTransferTimeout => { - limits.sponsor_transfer_timeout = Some(value); - } - EvmCollectionLimits::SponsorApproveTimeout => { - limits.sponsor_approve_timeout = Some(value); - } - EvmCollectionLimits::OwnerCanTransfer => { - limits.owner_can_transfer = Some(convert_value_to_bool()?); - } - EvmCollectionLimits::OwnerCanDestroy => { - limits.owner_can_destroy = Some(convert_value_to_bool()?); - } - EvmCollectionLimits::TransferEnabled => { - limits.transfers_enabled = Some(convert_value_to_bool()?); - } - _ => return Err(Error::Revert(format!("unknown limit \"{:?}\"", limit))), } let caller = T::CrossAccountId::from_eth(caller); - >::update_limits(&caller, self, limits).map_err(dispatch_to_evm::) + >::update_limits(&caller, self, limit.try_into()?).map_err(dispatch_to_evm::) } /// Get contract address. @@ -481,7 +362,7 @@ fn add_collection_admin_cross( &mut self, caller: caller, - new_admin: EthCrossAccount, + new_admin: eth::CrossAddress, ) -> Result { self.consume_store_reads_and_writes(2, 2)?; @@ -496,7 +377,7 @@ fn remove_collection_admin_cross( &mut self, caller: caller, - admin: EthCrossAccount, + admin: eth::CrossAddress, ) -> Result { self.consume_store_reads_and_writes(2, 2)?; @@ -595,10 +476,10 @@ /// Returns nesting for a collection #[solidity(rename_selector = "collectionNestingRestrictedCollectionIds")] - fn collection_nesting_restricted_ids(&self) -> Result<(bool, Vec)> { + fn collection_nesting_restricted_ids(&self) -> Result { let nesting = self.collection.permissions.nesting(); - Ok(( + Ok(eth::CollectionNesting::new( nesting.token_owner, nesting .restricted @@ -609,11 +490,17 @@ } /// Returns permissions for a collection - fn collection_nesting_permissions(&self) -> Result> { + fn collection_nesting_permissions(&self) -> Result> { let nesting = self.collection.permissions.nesting(); Ok(vec![ - (EvmPermissions::CollectionAdmin, nesting.collection_admin), - (EvmPermissions::TokenOwner, nesting.token_owner), + eth::CollectionNestingPermission::new( + eth::CollectionPermissionField::CollectionAdmin, + nesting.collection_admin, + ), + eth::CollectionNestingPermission::new( + eth::CollectionPermissionField::TokenOwner, + nesting.token_owner, + ), ]) } /// Set the collection access method. @@ -638,7 +525,7 @@ /// Checks that user allowed to operate with collection. /// /// @param user User address to check. - fn allowlisted_cross(&self, user: EthCrossAccount) -> Result { + fn allowlisted_cross(&self, user: eth::CrossAddress) -> Result { let user = user.into_sub_cross_account::()?; Ok(Pallet::::allowed(self.id, user)) } @@ -662,7 +549,7 @@ fn add_to_collection_allow_list_cross( &mut self, caller: caller, - user: EthCrossAccount, + user: eth::CrossAddress, ) -> Result { self.consume_store_writes(1)?; @@ -691,7 +578,7 @@ fn remove_from_collection_allow_list_cross( &mut self, caller: caller, - user: EthCrossAccount, + user: eth::CrossAddress, ) -> Result { self.consume_store_writes(1)?; @@ -729,7 +616,7 @@ /// /// @param user User cross account to verify /// @return "true" if account is the owner or admin - fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result { + fn is_owner_or_admin_cross(&self, user: eth::CrossAddress) -> Result { let user = user.into_sub_cross_account::()?; Ok(self.is_owner_or_admin(&user)) } @@ -750,8 +637,8 @@ /// /// @return Tuble with sponsor address and his substrate mirror. /// If address is canonical then substrate mirror is zero and vice versa. - fn collection_owner(&self) -> Result { - Ok(EthCrossAccount::from_sub_cross_account::( + fn collection_owner(&self) -> Result { + Ok(eth::CrossAddress::from_sub_cross_account::( &T::CrossAccountId::from_sub(self.owner.clone()), )) } @@ -774,9 +661,9 @@ /// /// @return Vector of tuples with admins address and his substrate mirror. /// If address is canonical then substrate mirror is zero and vice versa. - fn collection_admins(&self) -> Result> { + fn collection_admins(&self) -> Result> { let result = crate::IsAdmin::::iter_prefix((self.id,)) - .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::(&admin)) + .map(|(admin, _)| eth::CrossAddress::from_sub_cross_account::(&admin)) .collect(); Ok(result) } @@ -788,7 +675,7 @@ fn change_collection_owner_cross( &mut self, caller: caller, - new_owner: EthCrossAccount, + new_owner: eth::CrossAddress, ) -> Result { self.consume_store_writes(1)?; @@ -797,29 +684,6 @@ self.change_owner(caller, new_owner) .map_err(dispatch_to_evm::) } -} - -/// ### Note -/// Do not forget to add: `self.consume_store_reads(1)?;` -fn check_is_owner_or_admin( - caller: caller, - collection: &CollectionHandle, -) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - collection - .check_is_owner_or_admin(&caller) - .map_err(dispatch_to_evm::)?; - Ok(caller) -} - -/// ### Note -/// Do not forget to add: `self.consume_store_writes(1)?;` -fn save(collection: &CollectionHandle) -> Result { - collection - .check_is_internal() - .map_err(dispatch_to_evm::)?; - collection.save().map_err(dispatch_to_evm::)?; - Ok(()) } /// Contains static property keys and values. --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -16,6 +16,8 @@ //! The module contains a number of functions for converting and checking ethereum identifiers. +use alloc::format; +use sp_std::{vec, vec::Vec}; use evm_coder::{ AbiCoder, types::{uint256, address}, @@ -64,15 +66,78 @@ T::CrossAccountId::from_sub(account_id) } +/// Ethereum representation of Optional value with uint256. +#[derive(Debug, Default, AbiCoder)] +pub struct OptionUint { + status: bool, + value: uint256, +} + +impl From for OptionUint { + fn from(value: u32) -> Self { + Self { + status: true, + value: uint256::from(value), + } + } +} + +impl From> for OptionUint { + fn from(value: Option) -> Self { + match value { + Some(value) => Self { + status: true, + value: value.into(), + }, + None => Self { + status: false, + value: Default::default(), + }, + } + } +} + +impl From for OptionUint { + fn from(value: bool) -> Self { + Self { + status: true, + value: if value { + uint256::from(1) + } else { + Default::default() + }, + } + } +} + +impl From> for OptionUint { + fn from(value: Option) -> Self { + match value { + Some(value) => Self::from(value), + None => Self { + status: false, + value: Default::default(), + }, + } + } +} + +/// Ethereum representation of Optional value with CrossAddress. +#[derive(Debug, Default, AbiCoder)] +pub struct OptionCrossAddress { + pub status: bool, + pub value: CrossAddress, +} + /// Cross account struct #[derive(Debug, Default, AbiCoder)] -pub struct EthCrossAccount { +pub struct CrossAddress { pub(crate) eth: address, pub(crate) sub: uint256, } -impl EthCrossAccount { - /// Converts `CrossAccountId` to `EthCrossAccountId` +impl CrossAddress { + /// Converts `CrossAccountId` to [`CrossAddress`] pub fn from_sub_cross_account(cross_account_id: &T::CrossAccountId) -> Self where T: pallet_evm::Config, @@ -87,7 +152,7 @@ } } } - /// Creates `EthCrossAccount` from substrate account + /// Creates [`CrossAddress`] from substrate account pub fn from_sub(account_id: &T::AccountId) -> Self where T: pallet_evm::Config, @@ -98,7 +163,7 @@ sub: uint256::from_big_endian(account_id.as_ref()), } } - /// Converts `EthCrossAccount` to `CrossAccountId` + /// Converts [`CrossAddress`] to `CrossAccountId` pub fn into_sub_cross_account(&self) -> evm_coder::execution::Result where T: pallet_evm::Config, @@ -116,10 +181,42 @@ } } -/// [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). +#[derive(Debug, Default, AbiCoder)] +pub struct Property { + key: evm_coder::types::string, + value: evm_coder::types::bytes, +} + +impl TryFrom for Property { + type Error = evm_coder::execution::Error; + + fn try_from(from: up_data_structs::Property) -> Result { + let key = evm_coder::types::string::from_utf8(from.key.into()) + .map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {}", e)))?; + let value = evm_coder::types::bytes(from.value.to_vec()); + Ok(Property { key, value }) + } +} + +impl TryInto for Property { + type Error = evm_coder::execution::Error; + + fn try_into(self) -> Result { + let key = >::from(self.key) + .try_into() + .map_err(|_| "key too large")?; + + let value = self.value.0.try_into().map_err(|_| "value too large")?; + + Ok(up_data_structs::Property { key, value }) + } +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. #[derive(Debug, Default, Clone, Copy, AbiCoder)] #[repr(u8)] -pub enum CollectionLimits { +pub enum CollectionLimitField { /// How many tokens can a user have on one account. #[default] AccountTokenOwnership, @@ -148,10 +245,91 @@ /// Is it possible to send tokens from this collection between users. TransferEnabled, } + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +#[derive(Debug, Default, AbiCoder)] +pub struct CollectionLimit { + field: CollectionLimitField, + value: OptionUint, +} + +impl CollectionLimit { + /// Create [`CollectionLimit`] from field and value. + pub fn new(field: CollectionLimitField, value: T) -> Self + where + OptionUint: From, + { + Self { + field, + value: value.into(), + } + } + /// Whether the field contains a value. + pub fn has_value(&self) -> bool { + self.value.status + } +} + +impl TryInto for CollectionLimit { + type Error = evm_coder::execution::Error; + + fn try_into(self) -> Result { + let value = self.value.value.try_into().map_err(|error| { + Self::Error::Revert(format!( + "can't convert value to u32 \"{}\" because: \"{error}\"", + self.value.value + )) + })?; + + let convert_value_to_bool = || match value { + 0 => Ok(false), + 1 => Ok(true), + _ => { + return Err(Self::Error::Revert(format!( + "can't convert value to boolean \"{value}\"" + ))) + } + }; + + let mut limits = up_data_structs::CollectionLimits::default(); + match self.field { + CollectionLimitField::AccountTokenOwnership => { + limits.account_token_ownership_limit = Some(value); + } + CollectionLimitField::SponsoredDataSize => { + limits.sponsored_data_size = Some(value); + } + CollectionLimitField::SponsoredDataRateLimit => { + limits.sponsored_data_rate_limit = + Some(up_data_structs::SponsoringRateLimit::Blocks(value)); + } + CollectionLimitField::TokenLimit => { + limits.token_limit = Some(value); + } + CollectionLimitField::SponsorTransferTimeout => { + limits.sponsor_transfer_timeout = Some(value); + } + CollectionLimitField::SponsorApproveTimeout => { + limits.sponsor_approve_timeout = Some(value); + } + CollectionLimitField::OwnerCanTransfer => { + limits.owner_can_transfer = Some(convert_value_to_bool()?); + } + CollectionLimitField::OwnerCanDestroy => { + limits.owner_can_destroy = Some(convert_value_to_bool()?); + } + CollectionLimitField::TransferEnabled => { + limits.transfers_enabled = Some(convert_value_to_bool()?); + } + }; + Ok(limits) + } +} + /// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. #[derive(Default, Debug, Clone, Copy, AbiCoder)] #[repr(u8)] -pub enum CollectionPermissions { +pub enum CollectionPermissionField { /// Owner of token can nest tokens under it. #[default] TokenOwner, @@ -163,7 +341,7 @@ /// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. #[derive(AbiCoder, Copy, Clone, Default, Debug)] #[repr(u8)] -pub enum EthTokenPermissions { +pub enum TokenPermissionField { /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] #[default] Mutable, @@ -174,3 +352,122 @@ /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] CollectionAdmin, } + +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +#[derive(Debug, Default, AbiCoder)] +pub struct PropertyPermission { + /// TokenPermission field. + code: TokenPermissionField, + /// TokenPermission value. + value: bool, +} + +impl PropertyPermission { + /// Make vector of [`PropertyPermission`] from [`up_data_structs::PropertyPermission`]. + pub fn into_vec(pp: up_data_structs::PropertyPermission) -> Vec { + vec![ + PropertyPermission { + code: TokenPermissionField::Mutable, + value: pp.mutable, + }, + PropertyPermission { + code: TokenPermissionField::TokenOwner, + value: pp.token_owner, + }, + PropertyPermission { + code: TokenPermissionField::CollectionAdmin, + value: pp.collection_admin, + }, + ] + } + + /// Make [`up_data_structs::PropertyPermission`] from vector of [`PropertyPermission`]. + pub fn from_vec(permission: Vec) -> up_data_structs::PropertyPermission { + let mut token_permission = up_data_structs::PropertyPermission::default(); + + for PropertyPermission { code, value } in permission { + match code { + TokenPermissionField::Mutable => token_permission.mutable = value, + TokenPermissionField::TokenOwner => token_permission.token_owner = value, + TokenPermissionField::CollectionAdmin => token_permission.collection_admin = value, + } + } + token_permission + } +} + +/// Ethereum representation of Token Property Permissions. +#[derive(Debug, Default, AbiCoder)] +pub struct TokenPropertyPermission { + /// Token property key. + key: evm_coder::types::string, + /// Token property permissions. + permissions: Vec, +} + +impl + From<( + up_data_structs::PropertyKey, + up_data_structs::PropertyPermission, + )> for TokenPropertyPermission +{ + fn from( + value: ( + up_data_structs::PropertyKey, + up_data_structs::PropertyPermission, + ), + ) -> Self { + let (key, permission) = value; + let key = evm_coder::types::string::from_utf8(key.into_inner()) + .expect("Stored key must be valid"); + let permissions = PropertyPermission::into_vec(permission); + Self { key, permissions } + } +} + +impl TokenPropertyPermission { + /// Convert vector of [`TokenPropertyPermission`] into vector of [`up_data_structs::PropertyKeyPermission`]. + pub fn into_property_key_permissions( + permissions: Vec, + ) -> evm_coder::execution::Result> { + let mut perms = Vec::new(); + + for TokenPropertyPermission { key, permissions } in permissions { + let token_permission = PropertyPermission::from_vec(permissions); + + perms.push(up_data_structs::PropertyKeyPermission { + key: key.into_bytes().try_into().map_err(|_| "too long key")?, + permission: token_permission, + }); + } + Ok(perms) + } +} + +/// Nested collections. +#[derive(Debug, Default, AbiCoder)] +pub struct CollectionNesting { + token_owner: bool, + ids: Vec, +} + +impl CollectionNesting { + /// Create [`CollectionNesting`]. + pub fn new(token_owner: bool, ids: Vec) -> Self { + Self { token_owner, ids } + } +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +#[derive(Debug, Default, AbiCoder)] +pub struct CollectionNestingPermission { + field: CollectionPermissionField, + value: bool, +} + +impl CollectionNestingPermission { + /// Create [`CollectionNestingPermission`]. + pub fn new(field: CollectionPermissionField, value: bool) -> Self { + Self { field, value } + } +} --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -23,9 +23,9 @@ execution::Result, generate_stubgen, solidity_interface, types::*, - ToLog, + ToLog, AbiCoder, }; -use pallet_common::eth::EthCrossAccount; +use pallet_common::eth; use pallet_evm::{ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, account::CrossAccountId, @@ -175,10 +175,17 @@ /// /// @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 sponsor(&self, contract_address: address) -> Result { - Ok(EthCrossAccount::from_sub_cross_account::( - &Pallet::::get_sponsor(contract_address).ok_or("Contract has no sponsor")?, - )) + fn sponsor(&self, contract_address: address) -> Result { + Ok(match Pallet::::get_sponsor(contract_address) { + Some(ref value) => eth::OptionCrossAddress { + status: true, + value: eth::CrossAddress::from_sub_cross_account::(value), + }, + None => eth::OptionCrossAddress { + status: false, + value: Default::default(), + }, + }) } /// Check tat contract has confirmed sponsor. @@ -208,14 +215,12 @@ &mut self, caller: caller, contract_address: address, - // TODO: implement support for enums in evm-coder - mode: uint8, + mode: SponsoringModeT, ) -> Result { self.recorder().consume_sload()?; self.recorder().consume_sstore()?; >::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::)?; - let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?; >::set_sponsoring_mode(contract_address, mode); Ok(()) --- a/pallets/evm-contract-helpers/src/lib.rs +++ b/pallets/evm-contract-helpers/src/lib.rs @@ -19,6 +19,7 @@ #![warn(missing_docs)] use codec::{Decode, Encode, MaxEncodedLen}; +use evm_coder::AbiCoder; pub use pallet::*; pub use eth::*; use scale_info::TypeInfo; @@ -407,7 +408,10 @@ } /// Available contract sponsoring modes -#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)] +#[derive( + Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy, +)] +#[repr(u8)] pub enum SponsoringModeT { /// Sponsoring is disabled #[default] @@ -416,23 +420,4 @@ Allowlisted, /// All users will be sponsored Generous, -} - -impl SponsoringModeT { - fn from_eth(v: u8) -> Option { - Some(match v { - 0 => Self::Disabled, - 1 => Self::Allowlisted, - 2 => Self::Generous, - _ => return None, - }) - } - #[allow(dead_code)] - fn to_eth(self) -> u8 { - match self { - SponsoringModeT::Disabled => 0, - SponsoringModeT::Allowlisted => 1, - SponsoringModeT::Generous => 2, - } - } } --- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol +++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol @@ -96,11 +96,11 @@ /// @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: 0x766c4f37, /// or in textual repr: sponsor(address) - function sponsor(address contractAddress) public view returns (EthCrossAccount memory) { + function sponsor(address contractAddress) public view returns (OptionCrossAddress memory) { require(false, stub_error); contractAddress; dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return OptionCrossAddress(false, CrossAddress(0x0000000000000000000000000000000000000000, 0)); } /// Check tat contract has confirmed sponsor. @@ -140,7 +140,7 @@ /// @dev EVM selector for this function is: 0xfde8a560, /// or in textual repr: setSponsoringMode(address,uint8) - function setSponsoringMode(address contractAddress, uint8 mode) public { + function setSponsoringMode(address contractAddress, SponsoringModeT mode) public { require(false, stub_error); contractAddress; mode; @@ -265,8 +265,24 @@ } } -/// @dev Cross account struct -struct EthCrossAccount { +/// Available contract sponsoring modes +enum SponsoringModeT { + /// Sponsoring is disabled + Disabled, + /// Only users from allowlist will be sponsored + Allowlisted, + /// All users will be sponsored + Generous +} + +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } + +/// Ethereum representation of Optional value with CrossAddress. +struct OptionCrossAddress { + bool status; + CrossAddress value; +} --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -27,7 +27,6 @@ use pallet_common::{ CollectionHandle, erc::{CommonEvmHandler, PrecompileResult, CollectionCall}, - eth::EthCrossAccount, }; use sp_std::vec::Vec; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; @@ -175,7 +174,12 @@ } #[weight(>::create_item())] - fn mint_cross(&mut self, caller: caller, to: EthCrossAccount, amount: uint256) -> Result { + fn mint_cross( + &mut self, + caller: caller, + to: pallet_common::eth::CrossAddress, + amount: uint256, + ) -> Result { let caller = T::CrossAccountId::from_eth(caller); let to = to.into_sub_cross_account::()?; let amount = amount.try_into().map_err(|_| "amount overflow")?; @@ -191,7 +195,7 @@ fn approve_cross( &mut self, caller: caller, - spender: EthCrossAccount, + spender: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -232,7 +236,7 @@ fn burn_from_cross( &mut self, caller: caller, - from: EthCrossAccount, + from: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -274,7 +278,7 @@ fn transfer_cross( &mut self, caller: caller, - to: EthCrossAccount, + to: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -292,8 +296,8 @@ fn transfer_from_cross( &mut self, caller: caller, - from: EthCrossAccount, - to: EthCrossAccount, + from: pallet_common::eth::CrossAddress, + to: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); --- a/pallets/fungible/src/stubs/UniqueFungible.sol +++ b/pallets/fungible/src/stubs/UniqueFungible.sol @@ -18,7 +18,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -114,7 +114,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) public { + function setCollectionSponsorCross(CrossAddress memory sponsor) public { require(false, stub_error); sponsor; dummy = 0; @@ -152,58 +152,31 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (EthCrossAccount memory) { + function collectionSponsor() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple23[] memory) { + function collectionLimits() public view returns (CollectionLimit[] memory) { require(false, stub_error); dummy; - return new Tuple23[](0); + return new CollectionLimit[](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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) public { + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) public { require(false, stub_error); limit; - status; - value; dummy = 0; } @@ -220,7 +193,7 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) public { + function addCollectionAdminCross(CrossAddress memory newAdmin) public { require(false, stub_error); newAdmin; dummy = 0; @@ -230,7 +203,7 @@ /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) public { + function removeCollectionAdminCross(CrossAddress memory admin) public { require(false, stub_error); admin; dummy = 0; @@ -284,19 +257,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple29 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (CollectionNesting memory) { require(false, stub_error); dummy; - return Tuple29(false, new uint256[](0)); + return CollectionNesting(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple32[] memory) { + function collectionNestingPermissions() public view returns (CollectionNestingPermission[] memory) { require(false, stub_error); dummy; - return new Tuple32[](0); + return new CollectionNestingPermission[](0); } /// Set the collection access method. @@ -316,7 +289,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) public view returns (bool) { + function allowlistedCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -339,7 +312,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) public { + function addToCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -361,7 +334,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) public { + function removeFromCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -397,7 +370,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) public view returns (bool) { + function isOwnerOrAdminCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -421,10 +394,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() public view returns (EthCrossAccount memory) { + function collectionOwner() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } // /// Changes collection owner to another account @@ -445,10 +418,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() public view returns (EthCrossAccount[] memory) { + function collectionAdmins() public view returns (CrossAddress[] memory) { require(false, stub_error); dummy; - return new EthCrossAccount[](0); + return new CrossAddress[](0); } /// Changes collection owner to another account @@ -457,66 +430,74 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) public { + function changeCollectionOwnerCross(CrossAddress memory newOwner) public { require(false, stub_error); newOwner; dummy = 0; } } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; } -/// @dev anonymous struct -struct Tuple32 { - CollectionPermissions field_0; - bool field_1; +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin } -/// @dev anonymous struct -struct Tuple29 { - bool field_0; - uint256[] field_1; +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; +} + +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled } -/// @dev anonymous struct -struct Tuple23 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; -} - -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; @@ -535,7 +516,7 @@ /// @dev EVM selector for this function is: 0x269e6158, /// or in textual repr: mintCross((address,uint256),uint256) - function mintCross(EthCrossAccount memory to, uint256 amount) public returns (bool) { + function mintCross(CrossAddress memory to, uint256 amount) public returns (bool) { require(false, stub_error); to; amount; @@ -545,7 +526,7 @@ /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory spender, uint256 amount) public returns (bool) { + function approveCross(CrossAddress memory spender, uint256 amount) public returns (bool) { require(false, stub_error); spender; amount; @@ -575,7 +556,7 @@ /// @param amount The amount that will be burnt. /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 amount) public returns (bool) { + function burnFromCross(CrossAddress memory from, uint256 amount) public returns (bool) { require(false, stub_error); from; amount; @@ -596,7 +577,7 @@ /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 amount) public returns (bool) { + function transferCross(CrossAddress memory to, uint256 amount) public returns (bool) { require(false, stub_error); to; amount; @@ -607,8 +588,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 amount ) public returns (bool) { require(false, stub_error); --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -26,7 +26,7 @@ }; use evm_coder::{ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, - types::Property as PropertyStruct, weight, + weight, }; use frame_support::BoundedVec; use up_data_structs::{ @@ -38,7 +38,7 @@ use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key}, - eth::{EthCrossAccount, EthTokenPermissions}, + eth, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; use pallet_evm_coder_substrate::call; @@ -94,63 +94,21 @@ fn set_token_property_permissions( &mut self, caller: caller, - permissions: Vec<(string, Vec<(EthTokenPermissions, bool)>)>, + permissions: Vec, ) -> Result<()> { let caller = T::CrossAccountId::from_eth(caller); - const PERMISSIONS_FIELDS_COUNT: usize = 3; - - let mut perms = Vec::new(); - - for (key, pp) in permissions { - if pp.len() > PERMISSIONS_FIELDS_COUNT { - return Err(alloc::format!( - "Actual number of fields {} for {}, which exceeds the maximum value of {}", - pp.len(), - stringify!(EthTokenPermissions), - PERMISSIONS_FIELDS_COUNT - ) - .as_str() - .into()); - } - - let mut token_permission = PropertyPermission::default(); - - for (perm, value) in pp { - match perm { - EthTokenPermissions::Mutable => token_permission.mutable = value, - EthTokenPermissions::TokenOwner => token_permission.token_owner = value, - EthTokenPermissions::CollectionAdmin => { - token_permission.collection_admin = value - } - } - } - - perms.push(PropertyKeyPermission { - key: key.into_bytes().try_into().map_err(|_| "too long key")?, - permission: token_permission, - }); - } + let perms = eth::TokenPropertyPermission::into_property_key_permissions(permissions)?; >::set_token_property_permissions(self, &caller, perms) .map_err(dispatch_to_evm::) } /// @notice Get permissions for token properties. - fn token_property_permissions( - &self, - ) -> Result)>> { + fn token_property_permissions(&self) -> Result> { let perms = >::token_property_permission(self.id); Ok(perms .into_iter() - .map(|(key, pp)| { - let key = string::from_utf8(key.into_inner()).expect("Stored key must be valid"); - let pp = vec![ - (EthTokenPermissions::Mutable, pp.mutable), - (EthTokenPermissions::TokenOwner, pp.token_owner), - (EthTokenPermissions::CollectionAdmin, pp.collection_admin), - ]; - (key, pp) - }) + .map(eth::TokenPropertyPermission::from) .collect()) } @@ -198,7 +156,7 @@ &mut self, caller: caller, token_id: uint256, - properties: Vec, + properties: Vec, ) -> Result<()> { let caller = T::CrossAccountId::from_eth(caller); let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; @@ -209,15 +167,7 @@ let properties = properties .into_iter() - .map(|PropertyStruct { key, value }| { - let key = >::from(key) - .try_into() - .map_err(|_| "key too large")?; - - let value = value.0.try_into().map_err(|_| "value too large")?; - - Ok(Property { key, value }) - }) + .map(eth::Property::try_into) .collect::>>()?; >::set_token_properties( @@ -800,9 +750,9 @@ /// Returns the owner (in cross format) of the token. /// /// @param tokenId Id for the token. - fn cross_owner_of(&self, token_id: uint256) -> Result { + fn cross_owner_of(&self, token_id: uint256) -> Result { Self::token_owner(&self, token_id.try_into()?) - .map(|o| EthCrossAccount::from_sub_cross_account::(&o)) + .map(|o| eth::CrossAddress::from_sub_cross_account::(&o)) .ok_or(Error::Revert("key too large".into())) } @@ -811,7 +761,7 @@ /// @param tokenId Id for the token. /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. - fn properties(&self, token_id: uint256, keys: Vec) -> Result> { + fn properties(&self, token_id: uint256, keys: Vec) -> Result> { let keys = keys .into_iter() .map(|key| { @@ -827,12 +777,7 @@ if keys.is_empty() { None } else { Some(keys) }, ) .into_iter() - .map(|p| { - let key = string::from_utf8(p.key.to_vec()) - .map_err(|e| Error::Revert(alloc::format!("{}", e)))?; - let value = bytes(p.value.to_vec()); - Ok(PropertyStruct { key, value }) - }) + .map(eth::Property::try_from) .collect::>>() } @@ -846,7 +791,7 @@ fn approve_cross( &mut self, caller: caller, - approved: EthCrossAccount, + approved: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -885,7 +830,7 @@ fn transfer_cross( &mut self, caller: caller, - to: EthCrossAccount, + to: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -909,8 +854,8 @@ fn transfer_from_cross( &mut self, caller: caller, - from: EthCrossAccount, - to: EthCrossAccount, + from: eth::CrossAddress, + to: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -956,7 +901,7 @@ fn burn_from_cross( &mut self, caller: caller, - from: EthCrossAccount, + from: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -1078,8 +1023,8 @@ fn mint_cross( &mut self, caller: caller, - to: EthCrossAccount, - properties: Vec, + to: eth::CrossAddress, + properties: Vec, ) -> Result { let token_id = >::get(self.id) .checked_add(1) @@ -1089,15 +1034,7 @@ let properties = properties .into_iter() - .map(|PropertyStruct { key, value }| { - let key = >::from(key) - .try_into() - .map_err(|_| "key too large")?; - - let value = value.0.try_into().map_err(|_| "value too large")?; - - Ok(Property { key, value }) - }) + .map(eth::Property::try_into) .collect::>>()? .try_into() .map_err(|_| Error::Revert(alloc::format!("too many properties")))?; --- a/pallets/nonfungible/src/stubs/UniqueNFT.sol +++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol @@ -42,7 +42,7 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple61[] memory permissions) public { + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) public { require(false, stub_error); permissions; dummy = 0; @@ -51,10 +51,10 @@ /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() public view returns (Tuple61[] memory) { + function tokenPropertyPermissions() public view returns (TokenPropertyPermission[] memory) { require(false, stub_error); dummy; - return new Tuple61[](0); + return new TokenPropertyPermission[](0); } // /// @notice Set token property value. @@ -127,36 +127,40 @@ } } -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; } -/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum EthTokenPermissions { - /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; } -/// @dev anonymous struct -struct Tuple61 { - string field_0; - Tuple59[] field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; } -/// @dev anonymous struct -struct Tuple59 { - EthTokenPermissions field_0; - bool field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -252,7 +256,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) public { + function setCollectionSponsorCross(CrossAddress memory sponsor) public { require(false, stub_error); sponsor; dummy = 0; @@ -290,58 +294,31 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (EthCrossAccount memory) { + function collectionSponsor() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple35[] memory) { + function collectionLimits() public view returns (CollectionLimit[] memory) { require(false, stub_error); dummy; - return new Tuple35[](0); + return new CollectionLimit[](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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) public { + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) public { require(false, stub_error); limit; - status; - value; dummy = 0; } @@ -358,7 +335,7 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) public { + function addCollectionAdminCross(CrossAddress memory newAdmin) public { require(false, stub_error); newAdmin; dummy = 0; @@ -368,7 +345,7 @@ /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) public { + function removeCollectionAdminCross(CrossAddress memory admin) public { require(false, stub_error); admin; dummy = 0; @@ -422,19 +399,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple41 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (CollectionNesting memory) { require(false, stub_error); dummy; - return Tuple41(false, new uint256[](0)); + return CollectionNesting(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple44[] memory) { + function collectionNestingPermissions() public view returns (CollectionNestingPermission[] memory) { require(false, stub_error); dummy; - return new Tuple44[](0); + return new CollectionNestingPermission[](0); } /// Set the collection access method. @@ -454,7 +431,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) public view returns (bool) { + function allowlistedCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -477,7 +454,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) public { + function addToCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -499,7 +476,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) public { + function removeFromCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -535,7 +512,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) public view returns (bool) { + function isOwnerOrAdminCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -559,10 +536,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() public view returns (EthCrossAccount memory) { + function collectionOwner() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } // /// Changes collection owner to another account @@ -583,10 +560,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() public view returns (EthCrossAccount[] memory) { + function collectionAdmins() public view returns (CrossAddress[] memory) { require(false, stub_error); dummy; - return new EthCrossAccount[](0); + return new CrossAddress[](0); } /// Changes collection owner to another account @@ -595,65 +572,73 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) public { + function changeCollectionOwnerCross(CrossAddress memory newOwner) public { require(false, stub_error); newOwner; dummy = 0; } } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; +} + +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin +} + +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; } -/// @dev anonymous struct -struct Tuple44 { - CollectionPermissions field_0; - bool field_1; +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; } -/// @dev anonymous struct -struct Tuple41 { - bool field_0; - uint256[] field_1; +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled } -/// @dev anonymous struct -struct Tuple35 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; -} - /// @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 @@ -832,11 +817,11 @@ /// @param tokenId Id for the token. /// @dev EVM selector for this function is: 0x2b29dace, /// or in textual repr: crossOwnerOf(uint256) - function crossOwnerOf(uint256 tokenId) public view returns (EthCrossAccount memory) { + function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) { require(false, stub_error); tokenId; dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } /// Returns the token properties. @@ -862,7 +847,7 @@ /// @param tokenId The NFT to approve /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory approved, uint256 tokenId) public { + function approveCross(CrossAddress memory approved, uint256 tokenId) public { require(false, stub_error); approved; tokenId; @@ -890,7 +875,7 @@ /// @param tokenId The NFT to transfer /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 tokenId) public { + function transferCross(CrossAddress memory to, uint256 tokenId) public { require(false, stub_error); to; tokenId; @@ -906,8 +891,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 tokenId ) public { require(false, stub_error); @@ -940,7 +925,7 @@ /// @param tokenId The NFT to transfer /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 tokenId) public { + function burnFromCross(CrossAddress memory from, uint256 tokenId) public { require(false, stub_error); from; tokenId; @@ -992,7 +977,7 @@ /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0xb904db03, /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) { + function mintCross(CrossAddress memory to, Property[] memory properties) public returns (uint256) { require(false, stub_error); to; properties; --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -27,14 +27,14 @@ }; use evm_coder::{ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, - types::Property as PropertyStruct, weight, + weight, }; use frame_support::{BoundedBTreeMap, BoundedVec}; use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, - erc::{CommonEvmHandler, CollectionCall, static_property::key}, - eth::{EthCrossAccount, EthTokenPermissions}, Error as CommonError, + erc::{CommonEvmHandler, CollectionCall, static_property::key}, + eth, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; use pallet_evm_coder_substrate::{call, dispatch_to_evm}; @@ -97,67 +97,21 @@ fn set_token_property_permissions( &mut self, caller: caller, - permissions: Vec<(string, Vec<(EthTokenPermissions, bool)>)>, + permissions: Vec, ) -> Result<()> { let caller = T::CrossAccountId::from_eth(caller); - const PERMISSIONS_FIELDS_COUNT: usize = 3; - - let mut perms = Vec::new(); - - for (key, pp) in permissions { - if pp.len() > PERMISSIONS_FIELDS_COUNT { - return Err(alloc::format!( - "Actual number of fields {} for {}, which exceeds the maximum value of {}", - pp.len(), - stringify!(EthTokenPermissions), - PERMISSIONS_FIELDS_COUNT - ) - .as_str() - .into()); - } - - let mut token_permission = PropertyPermission { - mutable: false, - collection_admin: false, - token_owner: false, - }; - - for (perm, value) in pp { - match perm { - EthTokenPermissions::Mutable => token_permission.mutable = value, - EthTokenPermissions::TokenOwner => token_permission.token_owner = value, - EthTokenPermissions::CollectionAdmin => { - token_permission.collection_admin = value - } - } - } + let perms = eth::TokenPropertyPermission::into_property_key_permissions(permissions)?; - perms.push(PropertyKeyPermission { - key: key.into_bytes().try_into().map_err(|_| "too long key")?, - permission: token_permission, - }); - } - >::set_token_property_permissions(self, &caller, perms) .map_err(dispatch_to_evm::) } /// @notice Get permissions for token properties. - fn token_property_permissions( - &self, - ) -> Result)>> { + fn token_property_permissions(&self) -> Result> { let perms = >::token_property_permission(self.id); Ok(perms .into_iter() - .map(|(key, pp)| { - let key = string::from_utf8(key.into_inner()).expect("Stored key must be valid"); - let pp = vec![ - (EthTokenPermissions::Mutable, pp.mutable), - (EthTokenPermissions::TokenOwner, pp.token_owner), - (EthTokenPermissions::CollectionAdmin, pp.collection_admin), - ]; - (key, pp) - }) + .map(eth::TokenPropertyPermission::from) .collect()) } @@ -205,7 +159,7 @@ &mut self, caller: caller, token_id: uint256, - properties: Vec, + properties: Vec, ) -> Result<()> { let caller = T::CrossAccountId::from_eth(caller); let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; @@ -216,15 +170,7 @@ let properties = properties .into_iter() - .map(|PropertyStruct { key, value }| { - let key = >::from(key) - .try_into() - .map_err(|_| "key too large")?; - - let value = value.0.try_into().map_err(|_| "value too large")?; - - Ok(Property { key, value }) - }) + .map(eth::Property::try_into) .collect::>>()?; >::set_token_properties( @@ -839,9 +785,9 @@ /// Returns the owner (in cross format) of the token. /// /// @param tokenId Id for the token. - fn cross_owner_of(&self, token_id: uint256) -> Result { + fn cross_owner_of(&self, token_id: uint256) -> Result { Self::token_owner(&self, token_id.try_into()?) - .map(|o| EthCrossAccount::from_sub_cross_account::(&o)) + .map(|o| eth::CrossAddress::from_sub_cross_account::(&o)) .ok_or(Error::Revert("key too large".into())) } @@ -850,7 +796,7 @@ /// @param tokenId Id for the token. /// @param keys Properties keys. Empty keys for all propertyes. /// @return Vector of properties key/value pairs. - fn properties(&self, token_id: uint256, keys: Vec) -> Result> { + fn properties(&self, token_id: uint256, keys: Vec) -> Result> { let keys = keys .into_iter() .map(|key| { @@ -866,12 +812,7 @@ if keys.is_empty() { None } else { Some(keys) }, ) .into_iter() - .map(|p| { - let key = string::from_utf8(p.key.to_vec()) - .map_err(|e| Error::Revert(alloc::format!("{}", e)))?; - let value = bytes(p.value.to_vec()); - Ok(PropertyStruct { key, value }) - }) + .map(eth::Property::try_from) .collect::>>() } /// @notice Transfer ownership of an RFT @@ -907,7 +848,7 @@ fn transfer_cross( &mut self, caller: caller, - to: EthCrossAccount, + to: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -935,8 +876,8 @@ fn transfer_from_cross( &mut self, caller: caller, - from: EthCrossAccount, - to: EthCrossAccount, + from: eth::CrossAddress, + to: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -991,7 +932,7 @@ fn burn_from_cross( &mut self, caller: caller, - from: EthCrossAccount, + from: eth::CrossAddress, token_id: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -1128,8 +1069,8 @@ fn mint_cross( &mut self, caller: caller, - to: EthCrossAccount, - properties: Vec, + to: eth::CrossAddress, + properties: Vec, ) -> Result { let token_id = >::get(self.id) .checked_add(1) @@ -1139,15 +1080,7 @@ let properties = properties .into_iter() - .map(|PropertyStruct { key, value }| { - let key = >::from(key) - .try_into() - .map_err(|_| "key too large")?; - - let value = value.0.try_into().map_err(|_| "value too large")?; - - Ok(Property { key, value }) - }) + .map(eth::Property::try_into) .collect::>>()? .try_into() .map_err(|_| Error::Revert(alloc::format!("too many properties")))?; --- a/pallets/refungible/src/erc_token.rs +++ b/pallets/refungible/src/erc_token.rs @@ -30,7 +30,7 @@ use pallet_common::{ CommonWeightInfo, erc::{CommonEvmHandler, PrecompileResult}, - eth::{collection_id_to_address, EthCrossAccount}, + eth::collection_id_to_address, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder}; @@ -224,7 +224,7 @@ fn burn_from_cross( &mut self, caller: caller, - from: EthCrossAccount, + from: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -250,7 +250,7 @@ fn approve_cross( &mut self, caller: caller, - spender: EthCrossAccount, + spender: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -280,7 +280,7 @@ fn transfer_cross( &mut self, caller: caller, - to: EthCrossAccount, + to: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); @@ -303,8 +303,8 @@ fn transfer_from_cross( &mut self, caller: caller, - from: EthCrossAccount, - to: EthCrossAccount, + from: pallet_common::eth::CrossAddress, + to: pallet_common::eth::CrossAddress, amount: uint256, ) -> Result { let caller = T::CrossAccountId::from_eth(caller); --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -92,12 +92,8 @@ use codec::{Encode, Decode, MaxEncodedLen}; use core::ops::Deref; -use derivative::Derivative; use evm_coder::ToLog; -use frame_support::{ - BoundedBTreeMap, BoundedVec, ensure, fail, storage::with_transaction, transactional, - pallet_prelude::ConstU32, -}; +use frame_support::{BoundedVec, ensure, fail, storage::with_transaction, transactional}; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_evm_coder_substrate::WithRecorder; use pallet_common::{ @@ -110,11 +106,10 @@ use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome}; use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap}; use up_data_structs::{ - AccessMode, budget::Budget, CollectionId, CollectionFlags, CollectionPropertiesVec, - CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH, - MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission, - PropertyScope, PropertyValue, TokenId, TrySetProperty, PropertiesPermissionMap, - CreateRefungibleExMultipleOwners, + AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData, + CustomDataLimit, mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey, + PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId, + TrySetProperty, PropertiesPermissionMap, CreateRefungibleExMultipleOwners, }; pub use pallet::*; --- a/pallets/refungible/src/stubs/UniqueRefungible.sol +++ b/pallets/refungible/src/stubs/UniqueRefungible.sol @@ -42,7 +42,7 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple60[] memory permissions) public { + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) public { require(false, stub_error); permissions; dummy = 0; @@ -51,10 +51,10 @@ /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() public view returns (Tuple60[] memory) { + function tokenPropertyPermissions() public view returns (TokenPropertyPermission[] memory) { require(false, stub_error); dummy; - return new Tuple60[](0); + return new TokenPropertyPermission[](0); } // /// @notice Set token property value. @@ -127,36 +127,40 @@ } } -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; } -/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum EthTokenPermissions { - /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; } -/// @dev anonymous struct -struct Tuple60 { - string field_0; - Tuple58[] field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; } -/// @dev anonymous struct -struct Tuple58 { - EthTokenPermissions field_0; - bool field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 contract Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -252,7 +256,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) public { + function setCollectionSponsorCross(CrossAddress memory sponsor) public { require(false, stub_error); sponsor; dummy = 0; @@ -290,58 +294,31 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() public view returns (EthCrossAccount memory) { + function collectionSponsor() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() public view returns (Tuple34[] memory) { + function collectionLimits() public view returns (CollectionLimit[] memory) { require(false, stub_error); dummy; - return new Tuple34[](0); + return new CollectionLimit[](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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) public { + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) public { require(false, stub_error); limit; - status; - value; dummy = 0; } @@ -358,7 +335,7 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) public { + function addCollectionAdminCross(CrossAddress memory newAdmin) public { require(false, stub_error); newAdmin; dummy = 0; @@ -368,7 +345,7 @@ /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) public { + function removeCollectionAdminCross(CrossAddress memory admin) public { require(false, stub_error); admin; dummy = 0; @@ -422,19 +399,19 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() public view returns (Tuple40 memory) { + function collectionNestingRestrictedCollectionIds() public view returns (CollectionNesting memory) { require(false, stub_error); dummy; - return Tuple40(false, new uint256[](0)); + return CollectionNesting(false, new uint256[](0)); } /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() public view returns (Tuple43[] memory) { + function collectionNestingPermissions() public view returns (CollectionNestingPermission[] memory) { require(false, stub_error); dummy; - return new Tuple43[](0); + return new CollectionNestingPermission[](0); } /// Set the collection access method. @@ -454,7 +431,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) public view returns (bool) { + function allowlistedCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -477,7 +454,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) public { + function addToCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -499,7 +476,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) public { + function removeFromCollectionAllowListCross(CrossAddress memory user) public { require(false, stub_error); user; dummy = 0; @@ -535,7 +512,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) public view returns (bool) { + function isOwnerOrAdminCross(CrossAddress memory user) public view returns (bool) { require(false, stub_error); user; dummy; @@ -559,10 +536,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() public view returns (EthCrossAccount memory) { + function collectionOwner() public view returns (CrossAddress memory) { require(false, stub_error); dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } // /// Changes collection owner to another account @@ -583,10 +560,10 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() public view returns (EthCrossAccount[] memory) { + function collectionAdmins() public view returns (CrossAddress[] memory) { require(false, stub_error); dummy; - return new EthCrossAccount[](0); + return new CrossAddress[](0); } /// Changes collection owner to another account @@ -595,65 +572,73 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) public { + function changeCollectionOwnerCross(CrossAddress memory newOwner) public { require(false, stub_error); newOwner; dummy = 0; } } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; } -/// @dev anonymous struct -struct Tuple43 { - CollectionPermissions field_0; - bool field_1; +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin } -/// @dev anonymous struct -struct Tuple40 { - bool field_0; - uint256[] field_1; +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; +} + +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled } -/// @dev anonymous struct -struct Tuple34 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; -} - /// @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 @@ -830,11 +815,11 @@ /// @param tokenId Id for the token. /// @dev EVM selector for this function is: 0x2b29dace, /// or in textual repr: crossOwnerOf(uint256) - function crossOwnerOf(uint256 tokenId) public view returns (EthCrossAccount memory) { + function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) { require(false, stub_error); tokenId; dummy; - return EthCrossAccount(0x0000000000000000000000000000000000000000, 0); + return CrossAddress(0x0000000000000000000000000000000000000000, 0); } /// Returns the token properties. @@ -875,7 +860,7 @@ /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 tokenId) public { + function transferCross(CrossAddress memory to, uint256 tokenId) public { require(false, stub_error); to; tokenId; @@ -891,8 +876,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 tokenId ) public { require(false, stub_error); @@ -927,7 +912,7 @@ /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 tokenId) public { + function burnFromCross(CrossAddress memory from, uint256 tokenId) public { require(false, stub_error); from; tokenId; @@ -979,7 +964,7 @@ /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0xb904db03, /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(EthCrossAccount memory to, Property[] memory properties) public returns (uint256) { + function mintCross(CrossAddress memory to, Property[] memory properties) public returns (uint256) { require(false, stub_error); to; properties; --- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol +++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol @@ -58,7 +58,7 @@ /// @param amount The amount that will be burnt. /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 amount) public returns (bool) { + function burnFromCross(CrossAddress memory from, uint256 amount) public returns (bool) { require(false, stub_error); from; amount; @@ -75,7 +75,7 @@ /// @param amount The amount of tokens to be spent. /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory spender, uint256 amount) public returns (bool) { + function approveCross(CrossAddress memory spender, uint256 amount) public returns (bool) { require(false, stub_error); spender; amount; @@ -100,7 +100,7 @@ /// @param amount The amount to be transferred. /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 amount) public returns (bool) { + function transferCross(CrossAddress memory to, uint256 amount) public returns (bool) { require(false, stub_error); to; amount; @@ -115,8 +115,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 amount ) public returns (bool) { require(false, stub_error); @@ -128,8 +128,8 @@ } } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -74,7 +74,7 @@ extern crate alloc; use frame_support::{ - decl_module, decl_storage, decl_error, decl_event, + decl_module, decl_storage, decl_error, dispatch::DispatchResult, ensure, fail, weights::{Weight}, --- a/tests/src/eth/abi/contractHelpers.json +++ b/tests/src/eth/abi/contractHelpers.json @@ -190,7 +190,11 @@ "name": "contractAddress", "type": "address" }, - { "internalType": "uint8", "name": "mode", "type": "uint8" } + { + "internalType": "enum SponsoringModeT", + "name": "mode", + "type": "uint8" + } ], "name": "setSponsoringMode", "outputs": [], @@ -223,10 +227,18 @@ "outputs": [ { "components": [ - { "internalType": "address", "name": "eth", "type": "address" }, - { "internalType": "uint256", "name": "sub", "type": "uint256" } + { "internalType": "bool", "name": "status", "type": "bool" }, + { + "components": [ + { "internalType": "address", "name": "eth", "type": "address" }, + { "internalType": "uint256", "name": "sub", "type": "uint256" } + ], + "internalType": "struct CrossAddress", + "name": "value", + "type": "tuple" + } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct OptionCrossAddress", "name": "", "type": "tuple" } --- a/tests/src/eth/abi/fungible.json +++ b/tests/src/eth/abi/fungible.json @@ -56,7 +56,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newAdmin", "type": "tuple" } @@ -73,7 +73,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -100,7 +100,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -127,7 +127,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "spender", "type": "tuple" }, @@ -154,7 +154,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -172,7 +172,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newOwner", "type": "tuple" } @@ -191,7 +191,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount[]", + "internalType": "struct CrossAddress[]", "name": "", "type": "tuple[]" } @@ -213,14 +213,21 @@ { "components": [ { - "internalType": "enum CollectionLimits", - "name": "field_0", + "internalType": "enum CollectionLimitField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" }, - { "internalType": "uint256", "name": "field_2", "type": "uint256" } + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } ], - "internalType": "struct Tuple23[]", + "internalType": "struct CollectionLimit[]", "name": "", "type": "tuple[]" } @@ -235,13 +242,13 @@ { "components": [ { - "internalType": "enum CollectionPermissions", - "name": "field_0", + "internalType": "enum CollectionPermissionField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple32[]", + "internalType": "struct CollectionNestingPermission[]", "name": "", "type": "tuple[]" } @@ -255,14 +262,10 @@ "outputs": [ { "components": [ - { "internalType": "bool", "name": "field_0", "type": "bool" }, - { - "internalType": "uint256[]", - "name": "field_1", - "type": "uint256[]" - } + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } ], - "internalType": "struct Tuple29", + "internalType": "struct CollectionNesting", "name": "", "type": "tuple" } @@ -279,7 +282,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -322,7 +325,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -381,7 +384,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -425,7 +428,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -450,7 +453,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "admin", "type": "tuple" } @@ -474,7 +477,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -494,12 +497,26 @@ { "inputs": [ { - "internalType": "enum CollectionLimits", + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", "name": "limit", - "type": "uint8" - }, - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } + "type": "tuple" + } ], "name": "setCollectionLimit", "outputs": [], @@ -558,7 +575,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "sponsor", "type": "tuple" } @@ -608,7 +625,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -637,7 +654,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -646,7 +663,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, --- a/tests/src/eth/abi/nonFungible.json +++ b/tests/src/eth/abi/nonFungible.json @@ -87,7 +87,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newAdmin", "type": "tuple" } @@ -104,7 +104,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -121,7 +121,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -148,7 +148,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "approved", "type": "tuple" }, @@ -184,7 +184,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -202,7 +202,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newOwner", "type": "tuple" } @@ -221,7 +221,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount[]", + "internalType": "struct CrossAddress[]", "name": "", "type": "tuple[]" } @@ -243,14 +243,21 @@ { "components": [ { - "internalType": "enum CollectionLimits", - "name": "field_0", + "internalType": "enum CollectionLimitField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" }, - { "internalType": "uint256", "name": "field_2", "type": "uint256" } + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } ], - "internalType": "struct Tuple35[]", + "internalType": "struct CollectionLimit[]", "name": "", "type": "tuple[]" } @@ -265,13 +272,13 @@ { "components": [ { - "internalType": "enum CollectionPermissions", - "name": "field_0", + "internalType": "enum CollectionPermissionField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple44[]", + "internalType": "struct CollectionNestingPermission[]", "name": "", "type": "tuple[]" } @@ -285,14 +292,10 @@ "outputs": [ { "components": [ - { "internalType": "bool", "name": "field_0", "type": "bool" }, - { - "internalType": "uint256[]", - "name": "field_1", - "type": "uint256[]" - } + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } ], - "internalType": "struct Tuple41", + "internalType": "struct CollectionNesting", "name": "", "type": "tuple" } @@ -309,7 +312,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -352,7 +355,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -385,7 +388,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -459,7 +462,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -483,7 +486,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -579,7 +582,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "admin", "type": "tuple" } @@ -603,7 +606,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -656,12 +659,26 @@ { "inputs": [ { - "internalType": "enum CollectionLimits", + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", "name": "limit", - "type": "uint8" - }, - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } + "type": "tuple" + } ], "name": "setCollectionLimit", "outputs": [], @@ -720,7 +737,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "sponsor", "type": "tuple" } @@ -752,22 +769,22 @@ "inputs": [ { "components": [ - { "internalType": "string", "name": "field_0", "type": "string" }, + { "internalType": "string", "name": "key", "type": "string" }, { "components": [ { - "internalType": "enum EthTokenPermissions", - "name": "field_0", + "internalType": "enum TokenPermissionField", + "name": "code", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple59[]", - "name": "field_1", + "internalType": "struct PropertyPermission[]", + "name": "permissions", "type": "tuple[]" } ], - "internalType": "struct Tuple61[]", + "internalType": "struct TokenPropertyPermission[]", "name": "permissions", "type": "tuple[]" } @@ -818,22 +835,22 @@ "outputs": [ { "components": [ - { "internalType": "string", "name": "field_0", "type": "string" }, + { "internalType": "string", "name": "key", "type": "string" }, { "components": [ { - "internalType": "enum EthTokenPermissions", - "name": "field_0", + "internalType": "enum TokenPermissionField", + "name": "code", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple59[]", - "name": "field_1", + "internalType": "struct PropertyPermission[]", + "name": "permissions", "type": "tuple[]" } ], - "internalType": "struct Tuple61[]", + "internalType": "struct TokenPropertyPermission[]", "name": "", "type": "tuple[]" } @@ -874,7 +891,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -903,7 +920,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -912,7 +929,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, --- a/tests/src/eth/abi/reFungible.json +++ b/tests/src/eth/abi/reFungible.json @@ -87,7 +87,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newAdmin", "type": "tuple" } @@ -104,7 +104,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -121,7 +121,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -166,7 +166,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -184,7 +184,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "newOwner", "type": "tuple" } @@ -203,7 +203,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount[]", + "internalType": "struct CrossAddress[]", "name": "", "type": "tuple[]" } @@ -225,14 +225,21 @@ { "components": [ { - "internalType": "enum CollectionLimits", - "name": "field_0", + "internalType": "enum CollectionLimitField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" }, - { "internalType": "uint256", "name": "field_2", "type": "uint256" } + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } ], - "internalType": "struct Tuple34[]", + "internalType": "struct CollectionLimit[]", "name": "", "type": "tuple[]" } @@ -247,13 +254,13 @@ { "components": [ { - "internalType": "enum CollectionPermissions", - "name": "field_0", + "internalType": "enum CollectionPermissionField", + "name": "field", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple43[]", + "internalType": "struct CollectionNestingPermission[]", "name": "", "type": "tuple[]" } @@ -267,14 +274,10 @@ "outputs": [ { "components": [ - { "internalType": "bool", "name": "field_0", "type": "bool" }, - { - "internalType": "uint256[]", - "name": "field_1", - "type": "uint256[]" - } + { "internalType": "bool", "name": "token_owner", "type": "bool" }, + { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" } ], - "internalType": "struct Tuple40", + "internalType": "struct CollectionNesting", "name": "", "type": "tuple" } @@ -291,7 +294,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -334,7 +337,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -367,7 +370,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "", "type": "tuple" } @@ -441,7 +444,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -465,7 +468,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -561,7 +564,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "admin", "type": "tuple" } @@ -585,7 +588,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "user", "type": "tuple" } @@ -638,12 +641,26 @@ { "inputs": [ { - "internalType": "enum CollectionLimits", + "components": [ + { + "internalType": "enum CollectionLimitField", + "name": "field", + "type": "uint8" + }, + { + "components": [ + { "internalType": "bool", "name": "status", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "internalType": "struct OptionUint", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct CollectionLimit", "name": "limit", - "type": "uint8" - }, - { "internalType": "bool", "name": "status", "type": "bool" }, - { "internalType": "uint256", "name": "value", "type": "uint256" } + "type": "tuple" + } ], "name": "setCollectionLimit", "outputs": [], @@ -702,7 +719,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "sponsor", "type": "tuple" } @@ -734,22 +751,22 @@ "inputs": [ { "components": [ - { "internalType": "string", "name": "field_0", "type": "string" }, + { "internalType": "string", "name": "key", "type": "string" }, { "components": [ { - "internalType": "enum EthTokenPermissions", - "name": "field_0", + "internalType": "enum TokenPermissionField", + "name": "code", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple58[]", - "name": "field_1", + "internalType": "struct PropertyPermission[]", + "name": "permissions", "type": "tuple[]" } ], - "internalType": "struct Tuple60[]", + "internalType": "struct TokenPropertyPermission[]", "name": "permissions", "type": "tuple[]" } @@ -809,22 +826,22 @@ "outputs": [ { "components": [ - { "internalType": "string", "name": "field_0", "type": "string" }, + { "internalType": "string", "name": "key", "type": "string" }, { "components": [ { - "internalType": "enum EthTokenPermissions", - "name": "field_0", + "internalType": "enum TokenPermissionField", + "name": "code", "type": "uint8" }, - { "internalType": "bool", "name": "field_1", "type": "bool" } + { "internalType": "bool", "name": "value", "type": "bool" } ], - "internalType": "struct Tuple58[]", - "name": "field_1", + "internalType": "struct PropertyPermission[]", + "name": "permissions", "type": "tuple[]" } ], - "internalType": "struct Tuple60[]", + "internalType": "struct TokenPropertyPermission[]", "name": "", "type": "tuple[]" } @@ -865,7 +882,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -894,7 +911,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -903,7 +920,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, --- a/tests/src/eth/abi/reFungibleToken.json +++ b/tests/src/eth/abi/reFungibleToken.json @@ -76,7 +76,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "spender", "type": "tuple" }, @@ -113,7 +113,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -201,7 +201,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, @@ -230,7 +230,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "from", "type": "tuple" }, @@ -239,7 +239,7 @@ { "internalType": "address", "name": "eth", "type": "address" }, { "internalType": "uint256", "name": "sub", "type": "uint256" } ], - "internalType": "struct EthCrossAccount", + "internalType": "struct CrossAddress", "name": "to", "type": "tuple" }, --- a/tests/src/eth/api/ContractHelpers.sol +++ b/tests/src/eth/api/ContractHelpers.sol @@ -69,7 +69,7 @@ /// @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: 0x766c4f37, /// or in textual repr: sponsor(address) - function sponsor(address contractAddress) external view returns (EthCrossAccount memory); + function sponsor(address contractAddress) external view returns (OptionCrossAddress memory); /// Check tat contract has confirmed sponsor. /// @@ -93,7 +93,7 @@ /// @dev EVM selector for this function is: 0xfde8a560, /// or in textual repr: setSponsoringMode(address,uint8) - function setSponsoringMode(address contractAddress, uint8 mode) external; + function setSponsoringMode(address contractAddress, SponsoringModeT mode) external; /// Get current contract sponsoring rate limit /// @param contractAddress Contract to get sponsoring rate limit of @@ -171,8 +171,24 @@ function toggleAllowlist(address contractAddress, bool enabled) external; } -/// @dev Cross account struct -struct EthCrossAccount { +/// Available contract sponsoring modes +enum SponsoringModeT { + /// Sponsoring is disabled + Disabled, + /// Only users from allowlist will be sponsored + Allowlisted, + /// All users will be sponsored + Generous +} + +/// Ethereum representation of Optional value with CrossAddress. +struct OptionCrossAddress { + bool status; + CrossAddress value; +} + +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } --- a/tests/src/eth/api/UniqueFungible.sol +++ b/tests/src/eth/api/UniqueFungible.sol @@ -13,7 +13,7 @@ } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -78,7 +78,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) external; + function setCollectionSponsorCross(CrossAddress memory sponsor) external; /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, @@ -102,46 +102,21 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (EthCrossAccount memory); + function collectionSponsor() external view returns (CrossAddress memory); /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple21[] memory); + function collectionLimits() external view returns (CollectionLimit[] 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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) external; + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -152,13 +127,13 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) external; + function addCollectionAdminCross(CrossAddress memory newAdmin) external; /// Remove collection admin. /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) external; + function removeCollectionAdminCross(CrossAddress memory admin) external; // /// Add collection admin. // /// @param newAdmin Address of the added administrator. @@ -191,12 +166,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple26 memory); + function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple29[] memory); + function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); /// Set the collection access method. /// @param mode Access mode @@ -211,7 +186,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) external view returns (bool); + function allowlistedCross(CrossAddress memory user) external view returns (bool); // /// Add the user to the allowed list. // /// @@ -225,7 +200,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) external; + function addToCollectionAllowListCross(CrossAddress memory user) external; // /// Remove the user from the allowed list. // /// @@ -239,7 +214,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) external; + function removeFromCollectionAllowListCross(CrossAddress memory user) external; /// Switch permission for minting. /// @@ -262,7 +237,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) external view returns (bool); + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); /// Returns collection type /// @@ -277,7 +252,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (EthCrossAccount memory); + function collectionOwner() external view returns (CrossAddress memory); // /// Changes collection owner to another account // /// @@ -293,7 +268,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (EthCrossAccount[] memory); + function collectionAdmins() external view returns (CrossAddress[] memory); /// Changes collection owner to another account /// @@ -301,62 +276,70 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) external; + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -/// @dev anonymous struct -struct Tuple29 { - CollectionPermissions field_0; - bool field_1; +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin } -/// @dev anonymous struct -struct Tuple26 { - bool field_0; - uint256[] field_1; +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; +} + +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled } -/// @dev anonymous struct -struct Tuple21 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; -} - -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; @@ -371,11 +354,11 @@ /// @dev EVM selector for this function is: 0x269e6158, /// or in textual repr: mintCross((address,uint256),uint256) - function mintCross(EthCrossAccount memory to, uint256 amount) external returns (bool); + function mintCross(CrossAddress memory to, uint256 amount) external returns (bool); /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory spender, uint256 amount) external returns (bool); + function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool); // /// Burn tokens from account // /// @dev Function that burns an `amount` of the tokens of a given account, @@ -393,7 +376,7 @@ /// @param amount The amount that will be burnt. /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 amount) external returns (bool); + function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool); /// Mint tokens for multiple accounts. /// @param amounts array of pairs of account address and amount @@ -403,13 +386,13 @@ /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 amount) external returns (bool); + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 amount ) external returns (bool); } --- a/tests/src/eth/api/UniqueNFT.sol +++ b/tests/src/eth/api/UniqueNFT.sol @@ -30,12 +30,12 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple53[] memory permissions) external; + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (Tuple53[] memory); + function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -80,36 +80,40 @@ function property(uint256 tokenId, string memory key) external view returns (bytes memory); } -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; } -/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum EthTokenPermissions { - /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; } -/// @dev anonymous struct -struct Tuple53 { - string field_0; - Tuple51[] field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; } -/// @dev anonymous struct -struct Tuple51 { - EthTokenPermissions field_0; - bool field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -174,7 +178,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) external; + function setCollectionSponsorCross(CrossAddress memory sponsor) external; /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, @@ -198,46 +202,21 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (EthCrossAccount memory); + function collectionSponsor() external view returns (CrossAddress memory); /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple31[] memory); + function collectionLimits() external view returns (CollectionLimit[] 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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) external; + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -248,13 +227,13 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) external; + function addCollectionAdminCross(CrossAddress memory newAdmin) external; /// Remove collection admin. /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) external; + function removeCollectionAdminCross(CrossAddress memory admin) external; // /// Add collection admin. // /// @param newAdmin Address of the added administrator. @@ -287,12 +266,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple36 memory); + function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple39[] memory); + function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); /// Set the collection access method. /// @param mode Access mode @@ -307,7 +286,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) external view returns (bool); + function allowlistedCross(CrossAddress memory user) external view returns (bool); // /// Add the user to the allowed list. // /// @@ -321,7 +300,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) external; + function addToCollectionAllowListCross(CrossAddress memory user) external; // /// Remove the user from the allowed list. // /// @@ -335,7 +314,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) external; + function removeFromCollectionAllowListCross(CrossAddress memory user) external; /// Switch permission for minting. /// @@ -358,7 +337,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) external view returns (bool); + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); /// Returns collection type /// @@ -373,7 +352,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (EthCrossAccount memory); + function collectionOwner() external view returns (CrossAddress memory); // /// Changes collection owner to another account // /// @@ -389,7 +368,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (EthCrossAccount[] memory); + function collectionAdmins() external view returns (CrossAddress[] memory); /// Changes collection owner to another account /// @@ -397,59 +376,67 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) external; + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -/// @dev anonymous struct -struct Tuple39 { - CollectionPermissions field_0; - bool field_1; +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin } -/// @dev anonymous struct -struct Tuple36 { - bool field_0; - uint256[] field_1; +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; +} + +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled -} - -/// @dev anonymous struct -struct Tuple31 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; } /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension @@ -569,7 +556,7 @@ /// @param tokenId Id for the token. /// @dev EVM selector for this function is: 0x2b29dace, /// or in textual repr: crossOwnerOf(uint256) - function crossOwnerOf(uint256 tokenId) external view returns (EthCrossAccount memory); + function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); /// Returns the token properties. /// @@ -588,7 +575,7 @@ /// @param tokenId The NFT to approve /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory approved, uint256 tokenId) external; + function approveCross(CrossAddress memory approved, uint256 tokenId) external; /// @notice Transfer ownership of an NFT /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` @@ -606,7 +593,7 @@ /// @param tokenId The NFT to transfer /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 tokenId) external; + function transferCross(CrossAddress memory to, uint256 tokenId) external; /// @notice Transfer ownership of an NFT from cross account address to cross account address /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` @@ -617,8 +604,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 tokenId ) external; @@ -640,7 +627,7 @@ /// @param tokenId The NFT to transfer /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 tokenId) external; + function burnFromCross(CrossAddress memory from, uint256 tokenId) external; /// @notice Returns next free NFT ID. /// @dev EVM selector for this function is: 0x75794a3c, @@ -671,7 +658,7 @@ /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0xb904db03, /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256); + function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); } /// @dev anonymous struct --- a/tests/src/eth/api/UniqueRefungible.sol +++ b/tests/src/eth/api/UniqueRefungible.sol @@ -30,12 +30,12 @@ /// @param permissions Permissions for keys. /// @dev EVM selector for this function is: 0xbd92983a, /// or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[]) - function setTokenPropertyPermissions(Tuple52[] memory permissions) external; + function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external; /// @notice Get permissions for token properties. /// @dev EVM selector for this function is: 0xf23d7790, /// or in textual repr: tokenPropertyPermissions() - function tokenPropertyPermissions() external view returns (Tuple52[] memory); + function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory); // /// @notice Set token property value. // /// @dev Throws error if `msg.sender` has no permission to edit the property. @@ -80,36 +80,40 @@ function property(uint256 tokenId, string memory key) external view returns (bytes memory); } -/// @dev Property struct +/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue). struct Property { string key; bytes value; } -/// @dev Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. -enum EthTokenPermissions { - /// @dev Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] - Mutable, - /// @dev Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] - TokenOwner, - /// @dev Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] - CollectionAdmin +/// Ethereum representation of Token Property Permissions. +struct TokenPropertyPermission { + /// Token property key. + string key; + /// Token property permissions. + PropertyPermission[] permissions; } -/// @dev anonymous struct -struct Tuple52 { - string field_0; - Tuple50[] field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value. +struct PropertyPermission { + /// TokenPermission field. + TokenPermissionField code; + /// TokenPermission value. + bool value; } -/// @dev anonymous struct -struct Tuple50 { - EthTokenPermissions field_0; - bool field_1; +/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration. +enum TokenPermissionField { + /// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`] + Mutable, + /// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`] + TokenOwner, + /// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`] + CollectionAdmin } /// @title A contract that allows you to work with collections. -/// @dev the ERC-165 identifier for this interface is 0x81172a75 +/// @dev the ERC-165 identifier for this interface is 0x2a14cfd1 interface Collection is Dummy, ERC165 { // /// Set collection property. // /// @@ -174,7 +178,7 @@ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract. /// @dev EVM selector for this function is: 0x84a1d5a8, /// or in textual repr: setCollectionSponsorCross((address,uint256)) - function setCollectionSponsorCross(EthCrossAccount memory sponsor) external; + function setCollectionSponsorCross(CrossAddress memory sponsor) external; /// Whether there is a pending sponsor. /// @dev EVM selector for this function is: 0x058ac185, @@ -198,46 +202,21 @@ /// @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: 0x6ec0a9f1, /// or in textual repr: collectionSponsor() - function collectionSponsor() external view returns (EthCrossAccount memory); + function collectionSponsor() external view returns (CrossAddress memory); /// Get current collection limits. /// - /// @return Array of tuples (byte, bool, uint256) with limits and their values. Order of limits: - /// "accountTokenOwnershipLimit", - /// "sponsoredDataSize", - /// "sponsoredDataRateLimit", - /// "tokenLimit", - /// "sponsorTransferTimeout", - /// "sponsorApproveTimeout" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// Return `false` if a limit not set. + /// @return Array of collection limits /// @dev EVM selector for this function is: 0xf63bc572, /// or in textual repr: collectionLimits() - function collectionLimits() external view returns (Tuple30[] memory); + function collectionLimits() external view returns (CollectionLimit[] 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" - /// "ownerCanTransfer", - /// "ownerCanDestroy", - /// "transfersEnabled" - /// @param status enable\disable limit. Works only with `true`. - /// @param value Value of the limit. - /// @dev EVM selector for this function is: 0x88150bd0, - /// or in textual repr: setCollectionLimit(uint8,bool,uint256) - function setCollectionLimit( - CollectionLimits limit, - bool status, - uint256 value - ) external; + /// @param limit Some limit. + /// @dev EVM selector for this function is: 0x2316ee74, + /// or in textual repr: setCollectionLimit((uint8,(bool,uint256))) + function setCollectionLimit(CollectionLimit memory limit) external; /// Get contract address. /// @dev EVM selector for this function is: 0xf6b4dfb4, @@ -248,13 +227,13 @@ /// @param newAdmin Cross account administrator address. /// @dev EVM selector for this function is: 0x859aa7d6, /// or in textual repr: addCollectionAdminCross((address,uint256)) - function addCollectionAdminCross(EthCrossAccount memory newAdmin) external; + function addCollectionAdminCross(CrossAddress memory newAdmin) external; /// Remove collection admin. /// @param admin Cross account administrator address. /// @dev EVM selector for this function is: 0x6c0cd173, /// or in textual repr: removeCollectionAdminCross((address,uint256)) - function removeCollectionAdminCross(EthCrossAccount memory admin) external; + function removeCollectionAdminCross(CrossAddress memory admin) external; // /// Add collection admin. // /// @param newAdmin Address of the added administrator. @@ -287,12 +266,12 @@ /// Returns nesting for a collection /// @dev EVM selector for this function is: 0x22d25bfe, /// or in textual repr: collectionNestingRestrictedCollectionIds() - function collectionNestingRestrictedCollectionIds() external view returns (Tuple35 memory); + function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory); /// Returns permissions for a collection /// @dev EVM selector for this function is: 0x5b2eaf4b, /// or in textual repr: collectionNestingPermissions() - function collectionNestingPermissions() external view returns (Tuple38[] memory); + function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory); /// Set the collection access method. /// @param mode Access mode @@ -307,7 +286,7 @@ /// @param user User address to check. /// @dev EVM selector for this function is: 0x91b6df49, /// or in textual repr: allowlistedCross((address,uint256)) - function allowlistedCross(EthCrossAccount memory user) external view returns (bool); + function allowlistedCross(CrossAddress memory user) external view returns (bool); // /// Add the user to the allowed list. // /// @@ -321,7 +300,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0xa0184a3a, /// or in textual repr: addToCollectionAllowListCross((address,uint256)) - function addToCollectionAllowListCross(EthCrossAccount memory user) external; + function addToCollectionAllowListCross(CrossAddress memory user) external; // /// Remove the user from the allowed list. // /// @@ -335,7 +314,7 @@ /// @param user User cross account address. /// @dev EVM selector for this function is: 0x09ba452a, /// or in textual repr: removeFromCollectionAllowListCross((address,uint256)) - function removeFromCollectionAllowListCross(EthCrossAccount memory user) external; + function removeFromCollectionAllowListCross(CrossAddress memory user) external; /// Switch permission for minting. /// @@ -358,7 +337,7 @@ /// @return "true" if account is the owner or admin /// @dev EVM selector for this function is: 0x3e75a905, /// or in textual repr: isOwnerOrAdminCross((address,uint256)) - function isOwnerOrAdminCross(EthCrossAccount memory user) external view returns (bool); + function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool); /// Returns collection type /// @@ -373,7 +352,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0xdf727d3b, /// or in textual repr: collectionOwner() - function collectionOwner() external view returns (EthCrossAccount memory); + function collectionOwner() external view returns (CrossAddress memory); // /// Changes collection owner to another account // /// @@ -389,7 +368,7 @@ /// If address is canonical then substrate mirror is zero and vice versa. /// @dev EVM selector for this function is: 0x5813216b, /// or in textual repr: collectionAdmins() - function collectionAdmins() external view returns (EthCrossAccount[] memory); + function collectionAdmins() external view returns (CrossAddress[] memory); /// Changes collection owner to another account /// @@ -397,59 +376,67 @@ /// @param newOwner new owner cross account /// @dev EVM selector for this function is: 0x6496c497, /// or in textual repr: changeCollectionOwnerCross((address,uint256)) - function changeCollectionOwnerCross(EthCrossAccount memory newOwner) external; + function changeCollectionOwnerCross(CrossAddress memory newOwner) external; } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } -/// @dev anonymous struct -struct Tuple38 { - CollectionPermissions field_0; - bool field_1; +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field. +struct CollectionNestingPermission { + CollectionPermissionField field; + bool value; } -enum CollectionPermissions { - CollectionAdmin, - TokenOwner +/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration. +enum CollectionPermissionField { + /// Owner of token can nest tokens under it. + TokenOwner, + /// Admin of token collection can nest tokens under token. + CollectionAdmin } -/// @dev anonymous struct -struct Tuple35 { - bool field_0; - uint256[] field_1; +/// Nested collections. +struct CollectionNesting { + bool token_owner; + uint256[] ids; +} + +/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM. +struct CollectionLimit { + CollectionLimitField field; + OptionUint value; +} + +/// Ethereum representation of Optional value with uint256. +struct OptionUint { + bool status; + uint256 value; } -/// @dev [`CollectionLimits`](up_data_structs::CollectionLimits) representation for EVM. -enum CollectionLimits { - /// @dev How many tokens can a user have on one account. +/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM. +enum CollectionLimitField { + /// How many tokens can a user have on one account. AccountTokenOwnership, - /// @dev How many bytes of data are available for sponsorship. + /// How many bytes of data are available for sponsorship. SponsoredDataSize, - /// @dev In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] + /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] SponsoredDataRateLimit, - /// @dev How many tokens can be mined into this collection. + /// How many tokens can be mined into this collection. TokenLimit, - /// @dev Timeouts for transfer sponsoring. + /// Timeouts for transfer sponsoring. SponsorTransferTimeout, - /// @dev Timeout for sponsoring an approval in passed blocks. + /// Timeout for sponsoring an approval in passed blocks. SponsorApproveTimeout, - /// @dev Whether the collection owner of the collection can send tokens (which belong to other users). + /// Whether the collection owner of the collection can send tokens (which belong to other users). OwnerCanTransfer, - /// @dev Can the collection owner burn other people's tokens. + /// Can the collection owner burn other people's tokens. OwnerCanDestroy, - /// @dev Is it possible to send tokens from this collection between users. + /// Is it possible to send tokens from this collection between users. TransferEnabled -} - -/// @dev anonymous struct -struct Tuple30 { - CollectionLimits field_0; - bool field_1; - uint256 field_2; } /// @dev the ERC-165 identifier for this interface is 0x5b5e139f @@ -567,7 +554,7 @@ /// @param tokenId Id for the token. /// @dev EVM selector for this function is: 0x2b29dace, /// or in textual repr: crossOwnerOf(uint256) - function crossOwnerOf(uint256 tokenId) external view returns (EthCrossAccount memory); + function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory); /// Returns the token properties. /// @@ -596,7 +583,7 @@ /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 tokenId) external; + function transferCross(CrossAddress memory to, uint256 tokenId) external; /// @notice Transfer ownership of an RFT /// @dev Throws unless `msg.sender` is the current owner. Throws if `to` @@ -607,8 +594,8 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 tokenId ) external; @@ -632,7 +619,7 @@ /// @param tokenId The RFT to transfer /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 tokenId) external; + function burnFromCross(CrossAddress memory from, uint256 tokenId) external; /// @notice Returns next free RFT ID. /// @dev EVM selector for this function is: 0x75794a3c, @@ -663,7 +650,7 @@ /// @return uint256 The id of the newly minted token /// @dev EVM selector for this function is: 0xb904db03, /// or in textual repr: mintCross((address,uint256),(string,bytes)[]) - function mintCross(EthCrossAccount memory to, Property[] memory properties) external returns (uint256); + function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256); /// Returns EVM address for refungible token /// --- a/tests/src/eth/api/UniqueRefungibleToken.sol +++ b/tests/src/eth/api/UniqueRefungibleToken.sol @@ -39,7 +39,7 @@ /// @param amount The amount that will be burnt. /// @dev EVM selector for this function is: 0xbb2f5a58, /// or in textual repr: burnFromCross((address,uint256),uint256) - function burnFromCross(EthCrossAccount memory from, uint256 amount) external returns (bool); + function burnFromCross(CrossAddress memory from, 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 @@ -50,7 +50,7 @@ /// @param amount The amount of tokens to be spent. /// @dev EVM selector for this function is: 0x0ecd0ab0, /// or in textual repr: approveCross((address,uint256),uint256) - function approveCross(EthCrossAccount memory spender, uint256 amount) external returns (bool); + function approveCross(CrossAddress memory spender, 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. @@ -64,7 +64,7 @@ /// @param amount The amount to be transferred. /// @dev EVM selector for this function is: 0x2ada85ff, /// or in textual repr: transferCross((address,uint256),uint256) - function transferCross(EthCrossAccount memory to, uint256 amount) external returns (bool); + function transferCross(CrossAddress memory to, uint256 amount) external returns (bool); /// @dev Transfer tokens from one address to another /// @param from The address which you want to send tokens from @@ -73,14 +73,14 @@ /// @dev EVM selector for this function is: 0xd5cf430b, /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256) function transferFromCross( - EthCrossAccount memory from, - EthCrossAccount memory to, + CrossAddress memory from, + CrossAddress memory to, uint256 amount ) external returns (bool); } -/// @dev Cross account struct -struct EthCrossAccount { +/// Cross account struct +struct CrossAddress { address eth; uint256 sub; } --- a/tests/src/eth/collectionLimits.test.ts +++ b/tests/src/eth/collectionLimits.test.ts @@ -1,7 +1,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {Pallets} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; -import {CollectionLimits} from './util/playgrounds/types'; +import {CollectionLimitField} from './util/playgrounds/types'; describe('Can set collection limits', () => { @@ -46,15 +46,15 @@ }; const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, limits.accountTokenOwnershipLimit).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsoredDataSize, true, limits.sponsoredDataSize).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsoredDataRateLimit, true, limits.sponsoredDataRateLimit).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.TokenLimit, true, limits.tokenLimit).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsorTransferTimeout, true, limits.sponsorTransferTimeout).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.SponsorApproveTimeout, true, limits.sponsorApproveTimeout).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, limits.ownerCanTransfer).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.OwnerCanDestroy, true, limits.ownerCanDestroy).send(); - await collectionEvm.methods.setCollectionLimit(CollectionLimits.TransferEnabled, true, limits.transfersEnabled).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: limits.accountTokenOwnershipLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: limits.sponsoredDataSize}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: limits.sponsoredDataRateLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TokenLimit, value: {status: true, value: limits.tokenLimit}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorTransferTimeout, value: {status: true, value: limits.sponsorTransferTimeout}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorApproveTimeout, value: {status: true, value: limits.sponsorApproveTimeout}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: limits.ownerCanTransfer}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanDestroy, value: {status: true, value: limits.ownerCanDestroy}}).send(); + await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: limits.transfersEnabled}}).send(); // Check limits from sub: const data = (await helper.rft.getData(collectionId))!; @@ -63,15 +63,15 @@ // Check limits from eth: const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner}); expect(limitsEvm).to.have.length(9); - expect(limitsEvm[0]).to.deep.eq([CollectionLimits.AccountTokenOwnership.toString(), true, limits.accountTokenOwnershipLimit.toString()]); - expect(limitsEvm[1]).to.deep.eq([CollectionLimits.SponsoredDataSize.toString(), true, limits.sponsoredDataSize.toString()]); - expect(limitsEvm[2]).to.deep.eq([CollectionLimits.SponsoredDataRateLimit.toString(), true, limits.sponsoredDataRateLimit.toString()]); - expect(limitsEvm[3]).to.deep.eq([CollectionLimits.TokenLimit.toString(), true, limits.tokenLimit.toString()]); - expect(limitsEvm[4]).to.deep.eq([CollectionLimits.SponsorTransferTimeout.toString(), true, limits.sponsorTransferTimeout.toString()]); - expect(limitsEvm[5]).to.deep.eq([CollectionLimits.SponsorApproveTimeout.toString(), true, limits.sponsorApproveTimeout.toString()]); - expect(limitsEvm[6]).to.deep.eq([CollectionLimits.OwnerCanTransfer.toString(), true, limits.ownerCanTransfer.toString()]); - expect(limitsEvm[7]).to.deep.eq([CollectionLimits.OwnerCanDestroy.toString(), true, limits.ownerCanDestroy.toString()]); - expect(limitsEvm[8]).to.deep.eq([CollectionLimits.TransferEnabled.toString(), true, limits.transfersEnabled.toString()]); + expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]); + expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]); + expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]); + expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]); + expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]); + expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]); + expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]); + expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]); + expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]); })); }); @@ -101,24 +101,24 @@ // Cannot set non-existing limit await expect(collectionEvm.methods - .setCollectionLimit(9, true, 1) - .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert Value not convertible into enum "CollectionLimits"'); + .setCollectionLimit({field: 9, value: {status: true, value: 1}}) + .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert Value not convertible into enum "CollectionLimitField"'); // Cannot disable limits await expect(collectionEvm.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, false, 200) - .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert user can\'t disable limits'); + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: false, value: 200}}) + .call()).to.be.rejectedWith('user can\'t disable limits'); await expect(collectionEvm.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, invalidLimits.accountTokenOwnershipLimit) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: invalidLimits.accountTokenOwnershipLimit}}) .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); await expect(collectionEvm.methods - .setCollectionLimit(CollectionLimits.TransferEnabled, true, 3) + .setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: 3}}) .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); expect(() => collectionEvm.methods - .setCollectionLimit(CollectionLimits.SponsoredDataSize, true, -1).send()).to.throw('value out-of-bounds'); + .setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: -1}}).send()).to.throw('value out-of-bounds'); })); [ @@ -133,12 +133,12 @@ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); await expect(collectionEvm.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call({from: nonOwner})) .to.be.rejectedWith('NoPermission'); await expect(collectionEvm.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .send({from: nonOwner})) .to.be.rejected; })); --- a/tests/src/eth/contractSponsoring.test.ts +++ b/tests/src/eth/contractSponsoring.test.ts @@ -42,7 +42,9 @@ expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; // 1.1 Can get sponsor using methods.sponsor: - const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call(); + const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); + expect(actualSponsorOpt.status).to.be.true; + const actualSponsor = actualSponsorOpt.value; expect(actualSponsor.eth).to.eq(flipper.options.address); expect(actualSponsor.sub).to.eq('0'); @@ -151,7 +153,9 @@ expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true; // 1.1 Can get sponsor using methods.sponsor: - const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call(); + const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call(); + expect(actualSponsorOpt.status).to.be.true; + const actualSponsor = actualSponsorOpt.value; expect(actualSponsor.eth).to.eq(sponsor); expect(actualSponsor.sub).to.eq('0'); --- a/tests/src/eth/createFTCollection.test.ts +++ b/tests/src/eth/createFTCollection.test.ts @@ -18,7 +18,7 @@ import {evmToAddress} from '@polkadot/util-crypto'; import {Pallets, requirePalletsOrSkip} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; -import {CollectionLimits} from './util/playgrounds/types'; +import {CollectionLimitField} from './util/playgrounds/types'; const DECIMALS = 18; @@ -199,7 +199,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -224,7 +224,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -17,7 +17,7 @@ import {evmToAddress} from '@polkadot/util-crypto'; import {IKeyringPair} from '@polkadot/types/types'; import {expect, itEth, usingEthPlaygrounds} from './util'; -import {CollectionLimits} from './util/playgrounds/types'; +import {CollectionLimitField} from './util/playgrounds/types'; describe('Create NFT collection from EVM', () => { @@ -210,7 +210,7 @@ } { await expect(malfeasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -235,7 +235,7 @@ } { await expect(malfeasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -18,7 +18,7 @@ import {IKeyringPair} from '@polkadot/types/types'; import {Pallets, requirePalletsOrSkip} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; -import {CollectionLimits} from './util/playgrounds/types'; +import {CollectionLimitField} from './util/playgrounds/types'; describe('Create RFT collection from EVM', () => { @@ -242,7 +242,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); @@ -267,7 +267,7 @@ } { await expect(peasantCollection.methods - .setCollectionLimit(CollectionLimits.AccountTokenOwnership, true, 1000) + .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}) .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); --- a/tests/src/eth/events.test.ts +++ b/tests/src/eth/events.test.ts @@ -19,7 +19,7 @@ import {EthUniqueHelper, itEth, usingEthPlaygrounds} from './util'; import {IEvent, TCollectionMode} from '../util/playgrounds/types'; import {Pallets, requirePalletsOrSkip} from '../util'; -import {CollectionLimits, EthTokenPermissions, NormalizedEvent} from './util/playgrounds/types'; +import {CollectionLimitField, TokenPermissionField, NormalizedEvent} from './util/playgrounds/types'; let donor: IKeyringPair; @@ -121,9 +121,9 @@ const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]); await collection.methods.setTokenPropertyPermissions([ ['A', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ]).send({from: owner}); await helper.wait.newBlocks(1); @@ -233,7 +233,7 @@ }); const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]); { - await collection.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, 0).send({from: owner}); + await collection.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: 0}}).send({from: owner}); await helper.wait.newBlocks(1); expect(ethEvents).to.containSubset([ { @@ -379,9 +379,9 @@ const tokenId = result.events.Transfer.returnValues.tokenId; await collection.methods.setTokenPropertyPermissions([ ['A', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ]).send({from: owner}); --- a/tests/src/eth/fractionalizer/Fractionalizer.sol +++ b/tests/src/eth/fractionalizer/Fractionalizer.sol @@ -3,7 +3,7 @@ import {CollectionHelpers} from "../api/CollectionHelpers.sol"; import {ContractHelpers} from "../api/ContractHelpers.sol"; import {UniqueRefungibleToken} from "../api/UniqueRefungibleToken.sol"; -import {UniqueRefungible, EthCrossAccount} from "../api/UniqueRefungible.sol"; +import {UniqueRefungible, CrossAddress} from "../api/UniqueRefungible.sol"; import {UniqueNFT} from "../api/UniqueNFT.sol"; /// @dev Fractionalization contract. It stores mappings between NFT and RFT tokens, @@ -63,7 +63,7 @@ "Wrong collection type. Collection is not refungible." ); require( - refungibleContract.isOwnerOrAdminCross(EthCrossAccount({eth: address(this), sub: uint256(0)})), + refungibleContract.isOwnerOrAdminCross(CrossAddress({eth: address(this), sub: uint256(0)})), "Fractionalizer contract should be an admin of the collection" ); rftCollection = _collection; @@ -128,7 +128,7 @@ address rftTokenAddress; UniqueRefungibleToken rftTokenContract; if (nft2rftMapping[_collection][_token] == 0) { - rftTokenId = rftCollectionContract.mint(address(this)); + rftTokenId = rftCollectionContract.mint(address(this)); rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId); nft2rftMapping[_collection][_token] = rftTokenId; rft2nftMapping[rftTokenAddress] = Token(_collection, _token); --- a/tests/src/eth/tokenProperties.test.ts +++ b/tests/src/eth/tokenProperties.test.ts @@ -20,7 +20,7 @@ import {ITokenPropertyPermission} from '../util/playgrounds/types'; import {Pallets} from '../util'; import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '../util/playgrounds/unique'; -import {EthTokenPermissions} from './util/playgrounds/types'; +import {TokenPermissionField} from './util/playgrounds/types'; describe('EVM token properties', () => { let donor: IKeyringPair; @@ -47,9 +47,9 @@ await collection.methods.setTokenPropertyPermissions([ ['testKey', [ - [EthTokenPermissions.Mutable, mutable], - [EthTokenPermissions.TokenOwner, tokenOwner], - [EthTokenPermissions.CollectionAdmin, collectionAdmin]], + [TokenPermissionField.Mutable, mutable], + [TokenPermissionField.TokenOwner, tokenOwner], + [TokenPermissionField.CollectionAdmin, collectionAdmin]], ], ]).send({from: caller.eth}); @@ -60,9 +60,9 @@ expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ ['testKey', [ - [EthTokenPermissions.Mutable.toString(), mutable], - [EthTokenPermissions.TokenOwner.toString(), tokenOwner], - [EthTokenPermissions.CollectionAdmin.toString(), collectionAdmin]], + [TokenPermissionField.Mutable.toString(), mutable], + [TokenPermissionField.TokenOwner.toString(), tokenOwner], + [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]], ], ]); } @@ -80,19 +80,19 @@ await collection.methods.setTokenPropertyPermissions([ ['testKey_0', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ['testKey_1', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, false], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, true]], ], ['testKey_2', [ - [EthTokenPermissions.Mutable, false], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, false]], + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, false]], ], ]).send({from: owner}); @@ -113,19 +113,19 @@ expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([ ['testKey_0', [ - [EthTokenPermissions.Mutable.toString(), true], - [EthTokenPermissions.TokenOwner.toString(), true], - [EthTokenPermissions.CollectionAdmin.toString(), true]], + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), true]], ], ['testKey_1', [ - [EthTokenPermissions.Mutable.toString(), true], - [EthTokenPermissions.TokenOwner.toString(), false], - [EthTokenPermissions.CollectionAdmin.toString(), true]], + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), false], + [TokenPermissionField.CollectionAdmin.toString(), true]], ], ['testKey_2', [ - [EthTokenPermissions.Mutable.toString(), false], - [EthTokenPermissions.TokenOwner.toString(), true], - [EthTokenPermissions.CollectionAdmin.toString(), false]], + [TokenPermissionField.Mutable.toString(), false], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), false]], ], ]); })); @@ -144,19 +144,19 @@ await collection.methods.setTokenPropertyPermissions([ ['testKey_0', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ['testKey_1', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, false], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, true]], ], ['testKey_2', [ - [EthTokenPermissions.Mutable, false], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, false]], + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, false]], ], ]).send({from: caller.eth}); @@ -177,19 +177,19 @@ expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([ ['testKey_0', [ - [EthTokenPermissions.Mutable.toString(), true], - [EthTokenPermissions.TokenOwner.toString(), true], - [EthTokenPermissions.CollectionAdmin.toString(), true]], + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), true]], ], ['testKey_1', [ - [EthTokenPermissions.Mutable.toString(), true], - [EthTokenPermissions.TokenOwner.toString(), false], - [EthTokenPermissions.CollectionAdmin.toString(), true]], + [TokenPermissionField.Mutable.toString(), true], + [TokenPermissionField.TokenOwner.toString(), false], + [TokenPermissionField.CollectionAdmin.toString(), true]], ], ['testKey_2', [ - [EthTokenPermissions.Mutable.toString(), false], - [EthTokenPermissions.TokenOwner.toString(), true], - [EthTokenPermissions.CollectionAdmin.toString(), false]], + [TokenPermissionField.Mutable.toString(), false], + [TokenPermissionField.TokenOwner.toString(), true], + [TokenPermissionField.CollectionAdmin.toString(), false]], ], ]); @@ -460,9 +460,9 @@ await expect(collection.methods.setTokenPropertyPermissions([ ['testKey_0', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ]).call({from: caller})).to.be.rejectedWith('NoPermission'); })); @@ -480,9 +480,9 @@ await expect(collection.methods.setTokenPropertyPermissions([ // "Space" is invalid character ['testKey 0', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey'); })); @@ -500,9 +500,9 @@ // 1. Owner sets strict property-permissions: await collection.methods.setTokenPropertyPermissions([ ['testKey', [ - [EthTokenPermissions.Mutable, true], - [EthTokenPermissions.TokenOwner, true], - [EthTokenPermissions.CollectionAdmin, true]], + [TokenPermissionField.Mutable, true], + [TokenPermissionField.TokenOwner, true], + [TokenPermissionField.CollectionAdmin, true]], ], ]).send({from: owner}); @@ -510,9 +510,9 @@ for(const values of [[true, true, false], [true, false, false], [false, false, false]]) { await collection.methods.setTokenPropertyPermissions([ ['testKey', [ - [EthTokenPermissions.Mutable, values[0]], - [EthTokenPermissions.TokenOwner, values[1]], - [EthTokenPermissions.CollectionAdmin, values[2]]], + [TokenPermissionField.Mutable, values[0]], + [TokenPermissionField.TokenOwner, values[1]], + [TokenPermissionField.CollectionAdmin, values[2]]], ], ]).send({from: owner}); } @@ -536,9 +536,9 @@ // 1. Owner sets strict property-permissions: await collection.methods.setTokenPropertyPermissions([ ['testKey', [ - [EthTokenPermissions.Mutable, false], - [EthTokenPermissions.TokenOwner, false], - [EthTokenPermissions.CollectionAdmin, false]], + [TokenPermissionField.Mutable, false], + [TokenPermissionField.TokenOwner, false], + [TokenPermissionField.CollectionAdmin, false]], ], ]).send({from: owner}); @@ -546,9 +546,9 @@ for(const values of [[true, false, false], [false, true, false], [false, false, true]]) { await expect(collection.methods.setTokenPropertyPermissions([ ['testKey', [ - [EthTokenPermissions.Mutable, values[0]], - [EthTokenPermissions.TokenOwner, values[1]], - [EthTokenPermissions.CollectionAdmin, values[2]]], + [TokenPermissionField.Mutable, values[0]], + [TokenPermissionField.TokenOwner, values[1]], + [TokenPermissionField.CollectionAdmin, values[2]]], ], ]).call({from: owner})).to.be.rejectedWith('NoPermission'); } --- a/tests/src/eth/util/playgrounds/types.ts +++ b/tests/src/eth/util/playgrounds/types.ts @@ -13,19 +13,26 @@ event: string, args: { [key: string]: string } }; -export interface TEthCrossAccount { + +export interface OptionUint { + status: boolean, + value: bigint, +} + +export interface CrossAddress { readonly eth: string, readonly sub: string | Uint8Array, } export type EthProperty = string[]; -export enum EthTokenPermissions { +export enum TokenPermissionField { Mutable, TokenOwner, CollectionAdmin } -export enum CollectionLimits { + +export enum CollectionLimitField { AccountTokenOwnership, SponsoredDataSize, SponsoredDataRateLimit, @@ -36,3 +43,8 @@ OwnerCanDestroy, TransferEnabled } + +export interface CollectionLimit { + field: CollectionLimitField, + value: OptionUint, +} --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -18,7 +18,7 @@ import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev'; -import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent, EthProperty} from './types'; +import {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types'; // Native contracts ABI import collectionHelpersAbi from '../../abi/collectionHelpers.json'; @@ -435,7 +435,7 @@ export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper; export class EthCrossAccountGroup extends EthGroupBase { - createAccount(): TEthCrossAccount { + createAccount(): CrossAddress { return this.fromAddress(this.helper.eth.createAccount()); } @@ -443,14 +443,14 @@ return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount)); } - fromAddress(address: TEthereumAccount): TEthCrossAccount { + fromAddress(address: TEthereumAccount): CrossAddress { return { eth: address, sub: '0', }; } - fromKeyringPair(keyring: IKeyringPair): TEthCrossAccount { + fromKeyringPair(keyring: IKeyringPair): CrossAddress { return { eth: '0x0000000000000000000000000000000000000000', sub: keyring.addressRaw,