git.delta.rocks / unique-network / refs/commits / 3b21e2ddf7e4

difftreelog

CORE-302 Fix compile after rebase

Trubnikov Sergey2022-05-20parent: #34309ac.patch.diff
in: master

6 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6818,11 +6818,15 @@
 name = "pallet-unique"
 version = "0.1.0"
 dependencies = [
+ "ethereum",
+ "evm-coder",
  "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-common",
  "pallet-evm",
+ "pallet-evm-coder-substrate",
+ "pallet-nonfungible",
  "parity-scale-codec 3.1.2",
  "scale-info",
  "serde",
modifiedpallets/unique/Cargo.tomldiffbeforeafterboth
--- a/pallets/unique/Cargo.toml
+++ b/pallets/unique/Cargo.toml
@@ -33,6 +33,22 @@
 limit-testing = ["up-data-structs/limit-testing"]
 
 ################################################################################
+# Standart Dependencies
+
+[dependencies.serde]
+default-features = false
+features = ['derive']
+version = '1.0.130'
+
+[dependencies.serde-json-core]
+default-features = false
+version = "0.4"
+
+[dependencies.ethereum]
+version = "0.12.0"
+default-features = false
+
+################################################################################
 # Substrate Dependencies
 
 [dependencies.codec]
@@ -66,15 +82,6 @@
 # default-features = false
 # git = "https://github.com/paritytech/substrate"
 # branch = "polkadot-v0.9.21"
-
-[dependencies.serde]
-default-features = false
-features = ['derive']
-version = '1.0.130'
-
-[dependencies.serde-json-core]
-default-features = false
-version = "0.4"
 
 [dependencies.sp-runtime]
 default-features = false
@@ -90,7 +97,6 @@
 default-features = false
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.21"
-
 
 ################################################################################
 # Local Dependencies
@@ -101,3 +107,6 @@
 ] }
 pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" }
 pallet-common = { default-features = false, path = "../common" }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
+pallet-nonfungible = { default-features = false, path = '../../pallets/nonfungible' }
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -14,100 +14,19 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-pub mod sponsoring;
-
-use fp_evm::PrecompileResult;
-use pallet_common::{
-	CollectionById,
-	erc::CommonEvmHandler,
-	eth::{map_eth_to_id, map_eth_to_token_id},
-};
-use pallet_fungible::FungibleHandle;
-use pallet_nonfungible::NonfungibleHandle;
-use pallet_refungible::{RefungibleHandle, erc::RefungibleTokenHandle};
-use sp_std::borrow::ToOwned;
-use sp_std::vec::Vec;
-use sp_core::{H160, U256};
-use crate::{CollectionMode, Config, dispatch::Dispatched};
-use pallet_common::CollectionHandle;
-
-pub struct UniqueErcSupport<T: Config>(core::marker::PhantomData<T>);
-
-impl<T: Config> pallet_evm::OnMethodCall<T> for UniqueErcSupport<T> {
-	fn is_reserved(target: &H160) -> bool {
-		map_eth_to_id(target).is_some()
-	}
-	fn is_used(target: &H160) -> bool {
-		map_eth_to_id(target)
-			.map(<CollectionById<T>>::contains_key)
-			.unwrap_or(false)
-	}
-	fn get_code(target: &H160) -> Option<Vec<u8>> {
-		if let Some(collection_id) = map_eth_to_id(target) {
-			let collection = <CollectionById<T>>::get(collection_id)?;
-			Some(
-				match collection.mode {
-					CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,
-					CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,
-					CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,
-				}
-				.to_owned(),
-			)
-		} else if let Some((collection_id, _token_id)) = map_eth_to_token_id(target) {
-			let collection = <CollectionById<T>>::get(collection_id)?;
-			if collection.mode != CollectionMode::ReFungible {
-				return None;
-			}
-			// TODO: check token existence
-			Some(<RefungibleTokenHandle<T>>::CODE.to_owned())
-		} else {
-			None
-		}
-	}
-	fn call(
-		source: &H160,
-		target: &H160,
-		gas_limit: u64,
-		input: &[u8],
-		value: U256,
-	) -> Option<PrecompileResult> {
-		if let Some(collection_id) = map_eth_to_id(target) {
-			let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
-			let dispatched = Dispatched::dispatch(collection);
-
-			match dispatched {
-				Dispatched::Fungible(h) => h.call(source, input, value),
-				Dispatched::Nonfungible(h) => h.call(source, input, value),
-				Dispatched::Refungible(h) => h.call(source, input, value),
-			}
-		} else if let Some((collection_id, token_id)) = map_eth_to_token_id(target) {
-			let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
-			if collection.mode != CollectionMode::ReFungible {
-				return None;
-			}
-
-			let handle = RefungibleHandle::cast(collection);
-			// TODO: check token existence
-			RefungibleTokenHandle(handle, token_id).call(source, input, value)
-		} else {
-			None
-		}
-	}
-}
-
 pub mod evm_collection {
 	use core::marker::PhantomData;
 	use evm_coder::{execution::*, generate_stubgen, solidity_interface, types::*, ToLog};
 	use ethereum as _;
 	use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
-	use pallet_evm::{OnMethodCall, PrecompileResult, account::CrossAccountId};
+	use pallet_evm::{OnMethodCall, PrecompileResult, account::CrossAccountId, Pallet as PalletEvm};
 	use up_data_structs::{
 		CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
 		MAX_COLLECTION_NAME_LENGTH,
 	};
 	use frame_support::traits::Get;
 	use sp_core::H160;
-	use pallet_common::{CollectionHandle, save_eth, pallet::CollectionById};
+	use pallet_common::{CollectionHandle, CollectionById};
 	
 	use sp_std::{vec::Vec, rc::Rc};
 	use alloc::format;
@@ -121,13 +40,13 @@
 		type ContractAddress: Get<H160>;
 	}
 
-	struct EvmCollectionHelper<T: Config>(Rc<SubstrateRecorder<T>>);
+	struct EvmCollectionHelper<T: Config>(SubstrateRecorder<T>);
 	impl<T: Config> WithRecorder<T> for EvmCollectionHelper<T> {
 		fn recorder(&self) -> &SubstrateRecorder<T> {
 			&self.0
 		}
 	
-		fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {
+		fn into_recorder(self) -> SubstrateRecorder<T> {
 			self.0
 		}
 	}
@@ -171,10 +90,13 @@
 					.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
 	
 			let address = pallet_common::eth::collection_id_to_address(collection_id);
-			self.0.log_mirrored(EthCollectionEvent::CollectionCreated {
-				owner: *caller.as_eth(),
-				collection_id: address,
-			});
+			<PalletEvm<T>>::deposit_log(
+				EthCollectionEvent::CollectionCreated {
+					owner: *caller.as_eth(),
+					collection_id: address,
+				}
+				.to_log(address),
+			);
 			Ok(address)
 		}
 
@@ -188,14 +110,14 @@
 		}
 	}
 	
-	struct EvmCollection<T: Config>(Rc<SubstrateRecorder<T>>);
+	struct EvmCollection<T: Config>(H160, SubstrateRecorder<T>);
 	impl<T: Config> WithRecorder<T> for EvmCollection<T> {
 		fn recorder(&self) -> &SubstrateRecorder<T> {
-			&self.0
+			&self.1
 		}
 	
-		fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {
-			self.0
+		fn into_recorder(self) -> SubstrateRecorder<T> {
+			self.1
 		}
 	}
 	
@@ -216,21 +138,23 @@
 			caller: caller,
 			sponsor: address,
 		) -> Result<void> {
-			let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;
+			let mut collection = collection_from_address::<T>(self.contract_address(caller).unwrap(), self.1.gas_left())?;
 			check_is_owner(caller, &collection)?;
 	
 			let sponsor = T::CrossAccountId::from_eth(sponsor);
 			collection.set_sponsor(sponsor.as_sub().clone());
-			save_eth(collection)
+			collection.save().map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+		Ok(())
 		}
 	
 		fn confirm_sponsorship(&self, caller: caller) -> Result<void> {
-			let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;
+			let mut collection = collection_from_address::<T>(self.contract_address(caller).unwrap(), self.1.gas_left())?;
 			let caller = T::CrossAccountId::from_eth(caller);
 			if !collection.confirm_sponsorship(caller.as_sub()) {
 				return Err(Error::Revert("Caller is not set as sponsor".into()));
 			}
-			save_eth(collection)
+			collection.save().map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+		Ok(())
 		}
 	
 		fn set_limits(
@@ -238,17 +162,18 @@
 			caller: caller,
 			limits_json: string,
 		) -> Result<void> {
-			let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;
+			let mut collection = collection_from_address::<T>(self.contract_address(caller).unwrap(), self.1.gas_left())?;
 			check_is_owner(caller, &collection)?;
 	
 			let limits = serde_json_core::from_str(limits_json.as_ref())
 				.map_err(|e| Error::Revert(format!("Parse JSON error: {}", e)))?;
 			collection.limits = limits.0;
-			save_eth(collection)
+			collection.save().map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+		Ok(())
 		}
 
 		fn contract_address(&self, _caller: caller) -> Result<address> {
-			Ok(self.0.contract())
+			Ok(self.0)
 		}
 	}
 	
@@ -258,12 +183,13 @@
 	
 	fn collection_from_address<T: Config>(
 		collection_address: address,
-		recorder: &Rc<SubstrateRecorder<T>>,
+		gas_limit: u64
 	) -> Result<CollectionHandle<T>> {
 		let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)
-			.ok_or(Error::Revert("Contract is not an unique collection".into()))?;
+		.ok_or(Error::Revert("Contract is not an unique collection".into()))?;
+		let recorder = <SubstrateRecorder<T>>::new(gas_limit);
 		let collection =
-			pallet_common::CollectionHandle::new_with_recorder(collection_id, recorder.clone())
+			pallet_common::CollectionHandle::new_with_recorder(collection_id, recorder)
 				.ok_or(Error::Revert("Create collection handle error".into()))?;
 		Ok(collection)
 	}
@@ -297,7 +223,7 @@
 				return None;
 			}
 	
-			let helpers = EvmCollectionHelper::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));
+			let helpers = EvmCollectionHelper::<T>(SubstrateRecorder::<T>::new(gas_left));
 			pallet_evm_coder_substrate::call(*source, helpers, value, input)
 		}
 	
@@ -331,7 +257,7 @@
 				return None;
 			}
 
-			let helpers = EvmCollection::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));
+			let helpers = EvmCollection::<T>(*target, SubstrateRecorder::<T>::new(gas_left));
 			pallet_evm_coder_substrate::call(*source, helpers, value, input)
 		}
 	
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
before · pallets/unique/src/lib.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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20	clippy::too_many_arguments,21	clippy::unnecessary_mut_passed,22	clippy::unused_unit23)]2425use frame_support::{26	decl_module, decl_storage, decl_error, decl_event,27	dispatch::DispatchResult,28	ensure,29	weights::{Weight},30	transactional,31	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},32	BoundedVec,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use up_data_structs::{38	MAX_COLLECTION_NAME_LENGTH,39	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,40	CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,41	CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,42	PropertyKeyPermission,43};44use pallet_evm::account::CrossAccountId;45use pallet_common::{46	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,47	dispatch::CollectionDispatch,48};49pub use eth::evm_collection;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;53pub mod weights;54use weights::WeightInfo;5556decl_error! {57	/// Error for non-fungible-token module.58	pub enum Error for Module<T: Config> {59		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.60		CollectionDecimalPointLimitExceeded,61		/// This address is not set as sponsor, use setCollectionSponsor first.62		ConfirmUnsetSponsorFail,63		/// Length of items properties must be greater than 0.64		EmptyArgument,65	}66}6768pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {69	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7071	/// Weight information for extrinsics in this pallet.72	type WeightInfo: WeightInfo;73	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;74}7576decl_event! {77	pub enum Event<T>78	where79		<T as frame_system::Config>::AccountId,80		<T as pallet_evm::account::Config>::CrossAccountId,81	{82		/// Collection sponsor was removed83		///84		/// # Arguments85		///86		/// * collection_id: Globally unique collection identifier.87		CollectionSponsorRemoved(CollectionId),8889		/// Collection admin was added90		///91		/// # Arguments92		///93		/// * collection_id: Globally unique collection identifier.94		///95		/// * admin:  Admin address.96		CollectionAdminAdded(CollectionId, CrossAccountId),9798		/// Collection owned was change99		///100		/// # Arguments101		///102		/// * collection_id: Globally unique collection identifier.103		///104		/// * owner:  New owner address.105		CollectionOwnedChanged(CollectionId, AccountId),106107		/// Collection sponsor was set108		///109		/// # Arguments110		///111		/// * collection_id: Globally unique collection identifier.112		///113		/// * owner:  New sponsor address.114		CollectionSponsorSet(CollectionId, AccountId),115116		/// const on chain schema was set117		///118		/// # Arguments119		///120		/// * collection_id: Globally unique collection identifier.121		ConstOnChainSchemaSet(CollectionId),122123		/// New sponsor was confirm124		///125		/// # Arguments126		///127		/// * collection_id: Globally unique collection identifier.128		///129		/// * sponsor:  New sponsor address.130		SponsorshipConfirmed(CollectionId, AccountId),131132		/// Collection admin was removed133		///134		/// # Arguments135		///136		/// * collection_id: Globally unique collection identifier.137		///138		/// * admin:  Admin address.139		CollectionAdminRemoved(CollectionId, CrossAccountId),140141		/// Address was remove from allow list142		///143		/// # Arguments144		///145		/// * collection_id: Globally unique collection identifier.146		///147		/// * user:  Address.148		AllowListAddressRemoved(CollectionId, CrossAccountId),149150		/// Address was add to allow list151		///152		/// # Arguments153		///154		/// * collection_id: Globally unique collection identifier.155		///156		/// * user:  Address.157		AllowListAddressAdded(CollectionId, CrossAccountId),158159		/// Collection limits was set160		///161		/// # Arguments162		///163		/// * collection_id: Globally unique collection identifier.164		CollectionLimitSet(CollectionId),165166		CollectionPermissionSet(CollectionId),167168		/// Mint permission	was set169		///170		/// # Arguments171		///172		/// * collection_id: Globally unique collection identifier.173		MintPermissionSet(CollectionId),174175		/// Offchain schema was set176		///177		/// # Arguments178		///179		/// * collection_id: Globally unique collection identifier.180		OffchainSchemaSet(CollectionId),181182		/// Public access mode was set183		///184		/// # Arguments185		///186		/// * collection_id: Globally unique collection identifier.187		///188		/// * mode: New access state.189		PublicAccessModeSet(CollectionId, AccessMode),190191		/// Schema version was set192		///193		/// # Arguments194		///195		/// * collection_id: Globally unique collection identifier.196		SchemaVersionSet(CollectionId),197	}198}199200type SelfWeightOf<T> = <T as Config>::WeightInfo;201202// # Used definitions203//204// ## User control levels205//206// chain-controlled - key is uncontrolled by user207//                    i.e autoincrementing index208//                    can use non-cryptographic hash209// real - key is controlled by user210//        but it is hard to generate enough colliding values, i.e owner of signed txs211//        can use non-cryptographic hash212// controlled - key is completly controlled by users213//              i.e maps with mutable keys214//              should use cryptographic hash215//216// ## User control level downgrade reasons217//218// ?1 - chain-controlled -> controlled219//      collections/tokens can be destroyed, resulting in massive holes220// ?2 - chain-controlled -> controlled221//      same as ?1, but can be only added, resulting in easier exploitation222// ?3 - real -> controlled223//      no confirmation required, so addresses can be easily generated224decl_storage! {225	trait Store for Module<T: Config> as Unique {226227		//#region Private members228		/// Used for migrations229		ChainVersion: u64;230		//#endregion231232		//#region Tokens transfer rate limit baskets233		/// (Collection id (controlled?2), who created (real))234		/// TODO: Off chain worker should remove from this map when collection gets removed235		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;236		/// Collection id (controlled?2), token id (controlled?2)237		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;238		/// Collection id (controlled?2), owning user (real)239		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;240		/// Collection id (controlled?2), token id (controlled?2)241		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;242		//#endregion243244		/// Variable metadata sponsoring245		/// Collection id (controlled?2), token id (controlled?2)246		#[deprecated]247		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;248		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;249250		/// Approval sponsoring251		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;252		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;253		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254	}255}256257decl_module! {258	pub struct Module<T: Config> for enum Call259	where260		origin: T::Origin261	{262		type Error = Error<T>;263264		fn deposit_event() = default;265266		fn on_initialize(_now: T::BlockNumber) -> Weight {267			0268		}269270		fn on_runtime_upgrade() -> Weight {271			let limit = None;272273			<VariableMetaDataBasket<T>>::remove_all(limit);274275			0276		}277278		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.279		///280		/// # Permissions281		///282		/// * Anyone.283		///284		/// # Arguments285		///286		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.287		///288		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.289		///290		/// * token_prefix: UTF-8 string with token prefix.291		///292		/// * mode: [CollectionMode] collection type and type dependent data.293		// returns collection ID294		#[weight = <SelfWeightOf<T>>::create_collection()]295		#[transactional]296		#[deprecated]297		pub fn create_collection(origin,298								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,299								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,300								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,301								 mode: CollectionMode) -> DispatchResult  {302			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {303				name: collection_name,304				description: collection_description,305				token_prefix,306				mode,307				..Default::default()308			};309			Self::create_collection_ex(origin, data)310		}311312		/// This method creates a collection313		///314		/// Prefer it to deprecated [`created_collection`] method315		#[weight = <SelfWeightOf<T>>::create_collection()]316		#[transactional]317		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {318			let sender = ensure_signed(origin)?;319320			// =========321322			T::CollectionDispatch::create(sender, data)?;323324			Ok(())325		}326327		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.328		///329		/// # Permissions330		///331		/// * Collection Owner.332		///333		/// # Arguments334		///335		/// * collection_id: collection to destroy.336		#[weight = <SelfWeightOf<T>>::destroy_collection()]337		#[transactional]338		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {339			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);340			let collection = <CollectionHandle<T>>::try_get(collection_id)?;341342			// =========343344			T::CollectionDispatch::destroy(sender, collection)?;345346			<NftTransferBasket<T>>::remove_prefix(collection_id, None);347			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);348			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);349350			<NftApproveBasket<T>>::remove_prefix(collection_id, None);351			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);352			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);353354			Ok(())355		}356357		/// Add an address to allow list.358		///359		/// # Permissions360		///361		/// * Collection Owner362		/// * Collection Admin363		///364		/// # Arguments365		///366		/// * collection_id.367		///368		/// * address.369		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]370		#[transactional]371		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{372373			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);374			let collection = <CollectionHandle<T>>::try_get(collection_id)?;375376			<PalletCommon<T>>::toggle_allowlist(377				&collection,378				&sender,379				&address,380				true,381			)?;382383			Self::deposit_event(Event::<T>::AllowListAddressAdded(384				collection_id,385				address386			));387388			Ok(())389		}390391		/// Remove an address from allow list.392		///393		/// # Permissions394		///395		/// * Collection Owner396		/// * Collection Admin397		///398		/// # Arguments399		///400		/// * collection_id.401		///402		/// * address.403		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]404		#[transactional]405		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{406407			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);408			let collection = <CollectionHandle<T>>::try_get(collection_id)?;409410			<PalletCommon<T>>::toggle_allowlist(411				&collection,412				&sender,413				&address,414				false,415			)?;416417			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(418				collection_id,419				address420			));421422			Ok(())423		}424425		/// Change the owner of the collection.426		///427		/// # Permissions428		///429		/// * Collection Owner.430		///431		/// # Arguments432		///433		/// * collection_id.434		///435		/// * new_owner.436		#[weight = <SelfWeightOf<T>>::change_collection_owner()]437		#[transactional]438		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {439440			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);441442			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;443			target_collection.check_is_owner(&sender)?;444445			target_collection.owner = new_owner.clone();446			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(447				collection_id,448				new_owner449			));450451			target_collection.save()452		}453454		/// Adds an admin of the Collection.455		/// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.456		///457		/// # Permissions458		///459		/// * Collection Owner.460		/// * Collection Admin.461		///462		/// # Arguments463		///464		/// * collection_id: ID of the Collection to add admin for.465		///466		/// * new_admin_id: Address of new admin to add.467		#[weight = <SelfWeightOf<T>>::add_collection_admin()]468		#[transactional]469		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {470			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);471			let collection = <CollectionHandle<T>>::try_get(collection_id)?;472473			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(474				collection_id,475				new_admin_id.clone()476			));477478			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)479		}480481		/// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.482		///483		/// # Permissions484		///485		/// * Collection Owner.486		/// * Collection Admin.487		///488		/// # Arguments489		///490		/// * collection_id: ID of the Collection to remove admin for.491		///492		/// * account_id: Address of admin to remove.493		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]494		#[transactional]495		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {496			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);497			let collection = <CollectionHandle<T>>::try_get(collection_id)?;498499			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(500				collection_id,501				account_id.clone()502			));503504			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)505		}506507		/// # Permissions508		///509		/// * Collection Owner510		///511		/// # Arguments512		///513		/// * collection_id.514		///515		/// * new_sponsor.516		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]517		#[transactional]518		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {519			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);520521			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;522			target_collection.check_is_owner(&sender)?;523524			target_collection.set_sponsor(new_sponsor.clone());525526			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(527				collection_id,528				new_sponsor529			));530531			target_collection.save()532		}533534		/// # Permissions535		///536		/// * Sponsor.537		///538		/// # Arguments539		///540		/// * collection_id.541		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]542		#[transactional]543		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {544			let sender = ensure_signed(origin)?;545546			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;547			ensure!(548				target_collection.confirm_sponsorship(&sender),549				Error::<T>::ConfirmUnsetSponsorFail550			);551552			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(553				collection_id,554				sender555			));556557			target_collection.save()558		}559560		/// Switch back to pay-per-own-transaction model.561		///562		/// # Permissions563		///564		/// * Collection owner.565		///566		/// # Arguments567		///568		/// * collection_id.569		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]570		#[transactional]571		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {572			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);573574			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;575			target_collection.check_is_owner(&sender)?;576577			target_collection.sponsorship = SponsorshipState::Disabled;578579			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(580				collection_id581			));582			target_collection.save()583		}584585		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.586		///587		/// # Permissions588		///589		/// * Collection Owner.590		/// * Collection Admin.591		/// * Anyone if592		///     * Allow List is enabled, and593		///     * Address is added to allow list, and594		///     * MintPermission is enabled (see SetMintPermission method)595		///596		/// # Arguments597		///598		/// * collection_id: ID of the collection.599		///600		/// * owner: Address, initial owner of the NFT.601		///602		/// * data: Token data to store on chain.603		#[weight = T::CommonWeightInfo::create_item()]604		#[transactional]605		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {606			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);607			let budget = budget::Value::new(2);608609			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))610		}611612		/// This method creates multiple items in a collection created with CreateCollection method.613		///614		/// # Permissions615		///616		/// * Collection Owner.617		/// * Collection Admin.618		/// * Anyone if619		///     * Allow List is enabled, and620		///     * Address is added to allow list, and621		///     * MintPermission is enabled (see SetMintPermission method)622		///623		/// # Arguments624		///625		/// * collection_id: ID of the collection.626		///627		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].628		///629		/// * owner: Address, initial owner of the NFT.630		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]631		#[transactional]632		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {633			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);634			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);635			let budget = budget::Value::new(2);636637			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))638		}639640		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]641		#[transactional]642		pub fn set_collection_properties(643			origin,644			collection_id: CollectionId,645			properties: Vec<Property>646		) -> DispatchResultWithPostInfo {647			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);648649			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);650651			dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))652		}653654		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]655		#[transactional]656		pub fn delete_collection_properties(657			origin,658			collection_id: CollectionId,659			property_keys: Vec<PropertyKey>,660		) -> DispatchResultWithPostInfo {661			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);662663			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);664665			dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))666		}667668		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]669		#[transactional]670		pub fn set_token_properties(671			origin,672			collection_id: CollectionId,673			token_id: TokenId,674			properties: Vec<Property>675		) -> DispatchResultWithPostInfo {676			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);677678			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);679680			dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))681		}682683		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]684		#[transactional]685		pub fn delete_token_properties(686			origin,687			collection_id: CollectionId,688			token_id: TokenId,689			property_keys: Vec<PropertyKey>690		) -> DispatchResultWithPostInfo {691			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);692693			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);694695			dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))696		}697698		#[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]699		#[transactional]700		pub fn set_property_permissions(701			origin,702			collection_id: CollectionId,703			property_permissions: Vec<PropertyKeyPermission>,704		) -> DispatchResultWithPostInfo {705			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);706707			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);708709			dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))710		}711712		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]713		#[transactional]714		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {715			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);716			let budget = budget::Value::new(2);717718			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))719		}720721		// TODO! transaction weight722723		/// Set transfers_enabled value for particular collection724		///725		/// # Permissions726		///727		/// * Collection Owner.728		///729		/// # Arguments730		///731		/// * collection_id: ID of the collection.732		///733		/// * value: New flag value.734		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]735		#[transactional]736		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {737			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);738			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;739			target_collection.check_is_owner(&sender)?;740741			// =========742743			target_collection.limits.transfers_enabled = Some(value);744			target_collection.save()745		}746747		/// Destroys a concrete instance of NFT.748		///749		/// # Permissions750		///751		/// * Collection Owner.752		/// * Collection Admin.753		/// * Current NFT Owner.754		///755		/// # Arguments756		///757		/// * collection_id: ID of the collection.758		///759		/// * item_id: ID of NFT to burn.760		#[weight = T::CommonWeightInfo::burn_item()]761		#[transactional]762		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {763			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);764765			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;766			if value == 1 {767				<NftTransferBasket<T>>::remove(collection_id, item_id);768				<NftApproveBasket<T>>::remove(collection_id, item_id);769			}770			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?771			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());772			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));773			Ok(post_info)774		}775776		/// Destroys a concrete instance of NFT on behalf of the owner777		/// See also: [`approve`]778		///779		/// # Permissions780		///781		/// * Collection Owner.782		/// * Collection Admin.783		/// * Current NFT Owner.784		///785		/// # Arguments786		///787		/// * collection_id: ID of the collection.788		///789		/// * item_id: ID of NFT to burn.790		///791		/// * from: owner of item792		#[weight = T::CommonWeightInfo::burn_from()]793		#[transactional]794		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {795			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);796			let budget = budget::Value::new(2);797798			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))799		}800801		/// Change ownership of the token.802		///803		/// # Permissions804		///805		/// * Collection Owner806		/// * Collection Admin807		/// * Current NFT owner808		///809		/// # Arguments810		///811		/// * recipient: Address of token recipient.812		///813		/// * collection_id.814		///815		/// * item_id: ID of the item816		///     * Non-Fungible Mode: Required.817		///     * Fungible Mode: Ignored.818		///     * Re-Fungible Mode: Required.819		///820		/// * value: Amount to transfer.821		///     * Non-Fungible Mode: Ignored822		///     * Fungible Mode: Must specify transferred amount823		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)824		#[weight = T::CommonWeightInfo::transfer()]825		#[transactional]826		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {827			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);828			let budget = budget::Value::new(2);829830			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))831		}832833		/// Set, change, or remove approved address to transfer the ownership of the NFT.834		///835		/// # Permissions836		///837		/// * Collection Owner838		/// * Collection Admin839		/// * Current NFT owner840		///841		/// # Arguments842		///843		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).844		///845		/// * collection_id.846		///847		/// * item_id: ID of the item.848		#[weight = T::CommonWeightInfo::approve()]849		#[transactional]850		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {851			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);852853			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))854		}855856		/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.857		///858		/// # Permissions859		/// * Collection Owner860		/// * Collection Admin861		/// * Current NFT owner862		/// * Address approved by current NFT owner863		///864		/// # Arguments865		///866		/// * from: Address that owns token.867		///868		/// * recipient: Address of token recipient.869		///870		/// * collection_id.871		///872		/// * item_id: ID of the item.873		///874		/// * value: Amount to transfer.875		#[weight = T::CommonWeightInfo::transfer_from()]876		#[transactional]877		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {878			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);879			let budget = budget::Value::new(2);880881			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))882		}883884		#[weight = <SelfWeightOf<T>>::set_collection_limits()]885		#[transactional]886		pub fn set_collection_limits(887			origin,888			collection_id: CollectionId,889			new_limit: CollectionLimits,890		) -> DispatchResult {891			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);892			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;893			target_collection.check_is_owner(&sender)?;894			let old_limit = &target_collection.limits;895896			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;897898			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(899				collection_id900			));901902			target_collection.save()903		}904905		#[weight = <SelfWeightOf<T>>::set_collection_limits()]906		#[transactional]907		pub fn set_collection_permissions(908			origin,909			collection_id: CollectionId,910			new_limit: CollectionPermissions,911		) -> DispatchResult {912			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);913			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;914			target_collection.check_is_owner(&sender)?;915			let old_limit = &target_collection.permissions;916917			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;918919			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(920				collection_id921			));922923			target_collection.save()924		}925	}926}
after · pallets/unique/src/lib.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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20	clippy::too_many_arguments,21	clippy::unnecessary_mut_passed,22	clippy::unused_unit23)]2425extern crate alloc;2627use frame_support::{28	decl_module, decl_storage, decl_error, decl_event,29	dispatch::DispatchResult,30	ensure,31	weights::{Weight},32	transactional,33	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},34	BoundedVec,35};36use scale_info::TypeInfo;37use frame_system::{self as system, ensure_signed};38use sp_runtime::{sp_std::prelude::Vec};39use up_data_structs::{40	MAX_COLLECTION_NAME_LENGTH,41	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,42	CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,43	CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,44	PropertyKeyPermission,45};46use pallet_evm::account::CrossAccountId;47use pallet_common::{48	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,49	dispatch::CollectionDispatch,50};51pub mod eth;5253#[cfg(feature = "runtime-benchmarks")]54mod benchmarking;55pub mod weights;56use weights::WeightInfo;5758decl_error! {59	/// Error for non-fungible-token module.60	pub enum Error for Module<T: Config> {61		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.62		CollectionDecimalPointLimitExceeded,63		/// This address is not set as sponsor, use setCollectionSponsor first.64		ConfirmUnsetSponsorFail,65		/// Length of items properties must be greater than 0.66		EmptyArgument,67	}68}6970pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {71	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7273	/// Weight information for extrinsics in this pallet.74	type WeightInfo: WeightInfo;75	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;76}7778decl_event! {79	pub enum Event<T>80	where81		<T as frame_system::Config>::AccountId,82		<T as pallet_evm::account::Config>::CrossAccountId,83	{84		/// Collection sponsor was removed85		///86		/// # Arguments87		///88		/// * collection_id: Globally unique collection identifier.89		CollectionSponsorRemoved(CollectionId),9091		/// Collection admin was added92		///93		/// # Arguments94		///95		/// * collection_id: Globally unique collection identifier.96		///97		/// * admin:  Admin address.98		CollectionAdminAdded(CollectionId, CrossAccountId),99100		/// Collection owned was change101		///102		/// # Arguments103		///104		/// * collection_id: Globally unique collection identifier.105		///106		/// * owner:  New owner address.107		CollectionOwnedChanged(CollectionId, AccountId),108109		/// Collection sponsor was set110		///111		/// # Arguments112		///113		/// * collection_id: Globally unique collection identifier.114		///115		/// * owner:  New sponsor address.116		CollectionSponsorSet(CollectionId, AccountId),117118		/// const on chain schema was set119		///120		/// # Arguments121		///122		/// * collection_id: Globally unique collection identifier.123		ConstOnChainSchemaSet(CollectionId),124125		/// New sponsor was confirm126		///127		/// # Arguments128		///129		/// * collection_id: Globally unique collection identifier.130		///131		/// * sponsor:  New sponsor address.132		SponsorshipConfirmed(CollectionId, AccountId),133134		/// Collection admin was removed135		///136		/// # Arguments137		///138		/// * collection_id: Globally unique collection identifier.139		///140		/// * admin:  Admin address.141		CollectionAdminRemoved(CollectionId, CrossAccountId),142143		/// Address was remove from allow list144		///145		/// # Arguments146		///147		/// * collection_id: Globally unique collection identifier.148		///149		/// * user:  Address.150		AllowListAddressRemoved(CollectionId, CrossAccountId),151152		/// Address was add to allow list153		///154		/// # Arguments155		///156		/// * collection_id: Globally unique collection identifier.157		///158		/// * user:  Address.159		AllowListAddressAdded(CollectionId, CrossAccountId),160161		/// Collection limits was set162		///163		/// # Arguments164		///165		/// * collection_id: Globally unique collection identifier.166		CollectionLimitSet(CollectionId),167168		CollectionPermissionSet(CollectionId),169170		/// Mint permission	was set171		///172		/// # Arguments173		///174		/// * collection_id: Globally unique collection identifier.175		MintPermissionSet(CollectionId),176177		/// Offchain schema was set178		///179		/// # Arguments180		///181		/// * collection_id: Globally unique collection identifier.182		OffchainSchemaSet(CollectionId),183184		/// Public access mode was set185		///186		/// # Arguments187		///188		/// * collection_id: Globally unique collection identifier.189		///190		/// * mode: New access state.191		PublicAccessModeSet(CollectionId, AccessMode),192193		/// Schema version was set194		///195		/// # Arguments196		///197		/// * collection_id: Globally unique collection identifier.198		SchemaVersionSet(CollectionId),199	}200}201202type SelfWeightOf<T> = <T as Config>::WeightInfo;203204// # Used definitions205//206// ## User control levels207//208// chain-controlled - key is uncontrolled by user209//                    i.e autoincrementing index210//                    can use non-cryptographic hash211// real - key is controlled by user212//        but it is hard to generate enough colliding values, i.e owner of signed txs213//        can use non-cryptographic hash214// controlled - key is completly controlled by users215//              i.e maps with mutable keys216//              should use cryptographic hash217//218// ## User control level downgrade reasons219//220// ?1 - chain-controlled -> controlled221//      collections/tokens can be destroyed, resulting in massive holes222// ?2 - chain-controlled -> controlled223//      same as ?1, but can be only added, resulting in easier exploitation224// ?3 - real -> controlled225//      no confirmation required, so addresses can be easily generated226decl_storage! {227	trait Store for Module<T: Config> as Unique {228229		//#region Private members230		/// Used for migrations231		ChainVersion: u64;232		//#endregion233234		//#region Tokens transfer rate limit baskets235		/// (Collection id (controlled?2), who created (real))236		/// TODO: Off chain worker should remove from this map when collection gets removed237		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;238		/// Collection id (controlled?2), token id (controlled?2)239		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;240		/// Collection id (controlled?2), owning user (real)241		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;242		/// Collection id (controlled?2), token id (controlled?2)243		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;244		//#endregion245246		/// Variable metadata sponsoring247		/// Collection id (controlled?2), token id (controlled?2)248		#[deprecated]249		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;251252		/// Approval sponsoring253		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;254		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;255		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;256	}257}258259decl_module! {260	pub struct Module<T: Config> for enum Call261	where262		origin: T::Origin263	{264		type Error = Error<T>;265266		fn deposit_event() = default;267268		fn on_initialize(_now: T::BlockNumber) -> Weight {269			0270		}271272		fn on_runtime_upgrade() -> Weight {273			let limit = None;274275			<VariableMetaDataBasket<T>>::remove_all(limit);276277			0278		}279280		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.281		///282		/// # Permissions283		///284		/// * Anyone.285		///286		/// # Arguments287		///288		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.289		///290		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.291		///292		/// * token_prefix: UTF-8 string with token prefix.293		///294		/// * mode: [CollectionMode] collection type and type dependent data.295		// returns collection ID296		#[weight = <SelfWeightOf<T>>::create_collection()]297		#[transactional]298		#[deprecated]299		pub fn create_collection(origin,300								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,301								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,302								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,303								 mode: CollectionMode) -> DispatchResult  {304			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {305				name: collection_name,306				description: collection_description,307				token_prefix,308				mode,309				..Default::default()310			};311			Self::create_collection_ex(origin, data)312		}313314		/// This method creates a collection315		///316		/// Prefer it to deprecated [`created_collection`] method317		#[weight = <SelfWeightOf<T>>::create_collection()]318		#[transactional]319		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {320			let sender = ensure_signed(origin)?;321322			// =========323324			T::CollectionDispatch::create(sender, data)?;325326			Ok(())327		}328329		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.330		///331		/// # Permissions332		///333		/// * Collection Owner.334		///335		/// # Arguments336		///337		/// * collection_id: collection to destroy.338		#[weight = <SelfWeightOf<T>>::destroy_collection()]339		#[transactional]340		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {341			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);342			let collection = <CollectionHandle<T>>::try_get(collection_id)?;343344			// =========345346			T::CollectionDispatch::destroy(sender, collection)?;347348			<NftTransferBasket<T>>::remove_prefix(collection_id, None);349			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);350			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);351352			<NftApproveBasket<T>>::remove_prefix(collection_id, None);353			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);354			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);355356			Ok(())357		}358359		/// Add an address to allow list.360		///361		/// # Permissions362		///363		/// * Collection Owner364		/// * Collection Admin365		///366		/// # Arguments367		///368		/// * collection_id.369		///370		/// * address.371		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]372		#[transactional]373		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{374375			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);376			let collection = <CollectionHandle<T>>::try_get(collection_id)?;377378			<PalletCommon<T>>::toggle_allowlist(379				&collection,380				&sender,381				&address,382				true,383			)?;384385			Self::deposit_event(Event::<T>::AllowListAddressAdded(386				collection_id,387				address388			));389390			Ok(())391		}392393		/// Remove an address from allow list.394		///395		/// # Permissions396		///397		/// * Collection Owner398		/// * Collection Admin399		///400		/// # Arguments401		///402		/// * collection_id.403		///404		/// * address.405		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]406		#[transactional]407		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{408409			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);410			let collection = <CollectionHandle<T>>::try_get(collection_id)?;411412			<PalletCommon<T>>::toggle_allowlist(413				&collection,414				&sender,415				&address,416				false,417			)?;418419			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(420				collection_id,421				address422			));423424			Ok(())425		}426427		/// Change the owner of the collection.428		///429		/// # Permissions430		///431		/// * Collection Owner.432		///433		/// # Arguments434		///435		/// * collection_id.436		///437		/// * new_owner.438		#[weight = <SelfWeightOf<T>>::change_collection_owner()]439		#[transactional]440		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {441442			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);443444			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;445			target_collection.check_is_owner(&sender)?;446447			target_collection.owner = new_owner.clone();448			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(449				collection_id,450				new_owner451			));452453			target_collection.save()454		}455456		/// Adds an admin of the Collection.457		/// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.458		///459		/// # Permissions460		///461		/// * Collection Owner.462		/// * Collection Admin.463		///464		/// # Arguments465		///466		/// * collection_id: ID of the Collection to add admin for.467		///468		/// * new_admin_id: Address of new admin to add.469		#[weight = <SelfWeightOf<T>>::add_collection_admin()]470		#[transactional]471		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {472			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473			let collection = <CollectionHandle<T>>::try_get(collection_id)?;474475			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(476				collection_id,477				new_admin_id.clone()478			));479480			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)481		}482483		/// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.484		///485		/// # Permissions486		///487		/// * Collection Owner.488		/// * Collection Admin.489		///490		/// # Arguments491		///492		/// * collection_id: ID of the Collection to remove admin for.493		///494		/// * account_id: Address of admin to remove.495		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]496		#[transactional]497		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {498			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499			let collection = <CollectionHandle<T>>::try_get(collection_id)?;500501			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(502				collection_id,503				account_id.clone()504			));505506			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)507		}508509		/// # Permissions510		///511		/// * Collection Owner512		///513		/// # Arguments514		///515		/// * collection_id.516		///517		/// * new_sponsor.518		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]519		#[transactional]520		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {521			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);522523			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;524			target_collection.check_is_owner(&sender)?;525526			target_collection.set_sponsor(new_sponsor.clone());527528			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(529				collection_id,530				new_sponsor531			));532533			target_collection.save()534		}535536		/// # Permissions537		///538		/// * Sponsor.539		///540		/// # Arguments541		///542		/// * collection_id.543		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]544		#[transactional]545		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {546			let sender = ensure_signed(origin)?;547548			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;549			ensure!(550				target_collection.confirm_sponsorship(&sender),551				Error::<T>::ConfirmUnsetSponsorFail552			);553554			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(555				collection_id,556				sender557			));558559			target_collection.save()560		}561562		/// Switch back to pay-per-own-transaction model.563		///564		/// # Permissions565		///566		/// * Collection owner.567		///568		/// # Arguments569		///570		/// * collection_id.571		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]572		#[transactional]573		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {574			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);575576			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;577			target_collection.check_is_owner(&sender)?;578579			target_collection.sponsorship = SponsorshipState::Disabled;580581			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(582				collection_id583			));584			target_collection.save()585		}586587		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.588		///589		/// # Permissions590		///591		/// * Collection Owner.592		/// * Collection Admin.593		/// * Anyone if594		///     * Allow List is enabled, and595		///     * Address is added to allow list, and596		///     * MintPermission is enabled (see SetMintPermission method)597		///598		/// # Arguments599		///600		/// * collection_id: ID of the collection.601		///602		/// * owner: Address, initial owner of the NFT.603		///604		/// * data: Token data to store on chain.605		#[weight = T::CommonWeightInfo::create_item()]606		#[transactional]607		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {608			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);609			let budget = budget::Value::new(2);610611			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))612		}613614		/// This method creates multiple items in a collection created with CreateCollection method.615		///616		/// # Permissions617		///618		/// * Collection Owner.619		/// * Collection Admin.620		/// * Anyone if621		///     * Allow List is enabled, and622		///     * Address is added to allow list, and623		///     * MintPermission is enabled (see SetMintPermission method)624		///625		/// # Arguments626		///627		/// * collection_id: ID of the collection.628		///629		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].630		///631		/// * owner: Address, initial owner of the NFT.632		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]633		#[transactional]634		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {635			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);636			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);637			let budget = budget::Value::new(2);638639			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))640		}641642		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]643		#[transactional]644		pub fn set_collection_properties(645			origin,646			collection_id: CollectionId,647			properties: Vec<Property>648		) -> DispatchResultWithPostInfo {649			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);650651			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);652653			dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))654		}655656		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]657		#[transactional]658		pub fn delete_collection_properties(659			origin,660			collection_id: CollectionId,661			property_keys: Vec<PropertyKey>,662		) -> DispatchResultWithPostInfo {663			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);664665			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);666667			dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))668		}669670		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]671		#[transactional]672		pub fn set_token_properties(673			origin,674			collection_id: CollectionId,675			token_id: TokenId,676			properties: Vec<Property>677		) -> DispatchResultWithPostInfo {678			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);679680			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);681682			dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))683		}684685		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]686		#[transactional]687		pub fn delete_token_properties(688			origin,689			collection_id: CollectionId,690			token_id: TokenId,691			property_keys: Vec<PropertyKey>692		) -> DispatchResultWithPostInfo {693			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);694695			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696697			dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))698		}699700		#[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]701		#[transactional]702		pub fn set_property_permissions(703			origin,704			collection_id: CollectionId,705			property_permissions: Vec<PropertyKeyPermission>,706		) -> DispatchResultWithPostInfo {707			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);708709			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);710711			dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))712		}713714		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]715		#[transactional]716		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {717			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);718			let budget = budget::Value::new(2);719720			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))721		}722723		// TODO! transaction weight724725		/// Set transfers_enabled value for particular collection726		///727		/// # Permissions728		///729		/// * Collection Owner.730		///731		/// # Arguments732		///733		/// * collection_id: ID of the collection.734		///735		/// * value: New flag value.736		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]737		#[transactional]738		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {739			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);740			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;741			target_collection.check_is_owner(&sender)?;742743			// =========744745			target_collection.limits.transfers_enabled = Some(value);746			target_collection.save()747		}748749		/// Destroys a concrete instance of NFT.750		///751		/// # Permissions752		///753		/// * Collection Owner.754		/// * Collection Admin.755		/// * Current NFT Owner.756		///757		/// # Arguments758		///759		/// * collection_id: ID of the collection.760		///761		/// * item_id: ID of NFT to burn.762		#[weight = T::CommonWeightInfo::burn_item()]763		#[transactional]764		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {765			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);766767			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;768			if value == 1 {769				<NftTransferBasket<T>>::remove(collection_id, item_id);770				<NftApproveBasket<T>>::remove(collection_id, item_id);771			}772			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?773			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());774			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));775			Ok(post_info)776		}777778		/// Destroys a concrete instance of NFT on behalf of the owner779		/// See also: [`approve`]780		///781		/// # Permissions782		///783		/// * Collection Owner.784		/// * Collection Admin.785		/// * Current NFT Owner.786		///787		/// # Arguments788		///789		/// * collection_id: ID of the collection.790		///791		/// * item_id: ID of NFT to burn.792		///793		/// * from: owner of item794		#[weight = T::CommonWeightInfo::burn_from()]795		#[transactional]796		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {797			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);798			let budget = budget::Value::new(2);799800			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))801		}802803		/// Change ownership of the token.804		///805		/// # Permissions806		///807		/// * Collection Owner808		/// * Collection Admin809		/// * Current NFT owner810		///811		/// # Arguments812		///813		/// * recipient: Address of token recipient.814		///815		/// * collection_id.816		///817		/// * item_id: ID of the item818		///     * Non-Fungible Mode: Required.819		///     * Fungible Mode: Ignored.820		///     * Re-Fungible Mode: Required.821		///822		/// * value: Amount to transfer.823		///     * Non-Fungible Mode: Ignored824		///     * Fungible Mode: Must specify transferred amount825		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)826		#[weight = T::CommonWeightInfo::transfer()]827		#[transactional]828		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {829			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);830			let budget = budget::Value::new(2);831832			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))833		}834835		/// Set, change, or remove approved address to transfer the ownership of the NFT.836		///837		/// # Permissions838		///839		/// * Collection Owner840		/// * Collection Admin841		/// * Current NFT owner842		///843		/// # Arguments844		///845		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).846		///847		/// * collection_id.848		///849		/// * item_id: ID of the item.850		#[weight = T::CommonWeightInfo::approve()]851		#[transactional]852		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {853			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);854855			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))856		}857858		/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.859		///860		/// # Permissions861		/// * Collection Owner862		/// * Collection Admin863		/// * Current NFT owner864		/// * Address approved by current NFT owner865		///866		/// # Arguments867		///868		/// * from: Address that owns token.869		///870		/// * recipient: Address of token recipient.871		///872		/// * collection_id.873		///874		/// * item_id: ID of the item.875		///876		/// * value: Amount to transfer.877		#[weight = T::CommonWeightInfo::transfer_from()]878		#[transactional]879		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {880			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);881			let budget = budget::Value::new(2);882883			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))884		}885886		#[weight = <SelfWeightOf<T>>::set_collection_limits()]887		#[transactional]888		pub fn set_collection_limits(889			origin,890			collection_id: CollectionId,891			new_limit: CollectionLimits,892		) -> DispatchResult {893			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;895			target_collection.check_is_owner(&sender)?;896			let old_limit = &target_collection.limits;897898			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;899900			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(901				collection_id902			));903904			target_collection.save()905		}906907		#[weight = <SelfWeightOf<T>>::set_collection_limits()]908		#[transactional]909		pub fn set_collection_permissions(910			origin,911			collection_id: CollectionId,912			new_limit: CollectionPermissions,913		) -> DispatchResult {914			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;916			target_collection.check_is_owner(&sender)?;917			let old_limit = &target_collection.permissions;918919			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;920921			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(922				collection_id923			));924925			target_collection.save()926		}927	}928}
modifiedruntime/quartz/src/lib.rsdiffbeforeafterboth
--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -66,7 +66,6 @@
 		WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
 	},
 };
-use unique_runtime_common::dispatch::{CollectionDispatchT, CollectionDispatch};
 use up_data_structs::*;
 // use pallet_contracts::weights::WeightInfo;
 // #[cfg(any(feature = "std", test))]
@@ -79,8 +78,8 @@
 };
 use smallvec::smallvec;
 use codec::{Encode, Decode};
-use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};
-use pallet_unique::evm_collection;
+use pallet_unique::eth::evm_collection;
+use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping};
 use fp_rpc::TransactionStatus;
 use sp_runtime::{
 	traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
@@ -117,7 +116,15 @@
 //use xcm_executor::traits::MatchesFungible;
 use sp_runtime::traits::CheckedConversion;
 
-use unique_runtime_common::{impl_common_runtime_apis, types::*, constants::*};
+use unique_runtime_common::{
+	impl_common_runtime_apis,
+	types::*,
+	constants::*,
+	dispatch::{CollectionDispatchT, CollectionDispatch},
+	sponsoring::UniqueSponsorshipHandler,
+	eth_sponsoring::UniqueEthSponsorshipHandler,
+	weights::CommonWeights,
+};
 
 pub const RUNTIME_NAME: &str = "quartz";
 pub const TOKEN_SYMBOL: &str = "QTZ";
@@ -894,6 +901,7 @@
 impl pallet_unique::Config for Runtime {
 	type Event = Event;
 	type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;
+	type CommonWeightInfo = CommonWeights<Self>;
 }
 
 parameter_types! {
@@ -915,11 +923,11 @@
 // }
 
 type EvmSponsorshipHandler = (
-	pallet_unique::UniqueEthSponsorshipHandler<Runtime>,
+	UniqueEthSponsorshipHandler<Runtime>,
 	pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
 );
 type SponsorshipHandler = (
-	pallet_unique::UniqueSponsorshipHandler<Runtime>,
+	UniqueSponsorshipHandler<Runtime>,
 	//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
 	pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
 );
modifiedruntime/unique/src/lib.rsdiffbeforeafterboth
--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -50,6 +50,7 @@
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_evm::{
 	EnsureAddressTruncated, HashedAddressMapping, Runner, account::CrossAccountId as _, OnMethodCall,
+	Account as EVMAccount, FeeCalculator, GasWeightMapping,
 };
 pub use frame_support::{
 	construct_runtime, match_types,
@@ -84,8 +85,7 @@
 };
 use smallvec::smallvec;
 use codec::{Encode, Decode};
-use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};
-use pallet_unique::evm_collection;
+use pallet_unique::eth::evm_collection;
 use fp_rpc::TransactionStatus;
 use sp_runtime::{
 	traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
@@ -121,7 +121,15 @@
 //use xcm_executor::traits::MatchesFungible;
 use sp_runtime::traits::CheckedConversion;
 
-use unique_runtime_common::{impl_common_runtime_apis, types::*, constants::*};
+use unique_runtime_common::{
+	impl_common_runtime_apis,
+	types::*,
+	constants::*,
+	dispatch::{CollectionDispatchT, CollectionDispatch},
+	sponsoring::UniqueSponsorshipHandler,
+	eth_sponsoring::UniqueEthSponsorshipHandler,
+	weights::CommonWeights,
+};
 
 pub const RUNTIME_NAME: &str = "unique";
 pub const TOKEN_SYMBOL: &str = "UNQ";