difftreelog
feat Add custum signature with unlimited nesting.
in: master
17 files changed
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -21,7 +21,7 @@
// https://doc.rust-lang.org/reference/procedural-macros.html
use proc_macro2::{TokenStream, Group};
-use quote::{quote, ToTokens};
+use quote::{quote, ToTokens, format_ident};
use inflector::cases;
use std::fmt::Write;
use syn::{
@@ -725,25 +725,20 @@
}
}
- fn expand_selector(&self) -> proc_macro2::TokenStream {
- let custom_signature = self.expand_custom_signature();
- quote! {
- {
- let a = ::evm_coder::sha3_const::Keccak256::new()
- .update(#custom_signature.as_bytes())
- .finalize();
- [a[0], a[1], a[2], a[3]]
- }
- }
- }
-
fn expand_const(&self) -> proc_macro2::TokenStream {
let screaming_name = &self.screaming_name;
+ let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name);
let selector_str = &self.selector_str;
- let selector = &self.expand_selector();
+ let custom_signature = self.expand_custom_signature();
quote! {
+ const #screaming_name_signature: ::evm_coder::custom_signature::FunctionSignature = #custom_signature;
#[doc = #selector_str]
- const #screaming_name: ::evm_coder::types::bytes4 = #selector;
+ const #screaming_name: ::evm_coder::types::bytes4 = {
+ let a = ::evm_coder::sha3_const::Keccak256::new()
+ .update_with_size(&Self::#screaming_name_signature.signature, Self::#screaming_name_signature.signature_len)
+ .finalize();
+ [a[0], a[1], a[2], a[3]]
+ };
}
}
@@ -847,9 +842,8 @@
}
fn expand_custom_signature(&self) -> proc_macro2::TokenStream {
- let mut first_comma = true;
let mut custom_signature = TokenStream::new();
- let mut template = self.camel_name.clone() + "(";
+
self.args
.iter()
.filter_map(|a| {
@@ -863,39 +857,18 @@
}
})
.for_each(|ident| {
- if !first_comma {
- custom_signature.extend(quote!(,));
- template.push(',');
- } else {
- first_comma = false;
- };
- template.push_str("{}");
- let ident_str = ident.to_string();
- match ident_str.as_str() {
- "address" | "uint8" | "uint16" | "uint32" | "uint64" | "uint128"
- | "uint256" | "bytes4" | "topic" | "string" | "bytes" | "void" | "caller"
- | "bool" | "" => {
- custom_signature.extend(quote!(#ident_str));
- }
- _ => {
- custom_signature.extend(quote! {
- #ident::SIGNATURE_STRING
- });
- }
- }
+ custom_signature.extend(quote! {
+ (<#ident>::SIGNATURE, <#ident>::SIGNATURE_LEN)
+ });
});
- template.push(')');
- let mut template = quote!(#template);
- template.extend(quote!(,));
- template.extend(custom_signature);
- let custom_signature_group = Group::new(proc_macro2::Delimiter::Parenthesis, template);
- let mut custom_signature = quote! {
- ::evm_coder::const_format::formatcp!
- };
- custom_signature.extend(custom_signature_group.to_token_stream());
+ let func_name = self.camel_name.clone();
+ let func_name = quote!(FunctionName::new(#func_name));
+ custom_signature =
+ quote!({ ::evm_coder::make_signature!(new fn(& #func_name),#custom_signature) });
+
+ // println!("!!!!! {}", custom_signature);
- println!("!!!!! {}", custom_signature);
custom_signature
}
@@ -915,16 +888,23 @@
.map(MethodArg::expand_solidity_argument);
let docs = &self.docs;
let selector_str = &self.selector_str;
- let selector = &self.expand_selector();
+ let screaming_name = &self.screaming_name;
let hide = self.hide;
let custom_signature = self.expand_custom_signature();
+ let custom_signature = quote!(
+ {
+ const cs: FunctionSignature = #custom_signature;
+ cs
+ }
+ );
+ // println!("!!!!! {}", custom_signature);
let is_payable = self.has_value_args;
- quote! {
+ let out = quote! {
SolidityFunction {
docs: &[#(#docs),*],
selector_str: #selector_str,
hide: #hide,
- selector: u32::from_be_bytes(#selector),
+ selector: u32::from_be_bytes(Self::#screaming_name),
custom_signature: #custom_signature,
name: #camel_name,
mutability: #mutability,
@@ -936,7 +916,9 @@
),
result: <UnnamedArgument<#result>>::default(),
}
- }
+ };
+ // println!("@@@ {}", out);
+ out
}
}
crates/evm-coder/src/custom_signature.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/custom_signature.rs
@@ -0,0 +1,336 @@
+use core::str::from_utf8;
+
+pub const SIGNATURE_SIZE_LIMIT: usize = 256;
+
+pub trait Name {
+ const SIGNATURE: [u8; SIGNATURE_SIZE_LIMIT];
+ const SIGNATURE_LEN: usize;
+
+ fn name() -> &'static str {
+ from_utf8(&Self::SIGNATURE[..Self::SIGNATURE_LEN]).expect("bad utf-8")
+ }
+}
+
+#[derive(Debug)]
+pub struct FunctionSignature {
+ pub signature: [u8; SIGNATURE_SIZE_LIMIT],
+ pub signature_len: usize,
+}
+
+impl FunctionSignature {
+ pub const fn new(name: &'static FunctionName) -> FunctionSignature {
+ let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT];
+ let name_len = name.signature_len;
+ let name = name.signature;
+ let mut dst_offset = 0;
+ let bracket_open = {
+ let mut b = [0_u8; SIGNATURE_SIZE_LIMIT];
+ b[0] = b"("[0];
+ b
+ };
+ crate::make_signature!(@copy(name, signature, name_len, dst_offset));
+ crate::make_signature!(@copy(bracket_open, signature, 1, dst_offset));
+ FunctionSignature {
+ signature,
+ signature_len: dst_offset,
+ }
+ }
+
+ pub const fn add_param(
+ signature: FunctionSignature,
+ param: ([u8; SIGNATURE_SIZE_LIMIT], usize),
+ ) -> FunctionSignature {
+ let mut dst = signature.signature;
+ let mut dst_offset = signature.signature_len;
+ let (param_name, param_len) = param;
+ let comma = {
+ let mut b = [0_u8; SIGNATURE_SIZE_LIMIT];
+ b[0] = b","[0];
+ b
+ };
+ FunctionSignature {
+ signature: {
+ crate::make_signature!(@copy(param_name, dst, param_len, dst_offset));
+ crate::make_signature!(@copy(comma, dst, 1, dst_offset));
+ dst
+ },
+ signature_len: dst_offset,
+ }
+ }
+
+ pub const fn done(signature: FunctionSignature, owerride: bool) -> FunctionSignature {
+ let mut dst = signature.signature;
+ let mut dst_offset = signature.signature_len - if owerride { 1 } else { 0 };
+ let bracket_close = {
+ let mut b = [0_u8; SIGNATURE_SIZE_LIMIT];
+ b[0] = b")"[0];
+ b
+ };
+ FunctionSignature {
+ signature: {
+ crate::make_signature!(@copy(bracket_close, dst, 1, dst_offset));
+ dst
+ },
+ signature_len: dst_offset,
+ }
+ }
+
+ // fn as_str(&self) -> &'static str {
+ // from_utf8(&self.signature[..self.signature_len]).expect("bad utf-8")
+ // }
+}
+
+#[derive(Debug)]
+pub struct FunctionName {
+ signature: [u8; SIGNATURE_SIZE_LIMIT],
+ signature_len: usize,
+}
+
+impl FunctionName {
+ pub const fn new(name: &'static str) -> FunctionName {
+ let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT];
+ let name = name.as_bytes();
+ let name_len = name.len();
+ let mut dst_offset = 0;
+ crate::make_signature!(@copy(name, signature, name_len, dst_offset));
+ FunctionName {
+ signature,
+ signature_len: name_len,
+ }
+ }
+}
+
+#[macro_export]
+#[allow(missing_docs)]
+macro_rules! make_signature { // May be "define_signature"?
+ (new fn($func:expr)$(,)+) => {
+ {
+ let fs = FunctionSignature::new(& $func);
+ let fs = FunctionSignature::done(fs, false);
+ fs
+ }
+ };
+ (new fn($func:expr), $($tt:tt)*) => {
+ {
+ let fs = FunctionSignature::new(& $func);
+ let fs = make_signature!(@param; fs, $($tt),*);
+ fs
+ }
+ };
+
+ (@param; $func:expr) => {
+ FunctionSignature::done($func, true)
+ };
+ (@param; $func:expr, $param:expr) => {
+ make_signature!(@param; FunctionSignature::add_param($func, $param))
+ };
+ (@param; $func:expr, $param:expr, $($tt:tt),*) => {
+ make_signature!(@param; FunctionSignature::add_param($func, $param), $($tt),*)
+ };
+
+ (new $($tt:tt)*) => {
+ const SIGNATURE: [u8; SIGNATURE_SIZE_LIMIT] = {
+ let mut out = [0u8; SIGNATURE_SIZE_LIMIT];
+ let mut dst_offset = 0;
+ make_signature!(@data(out, dst_offset); $($tt)*);
+ out
+ };
+ const SIGNATURE_LEN: usize = 0 + make_signature!(@size; $($tt)*);
+ };
+
+ // (@bytes)
+
+ (@size;) => {
+ 0
+ };
+ (@size; fixed($expr:expr) $($tt:tt)*) => {
+ $expr.len() + make_signature!(@size; $($tt)*)
+ };
+ (@size; nameof($expr:ty) $($tt:tt)*) => {
+ <$expr>::SIGNATURE_LEN + make_signature!(@size; $($tt)*)
+ };
+
+ (@data($dst:ident, $dst_offset:ident);) => {};
+ (@data($dst:ident, $dst_offset:ident); fixed($expr:expr) $($tt:tt)*) => {
+ {
+ let data = $expr.as_bytes();
+ let data_len = data.len();
+ make_signature!(@copy(data, $dst, data_len, $dst_offset));
+ }
+ make_signature!(@data($dst, $dst_offset); $($tt)*)
+ };
+ (@data($dst:ident, $dst_offset:ident); nameof($expr:ty) $($tt:tt)*) => {
+ {
+ let data = &<$expr>::SIGNATURE;
+ let data_len = <$expr>::SIGNATURE_LEN;
+ make_signature!(@copy(data, $dst, data_len, $dst_offset));
+ }
+ make_signature!(@data($dst, $dst_offset); $($tt)*)
+ };
+
+ (@copy($src:ident, $dst:ident, $src_len:expr, $dst_offset:ident)) => {
+ {
+ let mut src_offset = 0;
+ let src_len: usize = $src_len;
+ while src_offset < src_len {
+ $dst[$dst_offset] = $src[src_offset];
+ $dst_offset += 1;
+ src_offset += 1;
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use core::str::from_utf8;
+
+ use frame_support::sp_runtime::app_crypto::sp_core::hexdisplay::AsBytesRef;
+
+ use super::{Name, SIGNATURE_SIZE_LIMIT, FunctionName, FunctionSignature};
+
+ impl Name for u8 {
+ make_signature!(new fixed("uint8"));
+ }
+ impl Name for u32 {
+ make_signature!(new fixed("uint32"));
+ }
+ impl<T: Name> Name for Vec<T> {
+ make_signature!(new nameof(T) fixed("[]"));
+ }
+ impl<A: Name, B: Name> Name for (A, B) {
+ make_signature!(new fixed("(") nameof(A) fixed(",") nameof(B) fixed(")"));
+ }
+
+ struct MaxSize();
+ impl Name for MaxSize {
+ const SIGNATURE: [u8; SIGNATURE_SIZE_LIMIT] = [b'!'; SIGNATURE_SIZE_LIMIT];
+ const SIGNATURE_LEN: usize = SIGNATURE_SIZE_LIMIT;
+ }
+
+ #[test]
+ fn simple() {
+ assert_eq!(u8::name(), "uint8");
+ assert_eq!(u32::name(), "uint32");
+ }
+
+ #[test]
+ fn vector_of_simple() {
+ assert_eq!(<Vec<u8>>::name(), "uint8[]");
+ assert_eq!(<Vec<u32>>::name(), "uint32[]");
+ }
+
+ #[test]
+ fn vector_of_vector() {
+ assert_eq!(<Vec<Vec<u8>>>::name(), "uint8[][]");
+ }
+
+ #[test]
+ fn tuple_of_simple() {
+ assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)");
+ }
+
+ #[test]
+ fn tuple_of_tuple() {
+ assert_eq!(
+ <((u32, u8), (u8, u32))>::name(),
+ "((uint32,uint8),(uint8,uint32))"
+ );
+ }
+
+ #[test]
+ fn vector_of_tuple() {
+ assert_eq!(<Vec<(u32, u8)>>::name(), "(uint32,uint8)[]");
+ }
+
+ #[test]
+ fn tuple_of_vector() {
+ assert_eq!(<(Vec<u32>, u8)>::name(), "(uint32[],uint8)");
+ }
+
+ #[test]
+ fn complex() {
+ assert_eq!(
+ <(Vec<u32>, (u32, Vec<u8>))>::name(),
+ "(uint32[],(uint32,uint8[]))"
+ );
+ }
+
+ #[test]
+ fn max_size() {
+ assert_eq!(<MaxSize>::name(), "!".repeat(SIGNATURE_SIZE_LIMIT));
+ }
+
+ // This test must NOT compile!
+ // #[test]
+ // fn over_max_size() {
+ // assert_eq!(<Vec<MaxSize>>::name(), "!".repeat(SIZE_LIMIT) + "[]");
+ // }
+
+ #[test]
+ fn make_func_without_args() {
+ const SOME_FUNK_NAME: FunctionName = FunctionName::new("some_funk");
+ const SIG: FunctionSignature = make_signature!(
+ new fn(&SOME_FUNK_NAME),
+ );
+ let name = from_utf8(&SIG.signature[..SIG.signature_len]).unwrap();
+ assert_eq!(name, "some_funk()");
+ }
+
+ #[test]
+ fn make_func_with_1_args() {
+ const SOME_FUNK_NAME: FunctionName = FunctionName::new("some_funk");
+ const SIG: FunctionSignature = make_signature!(
+ new fn(&SOME_FUNK_NAME),
+ (u8::SIGNATURE, u8::SIGNATURE_LEN)
+ );
+ let name = from_utf8(&SIG.signature[..SIG.signature_len]).unwrap();
+ assert_eq!(name, "some_funk(uint8)");
+ }
+
+ #[test]
+ fn make_func_with_2_args() {
+ const SOME_FUNK_NAME: FunctionName = FunctionName::new("some_funk");
+ const SIG: FunctionSignature = make_signature!(
+ new fn(&SOME_FUNK_NAME),
+ (u8::SIGNATURE, u8::SIGNATURE_LEN)
+ (<Vec<u32>>::SIGNATURE, <Vec<u32>>::SIGNATURE_LEN)
+ );
+ let name = from_utf8(&SIG.signature[..SIG.signature_len]).unwrap();
+ assert_eq!(name, "some_funk(uint8,uint32[])");
+ }
+
+ #[test]
+ fn make_func_with_3_args() {
+ const SOME_FUNK_NAME: FunctionName = FunctionName::new("some_funk");
+ const SIG: FunctionSignature = make_signature!(
+ new fn(&SOME_FUNK_NAME),
+ (u8::SIGNATURE, u8::SIGNATURE_LEN)
+ (u32::SIGNATURE, u32::SIGNATURE_LEN)
+ (<Vec<u32>>::SIGNATURE, <Vec<u32>>::SIGNATURE_LEN)
+ );
+ let name = from_utf8(&SIG.signature[..SIG.signature_len]).unwrap();
+ assert_eq!(name, "some_funk(uint8,uint32,uint32[])");
+ }
+
+ #[test]
+ fn make_slice_from_signature() {
+ const SOME_FUNK_NAME: FunctionName = FunctionName::new("some_funk");
+ const SIG: FunctionSignature = make_signature!(
+ new fn(&SOME_FUNK_NAME),
+ (u8::SIGNATURE, u8::SIGNATURE_LEN)
+ (u32::SIGNATURE, u32::SIGNATURE_LEN)
+ (<Vec<u32>>::SIGNATURE, <Vec<u32>>::SIGNATURE_LEN)
+ );
+ const NAME: [u8; SIG.signature_len] = {
+ let mut name: [u8; SIG.signature_len] = [0; SIG.signature_len];
+ let mut i = 0;
+ while i < SIG.signature_len {
+ name[i] = SIG.signature[i];
+ i += 1;
+ }
+ name
+ };
+ assert_eq!(&NAME, b"some_funk(uint8,uint32,uint32[])");
+ }
+}
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -15,7 +15,9 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
#![doc = include_str!("../README.md")]
-#![deny(missing_docs)]
+// #![deny(missing_docs)]
+#![warn(missing_docs)]
+#![macro_use]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
@@ -26,6 +28,8 @@
pub use events::{ToLog, ToTopic};
use execution::DispatchInfo;
pub mod execution;
+#[macro_use]
+pub mod custom_signature;
/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`]
/// and [`crate::Call`] from impl block.
@@ -118,27 +122,54 @@
use alloc::{vec::Vec};
use pallet_evm::account::CrossAccountId;
use primitive_types::{U256, H160, H256};
+ use core::str::from_utf8;
- pub type address = H160;
+ use crate::custom_signature::SIGNATURE_SIZE_LIMIT;
- pub type uint8 = u8;
- pub type uint16 = u16;
- pub type uint32 = u32;
- pub type uint64 = u64;
- pub type uint128 = u128;
- pub type uint256 = U256;
+ pub trait Signature {
+ const SIGNATURE: [u8; SIGNATURE_SIZE_LIMIT];
+ const SIGNATURE_LEN: usize;
- pub type bytes4 = [u8; 4];
+ fn as_str() -> &'static str {
+ from_utf8(&Self::SIGNATURE[..Self::SIGNATURE_LEN]).expect("bad utf-8")
+ }
+ }
- pub type topic = H256;
+ impl Signature for bool {
+ make_signature!(new fixed("bool"));
+ }
+ macro_rules! define_simple_type {
+ (type $ident:ident = $ty:ty) => {
+ pub type $ident = $ty;
+ impl Signature for $ty {
+ make_signature!(new fixed(stringify!($ident)));
+ }
+ };
+ }
+
+ define_simple_type!(type address = H160);
+
+ define_simple_type!(type uint8 = u8);
+ define_simple_type!(type uint16 = u16);
+ define_simple_type!(type uint32 = u32);
+ define_simple_type!(type uint64 = u64);
+ define_simple_type!(type uint128 = u128);
+ define_simple_type!(type uint256 = U256);
+ define_simple_type!(type bytes4 = [u8; 4]);
+
+ define_simple_type!(type topic = H256);
+
#[cfg(not(feature = "std"))]
- pub type string = ::alloc::string::String;
+ define_simple_type!(type string = ::alloc::string::String);
#[cfg(feature = "std")]
- pub type string = ::std::string::String;
+ define_simple_type!(type string = ::std::string::String);
#[derive(Default, Debug)]
pub struct bytes(pub Vec<u8>);
+ impl Signature for bytes {
+ make_signature!(new fixed("bytes"));
+ }
/// Solidity doesn't have `void` type, however we have special implementation
/// for empty tuple return type
@@ -230,12 +261,8 @@
}
}
- impl SignatureString for EthCrossAccount {
- const SIGNATURE_STRING: &'static str = "(address,uint256)";
- }
-
- pub trait SignatureString {
- const SIGNATURE_STRING: &'static str;
+ impl Signature for EthCrossAccount {
+ make_signature!(new fixed("(address,uint256)"));
}
/// Convert `CrossAccountId` to `uint256`.
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -30,9 +30,10 @@
marker::PhantomData,
cell::{Cell, RefCell},
cmp::Reverse,
+ str::from_utf8,
};
use impl_trait_for_tuples::impl_for_tuples;
-use crate::types::*;
+use crate::{types::*, custom_signature::FunctionSignature};
#[derive(Default)]
pub struct TypeCollector {
@@ -487,7 +488,7 @@
pub selector_str: &'static str,
pub selector: u32,
pub hide: bool,
- pub custom_signature: &'static str,
+ pub custom_signature: FunctionSignature,
pub name: &'static str,
pub args: A,
pub result: R,
@@ -513,7 +514,8 @@
writeln!(
writer,
"\t{hide_comment}/// or in textual repr: {}",
- self.custom_signature
+ // from_utf8(self.custom_signature.as_str()).expect("bad utf8")
+ self.selector_str
)?;
write!(writer, "\t{hide_comment}function {}(", self.name)?;
self.args.solidity_name(writer, tc)?;
pallets/common/src/erc.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 solidity_interface, solidity, ToLog,21 types::*,22 execution::{Result, Error},23 weight,24};25pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};26use pallet_evm_coder_substrate::dispatch_to_evm;27use sp_std::vec::Vec;28use up_data_structs::{29 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,30 SponsoringRateLimit, SponsorshipState,31};32use alloc::format;3334use crate::{35 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,36 eth::{37 convert_cross_account_to_uint256, convert_cross_account_to_tuple,38 convert_tuple_to_cross_account,39 },40 weights::WeightInfo,41};4243/// Events for ethereum collection helper.44#[derive(ToLog)]45pub enum CollectionHelpersEvents {46 /// The collection has been created.47 CollectionCreated {48 /// Collection owner.49 #[indexed]50 owner: address,5152 /// Collection ID.53 #[indexed]54 collection_id: address,55 },56 /// The collection has been destroyed.57 CollectionDestroyed {58 /// Collection ID.59 #[indexed]60 collection_id: address,61 },62}6364/// Does not always represent a full collection, for RFT it is either65/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).66pub trait CommonEvmHandler {67 const CODE: &'static [u8];6869 /// Call precompiled handle.70 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;71}7273/// @title A contract that allows you to work with collections.74#[solidity_interface(name = Collection)]75impl<T: Config> CollectionHandle<T>76where77 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,78{79 /// Set collection property.80 ///81 /// @param key Property key.82 /// @param value Propery value.83 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]84 fn set_collection_property(85 &mut self,86 caller: caller,87 key: string,88 value: bytes,89 ) -> Result<void> {90 let caller = T::CrossAccountId::from_eth(caller);91 let key = <Vec<u8>>::from(key)92 .try_into()93 .map_err(|_| "key too large")?;94 let value = value.0.try_into().map_err(|_| "value too large")?;9596 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })97 .map_err(dispatch_to_evm::<T>)98 }99100 /// Set collection properties.101 ///102 /// @param properties Vector of properties key/value pair.103 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]104 fn set_collection_properties(105 &mut self,106 caller: caller,107 properties: Vec<(string, bytes)>,108 ) -> Result<void> {109 let caller = T::CrossAccountId::from_eth(caller);110111 let properties = properties112 .into_iter()113 .map(|(key, value)| {114 let key = <Vec<u8>>::from(key)115 .try_into()116 .map_err(|_| "key too large")?;117118 let value = value.0.try_into().map_err(|_| "value too large")?;119120 Ok(Property { key, value })121 })122 .collect::<Result<Vec<_>>>()?;123124 <Pallet<T>>::set_collection_properties(self, &caller, properties)125 .map_err(dispatch_to_evm::<T>)126 }127128 /// Delete collection property.129 ///130 /// @param key Property key.131 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]132 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {133 let caller = T::CrossAccountId::from_eth(caller);134 let key = <Vec<u8>>::from(key)135 .try_into()136 .map_err(|_| "key too large")?;137138 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)139 }140141 /// Delete collection properties.142 ///143 /// @param keys Properties keys.144 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]145 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {146 let caller = T::CrossAccountId::from_eth(caller);147 let keys = keys148 .into_iter()149 .map(|key| {150 <Vec<u8>>::from(key)151 .try_into()152 .map_err(|_| Error::Revert("key too large".into()))153 })154 .collect::<Result<Vec<_>>>()?;155156 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)157 }158159 /// Get collection property.160 ///161 /// @dev Throws error if key not found.162 ///163 /// @param key Property key.164 /// @return bytes The property corresponding to the key.165 fn collection_property(&self, key: string) -> Result<bytes> {166 let key = <Vec<u8>>::from(key)167 .try_into()168 .map_err(|_| "key too large")?;169170 let props = CollectionProperties::<T>::get(self.id);171 let prop = props.get(&key).ok_or("key not found")?;172173 Ok(bytes(prop.to_vec()))174 }175176 /// Get collection properties.177 ///178 /// @param keys Properties keys. Empty keys for all propertyes.179 /// @return Vector of properties key/value pairs.180 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {181 let keys = keys182 .into_iter()183 .map(|key| {184 <Vec<u8>>::from(key)185 .try_into()186 .map_err(|_| Error::Revert("key too large".into()))187 })188 .collect::<Result<Vec<_>>>()?;189190 let properties = Pallet::<T>::filter_collection_properties(191 self.id,192 if keys.is_empty() { None } else { Some(keys) },193 )194 .map_err(dispatch_to_evm::<T>)?;195196 let properties = properties197 .into_iter()198 .map(|p| {199 let key =200 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;201 let value = bytes(p.value.to_vec());202 Ok((key, value))203 })204 .collect::<Result<Vec<_>>>()?;205 Ok(properties)206 }207208 /// Set the sponsor of the collection.209 ///210 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.211 ///212 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.213 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {214 self.consume_store_reads_and_writes(1, 1)?;215216 check_is_owner_or_admin(caller, self)?;217218 let sponsor = T::CrossAccountId::from_eth(sponsor);219 self.set_sponsor(sponsor.as_sub().clone())220 .map_err(dispatch_to_evm::<T>)?;221 save(self)222 }223224 /// Set the sponsor of the collection.225 ///226 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.227 ///228 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.229 fn set_collection_sponsor_cross(230 &mut self,231 caller: caller,232 sponsor: (address, uint256),233 ) -> Result<void> {234 self.consume_store_reads_and_writes(1, 1)?;235236 check_is_owner_or_admin(caller, self)?;237238 let sponsor = convert_tuple_to_cross_account::<T>(sponsor)?;239 self.set_sponsor(sponsor.as_sub().clone())240 .map_err(dispatch_to_evm::<T>)?;241 save(self)242 }243244 /// Whether there is a pending sponsor.245 fn has_collection_pending_sponsor(&self) -> Result<bool> {246 Ok(matches!(247 self.collection.sponsorship,248 SponsorshipState::Unconfirmed(_)249 ))250 }251252 /// Collection sponsorship confirmation.253 ///254 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.255 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {256 self.consume_store_writes(1)?;257258 let caller = T::CrossAccountId::from_eth(caller);259 if !self260 .confirm_sponsorship(caller.as_sub())261 .map_err(dispatch_to_evm::<T>)?262 {263 return Err("caller is not set as sponsor".into());264 }265 save(self)266 }267268 /// Remove collection sponsor.269 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {270 self.consume_store_reads_and_writes(1, 1)?;271 check_is_owner_or_admin(caller, self)?;272 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;273 save(self)274 }275276 /// Get current sponsor.277 ///278 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.279 fn collection_sponsor(&self) -> Result<(address, uint256)> {280 let sponsor = match self.collection.sponsorship.sponsor() {281 Some(sponsor) => sponsor,282 None => return Ok(Default::default()),283 };284 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());285 let result: (address, uint256) = if sponsor.is_canonical_substrate() {286 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);287 (Default::default(), sponsor)288 } else {289 let sponsor = *sponsor.as_eth();290 (sponsor, Default::default())291 };292 Ok(result)293 }294295 /// Set limits for the collection.296 /// @dev Throws error if limit not found.297 /// @param limit Name of the limit. Valid names:298 /// "accountTokenOwnershipLimit",299 /// "sponsoredDataSize",300 /// "sponsoredDataRateLimit",301 /// "tokenLimit",302 /// "sponsorTransferTimeout",303 /// "sponsorApproveTimeout"304 /// @param value Value of the limit.305 #[solidity(rename_selector = "setCollectionLimit")]306 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {307 self.consume_store_reads_and_writes(1, 1)?;308309 check_is_owner_or_admin(caller, self)?;310 let mut limits = self.limits.clone();311312 match limit.as_str() {313 "accountTokenOwnershipLimit" => {314 limits.account_token_ownership_limit = Some(value);315 }316 "sponsoredDataSize" => {317 limits.sponsored_data_size = Some(value);318 }319 "sponsoredDataRateLimit" => {320 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));321 }322 "tokenLimit" => {323 limits.token_limit = Some(value);324 }325 "sponsorTransferTimeout" => {326 limits.sponsor_transfer_timeout = Some(value);327 }328 "sponsorApproveTimeout" => {329 limits.sponsor_approve_timeout = Some(value);330 }331 _ => {332 return Err(Error::Revert(format!(333 "unknown integer limit \"{}\"",334 limit335 )))336 }337 }338 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)339 .map_err(dispatch_to_evm::<T>)?;340 save(self)341 }342343 /// Set limits for the collection.344 /// @dev Throws error if limit not found.345 /// @param limit Name of the limit. Valid names:346 /// "ownerCanTransfer",347 /// "ownerCanDestroy",348 /// "transfersEnabled"349 /// @param value Value of the limit.350 #[solidity(rename_selector = "setCollectionLimit")]351 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {352 self.consume_store_reads_and_writes(1, 1)?;353354 check_is_owner_or_admin(caller, self)?;355 let mut limits = self.limits.clone();356357 match limit.as_str() {358 "ownerCanTransfer" => {359 limits.owner_can_transfer = Some(value);360 }361 "ownerCanDestroy" => {362 limits.owner_can_destroy = Some(value);363 }364 "transfersEnabled" => {365 limits.transfers_enabled = Some(value);366 }367 _ => {368 return Err(Error::Revert(format!(369 "unknown boolean limit \"{}\"",370 limit371 )))372 }373 }374 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)375 .map_err(dispatch_to_evm::<T>)?;376 save(self)377 }378379 /// Get contract address.380 fn contract_address(&self) -> Result<address> {381 Ok(crate::eth::collection_id_to_address(self.id))382 }383384 /// Add collection admin.385 /// @param newAdmin Cross account administrator address.386 fn add_collection_admin_cross(387 &mut self,388 caller: caller,389 new_admin: (address, uint256),390 ) -> Result<void> {391 self.consume_store_writes(2)?;392393 let caller = T::CrossAccountId::from_eth(caller);394 let new_admin = convert_tuple_to_cross_account::<T>(new_admin)?;395 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;396 Ok(())397 }398399 /// Remove collection admin.400 /// @param admin Cross account administrator address.401 fn remove_collection_admin_cross(402 &mut self,403 caller: caller,404 admin: (address, uint256),405 ) -> Result<void> {406 self.consume_store_writes(2)?;407408 let caller = T::CrossAccountId::from_eth(caller);409 let admin = convert_tuple_to_cross_account::<T>(admin)?;410 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;411 Ok(())412 }413414 /// Add collection admin.415 /// @param newAdmin Address of the added administrator.416 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {417 self.consume_store_writes(2)?;418419 let caller = T::CrossAccountId::from_eth(caller);420 let new_admin = T::CrossAccountId::from_eth(new_admin);421 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;422 Ok(())423 }424425 /// Remove collection admin.426 ///427 /// @param admin Address of the removed administrator.428 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {429 self.consume_store_writes(2)?;430431 let caller = T::CrossAccountId::from_eth(caller);432 let admin = T::CrossAccountId::from_eth(admin);433 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;434 Ok(())435 }436437 /// Toggle accessibility of collection nesting.438 ///439 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'440 #[solidity(rename_selector = "setCollectionNesting")]441 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {442 self.consume_store_reads_and_writes(1, 1)?;443444 check_is_owner_or_admin(caller, self)?;445446 let mut permissions = self.collection.permissions.clone();447 let mut nesting = permissions.nesting().clone();448 nesting.token_owner = enable;449 nesting.restricted = None;450 permissions.nesting = Some(nesting);451452 self.collection.permissions = <Pallet<T>>::clamp_permissions(453 self.collection.mode.clone(),454 &self.collection.permissions,455 permissions,456 )457 .map_err(dispatch_to_evm::<T>)?;458459 save(self)460 }461462 /// Toggle accessibility of collection nesting.463 ///464 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'465 /// @param collections Addresses of collections that will be available for nesting.466 #[solidity(rename_selector = "setCollectionNesting")]467 fn set_nesting(468 &mut self,469 caller: caller,470 enable: bool,471 collections: Vec<address>,472 ) -> Result<void> {473 self.consume_store_reads_and_writes(1, 1)?;474475 if collections.is_empty() {476 return Err("no addresses provided".into());477 }478 check_is_owner_or_admin(caller, self)?;479480 let mut permissions = self.collection.permissions.clone();481 match enable {482 false => {483 let mut nesting = permissions.nesting().clone();484 nesting.token_owner = false;485 nesting.restricted = None;486 permissions.nesting = Some(nesting);487 }488 true => {489 let mut bv = OwnerRestrictedSet::new();490 for i in collections {491 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {492 Error::Revert("Can't convert address into collection id".into())493 })?)494 .map_err(|_| "too many collections")?;495 }496 let mut nesting = permissions.nesting().clone();497 nesting.token_owner = true;498 nesting.restricted = Some(bv);499 permissions.nesting = Some(nesting);500 }501 };502503 self.collection.permissions = <Pallet<T>>::clamp_permissions(504 self.collection.mode.clone(),505 &self.collection.permissions,506 permissions,507 )508 .map_err(dispatch_to_evm::<T>)?;509510 save(self)511 }512513 /// Set the collection access method.514 /// @param mode Access mode515 /// 0 for Normal516 /// 1 for AllowList517 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {518 self.consume_store_reads_and_writes(1, 1)?;519520 check_is_owner_or_admin(caller, self)?;521 let permissions = CollectionPermissions {522 access: Some(match mode {523 0 => AccessMode::Normal,524 1 => AccessMode::AllowList,525 _ => return Err("not supported access mode".into()),526 }),527 ..Default::default()528 };529 self.collection.permissions = <Pallet<T>>::clamp_permissions(530 self.collection.mode.clone(),531 &self.collection.permissions,532 permissions,533 )534 .map_err(dispatch_to_evm::<T>)?;535536 save(self)537 }538539 /// Checks that user allowed to operate with collection.540 ///541 /// @param user User address to check.542 fn allowed(&self, user: address) -> Result<bool> {543 Ok(Pallet::<T>::allowed(544 self.id,545 T::CrossAccountId::from_eth(user),546 ))547 }548549 /// Add the user to the allowed list.550 ///551 /// @param user Address of a trusted user.552 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {553 self.consume_store_writes(1)?;554555 let caller = T::CrossAccountId::from_eth(caller);556 let user = T::CrossAccountId::from_eth(user);557 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;558 Ok(())559 }560561 /// Add user to allowed list.562 ///563 /// @param user User cross account address.564 fn add_to_collection_allow_list_cross(565 &mut self,566 caller: caller,567 user: (address, uint256),568 ) -> Result<void> {569 self.consume_store_writes(1)?;570571 let caller = T::CrossAccountId::from_eth(caller);572 let user = convert_tuple_to_cross_account::<T>(user)?;573 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;574 Ok(())575 }576577 /// Remove the user from the allowed list.578 ///579 /// @param user Address of a removed user.580 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {581 self.consume_store_writes(1)?;582583 let caller = T::CrossAccountId::from_eth(caller);584 let user = T::CrossAccountId::from_eth(user);585 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;586 Ok(())587 }588589 /// Remove user from allowed list.590 ///591 /// @param user User cross account address.592 fn remove_from_collection_allow_list_cross(593 &mut self,594 caller: caller,595 user: (address, uint256),596 ) -> Result<void> {597 self.consume_store_writes(1)?;598599 let caller = T::CrossAccountId::from_eth(caller);600 let user = convert_tuple_to_cross_account::<T>(user)?;601 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;602 Ok(())603 }604605 /// Switch permission for minting.606 ///607 /// @param mode Enable if "true".608 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {609 self.consume_store_reads_and_writes(1, 1)?;610611 check_is_owner_or_admin(caller, self)?;612 let permissions = CollectionPermissions {613 mint_mode: Some(mode),614 ..Default::default()615 };616 self.collection.permissions = <Pallet<T>>::clamp_permissions(617 self.collection.mode.clone(),618 &self.collection.permissions,619 permissions,620 )621 .map_err(dispatch_to_evm::<T>)?;622623 save(self)624 }625626 /// Check that account is the owner or admin of the collection627 ///628 /// @param user account to verify629 /// @return "true" if account is the owner or admin630 #[solidity(rename_selector = "isOwnerOrAdmin")]631 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {632 let user = T::CrossAccountId::from_eth(user);633 Ok(self.is_owner_or_admin(&user))634 }635636 /// Check that account is the owner or admin of the collection637 ///638 /// @param user User cross account to verify639 /// @return "true" if account is the owner or admin640 fn is_owner_or_admin_cross(&self, user: (address, uint256)) -> Result<bool> {641 let user = convert_tuple_to_cross_account::<T>(user)?;642 Ok(self.is_owner_or_admin(&user))643 }644645 /// Returns collection type646 ///647 /// @return `Fungible` or `NFT` or `ReFungible`648 fn unique_collection_type(&self) -> Result<string> {649 let mode = match self.collection.mode {650 CollectionMode::Fungible(_) => "Fungible",651 CollectionMode::NFT => "NFT",652 CollectionMode::ReFungible => "ReFungible",653 };654 Ok(mode.into())655 }656657 /// Get collection owner.658 ///659 /// @return Tuble with sponsor address and his substrate mirror.660 /// If address is canonical then substrate mirror is zero and vice versa.661 fn collection_owner(&self) -> Result<(address, uint256)> {662 Ok(convert_cross_account_to_tuple::<T>(663 &T::CrossAccountId::from_sub(self.owner.clone()),664 ))665 }666667 /// Changes collection owner to another account668 ///669 /// @dev Owner can be changed only by current owner670 /// @param newOwner new owner account671 #[solidity(rename_selector = "changeCollectionOwner")]672 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {673 self.consume_store_writes(1)?;674675 let caller = T::CrossAccountId::from_eth(caller);676 let new_owner = T::CrossAccountId::from_eth(new_owner);677 self.set_owner_internal(caller, new_owner)678 .map_err(dispatch_to_evm::<T>)679 }680681 /// Get collection administrators682 ///683 /// @return Vector of tuples with admins address and his substrate mirror.684 /// If address is canonical then substrate mirror is zero and vice versa.685 fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {686 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))687 .map(|(admin, _)| crate::eth::convert_cross_account_to_tuple::<T>(&admin))688 .collect();689 Ok(result)690 }691692 /// Changes collection owner to another account693 ///694 /// @dev Owner can be changed only by current owner695 /// @param newOwner new owner cross account696 fn set_owner_cross(&mut self, caller: caller, new_owner: (address, uint256)) -> Result<void> {697 self.consume_store_writes(1)?;698699 let caller = T::CrossAccountId::from_eth(caller);700 let new_owner = convert_tuple_to_cross_account::<T>(new_owner)?;701 self.set_owner_internal(caller, new_owner)702 .map_err(dispatch_to_evm::<T>)703 }704}705706/// ### Note707/// Do not forget to add: `self.consume_store_reads(1)?;`708fn check_is_owner_or_admin<T: Config>(709 caller: caller,710 collection: &CollectionHandle<T>,711) -> Result<T::CrossAccountId> {712 let caller = T::CrossAccountId::from_eth(caller);713 collection714 .check_is_owner_or_admin(&caller)715 .map_err(dispatch_to_evm::<T>)?;716 Ok(caller)717}718719/// ### Note720/// Do not forget to add: `self.consume_store_writes(1)?;`721fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {722 collection723 .check_is_internal()724 .map_err(dispatch_to_evm::<T>)?;725 collection.save().map_err(dispatch_to_evm::<T>)?;726 Ok(())727}728729/// Contains static property keys and values.730pub mod static_property {731 use evm_coder::{732 execution::{Result, Error},733 };734 use alloc::format;735736 const EXPECT_CONVERT_ERROR: &str = "length < limit";737738 /// Keys.739 pub mod key {740 use super::*;741742 /// Key "baseURI".743 pub fn base_uri() -> up_data_structs::PropertyKey {744 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)745 }746747 /// Key "url".748 pub fn url() -> up_data_structs::PropertyKey {749 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)750 }751752 /// Key "suffix".753 pub fn suffix() -> up_data_structs::PropertyKey {754 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)755 }756757 /// Key "parentNft".758 pub fn parent_nft() -> up_data_structs::PropertyKey {759 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)760 }761 }762763 /// Convert `byte` to [`PropertyKey`].764 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {765 bytes.to_vec().try_into().map_err(|_| {766 Error::Revert(format!(767 "Property key is too long. Max length is {}.",768 up_data_structs::PropertyKey::bound()769 ))770 })771 }772773 /// Convert `bytes` to [`PropertyValue`].774 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {775 bytes.to_vec().try_into().map_err(|_| {776 Error::Revert(format!(777 "Property key is too long. Max length is {}.",778 up_data_structs::PropertyKey::bound()779 ))780 })781 }782}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 solidity_interface, solidity, ToLog,21 types::*,22 execution::{Result, Error},23 weight,24 custom_signature::{FunctionName, FunctionSignature},25 make_signature,26};27pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::vec::Vec;30use up_data_structs::{31 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,32 SponsoringRateLimit, SponsorshipState,33};34use alloc::format;3536use crate::{37 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,38 eth::{39 convert_cross_account_to_uint256, convert_cross_account_to_tuple,40 convert_tuple_to_cross_account,41 },42 weights::WeightInfo,43};4445/// Events for ethereum collection helper.46#[derive(ToLog)]47pub enum CollectionHelpersEvents {48 /// The collection has been created.49 CollectionCreated {50 /// Collection owner.51 #[indexed]52 owner: address,5354 /// Collection ID.55 #[indexed]56 collection_id: address,57 },58 /// The collection has been destroyed.59 CollectionDestroyed {60 /// Collection ID.61 #[indexed]62 collection_id: address,63 },64}6566/// Does not always represent a full collection, for RFT it is either67/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).68pub trait CommonEvmHandler {69 const CODE: &'static [u8];7071 /// Call precompiled handle.72 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;73}7475/// @title A contract that allows you to work with collections.76#[solidity_interface(name = Collection)]77impl<T: Config> CollectionHandle<T>78where79 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,80{81 /// Set collection property.82 ///83 /// @param key Property key.84 /// @param value Propery value.85 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]86 fn set_collection_property(87 &mut self,88 caller: caller,89 key: string,90 value: bytes,91 ) -> Result<void> {92 let caller = T::CrossAccountId::from_eth(caller);93 let key = <Vec<u8>>::from(key)94 .try_into()95 .map_err(|_| "key too large")?;96 let value = value.0.try_into().map_err(|_| "value too large")?;9798 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })99 .map_err(dispatch_to_evm::<T>)100 }101102 /// Set collection properties.103 ///104 /// @param properties Vector of properties key/value pair.105 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]106 fn set_collection_properties(107 &mut self,108 caller: caller,109 properties: Vec<(string, bytes)>,110 ) -> Result<void> {111 let caller = T::CrossAccountId::from_eth(caller);112113 let properties = properties114 .into_iter()115 .map(|(key, value)| {116 let key = <Vec<u8>>::from(key)117 .try_into()118 .map_err(|_| "key too large")?;119120 let value = value.0.try_into().map_err(|_| "value too large")?;121122 Ok(Property { key, value })123 })124 .collect::<Result<Vec<_>>>()?;125126 <Pallet<T>>::set_collection_properties(self, &caller, properties)127 .map_err(dispatch_to_evm::<T>)128 }129130 /// Delete collection property.131 ///132 /// @param key Property key.133 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]134 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {135 let caller = T::CrossAccountId::from_eth(caller);136 let key = <Vec<u8>>::from(key)137 .try_into()138 .map_err(|_| "key too large")?;139140 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)141 }142143 /// Delete collection properties.144 ///145 /// @param keys Properties keys.146 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]147 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {148 let caller = T::CrossAccountId::from_eth(caller);149 let keys = keys150 .into_iter()151 .map(|key| {152 <Vec<u8>>::from(key)153 .try_into()154 .map_err(|_| Error::Revert("key too large".into()))155 })156 .collect::<Result<Vec<_>>>()?;157158 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)159 }160161 /// Get collection property.162 ///163 /// @dev Throws error if key not found.164 ///165 /// @param key Property key.166 /// @return bytes The property corresponding to the key.167 fn collection_property(&self, key: string) -> Result<bytes> {168 let key = <Vec<u8>>::from(key)169 .try_into()170 .map_err(|_| "key too large")?;171172 let props = CollectionProperties::<T>::get(self.id);173 let prop = props.get(&key).ok_or("key not found")?;174175 Ok(bytes(prop.to_vec()))176 }177178 /// Get collection properties.179 ///180 /// @param keys Properties keys. Empty keys for all propertyes.181 /// @return Vector of properties key/value pairs.182 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {183 let keys = keys184 .into_iter()185 .map(|key| {186 <Vec<u8>>::from(key)187 .try_into()188 .map_err(|_| Error::Revert("key too large".into()))189 })190 .collect::<Result<Vec<_>>>()?;191192 let properties = Pallet::<T>::filter_collection_properties(193 self.id,194 if keys.is_empty() { None } else { Some(keys) },195 )196 .map_err(dispatch_to_evm::<T>)?;197198 let properties = properties199 .into_iter()200 .map(|p| {201 let key =202 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;203 let value = bytes(p.value.to_vec());204 Ok((key, value))205 })206 .collect::<Result<Vec<_>>>()?;207 Ok(properties)208 }209210 /// Set the sponsor of the collection.211 ///212 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.213 ///214 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.215 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {216 self.consume_store_reads_and_writes(1, 1)?;217218 check_is_owner_or_admin(caller, self)?;219220 let sponsor = T::CrossAccountId::from_eth(sponsor);221 self.set_sponsor(sponsor.as_sub().clone())222 .map_err(dispatch_to_evm::<T>)?;223 save(self)224 }225226 /// Set the sponsor of the collection.227 ///228 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.229 ///230 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.231 fn set_collection_sponsor_cross(232 &mut self,233 caller: caller,234 sponsor: (address, uint256),235 ) -> Result<void> {236 self.consume_store_reads_and_writes(1, 1)?;237238 check_is_owner_or_admin(caller, self)?;239240 let sponsor = convert_tuple_to_cross_account::<T>(sponsor)?;241 self.set_sponsor(sponsor.as_sub().clone())242 .map_err(dispatch_to_evm::<T>)?;243 save(self)244 }245246 /// Whether there is a pending sponsor.247 fn has_collection_pending_sponsor(&self) -> Result<bool> {248 Ok(matches!(249 self.collection.sponsorship,250 SponsorshipState::Unconfirmed(_)251 ))252 }253254 /// Collection sponsorship confirmation.255 ///256 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.257 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {258 self.consume_store_writes(1)?;259260 let caller = T::CrossAccountId::from_eth(caller);261 if !self262 .confirm_sponsorship(caller.as_sub())263 .map_err(dispatch_to_evm::<T>)?264 {265 return Err("caller is not set as sponsor".into());266 }267 save(self)268 }269270 /// Remove collection sponsor.271 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {272 self.consume_store_reads_and_writes(1, 1)?;273 check_is_owner_or_admin(caller, self)?;274 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;275 save(self)276 }277278 /// Get current sponsor.279 ///280 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.281 fn collection_sponsor(&self) -> Result<(address, uint256)> {282 let sponsor = match self.collection.sponsorship.sponsor() {283 Some(sponsor) => sponsor,284 None => return Ok(Default::default()),285 };286 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());287 let result: (address, uint256) = if sponsor.is_canonical_substrate() {288 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);289 (Default::default(), sponsor)290 } else {291 let sponsor = *sponsor.as_eth();292 (sponsor, Default::default())293 };294 Ok(result)295 }296297 /// Set limits for the collection.298 /// @dev Throws error if limit not found.299 /// @param limit Name of the limit. Valid names:300 /// "accountTokenOwnershipLimit",301 /// "sponsoredDataSize",302 /// "sponsoredDataRateLimit",303 /// "tokenLimit",304 /// "sponsorTransferTimeout",305 /// "sponsorApproveTimeout"306 /// @param value Value of the limit.307 #[solidity(rename_selector = "setCollectionLimit")]308 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {309 self.consume_store_reads_and_writes(1, 1)?;310311 check_is_owner_or_admin(caller, self)?;312 let mut limits = self.limits.clone();313314 match limit.as_str() {315 "accountTokenOwnershipLimit" => {316 limits.account_token_ownership_limit = Some(value);317 }318 "sponsoredDataSize" => {319 limits.sponsored_data_size = Some(value);320 }321 "sponsoredDataRateLimit" => {322 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));323 }324 "tokenLimit" => {325 limits.token_limit = Some(value);326 }327 "sponsorTransferTimeout" => {328 limits.sponsor_transfer_timeout = Some(value);329 }330 "sponsorApproveTimeout" => {331 limits.sponsor_approve_timeout = Some(value);332 }333 _ => {334 return Err(Error::Revert(format!(335 "unknown integer limit \"{}\"",336 limit337 )))338 }339 }340 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)341 .map_err(dispatch_to_evm::<T>)?;342 save(self)343 }344345 /// Set limits for the collection.346 /// @dev Throws error if limit not found.347 /// @param limit Name of the limit. Valid names:348 /// "ownerCanTransfer",349 /// "ownerCanDestroy",350 /// "transfersEnabled"351 /// @param value Value of the limit.352 #[solidity(rename_selector = "setCollectionLimit")]353 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {354 self.consume_store_reads_and_writes(1, 1)?;355356 check_is_owner_or_admin(caller, self)?;357 let mut limits = self.limits.clone();358359 match limit.as_str() {360 "ownerCanTransfer" => {361 limits.owner_can_transfer = Some(value);362 }363 "ownerCanDestroy" => {364 limits.owner_can_destroy = Some(value);365 }366 "transfersEnabled" => {367 limits.transfers_enabled = Some(value);368 }369 _ => {370 return Err(Error::Revert(format!(371 "unknown boolean limit \"{}\"",372 limit373 )))374 }375 }376 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)377 .map_err(dispatch_to_evm::<T>)?;378 save(self)379 }380381 /// Get contract address.382 fn contract_address(&self) -> Result<address> {383 Ok(crate::eth::collection_id_to_address(self.id))384 }385386 /// Add collection admin.387 /// @param newAdmin Cross account administrator address.388 fn add_collection_admin_cross(389 &mut self,390 caller: caller,391 new_admin: (address, uint256),392 ) -> Result<void> {393 self.consume_store_writes(2)?;394395 let caller = T::CrossAccountId::from_eth(caller);396 let new_admin = convert_tuple_to_cross_account::<T>(new_admin)?;397 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;398 Ok(())399 }400401 /// Remove collection admin.402 /// @param admin Cross account administrator address.403 fn remove_collection_admin_cross(404 &mut self,405 caller: caller,406 admin: (address, uint256),407 ) -> Result<void> {408 self.consume_store_writes(2)?;409410 let caller = T::CrossAccountId::from_eth(caller);411 let admin = convert_tuple_to_cross_account::<T>(admin)?;412 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;413 Ok(())414 }415416 /// Add collection admin.417 /// @param newAdmin Address of the added administrator.418 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {419 self.consume_store_writes(2)?;420421 let caller = T::CrossAccountId::from_eth(caller);422 let new_admin = T::CrossAccountId::from_eth(new_admin);423 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;424 Ok(())425 }426427 /// Remove collection admin.428 ///429 /// @param admin Address of the removed administrator.430 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {431 self.consume_store_writes(2)?;432433 let caller = T::CrossAccountId::from_eth(caller);434 let admin = T::CrossAccountId::from_eth(admin);435 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;436 Ok(())437 }438439 /// Toggle accessibility of collection nesting.440 ///441 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'442 #[solidity(rename_selector = "setCollectionNesting")]443 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {444 self.consume_store_reads_and_writes(1, 1)?;445446 check_is_owner_or_admin(caller, self)?;447448 let mut permissions = self.collection.permissions.clone();449 let mut nesting = permissions.nesting().clone();450 nesting.token_owner = enable;451 nesting.restricted = None;452 permissions.nesting = Some(nesting);453454 self.collection.permissions = <Pallet<T>>::clamp_permissions(455 self.collection.mode.clone(),456 &self.collection.permissions,457 permissions,458 )459 .map_err(dispatch_to_evm::<T>)?;460461 save(self)462 }463464 /// Toggle accessibility of collection nesting.465 ///466 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'467 /// @param collections Addresses of collections that will be available for nesting.468 #[solidity(rename_selector = "setCollectionNesting")]469 fn set_nesting(470 &mut self,471 caller: caller,472 enable: bool,473 collections: Vec<address>,474 ) -> Result<void> {475 self.consume_store_reads_and_writes(1, 1)?;476477 if collections.is_empty() {478 return Err("no addresses provided".into());479 }480 check_is_owner_or_admin(caller, self)?;481482 let mut permissions = self.collection.permissions.clone();483 match enable {484 false => {485 let mut nesting = permissions.nesting().clone();486 nesting.token_owner = false;487 nesting.restricted = None;488 permissions.nesting = Some(nesting);489 }490 true => {491 let mut bv = OwnerRestrictedSet::new();492 for i in collections {493 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {494 Error::Revert("Can't convert address into collection id".into())495 })?)496 .map_err(|_| "too many collections")?;497 }498 let mut nesting = permissions.nesting().clone();499 nesting.token_owner = true;500 nesting.restricted = Some(bv);501 permissions.nesting = Some(nesting);502 }503 };504505 self.collection.permissions = <Pallet<T>>::clamp_permissions(506 self.collection.mode.clone(),507 &self.collection.permissions,508 permissions,509 )510 .map_err(dispatch_to_evm::<T>)?;511512 save(self)513 }514515 /// Set the collection access method.516 /// @param mode Access mode517 /// 0 for Normal518 /// 1 for AllowList519 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {520 self.consume_store_reads_and_writes(1, 1)?;521522 check_is_owner_or_admin(caller, self)?;523 let permissions = CollectionPermissions {524 access: Some(match mode {525 0 => AccessMode::Normal,526 1 => AccessMode::AllowList,527 _ => return Err("not supported access mode".into()),528 }),529 ..Default::default()530 };531 self.collection.permissions = <Pallet<T>>::clamp_permissions(532 self.collection.mode.clone(),533 &self.collection.permissions,534 permissions,535 )536 .map_err(dispatch_to_evm::<T>)?;537538 save(self)539 }540541 /// Checks that user allowed to operate with collection.542 ///543 /// @param user User address to check.544 fn allowed(&self, user: address) -> Result<bool> {545 Ok(Pallet::<T>::allowed(546 self.id,547 T::CrossAccountId::from_eth(user),548 ))549 }550551 /// Add the user to the allowed list.552 ///553 /// @param user Address of a trusted user.554 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {555 self.consume_store_writes(1)?;556557 let caller = T::CrossAccountId::from_eth(caller);558 let user = T::CrossAccountId::from_eth(user);559 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;560 Ok(())561 }562563 /// Add user to allowed list.564 ///565 /// @param user User cross account address.566 fn add_to_collection_allow_list_cross(567 &mut self,568 caller: caller,569 user: (address, uint256),570 ) -> Result<void> {571 self.consume_store_writes(1)?;572573 let caller = T::CrossAccountId::from_eth(caller);574 let user = convert_tuple_to_cross_account::<T>(user)?;575 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;576 Ok(())577 }578579 /// Remove the user from the allowed list.580 ///581 /// @param user Address of a removed user.582 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {583 self.consume_store_writes(1)?;584585 let caller = T::CrossAccountId::from_eth(caller);586 let user = T::CrossAccountId::from_eth(user);587 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;588 Ok(())589 }590591 /// Remove user from allowed list.592 ///593 /// @param user User cross account address.594 fn remove_from_collection_allow_list_cross(595 &mut self,596 caller: caller,597 user: (address, uint256),598 ) -> Result<void> {599 self.consume_store_writes(1)?;600601 let caller = T::CrossAccountId::from_eth(caller);602 let user = convert_tuple_to_cross_account::<T>(user)?;603 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;604 Ok(())605 }606607 /// Switch permission for minting.608 ///609 /// @param mode Enable if "true".610 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {611 self.consume_store_reads_and_writes(1, 1)?;612613 check_is_owner_or_admin(caller, self)?;614 let permissions = CollectionPermissions {615 mint_mode: Some(mode),616 ..Default::default()617 };618 self.collection.permissions = <Pallet<T>>::clamp_permissions(619 self.collection.mode.clone(),620 &self.collection.permissions,621 permissions,622 )623 .map_err(dispatch_to_evm::<T>)?;624625 save(self)626 }627628 /// Check that account is the owner or admin of the collection629 ///630 /// @param user account to verify631 /// @return "true" if account is the owner or admin632 #[solidity(rename_selector = "isOwnerOrAdmin")]633 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {634 let user = T::CrossAccountId::from_eth(user);635 Ok(self.is_owner_or_admin(&user))636 }637638 /// Check that account is the owner or admin of the collection639 ///640 /// @param user User cross account to verify641 /// @return "true" if account is the owner or admin642 fn is_owner_or_admin_cross(&self, user: (address, uint256)) -> Result<bool> {643 let user = convert_tuple_to_cross_account::<T>(user)?;644 Ok(self.is_owner_or_admin(&user))645 }646647 /// Returns collection type648 ///649 /// @return `Fungible` or `NFT` or `ReFungible`650 fn unique_collection_type(&self) -> Result<string> {651 let mode = match self.collection.mode {652 CollectionMode::Fungible(_) => "Fungible",653 CollectionMode::NFT => "NFT",654 CollectionMode::ReFungible => "ReFungible",655 };656 Ok(mode.into())657 }658659 /// Get collection owner.660 ///661 /// @return Tuble with sponsor address and his substrate mirror.662 /// If address is canonical then substrate mirror is zero and vice versa.663 fn collection_owner(&self) -> Result<(address, uint256)> {664 Ok(convert_cross_account_to_tuple::<T>(665 &T::CrossAccountId::from_sub(self.owner.clone()),666 ))667 }668669 /// Changes collection owner to another account670 ///671 /// @dev Owner can be changed only by current owner672 /// @param newOwner new owner account673 #[solidity(rename_selector = "changeCollectionOwner")]674 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {675 self.consume_store_writes(1)?;676677 let caller = T::CrossAccountId::from_eth(caller);678 let new_owner = T::CrossAccountId::from_eth(new_owner);679 self.set_owner_internal(caller, new_owner)680 .map_err(dispatch_to_evm::<T>)681 }682683 /// Get collection administrators684 ///685 /// @return Vector of tuples with admins address and his substrate mirror.686 /// If address is canonical then substrate mirror is zero and vice versa.687 fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {688 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))689 .map(|(admin, _)| crate::eth::convert_cross_account_to_tuple::<T>(&admin))690 .collect();691 Ok(result)692 }693694 /// Changes collection owner to another account695 ///696 /// @dev Owner can be changed only by current owner697 /// @param newOwner new owner cross account698 fn set_owner_cross(&mut self, caller: caller, new_owner: (address, uint256)) -> Result<void> {699 self.consume_store_writes(1)?;700701 let caller = T::CrossAccountId::from_eth(caller);702 let new_owner = convert_tuple_to_cross_account::<T>(new_owner)?;703 self.set_owner_internal(caller, new_owner)704 .map_err(dispatch_to_evm::<T>)705 }706}707708/// ### Note709/// Do not forget to add: `self.consume_store_reads(1)?;`710fn check_is_owner_or_admin<T: Config>(711 caller: caller,712 collection: &CollectionHandle<T>,713) -> Result<T::CrossAccountId> {714 let caller = T::CrossAccountId::from_eth(caller);715 collection716 .check_is_owner_or_admin(&caller)717 .map_err(dispatch_to_evm::<T>)?;718 Ok(caller)719}720721/// ### Note722/// Do not forget to add: `self.consume_store_writes(1)?;`723fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {724 collection725 .check_is_internal()726 .map_err(dispatch_to_evm::<T>)?;727 collection.save().map_err(dispatch_to_evm::<T>)?;728 Ok(())729}730731/// Contains static property keys and values.732pub mod static_property {733 use evm_coder::{734 execution::{Result, Error},735 };736 use alloc::format;737738 const EXPECT_CONVERT_ERROR: &str = "length < limit";739740 /// Keys.741 pub mod key {742 use super::*;743744 /// Key "baseURI".745 pub fn base_uri() -> up_data_structs::PropertyKey {746 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)747 }748749 /// Key "url".750 pub fn url() -> up_data_structs::PropertyKey {751 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)752 }753754 /// Key "suffix".755 pub fn suffix() -> up_data_structs::PropertyKey {756 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)757 }758759 /// Key "parentNft".760 pub fn parent_nft() -> up_data_structs::PropertyKey {761 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)762 }763 }764765 /// Convert `byte` to [`PropertyKey`].766 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {767 bytes.to_vec().try_into().map_err(|_| {768 Error::Revert(format!(769 "Property key is too long. Max length is {}.",770 up_data_structs::PropertyKey::bound()771 ))772 })773 }774775 /// Convert `bytes` to [`PropertyValue`].776 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {777 bytes.to_vec().try_into().map_err(|_| {778 Error::Revert(format!(779 "Property key is too long. Max length is {}.",780 up_data_structs::PropertyKey::bound()781 ))782 })783 }784}pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -20,7 +20,13 @@
use alloc::{format, string::ToString};
use core::marker::PhantomData;
use evm_coder::{
- abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,
+ abi::AbiWriter,
+ execution::Result,
+ generate_stubgen, solidity_interface,
+ types::*,
+ ToLog,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
};
use pallet_evm::{
ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -20,7 +20,15 @@
use alloc::{format, string::ToString};
use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
use core::convert::TryInto;
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use evm_coder::{
+ ToLog,
+ execution::*,
+ generate_stubgen, solidity_interface,
+ types::*,
+ weight,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
+};
use pallet_common::eth::convert_tuple_to_cross_account;
use up_data_structs::CollectionMode;
use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -25,7 +25,15 @@
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ ToLog,
+ execution::*,
+ generate_stubgen, solidity, solidity_interface,
+ types::*,
+ weight,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
+};
use frame_support::BoundedVec;
use up_data_structs::{
TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -104,7 +104,11 @@
}
/// @title A contract that allows you to work with collections.
+<<<<<<< HEAD
/// @dev the ERC-165 identifier for this interface is 0xb3152af3
+=======
+/// @dev the ERC-165 identifier for this interface is 0x674be726
+>>>>>>> feat: Add custum signature with unlimited nesting.
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -198,7 +202,7 @@
/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
///
/// @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,
+ /// @dev EVM selector for this function is: 0x403e96a7,
/// or in textual repr: setCollectionSponsorCross((address,uint256))
function setCollectionSponsorCross(Tuple6 memory sponsor) public {
require(false, stub_error);
@@ -239,6 +243,7 @@
/// @dev EVM selector for this function is: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
<<<<<<< HEAD
+<<<<<<< HEAD
function collectionSponsor() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
@@ -249,6 +254,12 @@
dummy;
return Tuple19(0x0000000000000000000000000000000000000000, 0);
>>>>>>> feat: add `EthCrossAccount` type
+=======
+ function collectionSponsor() public view returns (Tuple8 memory) {
+ require(false, stub_error);
+ dummy;
+ return Tuple8(0x0000000000000000000000000000000000000000, 0);
+>>>>>>> feat: Add custum signature with unlimited nesting.
}
/// Set limits for the collection.
@@ -297,7 +308,7 @@
/// Add collection admin.
/// @param newAdmin Cross account administrator address.
- /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// @dev EVM selector for this function is: 0x62e3c7c2,
/// or in textual repr: addCollectionAdminCross((address,uint256))
function addCollectionAdminCross(Tuple6 memory newAdmin) public {
require(false, stub_error);
@@ -307,7 +318,7 @@
/// Remove collection admin.
/// @param admin Cross account administrator address.
- /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// @dev EVM selector for this function is: 0x810d1503,
/// or in textual repr: removeCollectionAdminCross((address,uint256))
function removeCollectionAdminCross(Tuple6 memory admin) public {
require(false, stub_error);
@@ -351,7 +362,7 @@
///
/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
/// @param collections Addresses of collections that will be available for nesting.
- /// @dev EVM selector for this function is: 0x64872396,
+ /// @dev EVM selector for this function is: 0x112d4586,
/// or in textual repr: setCollectionNesting(bool,address[])
function setCollectionNesting(bool enable, address[] memory collections) public {
require(false, stub_error);
@@ -398,7 +409,7 @@
/// Add user to allowed list.
///
/// @param user User cross account address.
- /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// @dev EVM selector for this function is: 0xf074da88,
/// or in textual repr: addToCollectionAllowListCross((address,uint256))
function addToCollectionAllowListCross(Tuple6 memory user) public {
require(false, stub_error);
@@ -420,7 +431,7 @@
/// Remove user from allowed list.
///
/// @param user User cross account address.
- /// @dev EVM selector for this function is: 0x09ba452a,
+ /// @dev EVM selector for this function is: 0xc00df45c,
/// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
function removeFromCollectionAllowListCross(Tuple6 memory user) public {
require(false, stub_error);
@@ -456,7 +467,7 @@
///
/// @param user User cross account to verify
/// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x3e75a905,
+ /// @dev EVM selector for this function is: 0x5aba3351,
/// or in textual repr: isOwnerOrAdminCross((address,uint256))
function isOwnerOrAdminCross(Tuple6 memory user) public view returns (bool) {
require(false, stub_error);
@@ -483,6 +494,7 @@
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
<<<<<<< HEAD
+<<<<<<< HEAD
function collectionOwner() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
@@ -493,6 +505,12 @@
dummy;
return Tuple19(0x0000000000000000000000000000000000000000, 0);
>>>>>>> feat: add `EthCrossAccount` type
+=======
+ function collectionOwner() public view returns (Tuple8 memory) {
+ require(false, stub_error);
+ dummy;
+ return Tuple8(0x0000000000000000000000000000000000000000, 0);
+>>>>>>> feat: Add custum signature with unlimited nesting.
}
/// Changes collection owner to another account
@@ -523,7 +541,7 @@
///
/// @dev Owner can be changed only by current owner
/// @param newOwner new owner cross account
- /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// @dev EVM selector for this function is: 0xbdff793d,
/// or in textual repr: setOwnerCross((address,uint256))
function setOwnerCross(Tuple6 memory newOwner) public {
require(false, stub_error);
@@ -532,6 +550,7 @@
}
}
+<<<<<<< HEAD
/// @dev anonymous struct
struct Tuple19 {
address field_0;
@@ -581,6 +600,8 @@
}
}
+=======
+>>>>>>> feat: Add custum signature with unlimited nesting.
/// @title ERC721 Token that can be irreversibly burned (destroyed).
/// @dev the ERC-165 identifier for this interface is 0x42966c68
contract ERC721Burnable is Dummy, ERC165 {
@@ -682,7 +703,11 @@
}
/// @title Unique extensions for ERC721.
+<<<<<<< HEAD
/// @dev the ERC-165 identifier for this interface is 0x244543ee
+=======
+/// @dev the ERC-165 identifier for this interface is 0xcc97cb35
+>>>>>>> feat: Add custum signature with unlimited nesting.
contract ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -708,7 +733,7 @@
/// operator of the current owner.
/// @param approved The new substrate address approved NFT controller
/// @param tokenId The NFT to approve
- /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// @dev EVM selector for this function is: 0x106fdb59,
/// or in textual repr: approveCross((address,uint256),uint256)
function approveCross(Tuple6 memory approved, uint256 tokenId) public {
require(false, stub_error);
@@ -738,10 +763,15 @@
/// @param to Cross acccount address of new owner
/// @param tokenId The NFT to transfer
/// @dev EVM selector for this function is: 0xd5cf430b,
- /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ /// or in textual repr: transferFromCross(EthCrossAccount,EthCrossAccount,uint256)
function transferFromCross(
+<<<<<<< HEAD
Tuple6 memory from,
Tuple6 memory to,
+=======
+ EthCrossAccount memory from,
+ EthCrossAccount memory to,
+>>>>>>> feat: Add custum signature with unlimited nesting.
uint256 tokenId
) public {
require(false, stub_error);
@@ -772,7 +802,7 @@
/// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
/// @param from The current owner of the NFT
/// @param tokenId The NFT to transfer
- /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// @dev EVM selector for this function is: 0xa8106d4a,
/// or in textual repr: burnFromCross((address,uint256),uint256)
function burnFromCross(Tuple6 memory from, uint256 tokenId) public {
require(false, stub_error);
@@ -819,10 +849,46 @@
// return false;
// }
+<<<<<<< HEAD
}
/// @dev anonymous struct
struct Tuple8 {
+=======
+ /// @notice Function to mint multiple tokens.
+ /// @dev `tokenIds` should be an array of consecutive numbers and first number
+ /// should be obtained with `nextTokenId` method
+ /// @param to The new owner
+ /// @param tokenIds IDs of the minted NFTs
+ /// @dev EVM selector for this function is: 0xf9d9a5a3,
+ /// or in textual repr: mintBulk(address,uint256[])
+ function mintBulk(address to, uint256[] memory tokenIds) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokenIds;
+ dummy = 0;
+ return false;
+ }
+
+ /// @notice Function to mint multiple tokens with the given tokenUris.
+ /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+ /// numbers and first number should be obtained with `nextTokenId` method
+ /// @param to The new owner
+ /// @param tokens array of pairs of token ID and token URI for minted tokens
+ /// @dev EVM selector for this function is: 0xfd4e2a99,
+ /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+ function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokens;
+ dummy = 0;
+ return false;
+ }
+}
+
+/// @dev anonymous struct
+struct Tuple12 {
+>>>>>>> feat: Add custum signature with unlimited nesting.
uint256 field_0;
string field_1;
}
@@ -840,6 +906,12 @@
>>>>>>> feat: add `EthCrossAccount` type
}
+/// @dev anonymous struct
+struct Tuple8 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -26,7 +26,15 @@
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ ToLog,
+ execution::*,
+ generate_stubgen, solidity, solidity_interface,
+ types::*,
+ weight,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
+};
use frame_support::{BoundedBTreeMap, BoundedVec};
use pallet_common::{
CollectionHandle, CollectionPropertyPermissions,
pallets/refungible/src/erc_token.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -29,7 +29,15 @@
convert::TryInto,
ops::Deref,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use evm_coder::{
+ ToLog,
+ execution::*,
+ generate_stubgen, solidity_interface,
+ types::*,
+ weight,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
+};
use pallet_common::{
CommonWeightInfo,
erc::{CommonEvmHandler, PrecompileResult},
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -18,7 +18,13 @@
use core::marker::PhantomData;
use ethereum as _;
-use evm_coder::{execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ execution::*,
+ generate_stubgen, solidity, solidity_interface,
+ types::*,
+ custom_signature::{FunctionName, FunctionSignature},
+ make_signature,
+, weight};
use frame_support::{traits::Get, storage::StorageNMap};
use crate::sp_api_hidden_includes_decl_storage::hidden_include::StorageDoubleMap;
use crate::Pallet;
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -70,7 +70,11 @@
}
/// @title A contract that allows you to work with collections.
+<<<<<<< HEAD
/// @dev the ERC-165 identifier for this interface is 0xb3152af3
+=======
+/// @dev the ERC-165 identifier for this interface is 0x674be726
+>>>>>>> feat: Add custum signature with unlimited nesting.
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -133,7 +137,7 @@
/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
///
/// @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,
+ /// @dev EVM selector for this function is: 0x403e96a7,
/// or in textual repr: setCollectionSponsorCross((address,uint256))
function setCollectionSponsorCross(Tuple6 memory sponsor) external;
@@ -193,13 +197,13 @@
/// Add collection admin.
/// @param newAdmin Cross account administrator address.
- /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// @dev EVM selector for this function is: 0x62e3c7c2,
/// or in textual repr: addCollectionAdminCross((address,uint256))
function addCollectionAdminCross(Tuple6 memory newAdmin) external;
/// Remove collection admin.
/// @param admin Cross account administrator address.
- /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// @dev EVM selector for this function is: 0x810d1503,
/// or in textual repr: removeCollectionAdminCross((address,uint256))
function removeCollectionAdminCross(Tuple6 memory admin) external;
@@ -227,7 +231,7 @@
///
/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
/// @param collections Addresses of collections that will be available for nesting.
- /// @dev EVM selector for this function is: 0x64872396,
+ /// @dev EVM selector for this function is: 0x112d4586,
/// or in textual repr: setCollectionNesting(bool,address[])
function setCollectionNesting(bool enable, address[] memory collections) external;
@@ -256,7 +260,7 @@
/// Add user to allowed list.
///
/// @param user User cross account address.
- /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// @dev EVM selector for this function is: 0xf074da88,
/// or in textual repr: addToCollectionAllowListCross((address,uint256))
function addToCollectionAllowListCross(Tuple6 memory user) external;
@@ -270,7 +274,7 @@
/// Remove user from allowed list.
///
/// @param user User cross account address.
- /// @dev EVM selector for this function is: 0x09ba452a,
+ /// @dev EVM selector for this function is: 0xc00df45c,
/// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
function removeFromCollectionAllowListCross(Tuple6 memory user) external;
@@ -293,7 +297,7 @@
///
/// @param user User cross account to verify
/// @return "true" if account is the owner or admin
- /// @dev EVM selector for this function is: 0x3e75a905,
+ /// @dev EVM selector for this function is: 0x5aba3351,
/// or in textual repr: isOwnerOrAdminCross((address,uint256))
function isOwnerOrAdminCross(Tuple6 memory user) external view returns (bool);
@@ -332,11 +336,12 @@
///
/// @dev Owner can be changed only by current owner
/// @param newOwner new owner cross account
- /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// @dev EVM selector for this function is: 0xbdff793d,
/// or in textual repr: setOwnerCross((address,uint256))
function setOwnerCross(Tuple6 memory newOwner) external;
}
+<<<<<<< HEAD
/// @dev anonymous struct
struct Tuple19 {
address field_0;
@@ -373,6 +378,8 @@
function tokenURI(uint256 tokenId) external view returns (string memory);
}
+=======
+>>>>>>> feat: Add custum signature with unlimited nesting.
/// @title ERC721 Token that can be irreversibly burned (destroyed).
/// @dev the ERC-165 identifier for this interface is 0x42966c68
interface ERC721Burnable is Dummy, ERC165 {
@@ -438,7 +445,11 @@
}
/// @title Unique extensions for ERC721.
+<<<<<<< HEAD
/// @dev the ERC-165 identifier for this interface is 0x244543ee
+=======
+/// @dev the ERC-165 identifier for this interface is 0xcc97cb35
+>>>>>>> feat: Add custum signature with unlimited nesting.
interface ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -456,7 +467,7 @@
/// operator of the current owner.
/// @param approved The new substrate address approved NFT controller
/// @param tokenId The NFT to approve
- /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// @dev EVM selector for this function is: 0x106fdb59,
/// or in textual repr: approveCross((address,uint256),uint256)
function approveCross(Tuple6 memory approved, uint256 tokenId) external;
@@ -476,10 +487,15 @@
/// @param to Cross acccount address of new owner
/// @param tokenId The NFT to transfer
/// @dev EVM selector for this function is: 0xd5cf430b,
- /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ /// or in textual repr: transferFromCross(EthCrossAccount,EthCrossAccount,uint256)
function transferFromCross(
+<<<<<<< HEAD
Tuple6 memory from,
Tuple6 memory to,
+=======
+ EthCrossAccount memory from,
+ EthCrossAccount memory to,
+>>>>>>> feat: Add custum signature with unlimited nesting.
uint256 tokenId
) external;
@@ -499,7 +515,7 @@
/// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
/// @param from The current owner of the NFT
/// @param tokenId The NFT to transfer
- /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// @dev EVM selector for this function is: 0xa8106d4a,
/// or in textual repr: burnFromCross((address,uint256),uint256)
function burnFromCross(Tuple6 memory from, uint256 tokenId) external;
@@ -507,6 +523,7 @@
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
function nextTokenId() external view returns (uint256);
+<<<<<<< HEAD
// /// @notice Function to mint multiple tokens.
// /// @dev `tokenIds` should be an array of consecutive numbers and first number
// /// should be obtained with `nextTokenId` method
@@ -528,6 +545,30 @@
/// @dev anonymous struct
struct Tuple8 {
+=======
+
+ /// @notice Function to mint multiple tokens.
+ /// @dev `tokenIds` should be an array of consecutive numbers and first number
+ /// should be obtained with `nextTokenId` method
+ /// @param to The new owner
+ /// @param tokenIds IDs of the minted NFTs
+ /// @dev EVM selector for this function is: 0xf9d9a5a3,
+ /// or in textual repr: mintBulk(address,uint256[])
+ function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);
+
+ /// @notice Function to mint multiple tokens with the given tokenUris.
+ /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+ /// numbers and first number should be obtained with `nextTokenId` method
+ /// @param to The new owner
+ /// @param tokens array of pairs of token ID and token URI for minted tokens
+ /// @dev EVM selector for this function is: 0xfd4e2a99,
+ /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+ function mintBulkWithTokenURI(address to, Tuple12[] memory tokens) external returns (bool);
+}
+
+/// @dev anonymous struct
+struct Tuple12 {
+>>>>>>> feat: Add custum signature with unlimited nesting.
uint256 field_0;
string field_1;
}
@@ -538,6 +579,12 @@
uint256 field_1;
}
+/// @dev anonymous struct
+struct Tuple8 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -409,7 +409,7 @@
expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
- itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
+ itEth.only('Can perform transferFromCross()', async ({helper, privateKey}) => {
const alice = privateKey('//Alice');
const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -251,10 +251,14 @@
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
<<<<<<< HEAD
+<<<<<<< HEAD
"internalType": "struct Tuple6",
=======
"internalType": "struct Tuple19",
>>>>>>> feat: add `EthCrossAccount` type
+=======
+ "internalType": "struct Tuple8",
+>>>>>>> feat: Add custum signature with unlimited nesting.
"name": "",
"type": "tuple"
}
@@ -263,6 +267,7 @@
"type": "function"
},
{
+<<<<<<< HEAD
"inputs": [
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
@@ -278,6 +283,11 @@
"type": "tuple[]"
}
],
+=======
+ "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+ "name": "collectionProperty",
+ "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+>>>>>>> feat: Add custum signature with unlimited nesting.
"stateMutability": "view",
"type": "function"
},
@@ -298,10 +308,14 @@
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
<<<<<<< HEAD
+<<<<<<< HEAD
"internalType": "struct Tuple6",
=======
"internalType": "struct Tuple19",
>>>>>>> feat: add `EthCrossAccount` type
+=======
+ "internalType": "struct Tuple8",
+>>>>>>> feat: Add custum signature with unlimited nesting.
"name": "",
"type": "tuple"
}
@@ -324,6 +338,7 @@
"type": "function"
},
{
+<<<<<<< HEAD
"inputs": [
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
@@ -333,6 +348,8 @@
"type": "function"
},
{
+=======
+>>>>>>> feat: Add custum signature with unlimited nesting.
"inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
"name": "deleteCollectionProperty",
"outputs": [],
@@ -409,19 +426,64 @@
"type": "function"
},
{
+<<<<<<< HEAD
"inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
"name": "mint",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+=======
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+ ],
+ "name": "mint",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+>>>>>>> feat: Add custum signature with unlimited nesting.
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "to", "type": "address" },
+<<<<<<< HEAD
{ "internalType": "string", "name": "tokenUri", "type": "string" }
],
"name": "mintWithTokenURI",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+=======
+ { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }
+ ],
+ "name": "mintBulk",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ {
+ "components": [
+ { "internalType": "uint256", "name": "field_0", "type": "uint256" },
+ { "internalType": "string", "name": "field_1", "type": "string" }
+ ],
+ "internalType": "struct Tuple12[]",
+ "name": "tokens",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "mintBulkWithTokenURI",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+ { "internalType": "string", "name": "tokenUri", "type": "string" }
+ ],
+ "name": "mintWithTokenURI",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+>>>>>>> feat: Add custum signature with unlimited nesting.
"stateMutability": "nonpayable",
"type": "function"
},
@@ -614,6 +676,7 @@
},
{
"inputs": [
+<<<<<<< HEAD
{
"components": [
{ "internalType": "string", "name": "field_0", "type": "string" },
@@ -623,6 +686,10 @@
"name": "properties",
"type": "tuple[]"
}
+=======
+ { "internalType": "string", "name": "key", "type": "string" },
+ { "internalType": "bytes", "name": "value", "type": "bytes" }
+>>>>>>> feat: Add custum signature with unlimited nesting.
],
"name": "setCollectionProperties",
"outputs": [],
@@ -667,6 +734,7 @@
},
{
"inputs": [
+<<<<<<< HEAD
{
"components": [
{ "internalType": "address", "name": "field_0", "type": "address" },
@@ -676,6 +744,9 @@
"name": "newOwner",
"type": "tuple"
}
+=======
+ { "internalType": "address", "name": "newOwner", "type": "address" }
+>>>>>>> feat: Add custum signature with unlimited nesting.
],
"name": "setOwnerCross",
"outputs": [],
@@ -687,8 +758,13 @@
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" },
{
"components": [
+<<<<<<< HEAD
{ "internalType": "string", "name": "field_0", "type": "string" },
{ "internalType": "bytes", "name": "field_1", "type": "bytes" }
+=======
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+>>>>>>> feat: Add custum signature with unlimited nesting.
],
"internalType": "struct Tuple19[]",
"name": "properties",
@@ -799,8 +875,13 @@
"inputs": [
{
"components": [
+<<<<<<< HEAD
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
+=======
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+>>>>>>> feat: Add custum signature with unlimited nesting.
],
<<<<<<< HEAD
"internalType": "struct Tuple6",
@@ -812,8 +893,13 @@
},
{
"components": [
+<<<<<<< HEAD
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
+=======
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+>>>>>>> feat: Add custum signature with unlimited nesting.
],
<<<<<<< HEAD
"internalType": "struct Tuple6",
tests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/types.ts
+++ b/tests/src/eth/util/playgrounds/types.ts
@@ -14,10 +14,8 @@
args: { [key: string]: string }
};
export interface TEthCrossAccount {
- readonly 0: string,
- readonly 1: string | Uint8Array,
- readonly field_0: string,
- readonly field_1: string | Uint8Array,
+ readonly eth: string,
+ readonly sub: string | Uint8Array,
}
export type EthProperty = string[];
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -364,19 +364,15 @@
export class EthCrossAccountGroup extends EthGroupBase {
fromAddress(address: TEthereumAccount): TEthCrossAccount {
return {
- 0: address,
- 1: '0',
- field_0: address,
- field_1: '0',
+ eth: address,
+ sub: '0',
};
}
fromKeyringPair(keyring: IKeyringPair): TEthCrossAccount {
return {
- 0: '0x0000000000000000000000000000000000000000',
- 1: keyring.addressRaw,
- field_0: '0x0000000000000000000000000000000000000000',
- field_1: keyring.addressRaw,
+ eth: '0x0000000000000000000000000000000000000000',
+ sub: keyring.addressRaw,
};
}
}
@@ -429,7 +425,6 @@
ethAddress: EthAddressGroup;
ethNativeContract: NativeContractGroup;
ethContract: ContractGroup;
- ethCrossAccount: EthCrossAccountGroup;
ethProperty: EthPropertyGroup;
constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {