git.delta.rocks / unique-network / refs/commits / 5a3fe2c70832

difftreelog

refactor unify function and type signature handling

Yaroslav Bolyukin2022-11-02parent: #4c1a924.patch.diff
in: master

12 files changed

modifiedcrates/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
@@ -24,8 +24,8 @@
 use quote::{quote, format_ident};
 use inflector::cases;
 use syn::{
-	Expr, FnArg, GenericArgument, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta,
-	MetaNameValue, PatType, PathArguments, ReturnType, Type,
+	Expr, FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta, MetaNameValue,
+	PatType, ReturnType, Type,
 	spanned::Spanned,
 	parse::{Parse, ParseStream},
 	parenthesized, Token, LitInt, LitStr,
@@ -601,12 +601,12 @@
 		let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name);
 		let custom_signature = self.expand_custom_signature();
 		quote! {
-			const #screaming_name_signature: ::evm_coder::custom_signature::FunctionSignature = #custom_signature;
+			const #screaming_name_signature: ::evm_coder::custom_signature::SignatureUnit = #custom_signature;
 			const #screaming_name: ::evm_coder::types::bytes4 = {
 				let mut sum = ::evm_coder::sha3_const::Keccak256::new();
 				let mut pos = 0;
-				while pos < Self::#screaming_name_signature.unit.len {
-					sum = sum.update(&[Self::#screaming_name_signature.unit.data[pos]; 1]);
+				while pos < Self::#screaming_name_signature.len {
+					sum = sum.update(&[Self::#screaming_name_signature.data[pos]; 1]);
 					pos += 1;
 				}
 				let a = sum.finalize();
@@ -710,192 +710,28 @@
 			};
 			quote! {
 				Self::#pascal_name #matcher => ().into()
-			}
-		}
-	}
-
-	fn expand_type(ty: &Type, token_stream: &mut proc_macro2::TokenStream, read_signature: bool) {
-		match ty {
-			Type::Path(tp) => {
-				if let Some(qself) = &tp.qself {
-					panic!("no receiver expected {:?}", qself.ty.span());
-				}
-				let path = &tp.path;
-				if path.segments.len() != 1 {
-					panic!("expected path to have only one segment {:?}", path.span());
-				}
-				let last_segment = path.segments.last().unwrap();
-
-				if last_segment.ident == "Vec" {
-					let args = match &last_segment.arguments {
-						PathArguments::AngleBracketed(e) => e,
-						_ => {
-							panic!("missing Vec generic {:?}", last_segment.arguments.span());
-						}
-					};
-					let args = &args.args;
-					if args.len() != 1 {
-						panic!("expected only one generic for vec {:?}", args.span());
-					}
-					let arg = args.first().expect("first arg");
-
-					let ty = match arg {
-						GenericArgument::Type(ty) => ty,
-						_ => {
-							panic!("expected first generic to be type {:?}", arg.span());
-						}
-					};
-
-					let mut vec_token = proc_macro2::TokenStream::new();
-					Self::expand_type(ty, &mut vec_token, false);
-					vec_token = if read_signature {
-						quote! { (<Vec<#vec_token>>::SIGNATURE) }
-					} else {
-						quote! { <Vec<#vec_token>> }
-					};
-					token_stream.extend(vec_token);
-				} else {
-					if !last_segment.arguments.is_empty() {
-						panic!(
-							"unexpected generic arguments for non-vec type {:?}",
-							last_segment.arguments.span()
-						);
-					}
-
-					let ident = &last_segment.ident;
-					let plain_token = if read_signature {
-						quote! {
-							(<#ident>::SIGNATURE)
-						}
-					} else {
-						quote! {
-							#ident
-						}
-					};
-
-					token_stream.extend(plain_token);
-				}
-			}
-
-			Type::Tuple(tt) => {
-				// for ty in tt.elems.iter() {
-				// 	out.push(AbiType::try_from(ty)?)
-				// }
-
-				let mut tuple_types = proc_macro2::TokenStream::new();
-				let mut is_first = true;
-
-				for ty in tt.elems.iter() {
-					if is_first {
-						is_first = false
-					} else {
-						tuple_types.extend(quote!(,));
-					}
-					Self::expand_type(ty, &mut tuple_types, false);
-				}
-				tuple_types = if read_signature {
-					quote! { (<(#tuple_types)>::SIGNATURE) }
-				} else {
-					quote! { (#tuple_types) }
-				};
-				token_stream.extend(tuple_types);
 			}
-
-			// Type::Array(arr) => {
-			// 	let wrapped = AbiType::try_from(&arr.elem)?;
-			// 	match &arr.len {
-			// 		Expr::Lit(l) => match &l.lit {
-			// 			Lit::Int(i) => {
-			// 				let num = i.base10_parse::<usize>()?;
-			// 				Ok(AbiType::Array(Box::new(wrapped), num as usize))
-			// 			}
-			// 			_ => Err(syn::Error::new(arr.len.span(), "should be int literal")),
-			// 		},
-			// 		_ => Err(syn::Error::new(arr.len.span(), "should be literal")),
-			// 	}
-			// }
-			_ => panic!("Unexpected type {ty:?}"),
 		}
-		// match ty {
-		// 	AbiType::Plain(ref ident) => {
-		// 		let plain_token = if read_signature {
-		// 			quote! {
-		// 				(<#ident>::SIGNATURE)
-		// 			}
-		// 		} else {
-		// 			quote! {
-		// 				#ident
-		// 			}
-		// 		};
-
-		// 		token_stream.extend(plain_token);
-		// 	}
-
-		// 	AbiType::Tuple(ref tuple_type) => {
-		// 		let mut tuple_types = proc_macro2::TokenStream::new();
-		// 		let mut is_first = true;
-
-		// 		for ty in tuple_type {
-		// 			if is_first {
-		// 				is_first = false
-		// 			} else {
-		// 				tuple_types.extend(quote!(,));
-		// 			}
-		// 			Self::expand_type(ty, &mut tuple_types, false);
-		// 		}
-		// 		tuple_types = if read_signature {
-		// 			quote! { (<(#tuple_types)>::SIGNATURE) }
-		// 		} else {
-		// 			quote! { (#tuple_types) }
-		// 		};
-		// 		token_stream.extend(tuple_types);
-		// 	}
-
-		// 	AbiType::Vec(ref vec_type) => {
-		// 		let mut vec_token = proc_macro2::TokenStream::new();
-		// 		Self::expand_type(vec_type.as_ref(), &mut vec_token, false);
-		// 		vec_token = if read_signature {
-		// 			quote! { (<Vec<#vec_token>>::SIGNATURE) }
-		// 		} else {
-		// 			quote! { <Vec<#vec_token>> }
-		// 		};
-		// 		token_stream.extend(vec_token);
-		// 	}
-
-		// 	AbiType::Array(_, _) => todo!("Array eth signature"),
-		// };
 	}
 
 	fn expand_custom_signature(&self) -> proc_macro2::TokenStream {
-		let mut token_stream = TokenStream::new();
+		let mut args = TokenStream::new();
 
 		let mut is_first = true;
-		for arg in &self.args {
-			if arg.is_special() {
-				continue;
-			}
-
-			if is_first {
-				is_first = false;
-			} else {
-				token_stream.extend(quote!(,));
-			}
-			Self::expand_type(&arg.ty, &mut token_stream, true);
+		for arg in self.args.iter().filter(|a| !a.is_special()) {
+			is_first = false;
+			let ty = &arg.ty;
+			args.extend(quote! {nameof(#ty)});
+			args.extend(quote! {fixed(",")})
 		}
 
+		// Remove trailing comma
 		if !is_first {
-			token_stream.extend(quote!(,));
+			args.extend(quote! {shift_left(1)})
 		}
 
 		let func_name = self.camel_name.clone();
-		let func_name = quote!(SignaturePreferences {
-			open_name: Some(SignatureUnit::new(#func_name)),
-			open_delimiter: Some(SignatureUnit::new("(")),
-			param_delimiter: Some(SignatureUnit::new(",")),
-			close_delimiter: Some(SignatureUnit::new(")")),
-			close_name: None,
-		});
-		quote!({ ::evm_coder::make_signature!(new fn(#func_name), #token_stream) })
+		quote! { ::evm_coder::make_signature!(new fixed(#func_name) fixed("(") #args fixed(")")) }
 	}
 
 	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
@@ -916,12 +752,6 @@
 		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
-			}
-		);
 		let is_payable = self.has_value_args;
 
 		quote! {
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -27,7 +27,7 @@
 	execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},
 	types::*,
 	make_signature,
-	custom_signature::{SignatureUnit, SIGNATURE_SIZE_LIMIT},
+	custom_signature::{SignatureUnit},
 };
 use crate::execution::Result;
 
@@ -428,7 +428,7 @@
 }
 
 impl<R: Signature> Signature for Vec<R> {
-	make_signature!(new nameof(R) fixed("[]"));
+	const SIGNATURE: SignatureUnit = make_signature!(new nameof(R) fixed("[]"));
 }
 
 impl sealed::CanBePlacedInVec for EthCrossAccount {}
@@ -522,7 +522,7 @@
 		where
 		$($ident: Signature,)+
 		{
-			make_signature!(
+			const SIGNATURE: SignatureUnit = make_signature!(
 				new fixed("(")
 				$(nameof($ident) fixed(","))+
 				shift_left(1)
@@ -758,13 +758,13 @@
 				1ACF2D55
 				0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]
 				0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]
-	
+
 				0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address
 				000000000000000000000000000000000000000000000000000000000000000A // uint256
-	
+
 				000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address
 				0000000000000000000000000000000000000000000000000000000000000014 // uint256
-	
+
 				0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address
 				000000000000000000000000000000000000000000000000000000000000001E // uint256
 			"
modifiedcrates/evm-coder/src/custom_signature.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/custom_signature.rs
+++ b/crates/evm-coder/src/custom_signature.rs
@@ -74,132 +74,11 @@
 //!	impl<T: SoliditySignature> SoliditySignature for Vec<T> {
 //!		make_signature!(new nameof(T) fixed("[]"));
 //!	}
-//!
-//! // Function signature settings
-//! const SIGNATURE_PREFERENCES: SignaturePreferences = SignaturePreferences {
-//!		open_name: Some(SignatureUnit::new("some_funk")),
-//!		open_delimiter: Some(SignatureUnit::new("(")),
-//!		param_delimiter: Some(SignatureUnit::new(",")),
-//!		close_delimiter: Some(SignatureUnit::new(")")),
-//!		close_name: None,
-//!	};
-//!
-//! // Create functions signatures
-//! fn make_func_without_args() {
-//!		const SIG: FunctionSignature = make_signature!(
-//!			new fn(SIGNATURE_PREFERENCES),
-//!		);
-//!		let name = SIG.as_str();
-//!		similar_asserts::assert_eq!(name, "some_funk()");
-//!	}
-//!
-//! fn make_func_with_3_args() {
-//!		const SIG: FunctionSignature = make_signature!(
-//!			new fn(SIGNATURE_PREFERENCES),
-//!			(<u8>::SIGNATURE),
-//!			(<u8>::SIGNATURE),
-//!			(<Vec<u8>>::SIGNATURE),
-//!		);
-//!		let name = SIG.as_str();
-//!		similar_asserts::assert_eq!(name, "some_funk(uint8,uint8,uint8[])");
-//!	}
 //! ```
-use core::str::from_utf8;
 
 /// The maximum length of the signature.
 pub const SIGNATURE_SIZE_LIMIT: usize = 256;
-
-/// Function signature formatting preferences.
-#[derive(Debug)]
-pub struct SignaturePreferences {
-	/// The name of the function before the list of parameters: `*some*(param1,param2)func`
-	pub open_name: Option<SignatureUnit>,
-	/// Opening separator: `some*(*param1,param2)func`
-	pub open_delimiter: Option<SignatureUnit>,
-	/// Parameters separator: `some(param1*,*param2)func`
-	pub param_delimiter: Option<SignatureUnit>,
-	/// Closinging separator: `some(param1,param2*)*func`
-	pub close_delimiter: Option<SignatureUnit>,
-	/// The name of the function after the list of parameters: `some(param1,param2)*func*`
-	pub close_name: Option<SignatureUnit>,
-}
-
-/// Constructs and stores the signature of the function.
-#[derive(Debug)]
-pub struct FunctionSignature {
-	/// Storage for function signature.
-	pub unit: SignatureUnit,
-	preferences: SignaturePreferences,
-}
 
-impl FunctionSignature {
-	/// Start constructing the signature. It is written to the storage
-	/// [`SignaturePreferences::open_name`] and [`SignaturePreferences::open_delimiter`].
-	pub const fn new(preferences: SignaturePreferences) -> FunctionSignature {
-		let mut dst = [0_u8; SIGNATURE_SIZE_LIMIT];
-		let mut dst_offset = 0;
-		if let Some(ref name) = preferences.open_name {
-			crate::make_signature!(@copy(name.data, dst, name.len, dst_offset));
-		}
-		if let Some(ref delimiter) = preferences.open_delimiter {
-			crate::make_signature!(@copy(delimiter.data, dst, delimiter.len, dst_offset));
-		}
-		FunctionSignature {
-			unit: SignatureUnit {
-				data: dst,
-				len: dst_offset,
-			},
-			preferences,
-		}
-	}
-
-	/// Add a function parameter to the signature. It is written to the storage
-	/// `param` [`SignatureUnit`] and [`SignaturePreferences::param_delimiter`].
-	pub const fn add_param(
-		signature: FunctionSignature,
-		param: SignatureUnit,
-	) -> FunctionSignature {
-		let mut dst = signature.unit.data;
-		let mut dst_offset = signature.unit.len;
-		crate::make_signature!(@copy(param.data, dst, param.len, dst_offset));
-		if let Some(ref delimiter) = signature.preferences.param_delimiter {
-			crate::make_signature!(@copy(delimiter.data, dst, delimiter.len, dst_offset));
-		}
-		FunctionSignature {
-			unit: SignatureUnit {
-				data: dst,
-				len: dst_offset,
-			},
-			..signature
-		}
-	}
-
-	/// Complete signature construction. It is written to the storage
-	/// [`SignaturePreferences::close_delimiter`] and [`SignaturePreferences::close_name`].
-	pub const fn done(signature: FunctionSignature, owerride: bool) -> FunctionSignature {
-		let mut dst = signature.unit.data;
-		let mut dst_offset = signature.unit.len - if owerride { 1 } else { 0 };
-		if let Some(ref delimiter) = signature.preferences.close_delimiter {
-			crate::make_signature!(@copy(delimiter.data, dst, delimiter.len, dst_offset));
-		}
-		if let Some(ref name) = signature.preferences.close_name {
-			crate::make_signature!(@copy(name.data, dst, name.len, dst_offset));
-		}
-		FunctionSignature {
-			unit: SignatureUnit {
-				data: dst,
-				len: dst_offset,
-			},
-			..signature
-		}
-	}
-
-	/// Represent the signature as `&str'.
-	pub fn as_str(&self) -> &str {
-		from_utf8(&self.unit.data[..self.unit.len]).expect("bad utf-8")
-	}
-}
-
 /// Storage for the signature or its elements.
 #[derive(Debug)]
 pub struct SignatureUnit {
@@ -222,6 +101,10 @@
 			len: name_len,
 		}
 	}
+	/// String conversion
+	pub fn as_str(&self) -> Option<&str> {
+		core::str::from_utf8(&self.data[0..self.len]).ok()
+	}
 }
 
 /// ### Macro to create signatures of types and functions.
@@ -231,85 +114,52 @@
 /// make_signature!(new fixed("uint8")); // Simple type
 /// make_signature!(new fixed("(") nameof(u8) fixed(",") nameof(u8) fixed(")")); // Composite type
 /// ```
-/// Format for creating a function of the function:
-/// ```ignore
-/// const SIG: FunctionSignature = make_signature!(
-///		new fn(SIGNATURE_PREFERENCES),
-///		(u8::SIGNATURE),
-///		(<(u8,u8)>::SIGNATURE),
-///	);
-/// ```
 #[macro_export]
 macro_rules! make_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
-		}
+	(new $($tt:tt)*) => {
+		($crate::custom_signature::SignatureUnit {
+			data: {
+				let mut out = [0u8; $crate::custom_signature::SIGNATURE_SIZE_LIMIT];
+				let mut dst_offset = 0;
+				$crate::make_signature!(@data(out, dst_offset); $($tt)*);
+				out
+			},
+			len: {0 + $crate::make_signature!(@size; $($tt)*)},
+		})
 	};
 
-	(@param; $func:expr) => {
-		FunctionSignature::done($func, true)
+	(@size;) => {
+		0
 	};
-	(@param; $func:expr, $param:expr) => {
-		make_signature!(@param; FunctionSignature::add_param($func, $param))
+	(@size; fixed($expr:expr) $($tt:tt)*) => {
+		$expr.len() + $crate::make_signature!(@size; $($tt)*)
 	};
-	(@param; $func:expr, $param:expr, $($tt:tt),*) => {
-		make_signature!(@param; FunctionSignature::add_param($func, $param), $($tt),*)
+	(@size; nameof($expr:ty) $($tt:tt)*) => {
+		<$expr>::SIGNATURE.len + $crate::make_signature!(@size; $($tt)*)
 	};
-
-    (new $($tt:tt)*) => {
-        const SIGNATURE: SignatureUnit = SignatureUnit {
-			data: {
-				let mut out = [0u8; SIGNATURE_SIZE_LIMIT];
-				let mut dst_offset = 0;
-				make_signature!(@data(out, dst_offset); $($tt)*);
-				out
-			},
-			len: {0 + make_signature!(@size; $($tt)*)},
-        };
-    };
-
-    (@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)*)
-    };
 	(@size; shift_left($expr:expr) $($tt:tt)*) => {
-		make_signature!(@size; $($tt)*) - $expr
+		$crate::make_signature!(@size; $($tt)*) - $expr
 	};
 
-    (@data($dst:ident, $dst_offset:ident);) => {};
-    (@data($dst:ident, $dst_offset:ident); fixed($expr:expr) $($tt:tt)*) => {
-        {
-            let data = $expr.as_bytes();
+	(@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)*) => {
-        {
-            make_signature!(@copy(&<$expr>::SIGNATURE.data, $dst, <$expr>::SIGNATURE.len, $dst_offset));
-        }
-        make_signature!(@data($dst, $dst_offset); $($tt)*)
-    };
+			$crate::make_signature!(@copy(data, $dst, data_len, $dst_offset));
+		}
+		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
+	};
+	(@data($dst:ident, $dst_offset:ident); nameof($expr:ty) $($tt:tt)*) => {
+		{
+			$crate::make_signature!(@copy(&<$expr>::SIGNATURE.data, $dst, <$expr>::SIGNATURE.len, $dst_offset));
+		}
+		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
+	};
 	(@data($dst:ident, $dst_offset:ident); shift_left($expr:expr) $($tt:tt)*) => {
-        $dst_offset -= $expr;
-        make_signature!(@data($dst, $dst_offset); $($tt)*)
-    };
+		$dst_offset -= $expr;
+		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
+	};
 
 	(@copy($src:expr, $dst:expr, $src_len:expr, $dst_offset:ident)) => {
 		{
@@ -328,7 +178,7 @@
 mod test {
 	use core::str::from_utf8;
 
-	use super::{SIGNATURE_SIZE_LIMIT, SignatureUnit, FunctionSignature, SignaturePreferences};
+	use super::{SIGNATURE_SIZE_LIMIT, SignatureUnit};
 
 	trait Name {
 		const SIGNATURE: SignatureUnit;
@@ -339,19 +189,21 @@
 	}
 
 	impl Name for u8 {
-		make_signature!(new fixed("uint8"));
+		const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));
 	}
 	impl Name for u32 {
-		make_signature!(new fixed("uint32"));
+		const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint32"));
 	}
 	impl<T: Name> Name for Vec<T> {
-		make_signature!(new nameof(T) fixed("[]"));
+		const SIGNATURE: SignatureUnit = 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(")"));
+		const SIGNATURE: SignatureUnit =
+			make_signature!(new fixed("(") nameof(A) fixed(",") nameof(B) fixed(")"));
 	}
 	impl<A: Name> Name for (A,) {
-		make_signature!(new fixed("(") nameof(A) fixed(",") shift_left(1) fixed(")"));
+		const SIGNATURE: SignatureUnit =
+			make_signature!(new fixed("(") nameof(A) fixed(",") shift_left(1) fixed(")"));
 	}
 
 	struct MaxSize();
@@ -361,14 +213,6 @@
 			len: SIGNATURE_SIZE_LIMIT,
 		};
 	}
-
-	const SIGNATURE_PREFERENCES: SignaturePreferences = SignaturePreferences {
-		open_name: Some(SignatureUnit::new("some_funk")),
-		open_delimiter: Some(SignatureUnit::new("(")),
-		param_delimiter: Some(SignatureUnit::new(",")),
-		close_delimiter: Some(SignatureUnit::new(")")),
-		close_name: None,
-	};
 
 	#[test]
 	fn simple() {
@@ -421,68 +265,6 @@
 	#[test]
 	fn max_size() {
 		assert_eq!(<MaxSize>::name(), "!".repeat(SIGNATURE_SIZE_LIMIT));
-	}
-
-	#[test]
-	fn make_func_without_args() {
-		const SIG: FunctionSignature = make_signature!(
-			new fn(SIGNATURE_PREFERENCES)
-		);
-		let name = SIG.as_str();
-		similar_asserts::assert_eq!(name, "some_funk()");
-	}
-
-	#[test]
-	fn make_func_with_1_args() {
-		const SIG: FunctionSignature = make_signature!(
-			new fn(SIGNATURE_PREFERENCES),
-			(<u8>::SIGNATURE),
-		);
-		let name = SIG.as_str();
-		similar_asserts::assert_eq!(name, "some_funk(uint8)");
-	}
-
-	#[test]
-	fn make_func_with_2_args() {
-		const SIG: FunctionSignature = make_signature!(
-			new fn(SIGNATURE_PREFERENCES),
-			(u8::SIGNATURE),
-			(<Vec<u32>>::SIGNATURE),
-		);
-		let name = SIG.as_str();
-		similar_asserts::assert_eq!(name, "some_funk(uint8,uint32[])");
-	}
-
-	#[test]
-	fn make_func_with_3_args() {
-		const SIG: FunctionSignature = make_signature!(
-			new fn(SIGNATURE_PREFERENCES),
-			(<u8>::SIGNATURE),
-			(<u32>::SIGNATURE),
-			(<Vec<u32>>::SIGNATURE),
-		);
-		let name = SIG.as_str();
-		similar_asserts::assert_eq!(name, "some_funk(uint8,uint32,uint32[])");
-	}
-
-	#[test]
-	fn make_slice_from_signature() {
-		const SIG: FunctionSignature = make_signature!(
-			new fn(SIGNATURE_PREFERENCES),
-			(<u8>::SIGNATURE),
-			(<u32>::SIGNATURE),
-			(<Vec<u32>>::SIGNATURE),
-		);
-		const NAME: [u8; SIG.unit.len] = {
-			let mut name: [u8; SIG.unit.len] = [0; SIG.unit.len];
-			let mut i = 0;
-			while i < SIG.unit.len {
-				name[i] = SIG.unit.data[i];
-				i += 1;
-			}
-			name
-		};
-		similar_asserts::assert_eq!(&NAME, b"some_funk(uint8,uint32,uint32[])");
 	}
 
 	#[test]
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -122,7 +122,7 @@
 	use primitive_types::{U256, H160, H256};
 	use core::str::from_utf8;
 
-	use crate::custom_signature::{SignatureUnit, SIGNATURE_SIZE_LIMIT};
+	use crate::custom_signature::SignatureUnit;
 
 	pub trait Signature {
 		const SIGNATURE: SignatureUnit;
@@ -133,14 +133,14 @@
 	}
 
 	impl Signature for bool {
-		make_signature!(new fixed("bool"));
+		const SIGNATURE: SignatureUnit = 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)));
+				const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ident)));
 			}
 		};
 	}
@@ -165,7 +165,7 @@
 	#[derive(Default, Debug)]
 	pub struct bytes(pub Vec<u8>);
 	impl Signature for bytes {
-		make_signature!(new fixed("bytes"));
+		const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));
 	}
 
 	/// Solidity doesn't have `void` type, however we have special implementation
@@ -259,7 +259,7 @@
 	}
 
 	impl Signature for EthCrossAccount {
-		make_signature!(new fixed("(address,uint256)"));
+		const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));
 	}
 
 	/// Convert `CrossAccountId` to `uint256`.
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -32,7 +32,7 @@
 	cmp::Reverse,
 };
 use impl_trait_for_tuples::impl_for_tuples;
-use crate::{types::*, custom_signature::FunctionSignature};
+use crate::{types::*, custom_signature::SignatureUnit};
 
 #[derive(Default)]
 pub struct TypeCollector {
@@ -486,7 +486,7 @@
 	pub docs: &'static [&'static str],
 	pub selector: u32,
 	pub hide: bool,
-	pub custom_signature: FunctionSignature,
+	pub custom_signature: SignatureUnit,
 	pub name: &'static str,
 	pub args: A,
 	pub result: R,
@@ -512,7 +512,7 @@
 		writeln!(
 			writer,
 			"\t{hide_comment}///  or in textual repr: {}",
-			self.custom_signature.as_str()
+			self.custom_signature.as_str().expect("bad utf-8")
 		)?;
 		write!(writer, "\t{hide_comment}function {}(", self.name)?;
 		self.args.solidity_name(writer, tc)?;
modifiedpallets/common/src/erc.rsdiffbeforeafterboth
before · pallets/common/src/erc.rs
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::{SignatureUnit, FunctionSignature, SignaturePreferences},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::convert_cross_account_to_uint256, weights::WeightInfo,39};4041/// Events for ethereum collection helper.42#[derive(ToLog)]43pub enum CollectionHelpersEvents {44	/// The collection has been created.45	CollectionCreated {46		/// Collection owner.47		#[indexed]48		owner: address,4950		/// Collection ID.51		#[indexed]52		collection_id: address,53	},54	/// The collection has been destroyed.55	CollectionDestroyed {56		/// Collection ID.57		#[indexed]58		collection_id: address,59	},60}6162/// Does not always represent a full collection, for RFT it is either63/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).64pub trait CommonEvmHandler {65	const CODE: &'static [u8];6667	/// Call precompiled handle.68	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;69}7071/// @title A contract that allows you to work with collections.72#[solidity_interface(name = Collection)]73impl<T: Config> CollectionHandle<T>74where75	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,76{77	/// Set collection property.78	///79	/// @param key Property key.80	/// @param value Propery value.81	#[weight(<SelfWeightOf<T>>::set_collection_properties(1))]82	fn set_collection_property(83		&mut self,84		caller: caller,85		key: string,86		value: bytes,87	) -> Result<void> {88		let caller = T::CrossAccountId::from_eth(caller);89		let key = <Vec<u8>>::from(key)90			.try_into()91			.map_err(|_| "key too large")?;92		let value = value.0.try_into().map_err(|_| "value too large")?;9394		<Pallet<T>>::set_collection_property(self, &caller, Property { key, value })95			.map_err(dispatch_to_evm::<T>)96	}9798	/// Set collection properties.99	///100	/// @param properties Vector of properties key/value pair.101	#[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]102	fn set_collection_properties(103		&mut self,104		caller: caller,105		properties: Vec<(string, bytes)>,106	) -> Result<void> {107		let caller = T::CrossAccountId::from_eth(caller);108109		let properties = properties110			.into_iter()111			.map(|(key, value)| {112				let key = <Vec<u8>>::from(key)113					.try_into()114					.map_err(|_| "key too large")?;115116				let value = value.0.try_into().map_err(|_| "value too large")?;117118				Ok(Property { key, value })119			})120			.collect::<Result<Vec<_>>>()?;121122		<Pallet<T>>::set_collection_properties(self, &caller, properties)123			.map_err(dispatch_to_evm::<T>)124	}125126	/// Delete collection property.127	///128	/// @param key Property key.129	#[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]130	fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {131		let caller = T::CrossAccountId::from_eth(caller);132		let key = <Vec<u8>>::from(key)133			.try_into()134			.map_err(|_| "key too large")?;135136		<Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)137	}138139	/// Delete collection properties.140	///141	/// @param keys Properties keys.142	#[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]143	fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {144		let caller = T::CrossAccountId::from_eth(caller);145		let keys = keys146			.into_iter()147			.map(|key| {148				<Vec<u8>>::from(key)149					.try_into()150					.map_err(|_| Error::Revert("key too large".into()))151			})152			.collect::<Result<Vec<_>>>()?;153154		<Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)155	}156157	/// Get collection property.158	///159	/// @dev Throws error if key not found.160	///161	/// @param key Property key.162	/// @return bytes The property corresponding to the key.163	fn collection_property(&self, key: string) -> Result<bytes> {164		let key = <Vec<u8>>::from(key)165			.try_into()166			.map_err(|_| "key too large")?;167168		let props = CollectionProperties::<T>::get(self.id);169		let prop = props.get(&key).ok_or("key not found")?;170171		Ok(bytes(prop.to_vec()))172	}173174	/// Get collection properties.175	///176	/// @param keys Properties keys. Empty keys for all propertyes.177	/// @return Vector of properties key/value pairs.178	fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {179		let keys = keys180			.into_iter()181			.map(|key| {182				<Vec<u8>>::from(key)183					.try_into()184					.map_err(|_| Error::Revert("key too large".into()))185			})186			.collect::<Result<Vec<_>>>()?;187188		let properties = Pallet::<T>::filter_collection_properties(189			self.id,190			if keys.is_empty() { None } else { Some(keys) },191		)192		.map_err(dispatch_to_evm::<T>)?;193194		let properties = properties195			.into_iter()196			.map(|p| {197				let key =198					string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;199				let value = bytes(p.value.to_vec());200				Ok((key, value))201			})202			.collect::<Result<Vec<_>>>()?;203		Ok(properties)204	}205206	/// Set the sponsor of the collection.207	///208	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.209	///210	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.211	fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {212		self.consume_store_reads_and_writes(1, 1)?;213214		check_is_owner_or_admin(caller, self)?;215216		let sponsor = T::CrossAccountId::from_eth(sponsor);217		self.set_sponsor(sponsor.as_sub().clone())218			.map_err(dispatch_to_evm::<T>)?;219		save(self)220	}221222	/// Set the sponsor of the collection.223	///224	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.225	///226	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.227	fn set_collection_sponsor_cross(228		&mut self,229		caller: caller,230		sponsor: EthCrossAccount,231	) -> Result<void> {232		self.consume_store_reads_and_writes(1, 1)?;233234		check_is_owner_or_admin(caller, self)?;235236		let sponsor = sponsor.into_sub_cross_account::<T>()?;237		self.set_sponsor(sponsor.as_sub().clone())238			.map_err(dispatch_to_evm::<T>)?;239		save(self)240	}241242	/// Whether there is a pending sponsor.243	fn has_collection_pending_sponsor(&self) -> Result<bool> {244		Ok(matches!(245			self.collection.sponsorship,246			SponsorshipState::Unconfirmed(_)247		))248	}249250	/// Collection sponsorship confirmation.251	///252	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.253	fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {254		self.consume_store_writes(1)?;255256		let caller = T::CrossAccountId::from_eth(caller);257		if !self258			.confirm_sponsorship(caller.as_sub())259			.map_err(dispatch_to_evm::<T>)?260		{261			return Err("caller is not set as sponsor".into());262		}263		save(self)264	}265266	/// Remove collection sponsor.267	fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {268		self.consume_store_reads_and_writes(1, 1)?;269		check_is_owner_or_admin(caller, self)?;270		self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;271		save(self)272	}273274	/// Get current sponsor.275	///276	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.277	fn collection_sponsor(&self) -> Result<(address, uint256)> {278		let sponsor = match self.collection.sponsorship.sponsor() {279			Some(sponsor) => sponsor,280			None => return Ok(Default::default()),281		};282		let sponsor = T::CrossAccountId::from_sub(sponsor.clone());283		let result: (address, uint256) = if sponsor.is_canonical_substrate() {284			let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);285			(Default::default(), sponsor)286		} else {287			let sponsor = *sponsor.as_eth();288			(sponsor, Default::default())289		};290		Ok(result)291	}292293	/// Set limits for the collection.294	/// @dev Throws error if limit not found.295	/// @param limit Name of the limit. Valid names:296	/// 	"accountTokenOwnershipLimit",297	/// 	"sponsoredDataSize",298	/// 	"sponsoredDataRateLimit",299	/// 	"tokenLimit",300	/// 	"sponsorTransferTimeout",301	/// 	"sponsorApproveTimeout"302	/// @param value Value of the limit.303	#[solidity(rename_selector = "setCollectionLimit")]304	fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {305		self.consume_store_reads_and_writes(1, 1)?;306307		check_is_owner_or_admin(caller, self)?;308		let mut limits = self.limits.clone();309310		match limit.as_str() {311			"accountTokenOwnershipLimit" => {312				limits.account_token_ownership_limit = Some(value);313			}314			"sponsoredDataSize" => {315				limits.sponsored_data_size = Some(value);316			}317			"sponsoredDataRateLimit" => {318				limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));319			}320			"tokenLimit" => {321				limits.token_limit = Some(value);322			}323			"sponsorTransferTimeout" => {324				limits.sponsor_transfer_timeout = Some(value);325			}326			"sponsorApproveTimeout" => {327				limits.sponsor_approve_timeout = Some(value);328			}329			_ => {330				return Err(Error::Revert(format!(331					"unknown integer limit \"{}\"",332					limit333				)))334			}335		}336		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)337			.map_err(dispatch_to_evm::<T>)?;338		save(self)339	}340341	/// Set limits for the collection.342	/// @dev Throws error if limit not found.343	/// @param limit Name of the limit. Valid names:344	/// 	"ownerCanTransfer",345	/// 	"ownerCanDestroy",346	/// 	"transfersEnabled"347	/// @param value Value of the limit.348	#[solidity(rename_selector = "setCollectionLimit")]349	fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {350		self.consume_store_reads_and_writes(1, 1)?;351352		check_is_owner_or_admin(caller, self)?;353		let mut limits = self.limits.clone();354355		match limit.as_str() {356			"ownerCanTransfer" => {357				limits.owner_can_transfer = Some(value);358			}359			"ownerCanDestroy" => {360				limits.owner_can_destroy = Some(value);361			}362			"transfersEnabled" => {363				limits.transfers_enabled = Some(value);364			}365			_ => {366				return Err(Error::Revert(format!(367					"unknown boolean limit \"{}\"",368					limit369				)))370			}371		}372		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)373			.map_err(dispatch_to_evm::<T>)?;374		save(self)375	}376377	/// Get contract address.378	fn contract_address(&self) -> Result<address> {379		Ok(crate::eth::collection_id_to_address(self.id))380	}381382	/// Add collection admin.383	/// @param newAdmin Cross account administrator address.384	fn add_collection_admin_cross(385		&mut self,386		caller: caller,387		new_admin: EthCrossAccount,388	) -> Result<void> {389		self.consume_store_writes(2)?;390391		let caller = T::CrossAccountId::from_eth(caller);392		let new_admin = new_admin.into_sub_cross_account::<T>()?;393		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;394		Ok(())395	}396397	/// Remove collection admin.398	/// @param admin Cross account administrator address.399	fn remove_collection_admin_cross(400		&mut self,401		caller: caller,402		admin: EthCrossAccount,403	) -> Result<void> {404		self.consume_store_writes(2)?;405406		let caller = T::CrossAccountId::from_eth(caller);407		let admin = admin.into_sub_cross_account::<T>()?;408		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;409		Ok(())410	}411412	/// Add collection admin.413	/// @param newAdmin Address of the added administrator.414	fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {415		self.consume_store_writes(2)?;416417		let caller = T::CrossAccountId::from_eth(caller);418		let new_admin = T::CrossAccountId::from_eth(new_admin);419		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;420		Ok(())421	}422423	/// Remove collection admin.424	///425	/// @param admin Address of the removed administrator.426	fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {427		self.consume_store_writes(2)?;428429		let caller = T::CrossAccountId::from_eth(caller);430		let admin = T::CrossAccountId::from_eth(admin);431		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;432		Ok(())433	}434435	/// Toggle accessibility of collection nesting.436	///437	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'438	#[solidity(rename_selector = "setCollectionNesting")]439	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {440		self.consume_store_reads_and_writes(1, 1)?;441442		check_is_owner_or_admin(caller, self)?;443444		let mut permissions = self.collection.permissions.clone();445		let mut nesting = permissions.nesting().clone();446		nesting.token_owner = enable;447		nesting.restricted = None;448		permissions.nesting = Some(nesting);449450		self.collection.permissions = <Pallet<T>>::clamp_permissions(451			self.collection.mode.clone(),452			&self.collection.permissions,453			permissions,454		)455		.map_err(dispatch_to_evm::<T>)?;456457		save(self)458	}459460	/// Toggle accessibility of collection nesting.461	///462	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'463	/// @param collections Addresses of collections that will be available for nesting.464	#[solidity(rename_selector = "setCollectionNesting")]465	fn set_nesting(466		&mut self,467		caller: caller,468		enable: bool,469		collections: Vec<address>,470	) -> Result<void> {471		self.consume_store_reads_and_writes(1, 1)?;472473		if collections.is_empty() {474			return Err("no addresses provided".into());475		}476		check_is_owner_or_admin(caller, self)?;477478		let mut permissions = self.collection.permissions.clone();479		match enable {480			false => {481				let mut nesting = permissions.nesting().clone();482				nesting.token_owner = false;483				nesting.restricted = None;484				permissions.nesting = Some(nesting);485			}486			true => {487				let mut bv = OwnerRestrictedSet::new();488				for i in collections {489					bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {490						Error::Revert("Can't convert address into collection id".into())491					})?)492					.map_err(|_| "too many collections")?;493				}494				let mut nesting = permissions.nesting().clone();495				nesting.token_owner = true;496				nesting.restricted = Some(bv);497				permissions.nesting = Some(nesting);498			}499		};500501		self.collection.permissions = <Pallet<T>>::clamp_permissions(502			self.collection.mode.clone(),503			&self.collection.permissions,504			permissions,505		)506		.map_err(dispatch_to_evm::<T>)?;507508		save(self)509	}510511	/// Set the collection access method.512	/// @param mode Access mode513	/// 	0 for Normal514	/// 	1 for AllowList515	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {516		self.consume_store_reads_and_writes(1, 1)?;517518		check_is_owner_or_admin(caller, self)?;519		let permissions = CollectionPermissions {520			access: Some(match mode {521				0 => AccessMode::Normal,522				1 => AccessMode::AllowList,523				_ => return Err("not supported access mode".into()),524			}),525			..Default::default()526		};527		self.collection.permissions = <Pallet<T>>::clamp_permissions(528			self.collection.mode.clone(),529			&self.collection.permissions,530			permissions,531		)532		.map_err(dispatch_to_evm::<T>)?;533534		save(self)535	}536537	/// Checks that user allowed to operate with collection.538	///539	/// @param user User address to check.540	fn allowed(&self, user: address) -> Result<bool> {541		Ok(Pallet::<T>::allowed(542			self.id,543			T::CrossAccountId::from_eth(user),544		))545	}546547	/// Add the user to the allowed list.548	///549	/// @param user Address of a trusted user.550	fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {551		self.consume_store_writes(1)?;552553		let caller = T::CrossAccountId::from_eth(caller);554		let user = T::CrossAccountId::from_eth(user);555		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;556		Ok(())557	}558559	/// Add user to allowed list.560	///561	/// @param user User cross account address.562	fn add_to_collection_allow_list_cross(563		&mut self,564		caller: caller,565		user: EthCrossAccount,566	) -> Result<void> {567		self.consume_store_writes(1)?;568569		let caller = T::CrossAccountId::from_eth(caller);570		let user = user.into_sub_cross_account::<T>()?;571		Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;572		Ok(())573	}574575	/// Remove the user from the allowed list.576	///577	/// @param user Address of a removed user.578	fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {579		self.consume_store_writes(1)?;580581		let caller = T::CrossAccountId::from_eth(caller);582		let user = T::CrossAccountId::from_eth(user);583		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;584		Ok(())585	}586587	/// Remove user from allowed list.588	///589	/// @param user User cross account address.590	fn remove_from_collection_allow_list_cross(591		&mut self,592		caller: caller,593		user: EthCrossAccount,594	) -> Result<void> {595		self.consume_store_writes(1)?;596597		let caller = T::CrossAccountId::from_eth(caller);598		let user = user.into_sub_cross_account::<T>()?;599		Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;600		Ok(())601	}602603	/// Switch permission for minting.604	///605	/// @param mode Enable if "true".606	fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {607		self.consume_store_reads_and_writes(1, 1)?;608609		check_is_owner_or_admin(caller, self)?;610		let permissions = CollectionPermissions {611			mint_mode: Some(mode),612			..Default::default()613		};614		self.collection.permissions = <Pallet<T>>::clamp_permissions(615			self.collection.mode.clone(),616			&self.collection.permissions,617			permissions,618		)619		.map_err(dispatch_to_evm::<T>)?;620621		save(self)622	}623624	/// Check that account is the owner or admin of the collection625	///626	/// @param user account to verify627	/// @return "true" if account is the owner or admin628	#[solidity(rename_selector = "isOwnerOrAdmin")]629	fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {630		let user = T::CrossAccountId::from_eth(user);631		Ok(self.is_owner_or_admin(&user))632	}633634	/// Check that account is the owner or admin of the collection635	///636	/// @param user User cross account to verify637	/// @return "true" if account is the owner or admin638	fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {639		let user = user.into_sub_cross_account::<T>()?;640		Ok(self.is_owner_or_admin(&user))641	}642643	/// Returns collection type644	///645	/// @return `Fungible` or `NFT` or `ReFungible`646	fn unique_collection_type(&self) -> Result<string> {647		let mode = match self.collection.mode {648			CollectionMode::Fungible(_) => "Fungible",649			CollectionMode::NFT => "NFT",650			CollectionMode::ReFungible => "ReFungible",651		};652		Ok(mode.into())653	}654655	/// Get collection owner.656	///657	/// @return Tuble with sponsor address and his substrate mirror.658	/// If address is canonical then substrate mirror is zero and vice versa.659	fn collection_owner(&self) -> Result<EthCrossAccount> {660		Ok(EthCrossAccount::from_sub_cross_account::<T>(661			&T::CrossAccountId::from_sub(self.owner.clone()),662		))663	}664665	/// Changes collection owner to another account666	///667	/// @dev Owner can be changed only by current owner668	/// @param newOwner new owner account669	#[solidity(rename_selector = "changeCollectionOwner")]670	fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {671		self.consume_store_writes(1)?;672673		let caller = T::CrossAccountId::from_eth(caller);674		let new_owner = T::CrossAccountId::from_eth(new_owner);675		self.set_owner_internal(caller, new_owner)676			.map_err(dispatch_to_evm::<T>)677	}678679	/// Get collection administrators680	///681	/// @return Vector of tuples with admins address and his substrate mirror.682	/// If address is canonical then substrate mirror is zero and vice versa.683	fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {684		let result = crate::IsAdmin::<T>::iter_prefix((self.id,))685			.map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))686			.collect();687		Ok(result)688	}689690	/// Changes collection owner to another account691	///692	/// @dev Owner can be changed only by current owner693	/// @param newOwner new owner cross account694	fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {695		self.consume_store_writes(1)?;696697		let caller = T::CrossAccountId::from_eth(caller);698		let new_owner = new_owner.into_sub_cross_account::<T>()?;699		self.set_owner_internal(caller, new_owner)700			.map_err(dispatch_to_evm::<T>)701	}702}703704/// ### Note705/// Do not forget to add: `self.consume_store_reads(1)?;`706fn check_is_owner_or_admin<T: Config>(707	caller: caller,708	collection: &CollectionHandle<T>,709) -> Result<T::CrossAccountId> {710	let caller = T::CrossAccountId::from_eth(caller);711	collection712		.check_is_owner_or_admin(&caller)713		.map_err(dispatch_to_evm::<T>)?;714	Ok(caller)715}716717/// ### Note718/// Do not forget to add: `self.consume_store_writes(1)?;`719fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {720	collection721		.check_is_internal()722		.map_err(dispatch_to_evm::<T>)?;723	collection.save().map_err(dispatch_to_evm::<T>)?;724	Ok(())725}726727/// Contains static property keys and values.728pub mod static_property {729	use evm_coder::{730		execution::{Result, Error},731	};732	use alloc::format;733734	const EXPECT_CONVERT_ERROR: &str = "length < limit";735736	/// Keys.737	pub mod key {738		use super::*;739740		/// Key "baseURI".741		pub fn base_uri() -> up_data_structs::PropertyKey {742			property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)743		}744745		/// Key "url".746		pub fn url() -> up_data_structs::PropertyKey {747			property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)748		}749750		/// Key "suffix".751		pub fn suffix() -> up_data_structs::PropertyKey {752			property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)753		}754755		/// Key "parentNft".756		pub fn parent_nft() -> up_data_structs::PropertyKey {757			property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)758		}759	}760761	/// Convert `byte` to [`PropertyKey`].762	pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {763		bytes.to_vec().try_into().map_err(|_| {764			Error::Revert(format!(765				"Property key is too long. Max length is {}.",766				up_data_structs::PropertyKey::bound()767			))768		})769	}770771	/// Convert `bytes` to [`PropertyValue`].772	pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {773		bytes.to_vec().try_into().map_err(|_| {774			Error::Revert(format!(775				"Property key is too long. Max length is {}.",776				up_data_structs::PropertyKey::bound()777			))778		})779	}780}
after · pallets/common/src/erc.rs
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};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::convert_cross_account_to_uint256, weights::WeightInfo,37};3839/// Events for ethereum collection helper.40#[derive(ToLog)]41pub enum CollectionHelpersEvents {42	/// The collection has been created.43	CollectionCreated {44		/// Collection owner.45		#[indexed]46		owner: address,4748		/// Collection ID.49		#[indexed]50		collection_id: address,51	},52	/// The collection has been destroyed.53	CollectionDestroyed {54		/// Collection ID.55		#[indexed]56		collection_id: address,57	},58}5960/// Does not always represent a full collection, for RFT it is either61/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).62pub trait CommonEvmHandler {63	const CODE: &'static [u8];6465	/// Call precompiled handle.66	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;67}6869/// @title A contract that allows you to work with collections.70#[solidity_interface(name = Collection)]71impl<T: Config> CollectionHandle<T>72where73	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,74{75	/// Set collection property.76	///77	/// @param key Property key.78	/// @param value Propery value.79	#[weight(<SelfWeightOf<T>>::set_collection_properties(1))]80	fn set_collection_property(81		&mut self,82		caller: caller,83		key: string,84		value: bytes,85	) -> Result<void> {86		let caller = T::CrossAccountId::from_eth(caller);87		let key = <Vec<u8>>::from(key)88			.try_into()89			.map_err(|_| "key too large")?;90		let value = value.0.try_into().map_err(|_| "value too large")?;9192		<Pallet<T>>::set_collection_property(self, &caller, Property { key, value })93			.map_err(dispatch_to_evm::<T>)94	}9596	/// Set collection properties.97	///98	/// @param properties Vector of properties key/value pair.99	#[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]100	fn set_collection_properties(101		&mut self,102		caller: caller,103		properties: Vec<(string, bytes)>,104	) -> Result<void> {105		let caller = T::CrossAccountId::from_eth(caller);106107		let properties = properties108			.into_iter()109			.map(|(key, value)| {110				let key = <Vec<u8>>::from(key)111					.try_into()112					.map_err(|_| "key too large")?;113114				let value = value.0.try_into().map_err(|_| "value too large")?;115116				Ok(Property { key, value })117			})118			.collect::<Result<Vec<_>>>()?;119120		<Pallet<T>>::set_collection_properties(self, &caller, properties)121			.map_err(dispatch_to_evm::<T>)122	}123124	/// Delete collection property.125	///126	/// @param key Property key.127	#[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]128	fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {129		let caller = T::CrossAccountId::from_eth(caller);130		let key = <Vec<u8>>::from(key)131			.try_into()132			.map_err(|_| "key too large")?;133134		<Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)135	}136137	/// Delete collection properties.138	///139	/// @param keys Properties keys.140	#[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]141	fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {142		let caller = T::CrossAccountId::from_eth(caller);143		let keys = keys144			.into_iter()145			.map(|key| {146				<Vec<u8>>::from(key)147					.try_into()148					.map_err(|_| Error::Revert("key too large".into()))149			})150			.collect::<Result<Vec<_>>>()?;151152		<Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)153	}154155	/// Get collection property.156	///157	/// @dev Throws error if key not found.158	///159	/// @param key Property key.160	/// @return bytes The property corresponding to the key.161	fn collection_property(&self, key: string) -> Result<bytes> {162		let key = <Vec<u8>>::from(key)163			.try_into()164			.map_err(|_| "key too large")?;165166		let props = CollectionProperties::<T>::get(self.id);167		let prop = props.get(&key).ok_or("key not found")?;168169		Ok(bytes(prop.to_vec()))170	}171172	/// Get collection properties.173	///174	/// @param keys Properties keys. Empty keys for all propertyes.175	/// @return Vector of properties key/value pairs.176	fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {177		let keys = keys178			.into_iter()179			.map(|key| {180				<Vec<u8>>::from(key)181					.try_into()182					.map_err(|_| Error::Revert("key too large".into()))183			})184			.collect::<Result<Vec<_>>>()?;185186		let properties = Pallet::<T>::filter_collection_properties(187			self.id,188			if keys.is_empty() { None } else { Some(keys) },189		)190		.map_err(dispatch_to_evm::<T>)?;191192		let properties = properties193			.into_iter()194			.map(|p| {195				let key =196					string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;197				let value = bytes(p.value.to_vec());198				Ok((key, value))199			})200			.collect::<Result<Vec<_>>>()?;201		Ok(properties)202	}203204	/// Set the sponsor of the collection.205	///206	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.207	///208	/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.209	fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {210		self.consume_store_reads_and_writes(1, 1)?;211212		check_is_owner_or_admin(caller, self)?;213214		let sponsor = T::CrossAccountId::from_eth(sponsor);215		self.set_sponsor(sponsor.as_sub().clone())216			.map_err(dispatch_to_evm::<T>)?;217		save(self)218	}219220	/// Set the sponsor of the collection.221	///222	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.223	///224	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.225	fn set_collection_sponsor_cross(226		&mut self,227		caller: caller,228		sponsor: EthCrossAccount,229	) -> Result<void> {230		self.consume_store_reads_and_writes(1, 1)?;231232		check_is_owner_or_admin(caller, self)?;233234		let sponsor = sponsor.into_sub_cross_account::<T>()?;235		self.set_sponsor(sponsor.as_sub().clone())236			.map_err(dispatch_to_evm::<T>)?;237		save(self)238	}239240	/// Whether there is a pending sponsor.241	fn has_collection_pending_sponsor(&self) -> Result<bool> {242		Ok(matches!(243			self.collection.sponsorship,244			SponsorshipState::Unconfirmed(_)245		))246	}247248	/// Collection sponsorship confirmation.249	///250	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.251	fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {252		self.consume_store_writes(1)?;253254		let caller = T::CrossAccountId::from_eth(caller);255		if !self256			.confirm_sponsorship(caller.as_sub())257			.map_err(dispatch_to_evm::<T>)?258		{259			return Err("caller is not set as sponsor".into());260		}261		save(self)262	}263264	/// Remove collection sponsor.265	fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {266		self.consume_store_reads_and_writes(1, 1)?;267		check_is_owner_or_admin(caller, self)?;268		self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;269		save(self)270	}271272	/// Get current sponsor.273	///274	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.275	fn collection_sponsor(&self) -> Result<(address, uint256)> {276		let sponsor = match self.collection.sponsorship.sponsor() {277			Some(sponsor) => sponsor,278			None => return Ok(Default::default()),279		};280		let sponsor = T::CrossAccountId::from_sub(sponsor.clone());281		let result: (address, uint256) = if sponsor.is_canonical_substrate() {282			let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);283			(Default::default(), sponsor)284		} else {285			let sponsor = *sponsor.as_eth();286			(sponsor, Default::default())287		};288		Ok(result)289	}290291	/// Set limits for the collection.292	/// @dev Throws error if limit not found.293	/// @param limit Name of the limit. Valid names:294	/// 	"accountTokenOwnershipLimit",295	/// 	"sponsoredDataSize",296	/// 	"sponsoredDataRateLimit",297	/// 	"tokenLimit",298	/// 	"sponsorTransferTimeout",299	/// 	"sponsorApproveTimeout"300	/// @param value Value of the limit.301	#[solidity(rename_selector = "setCollectionLimit")]302	fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {303		self.consume_store_reads_and_writes(1, 1)?;304305		check_is_owner_or_admin(caller, self)?;306		let mut limits = self.limits.clone();307308		match limit.as_str() {309			"accountTokenOwnershipLimit" => {310				limits.account_token_ownership_limit = Some(value);311			}312			"sponsoredDataSize" => {313				limits.sponsored_data_size = Some(value);314			}315			"sponsoredDataRateLimit" => {316				limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));317			}318			"tokenLimit" => {319				limits.token_limit = Some(value);320			}321			"sponsorTransferTimeout" => {322				limits.sponsor_transfer_timeout = Some(value);323			}324			"sponsorApproveTimeout" => {325				limits.sponsor_approve_timeout = Some(value);326			}327			_ => {328				return Err(Error::Revert(format!(329					"unknown integer limit \"{}\"",330					limit331				)))332			}333		}334		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)335			.map_err(dispatch_to_evm::<T>)?;336		save(self)337	}338339	/// Set limits for the collection.340	/// @dev Throws error if limit not found.341	/// @param limit Name of the limit. Valid names:342	/// 	"ownerCanTransfer",343	/// 	"ownerCanDestroy",344	/// 	"transfersEnabled"345	/// @param value Value of the limit.346	#[solidity(rename_selector = "setCollectionLimit")]347	fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {348		self.consume_store_reads_and_writes(1, 1)?;349350		check_is_owner_or_admin(caller, self)?;351		let mut limits = self.limits.clone();352353		match limit.as_str() {354			"ownerCanTransfer" => {355				limits.owner_can_transfer = Some(value);356			}357			"ownerCanDestroy" => {358				limits.owner_can_destroy = Some(value);359			}360			"transfersEnabled" => {361				limits.transfers_enabled = Some(value);362			}363			_ => {364				return Err(Error::Revert(format!(365					"unknown boolean limit \"{}\"",366					limit367				)))368			}369		}370		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)371			.map_err(dispatch_to_evm::<T>)?;372		save(self)373	}374375	/// Get contract address.376	fn contract_address(&self) -> Result<address> {377		Ok(crate::eth::collection_id_to_address(self.id))378	}379380	/// Add collection admin.381	/// @param newAdmin Cross account administrator address.382	fn add_collection_admin_cross(383		&mut self,384		caller: caller,385		new_admin: EthCrossAccount,386	) -> Result<void> {387		self.consume_store_writes(2)?;388389		let caller = T::CrossAccountId::from_eth(caller);390		let new_admin = new_admin.into_sub_cross_account::<T>()?;391		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;392		Ok(())393	}394395	/// Remove collection admin.396	/// @param admin Cross account administrator address.397	fn remove_collection_admin_cross(398		&mut self,399		caller: caller,400		admin: EthCrossAccount,401	) -> Result<void> {402		self.consume_store_writes(2)?;403404		let caller = T::CrossAccountId::from_eth(caller);405		let admin = admin.into_sub_cross_account::<T>()?;406		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;407		Ok(())408	}409410	/// Add collection admin.411	/// @param newAdmin Address of the added administrator.412	fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {413		self.consume_store_writes(2)?;414415		let caller = T::CrossAccountId::from_eth(caller);416		let new_admin = T::CrossAccountId::from_eth(new_admin);417		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;418		Ok(())419	}420421	/// Remove collection admin.422	///423	/// @param admin Address of the removed administrator.424	fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {425		self.consume_store_writes(2)?;426427		let caller = T::CrossAccountId::from_eth(caller);428		let admin = T::CrossAccountId::from_eth(admin);429		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;430		Ok(())431	}432433	/// Toggle accessibility of collection nesting.434	///435	/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'436	#[solidity(rename_selector = "setCollectionNesting")]437	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {438		self.consume_store_reads_and_writes(1, 1)?;439440		check_is_owner_or_admin(caller, self)?;441442		let mut permissions = self.collection.permissions.clone();443		let mut nesting = permissions.nesting().clone();444		nesting.token_owner = enable;445		nesting.restricted = None;446		permissions.nesting = Some(nesting);447448		self.collection.permissions = <Pallet<T>>::clamp_permissions(449			self.collection.mode.clone(),450			&self.collection.permissions,451			permissions,452		)453		.map_err(dispatch_to_evm::<T>)?;454455		save(self)456	}457458	/// Toggle accessibility of collection nesting.459	///460	/// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'461	/// @param collections Addresses of collections that will be available for nesting.462	#[solidity(rename_selector = "setCollectionNesting")]463	fn set_nesting(464		&mut self,465		caller: caller,466		enable: bool,467		collections: Vec<address>,468	) -> Result<void> {469		self.consume_store_reads_and_writes(1, 1)?;470471		if collections.is_empty() {472			return Err("no addresses provided".into());473		}474		check_is_owner_or_admin(caller, self)?;475476		let mut permissions = self.collection.permissions.clone();477		match enable {478			false => {479				let mut nesting = permissions.nesting().clone();480				nesting.token_owner = false;481				nesting.restricted = None;482				permissions.nesting = Some(nesting);483			}484			true => {485				let mut bv = OwnerRestrictedSet::new();486				for i in collections {487					bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {488						Error::Revert("Can't convert address into collection id".into())489					})?)490					.map_err(|_| "too many collections")?;491				}492				let mut nesting = permissions.nesting().clone();493				nesting.token_owner = true;494				nesting.restricted = Some(bv);495				permissions.nesting = Some(nesting);496			}497		};498499		self.collection.permissions = <Pallet<T>>::clamp_permissions(500			self.collection.mode.clone(),501			&self.collection.permissions,502			permissions,503		)504		.map_err(dispatch_to_evm::<T>)?;505506		save(self)507	}508509	/// Set the collection access method.510	/// @param mode Access mode511	/// 	0 for Normal512	/// 	1 for AllowList513	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {514		self.consume_store_reads_and_writes(1, 1)?;515516		check_is_owner_or_admin(caller, self)?;517		let permissions = CollectionPermissions {518			access: Some(match mode {519				0 => AccessMode::Normal,520				1 => AccessMode::AllowList,521				_ => return Err("not supported access mode".into()),522			}),523			..Default::default()524		};525		self.collection.permissions = <Pallet<T>>::clamp_permissions(526			self.collection.mode.clone(),527			&self.collection.permissions,528			permissions,529		)530		.map_err(dispatch_to_evm::<T>)?;531532		save(self)533	}534535	/// Checks that user allowed to operate with collection.536	///537	/// @param user User address to check.538	fn allowed(&self, user: address) -> Result<bool> {539		Ok(Pallet::<T>::allowed(540			self.id,541			T::CrossAccountId::from_eth(user),542		))543	}544545	/// Add the user to the allowed list.546	///547	/// @param user Address of a trusted user.548	fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {549		self.consume_store_writes(1)?;550551		let caller = T::CrossAccountId::from_eth(caller);552		let user = T::CrossAccountId::from_eth(user);553		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;554		Ok(())555	}556557	/// Add user to allowed list.558	///559	/// @param user User cross account address.560	fn add_to_collection_allow_list_cross(561		&mut self,562		caller: caller,563		user: EthCrossAccount,564	) -> Result<void> {565		self.consume_store_writes(1)?;566567		let caller = T::CrossAccountId::from_eth(caller);568		let user = user.into_sub_cross_account::<T>()?;569		Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;570		Ok(())571	}572573	/// Remove the user from the allowed list.574	///575	/// @param user Address of a removed user.576	fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {577		self.consume_store_writes(1)?;578579		let caller = T::CrossAccountId::from_eth(caller);580		let user = T::CrossAccountId::from_eth(user);581		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;582		Ok(())583	}584585	/// Remove user from allowed list.586	///587	/// @param user User cross account address.588	fn remove_from_collection_allow_list_cross(589		&mut self,590		caller: caller,591		user: EthCrossAccount,592	) -> Result<void> {593		self.consume_store_writes(1)?;594595		let caller = T::CrossAccountId::from_eth(caller);596		let user = user.into_sub_cross_account::<T>()?;597		Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;598		Ok(())599	}600601	/// Switch permission for minting.602	///603	/// @param mode Enable if "true".604	fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {605		self.consume_store_reads_and_writes(1, 1)?;606607		check_is_owner_or_admin(caller, self)?;608		let permissions = CollectionPermissions {609			mint_mode: Some(mode),610			..Default::default()611		};612		self.collection.permissions = <Pallet<T>>::clamp_permissions(613			self.collection.mode.clone(),614			&self.collection.permissions,615			permissions,616		)617		.map_err(dispatch_to_evm::<T>)?;618619		save(self)620	}621622	/// Check that account is the owner or admin of the collection623	///624	/// @param user account to verify625	/// @return "true" if account is the owner or admin626	#[solidity(rename_selector = "isOwnerOrAdmin")]627	fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {628		let user = T::CrossAccountId::from_eth(user);629		Ok(self.is_owner_or_admin(&user))630	}631632	/// Check that account is the owner or admin of the collection633	///634	/// @param user User cross account to verify635	/// @return "true" if account is the owner or admin636	fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {637		let user = user.into_sub_cross_account::<T>()?;638		Ok(self.is_owner_or_admin(&user))639	}640641	/// Returns collection type642	///643	/// @return `Fungible` or `NFT` or `ReFungible`644	fn unique_collection_type(&self) -> Result<string> {645		let mode = match self.collection.mode {646			CollectionMode::Fungible(_) => "Fungible",647			CollectionMode::NFT => "NFT",648			CollectionMode::ReFungible => "ReFungible",649		};650		Ok(mode.into())651	}652653	/// Get collection owner.654	///655	/// @return Tuble with sponsor address and his substrate mirror.656	/// If address is canonical then substrate mirror is zero and vice versa.657	fn collection_owner(&self) -> Result<EthCrossAccount> {658		Ok(EthCrossAccount::from_sub_cross_account::<T>(659			&T::CrossAccountId::from_sub(self.owner.clone()),660		))661	}662663	/// Changes collection owner to another account664	///665	/// @dev Owner can be changed only by current owner666	/// @param newOwner new owner account667	#[solidity(rename_selector = "changeCollectionOwner")]668	fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {669		self.consume_store_writes(1)?;670671		let caller = T::CrossAccountId::from_eth(caller);672		let new_owner = T::CrossAccountId::from_eth(new_owner);673		self.set_owner_internal(caller, new_owner)674			.map_err(dispatch_to_evm::<T>)675	}676677	/// Get collection administrators678	///679	/// @return Vector of tuples with admins address and his substrate mirror.680	/// If address is canonical then substrate mirror is zero and vice versa.681	fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {682		let result = crate::IsAdmin::<T>::iter_prefix((self.id,))683			.map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))684			.collect();685		Ok(result)686	}687688	/// Changes collection owner to another account689	///690	/// @dev Owner can be changed only by current owner691	/// @param newOwner new owner cross account692	fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {693		self.consume_store_writes(1)?;694695		let caller = T::CrossAccountId::from_eth(caller);696		let new_owner = new_owner.into_sub_cross_account::<T>()?;697		self.set_owner_internal(caller, new_owner)698			.map_err(dispatch_to_evm::<T>)699	}700}701702/// ### Note703/// Do not forget to add: `self.consume_store_reads(1)?;`704fn check_is_owner_or_admin<T: Config>(705	caller: caller,706	collection: &CollectionHandle<T>,707) -> Result<T::CrossAccountId> {708	let caller = T::CrossAccountId::from_eth(caller);709	collection710		.check_is_owner_or_admin(&caller)711		.map_err(dispatch_to_evm::<T>)?;712	Ok(caller)713}714715/// ### Note716/// Do not forget to add: `self.consume_store_writes(1)?;`717fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {718	collection719		.check_is_internal()720		.map_err(dispatch_to_evm::<T>)?;721	collection.save().map_err(dispatch_to_evm::<T>)?;722	Ok(())723}724725/// Contains static property keys and values.726pub mod static_property {727	use evm_coder::{728		execution::{Result, Error},729	};730	use alloc::format;731732	const EXPECT_CONVERT_ERROR: &str = "length < limit";733734	/// Keys.735	pub mod key {736		use super::*;737738		/// Key "baseURI".739		pub fn base_uri() -> up_data_structs::PropertyKey {740			property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)741		}742743		/// Key "url".744		pub fn url() -> up_data_structs::PropertyKey {745			property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)746		}747748		/// Key "suffix".749		pub fn suffix() -> up_data_structs::PropertyKey {750			property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)751		}752753		/// Key "parentNft".754		pub fn parent_nft() -> up_data_structs::PropertyKey {755			property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)756		}757	}758759	/// Convert `byte` to [`PropertyKey`].760	pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {761		bytes.to_vec().try_into().map_err(|_| {762			Error::Revert(format!(763				"Property key is too long. Max length is {}.",764				up_data_structs::PropertyKey::bound()765			))766		})767	}768769	/// Convert `bytes` to [`PropertyValue`].770	pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {771		bytes.to_vec().try_into().map_err(|_| {772			Error::Revert(format!(773				"Property key is too long. Max length is {}.",774				up_data_structs::PropertyKey::bound()775			))776		})777	}778}
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -19,13 +19,7 @@
 extern crate alloc;
 use core::marker::PhantomData;
 use evm_coder::{
-	abi::AbiWriter,
-	execution::Result,
-	generate_stubgen, solidity_interface,
-	types::*,
-	ToLog,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
+	abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,
 };
 use pallet_evm::{
 	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -19,15 +19,7 @@
 extern crate alloc;
 use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
 use core::convert::TryInto;
-use evm_coder::{
-	ToLog,
-	execution::*,
-	generate_stubgen, solidity_interface,
-	types::*,
-	weight,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
-};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
 use up_data_structs::CollectionMode;
 use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
 use sp_std::vec::Vec;
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -24,15 +24,7 @@
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,
 };
-use evm_coder::{
-	ToLog,
-	execution::*,
-	generate_stubgen, solidity, solidity_interface,
-	types::*,
-	weight,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
-};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
 use frame_support::BoundedVec;
 use up_data_structs::{
 	TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -25,15 +25,7 @@
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,
 };
-use evm_coder::{
-	ToLog,
-	execution::*,
-	generate_stubgen, solidity, solidity_interface,
-	types::*,
-	weight,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
-};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
 use frame_support::{BoundedBTreeMap, BoundedVec};
 use pallet_common::{
 	CollectionHandle, CollectionPropertyPermissions,
modifiedpallets/refungible/src/erc_token.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -29,15 +29,7 @@
 	convert::TryInto,
 	ops::Deref,
 };
-use evm_coder::{
-	ToLog,
-	execution::*,
-	generate_stubgen, solidity_interface,
-	types::*,
-	weight,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
-};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
 use pallet_common::{
 	CommonWeightInfo,
 	erc::{CommonEvmHandler, PrecompileResult},
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -18,13 +18,7 @@
 
 use core::marker::PhantomData;
 use ethereum as _;
-use evm_coder::{
-	execution::*,
-	generate_stubgen, solidity, solidity_interface,
-	types::*,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature, weight,
-};
+use evm_coder::{execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
 use frame_support::traits::Get;
 use crate::Pallet;