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
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -21,8 +21,6 @@
 	types::*,
 	execution::{Result, Error},
 	weight,
-	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
-	make_signature,
 };
 pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};
 use pallet_evm_coder_substrate::dispatch_to_evm;
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
before · pallets/unique/src/eth/mod.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//! Implementation of CollectionHelpers contract.1819use core::marker::PhantomData;20use ethereum as _;21use evm_coder::{22	execution::*,23	generate_stubgen, solidity, solidity_interface,24	types::*,25	custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},26	make_signature, weight,27};28use frame_support::traits::Get;29use crate::Pallet;3031use pallet_common::{32	CollectionById,33	dispatch::CollectionDispatch,34	erc::{static_property::key, CollectionHelpersEvents},35	Pallet as PalletCommon,36};37use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};38use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};39use sp_std::vec;40use up_data_structs::{41	CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,42	CreateCollectionData,43};4445use crate::{weights::WeightInfo, Config, SelfWeightOf};4647use alloc::format;48use sp_std::vec::Vec;4950/// See [`CollectionHelpersCall`]51pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);52impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {53	fn recorder(&self) -> &SubstrateRecorder<T> {54		&self.055	}5657	fn into_recorder(self) -> SubstrateRecorder<T> {58		self.059	}60}6162fn convert_data<T: Config>(63	caller: caller,64	name: string,65	description: string,66	token_prefix: string,67) -> Result<(68	T::CrossAccountId,69	CollectionName,70	CollectionDescription,71	CollectionTokenPrefix,72)> {73	let caller = T::CrossAccountId::from_eth(caller);74	let name = name75		.encode_utf16()76		.collect::<Vec<u16>>()77		.try_into()78		.map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;79	let description = description80		.encode_utf16()81		.collect::<Vec<u16>>()82		.try_into()83		.map_err(|_| {84			error_field_too_long(stringify!(description), CollectionDescription::bound())85		})?;86	let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {87		error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())88	})?;89	Ok((caller, name, description, token_prefix))90}9192#[inline(always)]93fn create_collection_internal<T: Config>(94	caller: caller,95	value: value,96	name: string,97	collection_mode: CollectionMode,98	description: string,99	token_prefix: string,100) -> Result<address> {101	let (caller, name, description, token_prefix) =102		convert_data::<T>(caller, name, description, token_prefix)?;103	let data = CreateCollectionData {104		name,105		mode: collection_mode,106		description,107		token_prefix,108		..Default::default()109	};110	check_sent_amount_equals_collection_creation_price::<T>(value)?;111	let collection_helpers_address =112		T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());113114	let collection_id = T::CollectionDispatch::create(115		caller.clone(),116		collection_helpers_address,117		data,118		Default::default(),119	)120	.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;121	let address = pallet_common::eth::collection_id_to_address(collection_id);122	Ok(address)123}124125fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {126	let value = value.as_u128();127	let creation_price: u128 = T::CollectionCreationPrice::get()128		.try_into()129		.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait130		.expect("Collection creation price should be convertible to u128");131	if value != creation_price {132		return Err(format!(133			"Sent amount not equals to collection creation price ({0})",134			creation_price135		)136		.into());137	}138	Ok(())139}140141/// @title Contract, which allows users to operate with collections142#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]143impl<T> EvmCollectionHelpers<T>144where145	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,146{147	/// Create an NFT collection148	/// @param name Name of the collection149	/// @param description Informative description of the collection150	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications151	/// @return address Address of the newly created collection152	#[weight(<SelfWeightOf<T>>::create_collection())]153	#[solidity(rename_selector = "createNFTCollection")]154	fn create_nft_collection(155		&mut self,156		caller: caller,157		value: value,158		name: string,159		description: string,160		token_prefix: string,161	) -> Result<address> {162		let (caller, name, description, token_prefix) =163			convert_data::<T>(caller, name, description, token_prefix)?;164		let data = CreateCollectionData {165			name,166			mode: CollectionMode::NFT,167			description,168			token_prefix,169			..Default::default()170		};171		check_sent_amount_equals_collection_creation_price::<T>(value)?;172		let collection_helpers_address =173			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());174		let collection_id = T::CollectionDispatch::create(175			caller,176			collection_helpers_address,177			data,178			Default::default(),179		)180		.map_err(dispatch_to_evm::<T>)?;181182		let address = pallet_common::eth::collection_id_to_address(collection_id);183		Ok(address)184	}185	/// Create an NFT collection186	/// @param name Name of the collection187	/// @param description Informative description of the collection188	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications189	/// @return address Address of the newly created collection190	#[weight(<SelfWeightOf<T>>::create_collection())]191	#[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]192	#[solidity(hide)]193	fn create_nonfungible_collection(194		&mut self,195		caller: caller,196		value: value,197		name: string,198		description: string,199		token_prefix: string,200	) -> Result<address> {201		create_collection_internal::<T>(202			caller,203			value,204			name,205			CollectionMode::NFT,206			description,207			token_prefix,208		)209	}210211	#[weight(<SelfWeightOf<T>>::create_collection())]212	#[solidity(rename_selector = "createRFTCollection")]213	fn create_rft_collection(214		&mut self,215		caller: caller,216		value: value,217		name: string,218		description: string,219		token_prefix: string,220	) -> Result<address> {221		create_collection_internal::<T>(222			caller,223			value,224			name,225			CollectionMode::ReFungible,226			description,227			token_prefix,228		)229	}230231	#[weight(<SelfWeightOf<T>>::create_collection())]232	#[solidity(rename_selector = "createFTCollection")]233	fn create_fungible_collection(234		&mut self,235		caller: caller,236		value: value,237		name: string,238		decimals: uint8,239		description: string,240		token_prefix: string,241	) -> Result<address> {242		create_collection_internal::<T>(243			caller,244			value,245			name,246			CollectionMode::Fungible(decimals),247			description,248			token_prefix,249		)250	}251252	#[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]253	fn make_collection_metadata_compatible(254		&mut self,255		caller: caller,256		collection: address,257		base_uri: string,258	) -> Result<()> {259		let caller = T::CrossAccountId::from_eth(caller);260		let collection =261			pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;262		let mut collection =263			<crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;264265		if !matches!(266			collection.mode,267			CollectionMode::NFT | CollectionMode::ReFungible268		) {269			return Err("target collection should be either NFT or Refungible".into());270		}271272		self.recorder().consume_sstore()?;273		collection274			.check_is_owner_or_admin(&caller)275			.map_err(dispatch_to_evm::<T>)?;276277		if collection.flags.erc721metadata {278			return Err("target collection is already Erc721Metadata compatible".into());279		}280		collection.flags.erc721metadata = true;281282		let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);283		if all_permissions.get(&key::url()).is_none() {284			self.recorder().consume_sstore()?;285			<PalletCommon<T>>::set_property_permission(286				&collection,287				&caller,288				up_data_structs::PropertyKeyPermission {289					key: key::url(),290					permission: up_data_structs::PropertyPermission {291						mutable: true,292						collection_admin: true,293						token_owner: false,294					},295				},296			)297			.map_err(dispatch_to_evm::<T>)?;298		}299		if all_permissions.get(&key::suffix()).is_none() {300			self.recorder().consume_sstore()?;301			<PalletCommon<T>>::set_property_permission(302				&collection,303				&caller,304				up_data_structs::PropertyKeyPermission {305					key: key::suffix(),306					permission: up_data_structs::PropertyPermission {307						mutable: true,308						collection_admin: true,309						token_owner: false,310					},311				},312			)313			.map_err(dispatch_to_evm::<T>)?;314		}315316		let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);317		if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {318			self.recorder().consume_sstore()?;319			<PalletCommon<T>>::set_collection_properties(320				&collection,321				&caller,322				vec![up_data_structs::Property {323					key: key::base_uri(),324					value: base_uri325						.into_bytes()326						.try_into()327						.map_err(|_| "base uri is too large")?,328				}],329			)330			.map_err(dispatch_to_evm::<T>)?;331		}332333		self.recorder().consume_sstore()?;334		collection.save().map_err(dispatch_to_evm::<T>)?;335336		Ok(())337	}338339	#[weight(<SelfWeightOf<T>>::destroy_collection())]340	fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {341		let caller = T::CrossAccountId::from_eth(caller);342343		let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)344			.ok_or("Invalid collection address format")?;345		<Pallet<T>>::destroy_collection_internal(caller, collection_id)346			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)347	}348349	/// Check if a collection exists350	/// @param collectionAddress Address of the collection in question351	/// @return bool Does the collection exist?352	fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {353		if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {354			let collection_id = id;355			return Ok(<CollectionById<T>>::contains_key(collection_id));356		}357358		Ok(false)359	}360361	fn collection_creation_fee(&self) -> Result<value> {362		let price: u128 = T::CollectionCreationPrice::get()363			.try_into()364			.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait365			.expect("Collection creation price should be convertible to u128");366		Ok(price.into())367	}368}369370/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]371pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);372impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>373	for CollectionHelpersOnMethodCall<T>374{375	fn is_reserved(contract: &sp_core::H160) -> bool {376		contract == &T::ContractAddress::get()377	}378379	fn is_used(contract: &sp_core::H160) -> bool {380		contract == &T::ContractAddress::get()381	}382383	fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {384		if handle.code_address() != T::ContractAddress::get() {385			return None;386		}387388		let helpers =389			EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));390		pallet_evm_coder_substrate::call(handle, helpers)391	}392393	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {394		(contract == &T::ContractAddress::get())395			.then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())396	}397}398399generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);400generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);401402fn error_field_too_long(feild: &str, bound: usize) -> Error {403	Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))404}
after · pallets/unique/src/eth/mod.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//! Implementation of CollectionHelpers contract.1819use core::marker::PhantomData;20use ethereum as _;21use evm_coder::{execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};22use frame_support::traits::Get;23use crate::Pallet;2425use pallet_common::{26	CollectionById,27	dispatch::CollectionDispatch,28	erc::{static_property::key, CollectionHelpersEvents},29	Pallet as PalletCommon,30};31use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};32use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};33use sp_std::vec;34use up_data_structs::{35	CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,36	CreateCollectionData,37};3839use crate::{weights::WeightInfo, Config, SelfWeightOf};4041use alloc::format;42use sp_std::vec::Vec;4344/// See [`CollectionHelpersCall`]45pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);46impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {47	fn recorder(&self) -> &SubstrateRecorder<T> {48		&self.049	}5051	fn into_recorder(self) -> SubstrateRecorder<T> {52		self.053	}54}5556fn convert_data<T: Config>(57	caller: caller,58	name: string,59	description: string,60	token_prefix: string,61) -> Result<(62	T::CrossAccountId,63	CollectionName,64	CollectionDescription,65	CollectionTokenPrefix,66)> {67	let caller = T::CrossAccountId::from_eth(caller);68	let name = name69		.encode_utf16()70		.collect::<Vec<u16>>()71		.try_into()72		.map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;73	let description = description74		.encode_utf16()75		.collect::<Vec<u16>>()76		.try_into()77		.map_err(|_| {78			error_field_too_long(stringify!(description), CollectionDescription::bound())79		})?;80	let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {81		error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())82	})?;83	Ok((caller, name, description, token_prefix))84}8586#[inline(always)]87fn create_collection_internal<T: Config>(88	caller: caller,89	value: value,90	name: string,91	collection_mode: CollectionMode,92	description: string,93	token_prefix: string,94) -> Result<address> {95	let (caller, name, description, token_prefix) =96		convert_data::<T>(caller, name, description, token_prefix)?;97	let data = CreateCollectionData {98		name,99		mode: collection_mode,100		description,101		token_prefix,102		..Default::default()103	};104	check_sent_amount_equals_collection_creation_price::<T>(value)?;105	let collection_helpers_address =106		T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());107108	let collection_id = T::CollectionDispatch::create(109		caller.clone(),110		collection_helpers_address,111		data,112		Default::default(),113	)114	.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;115	let address = pallet_common::eth::collection_id_to_address(collection_id);116	Ok(address)117}118119fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {120	let value = value.as_u128();121	let creation_price: u128 = T::CollectionCreationPrice::get()122		.try_into()123		.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait124		.expect("Collection creation price should be convertible to u128");125	if value != creation_price {126		return Err(format!(127			"Sent amount not equals to collection creation price ({0})",128			creation_price129		)130		.into());131	}132	Ok(())133}134135/// @title Contract, which allows users to operate with collections136#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]137impl<T> EvmCollectionHelpers<T>138where139	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,140{141	/// Create an NFT collection142	/// @param name Name of the collection143	/// @param description Informative description of the collection144	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications145	/// @return address Address of the newly created collection146	#[weight(<SelfWeightOf<T>>::create_collection())]147	#[solidity(rename_selector = "createNFTCollection")]148	fn create_nft_collection(149		&mut self,150		caller: caller,151		value: value,152		name: string,153		description: string,154		token_prefix: string,155	) -> Result<address> {156		let (caller, name, description, token_prefix) =157			convert_data::<T>(caller, name, description, token_prefix)?;158		let data = CreateCollectionData {159			name,160			mode: CollectionMode::NFT,161			description,162			token_prefix,163			..Default::default()164		};165		check_sent_amount_equals_collection_creation_price::<T>(value)?;166		let collection_helpers_address =167			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());168		let collection_id = T::CollectionDispatch::create(169			caller,170			collection_helpers_address,171			data,172			Default::default(),173		)174		.map_err(dispatch_to_evm::<T>)?;175176		let address = pallet_common::eth::collection_id_to_address(collection_id);177		Ok(address)178	}179	/// Create an NFT collection180	/// @param name Name of the collection181	/// @param description Informative description of the collection182	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications183	/// @return address Address of the newly created collection184	#[weight(<SelfWeightOf<T>>::create_collection())]185	#[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]186	#[solidity(hide)]187	fn create_nonfungible_collection(188		&mut self,189		caller: caller,190		value: value,191		name: string,192		description: string,193		token_prefix: string,194	) -> Result<address> {195		create_collection_internal::<T>(196			caller,197			value,198			name,199			CollectionMode::NFT,200			description,201			token_prefix,202		)203	}204205	#[weight(<SelfWeightOf<T>>::create_collection())]206	#[solidity(rename_selector = "createRFTCollection")]207	fn create_rft_collection(208		&mut self,209		caller: caller,210		value: value,211		name: string,212		description: string,213		token_prefix: string,214	) -> Result<address> {215		create_collection_internal::<T>(216			caller,217			value,218			name,219			CollectionMode::ReFungible,220			description,221			token_prefix,222		)223	}224225	#[weight(<SelfWeightOf<T>>::create_collection())]226	#[solidity(rename_selector = "createFTCollection")]227	fn create_fungible_collection(228		&mut self,229		caller: caller,230		value: value,231		name: string,232		decimals: uint8,233		description: string,234		token_prefix: string,235	) -> Result<address> {236		create_collection_internal::<T>(237			caller,238			value,239			name,240			CollectionMode::Fungible(decimals),241			description,242			token_prefix,243		)244	}245246	#[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]247	fn make_collection_metadata_compatible(248		&mut self,249		caller: caller,250		collection: address,251		base_uri: string,252	) -> Result<()> {253		let caller = T::CrossAccountId::from_eth(caller);254		let collection =255			pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;256		let mut collection =257			<crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;258259		if !matches!(260			collection.mode,261			CollectionMode::NFT | CollectionMode::ReFungible262		) {263			return Err("target collection should be either NFT or Refungible".into());264		}265266		self.recorder().consume_sstore()?;267		collection268			.check_is_owner_or_admin(&caller)269			.map_err(dispatch_to_evm::<T>)?;270271		if collection.flags.erc721metadata {272			return Err("target collection is already Erc721Metadata compatible".into());273		}274		collection.flags.erc721metadata = true;275276		let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);277		if all_permissions.get(&key::url()).is_none() {278			self.recorder().consume_sstore()?;279			<PalletCommon<T>>::set_property_permission(280				&collection,281				&caller,282				up_data_structs::PropertyKeyPermission {283					key: key::url(),284					permission: up_data_structs::PropertyPermission {285						mutable: true,286						collection_admin: true,287						token_owner: false,288					},289				},290			)291			.map_err(dispatch_to_evm::<T>)?;292		}293		if all_permissions.get(&key::suffix()).is_none() {294			self.recorder().consume_sstore()?;295			<PalletCommon<T>>::set_property_permission(296				&collection,297				&caller,298				up_data_structs::PropertyKeyPermission {299					key: key::suffix(),300					permission: up_data_structs::PropertyPermission {301						mutable: true,302						collection_admin: true,303						token_owner: false,304					},305				},306			)307			.map_err(dispatch_to_evm::<T>)?;308		}309310		let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);311		if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {312			self.recorder().consume_sstore()?;313			<PalletCommon<T>>::set_collection_properties(314				&collection,315				&caller,316				vec![up_data_structs::Property {317					key: key::base_uri(),318					value: base_uri319						.into_bytes()320						.try_into()321						.map_err(|_| "base uri is too large")?,322				}],323			)324			.map_err(dispatch_to_evm::<T>)?;325		}326327		self.recorder().consume_sstore()?;328		collection.save().map_err(dispatch_to_evm::<T>)?;329330		Ok(())331	}332333	#[weight(<SelfWeightOf<T>>::destroy_collection())]334	fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {335		let caller = T::CrossAccountId::from_eth(caller);336337		let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)338			.ok_or("Invalid collection address format")?;339		<Pallet<T>>::destroy_collection_internal(caller, collection_id)340			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)341	}342343	/// Check if a collection exists344	/// @param collectionAddress Address of the collection in question345	/// @return bool Does the collection exist?346	fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {347		if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {348			let collection_id = id;349			return Ok(<CollectionById<T>>::contains_key(collection_id));350		}351352		Ok(false)353	}354355	fn collection_creation_fee(&self) -> Result<value> {356		let price: u128 = T::CollectionCreationPrice::get()357			.try_into()358			.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait359			.expect("Collection creation price should be convertible to u128");360		Ok(price.into())361	}362}363364/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]365pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);366impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>367	for CollectionHelpersOnMethodCall<T>368{369	fn is_reserved(contract: &sp_core::H160) -> bool {370		contract == &T::ContractAddress::get()371	}372373	fn is_used(contract: &sp_core::H160) -> bool {374		contract == &T::ContractAddress::get()375	}376377	fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {378		if handle.code_address() != T::ContractAddress::get() {379			return None;380		}381382		let helpers =383			EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));384		pallet_evm_coder_substrate::call(handle, helpers)385	}386387	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {388		(contract == &T::ContractAddress::get())389			.then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())390	}391}392393generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);394generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);395396fn error_field_too_long(feild: &str, bound: usize) -> Error {397	Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))398}