git.delta.rocks / unique-network / refs/commits / 48f959311e08

difftreelog

chore code review requests

Grigoriy Simonov2022-10-12parent: #f8de52d.patch.diff
in: master

8 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -709,9 +709,6 @@
 		/// Value "1" ERC721 metadata supported.
 		pub const ERC721_METADATA_SUPPORTED: &[u8] = b"1";
 
-		/// Value "0" ERC721 metadata supported.
-		pub const ERC721_METADATA_UNSUPPORTED: &[u8] = b"0";
-
 		/// Value for [`ERC721_METADATA`].
 		pub fn erc721() -> up_data_structs::PropertyValue {
 			property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)
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::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};22use frame_support::traits::Get;23use pallet_common::{24	CollectionById,25	dispatch::CollectionDispatch,26	erc::{27		CollectionHelpersEvents,28		static_property::{key, value as property_value},29	},30};31use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};32use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};33use up_data_structs::{34	CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,35	CollectionMode, PropertyValue,36};3738use crate::{Config, SelfWeightOf, weights::WeightInfo};3940use sp_std::vec::Vec;41use alloc::format;4243/// See [`CollectionHelpersCall`]44pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);45impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {46	fn recorder(&self) -> &SubstrateRecorder<T> {47		&self.048	}4950	fn into_recorder(self) -> SubstrateRecorder<T> {51		self.052	}53}5455fn convert_data<T: Config>(56	caller: caller,57	name: string,58	description: string,59	token_prefix: string,60	base_uri: string,61) -> Result<(62	T::CrossAccountId,63	CollectionName,64	CollectionDescription,65	CollectionTokenPrefix,66	PropertyValue,67)> {68	let caller = T::CrossAccountId::from_eth(caller);69	let name = name70		.encode_utf16()71		.collect::<Vec<u16>>()72		.try_into()73		.map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;74	let description = description75		.encode_utf16()76		.collect::<Vec<u16>>()77		.try_into()78		.map_err(|_| {79			error_field_too_long(stringify!(description), CollectionDescription::bound())80		})?;81	let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {82		error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())83	})?;84	let base_uri_value = base_uri85		.into_bytes()86		.try_into()87		.map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;88	Ok((caller, name, description, token_prefix, base_uri_value))89}9091fn make_data<T: Config>(92	name: CollectionName,93	mode: CollectionMode,94	description: CollectionDescription,95	token_prefix: CollectionTokenPrefix,96	base_uri_value: PropertyValue,97	add_properties: bool,98) -> Result<CreateCollectionData<T::AccountId>> {99	let mut properties = up_data_structs::CollectionPropertiesVec::default();100	let mut token_property_permissions =101		up_data_structs::CollectionPropertiesPermissionsVec::default();102103	token_property_permissions104		.try_push(up_data_structs::PropertyKeyPermission {105			key: key::url(),106			permission: up_data_structs::PropertyPermission {107				mutable: false,108				collection_admin: true,109				token_owner: false,110			},111		})112		.map_err(|e| Error::Revert(format!("{:?}", e)))?;113114	if add_properties {115		token_property_permissions116			.try_push(up_data_structs::PropertyKeyPermission {117				key: key::suffix(),118				permission: up_data_structs::PropertyPermission {119					mutable: true,120					collection_admin: true,121					token_owner: false,122				},123			})124			.map_err(|e| Error::Revert(format!("{:?}", e)))?;125126		token_property_permissions127			.try_push(up_data_structs::PropertyKeyPermission {128				key: key::url(),129				permission: up_data_structs::PropertyPermission {130					mutable: true,131					collection_admin: true,132					token_owner: false,133				},134			})135			.map_err(|e| Error::Revert(format!("{:?}", e)))?;136137		properties138			.try_push(up_data_structs::Property {139				key: key::schema_name(),140				value: property_value::erc721(),141			})142			.map_err(|e| Error::Revert(format!("{:?}", e)))?;143144		properties145			.try_push(up_data_structs::Property {146				key: key::schema_version(),147				value: property_value::schema_version(),148			})149			.map_err(|e| Error::Revert(format!("{:?}", e)))?;150151		properties152			.try_push(up_data_structs::Property {153				key: key::erc721_metadata(),154				value: property_value::erc721_metadata_supported(),155			})156			.map_err(|e| Error::Revert(format!("{:?}", e)))?;157158		if !base_uri_value.is_empty() {159			properties160				.try_push(up_data_structs::Property {161					key: key::base_uri(),162					value: base_uri_value,163				})164				.map_err(|e| Error::Revert(format!("{:?}", e)))?;165		}166	}167168	let data = CreateCollectionData {169		name,170		mode,171		description,172		token_prefix,173		token_property_permissions,174		properties,175		..Default::default()176	};177	Ok(data)178}179180fn create_refungible_collection_internal<181	T: Config + pallet_nonfungible::Config + pallet_refungible::Config,182>(183	caller: caller,184	value: value,185	name: string,186	description: string,187	token_prefix: string,188	base_uri: string,189	add_properties: bool,190) -> Result<address> {191	let (caller, name, description, token_prefix, base_uri_value) =192		convert_data::<T>(caller, name, description, token_prefix, base_uri)?;193	let data = make_data::<T>(194		name,195		CollectionMode::ReFungible,196		description,197		token_prefix,198		base_uri_value,199		add_properties,200	)?;201	check_sent_amount_equals_collection_creation_price::<T>(value)?;202	let collection_helpers_address =203		T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());204205	let collection_id =206		T::CollectionDispatch::create(caller.clone(), collection_helpers_address, data)207			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;208	let address = pallet_common::eth::collection_id_to_address(collection_id);209	Ok(address)210}211212fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {213	let value = value.as_u128();214	let creation_price: u128 = T::CollectionCreationPrice::get()215		.try_into()216		.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait217		.expect("Collection creation price should be convertible to u128");218	if value != creation_price {219		return Err(format!(220			"Sent amount not equals to collection creation price ({0})",221			creation_price222		)223		.into());224	}225	Ok(())226}227228/// @title Contract, which allows users to operate with collections229#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]230impl<T> EvmCollectionHelpers<T>231where232	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,233{234	/// Create an NFT collection235	/// @param name Name of the collection236	/// @param description Informative description of the collection237	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications238	/// @return address Address of the newly created collection239	#[weight(<SelfWeightOf<T>>::create_collection())]240	#[solidity(rename_selector = "createNFTCollection")]241	fn create_nft_collection(242		&mut self,243		caller: caller,244		value: value,245		name: string,246		description: string,247		token_prefix: string,248	) -> Result<address> {249		let (caller, name, description, token_prefix, _base_uri_value) =250			convert_data::<T>(caller, name, description, token_prefix, "".into())?;251		let data = make_data::<T>(252			name,253			CollectionMode::NFT,254			description,255			token_prefix,256			Default::default(),257			false,258		)?;259		check_sent_amount_equals_collection_creation_price::<T>(value)?;260		let collection_helpers_address =261			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());262		let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)263			.map_err(dispatch_to_evm::<T>)?;264265		let address = pallet_common::eth::collection_id_to_address(collection_id);266		Ok(address)267	}268	/// Create an NFT collection269	/// @param name Name of the collection270	/// @param description Informative description of the collection271	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications272	/// @return address Address of the newly created collection273	#[weight(<SelfWeightOf<T>>::create_collection())]274	#[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]275	fn create_nonfungible_collection(276		&mut self,277		caller: caller,278		value: value,279		name: string,280		description: string,281		token_prefix: string,282	) -> Result<address> {283		self.create_nft_collection(caller, value, name, description, token_prefix)284	}285286	#[weight(<SelfWeightOf<T>>::create_collection())]287	#[solidity(rename_selector = "createERC721MetadataCompatibleNFTCollection")]288	fn create_nonfungible_collection_with_properties(289		&mut self,290		caller: caller,291		value: value,292		name: string,293		description: string,294		token_prefix: string,295		base_uri: string,296	) -> Result<address> {297		let (caller, name, description, token_prefix, base_uri_value) =298			convert_data::<T>(caller, name, description, token_prefix, base_uri)?;299		let data = make_data::<T>(300			name,301			CollectionMode::NFT,302			description,303			token_prefix,304			base_uri_value,305			true,306		)?;307		check_sent_amount_equals_collection_creation_price::<T>(value)?;308		let collection_helpers_address =309			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());310		let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)311			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;312313		let address = pallet_common::eth::collection_id_to_address(collection_id);314		Ok(address)315	}316317	#[weight(<SelfWeightOf<T>>::create_collection())]318	#[solidity(rename_selector = "createRFTCollection")]319	fn create_rft_collection(320		&mut self,321		caller: caller,322		value: value,323		name: string,324		description: string,325		token_prefix: string,326	) -> Result<address> {327		create_refungible_collection_internal::<T>(328			caller,329			value,330			name,331			description,332			token_prefix,333			Default::default(),334			false,335		)336	}337338	#[weight(<SelfWeightOf<T>>::create_collection())]339	#[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]340	fn create_refungible_collection_with_properties(341		&mut self,342		caller: caller,343		value: value,344		name: string,345		description: string,346		token_prefix: string,347		base_uri: string,348	) -> Result<address> {349		create_refungible_collection_internal::<T>(350			caller,351			value,352			name,353			description,354			token_prefix,355			base_uri,356			true,357		)358	}359360	/// Check if a collection exists361	/// @param collectionAddress Address of the collection in question362	/// @return bool Does the collection exist?363	fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {364		if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {365			let collection_id = id;366			return Ok(<CollectionById<T>>::contains_key(collection_id));367		}368369		Ok(false)370	}371372	fn collection_creation_fee(&self) -> Result<value> {373		let price: u128 = T::CollectionCreationPrice::get()374			.try_into()375			.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait376			.expect("Collection creation price should be convertible to u128");377		Ok(price.into())378	}379}380381/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]382pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);383impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>384	for CollectionHelpersOnMethodCall<T>385{386	fn is_reserved(contract: &sp_core::H160) -> bool {387		contract == &T::ContractAddress::get()388	}389390	fn is_used(contract: &sp_core::H160) -> bool {391		contract == &T::ContractAddress::get()392	}393394	fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {395		if handle.code_address() != T::ContractAddress::get() {396			return None;397		}398399		let helpers =400			EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));401		pallet_evm_coder_substrate::call(handle, helpers)402	}403404	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {405		(contract == &T::ContractAddress::get())406			.then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())407	}408}409410generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);411generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);412413fn error_field_too_long(feild: &str, bound: usize) -> Error {414	Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))415}
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_interface, solidity, weight, types::*};22use frame_support::traits::Get;23use pallet_common::{24	CollectionById,25	dispatch::CollectionDispatch,26	erc::{27		CollectionHelpersEvents,28		static_property::{key, value as property_value},29	},30};31use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};32use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};33use up_data_structs::{34	CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,35	CollectionMode, PropertyValue,36};3738use crate::{Config, SelfWeightOf, weights::WeightInfo};3940use sp_std::vec::Vec;41use alloc::format;4243/// See [`CollectionHelpersCall`]44pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);45impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {46	fn recorder(&self) -> &SubstrateRecorder<T> {47		&self.048	}4950	fn into_recorder(self) -> SubstrateRecorder<T> {51		self.052	}53}5455fn convert_data<T: Config>(56	caller: caller,57	name: string,58	description: string,59	token_prefix: string,60	base_uri: string,61) -> Result<(62	T::CrossAccountId,63	CollectionName,64	CollectionDescription,65	CollectionTokenPrefix,66	PropertyValue,67)> {68	let caller = T::CrossAccountId::from_eth(caller);69	let name = name70		.encode_utf16()71		.collect::<Vec<u16>>()72		.try_into()73		.map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;74	let description = description75		.encode_utf16()76		.collect::<Vec<u16>>()77		.try_into()78		.map_err(|_| {79			error_field_too_long(stringify!(description), CollectionDescription::bound())80		})?;81	let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {82		error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())83	})?;84	let base_uri_value = base_uri85		.into_bytes()86		.try_into()87		.map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;88	Ok((caller, name, description, token_prefix, base_uri_value))89}9091fn make_data<T: Config>(92	name: CollectionName,93	mode: CollectionMode,94	description: CollectionDescription,95	token_prefix: CollectionTokenPrefix,96	base_uri_value: PropertyValue,97	add_properties: bool,98) -> Result<CreateCollectionData<T::AccountId>> {99	let mut properties = up_data_structs::CollectionPropertiesVec::default();100	let mut token_property_permissions =101		up_data_structs::CollectionPropertiesPermissionsVec::default();102103	token_property_permissions104		.try_push(up_data_structs::PropertyKeyPermission {105			key: key::url(),106			permission: up_data_structs::PropertyPermission {107				mutable: true,108				collection_admin: true,109				token_owner: false,110			},111		})112		.map_err(|e| Error::Revert(format!("{:?}", e)))?;113114	if add_properties {115		token_property_permissions116			.try_push(up_data_structs::PropertyKeyPermission {117				key: key::suffix(),118				permission: up_data_structs::PropertyPermission {119					mutable: true,120					collection_admin: true,121					token_owner: false,122				},123			})124			.map_err(|e| Error::Revert(format!("{:?}", e)))?;125126		properties127			.try_push(up_data_structs::Property {128				key: key::schema_name(),129				value: property_value::erc721(),130			})131			.map_err(|e| Error::Revert(format!("{:?}", e)))?;132133		properties134			.try_push(up_data_structs::Property {135				key: key::schema_version(),136				value: property_value::schema_version(),137			})138			.map_err(|e| Error::Revert(format!("{:?}", e)))?;139140		properties141			.try_push(up_data_structs::Property {142				key: key::erc721_metadata(),143				value: property_value::erc721_metadata_supported(),144			})145			.map_err(|e| Error::Revert(format!("{:?}", e)))?;146147		if !base_uri_value.is_empty() {148			properties149				.try_push(up_data_structs::Property {150					key: key::base_uri(),151					value: base_uri_value,152				})153				.map_err(|e| Error::Revert(format!("{:?}", e)))?;154		}155	}156157	let data = CreateCollectionData {158		name,159		mode,160		description,161		token_prefix,162		token_property_permissions,163		properties,164		..Default::default()165	};166	Ok(data)167}168169fn create_refungible_collection_internal<170	T: Config + pallet_nonfungible::Config + pallet_refungible::Config,171>(172	caller: caller,173	value: value,174	name: string,175	description: string,176	token_prefix: string,177	base_uri: string,178	add_properties: bool,179) -> Result<address> {180	let (caller, name, description, token_prefix, base_uri_value) =181		convert_data::<T>(caller, name, description, token_prefix, base_uri)?;182	let data = make_data::<T>(183		name,184		CollectionMode::ReFungible,185		description,186		token_prefix,187		base_uri_value,188		add_properties,189	)?;190	check_sent_amount_equals_collection_creation_price::<T>(value)?;191	let collection_helpers_address =192		T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());193194	let collection_id =195		T::CollectionDispatch::create(caller.clone(), collection_helpers_address, data)196			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;197	let address = pallet_common::eth::collection_id_to_address(collection_id);198	Ok(address)199}200201fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {202	let value = value.as_u128();203	let creation_price: u128 = T::CollectionCreationPrice::get()204		.try_into()205		.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait206		.expect("Collection creation price should be convertible to u128");207	if value != creation_price {208		return Err(format!(209			"Sent amount not equals to collection creation price ({0})",210			creation_price211		)212		.into());213	}214	Ok(())215}216217/// @title Contract, which allows users to operate with collections218#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]219impl<T> EvmCollectionHelpers<T>220where221	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,222{223	/// Create an NFT collection224	/// @param name Name of the collection225	/// @param description Informative description of the collection226	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications227	/// @return address Address of the newly created collection228	#[weight(<SelfWeightOf<T>>::create_collection())]229	#[solidity(rename_selector = "createNFTCollection")]230	fn create_nft_collection(231		&mut self,232		caller: caller,233		value: value,234		name: string,235		description: string,236		token_prefix: string,237	) -> Result<address> {238		let (caller, name, description, token_prefix, _base_uri_value) =239			convert_data::<T>(caller, name, description, token_prefix, "".into())?;240		let data = make_data::<T>(241			name,242			CollectionMode::NFT,243			description,244			token_prefix,245			Default::default(),246			false,247		)?;248		check_sent_amount_equals_collection_creation_price::<T>(value)?;249		let collection_helpers_address =250			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());251		let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)252			.map_err(dispatch_to_evm::<T>)?;253254		let address = pallet_common::eth::collection_id_to_address(collection_id);255		Ok(address)256	}257	/// Create an NFT collection258	/// @param name Name of the collection259	/// @param description Informative description of the collection260	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications261	/// @return address Address of the newly created collection262	#[weight(<SelfWeightOf<T>>::create_collection())]263	#[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]264	fn create_nonfungible_collection(265		&mut self,266		caller: caller,267		value: value,268		name: string,269		description: string,270		token_prefix: string,271	) -> Result<address> {272		self.create_nft_collection(caller, value, name, description, token_prefix)273	}274275	#[weight(<SelfWeightOf<T>>::create_collection())]276	#[solidity(rename_selector = "createERC721MetadataCompatibleNFTCollection")]277	fn create_nonfungible_collection_with_properties(278		&mut self,279		caller: caller,280		value: value,281		name: string,282		description: string,283		token_prefix: string,284		base_uri: string,285	) -> Result<address> {286		let (caller, name, description, token_prefix, base_uri_value) =287			convert_data::<T>(caller, name, description, token_prefix, base_uri)?;288		let data = make_data::<T>(289			name,290			CollectionMode::NFT,291			description,292			token_prefix,293			base_uri_value,294			true,295		)?;296		check_sent_amount_equals_collection_creation_price::<T>(value)?;297		let collection_helpers_address =298			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());299		let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)300			.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;301302		let address = pallet_common::eth::collection_id_to_address(collection_id);303		Ok(address)304	}305306	#[weight(<SelfWeightOf<T>>::create_collection())]307	#[solidity(rename_selector = "createRFTCollection")]308	fn create_rft_collection(309		&mut self,310		caller: caller,311		value: value,312		name: string,313		description: string,314		token_prefix: string,315	) -> Result<address> {316		create_refungible_collection_internal::<T>(317			caller,318			value,319			name,320			description,321			token_prefix,322			Default::default(),323			false,324		)325	}326327	#[weight(<SelfWeightOf<T>>::create_collection())]328	#[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]329	fn create_refungible_collection_with_properties(330		&mut self,331		caller: caller,332		value: value,333		name: string,334		description: string,335		token_prefix: string,336		base_uri: string,337	) -> Result<address> {338		create_refungible_collection_internal::<T>(339			caller,340			value,341			name,342			description,343			token_prefix,344			base_uri,345			true,346		)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}
modifiedtests/src/deprecated-helpers/helpers.tsdiffbeforeafterboth
--- a/tests/src/deprecated-helpers/helpers.ts
+++ b/tests/src/deprecated-helpers/helpers.ts
@@ -433,7 +433,6 @@
   mode: {type: 'NFT'},
   name: 'name',
   tokenPrefix: 'prefix',
-  properties: [{key: 'ERC721Metadata', value: '1'}],
 };
 
 export async function
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -80,7 +80,7 @@
     await usingEthPlaygrounds(async (helper, privateKey) => {
       const donor = privateKey('//Alice');
       const [alice] = await helper.arrange.createAccounts([10n], donor);
-      ({collectionId: collection} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));
+      ({collectionId: collection} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties: [{key: 'ERC721Metadata', value: '1'}]}));
       minter = helper.eth.createAccount();
     });
   });
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -501,7 +501,7 @@
 
   itEth('Returns collection name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE', properties: [{key: 'ERC721Metadata', value: '1'}]});
 
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const name = await contract.methods.name().call();
@@ -510,7 +510,7 @@
 
   itEth('Returns symbol name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE'});
+    const collection = await helper.nft.mintCollection(alice, {name: 'oh River', tokenPrefix: 'CHANGE', properties: [{key: 'ERC721Metadata', value: '1'}]});
 
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
     const symbol = await contract.methods.symbol().call();
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -377,7 +377,7 @@
 
   itEth('Returns collection name', async ({helper}) => {
     const caller = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11'});
+    const collection = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '11', properties: [{key: 'ERC721Metadata', value: '1'}]});
     
     const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
     const name = await contract.methods.name().call();
@@ -386,7 +386,7 @@
 
   itEth('Returns symbol name', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
-    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '12'});
+    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'Leviathan', tokenPrefix: '12', properties: [{key: 'ERC721Metadata', value: '1'}]});
     const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);
     const symbol = await contract.methods.symbol().call();
     expect(symbol).to.equal('12');
modifiedtests/src/nesting/properties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/properties.test.ts
+++ b/tests/src/nesting/properties.test.ts
@@ -35,7 +35,7 @@
     expect(collectionOption).is.not.null;
     let collection = collectionOption;
     expect(collection.tokenPropertyPermissions).to.be.empty;
-    expect(collection.properties).to.be.deep.equal([{key: 'ERC721Metadata', value: '1'}]);
+    expect(collection.properties).to.be.empty;
 
     const propertyPermissions = [
       {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},
@@ -44,7 +44,6 @@
     await expect(await baseCollection.setTokenPropertyPermissions(alice, propertyPermissions)).to.be.true;
 
     const collectionProperties = [
-      {key: 'ERC721Metadata', value: '1'}, 
       {key: 'black_hole', value: 'LIGO'},
       {key: 'electron', value: 'come bond'}, 
     ];
@@ -80,10 +79,7 @@
   itSub('Properties are initially empty', async ({helper}) => {
     const collection = await helper.nft.mintCollection(alice);
     const properties = await collection.getProperties();
-    expect(properties).to.be.deep.equal([{
-      'key': 'ERC721Metadata',
-      'value': '1',
-    }]);
+    expect(properties).to.be.empty;
   });
 
   async function testSetsPropertiesForCollection(collection: UniqueBaseCollection) {
@@ -201,10 +197,7 @@
       .to.be.rejectedWith(/common\.NoPermission/);
 
     const properties = await collection.getProperties();
-    expect(properties).to.be.deep.equal([{
-      'key': 'ERC721Metadata',
-      'value': '1',
-    }]);
+    expect(properties).to.be.empty;
   }
 
   itSub('Fails to set properties in a NFT collection if not its onwer/administrator', async ({helper}) =>  {
@@ -257,10 +250,7 @@
       to.be.rejectedWith(/common\.PropertyLimitReached/);
 
     const properties = await collection.getProperties();
-    expect(properties).to.be.deep.equal([{
-      'key': 'ERC721Metadata',
-      'value': '1',
-    }]);
+    expect(properties).to.be.empty;
   }
 
   itSub('Fails to set more properties than it is allowed (NFT)', async ({helper}) =>  {
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -1297,7 +1297,6 @@
   async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
     collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
     collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
-    collectionOptions.properties = collectionOptions.properties || [{key: 'ERC721Metadata', value: '1'}];
     for (const key of ['name', 'description', 'tokenPrefix']) {
       if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
     }