git.delta.rocks / unique-network / refs/commits / bbd154a47827

difftreelog

test fix unit

Yaroslav Bolyukin2022-04-13parent: #f082ca5.patch.diff
in: master

19 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6829,37 +6829,18 @@
 name = "pallet-unique"
 version = "0.1.0"
 dependencies = [
- "ethereum",
- "evm-coder",
- "fp-evm",
- "fp-evm-mapping",
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "hex-literal",
- "pallet-balances",
  "pallet-common",
- "pallet-ethereum",
  "pallet-evm",
- "pallet-evm-coder-substrate",
- "pallet-fungible",
- "pallet-nonfungible",
- "pallet-randomness-collective-flip",
- "pallet-refungible",
- "pallet-timestamp",
- "pallet-transaction-payment",
  "parity-scale-codec",
- "primitive-types",
- "rlp",
  "scale-info",
- "serde",
- "sp-api",
  "sp-core",
  "sp-io",
  "sp-runtime",
  "sp-std",
  "up-data-structs",
- "up-sponsorship",
 ]
 
 [[package]]
@@ -11918,6 +11899,35 @@
 ]
 
 [[package]]
+name = "tests"
+version = "0.1.0"
+dependencies = [
+ "fp-evm-mapping",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-common",
+ "pallet-ethereum",
+ "pallet-evm",
+ "pallet-evm-coder-substrate",
+ "pallet-fungible",
+ "pallet-nonfungible",
+ "pallet-refungible",
+ "pallet-structure",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-unique",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "unique-runtime-common",
+ "up-data-structs",
+]
+
+[[package]]
 name = "textwrap"
 version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -12677,6 +12687,7 @@
 name = "unique-runtime-common"
 version = "0.9.18"
 dependencies = [
+ "evm-coder",
  "fp-rpc",
  "frame-support",
  "frame-system",
@@ -12693,6 +12704,7 @@
  "sp-runtime",
  "sp-std",
  "up-data-structs",
+ "up-sponsorship",
 ]
 
 [[package]]
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,16 @@
 [workspace]
 resolver = "2"
-members = ['node/*', 'pallets/*', 'client/*', 'primitives/*', 'crates/*']
-exclude = ["runtime/unique", "runtime/quartz"]
+members = [
+    'node/*',
+    'pallets/*',
+    'client/*',
+    'primitives/*',
+    'crates/*',
+    'runtime/tests',
+]
+exclude = [
+    "runtime/unique",
+    "runtime/quartz"
+]
 [profile.release]
 panic = 'unwind'
modifiedpallets/unique/Cargo.tomldiffbeforeafterboth
--- a/pallets/unique/Cargo.toml
+++ b/pallets/unique/Cargo.toml
@@ -19,32 +19,14 @@
 runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
 std = [
     'codec/std',
-    'serde/std',
     'frame-support/std',
     'frame-system/std',
-    'pallet-balances/std',
     'pallet-evm/std',
-    'pallet-timestamp/std',
-    'pallet-randomness-collective-flip/std',
-    'pallet-transaction-payment/std',
     'pallet-common/std',
-    'pallet-fungible/std',
-    'pallet-nonfungible/std',
-    'pallet-refungible/std',
-    'fp-evm/std',
     'up-data-structs/std',
-    'up-sponsorship/std',
-    'fp-evm-mapping/std',
     'sp-std/std',
-    'sp-api/std',
     'sp-runtime/std',
     'frame-benchmarking/std',
-    'ethereum/std',
-    'rlp/std',
-
-    'primitive-types/std',
-    'evm-coder/std',
-    'pallet-evm-coder-substrate/std',
 ]
 limit-testing = ["up-data-structs/limit-testing"]
 
@@ -69,21 +51,6 @@
 branch = "polkadot-v0.9.20"
 
 [dependencies.frame-system]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-balances]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-timestamp]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.pallet-randomness-collective-flip]
 default-features = false
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.20"
@@ -93,16 +60,6 @@
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.20"
 
-[dependencies.pallet-transaction-payment]
-default-features = false
-git = "https://github.com/paritytech/substrate"
-branch = "polkadot-v0.9.20"
-
-[dependencies.serde]
-default-features = false
-features = ['derive']
-version = '1.0.130'
-
 [dependencies.sp-runtime]
 default-features = false
 git = "https://github.com/paritytech/substrate"
@@ -123,28 +80,8 @@
 # Local Dependencies
 [dependencies]
 up-data-structs = { default-features = false, path = "../../primitives/data-structs" }
-
 scale-info = { version = "2.0.1", default-features = false, features = [
     "derive",
 ] }
-ethereum = { version = "0.12.0", default-features = false }
-rlp = { default-features = false, version = "0.5.0" }
-sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20" }
-
-up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.20" }
-fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-evm-coder = { default-features = false, path = "../../crates/evm-coder" }
-pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
-primitive-types = { version = "0.11.1", default-features = false, features = [
-    "serde_no_std",
-] }
-
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
-hex-literal = "0.3.3"
-
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
 pallet-common = { default-features = false, path = "../common" }
-pallet-fungible = { default-features = false, path = "../fungible" }
-pallet-nonfungible = { default-features = false, path = "../nonfungible" }
-pallet-refungible = { default-features = false, path = "../refungible" }
deletedpallets/unique/src/common.rsdiffbeforeafterboth
--- a/pallets/unique/src/common.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use core::marker::PhantomData;
-use frame_support::{weights::Weight};
-use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight};
-
-use pallet_fungible::{common::CommonWeights as FungibleWeights};
-use pallet_nonfungible::{common::CommonWeights as NonfungibleWeights};
-use pallet_refungible::{common::CommonWeights as RefungibleWeights};
-use up_data_structs::CreateItemExData;
-
-use crate::Config;
-
-macro_rules! max_weight_of {
-	($method:ident ( $($args:tt)* )) => {
-		<FungibleWeights<T>>::$method($($args)*)
-		.max(<NonfungibleWeights<T>>::$method($($args)*))
-		.max(<RefungibleWeights<T>>::$method($($args)*))
-	};
-}
-
-pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
-	fn create_item() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(create_item())
-	}
-
-	fn create_multiple_items(amount: u32) -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
-	}
-
-	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
-	}
-
-	fn burn_item() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(burn_item())
-	}
-
-	fn transfer() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(transfer())
-	}
-
-	fn approve() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(approve())
-	}
-
-	fn transfer_from() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(transfer_from())
-	}
-
-	fn set_variable_metadata(bytes: u32) -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
-	}
-
-	fn burn_from() -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(burn_from())
-	}
-}
deletedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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;
deletedpallets/unique/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/sponsoring.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implements EVM sponsoring logic via TransactionValidityHack
-
-use crate::{Config, sponsorship::*};
-use evm_coder::{Call, abi::AbiReader};
-use pallet_common::{CollectionHandle, eth::map_eth_to_id};
-use sp_core::H160;
-use sp_std::prelude::*;
-use up_sponsorship::SponsorshipHandler;
-use core::marker::PhantomData;
-use core::convert::TryInto;
-use pallet_evm::account::CrossAccountId;
-use up_data_structs::{TokenId, CreateItemData, CreateNftData};
-
-use pallet_nonfungible::erc::{
-	UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,
-};
-use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
-
-pub struct UniqueEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
-	for UniqueEthSponsorshipHandler<T>
-{
-	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
-		let collection_id = map_eth_to_id(&call.0)?;
-		let collection = <CollectionHandle<T>>::new(collection_id)?;
-		let sponsor = collection.sponsorship.sponsor()?.clone();
-		let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
-		Some(T::CrossAccountId::from_sub(match &collection.mode {
-			crate::CollectionMode::NFT => {
-				let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
-				match call {
-					UniqueNFTCall::ERC721UniqueExtensions(
-						ERC721UniqueExtensionsCall::Transfer { token_id, .. },
-					) => {
-						let token_id: TokenId = token_id.try_into().ok()?;
-						withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
-					}
-					UniqueNFTCall::ERC721Mintable(
-						ERC721MintableCall::Mint { token_id, .. }
-						| ERC721MintableCall::MintWithTokenUri { token_id, .. },
-					) => {
-						let _token_id: TokenId = token_id.try_into().ok()?;
-						withdraw_create_item::<T>(
-							&collection,
-							&who,
-							&CreateItemData::NFT(CreateNftData::default()),
-						)
-						.map(|()| sponsor)
-					}
-					UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {
-						let token_id: TokenId = token_id.try_into().ok()?;
-						let from = T::CrossAccountId::from_eth(from);
-						withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)
-					}
-					UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
-						let token_id: TokenId = token_id.try_into().ok()?;
-						withdraw_approve::<T>(&collection, who.as_sub(), &token_id)
-							.map(|()| sponsor)
-					}
-					_ => None,
-				}
-			}
-			crate::CollectionMode::Fungible(_) => {
-				let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
-				#[allow(clippy::single_match)]
-				match call {
-					UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
-						withdraw_transfer::<T>(&collection, who, &TokenId::default())
-							.map(|()| sponsor)
-					}
-					UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {
-						let from = T::CrossAccountId::from_eth(from);
-						withdraw_transfer::<T>(&collection, &from, &TokenId::default())
-							.map(|()| sponsor)
-					}
-					UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
-						withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())
-							.map(|()| sponsor)
-					}
-					_ => None,
-				}
-			}
-			_ => None,
-		}?))
-	}
-}
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)]2425extern crate alloc;2627pub use serde::{Serialize, Deserialize};2829use frame_support::{30	decl_module, decl_storage, decl_error, decl_event,31	dispatch::DispatchResult,32	ensure,33	weights::{Weight},34	transactional,35	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},36	BoundedVec,37};38use scale_info::TypeInfo;39use frame_system::{self as system, ensure_signed};40use sp_runtime::{sp_std::prelude::Vec};41use up_data_structs::{42	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,43	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,44	AccessMode, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId,45	SchemaVersion, SponsorshipState, MetaUpdatePermission, CreateCollectionData, CustomDataLimit,46	CreateItemExData, budget, CollectionField,47};48use pallet_evm::account::CrossAccountId;49use pallet_common::{50	CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo,51	dispatch::dispatch_call, dispatch::CollectionDispatch,52};5354#[cfg(test)]55mod mock;5657#[cfg(test)]58mod tests;5960mod eth;61mod sponsorship;62pub use sponsorship::{UniqueSponsorshipHandler, UniqueSponsorshipPredict};63pub use eth::sponsoring::UniqueEthSponsorshipHandler;6465pub mod common;66use common::CommonWeights;6768#[cfg(feature = "runtime-benchmarks")]69mod benchmarking;70pub mod weights;71use weights::WeightInfo;7273pub trait SponsorshipPredict<T: Config> {74	fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>75	where76		u64: From<<T as frame_system::Config>::BlockNumber>;77}7879decl_error! {80	/// Error for non-fungible-token module.81	pub enum Error for Module<T: Config> {82		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.83		CollectionDecimalPointLimitExceeded,84		/// This address is not set as sponsor, use setCollectionSponsor first.85		ConfirmUnsetSponsorFail,86		/// Length of items properties must be greater than 0.87		EmptyArgument,88	}89}9091pub trait Config:92	system::Config93	+ pallet_evm_coder_substrate::Config94	+ pallet_common::Config95	+ pallet_nonfungible::Config96	+ pallet_refungible::Config97	+ pallet_fungible::Config98	+ Sized99	+ TypeInfo100{101	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;102103	/// Weight information for extrinsics in this pallet.104	type WeightInfo: WeightInfo;105}106107decl_event! {108	pub enum Event<T>109	where110		<T as frame_system::Config>::AccountId,111		<T as pallet_evm::account::Config>::CrossAccountId,112	{113		/// Collection sponsor was removed114		///115		/// # Arguments116		///117		/// * collection_id: Globally unique collection identifier.118		CollectionSponsorRemoved(CollectionId),119120		/// Collection admin was added121		///122		/// # Arguments123		///124		/// * collection_id: Globally unique collection identifier.125		///126		/// * admin:  Admin address.127		CollectionAdminAdded(CollectionId, CrossAccountId),128129		/// Collection owned was change130		///131		/// # Arguments132		///133		/// * collection_id: Globally unique collection identifier.134		///135		/// * owner:  New owner address.136		CollectionOwnedChanged(CollectionId, AccountId),137138		/// Collection sponsor was set139		///140		/// # Arguments141		///142		/// * collection_id: Globally unique collection identifier.143		///144		/// * owner:  New sponsor address.145		CollectionSponsorSet(CollectionId, AccountId),146147		/// const on chain schema was set148		///149		/// # Arguments150		///151		/// * collection_id: Globally unique collection identifier.152		ConstOnChainSchemaSet(CollectionId),153154		/// New sponsor was confirm155		///156		/// # Arguments157		///158		/// * collection_id: Globally unique collection identifier.159		///160		/// * sponsor:  New sponsor address.161		SponsorshipConfirmed(CollectionId, AccountId),162163		/// Collection admin was removed164		///165		/// # Arguments166		///167		/// * collection_id: Globally unique collection identifier.168		///169		/// * admin:  Admin address.170		CollectionAdminRemoved(CollectionId, CrossAccountId),171172		/// Address was remove from allow list173		///174		/// # Arguments175		///176		/// * collection_id: Globally unique collection identifier.177		///178		/// * user:  Address.179		AllowListAddressRemoved(CollectionId, CrossAccountId),180181		/// Address was add to allow list182		///183		/// # Arguments184		///185		/// * collection_id: Globally unique collection identifier.186		///187		/// * user:  Address.188		AllowListAddressAdded(CollectionId, CrossAccountId),189190		/// Collection limits was set191		///192		/// # Arguments193		///194		/// * collection_id: Globally unique collection identifier.195		CollectionLimitSet(CollectionId),196197		/// Mint permission	was set198		///199		/// # Arguments200		///201		/// * collection_id: Globally unique collection identifier.202		MintPermissionSet(CollectionId),203204		/// Offchain schema was set205		///206		/// # Arguments207		///208		/// * collection_id: Globally unique collection identifier.209		OffchainSchemaSet(CollectionId),210211		/// Public access mode was set212		///213		/// # Arguments214		///215		/// * collection_id: Globally unique collection identifier.216		///217		/// * mode: New access state.218		PublicAccessModeSet(CollectionId, AccessMode),219220		/// Schema version was set221		///222		/// # Arguments223		///224		/// * collection_id: Globally unique collection identifier.225		SchemaVersionSet(CollectionId),226227		/// Variable on chain schema was set228		///229		/// # Arguments230		///231		/// * collection_id: Globally unique collection identifier.232		VariableOnChainSchemaSet(CollectionId),233	}234}235236type SelfWeightOf<T> = <T as Config>::WeightInfo;237238// # Used definitions239//240// ## User control levels241//242// chain-controlled - key is uncontrolled by user243//                    i.e autoincrementing index244//                    can use non-cryptographic hash245// real - key is controlled by user246//        but it is hard to generate enough colliding values, i.e owner of signed txs247//        can use non-cryptographic hash248// controlled - key is completly controlled by users249//              i.e maps with mutable keys250//              should use cryptographic hash251//252// ## User control level downgrade reasons253//254// ?1 - chain-controlled -> controlled255//      collections/tokens can be destroyed, resulting in massive holes256// ?2 - chain-controlled -> controlled257//      same as ?1, but can be only added, resulting in easier exploitation258// ?3 - real -> controlled259//      no confirmation required, so addresses can be easily generated260decl_storage! {261	trait Store for Module<T: Config> as Unique {262263		//#region Private members264		/// Used for migrations265		ChainVersion: u64;266		//#endregion267268		//#region Tokens transfer rate limit baskets269		/// (Collection id (controlled?2), who created (real))270		/// TODO: Off chain worker should remove from this map when collection gets removed271		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;272		/// Collection id (controlled?2), token id (controlled?2)273		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;274		/// Collection id (controlled?2), owning user (real)275		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;276		/// Collection id (controlled?2), token id (controlled?2)277		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>;278		//#endregion279280		/// Variable metadata sponsoring281		/// Collection id (controlled?2), token id (controlled?2)282		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;283		/// Approval sponsoring284		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;285		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;286		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>;287	}288}289290decl_module! {291	pub struct Module<T: Config> for enum Call292	where293		origin: T::Origin294	{295		type Error = Error<T>;296297		fn deposit_event() = default;298299		fn on_initialize(_now: T::BlockNumber) -> Weight {300			0301		}302303		/// 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.304		///305		/// # Permissions306		///307		/// * Anyone.308		///309		/// # Arguments310		///311		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.312		///313		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.314		///315		/// * token_prefix: UTF-8 string with token prefix.316		///317		/// * mode: [CollectionMode] collection type and type dependent data.318		// returns collection ID319		#[weight = <SelfWeightOf<T>>::create_collection()]320		#[transactional]321		#[deprecated]322		pub fn create_collection(origin,323								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,324								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,325								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,326								 mode: CollectionMode) -> DispatchResult  {327			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {328				name: collection_name,329				description: collection_description,330				token_prefix,331				mode,332				..Default::default()333			};334			Self::create_collection_ex(origin, data)335		}336337		/// This method creates a collection338		///339		/// Prefer it to deprecated [`created_collection`] method340		#[weight = <SelfWeightOf<T>>::create_collection()]341		#[transactional]342		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {343			let sender = ensure_signed(origin)?;344345			// =========346347			T::CollectionDispatch::create(sender, data)?;348349			Ok(())350		}351352		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.353		///354		/// # Permissions355		///356		/// * Collection Owner.357		///358		/// # Arguments359		///360		/// * collection_id: collection to destroy.361		#[weight = <SelfWeightOf<T>>::destroy_collection()]362		#[transactional]363		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365			let collection = <CollectionHandle<T>>::try_get(collection_id)?;366367			// =========368369			T::CollectionDispatch::destroy(sender, collection)?;370371			<NftTransferBasket<T>>::remove_prefix(collection_id, None);372			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);373			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);374375			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);376			<NftApproveBasket<T>>::remove_prefix(collection_id, None);377			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);378			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);379380			Ok(())381		}382383		/// Add an address to allow list.384		///385		/// # Permissions386		///387		/// * Collection Owner388		/// * Collection Admin389		///390		/// # Arguments391		///392		/// * collection_id.393		///394		/// * address.395		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]396		#[transactional]397		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{398399			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);400			let collection = <CollectionHandle<T>>::try_get(collection_id)?;401402			<PalletCommon<T>>::toggle_allowlist(403				&collection,404				&sender,405				&address,406				true,407			)?;408409			Self::deposit_event(Event::<T>::AllowListAddressAdded(410				collection_id,411				address412			));413414			Ok(())415		}416417		/// Remove an address from allow list.418		///419		/// # Permissions420		///421		/// * Collection Owner422		/// * Collection Admin423		///424		/// # Arguments425		///426		/// * collection_id.427		///428		/// * address.429		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]430		#[transactional]431		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{432433			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);434			let collection = <CollectionHandle<T>>::try_get(collection_id)?;435436			<PalletCommon<T>>::toggle_allowlist(437				&collection,438				&sender,439				&address,440				false,441			)?;442443			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(444				collection_id,445				address446			));447448			Ok(())449		}450451		/// Toggle between normal and allow list access for the methods with access for `Anyone`.452		///453		/// # Permissions454		///455		/// * Collection Owner.456		///457		/// # Arguments458		///459		/// * collection_id.460		///461		/// * mode: [AccessMode]462		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]463		#[transactional]464		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult465		{466			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);467468			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;469			target_collection.check_is_owner(&sender)?;470471			target_collection.access = mode.clone();472473			<Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(474				collection_id,475				mode476			));477478			target_collection.save()479		}480481		/// Allows Anyone to create tokens if:482		/// * Allow List is enabled, and483		/// * Address is added to allow list, and484		/// * This method was called with True parameter485		///486		/// # Permissions487		/// * Collection Owner488		///489		/// # Arguments490		///491		/// * collection_id.492		///493		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.494		#[weight = <SelfWeightOf<T>>::set_mint_permission()]495		#[transactional]496		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult497		{498			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499500			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;501			target_collection.check_is_owner(&sender)?;502503			target_collection.mint_mode = mint_permission;504505			<Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(506				collection_id507			));508509			target_collection.save()510		}511512		/// Change the owner of the collection.513		///514		/// # Permissions515		///516		/// * Collection Owner.517		///518		/// # Arguments519		///520		/// * collection_id.521		///522		/// * new_owner.523		#[weight = <SelfWeightOf<T>>::change_collection_owner()]524		#[transactional]525		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {526527			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);528529			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;530			target_collection.check_is_owner(&sender)?;531532			target_collection.owner = new_owner.clone();533			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(534				collection_id,535				new_owner536			));537538			target_collection.save()539		}540541		/// Adds an admin of the Collection.542		/// 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.543		///544		/// # Permissions545		///546		/// * Collection Owner.547		/// * Collection Admin.548		///549		/// # Arguments550		///551		/// * collection_id: ID of the Collection to add admin for.552		///553		/// * new_admin_id: Address of new admin to add.554		#[weight = <SelfWeightOf<T>>::add_collection_admin()]555		#[transactional]556		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {557			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);558			let collection = <CollectionHandle<T>>::try_get(collection_id)?;559560			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(561				collection_id,562				new_admin_id.clone()563			));564565			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)566		}567568		/// 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.569		///570		/// # Permissions571		///572		/// * Collection Owner.573		/// * Collection Admin.574		///575		/// # Arguments576		///577		/// * collection_id: ID of the Collection to remove admin for.578		///579		/// * account_id: Address of admin to remove.580		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]581		#[transactional]582		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {583			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);584			let collection = <CollectionHandle<T>>::try_get(collection_id)?;585586			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(587				collection_id,588				account_id.clone()589			));590591			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)592		}593594		/// # Permissions595		///596		/// * Collection Owner597		///598		/// # Arguments599		///600		/// * collection_id.601		///602		/// * new_sponsor.603		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]604		#[transactional]605		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {606			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);607608			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;609			target_collection.check_is_owner(&sender)?;610611			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());612613			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(614				collection_id,615				new_sponsor616			));617618			target_collection.save()619		}620621		/// # Permissions622		///623		/// * Sponsor.624		///625		/// # Arguments626		///627		/// * collection_id.628		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]629		#[transactional]630		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {631			let sender = ensure_signed(origin)?;632633			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;634			ensure!(635				target_collection.sponsorship.pending_sponsor() == Some(&sender),636				Error::<T>::ConfirmUnsetSponsorFail637			);638639			target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());640641			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(642				collection_id,643				sender644			));645646			target_collection.save()647		}648649		/// Switch back to pay-per-own-transaction model.650		///651		/// # Permissions652		///653		/// * Collection owner.654		///655		/// # Arguments656		///657		/// * collection_id.658		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]659		#[transactional]660		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {661			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);662663			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;664			target_collection.check_is_owner(&sender)?;665666			target_collection.sponsorship = SponsorshipState::Disabled;667668			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(669				collection_id670			));671			target_collection.save()672		}673674		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.675		///676		/// # Permissions677		///678		/// * Collection Owner.679		/// * Collection Admin.680		/// * Anyone if681		///     * Allow List is enabled, and682		///     * Address is added to allow list, and683		///     * MintPermission is enabled (see SetMintPermission method)684		///685		/// # Arguments686		///687		/// * collection_id: ID of the collection.688		///689		/// * owner: Address, initial owner of the NFT.690		///691		/// * data: Token data to store on chain.692		#[weight = <CommonWeights<T>>::create_item()]693		#[transactional]694		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {695			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);696			let budget = budget::Value::new(2);697698			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))699		}700701		/// This method creates multiple items in a collection created with CreateCollection method.702		///703		/// # Permissions704		///705		/// * Collection Owner.706		/// * Collection Admin.707		/// * Anyone if708		///     * Allow List is enabled, and709		///     * Address is added to allow list, and710		///     * MintPermission is enabled (see SetMintPermission method)711		///712		/// # Arguments713		///714		/// * collection_id: ID of the collection.715		///716		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].717		///718		/// * owner: Address, initial owner of the NFT.719		#[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]720		#[transactional]721		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {722			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);723			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);724			let budget = budget::Value::new(2);725726			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))727		}728729		#[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]730		#[transactional]731		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {732			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);733			let budget = budget::Value::new(2);734735			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))736		}737738		// TODO! transaction weight739740		/// Set transfers_enabled value for particular collection741		///742		/// # Permissions743		///744		/// * Collection Owner.745		///746		/// # Arguments747		///748		/// * collection_id: ID of the collection.749		///750		/// * value: New flag value.751		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]752		#[transactional]753		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {754			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);755			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;756			target_collection.check_is_owner(&sender)?;757758			// =========759760			target_collection.limits.transfers_enabled = Some(value);761			target_collection.save()762		}763764		/// Destroys a concrete instance of NFT.765		///766		/// # Permissions767		///768		/// * Collection Owner.769		/// * Collection Admin.770		/// * Current NFT Owner.771		///772		/// # Arguments773		///774		/// * collection_id: ID of the collection.775		///776		/// * item_id: ID of NFT to burn.777		#[weight = <CommonWeights<T>>::burn_item()]778		#[transactional]779		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {780			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);781782			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;783			if value == 1 {784				<NftTransferBasket<T>>::remove(collection_id, item_id);785				<NftApproveBasket<T>>::remove(collection_id, item_id);786			}787			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?788			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());789			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));790			Ok(post_info)791		}792793		/// Destroys a concrete instance of NFT on behalf of the owner794		/// See also: [`approve`]795		///796		/// # Permissions797		///798		/// * Collection Owner.799		/// * Collection Admin.800		/// * Current NFT Owner.801		///802		/// # Arguments803		///804		/// * collection_id: ID of the collection.805		///806		/// * item_id: ID of NFT to burn.807		///808		/// * from: owner of item809		#[weight = <CommonWeights<T>>::burn_from()]810		#[transactional]811		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {812			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);813			let budget = budget::Value::new(2);814815			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))816		}817818		/// Change ownership of the token.819		///820		/// # Permissions821		///822		/// * Collection Owner823		/// * Collection Admin824		/// * Current NFT owner825		///826		/// # Arguments827		///828		/// * recipient: Address of token recipient.829		///830		/// * collection_id.831		///832		/// * item_id: ID of the item833		///     * Non-Fungible Mode: Required.834		///     * Fungible Mode: Ignored.835		///     * Re-Fungible Mode: Required.836		///837		/// * value: Amount to transfer.838		///     * Non-Fungible Mode: Ignored839		///     * Fungible Mode: Must specify transferred amount840		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)841		#[weight = <CommonWeights<T>>::transfer()]842		#[transactional]843		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {844			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);845			let budget = budget::Value::new(2);846847			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))848		}849850		/// Set, change, or remove approved address to transfer the ownership of the NFT.851		///852		/// # Permissions853		///854		/// * Collection Owner855		/// * Collection Admin856		/// * Current NFT owner857		///858		/// # Arguments859		///860		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).861		///862		/// * collection_id.863		///864		/// * item_id: ID of the item.865		#[weight = <CommonWeights<T>>::approve()]866		#[transactional]867		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {868			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);869870			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))871		}872873		/// 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.874		///875		/// # Permissions876		/// * Collection Owner877		/// * Collection Admin878		/// * Current NFT owner879		/// * Address approved by current NFT owner880		///881		/// # Arguments882		///883		/// * from: Address that owns token.884		///885		/// * recipient: Address of token recipient.886		///887		/// * collection_id.888		///889		/// * item_id: ID of the item.890		///891		/// * value: Amount to transfer.892		#[weight = <CommonWeights<T>>::transfer_from()]893		#[transactional]894		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {895			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896			let budget = budget::Value::new(2);897898			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))899		}900901		/// Set off-chain data schema.902		///903		/// # Permissions904		///905		/// * Collection Owner906		/// * Collection Admin907		///908		/// # Arguments909		///910		/// * collection_id.911		///912		/// * schema: String representing the offchain data schema.913		#[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]914		#[transactional]915		pub fn set_variable_meta_data (916			origin,917			collection_id: CollectionId,918			item_id: TokenId,919			data: BoundedVec<u8, CustomDataLimit>,920		) -> DispatchResultWithPostInfo {921			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);922923			dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))924		}925926		/// Set meta_update_permission value for particular collection927		///928		/// # Permissions929		///930		/// * Collection Owner.931		///932		/// # Arguments933		///934		/// * collection_id: ID of the collection.935		///936		/// * value: New flag value.937		#[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]938		#[transactional]939		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {940			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);941			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;942943			ensure!(944				target_collection.meta_update_permission != MetaUpdatePermission::None,945				<CommonError<T>>::MetadataFlagFrozen,946			);947			target_collection.check_is_owner(&sender)?;948949			target_collection.meta_update_permission = value;950951			target_collection.save()952		}953954		/// Set schema standard955		/// ImageURL956		/// Unique957		///958		/// # Permissions959		///960		/// * Collection Owner961		/// * Collection Admin962		///963		/// # Arguments964		///965		/// * collection_id.966		///967		/// * schema: SchemaVersion: enum968		#[weight = <SelfWeightOf<T>>::set_schema_version()]969		#[transactional]970		pub fn set_schema_version(971			origin,972			collection_id: CollectionId,973			version: SchemaVersion974		) -> DispatchResult {975			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);976			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;977			target_collection.check_is_owner_or_admin(&sender)?;978			target_collection.schema_version = version;979980			<Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(981				collection_id982			));983984			target_collection.save()985		}986987		/// Set off-chain data schema.988		///989		/// # Permissions990		///991		/// * Collection Owner992		/// * Collection Admin993		///994		/// # Arguments995		///996		/// * collection_id.997		///998		/// * schema: String representing the offchain data schema.999		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1000		#[transactional]1001		pub fn set_offchain_schema(1002			origin,1003			collection_id: CollectionId,1004			schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1005		) -> DispatchResult {1006			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1007			let collection = <CollectionHandle<T>>::try_get(collection_id)?;10081009			// =========10101011			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::OffchainSchema, schema.into_inner())?;10121013			<Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1014				collection_id1015			));1016			Ok(())1017		}10181019		/// Set const on-chain data schema.1020		///1021		/// # Permissions1022		///1023		/// * Collection Owner1024		/// * Collection Admin1025		///1026		/// # Arguments1027		///1028		/// * collection_id.1029		///1030		/// * schema: String representing the const on-chain data schema.1031		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1032		#[transactional]1033		pub fn set_const_on_chain_schema (1034			origin,1035			collection_id: CollectionId,1036			schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1037		) -> DispatchResult {1038			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039			let collection = <CollectionHandle<T>>::try_get(collection_id)?;10401041			// =========10421043			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::ConstOnChainSchema, schema.into_inner())?;10441045			<Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1046				collection_id1047			));1048			Ok(())1049		}10501051		/// Set variable on-chain data schema.1052		///1053		/// # Permissions1054		///1055		/// * Collection Owner1056		/// * Collection Admin1057		///1058		/// # Arguments1059		///1060		/// * collection_id.1061		///1062		/// * schema: String representing the variable on-chain data schema.1063		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1064		#[transactional]1065		pub fn set_variable_on_chain_schema (1066			origin,1067			collection_id: CollectionId,1068			schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1069		) -> DispatchResult {1070			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1071			let collection = <CollectionHandle<T>>::try_get(collection_id)?;10721073			// =========10741075			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::VariableOnChainSchema, schema.into_inner())?;10761077			<Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1078				collection_id1079			));1080			Ok(())1081		}10821083		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1084		#[transactional]1085		pub fn set_collection_limits(1086			origin,1087			collection_id: CollectionId,1088			new_limit: CollectionLimits,1089		) -> DispatchResult {1090			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1091			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1092			target_collection.check_is_owner(&sender)?;1093			let old_limit = &target_collection.limits;10941095			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10961097			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1098				collection_id1099			));11001101			target_collection.save()1102		}1103	}1104}
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)]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	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,39	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,40	AccessMode, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId,41	SchemaVersion, SponsorshipState, MetaUpdatePermission, CreateCollectionData, CustomDataLimit,42	CreateItemExData, budget, CollectionField,43};44use pallet_evm::account::CrossAccountId;45use pallet_common::{46	CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo,47	dispatch::dispatch_call, dispatch::CollectionDispatch,48};4950#[cfg(feature = "runtime-benchmarks")]51mod benchmarking;52pub mod weights;53use weights::WeightInfo;5455pub trait SponsorshipPredict<T: Config> {56	fn predict(collection: CollectionId, account: T::CrossAccountId, token: TokenId) -> Option<u64>57	where58		u64: From<<T as frame_system::Config>::BlockNumber>;59}6061decl_error! {62	/// Error for non-fungible-token module.63	pub enum Error for Module<T: Config> {64		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.65		CollectionDecimalPointLimitExceeded,66		/// This address is not set as sponsor, use setCollectionSponsor first.67		ConfirmUnsetSponsorFail,68		/// Length of items properties must be greater than 0.69		EmptyArgument,70	}71}7273pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {74	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7576	/// Weight information for extrinsics in this pallet.77	type WeightInfo: WeightInfo;78	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;79}8081decl_event! {82	pub enum Event<T>83	where84		<T as frame_system::Config>::AccountId,85		<T as pallet_evm::account::Config>::CrossAccountId,86	{87		/// Collection sponsor was removed88		///89		/// # Arguments90		///91		/// * collection_id: Globally unique collection identifier.92		CollectionSponsorRemoved(CollectionId),9394		/// Collection admin was added95		///96		/// # Arguments97		///98		/// * collection_id: Globally unique collection identifier.99		///100		/// * admin:  Admin address.101		CollectionAdminAdded(CollectionId, CrossAccountId),102103		/// Collection owned was change104		///105		/// # Arguments106		///107		/// * collection_id: Globally unique collection identifier.108		///109		/// * owner:  New owner address.110		CollectionOwnedChanged(CollectionId, AccountId),111112		/// Collection sponsor was set113		///114		/// # Arguments115		///116		/// * collection_id: Globally unique collection identifier.117		///118		/// * owner:  New sponsor address.119		CollectionSponsorSet(CollectionId, AccountId),120121		/// const on chain schema was set122		///123		/// # Arguments124		///125		/// * collection_id: Globally unique collection identifier.126		ConstOnChainSchemaSet(CollectionId),127128		/// New sponsor was confirm129		///130		/// # Arguments131		///132		/// * collection_id: Globally unique collection identifier.133		///134		/// * sponsor:  New sponsor address.135		SponsorshipConfirmed(CollectionId, AccountId),136137		/// Collection admin was removed138		///139		/// # Arguments140		///141		/// * collection_id: Globally unique collection identifier.142		///143		/// * admin:  Admin address.144		CollectionAdminRemoved(CollectionId, CrossAccountId),145146		/// Address was remove from allow list147		///148		/// # Arguments149		///150		/// * collection_id: Globally unique collection identifier.151		///152		/// * user:  Address.153		AllowListAddressRemoved(CollectionId, CrossAccountId),154155		/// Address was add to allow list156		///157		/// # Arguments158		///159		/// * collection_id: Globally unique collection identifier.160		///161		/// * user:  Address.162		AllowListAddressAdded(CollectionId, CrossAccountId),163164		/// Collection limits was set165		///166		/// # Arguments167		///168		/// * collection_id: Globally unique collection identifier.169		CollectionLimitSet(CollectionId),170171		/// Mint permission	was set172		///173		/// # Arguments174		///175		/// * collection_id: Globally unique collection identifier.176		MintPermissionSet(CollectionId),177178		/// Offchain schema was set179		///180		/// # Arguments181		///182		/// * collection_id: Globally unique collection identifier.183		OffchainSchemaSet(CollectionId),184185		/// Public access mode was set186		///187		/// # Arguments188		///189		/// * collection_id: Globally unique collection identifier.190		///191		/// * mode: New access state.192		PublicAccessModeSet(CollectionId, AccessMode),193194		/// Schema version was set195		///196		/// # Arguments197		///198		/// * collection_id: Globally unique collection identifier.199		SchemaVersionSet(CollectionId),200201		/// Variable on chain schema was set202		///203		/// # Arguments204		///205		/// * collection_id: Globally unique collection identifier.206		VariableOnChainSchemaSet(CollectionId),207	}208}209210type SelfWeightOf<T> = <T as Config>::WeightInfo;211212// # Used definitions213//214// ## User control levels215//216// chain-controlled - key is uncontrolled by user217//                    i.e autoincrementing index218//                    can use non-cryptographic hash219// real - key is controlled by user220//        but it is hard to generate enough colliding values, i.e owner of signed txs221//        can use non-cryptographic hash222// controlled - key is completly controlled by users223//              i.e maps with mutable keys224//              should use cryptographic hash225//226// ## User control level downgrade reasons227//228// ?1 - chain-controlled -> controlled229//      collections/tokens can be destroyed, resulting in massive holes230// ?2 - chain-controlled -> controlled231//      same as ?1, but can be only added, resulting in easier exploitation232// ?3 - real -> controlled233//      no confirmation required, so addresses can be easily generated234decl_storage! {235	trait Store for Module<T: Config> as Unique {236237		//#region Private members238		/// Used for migrations239		ChainVersion: u64;240		//#endregion241242		//#region Tokens transfer rate limit baskets243		/// (Collection id (controlled?2), who created (real))244		/// TODO: Off chain worker should remove from this map when collection gets removed245		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;246		/// Collection id (controlled?2), token id (controlled?2)247		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;248		/// Collection id (controlled?2), owning user (real)249		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;250		/// Collection id (controlled?2), token id (controlled?2)251		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>;252		//#endregion253254		/// Variable metadata sponsoring255		/// Collection id (controlled?2), token id (controlled?2)256		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;257		/// Approval sponsoring258		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;259		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;260		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>;261	}262}263264decl_module! {265	pub struct Module<T: Config> for enum Call266	where267		origin: T::Origin268	{269		type Error = Error<T>;270271		fn deposit_event() = default;272273		fn on_initialize(_now: T::BlockNumber) -> Weight {274			0275		}276277		/// 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.278		///279		/// # Permissions280		///281		/// * Anyone.282		///283		/// # Arguments284		///285		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.286		///287		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.288		///289		/// * token_prefix: UTF-8 string with token prefix.290		///291		/// * mode: [CollectionMode] collection type and type dependent data.292		// returns collection ID293		#[weight = <SelfWeightOf<T>>::create_collection()]294		#[transactional]295		#[deprecated]296		pub fn create_collection(origin,297								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,298								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,299								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,300								 mode: CollectionMode) -> DispatchResult  {301			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {302				name: collection_name,303				description: collection_description,304				token_prefix,305				mode,306				..Default::default()307			};308			Self::create_collection_ex(origin, data)309		}310311		/// This method creates a collection312		///313		/// Prefer it to deprecated [`created_collection`] method314		#[weight = <SelfWeightOf<T>>::create_collection()]315		#[transactional]316		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {317			let sender = ensure_signed(origin)?;318319			// =========320321			T::CollectionDispatch::create(sender, data)?;322323			Ok(())324		}325326		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.327		///328		/// # Permissions329		///330		/// * Collection Owner.331		///332		/// # Arguments333		///334		/// * collection_id: collection to destroy.335		#[weight = <SelfWeightOf<T>>::destroy_collection()]336		#[transactional]337		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {338			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);339			let collection = <CollectionHandle<T>>::try_get(collection_id)?;340341			// =========342343			T::CollectionDispatch::destroy(sender, collection)?;344345			<NftTransferBasket<T>>::remove_prefix(collection_id, None);346			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);347			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);348349			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);350			<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		/// Toggle between normal and allow list access for the methods with access for `Anyone`.426		///427		/// # Permissions428		///429		/// * Collection Owner.430		///431		/// # Arguments432		///433		/// * collection_id.434		///435		/// * mode: [AccessMode]436		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]437		#[transactional]438		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult439		{440			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.access = mode.clone();446447			<Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(448				collection_id,449				mode450			));451452			target_collection.save()453		}454455		/// Allows Anyone to create tokens if:456		/// * Allow List is enabled, and457		/// * Address is added to allow list, and458		/// * This method was called with True parameter459		///460		/// # Permissions461		/// * Collection Owner462		///463		/// # Arguments464		///465		/// * collection_id.466		///467		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.468		#[weight = <SelfWeightOf<T>>::set_mint_permission()]469		#[transactional]470		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult471		{472			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473474			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;475			target_collection.check_is_owner(&sender)?;476477			target_collection.mint_mode = mint_permission;478479			<Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(480				collection_id481			));482483			target_collection.save()484		}485486		/// Change the owner of the collection.487		///488		/// # Permissions489		///490		/// * Collection Owner.491		///492		/// # Arguments493		///494		/// * collection_id.495		///496		/// * new_owner.497		#[weight = <SelfWeightOf<T>>::change_collection_owner()]498		#[transactional]499		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {500501			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);502503			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;504			target_collection.check_is_owner(&sender)?;505506			target_collection.owner = new_owner.clone();507			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(508				collection_id,509				new_owner510			));511512			target_collection.save()513		}514515		/// Adds an admin of the Collection.516		/// 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.517		///518		/// # Permissions519		///520		/// * Collection Owner.521		/// * Collection Admin.522		///523		/// # Arguments524		///525		/// * collection_id: ID of the Collection to add admin for.526		///527		/// * new_admin_id: Address of new admin to add.528		#[weight = <SelfWeightOf<T>>::add_collection_admin()]529		#[transactional]530		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {531			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);532			let collection = <CollectionHandle<T>>::try_get(collection_id)?;533534			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(535				collection_id,536				new_admin_id.clone()537			));538539			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)540		}541542		/// 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.543		///544		/// # Permissions545		///546		/// * Collection Owner.547		/// * Collection Admin.548		///549		/// # Arguments550		///551		/// * collection_id: ID of the Collection to remove admin for.552		///553		/// * account_id: Address of admin to remove.554		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]555		#[transactional]556		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {557			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);558			let collection = <CollectionHandle<T>>::try_get(collection_id)?;559560			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(561				collection_id,562				account_id.clone()563			));564565			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)566		}567568		/// # Permissions569		///570		/// * Collection Owner571		///572		/// # Arguments573		///574		/// * collection_id.575		///576		/// * new_sponsor.577		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]578		#[transactional]579		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {580			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);581582			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;583			target_collection.check_is_owner(&sender)?;584585			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());586587			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(588				collection_id,589				new_sponsor590			));591592			target_collection.save()593		}594595		/// # Permissions596		///597		/// * Sponsor.598		///599		/// # Arguments600		///601		/// * collection_id.602		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]603		#[transactional]604		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {605			let sender = ensure_signed(origin)?;606607			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;608			ensure!(609				target_collection.sponsorship.pending_sponsor() == Some(&sender),610				Error::<T>::ConfirmUnsetSponsorFail611			);612613			target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());614615			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(616				collection_id,617				sender618			));619620			target_collection.save()621		}622623		/// Switch back to pay-per-own-transaction model.624		///625		/// # Permissions626		///627		/// * Collection owner.628		///629		/// # Arguments630		///631		/// * collection_id.632		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]633		#[transactional]634		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {635			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);636637			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;638			target_collection.check_is_owner(&sender)?;639640			target_collection.sponsorship = SponsorshipState::Disabled;641642			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(643				collection_id644			));645			target_collection.save()646		}647648		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.649		///650		/// # Permissions651		///652		/// * Collection Owner.653		/// * Collection Admin.654		/// * Anyone if655		///     * Allow List is enabled, and656		///     * Address is added to allow list, and657		///     * MintPermission is enabled (see SetMintPermission method)658		///659		/// # Arguments660		///661		/// * collection_id: ID of the collection.662		///663		/// * owner: Address, initial owner of the NFT.664		///665		/// * data: Token data to store on chain.666		#[weight = T::CommonWeightInfo::create_item()]667		#[transactional]668		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {669			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);670			let budget = budget::Value::new(2);671672			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))673		}674675		/// This method creates multiple items in a collection created with CreateCollection method.676		///677		/// # Permissions678		///679		/// * Collection Owner.680		/// * Collection Admin.681		/// * Anyone if682		///     * Allow List is enabled, and683		///     * Address is added to allow list, and684		///     * MintPermission is enabled (see SetMintPermission method)685		///686		/// # Arguments687		///688		/// * collection_id: ID of the collection.689		///690		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].691		///692		/// * owner: Address, initial owner of the NFT.693		#[weight = T::CommonWeightInfo::create_multiple_items(items_data.len() as u32)]694		#[transactional]695		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {696			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);697			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);698			let budget = budget::Value::new(2);699700			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))701		}702703		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]704		#[transactional]705		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {706			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);707			let budget = budget::Value::new(2);708709			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))710		}711712		// TODO! transaction weight713714		/// Set transfers_enabled value for particular collection715		///716		/// # Permissions717		///718		/// * Collection Owner.719		///720		/// # Arguments721		///722		/// * collection_id: ID of the collection.723		///724		/// * value: New flag value.725		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]726		#[transactional]727		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {728			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;730			target_collection.check_is_owner(&sender)?;731732			// =========733734			target_collection.limits.transfers_enabled = Some(value);735			target_collection.save()736		}737738		/// Destroys a concrete instance of NFT.739		///740		/// # Permissions741		///742		/// * Collection Owner.743		/// * Collection Admin.744		/// * Current NFT Owner.745		///746		/// # Arguments747		///748		/// * collection_id: ID of the collection.749		///750		/// * item_id: ID of NFT to burn.751		#[weight = T::CommonWeightInfo::burn_item()]752		#[transactional]753		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {754			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);755756			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;757			if value == 1 {758				<NftTransferBasket<T>>::remove(collection_id, item_id);759				<NftApproveBasket<T>>::remove(collection_id, item_id);760			}761			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?762			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());763			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));764			Ok(post_info)765		}766767		/// Destroys a concrete instance of NFT on behalf of the owner768		/// See also: [`approve`]769		///770		/// # Permissions771		///772		/// * Collection Owner.773		/// * Collection Admin.774		/// * Current NFT Owner.775		///776		/// # Arguments777		///778		/// * collection_id: ID of the collection.779		///780		/// * item_id: ID of NFT to burn.781		///782		/// * from: owner of item783		#[weight = T::CommonWeightInfo::burn_from()]784		#[transactional]785		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {786			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);787			let budget = budget::Value::new(2);788789			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))790		}791792		/// Change ownership of the token.793		///794		/// # Permissions795		///796		/// * Collection Owner797		/// * Collection Admin798		/// * Current NFT owner799		///800		/// # Arguments801		///802		/// * recipient: Address of token recipient.803		///804		/// * collection_id.805		///806		/// * item_id: ID of the item807		///     * Non-Fungible Mode: Required.808		///     * Fungible Mode: Ignored.809		///     * Re-Fungible Mode: Required.810		///811		/// * value: Amount to transfer.812		///     * Non-Fungible Mode: Ignored813		///     * Fungible Mode: Must specify transferred amount814		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)815		#[weight = T::CommonWeightInfo::transfer()]816		#[transactional]817		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {818			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);819			let budget = budget::Value::new(2);820821			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))822		}823824		/// Set, change, or remove approved address to transfer the ownership of the NFT.825		///826		/// # Permissions827		///828		/// * Collection Owner829		/// * Collection Admin830		/// * Current NFT owner831		///832		/// # Arguments833		///834		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).835		///836		/// * collection_id.837		///838		/// * item_id: ID of the item.839		#[weight = T::CommonWeightInfo::approve()]840		#[transactional]841		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {842			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);843844			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))845		}846847		/// 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.848		///849		/// # Permissions850		/// * Collection Owner851		/// * Collection Admin852		/// * Current NFT owner853		/// * Address approved by current NFT owner854		///855		/// # Arguments856		///857		/// * from: Address that owns token.858		///859		/// * recipient: Address of token recipient.860		///861		/// * collection_id.862		///863		/// * item_id: ID of the item.864		///865		/// * value: Amount to transfer.866		#[weight = T::CommonWeightInfo::transfer_from()]867		#[transactional]868		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {869			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);870			let budget = budget::Value::new(2);871872			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))873		}874875		/// Set off-chain data schema.876		///877		/// # Permissions878		///879		/// * Collection Owner880		/// * Collection Admin881		///882		/// # Arguments883		///884		/// * collection_id.885		///886		/// * schema: String representing the offchain data schema.887		#[weight = T::CommonWeightInfo::set_variable_metadata(data.len() as u32)]888		#[transactional]889		pub fn set_variable_meta_data (890			origin,891			collection_id: CollectionId,892			item_id: TokenId,893			data: BoundedVec<u8, CustomDataLimit>,894		) -> DispatchResultWithPostInfo {895			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896897			dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))898		}899900		/// Set meta_update_permission value for particular collection901		///902		/// # Permissions903		///904		/// * Collection Owner.905		///906		/// # Arguments907		///908		/// * collection_id: ID of the collection.909		///910		/// * value: New flag value.911		#[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]912		#[transactional]913		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {914			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;916917			ensure!(918				target_collection.meta_update_permission != MetaUpdatePermission::None,919				<CommonError<T>>::MetadataFlagFrozen,920			);921			target_collection.check_is_owner(&sender)?;922923			target_collection.meta_update_permission = value;924925			target_collection.save()926		}927928		/// Set schema standard929		/// ImageURL930		/// Unique931		///932		/// # Permissions933		///934		/// * Collection Owner935		/// * Collection Admin936		///937		/// # Arguments938		///939		/// * collection_id.940		///941		/// * schema: SchemaVersion: enum942		#[weight = <SelfWeightOf<T>>::set_schema_version()]943		#[transactional]944		pub fn set_schema_version(945			origin,946			collection_id: CollectionId,947			version: SchemaVersion948		) -> DispatchResult {949			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);950			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;951			target_collection.check_is_owner_or_admin(&sender)?;952			target_collection.schema_version = version;953954			<Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(955				collection_id956			));957958			target_collection.save()959		}960961		/// Set off-chain data schema.962		///963		/// # Permissions964		///965		/// * Collection Owner966		/// * Collection Admin967		///968		/// # Arguments969		///970		/// * collection_id.971		///972		/// * schema: String representing the offchain data schema.973		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]974		#[transactional]975		pub fn set_offchain_schema(976			origin,977			collection_id: CollectionId,978			schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,979		) -> DispatchResult {980			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981			let collection = <CollectionHandle<T>>::try_get(collection_id)?;982983			// =========984985			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::OffchainSchema, schema.into_inner())?;986987			<Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(988				collection_id989			));990			Ok(())991		}992993		/// Set const on-chain data schema.994		///995		/// # Permissions996		///997		/// * Collection Owner998		/// * Collection Admin999		///1000		/// # Arguments1001		///1002		/// * collection_id.1003		///1004		/// * schema: String representing the const on-chain data schema.1005		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1006		#[transactional]1007		pub fn set_const_on_chain_schema (1008			origin,1009			collection_id: CollectionId,1010			schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1011		) -> DispatchResult {1012			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013			let collection = <CollectionHandle<T>>::try_get(collection_id)?;10141015			// =========10161017			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::ConstOnChainSchema, schema.into_inner())?;10181019			<Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1020				collection_id1021			));1022			Ok(())1023		}10241025		/// Set variable on-chain data schema.1026		///1027		/// # Permissions1028		///1029		/// * Collection Owner1030		/// * Collection Admin1031		///1032		/// # Arguments1033		///1034		/// * collection_id.1035		///1036		/// * schema: String representing the variable on-chain data schema.1037		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1038		#[transactional]1039		pub fn set_variable_on_chain_schema (1040			origin,1041			collection_id: CollectionId,1042			schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1043		) -> DispatchResult {1044			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1045			let collection = <CollectionHandle<T>>::try_get(collection_id)?;10461047			// =========10481049			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::VariableOnChainSchema, schema.into_inner())?;10501051			<Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1052				collection_id1053			));1054			Ok(())1055		}10561057		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1058		#[transactional]1059		pub fn set_collection_limits(1060			origin,1061			collection_id: CollectionId,1062			new_limit: CollectionLimits,1063		) -> DispatchResult {1064			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1065			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1066			target_collection.check_is_owner(&sender)?;1067			let old_limit = &target_collection.limits;10681069			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10701071			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1072				collection_id1073			));10741075			target_collection.save()1076		}1077	}1078}
deletedpallets/unique/src/mock.rsdiffbeforeafterboth
--- a/pallets/unique/src/mock.rs
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-#![allow(clippy::from_over_into)]
-
-use crate as pallet_template;
-use sp_core::{H160, H256};
-use frame_support::{parameter_types, traits::Everything, weights::IdentityFee};
-use sp_runtime::{
-	traits::{BlakeTwo256, IdentityLookup},
-	testing::Header,
-};
-use pallet_transaction_payment::{CurrencyAdapter};
-use frame_system as system;
-use pallet_evm::{AddressMapping, runner::stack::MaybeMirroredLog, account::CrossAccountId};
-use fp_evm_mapping::EvmBackwardsAddressMapping;
-use codec::{Encode, Decode, MaxEncodedLen};
-use scale_info::TypeInfo;
-use up_data_structs::ConstU32;
-
-type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
-type Block = frame_system::mocking::MockBlock<Test>;
-
-// Configure a mock runtime to test the pallet.
-frame_support::construct_runtime!(
-	pub enum Test where
-		Block = Block,
-		NodeBlock = Block,
-		UncheckedExtrinsic = UncheckedExtrinsic,
-	{
-		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
-		TemplateModule: pallet_template::{Pallet, Call, Storage},
-		Balances: pallet_balances::{Pallet, Call, Storage},
-		Common: pallet_common::{Pallet, Storage, Event<T>},
-		Fungible: pallet_fungible::{Pallet, Storage},
-		Refungible: pallet_refungible::{Pallet, Storage},
-		Nonfungible: pallet_nonfungible::{Pallet, Storage},
-	}
-);
-
-parameter_types! {
-	pub const BlockHashCount: u64 = 250;
-	pub const SS58Prefix: u8 = 42;
-}
-
-impl system::Config for Test {
-	type BaseCallFilter = Everything;
-	type BlockWeights = ();
-	type BlockLength = ();
-	type DbWeight = ();
-	type Origin = Origin;
-	type Call = Call;
-	type Index = u64;
-	type BlockNumber = u64;
-	type Hash = H256;
-	type Hashing = BlakeTwo256;
-	type AccountId = u64;
-	type Lookup = IdentityLookup<Self::AccountId>;
-	type Header = Header;
-	type Event = ();
-	type BlockHashCount = BlockHashCount;
-	type Version = ();
-	type PalletInfo = PalletInfo;
-	type AccountData = pallet_balances::AccountData<u64>;
-	type OnNewAccount = ();
-	type OnKilledAccount = ();
-	type SystemWeightInfo = ();
-	type SS58Prefix = SS58Prefix;
-	type OnSetCode = ();
-	type MaxConsumers = ConstU32<16>;
-}
-
-parameter_types! {
-	pub const ExistentialDeposit: u64 = 1;
-	pub const MaxLocks: u32 = 50;
-}
-//frame_system::Module<Test>;
-impl pallet_balances::Config for Test {
-	type AccountStore = System;
-	type Balance = u64;
-	type DustRemoval = ();
-	type Event = ();
-	type ExistentialDeposit = ExistentialDeposit;
-	type WeightInfo = ();
-	type MaxLocks = MaxLocks;
-	type MaxReserves = ();
-	type ReserveIdentifier = [u8; 8];
-}
-
-parameter_types! {
-	pub const OperationalFeeMultiplier: u8 = 5;
-}
-
-impl pallet_transaction_payment::Config for Test {
-	type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;
-	type LengthToFee = IdentityFee<u64>;
-	type WeightToFee = IdentityFee<u64>;
-	type FeeMultiplierUpdate = ();
-	type OperationalFeeMultiplier = OperationalFeeMultiplier;
-}
-
-parameter_types! {
-	pub const MinimumPeriod: u64 = 1;
-}
-impl pallet_timestamp::Config for Test {
-	type Moment = u64;
-	type OnTimestampSet = ();
-	type MinimumPeriod = MinimumPeriod;
-	type WeightInfo = ();
-}
-
-parameter_types! {
-	pub const CollectionCreationPrice: u32 = 100;
-	pub TreasuryAccountId: u64 = 1234;
-	pub EthereumChainId: u32 = 1111;
-}
-
-pub struct TestEvmAddressMapping;
-impl AddressMapping<u64> for TestEvmAddressMapping {
-	fn into_account_id(_addr: sp_core::H160) -> u64 {
-		unimplemented!()
-	}
-}
-
-pub struct TestEvmBackwardsAddressMapping;
-impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {
-	fn from_account_id(_account_id: u64) -> sp_core::H160 {
-		unimplemented!()
-	}
-}
-
-#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, MaxEncodedLen)]
-pub struct TestCrossAccountId(u64, sp_core::H160);
-impl CrossAccountId<u64> for TestCrossAccountId {
-	fn as_sub(&self) -> &u64 {
-		&self.0
-	}
-	fn as_eth(&self) -> &sp_core::H160 {
-		&self.1
-	}
-	fn from_sub(sub: u64) -> Self {
-		let mut eth = [0; 20];
-		eth[12..20].copy_from_slice(&sub.to_be_bytes());
-		Self(sub, sp_core::H160(eth))
-	}
-	fn from_eth(eth: sp_core::H160) -> Self {
-		let mut sub_raw = [0; 8];
-		sub_raw.copy_from_slice(&eth.0[0..8]);
-		let sub = u64::from_be_bytes(sub_raw);
-		Self(sub, eth)
-	}
-	fn conv_eq(&self, other: &Self) -> bool {
-		self.as_sub() == other.as_sub()
-	}
-}
-
-impl Default for TestCrossAccountId {
-	fn default() -> Self {
-		Self::from_sub(0)
-	}
-}
-
-pub struct TestEtheremTransactionSender;
-impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {
-	fn submit_logs_transaction(_source: H160, _logs: Vec<MaybeMirroredLog>) {}
-}
-
-impl pallet_evm_coder_substrate::Config for Test {
-	type EthereumTransactionSender = TestEtheremTransactionSender;
-	type GasWeightMapping = ();
-}
-
-impl pallet_common::Config for Test {
-	type Event = ();
-	type Currency = Balances;
-	type CollectionCreationPrice = CollectionCreationPrice;
-	type TreasuryAccountId = TreasuryAccountId;
-}
-
-impl pallet_evm::account::Config for Test {
-	type CrossAccountId = TestCrossAccountId;
-	type EvmAddressMapping = TestEvmAddressMapping;
-	type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
-}
-
-impl pallet_fungible::Config for Test {
-	type WeightInfo = ();
-}
-impl pallet_refungible::Config for Test {
-	type WeightInfo = ();
-}
-impl pallet_nonfungible::Config for Test {
-	type WeightInfo = ();
-}
-
-impl pallet_template::Config for Test {
-	type Event = ();
-	type WeightInfo = ();
-}
-
-// Build genesis storage according to the mock runtime.
-pub fn new_test_ext() -> sp_io::TestExternalities {
-	system::GenesisConfig::default()
-		.build_storage::<Test>()
-		.unwrap()
-		.into()
-}
deletedpallets/unique/src/sponsorship.rsdiffbeforeafterboth
--- a/pallets/unique/src/sponsorship.rs
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use crate::{
-	Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
-	FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
-	FungibleApproveBasket, RefungibleApproveBasket,
-};
-use core::marker::PhantomData;
-use up_sponsorship::SponsorshipHandler;
-use frame_support::{
-	traits::{IsSubType},
-	storage::{StorageMap, StorageDoubleMap, StorageNMap},
-};
-use up_data_structs::{
-	CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MetaUpdatePermission,
-	NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,
-};
-use sp_runtime::traits::Saturating;
-use pallet_common::{CollectionHandle};
-use pallet_evm::account::CrossAccountId;
-
-pub fn withdraw_transfer<T: Config>(
-	collection: &CollectionHandle<T>,
-	who: &T::CrossAccountId,
-	item_id: &TokenId,
-) -> Option<()> {
-	// preliminary sponsoring correctness check
-	match collection.mode {
-		CollectionMode::NFT => {
-			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
-			if !owner.conv_eq(who) {
-				return None;
-			}
-		}
-		CollectionMode::Fungible(_) => {
-			if item_id != &TokenId::default() {
-				return None;
-			}
-			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
-				return None;
-			}
-		}
-		CollectionMode::ReFungible => {
-			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
-				return None;
-			}
-		}
-	}
-
-	// sponsor timeout
-	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-	let limit = collection
-		.limits
-		.sponsor_transfer_timeout(match collection.mode {
-			CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
-			CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-			CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-		});
-
-	let last_tx_block = match collection.mode {
-		CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
-		CollectionMode::Fungible(_) => {
-			<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
-		}
-		CollectionMode::ReFungible => {
-			<ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))
-		}
-	};
-
-	if let Some(last_tx_block) = last_tx_block {
-		let timeout = last_tx_block + limit.into();
-		if block_number < timeout {
-			return None;
-		}
-	}
-
-	match collection.mode {
-		CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
-		CollectionMode::Fungible(_) => {
-			<FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)
-		}
-		CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(
-			(collection.id, item_id, who.as_sub()),
-			block_number,
-		),
-	};
-
-	Some(())
-}
-
-pub fn withdraw_create_item<T: Config>(
-	collection: &CollectionHandle<T>,
-	who: &T::CrossAccountId,
-	_properties: &CreateItemData,
-) -> Option<()> {
-	if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
-		return None;
-	}
-
-	// sponsor timeout
-	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-	let limit = collection
-		.limits
-		.sponsor_transfer_timeout(match _properties {
-			CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
-			CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-			CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-		});
-
-	if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {
-		let timeout = last_tx_block + limit.into();
-		if block_number < timeout {
-			return None;
-		}
-	}
-
-	CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);
-
-	Some(())
-}
-
-pub fn withdraw_set_variable_meta_data<T: Config>(
-	who: &T::CrossAccountId,
-	collection: &CollectionHandle<T>,
-	item_id: &TokenId,
-	data: &[u8],
-) -> Option<()> {
-	// TODO: make it work for admins
-	if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {
-		return None;
-	}
-	// preliminary sponsoring correctness check
-	match collection.mode {
-		CollectionMode::NFT => {
-			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
-			if !owner.conv_eq(who) {
-				return None;
-			}
-		}
-		CollectionMode::Fungible(_) => {
-			if item_id != &TokenId::default() {
-				return None;
-			}
-			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
-				return None;
-			}
-		}
-		CollectionMode::ReFungible => {
-			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
-				return None;
-			}
-		}
-	}
-
-	// Can't sponsor fungible collection, this tx will be rejected
-	// as invalid
-	if matches!(collection.mode, CollectionMode::Fungible(_)) {
-		return None;
-	}
-	if data.len() > collection.limits.sponsored_data_size() as usize {
-		return None;
-	}
-
-	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-	let limit = collection.limits.sponsored_data_rate_limit()?;
-
-	if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
-		let timeout = last_tx_block + limit.into();
-		if block_number < timeout {
-			return None;
-		}
-	}
-
-	<VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
-
-	Some(())
-}
-
-pub fn withdraw_approve<T: Config>(
-	collection: &CollectionHandle<T>,
-	who: &T::AccountId,
-	item_id: &TokenId,
-) -> Option<()> {
-	// sponsor timeout
-	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-	let limit = collection.limits.sponsor_approve_timeout();
-
-	let last_tx_block = match collection.mode {
-		CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
-		CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
-		CollectionMode::ReFungible => {
-			<RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
-		}
-	};
-
-	if let Some(last_tx_block) = last_tx_block {
-		let timeout = last_tx_block + limit.into();
-		if block_number < timeout {
-			return None;
-		}
-	}
-
-	match collection.mode {
-		CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
-		CollectionMode::Fungible(_) => {
-			<FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
-		}
-		CollectionMode::ReFungible => {
-			<RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
-		}
-	};
-
-	Some(())
-}
-
-fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
-	let collection = CollectionHandle::new(id)?;
-	let sponsor = collection.sponsorship.sponsor().cloned()?;
-	Some((sponsor, collection))
-}
-
-pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);
-impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>
-where
-	T: Config,
-	C: IsSubType<Call<T>>,
-{
-	fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
-		match IsSubType::<Call<T>>::is_sub_type(call)? {
-			Call::create_item {
-				collection_id,
-				data,
-				..
-			} => {
-				let (sponsor, collection) = load(*collection_id)?;
-				withdraw_create_item::<T>(
-					&collection,
-					&T::CrossAccountId::from_sub(who.clone()),
-					data,
-				)
-				.map(|()| sponsor)
-			}
-			Call::transfer {
-				collection_id,
-				item_id,
-				..
-			} => {
-				let (sponsor, collection) = load(*collection_id)?;
-				withdraw_transfer::<T>(
-					&collection,
-					&T::CrossAccountId::from_sub(who.clone()),
-					item_id,
-				)
-				.map(|()| sponsor)
-			}
-			Call::transfer_from {
-				collection_id,
-				item_id,
-				from,
-				..
-			} => {
-				let (sponsor, collection) = load(*collection_id)?;
-				withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)
-			}
-			Call::approve {
-				collection_id,
-				item_id,
-				..
-			} => {
-				let (sponsor, collection) = load(*collection_id)?;
-				withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
-			}
-			Call::set_variable_meta_data {
-				collection_id,
-				item_id,
-				data,
-			} => {
-				let (sponsor, collection) = load(*collection_id)?;
-				withdraw_set_variable_meta_data::<T>(
-					&T::CrossAccountId::from_sub(who.clone()),
-					&collection,
-					item_id,
-					data,
-				)
-				.map(|()| sponsor)
-			}
-			_ => None,
-		}
-	}
-}
-
-use crate::SponsorshipPredict;
-pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);
-
-impl<T> SponsorshipPredict<T> for UniqueSponsorshipPredict<T>
-where
-	T: Config,
-{
-	fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>
-	where
-		u64: From<<T as frame_system::Config>::BlockNumber>,
-	{
-		let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;
-		let _ = collection.sponsorship.sponsor()?;
-
-		// sponsor timeout
-		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-		let limit = collection
-			.limits
-			.sponsor_transfer_timeout(match collection.mode {
-				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
-				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-			});
-
-		let last_tx_block = match collection.mode {
-			CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),
-			CollectionMode::Fungible(_) => {
-				<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
-			}
-			CollectionMode::ReFungible => {
-				<ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))
-			}
-		};
-
-		if let Some(last_tx_block) = last_tx_block {
-			return Some(
-				last_tx_block
-					.saturating_add(limit.into())
-					.saturating_sub(block_number)
-					.into(),
-			);
-		}
-
-		let token_exists = match collection.mode {
-			CollectionMode::NFT => {
-				<pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))
-			}
-			CollectionMode::Fungible(_) => true,
-			CollectionMode::ReFungible => {
-				<pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))
-			}
-		};
-
-		if token_exists {
-			Some(0)
-		} else {
-			None
-		}
-	}
-}
deletedpallets/unique/src/tests.rsdiffbeforeafterboth
--- a/pallets/unique/src/tests.rs
+++ /dev/null
@@ -1,2912 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// Tests to be written here
-use super::*;
-use crate::mock::*;
-use crate::{AccessMode, CollectionMode};
-use up_data_structs::{
-	COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
-	CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
-	TokenId, MAX_TOKEN_OWNERSHIP,
-};
-use frame_support::{assert_noop, assert_ok, assert_err};
-use sp_std::convert::TryInto;
-use pallet_balances;
-
-fn add_balance(user: u64, value: u64) {
-	const DONOR_USER: u64 = 999;
-	assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(
-		Origin::root(),
-		DONOR_USER,
-		value,
-		0
-	));
-	assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(
-		Origin::root(),
-		DONOR_USER,
-		user,
-		value
-	));
-}
-
-fn default_nft_data() -> CreateNftData {
-	CreateNftData {
-		const_data: vec![1, 2, 3].try_into().unwrap(),
-		variable_data: vec![3, 2, 1].try_into().unwrap(),
-	}
-}
-
-fn default_fungible_data() -> CreateFungibleData {
-	CreateFungibleData { value: 5 }
-}
-
-fn default_re_fungible_data() -> CreateReFungibleData {
-	CreateReFungibleData {
-		const_data: vec![1, 2, 3].try_into().unwrap(),
-		variable_data: vec![3, 2, 1].try_into().unwrap(),
-		pieces: 1023,
-	}
-}
-
-fn create_test_collection_for_owner(
-	mode: &CollectionMode,
-	owner: u64,
-	id: CollectionId,
-) -> CollectionId {
-	add_balance(owner, CollectionCreationPrice::get() as u64 + 1);
-
-	let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-	let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-	let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
-
-	let data: CreateCollectionData<u64> = CreateCollectionData {
-		name: col_name1.try_into().unwrap(),
-		description: col_desc1.try_into().unwrap(),
-		token_prefix: token_prefix1.try_into().unwrap(),
-		mode: mode.clone(),
-		..Default::default()
-	};
-
-	let origin1 = Origin::signed(owner);
-	assert_ok!(TemplateModule::create_collection_ex(origin1, data));
-
-	let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-	let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-	let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
-	assert_eq!(
-		<pallet_common::CollectionById<Test>>::get(id)
-			.unwrap()
-			.owner,
-		owner
-	);
-	assert_eq!(
-		<pallet_common::CollectionById<Test>>::get(id).unwrap().name,
-		saved_col_name
-	);
-	assert_eq!(
-		<pallet_common::CollectionById<Test>>::get(id).unwrap().mode,
-		*mode
-	);
-	assert_eq!(
-		<pallet_common::CollectionById<Test>>::get(id)
-			.unwrap()
-			.description,
-		saved_description
-	);
-	assert_eq!(
-		<pallet_common::CollectionById<Test>>::get(id)
-			.unwrap()
-			.token_prefix,
-		saved_prefix
-	);
-	id
-}
-
-fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {
-	create_test_collection_for_owner(&mode, 1, id)
-}
-
-fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {
-	let origin1 = Origin::signed(1);
-	assert_ok!(TemplateModule::create_item(
-		origin1,
-		collection_id,
-		account(1),
-		data.clone()
-	));
-}
-
-fn account(sub: u64) -> TestCrossAccountId {
-	TestCrossAccountId::from_sub(sub)
-}
-
-// Use cases tests region
-// #region
-
-#[test]
-fn set_version_schema() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		assert_ok!(TemplateModule::set_schema_version(
-			origin1,
-			collection_id,
-			SchemaVersion::Unique
-		));
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.schema_version,
-			SchemaVersion::Unique
-		);
-	});
-}
-
-#[test]
-fn check_not_sufficient_founds() {
-	new_test_ext().execute_with(|| {
-		let acc: u64 = 1;
-		<pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();
-
-		let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-		let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
-
-		let data: CreateCollectionData<<Test as system::Config>::AccountId> =
-			CreateCollectionData {
-				name: name.try_into().unwrap(),
-				description: description.try_into().unwrap(),
-				token_prefix: token_prefix.try_into().unwrap(),
-				mode: CollectionMode::NFT,
-				..Default::default()
-			};
-
-		let result = TemplateModule::create_collection_ex(Origin::signed(acc), data);
-		assert_err!(result, <CommonError<Test>>::NotSufficientFounds);
-	});
-}
-
-#[test]
-fn create_fungible_collection_fails_with_large_decimal_numbers() {
-	new_test_ext().execute_with(|| {
-		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
-
-		let data: CreateCollectionData<u64> = CreateCollectionData {
-			name: col_name1.try_into().unwrap(),
-			description: col_desc1.try_into().unwrap(),
-			token_prefix: token_prefix1.try_into().unwrap(),
-			mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),
-			..Default::default()
-		};
-
-		let origin1 = Origin::signed(1);
-		assert_noop!(
-			TemplateModule::create_collection_ex(origin1, data),
-			Error::<Test>::CollectionDecimalPointLimitExceeded
-		);
-	});
-}
-
-#[test]
-fn create_nft_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.clone().into());
-
-		let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();
-		assert_eq!(item.const_data, data.const_data.into_inner());
-		assert_eq!(item.variable_data, data.variable_data.into_inner());
-	});
-}
-
-// Use cases tests region
-// #region
-#[test]
-fn create_nft_multiple_items() {
-	new_test_ext().execute_with(|| {
-		create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];
-
-		assert_ok!(TemplateModule::create_multiple_items(
-			origin1,
-			CollectionId(1),
-			account(1),
-			items_data
-				.clone()
-				.into_iter()
-				.map(|d| { d.into() })
-				.collect()
-		));
-		for (index, data) in items_data.into_iter().enumerate() {
-			let item = <pallet_nonfungible::TokenData<Test>>::get((
-				CollectionId(1),
-				TokenId((index + 1) as u32),
-			))
-			.unwrap();
-			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
-			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
-		}
-	});
-}
-
-#[test]
-fn create_refungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-
-		let data = default_re_fungible_data();
-		create_test_item(collection_id, &data.clone().into());
-		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
-		let balance =
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));
-		assert_eq!(item.const_data, data.const_data.into_inner());
-		assert_eq!(item.variable_data, data.variable_data.into_inner());
-		assert_eq!(balance, 1023);
-	});
-}
-
-#[test]
-fn create_multiple_refungible_items() {
-	new_test_ext().execute_with(|| {
-		create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let items_data = vec![
-			default_re_fungible_data(),
-			default_re_fungible_data(),
-			default_re_fungible_data(),
-		];
-
-		assert_ok!(TemplateModule::create_multiple_items(
-			origin1,
-			CollectionId(1),
-			account(1),
-			items_data
-				.clone()
-				.into_iter()
-				.map(|d| { d.into() })
-				.collect()
-		));
-		for (index, data) in items_data.into_iter().enumerate() {
-			let item = <pallet_refungible::TokenData<Test>>::get((
-				CollectionId(1),
-				TokenId((index + 1) as u32),
-			));
-			let balance =
-				<pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));
-			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
-			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
-			assert_eq!(balance, 1023);
-		}
-	});
-}
-
-#[test]
-fn create_fungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
-			5
-		);
-	});
-}
-
-//#[test]
-// fn create_multiple_fungible_items() {
-//     new_test_ext().execute_with(|| {
-//         default_limits();
-
-//         create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-//         let origin1 = Origin::signed(1);
-
-//         let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
-
-//         assert_ok!(TemplateModule::create_multiple_items(
-//             origin1.clone(),
-//             1,
-//             1,
-//             items_data.clone().into_iter().map(|d| { d.into() }).collect()
-//         ));
-
-//         for (index, _) in items_data.iter().enumerate() {
-//             assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);
-//         }
-//         assert_eq!(TemplateModule::balance_count(1, 1), 3000);
-//         assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
-//     });
-// }
-
-#[test]
-fn transfer_fungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
-			5
-		);
-
-		// change owner scenario
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(0),
-			5
-		));
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
-			0
-		);
-
-		// split item scenario
-		assert_ok!(TemplateModule::transfer(
-			origin2.clone(),
-			account(3),
-			CollectionId(1),
-			TokenId(0),
-			3
-		));
-
-		// split item and new owner has account scenario
-		assert_ok!(TemplateModule::transfer(
-			origin2,
-			account(3),
-			CollectionId(1),
-			TokenId(0),
-			1
-		));
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),
-			1
-		);
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),
-			4
-		);
-	});
-}
-
-#[test]
-fn transfer_refungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-
-		// Create RFT 1 in 1023 pieces for account 1
-		let data = default_re_fungible_data();
-		create_test_item(collection_id, &data.clone().into());
-		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
-		assert_eq!(item.const_data, data.const_data.into_inner());
-		assert_eq!(item.variable_data, data.variable_data.into_inner());
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
-			1023
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		// Account 1 transfers all 1023 pieces of RFT 1 to account 2
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1023
-		));
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
-			1023
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			false
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			true
-		);
-
-		// Account 2 transfers 500 pieces of RFT 1 to account 3
-		assert_ok!(TemplateModule::transfer(
-			origin2.clone(),
-			account(3),
-			CollectionId(1),
-			TokenId(1),
-			500
-		));
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
-			523
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
-			500
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
-			true
-		);
-
-		// Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance
-		assert_ok!(TemplateModule::transfer(
-			origin2,
-			account(3),
-			CollectionId(1),
-			TokenId(1),
-			200
-		));
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
-			323
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
-			700
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
-			true
-		);
-	});
-}
-
-#[test]
-fn transfer_nft_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		let origin1 = Origin::signed(1);
-		// default scenario
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			false
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			true
-		);
-	});
-}
-
-#[test]
-fn transfer_nft_item_wrong_value() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		let origin1 = Origin::signed(1);
-
-		assert_noop!(
-			TemplateModule::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)
-				.map_err(|e| e.error),
-			<pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount
-		);
-	});
-}
-
-#[test]
-fn transfer_nft_item_zero_value() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		let origin1 = Origin::signed(1);
-
-		// Transferring 0 amount works on NFT...
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			0
-		));
-		// ... and results in no transfer
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-	});
-}
-
-#[test]
-fn nft_approve_and_transfer_from() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		// neg transfer_from
-		assert_noop!(
-			TemplateModule::transfer_from(
-				origin2.clone(),
-				account(1),
-				account(2),
-				CollectionId(1),
-				TokenId(1),
-				1
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::ApprovedValueTooLow
-		);
-
-		// do approve
-		assert_ok!(TemplateModule::approve(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(2)
-		);
-
-		assert_ok!(TemplateModule::transfer_from(
-			origin2,
-			account(1),
-			account(3),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
-		);
-	});
-}
-
-#[test]
-fn nft_approve_and_transfer_from_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		// Create NFT 1 for account 1
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.clone().into());
-		assert_eq!(
-			&<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
-				.unwrap()
-				.const_data,
-			&data.const_data.into_inner()
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		// Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			CollectionId(1),
-			true
-		));
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			CollectionId(1),
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(3)
-		));
-
-		// Account 1 approves account 2 for NFT 1
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(2)
-		);
-
-		// Account 2 transfers NFT 1 from account 1 to account 3
-		assert_ok!(TemplateModule::transfer_from(
-			origin2,
-			account(1),
-			account(3),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
-		);
-	});
-}
-
-#[test]
-fn refungible_approve_and_transfer_from() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		// Create RFT 1 in 1023 pieces for account 1
-		let data = default_re_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
-			1023
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		// Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			CollectionId(1),
-			true
-		));
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			CollectionId(1),
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(3)
-		));
-
-		// Account 1 approves account 2 for 1023 pieces of RFT 1
-		assert_ok!(TemplateModule::approve(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1023
-		));
-		assert_eq!(
-			<pallet_refungible::Allowance<Test>>::get((
-				CollectionId(1),
-				TokenId(1),
-				account(1),
-				account(2)
-			)),
-			1023
-		);
-
-		// Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3
-		assert_ok!(TemplateModule::transfer_from(
-			origin2,
-			account(1),
-			account(3),
-			CollectionId(1),
-			TokenId(1),
-			100
-		));
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
-			923
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
-			100
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_refungible::Allowance<Test>>::get((
-				CollectionId(1),
-				TokenId(1),
-				account(1),
-				account(2)
-			)),
-			923
-		);
-	});
-}
-
-#[test]
-fn fungible_approve_and_transfer_from() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			CollectionId(1),
-			true
-		));
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			CollectionId(1),
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(3)
-		));
-
-		// do approve
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(2),
-			CollectionId(1),
-			TokenId(0),
-			5
-		));
-		assert_eq!(
-			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
-			5
-		);
-		assert_ok!(TemplateModule::approve(
-			origin1,
-			account(3),
-			CollectionId(1),
-			TokenId(0),
-			5
-		));
-		assert_eq!(
-			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
-			5
-		);
-		assert_eq!(
-			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),
-			5
-		);
-
-		assert_ok!(TemplateModule::transfer_from(
-			origin2.clone(),
-			account(1),
-			account(3),
-			CollectionId(1),
-			TokenId(0),
-			4
-		));
-
-		assert_eq!(
-			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
-			1
-		);
-
-		assert_noop!(
-			TemplateModule::transfer_from(
-				origin2,
-				account(1),
-				account(3),
-				CollectionId(1),
-				TokenId(0),
-				4
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::ApprovedValueTooLow
-		);
-	});
-}
-
-#[test]
-fn change_collection_owner() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::change_collection_owner(
-			origin1,
-			collection_id,
-			2
-		));
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.owner,
-			2
-		);
-	});
-}
-
-#[test]
-fn destroy_collection() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::destroy_collection(origin1, collection_id));
-	});
-}
-
-#[test]
-fn burn_nft_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		// check balance (collection with id = 1, user id = 1)
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-
-		// burn item
-		assert_ok!(TemplateModule::burn_item(
-			origin1.clone(),
-			collection_id,
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			0
-		);
-	});
-}
-
-#[test]
-fn burn_same_nft_item_twice() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		// check balance (collection with id = 1, user id = 1)
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-
-		// burn item
-		assert_ok!(TemplateModule::burn_item(
-			origin1.clone(),
-			collection_id,
-			TokenId(1),
-			1
-		));
-
-		// burn item again
-		assert_noop!(
-			TemplateModule::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),
-			CommonError::<Test>::TokenNotFound
-		);
-
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			0
-		);
-	});
-}
-
-#[test]
-fn burn_fungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		// check balance (collection with id = 1, user id = 1)
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
-			5
-		);
-
-		// burn item
-		assert_ok!(TemplateModule::burn_item(
-			origin1.clone(),
-			CollectionId(1),
-			TokenId(0),
-			5
-		));
-		assert_noop!(
-			TemplateModule::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),
-			CommonError::<Test>::TokenValueTooLow
-		);
-
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
-			0
-		);
-	});
-}
-
-#[test]
-fn burn_fungible_item_with_token_id() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		// check balance (collection with id = 1, user id = 1)
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
-			5
-		);
-
-		// Try to burn item using Token ID
-		assert_noop!(
-			TemplateModule::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),
-			<pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId
-		);
-	});
-}
-#[test]
-fn burn_refungible_item() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			true
-		));
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		let data = default_re_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		// check balance (collection with id = 1, user id = 2)
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
-			1023
-		);
-
-		// burn item
-		assert_ok!(TemplateModule::burn_item(
-			origin1.clone(),
-			collection_id,
-			TokenId(1),
-			1023
-		));
-		assert_noop!(
-			TemplateModule::burn_item(origin1, collection_id, TokenId(1), 1023)
-				.map_err(|e| e.error),
-			CommonError::<Test>::TokenValueTooLow
-		);
-
-		assert_eq!(
-			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
-			0
-		);
-	});
-}
-
-#[test]
-fn add_collection_admin() {
-	new_test_ext().execute_with(|| {
-		let collection1_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		// Add collection admins
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection1_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection1_id,
-			account(3)
-		));
-
-		// Owner is not an admin by default
-		assert_eq!(
-			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),
-			false
-		);
-		assert!(<pallet_common::IsAdmin<Test>>::get((
-			CollectionId(1),
-			account(2)
-		)));
-		assert!(<pallet_common::IsAdmin<Test>>::get((
-			CollectionId(1),
-			account(3)
-		)));
-	});
-}
-
-#[test]
-fn remove_collection_admin() {
-	new_test_ext().execute_with(|| {
-		let collection1_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		// Add collection admins 2 and 3
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection1_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection1_id,
-			account(3)
-		));
-
-		assert!(<pallet_common::IsAdmin<Test>>::get((
-			CollectionId(1),
-			account(2)
-		)));
-		assert!(<pallet_common::IsAdmin<Test>>::get((
-			CollectionId(1),
-			account(3)
-		)));
-
-		// remove admin 3
-		assert_ok!(TemplateModule::remove_collection_admin(
-			origin2,
-			CollectionId(1),
-			account(3)
-		));
-
-		// 2 is still admin, 3 is not an admin anymore
-		assert!(<pallet_common::IsAdmin<Test>>::get((
-			CollectionId(1),
-			account(2)
-		)));
-		assert_eq!(
-			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),
-			false
-		);
-	});
-}
-
-#[test]
-fn balance_of() {
-	new_test_ext().execute_with(|| {
-		let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let fungible_collection_id =
-			create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));
-		let re_fungible_collection_id =
-			create_test_collection(&CollectionMode::ReFungible, CollectionId(3));
-
-		// check balance before
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
-			0
-		);
-
-		let nft_data = default_nft_data();
-		create_test_item(nft_collection_id, &nft_data.into());
-
-		let fungible_data = default_fungible_data();
-		create_test_item(fungible_collection_id, &fungible_data.into());
-
-		let re_fungible_data = default_re_fungible_data();
-		create_test_item(re_fungible_collection_id, &re_fungible_data.into());
-
-		// check balance (collection with id = 1, user id = 1)
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
-			5
-		);
-		assert_eq!(
-			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
-			1
-		);
-
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_refungible::Owned<Test>>::get((
-				re_fungible_collection_id,
-				account(1),
-				TokenId(1)
-			)),
-			true
-		);
-	});
-}
-
-#[test]
-fn approve() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		let origin1 = Origin::signed(1);
-
-		// approve
-		assert_ok!(TemplateModule::approve(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(2)
-		);
-	});
-}
-
-#[test]
-fn transfer_from() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		// approve
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(2)
-		);
-
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			CollectionId(1),
-			true
-		));
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			CollectionId(1),
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			CollectionId(1),
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			CollectionId(1),
-			account(3)
-		));
-
-		assert_ok!(TemplateModule::transfer_from(
-			origin2,
-			account(1),
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-
-		// after transfer
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),
-			1
-		);
-	});
-}
-
-// #endregion
-
-// Coverage tests region
-// #region
-
-#[test]
-fn owner_can_add_address_to_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert!(<pallet_common::Allowlist<Test>>::get((
-			collection_id,
-			account(2)
-		)));
-	});
-}
-
-#[test]
-fn admin_can_add_address_to_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin2,
-			collection_id,
-			account(3)
-		));
-		assert!(<pallet_common::Allowlist<Test>>::get((
-			collection_id,
-			account(3)
-		)));
-	});
-}
-
-#[test]
-fn nonprivileged_user_cannot_add_address_to_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin2 = Origin::signed(2);
-		assert_noop!(
-			TemplateModule::add_to_allow_list(origin2, collection_id, account(3)),
-			CommonError::<Test>::NoPermission
-		);
-	});
-}
-
-#[test]
-fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-
-		assert_noop!(
-			TemplateModule::add_to_allow_list(origin1, CollectionId(1), account(2)),
-			CommonError::<Test>::CollectionNotFound
-		);
-	});
-}
-
-#[test]
-fn nobody_can_add_address_to_allow_list_of_deleted_collection() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::destroy_collection(
-			origin1.clone(),
-			collection_id
-		));
-		assert_noop!(
-			TemplateModule::add_to_allow_list(origin1, collection_id, account(2)),
-			CommonError::<Test>::CollectionNotFound
-		);
-	});
-}
-
-// If address is already added to allow list, nothing happens
-#[test]
-fn address_is_already_added_to_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert!(<pallet_common::Allowlist<Test>>::get((
-			collection_id,
-			account(2)
-		)));
-	});
-}
-
-#[test]
-fn owner_can_remove_address_from_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert_eq!(
-			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
-			false
-		);
-	});
-}
-
-#[test]
-fn admin_can_remove_address_from_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		// Owner adds admin
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		// Owner adds address 3 to allow list
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(3)
-		));
-
-		// Admin removes address 3 from allow list
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin2,
-			collection_id,
-			account(3)
-		));
-		assert_eq!(
-			<pallet_common::Allowlist<Test>>::get((collection_id, account(3))),
-			false
-		);
-	});
-}
-
-#[test]
-fn nonprivileged_user_cannot_remove_address_from_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert_noop!(
-			TemplateModule::remove_from_allow_list(origin2, collection_id, account(2)),
-			CommonError::<Test>::NoPermission
-		);
-		assert!(<pallet_common::Allowlist<Test>>::get((
-			collection_id,
-			account(2)
-		)));
-	});
-}
-
-#[test]
-fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-
-		assert_noop!(
-			TemplateModule::remove_from_allow_list(origin1, CollectionId(1), account(2)),
-			CommonError::<Test>::CollectionNotFound
-		);
-	});
-}
-
-#[test]
-fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		// Add account 2 to allow list
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		// Account 2 is in collection allow-list
-		assert!(<pallet_common::Allowlist<Test>>::get((
-			collection_id,
-			account(2)
-		)));
-
-		// Destroy collection
-		assert_ok!(TemplateModule::destroy_collection(origin1, collection_id));
-
-		// Attempt to remove account 2 from collection allow-list => error
-		assert_noop!(
-			TemplateModule::remove_from_allow_list(origin2, collection_id, account(2)),
-			CommonError::<Test>::CollectionNotFound
-		);
-
-		// Account 2 is not found in collection allow-list anyway
-		assert_eq!(
-			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
-			false
-		);
-	});
-}
-
-// If address is already removed from allow list, nothing happens
-#[test]
-fn address_is_already_removed_from_allow_list() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-		assert_eq!(
-			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
-			false
-		);
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-		assert_eq!(
-			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
-			false
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)
-#[test]
-fn allow_list_test_1() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		assert_noop!(
-			TemplateModule::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)
-				.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-#[test]
-fn allow_list_test_2() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		// do approve
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(1),
-			collection_id,
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(1)
-		);
-
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-
-		assert_noop!(
-			TemplateModule::transfer_from(
-				origin1,
-				account(1),
-				account(3),
-				CollectionId(1),
-				TokenId(1),
-				1
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)
-#[test]
-fn allow_list_test_3() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-
-		assert_noop!(
-			TemplateModule::transfer(origin1, account(3), collection_id, TokenId(1), 1)
-				.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-#[test]
-fn allow_list_test_4() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		// do approve
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(1),
-			collection_id,
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(1)
-		);
-
-		assert_ok!(TemplateModule::remove_from_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		assert_noop!(
-			TemplateModule::transfer_from(
-				origin1,
-				account(1),
-				account(3),
-				collection_id,
-				TokenId(1),
-				1
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)
-#[test]
-fn allow_list_test_5() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_noop!(
-			TemplateModule::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1)
-				.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).
-#[test]
-fn allow_list_test_6() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-
-		// do approve
-		assert_noop!(
-			TemplateModule::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)
-				.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and
-//          tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)
-#[test]
-fn allow_list_test_7() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-	});
-}
-
-#[test]
-fn allow_list_test_8() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		// Create NFT for account 1
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		let origin1 = Origin::signed(1);
-
-		// Toggle Allow List mode and add accounts 1 and 2
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(1)
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-
-		// Sself-approve account 1 for NFT 1
-		assert_ok!(TemplateModule::approve(
-			origin1.clone(),
-			account(1),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
-			account(1)
-		);
-
-		// Transfer from 1 to 2
-		assert_ok!(TemplateModule::transfer_from(
-			origin1,
-			account(1),
-			account(2),
-			CollectionId(1),
-			TokenId(1),
-			1
-		));
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.
-#[test]
-fn allow_list_test_9() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1,
-			collection_id,
-			false
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.
-#[test]
-fn allow_list_test_10() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			false
-		));
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection_id,
-			account(2)
-		));
-
-		assert_ok!(TemplateModule::create_item(
-			origin2,
-			collection_id,
-			account(2),
-			default_nft_data().into()
-		));
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.
-#[test]
-fn allow_list_test_11() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			false
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-
-		assert_noop!(
-			TemplateModule::create_item(
-				origin2,
-				CollectionId(1),
-				account(2),
-				default_nft_data().into()
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::PublicMintingNotAllowed
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.
-#[test]
-fn allow_list_test_12() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1,
-			collection_id,
-			false
-		));
-
-		assert_noop!(
-			TemplateModule::create_item(
-				origin2,
-				CollectionId(1),
-				account(2),
-				default_nft_data().into()
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::PublicMintingNotAllowed
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.
-#[test]
-fn allow_list_test_13() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1,
-			collection_id,
-			true
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.
-#[test]
-fn allow_list_test_14() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			true
-		));
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection_id,
-			account(2)
-		));
-
-		assert_ok!(TemplateModule::create_item(
-			origin2,
-			collection_id,
-			account(2),
-			default_nft_data().into()
-		));
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.
-#[test]
-fn allow_list_test_15() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1,
-			collection_id,
-			true
-		));
-
-		assert_noop!(
-			TemplateModule::create_item(
-				origin2,
-				collection_id,
-				account(2),
-				default_nft_data().into()
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::AddressNotInAllowlist
-		);
-	});
-}
-
-// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.
-#[test]
-fn allow_list_test_16() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			true
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1,
-			collection_id,
-			account(2)
-		));
-
-		assert_ok!(TemplateModule::create_item(
-			origin2,
-			collection_id,
-			account(2),
-			default_nft_data().into()
-		));
-	});
-}
-
-// Total number of collections. Positive test
-#[test]
-fn total_number_collections_bound() {
-	new_test_ext().execute_with(|| {
-		create_test_collection(&CollectionMode::NFT, CollectionId(1));
-	});
-}
-
-#[test]
-fn create_max_collections() {
-	new_test_ext().execute_with(|| {
-		for i in 1..COLLECTION_NUMBER_LIMIT {
-			create_test_collection(&CollectionMode::NFT, CollectionId(i));
-		}
-	});
-}
-
-// Total number of collections. Negative test
-#[test]
-fn total_number_collections_bound_neg() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-
-		for i in 1..=COLLECTION_NUMBER_LIMIT {
-			create_test_collection(&CollectionMode::NFT, CollectionId(i));
-		}
-
-		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
-
-		let data: CreateCollectionData<u64> = CreateCollectionData {
-			name: col_name1.try_into().unwrap(),
-			description: col_desc1.try_into().unwrap(),
-			token_prefix: token_prefix1.try_into().unwrap(),
-			mode: CollectionMode::NFT,
-			..Default::default()
-		};
-
-		// 11-th collection in chain. Expects error
-		assert_noop!(
-			TemplateModule::create_collection_ex(origin1, data),
-			CommonError::<Test>::TotalCollectionsLimitExceeded
-		);
-	});
-}
-
-// Owned tokens by a single address. Positive test
-#[test]
-fn owned_tokens_bound() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.clone().into());
-		create_test_item(collection_id, &data.into());
-	});
-}
-
-// Owned tokens by a single address. Negotive test
-#[test]
-fn owned_tokens_bound_neg() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		for _ in 1..=MAX_TOKEN_OWNERSHIP {
-			let data = default_nft_data();
-			create_test_item(collection_id, &data.clone().into());
-		}
-
-		let data = default_nft_data();
-		assert_noop!(
-			TemplateModule::create_item(origin1, CollectionId(1), account(1), data.into())
-				.map_err(|e| e.error),
-			CommonError::<Test>::AccountTokenLimitExceeded
-		);
-	});
-}
-
-// Number of collection admins. Positive test
-#[test]
-fn collection_admins_bound() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1.clone(),
-			collection_id,
-			account(2)
-		));
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin1,
-			collection_id,
-			account(3)
-		));
-	});
-}
-
-// Number of collection admins. Negotive test
-#[test]
-fn collection_admins_bound_neg() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		for i in 0..COLLECTION_ADMINS_LIMIT {
-			assert_ok!(TemplateModule::add_collection_admin(
-				origin1.clone(),
-				collection_id,
-				account((2 + i).into())
-			));
-		}
-		assert_noop!(
-			TemplateModule::add_collection_admin(
-				origin1,
-				collection_id,
-				account((3 + COLLECTION_ADMINS_LIMIT).into())
-			),
-			CommonError::<Test>::CollectionAdminCountExceeded
-		);
-	});
-}
-// #endregion
-
-#[test]
-fn set_const_on_chain_schema() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::set_const_on_chain_schema(
-			origin1,
-			collection_id,
-			b"test const on chain schema".to_vec().try_into().unwrap()
-		));
-
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.const_on_chain_schema,
-			b"test const on chain schema".to_vec()
-		);
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.variable_on_chain_schema,
-			b"".to_vec()
-		);
-	});
-}
-
-#[test]
-fn set_variable_on_chain_schema() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		assert_ok!(TemplateModule::set_variable_on_chain_schema(
-			origin1,
-			collection_id,
-			b"test variable on chain schema"
-				.to_vec()
-				.try_into()
-				.unwrap()
-		));
-
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.const_on_chain_schema,
-			b"".to_vec()
-		);
-		assert_eq!(
-			<pallet_common::CollectionById<Test>>::get(collection_id)
-				.unwrap()
-				.variable_on_chain_schema,
-			b"test variable on chain schema".to_vec()
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(CollectionId(1), &data.into());
-
-		let variable_data = b"test data".to_vec();
-		assert_ok!(TemplateModule::set_variable_meta_data(
-			origin1,
-			collection_id,
-			TokenId(1),
-			variable_data.clone().try_into().unwrap()
-		));
-
-		assert_eq!(
-			<pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
-				.unwrap()
-				.variable_data,
-			variable_data
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_re_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		let variable_data = b"test data".to_vec();
-		assert_ok!(TemplateModule::set_variable_meta_data(
-			origin1,
-			collection_id,
-			TokenId(1),
-			variable_data.clone().try_into().unwrap()
-		));
-
-		assert_eq!(
-			<pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1))).variable_data,
-			variable_data
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_fungible_token_fails() {
-	new_test_ext().execute_with(|| {
-		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_fungible_data();
-		create_test_item(collection_id, &data.into());
-
-		let variable_data = b"test data".to_vec();
-		assert_noop!(
-			TemplateModule::set_variable_meta_data(
-				origin1,
-				collection_id,
-				TokenId(0),
-				variable_data.try_into().unwrap()
-			)
-			.map_err(|e| e.error),
-			<pallet_fungible::Error<Test>>::FungibleItemsDontHaveData
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {
-	new_test_ext().execute_with(|| {
-		//default_limits();
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_meta_update_permission_flag(
-			origin1.clone(),
-			collection_id,
-			MetaUpdatePermission::ItemOwner,
-		));
-
-		let variable_data = b"ten chars.".to_vec();
-		assert_ok!(TemplateModule::set_variable_meta_data(
-			origin1,
-			collection_id,
-			TokenId(1),
-			variable_data.clone().try_into().unwrap()
-		));
-
-		assert_eq!(
-			<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
-				.unwrap()
-				.variable_data,
-			variable_data
-		);
-	});
-}
-
-#[test]
-fn collection_transfer_flag_works() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		assert_ok!(TemplateModule::set_transfers_enabled_flag(
-			origin1,
-			collection_id,
-			true
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		let origin1 = Origin::signed(1);
-
-		// default scenario
-		assert_ok!(TemplateModule::transfer(
-			origin1,
-			account(2),
-			collection_id,
-			TokenId(1),
-			1
-		));
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			false
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			0
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			1
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_nft_with_admin_flag() {
-	new_test_ext().execute_with(|| {
-		// default_limits();
-
-		let collection_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin2.clone(),
-			collection_id,
-			true
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin2.clone(),
-			collection_id,
-			account(1)
-		));
-
-		assert_ok!(TemplateModule::add_collection_admin(
-			origin2.clone(),
-			collection_id,
-			account(1)
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_meta_update_permission_flag(
-			origin2.clone(),
-			collection_id,
-			MetaUpdatePermission::Admin,
-		));
-
-		let variable_data = b"test.".to_vec();
-		assert_ok!(TemplateModule::set_variable_meta_data(
-			origin1,
-			collection_id,
-			TokenId(1),
-			variable_data.clone().try_into().unwrap()
-		));
-
-		assert_eq!(
-			<pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
-				.unwrap()
-				.variable_data,
-			variable_data
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
-	new_test_ext().execute_with(|| {
-		// default_limits();
-
-		let collection_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
-
-		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin2.clone(),
-			collection_id,
-			true
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin2.clone(),
-			collection_id,
-			account(1)
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_meta_update_permission_flag(
-			origin2.clone(),
-			collection_id,
-			MetaUpdatePermission::Admin,
-		));
-
-		let variable_data = b"test.".to_vec();
-		assert_noop!(
-			TemplateModule::set_variable_meta_data(
-				origin1,
-				collection_id,
-				TokenId(1),
-				variable_data.try_into().unwrap()
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::NoPermission
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_flag_after_freeze() {
-	new_test_ext().execute_with(|| {
-		// default_limits();
-
-		let collection_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
-
-		let origin2 = Origin::signed(2);
-
-		assert_ok!(TemplateModule::set_meta_update_permission_flag(
-			origin2.clone(),
-			collection_id,
-			MetaUpdatePermission::None,
-		));
-		assert_noop!(
-			TemplateModule::set_meta_update_permission_flag(
-				origin2.clone(),
-				collection_id,
-				MetaUpdatePermission::Admin
-			),
-			CommonError::<Test>::MetadataFlagFrozen
-		);
-	});
-}
-
-#[test]
-fn set_variable_meta_data_on_nft_with_none_flag_neg() {
-	new_test_ext().execute_with(|| {
-		// default_limits();
-
-		let collection_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
-		let origin1 = Origin::signed(1);
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-
-		assert_ok!(TemplateModule::set_meta_update_permission_flag(
-			origin1.clone(),
-			collection_id,
-			MetaUpdatePermission::None,
-		));
-
-		let variable_data = b"test.".to_vec();
-		assert_noop!(
-			TemplateModule::set_variable_meta_data(
-				origin1.clone(),
-				collection_id,
-				TokenId(1),
-				variable_data.try_into().unwrap()
-			)
-			.map_err(|e| e.error),
-			CommonError::<Test>::NoPermission
-		);
-	});
-}
-
-#[test]
-fn collection_transfer_flag_works_neg() {
-	new_test_ext().execute_with(|| {
-		let origin1 = Origin::signed(1);
-
-		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
-		assert_ok!(TemplateModule::set_transfers_enabled_flag(
-			origin1,
-			collection_id,
-			false
-		));
-
-		let data = default_nft_data();
-		create_test_item(collection_id, &data.into());
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-
-		let origin1 = Origin::signed(1);
-
-		// default scenario
-		assert_noop!(
-			TemplateModule::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)
-				.map_err(|e| e.error),
-			CommonError::<Test>::TransferNotAllowed
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
-			1
-		);
-		assert_eq!(
-			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
-			0
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
-			true
-		);
-		assert_eq!(
-			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
-			false
-		);
-	});
-}
-
-#[test]
-fn collection_sponsoring() {
-	new_test_ext().execute_with(|| {
-		// default_limits();
-		let user1 = 1_u64;
-		let user2 = 777_u64;
-		let origin1 = Origin::signed(user1);
-		let origin2 = Origin::signed(user2);
-		let account2 = account(user2);
-
-		let collection_id =
-			create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
-		assert_ok!(TemplateModule::set_collection_sponsor(
-			origin1.clone(),
-			collection_id,
-			user1
-		));
-		assert_ok!(TemplateModule::confirm_sponsorship(
-			origin1.clone(),
-			collection_id
-		));
-
-		// Expect error while have no permissions
-		assert!(TemplateModule::create_item(
-			origin2.clone(),
-			collection_id,
-			account2.clone(),
-			default_nft_data().into()
-		)
-		.is_err());
-
-		assert_ok!(TemplateModule::set_public_access_mode(
-			origin1.clone(),
-			collection_id,
-			AccessMode::AllowList
-		));
-		assert_ok!(TemplateModule::add_to_allow_list(
-			origin1.clone(),
-			collection_id,
-			account2.clone()
-		));
-		assert_ok!(TemplateModule::set_mint_permission(
-			origin1.clone(),
-			collection_id,
-			true
-		));
-
-		assert_ok!(TemplateModule::create_item(
-			origin2,
-			collection_id,
-			account2,
-			default_nft_data().into()
-		));
-	});
-}
modifiedruntime/common/Cargo.tomldiffbeforeafterboth
--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -104,4 +104,6 @@
 branch = "unique-polkadot-v0.9.20"
 
 [dependencies]
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+up-sponsorship = { default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring", branch = 'polkadot-v0.9.18' }
addedruntime/common/src/eth_sponsoring.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/common/src/eth_sponsoring.rs
@@ -0,0 +1,107 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Implements EVM sponsoring logic via TransactionValidityHack
+
+use evm_coder::{Call, abi::AbiReader};
+use pallet_common::{CollectionHandle, eth::map_eth_to_id};
+use sp_core::H160;
+use sp_std::prelude::*;
+use up_sponsorship::SponsorshipHandler;
+use core::marker::PhantomData;
+use core::convert::TryInto;
+use pallet_evm::account::CrossAccountId;
+use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode};
+use pallet_unique::Config as UniqueConfig;
+
+use crate::sponsoring::*;
+
+use pallet_nonfungible::erc::{
+	UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,
+};
+use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
+use pallet_fungible::Config as FungibleConfig;
+use pallet_nonfungible::Config as NonfungibleConfig;
+use pallet_refungible::Config as RefungibleConfig;
+
+pub struct UniqueEthSponsorshipHandler<T: UniqueConfig>(PhantomData<*const T>);
+impl<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>
+	SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T>
+{
+	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
+		let collection_id = map_eth_to_id(&call.0)?;
+		let collection = <CollectionHandle<T>>::new(collection_id)?;
+		let sponsor = collection.sponsorship.sponsor()?.clone();
+		let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
+		Some(T::CrossAccountId::from_sub(match &collection.mode {
+			CollectionMode::NFT => {
+				let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
+				match call {
+					UniqueNFTCall::ERC721UniqueExtensions(
+						ERC721UniqueExtensionsCall::Transfer { token_id, .. },
+					) => {
+						let token_id: TokenId = token_id.try_into().ok()?;
+						withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
+					}
+					UniqueNFTCall::ERC721Mintable(
+						ERC721MintableCall::Mint { token_id, .. }
+						| ERC721MintableCall::MintWithTokenUri { token_id, .. },
+					) => {
+						let _token_id: TokenId = token_id.try_into().ok()?;
+						withdraw_create_item::<T>(
+							&collection,
+							&who,
+							&CreateItemData::NFT(CreateNftData::default()),
+						)
+						.map(|()| sponsor)
+					}
+					UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {
+						let token_id: TokenId = token_id.try_into().ok()?;
+						let from = T::CrossAccountId::from_eth(from);
+						withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)
+					}
+					UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
+						let token_id: TokenId = token_id.try_into().ok()?;
+						withdraw_approve::<T>(&collection, who.as_sub(), &token_id)
+							.map(|()| sponsor)
+					}
+					_ => None,
+				}
+			}
+			CollectionMode::Fungible(_) => {
+				let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
+				#[allow(clippy::single_match)]
+				match call {
+					UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
+						withdraw_transfer::<T>(&collection, who, &TokenId::default())
+							.map(|()| sponsor)
+					}
+					UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {
+						let from = T::CrossAccountId::from_eth(from);
+						withdraw_transfer::<T>(&collection, &from, &TokenId::default())
+							.map(|()| sponsor)
+					}
+					UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
+						withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())
+							.map(|()| sponsor)
+					}
+					_ => None,
+				}
+			}
+			_ => None,
+		}?))
+	}
+}
modifiedruntime/common/src/lib.rsdiffbeforeafterboth
--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.rs
@@ -2,5 +2,8 @@
 
 pub mod constants;
 pub mod dispatch;
+pub mod eth_sponsoring;
 pub mod runtime_apis;
+pub mod sponsoring;
 pub mod types;
+pub mod weights;
addedruntime/common/src/sponsoring.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/common/src/sponsoring.rs
@@ -0,0 +1,375 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use core::marker::PhantomData;
+use up_sponsorship::SponsorshipHandler;
+use frame_support::{
+	traits::{IsSubType},
+	storage::{StorageMap, StorageDoubleMap, StorageNMap},
+};
+use up_data_structs::{
+	CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MetaUpdatePermission,
+	NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId, CollectionMode,
+	CreateItemData,
+};
+use sp_runtime::traits::Saturating;
+use pallet_common::{CollectionHandle};
+use pallet_evm::account::CrossAccountId;
+use pallet_unique::{
+	Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,
+	NftApproveBasket, VariableMetaDataBasket, CreateItemBasket, ReFungibleTransferBasket,
+	FungibleTransferBasket, NftTransferBasket,
+};
+use pallet_fungible::Config as FungibleConfig;
+use pallet_nonfungible::Config as NonfungibleConfig;
+use pallet_refungible::Config as RefungibleConfig;
+
+pub fn withdraw_transfer<
+	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+	collection: &CollectionHandle<T>,
+	who: &T::CrossAccountId,
+	item_id: &TokenId,
+) -> Option<()> {
+	// preliminary sponsoring correctness check
+	match collection.mode {
+		CollectionMode::NFT => {
+			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
+			if !owner.conv_eq(who) {
+				return None;
+			}
+		}
+		CollectionMode::Fungible(_) => {
+			if item_id != &TokenId::default() {
+				return None;
+			}
+			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
+				return None;
+			}
+		}
+		CollectionMode::ReFungible => {
+			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
+				return None;
+			}
+		}
+	}
+
+	// sponsor timeout
+	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+	let limit = collection
+		.limits
+		.sponsor_transfer_timeout(match collection.mode {
+			CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+			CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+			CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+		});
+
+	let last_tx_block = match collection.mode {
+		CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
+		CollectionMode::Fungible(_) => {
+			<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
+		}
+		CollectionMode::ReFungible => {
+			<ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))
+		}
+	};
+
+	if let Some(last_tx_block) = last_tx_block {
+		let timeout = last_tx_block + limit.into();
+		if block_number < timeout {
+			return None;
+		}
+	}
+
+	match collection.mode {
+		CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
+		CollectionMode::Fungible(_) => {
+			<FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)
+		}
+		CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(
+			(collection.id, item_id, who.as_sub()),
+			block_number,
+		),
+	};
+
+	Some(())
+}
+
+pub fn withdraw_create_item<
+	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+	collection: &CollectionHandle<T>,
+	who: &T::CrossAccountId,
+	_properties: &CreateItemData,
+) -> Option<()> {
+	if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
+		return None;
+	}
+
+	// sponsor timeout
+	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+	let limit = collection
+		.limits
+		.sponsor_transfer_timeout(match _properties {
+			CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+			CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+			CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+		});
+
+	if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, who.as_sub())) {
+		let timeout = last_tx_block + limit.into();
+		if block_number < timeout {
+			return None;
+		}
+	}
+
+	CreateItemBasket::<T>::insert((collection.id, who.as_sub()), block_number);
+
+	Some(())
+}
+
+pub fn withdraw_set_variable_meta_data<
+	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+>(
+	who: &T::CrossAccountId,
+	collection: &CollectionHandle<T>,
+	item_id: &TokenId,
+	data: &[u8],
+) -> Option<()> {
+	// TODO: make it work for admins
+	if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {
+		return None;
+	}
+	// preliminary sponsoring correctness check
+	match collection.mode {
+		CollectionMode::NFT => {
+			let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
+			if !owner.conv_eq(who) {
+				return None;
+			}
+		}
+		CollectionMode::Fungible(_) => {
+			if item_id != &TokenId::default() {
+				return None;
+			}
+			if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
+				return None;
+			}
+		}
+		CollectionMode::ReFungible => {
+			if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
+				return None;
+			}
+		}
+	}
+
+	// Can't sponsor fungible collection, this tx will be rejected
+	// as invalid
+	if matches!(collection.mode, CollectionMode::Fungible(_)) {
+		return None;
+	}
+	if data.len() > collection.limits.sponsored_data_size() as usize {
+		return None;
+	}
+
+	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+	let limit = collection.limits.sponsored_data_rate_limit()?;
+
+	if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
+		let timeout = last_tx_block + limit.into();
+		if block_number < timeout {
+			return None;
+		}
+	}
+
+	<VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
+
+	Some(())
+}
+
+pub fn withdraw_approve<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>(
+	collection: &CollectionHandle<T>,
+	who: &T::AccountId,
+	item_id: &TokenId,
+) -> Option<()> {
+	// sponsor timeout
+	let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+	let limit = collection.limits.sponsor_approve_timeout();
+
+	let last_tx_block = match collection.mode {
+		CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
+		CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
+		CollectionMode::ReFungible => {
+			<RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
+		}
+	};
+
+	if let Some(last_tx_block) = last_tx_block {
+		let timeout = last_tx_block + limit.into();
+		if block_number < timeout {
+			return None;
+		}
+	}
+
+	match collection.mode {
+		CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
+		CollectionMode::Fungible(_) => {
+			<FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
+		}
+		CollectionMode::ReFungible => {
+			<RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
+		}
+	};
+
+	Some(())
+}
+
+fn load<T: UniqueConfig>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
+	let collection = CollectionHandle::new(id)?;
+	let sponsor = collection.sponsorship.sponsor().cloned()?;
+	Some((sponsor, collection))
+}
+
+pub struct UniqueSponsorshipHandler<T>(PhantomData<T>);
+impl<T, C> SponsorshipHandler<T::AccountId, C> for UniqueSponsorshipHandler<T>
+where
+	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
+	C: IsSubType<UniqueCall<T>>,
+{
+	fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+		match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {
+			UniqueCall::create_item {
+				collection_id,
+				data,
+				..
+			} => {
+				let (sponsor, collection) = load(*collection_id)?;
+				withdraw_create_item::<T>(
+					&collection,
+					&T::CrossAccountId::from_sub(who.clone()),
+					data,
+				)
+				.map(|()| sponsor)
+			}
+			UniqueCall::transfer {
+				collection_id,
+				item_id,
+				..
+			} => {
+				let (sponsor, collection) = load(*collection_id)?;
+				withdraw_transfer::<T>(
+					&collection,
+					&T::CrossAccountId::from_sub(who.clone()),
+					item_id,
+				)
+				.map(|()| sponsor)
+			}
+			UniqueCall::transfer_from {
+				collection_id,
+				item_id,
+				from,
+				..
+			} => {
+				let (sponsor, collection) = load(*collection_id)?;
+				withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)
+			}
+			UniqueCall::approve {
+				collection_id,
+				item_id,
+				..
+			} => {
+				let (sponsor, collection) = load(*collection_id)?;
+				withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
+			}
+			UniqueCall::set_variable_meta_data {
+				collection_id,
+				item_id,
+				data,
+			} => {
+				let (sponsor, collection) = load(*collection_id)?;
+				withdraw_set_variable_meta_data::<T>(
+					&T::CrossAccountId::from_sub(who.clone()),
+					&collection,
+					item_id,
+					data,
+				)
+				.map(|()| sponsor)
+			}
+			_ => None,
+		}
+	}
+}
+
+use crate::SponsorshipPredict;
+pub struct UniqueSponsorshipPredict<T>(PhantomData<T>);
+
+impl<T> SponsorshipPredict<T> for UniqueSponsorshipPredict<T>
+where
+	T: Config,
+{
+	fn predict(collection_id: CollectionId, who: T::CrossAccountId, token: TokenId) -> Option<u64>
+	where
+		u64: From<<T as frame_system::Config>::BlockNumber>,
+	{
+		let collection = <CollectionHandle<T>>::try_get(collection_id).ok()?;
+		let _ = collection.sponsorship.sponsor()?;
+
+		// sponsor timeout
+		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+		let limit = collection
+			.limits
+			.sponsor_transfer_timeout(match collection.mode {
+				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+			});
+
+		let last_tx_block = match collection.mode {
+			CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, token),
+			CollectionMode::Fungible(_) => {
+				<FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
+			}
+			CollectionMode::ReFungible => {
+				<ReFungibleTransferBasket<T>>::get((collection.id, token, who.as_sub()))
+			}
+		};
+
+		if let Some(last_tx_block) = last_tx_block {
+			return Some(
+				last_tx_block
+					.saturating_add(limit.into())
+					.saturating_sub(block_number)
+					.into(),
+			);
+		}
+
+		let token_exists = match collection.mode {
+			CollectionMode::NFT => {
+				<pallet_nonfungible::TokenData<T>>::contains_key((collection.id, token))
+			}
+			CollectionMode::Fungible(_) => true,
+			CollectionMode::ReFungible => {
+				<pallet_refungible::TotalSupply<T>>::contains_key((collection.id, token))
+			}
+		};
+
+		if token_exists {
+			Some(0)
+		} else {
+			None
+		}
+	}
+}
addedruntime/common/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/common/src/weights.rs
@@ -0,0 +1,76 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use core::marker::PhantomData;
+use frame_support::{weights::Weight};
+use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight};
+
+use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};
+use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};
+use pallet_refungible::{Config as RefungibleConfig, common::CommonWeights as RefungibleWeights};
+use up_data_structs::CreateItemExData;
+
+macro_rules! max_weight_of {
+	($method:ident ( $($args:tt)* )) => {
+		<FungibleWeights<T>>::$method($($args)*)
+		.max(<NonfungibleWeights<T>>::$method($($args)*))
+		.max(<RefungibleWeights<T>>::$method($($args)*))
+	};
+}
+
+pub struct CommonWeights<T>(PhantomData<T>)
+where
+	T: FungibleConfig + NonfungibleConfig + RefungibleConfig;
+impl<T> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T>
+where
+	T: FungibleConfig + NonfungibleConfig + RefungibleConfig,
+{
+	fn create_item() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(create_item())
+	}
+
+	fn create_multiple_items(amount: u32) -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
+	}
+
+	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
+	}
+
+	fn burn_item() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(burn_item())
+	}
+
+	fn transfer() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(transfer())
+	}
+
+	fn approve() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(approve())
+	}
+
+	fn transfer_from() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(transfer_from())
+	}
+
+	fn set_variable_metadata(bytes: u32) -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
+	}
+
+	fn burn_from() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(burn_from())
+	}
+}
modifiedruntime/opal/src/lib.rsdiffbeforeafterboth
--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -120,6 +120,9 @@
 	types::*,
 	constants::*,
 	dispatch::{CollectionDispatchT, CollectionDispatch},
+	sponsoring::UniqueSponsorshipHandler,
+	eth_sponsoring::UniqueEthSponsorshipHandler,
+	weights::CommonWeights,
 };
 
 pub const RUNTIME_NAME: &str = "opal";
@@ -902,6 +905,7 @@
 impl pallet_unique::Config for Runtime {
 	type Event = Event;
 	type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;
+	type CommonWeightInfo = CommonWeights<Self>;
 }
 
 parameter_types! {
@@ -923,11 +927,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>,
 );
addedruntime/tests/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/runtime/tests/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "tests"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+unique-runtime-common = { path = '../common' }
+up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
+
+sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.18-2" }
+
+frame-support = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+frame-system = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+pallet-transaction-payment = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.18' }
+
+pallet-evm = { default-features = false, git = 'https://github.com/uniquenetwork/frontier.git', branch = 'unique-polkadot-v0.9.18-2' }
+pallet-ethereum = { default-features = false, git = 'https://github.com/uniquenetwork/frontier.git', branch = 'unique-polkadot-v0.9.18-2' }
+
+pallet-common = { path = '../../pallets/common' }
+pallet-structure = { path = '../../pallets/structure' }
+pallet-fungible = { path = '../../pallets/fungible' }
+pallet-nonfungible = { path = '../../pallets/nonfungible' }
+pallet-refungible = { path = '../../pallets/refungible' }
+pallet-unique = { path = '../../pallets/unique' }
+
+pallet-evm-coder-substrate = { path = '../../pallets/evm-coder-substrate' }
+
+parity-scale-codec = "*"
+scale-info = "*"
addedruntime/tests/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/tests/src/lib.rs
@@ -0,0 +1,238 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(clippy::from_over_into)]
+
+use sp_core::{H160, H256};
+use frame_support::{
+	parameter_types,
+	traits::{Everything, ConstU32},
+	weights::IdentityFee,
+};
+use sp_runtime::{
+	traits::{BlakeTwo256, IdentityLookup},
+	testing::Header,
+};
+use pallet_transaction_payment::{CurrencyAdapter};
+use frame_system as system;
+use pallet_evm::{AddressMapping, runner::stack::MaybeMirroredLog, account::CrossAccountId};
+use fp_evm_mapping::EvmBackwardsAddressMapping;
+use parity_scale_codec::{Encode, Decode, MaxEncodedLen};
+use scale_info::TypeInfo;
+
+use unique_runtime_common::{dispatch::CollectionDispatchT, weights::CommonWeights};
+use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+type Block = frame_system::mocking::MockBlock<Test>;
+
+#[cfg(test)]
+mod tests;
+
+// Configure a mock runtime to test the pallet.
+frame_support::construct_runtime!(
+	pub enum Test where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic,
+	{
+		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+		Unique: pallet_unique::{Pallet, Call, Storage},
+		Balances: pallet_balances::{Pallet, Call, Storage},
+		Common: pallet_common::{Pallet, Storage, Event<T>},
+		Fungible: pallet_fungible::{Pallet, Storage},
+		Refungible: pallet_refungible::{Pallet, Storage},
+		Nonfungible: pallet_nonfungible::{Pallet, Storage},
+	}
+);
+
+parameter_types! {
+	pub const BlockHashCount: u64 = 250;
+	pub const SS58Prefix: u8 = 42;
+}
+
+impl system::Config for Test {
+	type BaseCallFilter = Everything;
+	type BlockWeights = ();
+	type BlockLength = ();
+	type DbWeight = ();
+	type Origin = Origin;
+	type Call = Call;
+	type Index = u64;
+	type BlockNumber = u64;
+	type Hash = H256;
+	type Hashing = BlakeTwo256;
+	type AccountId = u64;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Header = Header;
+	type Event = ();
+	type BlockHashCount = BlockHashCount;
+	type Version = ();
+	type PalletInfo = PalletInfo;
+	type AccountData = pallet_balances::AccountData<u64>;
+	type OnNewAccount = ();
+	type OnKilledAccount = ();
+	type SystemWeightInfo = ();
+	type SS58Prefix = SS58Prefix;
+	type OnSetCode = ();
+	type MaxConsumers = ConstU32<16>;
+}
+
+parameter_types! {
+	pub const ExistentialDeposit: u64 = 1;
+	pub const MaxLocks: u32 = 50;
+}
+//frame_system::Module<Test>;
+impl pallet_balances::Config for Test {
+	type AccountStore = System;
+	type Balance = u64;
+	type DustRemoval = ();
+	type Event = ();
+	type ExistentialDeposit = ExistentialDeposit;
+	type WeightInfo = ();
+	type MaxLocks = MaxLocks;
+	type MaxReserves = ();
+	type ReserveIdentifier = [u8; 8];
+}
+
+parameter_types! {
+	pub const OperationalFeeMultiplier: u8 = 5;
+}
+
+impl pallet_transaction_payment::Config for Test {
+	type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;
+	type LengthToFee = IdentityFee<u64>;
+	type WeightToFee = IdentityFee<u64>;
+	type FeeMultiplierUpdate = ();
+	type OperationalFeeMultiplier = OperationalFeeMultiplier;
+}
+
+parameter_types! {
+	pub const MinimumPeriod: u64 = 1;
+}
+impl pallet_timestamp::Config for Test {
+	type Moment = u64;
+	type OnTimestampSet = ();
+	type MinimumPeriod = MinimumPeriod;
+	type WeightInfo = ();
+}
+
+parameter_types! {
+	pub const CollectionCreationPrice: u32 = 100;
+	pub TreasuryAccountId: u64 = 1234;
+	pub EthereumChainId: u32 = 1111;
+}
+
+pub struct TestEvmAddressMapping;
+impl AddressMapping<u64> for TestEvmAddressMapping {
+	fn into_account_id(_addr: sp_core::H160) -> u64 {
+		unimplemented!()
+	}
+}
+
+pub struct TestEvmBackwardsAddressMapping;
+impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {
+	fn from_account_id(_account_id: u64) -> sp_core::H160 {
+		unimplemented!()
+	}
+}
+
+#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, TypeInfo, MaxEncodedLen)]
+pub struct TestCrossAccountId(u64, sp_core::H160);
+impl CrossAccountId<u64> for TestCrossAccountId {
+	fn as_sub(&self) -> &u64 {
+		&self.0
+	}
+	fn as_eth(&self) -> &sp_core::H160 {
+		&self.1
+	}
+	fn from_sub(sub: u64) -> Self {
+		let mut eth = [0; 20];
+		eth[12..20].copy_from_slice(&sub.to_be_bytes());
+		Self(sub, sp_core::H160(eth))
+	}
+	fn from_eth(eth: sp_core::H160) -> Self {
+		let mut sub_raw = [0; 8];
+		sub_raw.copy_from_slice(&eth.0[0..8]);
+		let sub = u64::from_be_bytes(sub_raw);
+		Self(sub, eth)
+	}
+	fn conv_eq(&self, other: &Self) -> bool {
+		self.as_sub() == other.as_sub()
+	}
+}
+
+impl Default for TestCrossAccountId {
+	fn default() -> Self {
+		Self::from_sub(0)
+	}
+}
+
+pub struct TestEtheremTransactionSender;
+impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {
+	fn submit_logs_transaction(_source: H160, _logs: Vec<MaybeMirroredLog>) {}
+}
+
+impl pallet_evm_coder_substrate::Config for Test {
+	type EthereumTransactionSender = TestEtheremTransactionSender;
+	type GasWeightMapping = ();
+}
+
+impl pallet_common::Config for Test {
+	type Event = ();
+	type Currency = Balances;
+	type CollectionCreationPrice = CollectionCreationPrice;
+	type TreasuryAccountId = TreasuryAccountId;
+
+	type CollectionDispatch = CollectionDispatchT<Self>;
+	type EvmTokenAddressMapping = EvmTokenAddressMapping;
+	type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;
+}
+
+impl pallet_evm::account::Config for Test {
+	type CrossAccountId = TestCrossAccountId;
+	type EvmAddressMapping = TestEvmAddressMapping;
+	type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
+}
+
+impl pallet_structure::Config for Test {
+	type WeightInfo = ();
+	type Event = ();
+	type Call = Call;
+}
+impl pallet_fungible::Config for Test {
+	type WeightInfo = ();
+}
+impl pallet_refungible::Config for Test {
+	type WeightInfo = ();
+}
+impl pallet_nonfungible::Config for Test {
+	type WeightInfo = ();
+}
+
+impl pallet_unique::Config for Test {
+	type Event = ();
+	type WeightInfo = ();
+	type CommonWeightInfo = CommonWeights<Self>;
+}
+
+// Build genesis storage according to the mock runtime.
+pub fn new_test_ext() -> sp_io::TestExternalities {
+	system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap()
+		.into()
+}
addedruntime/tests/src/tests.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/tests/src/tests.rs
@@ -0,0 +1,2889 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Tests to be written here
+use crate::{Test, TestCrossAccountId, CollectionCreationPrice, Origin, Unique, new_test_ext};
+use up_data_structs::{
+	COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
+	CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
+	TokenId, MAX_TOKEN_OWNERSHIP, CreateCollectionData, CollectionField, SchemaVersion,
+	CollectionMode, AccessMode,
+};
+use frame_support::{assert_noop, assert_ok, assert_err};
+use sp_std::convert::TryInto;
+use pallet_evm::account::CrossAccountId;
+use pallet_common::Error as CommonError;
+use pallet_unique::Error as UniqueError;
+
+fn add_balance(user: u64, value: u64) {
+	const DONOR_USER: u64 = 999;
+	assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(
+		Origin::root(),
+		DONOR_USER,
+		value,
+		0
+	));
+	assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(
+		Origin::root(),
+		DONOR_USER,
+		user,
+		value
+	));
+}
+
+fn default_nft_data() -> CreateNftData {
+	CreateNftData {
+		const_data: vec![1, 2, 3].try_into().unwrap(),
+		variable_data: vec![3, 2, 1].try_into().unwrap(),
+	}
+}
+
+fn default_fungible_data() -> CreateFungibleData {
+	CreateFungibleData { value: 5 }
+}
+
+fn default_re_fungible_data() -> CreateReFungibleData {
+	CreateReFungibleData {
+		const_data: vec![1, 2, 3].try_into().unwrap(),
+		variable_data: vec![3, 2, 1].try_into().unwrap(),
+		pieces: 1023,
+	}
+}
+
+fn create_test_collection_for_owner(
+	mode: &CollectionMode,
+	owner: u64,
+	id: CollectionId,
+) -> CollectionId {
+	add_balance(owner, CollectionCreationPrice::get() as u64 + 1);
+
+	let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+	let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+	let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+	let data: CreateCollectionData<u64> = CreateCollectionData {
+		name: col_name1.try_into().unwrap(),
+		description: col_desc1.try_into().unwrap(),
+		token_prefix: token_prefix1.try_into().unwrap(),
+		mode: mode.clone(),
+		..Default::default()
+	};
+
+	let origin1 = Origin::signed(owner);
+	assert_ok!(Unique::create_collection_ex(origin1, data));
+
+	let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+	let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+	let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
+	assert_eq!(
+		<pallet_common::CollectionById<Test>>::get(id)
+			.unwrap()
+			.owner,
+		owner
+	);
+	assert_eq!(
+		<pallet_common::CollectionById<Test>>::get(id).unwrap().name,
+		saved_col_name
+	);
+	assert_eq!(
+		<pallet_common::CollectionById<Test>>::get(id).unwrap().mode,
+		*mode
+	);
+	assert_eq!(
+		<pallet_common::CollectionById<Test>>::get(id)
+			.unwrap()
+			.description,
+		saved_description
+	);
+	assert_eq!(
+		<pallet_common::CollectionById<Test>>::get(id)
+			.unwrap()
+			.token_prefix,
+		saved_prefix
+	);
+	id
+}
+
+fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {
+	create_test_collection_for_owner(&mode, 1, id)
+}
+
+fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {
+	let origin1 = Origin::signed(1);
+	assert_ok!(Unique::create_item(
+		origin1,
+		collection_id,
+		account(1),
+		data.clone()
+	));
+}
+
+fn account(sub: u64) -> TestCrossAccountId {
+	TestCrossAccountId::from_sub(sub)
+}
+
+// Use cases tests region
+// #region
+
+#[test]
+fn set_version_schema() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		assert_ok!(Unique::set_schema_version(
+			origin1,
+			collection_id,
+			SchemaVersion::Unique
+		));
+		assert_eq!(
+			<pallet_common::CollectionById<Test>>::get(collection_id)
+				.unwrap()
+				.schema_version,
+			SchemaVersion::Unique
+		);
+	});
+}
+
+#[test]
+fn check_not_sufficient_founds() {
+	new_test_ext().execute_with(|| {
+		let acc: u64 = 1;
+		<pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();
+
+		let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+		let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+		let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();
+
+		let data: CreateCollectionData<<Test as frame_system::Config>::AccountId> =
+			CreateCollectionData {
+				name: name.try_into().unwrap(),
+				description: description.try_into().unwrap(),
+				token_prefix: token_prefix.try_into().unwrap(),
+				mode: CollectionMode::NFT,
+				..Default::default()
+			};
+
+		let result = Unique::create_collection_ex(Origin::signed(acc), data);
+		assert_err!(result, <CommonError<Test>>::NotSufficientFounds);
+	});
+}
+
+#[test]
+fn create_fungible_collection_fails_with_large_decimal_numbers() {
+	new_test_ext().execute_with(|| {
+		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+		let data: CreateCollectionData<u64> = CreateCollectionData {
+			name: col_name1.try_into().unwrap(),
+			description: col_desc1.try_into().unwrap(),
+			token_prefix: token_prefix1.try_into().unwrap(),
+			mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),
+			..Default::default()
+		};
+
+		let origin1 = Origin::signed(1);
+		assert_noop!(
+			Unique::create_collection_ex(origin1, data),
+			UniqueError::<Test>::CollectionDecimalPointLimitExceeded
+		);
+	});
+}
+
+#[test]
+fn create_nft_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.clone().into());
+
+		let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();
+		assert_eq!(item.const_data, data.const_data.into_inner());
+		assert_eq!(item.variable_data, data.variable_data.into_inner());
+	});
+}
+
+// Use cases tests region
+// #region
+#[test]
+fn create_nft_multiple_items() {
+	new_test_ext().execute_with(|| {
+		create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];
+
+		assert_ok!(Unique::create_multiple_items(
+			origin1,
+			CollectionId(1),
+			account(1),
+			items_data
+				.clone()
+				.into_iter()
+				.map(|d| { d.into() })
+				.collect()
+		));
+		for (index, data) in items_data.into_iter().enumerate() {
+			let item = <pallet_nonfungible::TokenData<Test>>::get((
+				CollectionId(1),
+				TokenId((index + 1) as u32),
+			))
+			.unwrap();
+			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
+		}
+	});
+}
+
+#[test]
+fn create_refungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+		let data = default_re_fungible_data();
+		create_test_item(collection_id, &data.clone().into());
+		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
+		let balance =
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));
+		assert_eq!(item.const_data, data.const_data.into_inner());
+		assert_eq!(item.variable_data, data.variable_data.into_inner());
+		assert_eq!(balance, 1023);
+	});
+}
+
+#[test]
+fn create_multiple_refungible_items() {
+	new_test_ext().execute_with(|| {
+		create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let items_data = vec![
+			default_re_fungible_data(),
+			default_re_fungible_data(),
+			default_re_fungible_data(),
+		];
+
+		assert_ok!(Unique::create_multiple_items(
+			origin1,
+			CollectionId(1),
+			account(1),
+			items_data
+				.clone()
+				.into_iter()
+				.map(|d| { d.into() })
+				.collect()
+		));
+		for (index, data) in items_data.into_iter().enumerate() {
+			let item = <pallet_refungible::TokenData<Test>>::get((
+				CollectionId(1),
+				TokenId((index + 1) as u32),
+			));
+			let balance =
+				<pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));
+			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+			assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
+			assert_eq!(balance, 1023);
+		}
+	});
+}
+
+#[test]
+fn create_fungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+			5
+		);
+	});
+}
+
+//#[test]
+// fn create_multiple_fungible_items() {
+//     new_test_ext().execute_with(|| {
+//         default_limits();
+
+//         create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+//         let origin1 = Origin::signed(1);
+
+//         let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
+
+//         assert_ok!(Unique::create_multiple_items(
+//             origin1.clone(),
+//             1,
+//             1,
+//             items_data.clone().into_iter().map(|d| { d.into() }).collect()
+//         ));
+
+//         for (index, _) in items_data.iter().enumerate() {
+//             assert_eq!(Unique::fungible_item_id(1, (index + 1) as TokenId).value, 5);
+//         }
+//         assert_eq!(Unique::balance_count(1, 1), 3000);
+//         assert_eq!(Unique::address_tokens(1, 1), [1, 2, 3]);
+//     });
+// }
+
+#[test]
+fn transfer_fungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
+			5
+		);
+
+		// change owner scenario
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(0),
+			5
+		));
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),
+			0
+		);
+
+		// split item scenario
+		assert_ok!(Unique::transfer(
+			origin2.clone(),
+			account(3),
+			CollectionId(1),
+			TokenId(0),
+			3
+		));
+
+		// split item and new owner has account scenario
+		assert_ok!(Unique::transfer(
+			origin2,
+			account(3),
+			CollectionId(1),
+			TokenId(0),
+			1
+		));
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),
+			1
+		);
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),
+			4
+		);
+	});
+}
+
+#[test]
+fn transfer_refungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+		// Create RFT 1 in 1023 pieces for account 1
+		let data = default_re_fungible_data();
+		create_test_item(collection_id, &data.clone().into());
+		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));
+		assert_eq!(item.const_data, data.const_data.into_inner());
+		assert_eq!(item.variable_data, data.variable_data.into_inner());
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+			1023
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		// Account 1 transfers all 1023 pieces of RFT 1 to account 2
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1023
+		));
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+			1023
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			false
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			true
+		);
+
+		// Account 2 transfers 500 pieces of RFT 1 to account 3
+		assert_ok!(Unique::transfer(
+			origin2.clone(),
+			account(3),
+			CollectionId(1),
+			TokenId(1),
+			500
+		));
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+			523
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+			500
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
+			true
+		);
+
+		// Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance
+		assert_ok!(Unique::transfer(
+			origin2,
+			account(3),
+			CollectionId(1),
+			TokenId(1),
+			200
+		));
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),
+			323
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+			700
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),
+			true
+		);
+	});
+}
+
+#[test]
+fn transfer_nft_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		let origin1 = Origin::signed(1);
+		// default scenario
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			false
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			true
+		);
+	});
+}
+
+#[test]
+fn transfer_nft_item_wrong_value() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		let origin1 = Origin::signed(1);
+
+		assert_noop!(
+			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)
+				.map_err(|e| e.error),
+			<pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount
+		);
+	});
+}
+
+#[test]
+fn transfer_nft_item_zero_value() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		let origin1 = Origin::signed(1);
+
+		// Transferring 0 amount works on NFT...
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			0
+		));
+		// ... and results in no transfer
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+	});
+}
+
+#[test]
+fn nft_approve_and_transfer_from() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		// neg transfer_from
+		assert_noop!(
+			Unique::transfer_from(
+				origin2.clone(),
+				account(1),
+				account(2),
+				CollectionId(1),
+				TokenId(1),
+				1
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::ApprovedValueTooLow
+		);
+
+		// do approve
+		assert_ok!(Unique::approve(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(2)
+		);
+
+		assert_ok!(Unique::transfer_from(
+			origin2,
+			account(1),
+			account(3),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
+		);
+	});
+}
+
+#[test]
+fn nft_approve_and_transfer_from_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		// Create NFT 1 for account 1
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.clone().into());
+		assert_eq!(
+			&<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
+				.unwrap()
+				.const_data,
+			&data.const_data.into_inner()
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		// Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			CollectionId(1),
+			true
+		));
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			CollectionId(1),
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(3)
+		));
+
+		// Account 1 approves account 2 for NFT 1
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(2)
+		);
+
+		// Account 2 transfers NFT 1 from account 1 to account 3
+		assert_ok!(Unique::transfer_from(
+			origin2,
+			account(1),
+			account(3),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()
+		);
+	});
+}
+
+#[test]
+fn refungible_approve_and_transfer_from() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		// Create RFT 1 in 1023 pieces for account 1
+		let data = default_re_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+			1023
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		// Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			CollectionId(1),
+			true
+		));
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			CollectionId(1),
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(3)
+		));
+
+		// Account 1 approves account 2 for 1023 pieces of RFT 1
+		assert_ok!(Unique::approve(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1023
+		));
+		assert_eq!(
+			<pallet_refungible::Allowance<Test>>::get((
+				CollectionId(1),
+				TokenId(1),
+				account(1),
+				account(2)
+			)),
+			1023
+		);
+
+		// Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3
+		assert_ok!(Unique::transfer_from(
+			origin2,
+			account(1),
+			account(3),
+			CollectionId(1),
+			TokenId(1),
+			100
+		));
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+			923
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),
+			100
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_refungible::Allowance<Test>>::get((
+				CollectionId(1),
+				TokenId(1),
+				account(1),
+				account(2)
+			)),
+			923
+		);
+	});
+}
+
+#[test]
+fn fungible_approve_and_transfer_from() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			CollectionId(1),
+			true
+		));
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			CollectionId(1),
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(3)
+		));
+
+		// do approve
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(2),
+			CollectionId(1),
+			TokenId(0),
+			5
+		));
+		assert_eq!(
+			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+			5
+		);
+		assert_ok!(Unique::approve(
+			origin1,
+			account(3),
+			CollectionId(1),
+			TokenId(0),
+			5
+		));
+		assert_eq!(
+			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+			5
+		);
+		assert_eq!(
+			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),
+			5
+		);
+
+		assert_ok!(Unique::transfer_from(
+			origin2.clone(),
+			account(1),
+			account(3),
+			CollectionId(1),
+			TokenId(0),
+			4
+		));
+
+		assert_eq!(
+			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),
+			1
+		);
+
+		assert_noop!(
+			Unique::transfer_from(
+				origin2,
+				account(1),
+				account(3),
+				CollectionId(1),
+				TokenId(0),
+				4
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::ApprovedValueTooLow
+		);
+	});
+}
+
+#[test]
+fn change_collection_owner() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::change_collection_owner(origin1, collection_id, 2));
+		assert_eq!(
+			<pallet_common::CollectionById<Test>>::get(collection_id)
+				.unwrap()
+				.owner,
+			2
+		);
+	});
+}
+
+#[test]
+fn destroy_collection() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::destroy_collection(origin1, collection_id));
+	});
+}
+
+#[test]
+fn burn_nft_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		// check balance (collection with id = 1, user id = 1)
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+
+		// burn item
+		assert_ok!(Unique::burn_item(
+			origin1.clone(),
+			collection_id,
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			0
+		);
+	});
+}
+
+#[test]
+fn burn_same_nft_item_twice() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		// check balance (collection with id = 1, user id = 1)
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+
+		// burn item
+		assert_ok!(Unique::burn_item(
+			origin1.clone(),
+			collection_id,
+			TokenId(1),
+			1
+		));
+
+		// burn item again
+		assert_noop!(
+			Unique::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),
+			CommonError::<Test>::TokenNotFound
+		);
+
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			0
+		);
+	});
+}
+
+#[test]
+fn burn_fungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		// check balance (collection with id = 1, user id = 1)
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+			5
+		);
+
+		// burn item
+		assert_ok!(Unique::burn_item(
+			origin1.clone(),
+			CollectionId(1),
+			TokenId(0),
+			5
+		));
+		assert_noop!(
+			Unique::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),
+			CommonError::<Test>::TokenValueTooLow
+		);
+
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+			0
+		);
+	});
+}
+
+#[test]
+fn burn_fungible_item_with_token_id() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		// check balance (collection with id = 1, user id = 1)
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),
+			5
+		);
+
+		// Try to burn item using Token ID
+		assert_noop!(
+			Unique::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),
+			<pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId
+		);
+	});
+}
+#[test]
+fn burn_refungible_item() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		let data = default_re_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		// check balance (collection with id = 1, user id = 2)
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+			1023
+		);
+
+		// burn item
+		assert_ok!(Unique::burn_item(
+			origin1.clone(),
+			collection_id,
+			TokenId(1),
+			1023
+		));
+		assert_noop!(
+			Unique::burn_item(origin1, collection_id, TokenId(1), 1023).map_err(|e| e.error),
+			CommonError::<Test>::TokenValueTooLow
+		);
+
+		assert_eq!(
+			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),
+			0
+		);
+	});
+}
+
+#[test]
+fn add_collection_admin() {
+	new_test_ext().execute_with(|| {
+		let collection1_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		// Add collection admins
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection1_id,
+			account(2)
+		));
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection1_id,
+			account(3)
+		));
+
+		// Owner is not an admin by default
+		assert_eq!(
+			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),
+			false
+		);
+		assert!(<pallet_common::IsAdmin<Test>>::get((
+			CollectionId(1),
+			account(2)
+		)));
+		assert!(<pallet_common::IsAdmin<Test>>::get((
+			CollectionId(1),
+			account(3)
+		)));
+	});
+}
+
+#[test]
+fn remove_collection_admin() {
+	new_test_ext().execute_with(|| {
+		let collection1_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		// Add collection admins 2 and 3
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection1_id,
+			account(2)
+		));
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection1_id,
+			account(3)
+		));
+
+		assert!(<pallet_common::IsAdmin<Test>>::get((
+			CollectionId(1),
+			account(2)
+		)));
+		assert!(<pallet_common::IsAdmin<Test>>::get((
+			CollectionId(1),
+			account(3)
+		)));
+
+		// remove admin 3
+		assert_ok!(Unique::remove_collection_admin(
+			origin2,
+			CollectionId(1),
+			account(3)
+		));
+
+		// 2 is still admin, 3 is not an admin anymore
+		assert!(<pallet_common::IsAdmin<Test>>::get((
+			CollectionId(1),
+			account(2)
+		)));
+		assert_eq!(
+			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),
+			false
+		);
+	});
+}
+
+#[test]
+fn balance_of() {
+	new_test_ext().execute_with(|| {
+		let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let fungible_collection_id =
+			create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));
+		let re_fungible_collection_id =
+			create_test_collection(&CollectionMode::ReFungible, CollectionId(3));
+
+		// check balance before
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
+			0
+		);
+
+		let nft_data = default_nft_data();
+		create_test_item(nft_collection_id, &nft_data.into());
+
+		let fungible_data = default_fungible_data();
+		create_test_item(fungible_collection_id, &fungible_data.into());
+
+		let re_fungible_data = default_re_fungible_data();
+		create_test_item(re_fungible_collection_id, &re_fungible_data.into());
+
+		// check balance (collection with id = 1, user id = 1)
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),
+			5
+		);
+		assert_eq!(
+			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),
+			1
+		);
+
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_refungible::Owned<Test>>::get((
+				re_fungible_collection_id,
+				account(1),
+				TokenId(1)
+			)),
+			true
+		);
+	});
+}
+
+#[test]
+fn approve() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		let origin1 = Origin::signed(1);
+
+		// approve
+		assert_ok!(Unique::approve(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(2)
+		);
+	});
+}
+
+#[test]
+fn transfer_from() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		// approve
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(2)
+		);
+
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			CollectionId(1),
+			true
+		));
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			CollectionId(1),
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			CollectionId(1),
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			CollectionId(1),
+			account(3)
+		));
+
+		assert_ok!(Unique::transfer_from(
+			origin2,
+			account(1),
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+
+		// after transfer
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),
+			1
+		);
+	});
+}
+
+// #endregion
+
+// Coverage tests region
+// #region
+
+#[test]
+fn owner_can_add_address_to_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert!(<pallet_common::Allowlist<Test>>::get((
+			collection_id,
+			account(2)
+		)));
+	});
+}
+
+#[test]
+fn admin_can_add_address_to_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin2,
+			collection_id,
+			account(3)
+		));
+		assert!(<pallet_common::Allowlist<Test>>::get((
+			collection_id,
+			account(3)
+		)));
+	});
+}
+
+#[test]
+fn nonprivileged_user_cannot_add_address_to_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin2 = Origin::signed(2);
+		assert_noop!(
+			Unique::add_to_allow_list(origin2, collection_id, account(3)),
+			CommonError::<Test>::NoPermission
+		);
+	});
+}
+
+#[test]
+fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+
+		assert_noop!(
+			Unique::add_to_allow_list(origin1, CollectionId(1), account(2)),
+			CommonError::<Test>::CollectionNotFound
+		);
+	});
+}
+
+#[test]
+fn nobody_can_add_address_to_allow_list_of_deleted_collection() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::destroy_collection(origin1.clone(), collection_id));
+		assert_noop!(
+			Unique::add_to_allow_list(origin1, collection_id, account(2)),
+			CommonError::<Test>::CollectionNotFound
+		);
+	});
+}
+
+// If address is already added to allow list, nothing happens
+#[test]
+fn address_is_already_added_to_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert!(<pallet_common::Allowlist<Test>>::get((
+			collection_id,
+			account(2)
+		)));
+	});
+}
+
+#[test]
+fn owner_can_remove_address_from_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+		assert_ok!(Unique::remove_from_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert_eq!(
+			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+			false
+		);
+	});
+}
+
+#[test]
+fn admin_can_remove_address_from_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		// Owner adds admin
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		// Owner adds address 3 to allow list
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(3)
+		));
+
+		// Admin removes address 3 from allow list
+		assert_ok!(Unique::remove_from_allow_list(
+			origin2,
+			collection_id,
+			account(3)
+		));
+		assert_eq!(
+			<pallet_common::Allowlist<Test>>::get((collection_id, account(3))),
+			false
+		);
+	});
+}
+
+#[test]
+fn nonprivileged_user_cannot_remove_address_from_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert_noop!(
+			Unique::remove_from_allow_list(origin2, collection_id, account(2)),
+			CommonError::<Test>::NoPermission
+		);
+		assert!(<pallet_common::Allowlist<Test>>::get((
+			collection_id,
+			account(2)
+		)));
+	});
+}
+
+#[test]
+fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+
+		assert_noop!(
+			Unique::remove_from_allow_list(origin1, CollectionId(1), account(2)),
+			CommonError::<Test>::CollectionNotFound
+		);
+	});
+}
+
+#[test]
+fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		// Add account 2 to allow list
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		// Account 2 is in collection allow-list
+		assert!(<pallet_common::Allowlist<Test>>::get((
+			collection_id,
+			account(2)
+		)));
+
+		// Destroy collection
+		assert_ok!(Unique::destroy_collection(origin1, collection_id));
+
+		// Attempt to remove account 2 from collection allow-list => error
+		assert_noop!(
+			Unique::remove_from_allow_list(origin2, collection_id, account(2)),
+			CommonError::<Test>::CollectionNotFound
+		);
+
+		// Account 2 is not found in collection allow-list anyway
+		assert_eq!(
+			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+			false
+		);
+	});
+}
+
+// If address is already removed from allow list, nothing happens
+#[test]
+fn address_is_already_removed_from_allow_list() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+		assert_ok!(Unique::remove_from_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+		assert_eq!(
+			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+			false
+		);
+		assert_ok!(Unique::remove_from_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+		assert_eq!(
+			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),
+			false
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_1() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		assert_noop!(
+			Unique::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)
+				.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+#[test]
+fn allow_list_test_2() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		// do approve
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(1),
+			collection_id,
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(1)
+		);
+
+		assert_ok!(Unique::remove_from_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+
+		assert_noop!(
+			Unique::transfer_from(
+				origin1,
+				account(1),
+				account(3),
+				CollectionId(1),
+				TokenId(1),
+				1
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_3() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+
+		assert_noop!(
+			Unique::transfer(origin1, account(3), collection_id, TokenId(1), 1)
+				.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+#[test]
+fn allow_list_test_4() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		// do approve
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(1),
+			collection_id,
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(1)
+		);
+
+		assert_ok!(Unique::remove_from_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		assert_noop!(
+			Unique::transfer_from(
+				origin1,
+				account(1),
+				account(3),
+				collection_id,
+				TokenId(1),
+				1
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)
+#[test]
+fn allow_list_test_5() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_noop!(
+			Unique::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1).map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).
+#[test]
+fn allow_list_test_6() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+
+		// do approve
+		assert_noop!(
+			Unique::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)
+				.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and
+//          tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)
+#[test]
+fn allow_list_test_7() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+	});
+}
+
+#[test]
+fn allow_list_test_8() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		// Create NFT for account 1
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		let origin1 = Origin::signed(1);
+
+		// Toggle Allow List mode and add accounts 1 and 2
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(1)
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+
+		// Sself-approve account 1 for NFT 1
+		assert_ok!(Unique::approve(
+			origin1.clone(),
+			account(1),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),
+			account(1)
+		);
+
+		// Transfer from 1 to 2
+		assert_ok!(Unique::transfer_from(
+			origin1,
+			account(1),
+			account(2),
+			CollectionId(1),
+			TokenId(1),
+			1
+		));
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.
+#[test]
+fn allow_list_test_9() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.
+#[test]
+fn allow_list_test_10() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			false
+		));
+
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection_id,
+			account(2)
+		));
+
+		assert_ok!(Unique::create_item(
+			origin2,
+			collection_id,
+			account(2),
+			default_nft_data().into()
+		));
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.
+#[test]
+fn allow_list_test_11() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			false
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+
+		assert_noop!(
+			Unique::create_item(
+				origin2,
+				CollectionId(1),
+				account(2),
+				default_nft_data().into()
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::PublicMintingNotAllowed
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.
+#[test]
+fn allow_list_test_12() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));
+
+		assert_noop!(
+			Unique::create_item(
+				origin2,
+				CollectionId(1),
+				account(2),
+				default_nft_data().into()
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::PublicMintingNotAllowed
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.
+#[test]
+fn allow_list_test_13() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.
+#[test]
+fn allow_list_test_14() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			true
+		));
+
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection_id,
+			account(2)
+		));
+
+		assert_ok!(Unique::create_item(
+			origin2,
+			collection_id,
+			account(2),
+			default_nft_data().into()
+		));
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.
+#[test]
+fn allow_list_test_15() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));
+
+		assert_noop!(
+			Unique::create_item(
+				origin2,
+				collection_id,
+				account(2),
+				default_nft_data().into()
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::AddressNotInAllowlist
+		);
+	});
+}
+
+// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.
+#[test]
+fn allow_list_test_16() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1,
+			collection_id,
+			account(2)
+		));
+
+		assert_ok!(Unique::create_item(
+			origin2,
+			collection_id,
+			account(2),
+			default_nft_data().into()
+		));
+	});
+}
+
+// Total number of collections. Positive test
+#[test]
+fn total_number_collections_bound() {
+	new_test_ext().execute_with(|| {
+		create_test_collection(&CollectionMode::NFT, CollectionId(1));
+	});
+}
+
+#[test]
+fn create_max_collections() {
+	new_test_ext().execute_with(|| {
+		for i in 1..COLLECTION_NUMBER_LIMIT {
+			create_test_collection(&CollectionMode::NFT, CollectionId(i));
+		}
+	});
+}
+
+// Total number of collections. Negative test
+#[test]
+fn total_number_collections_bound_neg() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+
+		for i in 1..=COLLECTION_NUMBER_LIMIT {
+			create_test_collection(&CollectionMode::NFT, CollectionId(i));
+		}
+
+		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+		let data: CreateCollectionData<u64> = CreateCollectionData {
+			name: col_name1.try_into().unwrap(),
+			description: col_desc1.try_into().unwrap(),
+			token_prefix: token_prefix1.try_into().unwrap(),
+			mode: CollectionMode::NFT,
+			..Default::default()
+		};
+
+		// 11-th collection in chain. Expects error
+		assert_noop!(
+			Unique::create_collection_ex(origin1, data),
+			CommonError::<Test>::TotalCollectionsLimitExceeded
+		);
+	});
+}
+
+// Owned tokens by a single address. Positive test
+#[test]
+fn owned_tokens_bound() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.clone().into());
+		create_test_item(collection_id, &data.into());
+	});
+}
+
+// Owned tokens by a single address. Negotive test
+#[test]
+fn owned_tokens_bound_neg() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		for _ in 1..=MAX_TOKEN_OWNERSHIP {
+			let data = default_nft_data();
+			create_test_item(collection_id, &data.clone().into());
+		}
+
+		let data = default_nft_data();
+		assert_noop!(
+			Unique::create_item(origin1, CollectionId(1), account(1), data.into())
+				.map_err(|e| e.error),
+			CommonError::<Test>::AccountTokenLimitExceeded
+		);
+	});
+}
+
+// Number of collection admins. Positive test
+#[test]
+fn collection_admins_bound() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		assert_ok!(Unique::add_collection_admin(
+			origin1.clone(),
+			collection_id,
+			account(2)
+		));
+		assert_ok!(Unique::add_collection_admin(
+			origin1,
+			collection_id,
+			account(3)
+		));
+	});
+}
+
+// Number of collection admins. Negotive test
+#[test]
+fn collection_admins_bound_neg() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		for i in 0..COLLECTION_ADMINS_LIMIT {
+			assert_ok!(Unique::add_collection_admin(
+				origin1.clone(),
+				collection_id,
+				account((2 + i).into())
+			));
+		}
+		assert_noop!(
+			Unique::add_collection_admin(
+				origin1,
+				collection_id,
+				account((3 + COLLECTION_ADMINS_LIMIT).into())
+			),
+			CommonError::<Test>::CollectionAdminCountExceeded
+		);
+	});
+}
+// #endregion
+
+#[test]
+fn set_const_on_chain_schema() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::set_const_on_chain_schema(
+			origin1,
+			collection_id,
+			b"test const on chain schema".to_vec().try_into().unwrap()
+		));
+
+		assert_eq!(
+			<pallet_common::CollectionData<Test>>::get((
+				collection_id,
+				CollectionField::ConstOnChainSchema
+			)),
+			b"test const on chain schema".to_vec()
+		);
+		assert_eq!(
+			<pallet_common::CollectionData<Test>>::get((
+				collection_id,
+				CollectionField::VariableOnChainSchema
+			)),
+			b"".to_vec()
+		);
+	});
+}
+
+#[test]
+fn set_variable_on_chain_schema() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		assert_ok!(Unique::set_variable_on_chain_schema(
+			origin1,
+			collection_id,
+			b"test variable on chain schema"
+				.to_vec()
+				.try_into()
+				.unwrap()
+		));
+
+		assert_eq!(
+			<pallet_common::CollectionData<Test>>::get((
+				collection_id,
+				CollectionField::ConstOnChainSchema
+			)),
+			b"".to_vec()
+		);
+		assert_eq!(
+			<pallet_common::CollectionData<Test>>::get((
+				collection_id,
+				CollectionField::VariableOnChainSchema
+			)),
+			b"test variable on chain schema".to_vec()
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(CollectionId(1), &data.into());
+
+		let variable_data = b"test data".to_vec();
+		assert_ok!(Unique::set_variable_meta_data(
+			origin1,
+			collection_id,
+			TokenId(1),
+			variable_data.clone().try_into().unwrap()
+		));
+
+		assert_eq!(
+			<pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
+				.unwrap()
+				.variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_re_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		let variable_data = b"test data".to_vec();
+		assert_ok!(Unique::set_variable_meta_data(
+			origin1,
+			collection_id,
+			TokenId(1),
+			variable_data.clone().try_into().unwrap()
+		));
+
+		assert_eq!(
+			<pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1))).variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_fungible_token_fails() {
+	new_test_ext().execute_with(|| {
+		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_fungible_data();
+		create_test_item(collection_id, &data.into());
+
+		let variable_data = b"test data".to_vec();
+		assert_noop!(
+			Unique::set_variable_meta_data(
+				origin1,
+				collection_id,
+				TokenId(0),
+				variable_data.try_into().unwrap()
+			)
+			.map_err(|e| e.error),
+			<pallet_fungible::Error<Test>>::FungibleItemsDontHaveData
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {
+	new_test_ext().execute_with(|| {
+		//default_limits();
+
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_meta_update_permission_flag(
+			origin1.clone(),
+			collection_id,
+			MetaUpdatePermission::ItemOwner,
+		));
+
+		let variable_data = b"ten chars.".to_vec();
+		assert_ok!(Unique::set_variable_meta_data(
+			origin1,
+			collection_id,
+			TokenId(1),
+			variable_data.clone().try_into().unwrap()
+		));
+
+		assert_eq!(
+			<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))
+				.unwrap()
+				.variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn collection_transfer_flag_works() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		assert_ok!(Unique::set_transfers_enabled_flag(
+			origin1,
+			collection_id,
+			true
+		));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		let origin1 = Origin::signed(1);
+
+		// default scenario
+		assert_ok!(Unique::transfer(
+			origin1,
+			account(2),
+			collection_id,
+			TokenId(1),
+			1
+		));
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			false
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			0
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			1
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag() {
+	new_test_ext().execute_with(|| {
+		// default_limits();
+
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_mint_permission(
+			origin2.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		assert_ok!(Unique::add_collection_admin(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_meta_update_permission_flag(
+			origin2.clone(),
+			collection_id,
+			MetaUpdatePermission::Admin,
+		));
+
+		let variable_data = b"test.".to_vec();
+		assert_ok!(Unique::set_variable_meta_data(
+			origin1,
+			collection_id,
+			TokenId(1),
+			variable_data.clone().try_into().unwrap()
+		));
+
+		assert_eq!(
+			<pallet_nonfungible::TokenData<Test>>::get((collection_id, 1))
+				.unwrap()
+				.variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
+	new_test_ext().execute_with(|| {
+		// default_limits();
+
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_mint_permission(
+			origin2.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_meta_update_permission_flag(
+			origin2.clone(),
+			collection_id,
+			MetaUpdatePermission::Admin,
+		));
+
+		let variable_data = b"test.".to_vec();
+		assert_noop!(
+			Unique::set_variable_meta_data(
+				origin1,
+				collection_id,
+				TokenId(1),
+				variable_data.try_into().unwrap()
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::NoPermission
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_flag_after_freeze() {
+	new_test_ext().execute_with(|| {
+		// default_limits();
+
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 2, CollectionId(1));
+
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(Unique::set_meta_update_permission_flag(
+			origin2.clone(),
+			collection_id,
+			MetaUpdatePermission::None,
+		));
+		assert_noop!(
+			Unique::set_meta_update_permission_flag(
+				origin2.clone(),
+				collection_id,
+				MetaUpdatePermission::Admin
+			),
+			CommonError::<Test>::MetadataFlagFrozen
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_none_flag_neg() {
+	new_test_ext().execute_with(|| {
+		// default_limits();
+
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+
+		assert_ok!(Unique::set_meta_update_permission_flag(
+			origin1.clone(),
+			collection_id,
+			MetaUpdatePermission::None,
+		));
+
+		let variable_data = b"test.".to_vec();
+		assert_noop!(
+			Unique::set_variable_meta_data(
+				origin1.clone(),
+				collection_id,
+				TokenId(1),
+				variable_data.try_into().unwrap()
+			)
+			.map_err(|e| e.error),
+			CommonError::<Test>::NoPermission
+		);
+	});
+}
+
+#[test]
+fn collection_transfer_flag_works_neg() {
+	new_test_ext().execute_with(|| {
+		let origin1 = Origin::signed(1);
+
+		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));
+		assert_ok!(Unique::set_transfers_enabled_flag(
+			origin1,
+			collection_id,
+			false
+		));
+
+		let data = default_nft_data();
+		create_test_item(collection_id, &data.into());
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+
+		let origin1 = Origin::signed(1);
+
+		// default scenario
+		assert_noop!(
+			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)
+				.map_err(|e| e.error),
+			CommonError::<Test>::TransferNotAllowed
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),
+			1
+		);
+		assert_eq!(
+			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),
+			0
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),
+			true
+		);
+		assert_eq!(
+			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),
+			false
+		);
+	});
+}
+
+#[test]
+fn collection_sponsoring() {
+	new_test_ext().execute_with(|| {
+		// default_limits();
+		let user1 = 1_u64;
+		let user2 = 777_u64;
+		let origin1 = Origin::signed(user1);
+		let origin2 = Origin::signed(user2);
+		let account2 = account(user2);
+
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
+		assert_ok!(Unique::set_collection_sponsor(
+			origin1.clone(),
+			collection_id,
+			user1
+		));
+		assert_ok!(Unique::confirm_sponsorship(origin1.clone(), collection_id));
+
+		// Expect error while have no permissions
+		assert!(Unique::create_item(
+			origin2.clone(),
+			collection_id,
+			account2.clone(),
+			default_nft_data().into()
+		)
+		.is_err());
+
+		assert_ok!(Unique::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(Unique::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account2.clone()
+		));
+		assert_ok!(Unique::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			true
+		));
+
+		assert_ok!(Unique::create_item(
+			origin2,
+			collection_id,
+			account2,
+			default_nft_data().into()
+		));
+	});
+}