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

difftreelog

feat nft benchmarking

Yaroslav Bolyukin2021-08-30parent: #ececb8d.patch.diff
in: master

8 files changed

modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -10,5 +10,9 @@
 bench-evm-migration:
 	make _bench PALLET=evm-migration
 
+.PHONY: bench-nft
+bench-nft:
+	make _bench PALLET=nft
+
 .PHONY: bench
-bench: bench-evm-migration
+bench: bench-evm-migration bench-nft
modifiedpallets/nft/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -1,419 +1,309 @@
 #![cfg(feature = "runtime-benchmarks")]
 
 use super::*;
-use crate::Module as Nft;
-
-use sp_std::prelude::*;
+use crate::Pallet;
 use frame_system::RawOrigin;
-use frame_benchmarking::{benchmarks, account, whitelisted_caller}; // , TrackedStorageKey,
+use frame_benchmarking::{benchmarks, account};
+use nft_data_structs::*;
+use core::convert::TryInto;
+use sp_runtime::DispatchError;
 
 const SEED: u32 = 1;
-/*
+
+fn create_data(size: usize) -> Vec<u8> {
+	(0..size).map(|v| (v & 0xff) as u8).collect()
+}
+fn create_u16_data(size: usize) -> Vec<u16> {
+	(0..size).map(|v| (v & 0xffff) as u16).collect()
+}
+
 fn default_nft_data() -> CreateItemData {
-	CreateItemData::NFT(CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+	CreateItemData::NFT(CreateNftData {
+		const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+	})
+}
+
+fn default_fungible_data() -> CreateItemData {
+	CreateItemData::Fungible(CreateFungibleData { value: 1000 })
 }
 
-fn default_fungible_data () -> CreateItemData {
-	CreateItemData::Fungible(CreateFungibleData { })
+fn default_re_fungible_data() -> CreateItemData {
+	CreateItemData::ReFungible(CreateReFungibleData {
+		const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+		pieces: 1000,
+	})
 }
 
-fn default_re_fungible_data () -> CreateItemData {
-	CreateItemData::ReFungible(CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+fn create_collection_helper<T: Config>(
+	owner: T::AccountId,
+	mode: CollectionMode,
+) -> Result<CollectionId, DispatchError> {
+	T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
+	let col_name = create_u16_data(MAX_COLLECTION_NAME_LENGTH)
+		.try_into()
+		.unwrap();
+	let col_desc = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH)
+		.try_into()
+		.unwrap();
+	let token_prefix = create_data(MAX_TOKEN_PREFIX_LENGTH).try_into().unwrap();
+	<Pallet<T>>::create_collection(
+		RawOrigin::Signed(owner).into(),
+		col_name,
+		col_desc,
+		token_prefix,
+		mode,
+	)?;
+	Ok(CreatedCollectionCount::get())
+}
+fn create_nft_collection<T: Config>(owner: T::AccountId) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::NFT)
+}
+fn create_fungible_collection<T: Config>(
+	owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::Fungible(0))
+}
+fn create_refungible_collection<T: Config>(
+	owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+	create_collection_helper::<T>(owner, CollectionMode::ReFungible)
 }
-*/
 
 benchmarks! {
 
 	create_collection {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+		let col_name: Vec<u16> = create_u16_data(MAX_COLLECTION_NAME_LENGTH);
+		let col_desc: Vec<u16> = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH);
+		let token_prefix: Vec<u8> = create_data(MAX_TOKEN_PREFIX_LENGTH);
 		let mode: CollectionMode = CollectionMode::NFT;
 		let caller: T::AccountId = account("caller", 0, SEED);
-	}: _(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)
-/*
+		T::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+	}: _(RawOrigin::Signed(caller.clone()), col_name.clone(), col_desc.clone(), token_prefix.clone(), mode)
 	verify {
-		assert_eq!(Nft::<T>::collection_id(2).owner, caller);
+		assert_eq!(<Pallet<T>>::collection_id(2).unwrap().owner, caller);
 	}
+
 	destroy_collection {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: _(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	add_to_white_list {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+		let caller: T::AccountId = account("caller", 0, SEED);
 		let whitelist_account: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: add_to_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
 
 	remove_from_white_list {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+		let caller: T::AccountId = account("caller", 0, SEED);
 		let whitelist_account: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		Nft::<T>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), 2, whitelist_account.clone())?;
-	}: remove_from_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(whitelist_account.clone()))?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
 
 	set_public_access_mode {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: set_public_access_mode(RawOrigin::Signed(caller.clone()), 2, AccessMode::WhiteList)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, AccessMode::WhiteList)
 
 	set_mint_permission {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: set_mint_permission(RawOrigin::Signed(caller.clone()), 2, true)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, true)
 
 	change_collection_owner {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_owner: T::AccountId = account("admin", 0, SEED);
-	}: change_collection_owner(RawOrigin::Signed(caller.clone()), 2, new_owner)
+	}: _(RawOrigin::Signed(caller.clone()), collection, new_owner)
 
 	add_collection_admin {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_admin: T::AccountId = account("admin", 0, SEED);
-	}: add_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
 
 	remove_collection_admin {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let new_admin: T::AccountId = account("admin", 0, SEED);
-		Nft::<T>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, new_admin.clone())?;
-	}: remove_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+		<Pallet<T>>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(new_admin.clone()))?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
 
 	set_collection_sponsor {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: set_collection_sponsor(RawOrigin::Signed(caller.clone()), 2, caller.clone())
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection, caller.clone())
 
 	confirm_sponsorship {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
-	}: confirm_sponsorship(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	remove_collection_sponsor {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
-		Nft::<T>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
-	}: remove_collection_sponsor(RawOrigin::Signed(caller.clone()), 2)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		<Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+		<Pallet<T>>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
+	}: _(RawOrigin::Signed(caller.clone()), collection)
 
 	// nft item
 	create_item_nft {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let data = default_nft_data();
-
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
-
-	#[extra]
-	create_item_nft_large {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		let mut nft_data = CreateNftData {
-			const_data: vec![],
-			variable_data: vec![]
-		};
-		for i in 0..1998 {
-			nft_data.const_data.push(10);
-			nft_data.variable_data.push(10);
-		}
-		let data = CreateItemData::NFT(nft_data);
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let b in 0..(CUSTOM_DATA_LIMIT * 2);
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = CreateItemData::NFT(CreateNftData {
+			const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
 	// fungible item
 	create_item_fungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::Fungible(3);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let data = default_fungible_data();
-
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
+		let data = CreateItemData::Fungible(CreateFungibleData {
+			value: 1000,
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
 	// refungible item
 	create_item_refungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let data = default_re_fungible_data();
+		let b in 0..(CUSTOM_DATA_LIMIT * 2);
 
-	}: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
+		let data = CreateItemData::ReFungible(CreateReFungibleData {
+			const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+			pieces: 1000,
+		});
+	}: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
 
-	burn_item {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+	burn_item_nft {
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: burn_item(RawOrigin::Signed(caller.clone()), 2, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: burn_item(RawOrigin::Signed(caller.clone()), collection, 1, 1)
 
 	transfer_nft {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
 	transfer_fungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::Fungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
-
 	transfer_refungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
-	}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
-
-	approve {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
+	approve_nft {
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
-	}: approve(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1)
+		let data = default_nft_data();
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+	}: approve(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
 
 	// Nft
 	transfer_from_nft {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
 	// Fungible
 	transfer_from_fungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::Fungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_fungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
-
 	// ReFungible
 	transfer_from_refungible {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_refungible_collection::<T>(caller.clone())?;
 		let recipient: T::AccountId = account("recipient", 0, SEED);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 		let data = default_re_fungible_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-		Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
-	}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
-
-	enable_contract_sponsoring {
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		<Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+	}: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
 
-	}: enable_contract_sponsoring(RawOrigin::Signed(caller.clone()), caller.clone(), true)
-
 	set_offchain_schema {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let b in 0..OFFCHAIN_SCHEMA_LIMIT;
 
-	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_const_on_chain_schema {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let b in 0..CONST_ON_CHAIN_SCHEMA_LIMIT;
+
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_variable_on_chain_schema {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::ReFungible(3);
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-	}: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+		let b in 0..VARIABLE_ON_CHAIN_SCHEMA_LIMIT;
 
-	set_variable_meta_data {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let data = default_nft_data();
-		Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = create_data(b as usize);
+	}: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, data)
 
-	}: set_variable_meta_data(RawOrigin::Signed(caller.clone()), 2, 1, [1, 2, 3].to_vec())
+	set_variable_meta_data_nft {
+		let b in 0..CUSTOM_DATA_LIMIT;
 
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		let data = default_nft_data();
+		<Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+		let data = create_data(b as usize);
+	}: set_variable_meta_data(RawOrigin::Signed(caller.clone()), collection, 1, data)
+
 	set_schema_version {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 	}: set_schema_version(RawOrigin::Signed(caller.clone()), 2, SchemaVersion::Unique)
-
-	set_chain_limits {
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		let limits = ChainLimits {
-			collection_numbers_limit: 0,
-			account_token_ownership_limit: 0,
-			collections_admins_limit: 0,
-			custom_data_limit: 0,
-			nft_sponsor_transfer_timeout: 0,
-			fungible_sponsor_transfer_timeout: 0,
-			refungible_sponsor_transfer_timeout: 0
-		};
-	}: set_chain_limits(RawOrigin::Signed(caller.clone()), limits)
 
-	set_contract_sponsoring_rate_limit {
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-		let block_number: T::BlockNumber = 0.into();
-	}: set_contract_sponsoring_rate_limit(RawOrigin::Signed(caller.clone()), caller.clone(), block_number)
-
 	set_collection_limits{
-		let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
-		let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
-		let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
-		let mode: CollectionMode = CollectionMode::NFT;
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let collection = create_nft_collection::<T>(caller.clone())?;
 
 		let cl = CollectionLimits {
 			account_token_ownership_limit: 0,
 			sponsored_data_size: 0,
-			token_limit: 0,
-			sponsor_transfer_timeout: 0
+			token_limit: 1,
+			sponsor_transfer_timeout: 0,
+			owner_can_destroy: true,
+			owner_can_transfer: true,
+			sponsored_data_rate_limit: None,
 		};
-
 	}: set_collection_limits(RawOrigin::Signed(caller.clone()), 2, cl)
-
-	add_to_contract_white_list{
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-	}: add_to_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
-
-	remove_from_contract_white_list{
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-		Nft::<T>::add_to_contract_white_list(RawOrigin::Signed(caller.clone()).into(), caller.clone(), caller.clone())?;
-	}: remove_from_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
-
-	toggle_contract_white_list{
-		let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-	}: toggle_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), true)
-*/
 }
deletedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
-
-impl crate::WeightInfo for () {
-	fn create_collection() -> Weight {
-		70_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn destroy_collection() -> Weight {
-		90_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn add_to_white_list() -> Weight {
-		30_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_from_white_list() -> Weight {
-		35_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_public_access_mode() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_mint_permission() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn change_collection_owner() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn add_collection_admin() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_admin() -> Weight {
-		50_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_collection_sponsor() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn confirm_sponsorship() -> Weight {
-		22_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_sponsor() -> Weight {
-		24_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn create_item(s: usize) -> Weight {
-		130_000_000_u64
-			.saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temporary multiplier, fee for storage
-			.saturating_add(DbWeight::get().reads(10_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn burn_item() -> Weight {
-		170_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn transfer() -> Weight {
-		125_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn approve() -> Weight {
-		45_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn transfer_from() -> Weight {
-		150_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn set_offchain_schema() -> Weight {
-		33_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_const_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_meta_data() -> Weight {
-		17_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn enable_contract_sponsoring() -> Weight {
-		13_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_schema_version() -> Weight {
-		8_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_contract_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn toggle_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn add_to_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn remove_from_contract_white_list() -> Weight {
-		3_200_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_collection_limits() -> Weight {
-		8_900_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-}
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9	clippy::too_many_arguments,10	clippy::unnecessary_mut_passed,11	clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19	construct_runtime, decl_event, decl_module, decl_storage, decl_error,20	dispatch::DispatchResult,21	ensure, fail, parameter_types,22	traits::{23		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24		Randomness, IsSubType, WithdrawReasons,25	},26	weights::{27		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29		WeightToFeePolynomial, DispatchClass,30	},31	StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41	CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43	OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44	CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45	FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;6667pub trait WeightInfo {68	fn create_collection() -> Weight;69	fn destroy_collection() -> Weight;70	fn add_to_white_list() -> Weight;71	fn remove_from_white_list() -> Weight;72	fn set_public_access_mode() -> Weight;73	fn set_mint_permission() -> Weight;74	fn change_collection_owner() -> Weight;75	fn add_collection_admin() -> Weight;76	fn remove_collection_admin() -> Weight;77	fn set_collection_sponsor() -> Weight;78	fn confirm_sponsorship() -> Weight;79	fn remove_collection_sponsor() -> Weight;80	fn create_item(s: usize) -> Weight;81	fn burn_item() -> Weight;82	fn transfer() -> Weight;83	fn approve() -> Weight;84	fn transfer_from() -> Weight;85	fn set_offchain_schema() -> Weight;86	fn set_const_on_chain_schema() -> Weight;87	fn set_variable_on_chain_schema() -> Weight;88	fn set_variable_meta_data() -> Weight;89	fn enable_contract_sponsoring() -> Weight;90	fn set_schema_version() -> Weight;91	fn set_contract_sponsoring_rate_limit() -> Weight;92	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;93	fn toggle_contract_white_list() -> Weight;94	fn add_to_contract_white_list() -> Weight;95	fn remove_from_contract_white_list() -> Weight;96	fn set_collection_limits() -> Weight;97}9899decl_error! {100	/// Error for non-fungible-token module.101	pub enum Error for Module<T: Config> {102		/// Total collections bound exceeded.103		TotalCollectionsLimitExceeded,104		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.105		CollectionDecimalPointLimitExceeded,106		/// Collection name can not be longer than 63 char.107		CollectionNameLimitExceeded,108		/// Collection description can not be longer than 255 char.109		CollectionDescriptionLimitExceeded,110		/// Token prefix can not be longer than 15 char.111		CollectionTokenPrefixLimitExceeded,112		/// This collection does not exist.113		CollectionNotFound,114		/// Item not exists.115		TokenNotFound,116		/// Admin not found117		AdminNotFound,118		/// Arithmetic calculation overflow.119		NumOverflow,120		/// Account already has admin role.121		AlreadyAdmin,122		/// You do not own this collection.123		NoPermission,124		/// This address is not set as sponsor, use setCollectionSponsor first.125		ConfirmUnsetSponsorFail,126		/// Collection is not in mint mode.127		PublicMintingNotAllowed,128		/// Sender parameter and item owner must be equal.129		MustBeTokenOwner,130		/// Item balance not enough.131		TokenValueTooLow,132		/// Size of item is too large.133		NftSizeLimitExceeded,134		/// No approve found135		ApproveNotFound,136		/// Requested value more than approved.137		TokenValueNotEnough,138		/// Only approved addresses can call this method.139		ApproveRequired,140		/// Address is not in white list.141		AddresNotInWhiteList,142		/// Number of collection admins bound exceeded.143		CollectionAdminsLimitExceeded,144		/// Owned tokens by a single address bound exceeded.145		AddressOwnershipLimitExceeded,146		/// Length of items properties must be greater than 0.147		EmptyArgument,148		/// const_data exceeded data limit.149		TokenConstDataLimitExceeded,150		/// variable_data exceeded data limit.151		TokenVariableDataLimitExceeded,152		/// Not NFT item data used to mint in NFT collection.153		NotNftDataUsedToMintNftCollectionToken,154		/// Not Fungible item data used to mint in Fungible collection.155		NotFungibleDataUsedToMintFungibleCollectionToken,156		/// Not Re Fungible item data used to mint in Re Fungible collection.157		NotReFungibleDataUsedToMintReFungibleCollectionToken,158		/// Unexpected collection type.159		UnexpectedCollectionType,160		/// Can't store metadata in fungible tokens.161		CantStoreMetadataInFungibleTokens,162		/// Collection token limit exceeded163		CollectionTokenLimitExceeded,164		/// Account token limit exceeded per collection165		AccountTokenLimitExceeded,166		/// Collection limit bounds per collection exceeded167		CollectionLimitBoundsExceeded,168		/// Tried to enable permissions which are only permitted to be disabled169		OwnerPermissionsCantBeReverted,170		/// Schema data size limit bound exceeded171		SchemaDataLimitExceeded,172		/// Maximum refungibility exceeded173		WrongRefungiblePieces,174		/// createRefungible should be called with one owner175		BadCreateRefungibleCall,176		/// Gas limit exceeded177		OutOfGas,178		/// Collection settings not allowing items transferring179		TransferNotAllowed,180	}181}182183#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]184pub struct CollectionHandle<T: Config> {185	pub id: CollectionId,186	collection: Collection<T>,187	recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,188}189impl<T: Config> CollectionHandle<T> {190	pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {191		<CollectionById<T>>::get(id).map(|collection| Self {192			id,193			collection,194			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(195				eth::collection_id_to_address(id),196				gas_limit,197			),198		})199	}200	pub fn get(id: CollectionId) -> Option<Self> {201		Self::get_with_gas_limit(id, u64::MAX)202	}203	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {204		self.recorder.log_sub(log)205	}206	fn consume_gas(&self, gas: u64) -> DispatchResult {207		self.recorder.consume_gas_sub(gas)208	}209	pub fn submit_logs(self) -> DispatchResult {210		self.recorder.submit_logs()211	}212	pub fn save(self) -> DispatchResult {213		self.recorder.submit_logs()?;214		<CollectionById<T>>::insert(self.id, self.collection);215		Ok(())216	}217}218impl<T: Config> Deref for CollectionHandle<T> {219	type Target = Collection<T>;220221	fn deref(&self) -> &Self::Target {222		&self.collection223	}224}225226impl<T: Config> DerefMut for CollectionHandle<T> {227	fn deref_mut(&mut self) -> &mut Self::Target {228		&mut self.collection229	}230}231232pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {233	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;234235	/// Weight information for extrinsics in this pallet.236	type WeightInfo: WeightInfo;237238	type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;239	type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;240241	type CrossAccountId: CrossAccountId<Self::AccountId>;242	type Currency: Currency<Self::AccountId>;243	type CollectionCreationPrice: Get<244		<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,245	>;246	type TreasuryAccountId: Get<Self::AccountId>;247}248249// # Used definitions250//251// ## User control levels252//253// chain-controlled - key is uncontrolled by user254//                    i.e autoincrementing index255//                    can use non-cryptographic hash256// real - key is controlled by user257//        but it is hard to generate enough colliding values, i.e owner of signed txs258//        can use non-cryptographic hash259// controlled - key is completly controlled by users260//              i.e maps with mutable keys261//              should use cryptographic hash262//263// ## User control level downgrade reasons264//265// ?1 - chain-controlled -> controlled266//      collections/tokens can be destroyed, resulting in massive holes267// ?2 - chain-controlled -> controlled268//      same as ?1, but can be only added, resulting in easier exploitation269// ?3 - real -> controlled270//      no confirmation required, so addresses can be easily generated271decl_storage! {272	trait Store for Module<T: Config> as Nft {273274		//#region Private members275		/// Id of next collection276		CreatedCollectionCount: u32;277		/// Used for migrations278		ChainVersion: u64;279		/// Id of last collection token280		/// Collection id (controlled?1)281		ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;282		//#endregion283284		//#region Bound counters285		/// Amount of collections destroyed, used for total amount tracking with286		/// CreatedCollectionCount287		DestroyedCollectionCount: u32;288		/// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)289		/// Account id (real)290		pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;291		//#endregion292293		//#region Basic collections294		/// Collection info295		/// Collection id (controlled?1)296		pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;297		/// List of collection admins298		/// Collection id (controlled?2)299		pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;300		/// Whitelisted collection users301		/// Collection id (controlled?2), user id (controlled?3)302		pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;303		//#endregion304305		/// How many of collection items user have306		/// Collection id (controlled?2), account id (real)307		pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;308309		/// Amount of items which spender can transfer out of owners account (via transferFrom)310		/// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))311		/// TODO: Off chain worker should remove from this map when token gets removed312		pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;313314		//#region Item collections315		/// Collection id (controlled?2), token id (controlled?1)316		pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;317		/// Collection id (controlled?2), owner (controlled?2)318		pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;319		/// Collection id (controlled?2), token id (controlled?1)320		pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;321		//#endregion322323		//#region Index list324		/// Collection id (controlled?2), tokens owner (controlled?2)325		pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;326		//#endregion327328		//#region Tokens transfer rate limit baskets329		/// (Collection id (controlled?2), who created (real))330		/// TODO: Off chain worker should remove from this map when collection gets removed331		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;332		/// Collection id (controlled?2), token id (controlled?2)333		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;334		/// Collection id (controlled?2), owning user (real)335		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;336		/// Collection id (controlled?2), token id (controlled?2)337		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;338		//#endregion339340		/// Variable metadata sponsoring341		/// Collection id (controlled?2), token id (controlled?2)342		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;343	}344	add_extra_genesis {345		build(|config: &GenesisConfig<T>| {346			// Modification of storage347			for (_num, _c) in &config.collection_id {348				<Module<T>>::init_collection(_c);349			}350351			for (_num, _c, _i) in &config.nft_item_id {352				<Module<T>>::init_nft_token(*_c, _i);353			}354355			for (collection_id, account_id, fungible_item) in &config.fungible_item_id {356				<Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);357			}358359			for (_num, _c, _i) in &config.refungible_item_id {360				<Module<T>>::init_refungible_token(*_c, _i);361			}362		})363	}364}365366decl_event!(367	pub enum Event<T>368	where369		AccountId = <T as frame_system::Config>::AccountId,370		CrossAccountId = <T as Config>::CrossAccountId,371	{372		/// New collection was created373		///374		/// # Arguments375		///376		/// * collection_id: Globally unique identifier of newly created collection.377		///378		/// * mode: [CollectionMode] converted into u8.379		///380		/// * account_id: Collection owner.381		CollectionCreated(CollectionId, u8, AccountId),382383		/// New item was created.384		///385		/// # Arguments386		///387		/// * collection_id: Id of the collection where item was created.388		///389		/// * item_id: Id of an item. Unique within the collection.390		///391		/// * recipient: Owner of newly created item392		ItemCreated(CollectionId, TokenId, CrossAccountId),393394		/// Collection item was burned.395		///396		/// # Arguments397		///398		/// collection_id.399		///400		/// item_id: Identifier of burned NFT.401		ItemDestroyed(CollectionId, TokenId),402403		/// Item was transferred404		///405		/// * collection_id: Id of collection to which item is belong406		///407		/// * item_id: Id of an item408		///409		/// * sender: Original owner of item410		///411		/// * recipient: New owner of item412		///413		/// * amount: Always 1 for NFT414		Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),415416		/// * collection_id417		///418		/// * item_id419		///420		/// * sender421		///422		/// * spender423		///424		/// * amount425		Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),426	}427);428429decl_module! {430	pub struct Module<T: Config> for enum Call431	where432		origin: T::Origin433	{434		fn deposit_event() = default;435		const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;436		type Error = Error<T>;437438		fn on_initialize(_now: T::BlockNumber) -> Weight {439			0440		}441442		/// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.443		///444		/// # Permissions445		///446		/// * Anyone.447		///448		/// # Arguments449		///450		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.451		///452		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.453		///454		/// * token_prefix: UTF-8 string with token prefix.455		///456		/// * mode: [CollectionMode] collection type and type dependent data.457		// returns collection ID458		#[weight = <T as Config>::WeightInfo::create_collection()]459		#[transactional]460		pub fn create_collection(origin,461								 collection_name: Vec<u16>,462								 collection_description: Vec<u16>,463								 token_prefix: Vec<u8>,464								 mode: CollectionMode) -> DispatchResult {465466			// Anyone can create a collection467			let who = ensure_signed(origin)?;468469			// Take a (non-refundable) deposit of collection creation470			let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();471			imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(472				&T::TreasuryAccountId::get(),473				T::CollectionCreationPrice::get(),474			));475			<T as Config>::Currency::settle(476				&who,477				imbalance,478				WithdrawReasons::TRANSFER,479				ExistenceRequirement::KeepAlive,480			).map_err(|_| Error::<T>::NoPermission)?;481482			let decimal_points = match mode {483				CollectionMode::Fungible(points) => points,484				_ => 0485			};486487			let created_count = CreatedCollectionCount::get();488			let destroyed_count = DestroyedCollectionCount::get();489490			// bound Total number of collections491			ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);492493			// check params494			ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);495			ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);496			ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);497			ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);498499			// Generate next collection ID500			let next_id = created_count501				.checked_add(1)502				.ok_or(Error::<T>::NumOverflow)?;503504			CreatedCollectionCount::put(next_id);505506			let limits = CollectionLimits {507				sponsored_data_size: CUSTOM_DATA_LIMIT,508				..Default::default()509			};510511			// Create new collection512			let new_collection = Collection {513				owner: who.clone(),514				name: collection_name,515				mode: mode.clone(),516				mint_mode: false,517				access: AccessMode::Normal,518				description: collection_description,519				decimal_points,520				token_prefix,521				offchain_schema: Vec::new(),522				schema_version: SchemaVersion::ImageURL,523				sponsorship: SponsorshipState::Disabled,524				variable_on_chain_schema: Vec::new(),525				const_on_chain_schema: Vec::new(),526				limits,527				transfers_enabled: true,528			};529530			// Add new collection to map531			<CollectionById<T>>::insert(next_id, new_collection);532533			// call event534			Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));535536			Ok(())537		}538539		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.540		///541		/// # Permissions542		///543		/// * Collection Owner.544		///545		/// # Arguments546		///547		/// * collection_id: collection to destroy.548		#[weight = <T as Config>::WeightInfo::destroy_collection()]549		#[transactional]550		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {551552			let sender = ensure_signed(origin)?;553			let collection = Self::get_collection(collection_id)?;554			Self::check_owner_permissions(&collection, &sender)?;555			if !collection.limits.owner_can_destroy {556				fail!(Error::<T>::NoPermission);557			}558559			<AddressTokens<T>>::remove_prefix(collection_id, None);560			<Allowances<T>>::remove_prefix(collection_id, None);561			<Balance<T>>::remove_prefix(collection_id, None);562			<ItemListIndex>::remove(collection_id);563			<AdminList<T>>::remove(collection_id);564			<CollectionById<T>>::remove(collection_id);565			<WhiteList<T>>::remove_prefix(collection_id, None);566567			<NftItemList<T>>::remove_prefix(collection_id, None);568			<FungibleItemList<T>>::remove_prefix(collection_id, None);569			<ReFungibleItemList<T>>::remove_prefix(collection_id, None);570571			<NftTransferBasket<T>>::remove_prefix(collection_id, None);572			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);573			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);574575			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);576577			DestroyedCollectionCount::put(DestroyedCollectionCount::get()578				.checked_add(1)579				.ok_or(Error::<T>::NumOverflow)?);580581			Ok(())582		}583584		/// Add an address to white list.585		///586		/// # Permissions587		///588		/// * Collection Owner589		/// * Collection Admin590		///591		/// # Arguments592		///593		/// * collection_id.594		///595		/// * address.596		#[weight = <T as Config>::WeightInfo::add_to_white_list()]597		#[transactional]598		pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{599600			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601			let collection = Self::get_collection(collection_id)?;602603			Self::toggle_white_list_internal(604				&sender,605				&collection,606				&address,607				true,608			)?;609610			Ok(())611		}612613		/// Remove an address from white list.614		///615		/// # Permissions616		///617		/// * Collection Owner618		/// * Collection Admin619		///620		/// # Arguments621		///622		/// * collection_id.623		///624		/// * address.625		#[weight = <T as Config>::WeightInfo::remove_from_white_list()]626		#[transactional]627		pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{628629			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630			let collection = Self::get_collection(collection_id)?;631632			Self::toggle_white_list_internal(633				&sender,634				&collection,635				&address,636				false,637			)?;638639			Ok(())640		}641642		/// Toggle between normal and white list access for the methods with access for `Anyone`.643		///644		/// # Permissions645		///646		/// * Collection Owner.647		///648		/// # Arguments649		///650		/// * collection_id.651		///652		/// * mode: [AccessMode]653		#[weight = <T as Config>::WeightInfo::set_public_access_mode()]654		#[transactional]655		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult656		{657			let sender = ensure_signed(origin)?;658659			let mut target_collection = Self::get_collection(collection_id)?;660			Self::check_owner_permissions(&target_collection, &sender)?;661			target_collection.access = mode;662			target_collection.save()663		}664665		/// Allows Anyone to create tokens if:666		/// * White List is enabled, and667		/// * Address is added to white list, and668		/// * This method was called with True parameter669		///670		/// # Permissions671		/// * Collection Owner672		///673		/// # Arguments674		///675		/// * collection_id.676		///677		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.678		#[weight = <T as Config>::WeightInfo::set_mint_permission()]679		#[transactional]680		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult681		{682			let sender = ensure_signed(origin)?;683684			let mut target_collection = Self::get_collection(collection_id)?;685			Self::check_owner_permissions(&target_collection, &sender)?;686			target_collection.mint_mode = mint_permission;687			target_collection.save()688		}689690		/// Change the owner of the collection.691		///692		/// # Permissions693		///694		/// * Collection Owner.695		///696		/// # Arguments697		///698		/// * collection_id.699		///700		/// * new_owner.701		#[weight = <T as Config>::WeightInfo::change_collection_owner()]702		#[transactional]703		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {704705			let sender = ensure_signed(origin)?;706			let mut target_collection = Self::get_collection(collection_id)?;707			Self::check_owner_permissions(&target_collection, &sender)?;708			target_collection.owner = new_owner;709			target_collection.save()710		}711712		/// Adds an admin of the Collection.713		/// 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.714		///715		/// # Permissions716		///717		/// * Collection Owner.718		/// * Collection Admin.719		///720		/// # Arguments721		///722		/// * collection_id: ID of the Collection to add admin for.723		///724		/// * new_admin_id: Address of new admin to add.725		#[weight = <T as Config>::WeightInfo::add_collection_admin()]726		#[transactional]727		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {728			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729			let collection = Self::get_collection(collection_id)?;730			Self::check_owner_or_admin_permissions(&collection, &sender)?;731			let mut admin_arr = <AdminList<T>>::get(collection_id);732733			match admin_arr.binary_search(&new_admin_id) {734				Ok(_) => {},735				Err(idx) => {736					ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);737					admin_arr.insert(idx, new_admin_id);738					<AdminList<T>>::insert(collection_id, admin_arr);739				}740			}741			Ok(())742		}743744		/// 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.745		///746		/// # Permissions747		///748		/// * Collection Owner.749		/// * Collection Admin.750		///751		/// # Arguments752		///753		/// * collection_id: ID of the Collection to remove admin for.754		///755		/// * account_id: Address of admin to remove.756		#[weight = <T as Config>::WeightInfo::remove_collection_admin()]757		#[transactional]758		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {759			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);760			let collection = Self::get_collection(collection_id)?;761			Self::check_owner_or_admin_permissions(&collection, &sender)?;762			let mut admin_arr = <AdminList<T>>::get(collection_id);763764			if let Ok(idx) = admin_arr.binary_search(&account_id) {765				admin_arr.remove(idx);766				<AdminList<T>>::insert(collection_id, admin_arr);767			}768			Ok(())769		}770771		/// # Permissions772		///773		/// * Collection Owner774		///775		/// # Arguments776		///777		/// * collection_id.778		///779		/// * new_sponsor.780		#[weight = <T as Config>::WeightInfo::set_collection_sponsor()]781		#[transactional]782		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {783			let sender = ensure_signed(origin)?;784			let mut target_collection = Self::get_collection(collection_id)?;785			Self::check_owner_permissions(&target_collection, &sender)?;786787			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);788			target_collection.save()789		}790791		/// # Permissions792		///793		/// * Sponsor.794		///795		/// # Arguments796		///797		/// * collection_id.798		#[weight = <T as Config>::WeightInfo::confirm_sponsorship()]799		#[transactional]800		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {801			let sender = ensure_signed(origin)?;802803			let mut target_collection = Self::get_collection(collection_id)?;804			ensure!(805				target_collection.sponsorship.pending_sponsor() == Some(&sender),806				Error::<T>::ConfirmUnsetSponsorFail807			);808809			target_collection.sponsorship = SponsorshipState::Confirmed(sender);810			target_collection.save()811		}812813		/// Switch back to pay-per-own-transaction model.814		///815		/// # Permissions816		///817		/// * Collection owner.818		///819		/// # Arguments820		///821		/// * collection_id.822		#[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]823		#[transactional]824		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {825			let sender = ensure_signed(origin)?;826827			let mut target_collection = Self::get_collection(collection_id)?;828			Self::check_owner_permissions(&target_collection, &sender)?;829830			target_collection.sponsorship = SponsorshipState::Disabled;831			target_collection.save()832		}833834		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.835		///836		/// # Permissions837		///838		/// * Collection Owner.839		/// * Collection Admin.840		/// * Anyone if841		///     * White List is enabled, and842		///     * Address is added to white list, and843		///     * MintPermission is enabled (see SetMintPermission method)844		///845		/// # Arguments846		///847		/// * collection_id: ID of the collection.848		///849		/// * owner: Address, initial owner of the NFT.850		///851		/// * data: Token data to store on chain.852		// #[weight =853		// (130_000_000 as Weight)854		// .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))855		// .saturating_add(RocksDbWeight::get().reads(10 as Weight))856		// .saturating_add(RocksDbWeight::get().writes(8 as Weight))]857858		#[weight = <T as Config>::WeightInfo::create_item(data.data_size())]859		#[transactional]860		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {861			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);862			let collection = Self::get_collection(collection_id)?;863864			Self::create_item_internal(&sender, &collection, &owner, data)?;865866			collection.submit_logs()867		}868869		/// This method creates multiple items in a collection created with CreateCollection method.870		///871		/// # Permissions872		///873		/// * Collection Owner.874		/// * Collection Admin.875		/// * Anyone if876		///     * White List is enabled, and877		///     * Address is added to white list, and878		///     * MintPermission is enabled (see SetMintPermission method)879		///880		/// # Arguments881		///882		/// * collection_id: ID of the collection.883		///884		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].885		///886		/// * owner: Address, initial owner of the NFT.887		#[weight = <T as Config>::WeightInfo::create_item(items_data.iter()888							   .map(|data| { data.data_size() })889							   .sum())]890		#[transactional]891		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {892893			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);894			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895			let collection = Self::get_collection(collection_id)?;896897			Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;898899			collection.submit_logs()900		}901902		// TODO! transaction weight903904		/// Set transfers_enabled value for particular collection905		///906		/// # Permissions907		///908		/// * Collection Owner.909		///910		/// # Arguments911		///912		/// * collection_id: ID of the collection.913		///914		/// * value: New flag value.915		#[weight = <T as Config>::WeightInfo::burn_item()]916		#[transactional]917		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {918919			let sender = ensure_signed(origin)?;920			let mut target_collection = Self::get_collection(collection_id)?;921922			Self::check_owner_permissions(&target_collection, &sender)?;923924			target_collection.transfers_enabled = value;925			target_collection.save()926		}927928		/// Destroys a concrete instance of NFT.929		///930		/// # Permissions931		///932		/// * Collection Owner.933		/// * Collection Admin.934		/// * Current NFT Owner.935		///936		/// # Arguments937		///938		/// * collection_id: ID of the collection.939		///940		/// * item_id: ID of NFT to burn.941		#[weight = <T as Config>::WeightInfo::burn_item()]942		#[transactional]943		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {944945			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946			let target_collection = Self::get_collection(collection_id)?;947948			Self::burn_item_internal(&sender, &target_collection, item_id, value)?;949950			target_collection.submit_logs()951		}952953		/// Change ownership of the token.954		///955		/// # Permissions956		///957		/// * Collection Owner958		/// * Collection Admin959		/// * Current NFT owner960		///961		/// # Arguments962		///963		/// * recipient: Address of token recipient.964		///965		/// * collection_id.966		///967		/// * item_id: ID of the item968		///     * Non-Fungible Mode: Required.969		///     * Fungible Mode: Ignored.970		///     * Re-Fungible Mode: Required.971		///972		/// * value: Amount to transfer.973		///     * Non-Fungible Mode: Ignored974		///     * Fungible Mode: Must specify transferred amount975		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)976		#[weight = <T as Config>::WeightInfo::transfer()]977		#[transactional]978		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {979			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);980			let collection = Self::get_collection(collection_id)?;981982			Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;983984			collection.submit_logs()985		}986987		/// Set, change, or remove approved address to transfer the ownership of the NFT.988		///989		/// # Permissions990		///991		/// * Collection Owner992		/// * Collection Admin993		/// * Current NFT owner994		///995		/// # Arguments996		///997		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).998		///999		/// * collection_id.1000		///1001		/// * item_id: ID of the item.1002		#[weight = <T as Config>::WeightInfo::approve()]1003		#[transactional]1004		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1005			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006			let collection = Self::get_collection(collection_id)?;10071008			Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10091010			collection.submit_logs()1011		}10121013		/// 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.1014		///1015		/// # Permissions1016		/// * Collection Owner1017		/// * Collection Admin1018		/// * Current NFT owner1019		/// * Address approved by current NFT owner1020		///1021		/// # Arguments1022		///1023		/// * from: Address that owns token.1024		///1025		/// * recipient: Address of token recipient.1026		///1027		/// * collection_id.1028		///1029		/// * item_id: ID of the item.1030		///1031		/// * value: Amount to transfer.1032		#[weight = <T as Config>::WeightInfo::transfer_from()]1033		#[transactional]1034		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1035			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036			let collection = Self::get_collection(collection_id)?;10371038			Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10391040			collection.submit_logs()1041		}1042		// #[weight = 0]1043		//     // let no_perm_mes = "You do not have permissions to modify this collection";1044		//     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1045		//     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1046		//     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10471048		//     // // on_nft_received  call10491050		//     // Self::transfer(origin, collection_id, item_id, new_owner)?;10511052		//     Ok(())1053		// }10541055		/// Set off-chain data schema.1056		///1057		/// # Permissions1058		///1059		/// * Collection Owner1060		/// * Collection Admin1061		///1062		/// # Arguments1063		///1064		/// * collection_id.1065		///1066		/// * schema: String representing the offchain data schema.1067		#[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1068		#[transactional]1069		pub fn set_variable_meta_data (1070			origin,1071			collection_id: CollectionId,1072			item_id: TokenId,1073			data: Vec<u8>1074		) -> DispatchResult {1075			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10761077			let collection = Self::get_collection(collection_id)?;10781079			Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10801081			Ok(())1082		}10831084		/// Set schema standard1085		/// ImageURL1086		/// Unique1087		///1088		/// # Permissions1089		///1090		/// * Collection Owner1091		/// * Collection Admin1092		///1093		/// # Arguments1094		///1095		/// * collection_id.1096		///1097		/// * schema: SchemaVersion: enum1098		#[weight = <T as Config>::WeightInfo::set_schema_version()]1099		#[transactional]1100		pub fn set_schema_version(1101			origin,1102			collection_id: CollectionId,1103			version: SchemaVersion1104		) -> DispatchResult {1105			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1106			let mut target_collection = Self::get_collection(collection_id)?;1107			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1108			target_collection.schema_version = version;1109			target_collection.save()1110		}11111112		/// Set off-chain data schema.1113		///1114		/// # Permissions1115		///1116		/// * Collection Owner1117		/// * Collection Admin1118		///1119		/// # Arguments1120		///1121		/// * collection_id.1122		///1123		/// * schema: String representing the offchain data schema.1124		#[weight = <T as Config>::WeightInfo::set_offchain_schema()]1125		#[transactional]1126		pub fn set_offchain_schema(1127			origin,1128			collection_id: CollectionId,1129			schema: Vec<u8>1130		) -> DispatchResult {1131			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1132			let mut target_collection = Self::get_collection(collection_id)?;1133			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11341135			// check schema limit1136			ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11371138			target_collection.offchain_schema = schema;1139			target_collection.save()1140		}11411142		/// Set const on-chain data schema.1143		///1144		/// # Permissions1145		///1146		/// * Collection Owner1147		/// * Collection Admin1148		///1149		/// # Arguments1150		///1151		/// * collection_id.1152		///1153		/// * schema: String representing the const on-chain data schema.1154		#[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1155		#[transactional]1156		pub fn set_const_on_chain_schema (1157			origin,1158			collection_id: CollectionId,1159			schema: Vec<u8>1160		) -> DispatchResult {1161			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1162			let mut target_collection = Self::get_collection(collection_id)?;1163			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11641165			// check schema limit1166			ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11671168			target_collection.const_on_chain_schema = schema;1169			target_collection.save()1170		}11711172		/// Set variable on-chain data schema.1173		///1174		/// # Permissions1175		///1176		/// * Collection Owner1177		/// * Collection Admin1178		///1179		/// # Arguments1180		///1181		/// * collection_id.1182		///1183		/// * schema: String representing the variable on-chain data schema.1184		#[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1185		#[transactional]1186		pub fn set_variable_on_chain_schema (1187			origin,1188			collection_id: CollectionId,1189			schema: Vec<u8>1190		) -> DispatchResult {1191			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1192			let mut target_collection = Self::get_collection(collection_id)?;1193			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11941195			// check schema limit1196			ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11971198			target_collection.variable_on_chain_schema = schema;1199			target_collection.save()1200		}12011202		#[weight = <T as Config>::WeightInfo::set_collection_limits()]1203		#[transactional]1204		pub fn set_collection_limits(1205			origin,1206			collection_id: u32,1207			new_limits: CollectionLimits<T::BlockNumber>,1208		) -> DispatchResult {1209			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210			let mut target_collection = Self::get_collection(collection_id)?;1211			Self::check_owner_permissions(&target_collection, sender.as_sub())?;1212			let old_limits = &target_collection.limits;12131214			// collection bounds1215			ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1216				new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1217				new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1218				Error::<T>::CollectionLimitBoundsExceeded);12191220			// token_limit   check  prev1221			ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1222			ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12231224			ensure!(1225				(old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1226				(old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1227				Error::<T>::OwnerPermissionsCantBeReverted,1228			);12291230			target_collection.limits = new_limits;12311232			target_collection.save()1233		}1234	}1235}12361237impl<T: Config> Module<T> {1238	pub fn create_item_internal(1239		sender: &T::CrossAccountId,1240		collection: &CollectionHandle<T>,1241		owner: &T::CrossAccountId,1242		data: CreateItemData,1243	) -> DispatchResult {1244		Self::can_create_items_in_collection(collection, sender, owner, 1)?;1245		Self::validate_create_item_args(collection, &data)?;1246		Self::create_item_no_validation(collection, owner, data)?;12471248		Ok(())1249	}12501251	pub fn transfer_internal(1252		sender: &T::CrossAccountId,1253		recipient: &T::CrossAccountId,1254		target_collection: &CollectionHandle<T>,1255		item_id: TokenId,1256		value: u128,1257	) -> DispatchResult {1258		target_collection.consume_gas(2000000)?;1259		// Limits check1260		Self::is_correct_transfer(target_collection, recipient)?;12611262		// Transfer permissions check1263		ensure!(1264			Self::is_item_owner(sender, target_collection, item_id)1265				|| Self::is_owner_or_admin_permissions(target_collection, sender),1266			Error::<T>::NoPermission1267		);12681269		if target_collection.access == AccessMode::WhiteList {1270			Self::check_white_list(target_collection, sender)?;1271			Self::check_white_list(target_collection, recipient)?;1272		}12731274		match target_collection.mode {1275			CollectionMode::NFT => Self::transfer_nft(1276				target_collection,1277				item_id,1278				sender.clone(),1279				recipient.clone(),1280			)?,1281			CollectionMode::Fungible(_) => {1282				Self::transfer_fungible(target_collection, value, sender, recipient)?1283			}1284			CollectionMode::ReFungible => Self::transfer_refungible(1285				target_collection,1286				item_id,1287				value,1288				sender.clone(),1289				recipient.clone(),1290			)?,1291			_ => (),1292		};12931294		Self::deposit_event(RawEvent::Transfer(1295			target_collection.id,1296			item_id,1297			sender.clone(),1298			recipient.clone(),1299			value,1300		));13011302		Ok(())1303	}13041305	pub fn approve_internal(1306		sender: &T::CrossAccountId,1307		spender: &T::CrossAccountId,1308		collection: &CollectionHandle<T>,1309		item_id: TokenId,1310		amount: u128,1311	) -> DispatchResult {1312		collection.consume_gas(2000000)?;1313		Self::token_exists(collection, item_id)?;13141315		// Transfer permissions check1316		let bypasses_limits = collection.limits.owner_can_transfer1317			&& Self::is_owner_or_admin_permissions(collection, sender);13181319		let allowance_limit = if bypasses_limits {1320			None1321		} else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1322			Some(amount)1323		} else {1324			fail!(Error::<T>::NoPermission);1325		};13261327		if collection.access == AccessMode::WhiteList {1328			Self::check_white_list(collection, sender)?;1329			Self::check_white_list(collection, spender)?;1330		}13311332		let allowance: u128 = amount1333			.checked_add(<Allowances<T>>::get(1334				collection.id,1335				(item_id, sender.as_sub(), spender.as_sub()),1336			))1337			.ok_or(Error::<T>::NumOverflow)?;1338		if let Some(limit) = allowance_limit {1339			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1340		}1341		<Allowances<T>>::insert(1342			collection.id,1343			(item_id, sender.as_sub(), spender.as_sub()),1344			allowance,1345		);13461347		if matches!(collection.mode, CollectionMode::NFT) {1348			// TODO: NFT: only one owner may exist for token in ERC7211349			collection.log(ERC721Events::Approval {1350				owner: *sender.as_eth(),1351				approved: *spender.as_eth(),1352				token_id: item_id.into(),1353			})?;1354		}13551356		if matches!(collection.mode, CollectionMode::Fungible(_)) {1357			// TODO: NFT: only one owner may exist for token in ERC201358			collection.log(ERC20Events::Approval {1359				owner: *sender.as_eth(),1360				spender: *spender.as_eth(),1361				value: allowance.into(),1362			})?;1363		}13641365		Self::deposit_event(RawEvent::Approved(1366			collection.id,1367			item_id,1368			sender.clone(),1369			spender.clone(),1370			allowance,1371		));1372		Ok(())1373	}13741375	pub fn transfer_from_internal(1376		sender: &T::CrossAccountId,1377		from: &T::CrossAccountId,1378		recipient: &T::CrossAccountId,1379		collection: &CollectionHandle<T>,1380		item_id: TokenId,1381		amount: u128,1382	) -> DispatchResult {1383		collection.consume_gas(2000000)?;1384		// Check approval1385		let approval: u128 =1386			<Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13871388		// Limits check1389		Self::is_correct_transfer(collection, recipient)?;13901391		// Transfer permissions check1392		ensure!(1393			approval >= amount1394				|| (collection.limits.owner_can_transfer1395					&& Self::is_owner_or_admin_permissions(collection, sender)),1396			Error::<T>::NoPermission1397		);13981399		if collection.access == AccessMode::WhiteList {1400			Self::check_white_list(collection, sender)?;1401			Self::check_white_list(collection, recipient)?;1402		}14031404		// Reduce approval by transferred amount or remove if remaining approval drops to 01405		let allowance = approval.saturating_sub(amount);1406		if allowance > 0 {1407			<Allowances<T>>::insert(1408				collection.id,1409				(item_id, from.as_sub(), sender.as_sub()),1410				allowance,1411			);1412		} else {1413			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1414		}14151416		match collection.mode {1417			CollectionMode::NFT => {1418				Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1419			}1420			CollectionMode::Fungible(_) => {1421				Self::transfer_fungible(collection, amount, from, recipient)?1422			}1423			CollectionMode::ReFungible => Self::transfer_refungible(1424				collection,1425				item_id,1426				amount,1427				from.clone(),1428				recipient.clone(),1429			)?,1430			_ => (),1431		};14321433		if matches!(collection.mode, CollectionMode::Fungible(_)) {1434			collection.log(ERC20Events::Approval {1435				owner: *from.as_eth(),1436				spender: *sender.as_eth(),1437				value: allowance.into(),1438			})?;1439		}14401441		Ok(())1442	}14431444	pub fn set_variable_meta_data_internal(1445		sender: &T::CrossAccountId,1446		collection: &CollectionHandle<T>,1447		item_id: TokenId,1448		data: Vec<u8>,1449	) -> DispatchResult {1450		Self::token_exists(collection, item_id)?;14511452		ensure!(1453			CUSTOM_DATA_LIMIT >= data.len() as u32,1454			Error::<T>::TokenVariableDataLimitExceeded1455		);14561457		// Modify permissions check1458		ensure!(1459			Self::is_item_owner(sender, collection, item_id)1460				|| Self::is_owner_or_admin_permissions(collection, sender),1461			Error::<T>::NoPermission1462		);14631464		match collection.mode {1465			CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1466			CollectionMode::ReFungible => {1467				Self::set_re_fungible_variable_data(collection, item_id, data)?1468			}1469			CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1470			_ => fail!(Error::<T>::UnexpectedCollectionType),1471		};14721473		Ok(())1474	}14751476	pub fn create_multiple_items_internal(1477		sender: &T::CrossAccountId,1478		collection: &CollectionHandle<T>,1479		owner: &T::CrossAccountId,1480		items_data: Vec<CreateItemData>,1481	) -> DispatchResult {1482		Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14831484		for data in &items_data {1485			Self::validate_create_item_args(collection, data)?;1486		}1487		for data in &items_data {1488			Self::create_item_no_validation(collection, owner, data.clone())?;1489		}14901491		Ok(())1492	}14931494	pub fn burn_item_internal(1495		sender: &T::CrossAccountId,1496		collection: &CollectionHandle<T>,1497		item_id: TokenId,1498		value: u128,1499	) -> DispatchResult {1500		ensure!(1501			Self::is_item_owner(sender, collection, item_id)1502				|| (collection.limits.owner_can_transfer1503					&& Self::is_owner_or_admin_permissions(collection, sender)),1504			Error::<T>::NoPermission1505		);15061507		if collection.access == AccessMode::WhiteList {1508			Self::check_white_list(collection, sender)?;1509		}15101511		match collection.mode {1512			CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1513			CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1514			CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1515			_ => (),1516		};15171518		Ok(())1519	}15201521	pub fn toggle_white_list_internal(1522		sender: &T::CrossAccountId,1523		collection: &CollectionHandle<T>,1524		address: &T::CrossAccountId,1525		whitelisted: bool,1526	) -> DispatchResult {1527		Self::check_owner_or_admin_permissions(collection, sender)?;15281529		if whitelisted {1530			<WhiteList<T>>::insert(collection.id, address.as_sub(), true);1531		} else {1532			<WhiteList<T>>::remove(collection.id, address.as_sub());1533		}15341535		Ok(())1536	}15371538	fn is_correct_transfer(1539		collection: &CollectionHandle<T>,1540		recipient: &T::CrossAccountId,1541	) -> DispatchResult {1542		let collection_id = collection.id;15431544		// check token limit and account token limit1545		let account_items: u32 =1546			<AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1547		ensure!(1548			collection.limits.account_token_ownership_limit > account_items,1549			Error::<T>::AccountTokenLimitExceeded1550		);15511552		// preliminary transfer check1553		ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15541555		Ok(())1556	}15571558	fn can_create_items_in_collection(1559		collection: &CollectionHandle<T>,1560		sender: &T::CrossAccountId,1561		owner: &T::CrossAccountId,1562		amount: u32,1563	) -> DispatchResult {1564		let collection_id = collection.id;15651566		// check token limit and account token limit1567		let total_items: u32 = ItemListIndex::get(collection_id)1568			.checked_add(amount)1569			.ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1570		let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1571			as u32)1572			.checked_add(amount)1573			.ok_or(Error::<T>::AccountTokenLimitExceeded)?;1574		ensure!(1575			collection.limits.token_limit >= total_items,1576			Error::<T>::CollectionTokenLimitExceeded1577		);1578		ensure!(1579			collection.limits.account_token_ownership_limit >= account_items,1580			Error::<T>::AccountTokenLimitExceeded1581		);15821583		if !Self::is_owner_or_admin_permissions(collection, sender) {1584			ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1585			Self::check_white_list(collection, owner)?;1586			Self::check_white_list(collection, sender)?;1587		}15881589		Ok(())1590	}15911592	fn validate_create_item_args(1593		target_collection: &CollectionHandle<T>,1594		data: &CreateItemData,1595	) -> DispatchResult {1596		match target_collection.mode {1597			CollectionMode::NFT => {1598				if !matches!(data, CreateItemData::NFT(_)) {1599					fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1600				}1601			}1602			CollectionMode::Fungible(_) => {1603				if !matches!(data, CreateItemData::Fungible(_)) {1604					fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1605				}1606			}1607			CollectionMode::ReFungible => {1608				if let CreateItemData::ReFungible(data) = data {1609					// Check refungibility limits1610					ensure!(1611						data.pieces <= MAX_REFUNGIBLE_PIECES,1612						Error::<T>::WrongRefungiblePieces1613					);1614					ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1615				} else {1616					fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1617				}1618			}1619			_ => {1620				fail!(Error::<T>::UnexpectedCollectionType);1621			}1622		};16231624		Ok(())1625	}16261627	fn create_item_no_validation(1628		collection: &CollectionHandle<T>,1629		owner: &T::CrossAccountId,1630		data: CreateItemData,1631	) -> DispatchResult {1632		match data {1633			CreateItemData::NFT(data) => {1634				let item = NftItemType {1635					owner: owner.clone(),1636					const_data: data.const_data.into_inner(),1637					variable_data: data.variable_data.into_inner(),1638				};16391640				Self::add_nft_item(collection, item)?;1641			}1642			CreateItemData::Fungible(data) => {1643				Self::add_fungible_item(collection, owner, data.value)?;1644			}1645			CreateItemData::ReFungible(data) => {1646				let owner_list = vec![Ownership {1647					owner: owner.clone(),1648					fraction: data.pieces,1649				}];16501651				let item = ReFungibleItemType {1652					owner: owner_list,1653					const_data: data.const_data.into_inner(),1654					variable_data: data.variable_data.into_inner(),1655				};16561657				Self::add_refungible_item(collection, item)?;1658			}1659		};16601661		Ok(())1662	}16631664	fn add_fungible_item(1665		collection: &CollectionHandle<T>,1666		owner: &T::CrossAccountId,1667		value: u128,1668	) -> DispatchResult {1669		let collection_id = collection.id;16701671		// Does new owner already have an account?1672		let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16731674		// Mint1675		let item = FungibleItemType {1676			value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1677		};1678		<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16791680		// Update balance1681		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1682			.checked_add(value)1683			.ok_or(Error::<T>::NumOverflow)?;1684		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16851686		Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1687		Ok(())1688	}16891690	fn add_refungible_item(1691		collection: &CollectionHandle<T>,1692		item: ReFungibleItemType<T::CrossAccountId>,1693	) -> DispatchResult {1694		let collection_id = collection.id;16951696		let current_index = <ItemListIndex>::get(collection_id)1697			.checked_add(1)1698			.ok_or(Error::<T>::NumOverflow)?;1699		let itemcopy = item.clone();17001701		ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1702		let item_owner = item.owner.first().expect("only one owner is defined");17031704		let value = item_owner.fraction;1705		let owner = item_owner.owner.clone();17061707		Self::add_token_index(collection_id, current_index, &owner)?;17081709		<ItemListIndex>::insert(collection_id, current_index);1710		<ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17111712		// Update balance1713		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1714			.checked_add(value)1715			.ok_or(Error::<T>::NumOverflow)?;1716		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17171718		Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1719		Ok(())1720	}17211722	fn add_nft_item(1723		collection: &CollectionHandle<T>,1724		item: NftItemType<T::CrossAccountId>,1725	) -> DispatchResult {1726		let collection_id = collection.id;17271728		let current_index = <ItemListIndex>::get(collection_id)1729			.checked_add(1)1730			.ok_or(Error::<T>::NumOverflow)?;17311732		let item_owner = item.owner.clone();1733		Self::add_token_index(collection_id, current_index, &item.owner)?;17341735		<ItemListIndex>::insert(collection_id, current_index);1736		<NftItemList<T>>::insert(collection_id, current_index, item);17371738		// Update balance1739		let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1740			.checked_add(1)1741			.ok_or(Error::<T>::NumOverflow)?;1742		<Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17431744		collection.log(ERC721Events::Transfer {1745			from: H160::default(),1746			to: *item_owner.as_eth(),1747			token_id: current_index.into(),1748		})?;1749		Self::deposit_event(RawEvent::ItemCreated(1750			collection_id,1751			current_index,1752			item_owner,1753		));1754		Ok(())1755	}17561757	fn burn_refungible_item(1758		collection: &CollectionHandle<T>,1759		item_id: TokenId,1760		owner: &T::CrossAccountId,1761	) -> DispatchResult {1762		let collection_id = collection.id;17631764		let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1765			.ok_or(Error::<T>::TokenNotFound)?;1766		let rft_balance = token1767			.owner1768			.iter()1769			.find(|&i| i.owner == *owner)1770			.ok_or(Error::<T>::TokenNotFound)?;1771		Self::remove_token_index(collection_id, item_id, owner)?;17721773		// update balance1774		let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1775			.checked_sub(rft_balance.fraction)1776			.ok_or(Error::<T>::NumOverflow)?;1777		<Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17781779		// Re-create owners list with sender removed1780		let index = token1781			.owner1782			.iter()1783			.position(|i| i.owner == *owner)1784			.expect("owned item is exists");1785		token.owner.remove(index);1786		let owner_count = token.owner.len();17871788		// Burn the token completely if this was the last (only) owner1789		if owner_count == 0 {1790			<ReFungibleItemList<T>>::remove(collection_id, item_id);1791			<VariableMetaDataBasket<T>>::remove(collection_id, item_id);1792		} else {1793			<ReFungibleItemList<T>>::insert(collection_id, item_id, token);1794		}17951796		Ok(())1797	}17981799	fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1800		let collection_id = collection.id;18011802		let item =1803			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1804		Self::remove_token_index(collection_id, item_id, &item.owner)?;18051806		// update balance1807		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1808			.checked_sub(1)1809			.ok_or(Error::<T>::NumOverflow)?;1810		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1811		<NftItemList<T>>::remove(collection_id, item_id);1812		<VariableMetaDataBasket<T>>::remove(collection_id, item_id);18131814		Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1815		Ok(())1816	}18171818	fn burn_fungible_item(1819		owner: &T::CrossAccountId,1820		collection: &CollectionHandle<T>,1821		value: u128,1822	) -> DispatchResult {1823		let collection_id = collection.id;18241825		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1826		ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18271828		// update balance1829		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830			.checked_sub(value)1831			.ok_or(Error::<T>::NumOverflow)?;1832		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834		if balance.value - value > 0 {1835			balance.value -= value;1836			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1837		} else {1838			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1839		}18401841		collection.log(ERC20Events::Transfer {1842			from: *owner.as_eth(),1843			to: H160::default(),1844			value: value.into(),1845		})?;1846		Ok(())1847	}18481849	pub fn get_collection(1850		collection_id: CollectionId,1851	) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1852		Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1853	}18541855	fn check_owner_permissions(1856		target_collection: &CollectionHandle<T>,1857		subject: &T::AccountId,1858	) -> DispatchResult {1859		ensure!(1860			*subject == target_collection.owner,1861			Error::<T>::NoPermission1862		);18631864		Ok(())1865	}18661867	fn is_owner_or_admin_permissions(1868		collection: &CollectionHandle<T>,1869		subject: &T::CrossAccountId,1870	) -> bool {1871		*subject.as_sub() == collection.owner1872			|| <AdminList<T>>::get(collection.id).contains(subject)1873	}18741875	fn check_owner_or_admin_permissions(1876		collection: &CollectionHandle<T>,1877		subject: &T::CrossAccountId,1878	) -> DispatchResult {1879		ensure!(1880			Self::is_owner_or_admin_permissions(collection, subject),1881			Error::<T>::NoPermission1882		);18831884		Ok(())1885	}18861887	fn owned_amount(1888		subject: &T::CrossAccountId,1889		target_collection: &CollectionHandle<T>,1890		item_id: TokenId,1891	) -> Option<u128> {1892		let collection_id = target_collection.id;18931894		match target_collection.mode {1895			CollectionMode::NFT => {1896				(<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1897			}1898			CollectionMode::Fungible(_) => {1899				Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1900			}1901			CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1902				.owner1903				.iter()1904				.find(|i| i.owner == *subject)1905				.map(|i| i.fraction),1906			CollectionMode::Invalid => None,1907		}1908	}19091910	fn is_item_owner(1911		subject: &T::CrossAccountId,1912		target_collection: &CollectionHandle<T>,1913		item_id: TokenId,1914	) -> bool {1915		match target_collection.mode {1916			CollectionMode::Fungible(_) => true,1917			_ => Self::owned_amount(subject, target_collection, item_id).is_some(),1918		}1919	}19201921	fn check_white_list(1922		collection: &CollectionHandle<T>,1923		address: &T::CrossAccountId,1924	) -> DispatchResult {1925		let collection_id = collection.id;19261927		let mes = Error::<T>::AddresNotInWhiteList;1928		ensure!(1929			<WhiteList<T>>::contains_key(collection_id, address.as_sub()),1930			mes1931		);19321933		Ok(())1934	}19351936	/// Check if token exists. In case of Fungible, check if there is an entry for1937	/// the owner in fungible balances double map1938	fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1939		let collection_id = target_collection.id;1940		let exists = match target_collection.mode {1941			CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1942			CollectionMode::Fungible(_) => true,1943			CollectionMode::ReFungible => {1944				<ReFungibleItemList<T>>::contains_key(collection_id, item_id)1945			}1946			_ => false,1947		};19481949		ensure!(exists, Error::<T>::TokenNotFound);1950		Ok(())1951	}19521953	fn transfer_fungible(1954		collection: &CollectionHandle<T>,1955		value: u128,1956		owner: &T::CrossAccountId,1957		recipient: &T::CrossAccountId,1958	) -> DispatchResult {1959		let collection_id = collection.id;19601961		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1962		ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19631964		// Send balance to recipient (updates balanceOf of recipient)1965		Self::add_fungible_item(collection, recipient, value)?;19661967		// update balanceOf of sender1968		<Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19691970		// Reduce or remove sender1971		if balance.value == value {1972			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1973		} else {1974			balance.value -= value;1975			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1976		}19771978		collection.log(ERC20Events::Transfer {1979			from: *owner.as_eth(),1980			to: *recipient.as_eth(),1981			value: value.into(),1982		})?;1983		Self::deposit_event(RawEvent::Transfer(1984			collection.id,1985			1,1986			owner.clone(),1987			recipient.clone(),1988			value,1989		));19901991		Ok(())1992	}19931994	fn transfer_refungible(1995		collection: &CollectionHandle<T>,1996		item_id: TokenId,1997		value: u128,1998		owner: T::CrossAccountId,1999		new_owner: T::CrossAccountId,2000	) -> DispatchResult {2001		let collection_id = collection.id;2002		let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2003			.ok_or(Error::<T>::TokenNotFound)?;20042005		let item = full_item2006			.owner2007			.iter()2008			.find(|i| i.owner == owner)2009			.ok_or(Error::<T>::TokenNotFound)?;2010		let amount = item.fraction;20112012		ensure!(amount >= value, Error::<T>::TokenValueTooLow);20132014		// update balance2015		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2016			.checked_sub(value)2017			.ok_or(Error::<T>::NumOverflow)?;2018		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20192020		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2021			.checked_add(value)2022			.ok_or(Error::<T>::NumOverflow)?;2023		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20242025		let old_owner = item.owner.clone();2026		let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20272028		let mut new_full_item = full_item.clone();2029		// transfer2030		if amount == value && !new_owner_has_account {2031			// change owner2032			// new owner do not have account2033			new_full_item2034				.owner2035				.iter_mut()2036				.find(|i| i.owner == owner)2037				.expect("old owner does present in refungible")2038				.owner = new_owner.clone();2039			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20402041			// update index collection2042			Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2043		} else {2044			new_full_item2045				.owner2046				.iter_mut()2047				.find(|i| i.owner == owner)2048				.expect("old owner does present in refungible")2049				.fraction -= value;20502051			// separate amount2052			if new_owner_has_account {2053				// new owner has account2054				new_full_item2055					.owner2056					.iter_mut()2057					.find(|i| i.owner == new_owner)2058					.expect("new owner has account")2059					.fraction += value;2060			} else {2061				// new owner do not have account2062				new_full_item.owner.push(Ownership {2063					owner: new_owner.clone(),2064					fraction: value,2065				});2066				Self::add_token_index(collection_id, item_id, &new_owner)?;2067			}20682069			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2070		}20712072		Self::deposit_event(RawEvent::Transfer(2073			collection.id,2074			item_id,2075			owner,2076			new_owner,2077			amount,2078		));20792080		Ok(())2081	}20822083	fn transfer_nft(2084		collection: &CollectionHandle<T>,2085		item_id: TokenId,2086		sender: T::CrossAccountId,2087		new_owner: T::CrossAccountId,2088	) -> DispatchResult {2089		let collection_id = collection.id;2090		let mut item =2091			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20922093		ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20942095		// update balance2096		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2097			.checked_sub(1)2098			.ok_or(Error::<T>::NumOverflow)?;2099		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21002101		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2102			.checked_add(1)2103			.ok_or(Error::<T>::NumOverflow)?;2104		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21052106		// change owner2107		let old_owner = item.owner.clone();2108		item.owner = new_owner.clone();2109		<NftItemList<T>>::insert(collection_id, item_id, item);21102111		// update index collection2112		Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21132114		collection.log(ERC721Events::Transfer {2115			from: *sender.as_eth(),2116			to: *new_owner.as_eth(),2117			token_id: item_id.into(),2118		})?;2119		Self::deposit_event(RawEvent::Transfer(2120			collection.id,2121			item_id,2122			sender,2123			new_owner,2124			1,2125		));21262127		Ok(())2128	}21292130	fn set_re_fungible_variable_data(2131		collection: &CollectionHandle<T>,2132		item_id: TokenId,2133		data: Vec<u8>,2134	) -> DispatchResult {2135		let collection_id = collection.id;2136		let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2137			.ok_or(Error::<T>::TokenNotFound)?;21382139		item.variable_data = data;21402141		<ReFungibleItemList<T>>::insert(collection_id, item_id, item);21422143		Ok(())2144	}21452146	fn set_nft_variable_data(2147		collection: &CollectionHandle<T>,2148		item_id: TokenId,2149		data: Vec<u8>,2150	) -> DispatchResult {2151		let collection_id = collection.id;2152		let mut item =2153			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21542155		item.variable_data = data;21562157		<NftItemList<T>>::insert(collection_id, item_id, item);21582159		Ok(())2160	}21612162	#[allow(dead_code)]2163	fn init_collection(item: &Collection<T>) {2164		// check params2165		assert!(2166			item.decimal_points <= MAX_DECIMAL_POINTS,2167			"decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2168		);2169		assert!(2170			item.name.len() <= 64,2171			"Collection name can not be longer than 63 char"2172		);2173		assert!(2174			item.name.len() <= 256,2175			"Collection description can not be longer than 255 char"2176		);2177		assert!(2178			item.token_prefix.len() <= 16,2179			"Token prefix can not be longer than 15 char"2180		);21812182		// Generate next collection ID2183		let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21842185		CreatedCollectionCount::put(next_id);2186	}21872188	#[allow(dead_code)]2189	fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2190		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21912192		Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21932194		<ItemListIndex>::insert(collection_id, current_index);21952196		// Update balance2197		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2198			.checked_add(1)2199			.unwrap();2200		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2201	}22022203	#[allow(dead_code)]2204	fn init_fungible_token(2205		collection_id: CollectionId,2206		owner: &T::CrossAccountId,2207		item: &FungibleItemType,2208	) {2209		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22102211		Self::add_token_index(collection_id, current_index, owner).unwrap();22122213		<ItemListIndex>::insert(collection_id, current_index);22142215		// Update balance2216		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2217			.checked_add(item.value)2218			.unwrap();2219		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2220	}22212222	#[allow(dead_code)]2223	fn init_refungible_token(2224		collection_id: CollectionId,2225		item: &ReFungibleItemType<T::CrossAccountId>,2226	) {2227		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22282229		let value = item.owner.first().unwrap().fraction;2230		let owner = item.owner.first().unwrap().owner.clone();22312232		Self::add_token_index(collection_id, current_index, &owner).unwrap();22332234		<ItemListIndex>::insert(collection_id, current_index);22352236		// Update balance2237		let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2238			.checked_add(value)2239			.unwrap();2240		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241	}22422243	fn add_token_index(2244		collection_id: CollectionId,2245		item_index: TokenId,2246		owner: &T::CrossAccountId,2247	) -> DispatchResult {2248		// add to account limit2249		if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2250			// bound Owned tokens by a single address2251			let count = <AccountItemCount<T>>::get(owner.as_sub());2252			ensure!(2253				count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2254				Error::<T>::AddressOwnershipLimitExceeded2255			);22562257			<AccountItemCount<T>>::insert(2258				owner.as_sub(),2259				count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2260			);2261		} else {2262			<AccountItemCount<T>>::insert(owner.as_sub(), 1);2263		}22642265		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2266		if list_exists {2267			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2268			let item_contains = list.contains(&item_index.clone());22692270			if !item_contains {2271				list.push(item_index);2272			}22732274			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2275		} else {2276			let itm = vec![item_index];2277			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2278		}22792280		Ok(())2281	}22822283	fn remove_token_index(2284		collection_id: CollectionId,2285		item_index: TokenId,2286		owner: &T::CrossAccountId,2287	) -> DispatchResult {2288		// update counter2289		<AccountItemCount<T>>::insert(2290			owner.as_sub(),2291			<AccountItemCount<T>>::get(owner.as_sub())2292				.checked_sub(1)2293				.ok_or(Error::<T>::NumOverflow)?,2294		);22952296		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2297		if list_exists {2298			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2299			let item_contains = list.contains(&item_index.clone());23002301			if item_contains {2302				list.retain(|&item| item != item_index);2303				<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2304			}2305		}23062307		Ok(())2308	}23092310	fn move_token_index(2311		collection_id: CollectionId,2312		item_index: TokenId,2313		old_owner: &T::CrossAccountId,2314		new_owner: &T::CrossAccountId,2315	) -> DispatchResult {2316		Self::remove_token_index(collection_id, item_index, old_owner)?;2317		Self::add_token_index(collection_id, item_index, new_owner)?;23182319		Ok(())2320	}2321}23222323sp_api::decl_runtime_apis! {2324	pub trait NftApi {2325		/// Used for ethereum integration2326		fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2327	}2328}
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9	clippy::too_many_arguments,10	clippy::unnecessary_mut_passed,11	clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19	construct_runtime, decl_event, decl_module, decl_storage, decl_error,20	dispatch::DispatchResult,21	ensure, fail, parameter_types,22	traits::{23		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24		Randomness, IsSubType, WithdrawReasons,25	},26	weights::{27		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29		WeightToFeePolynomial, DispatchClass,30	},31	StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41	CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43	OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,44	MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,45	CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,46	FungibleItemType, ReFungibleItemType,47};4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;66pub mod weights;67use weights::WeightInfo;6869decl_error! {70	/// Error for non-fungible-token module.71	pub enum Error for Module<T: Config> {72		/// Total collections bound exceeded.73		TotalCollectionsLimitExceeded,74		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.75		CollectionDecimalPointLimitExceeded,76		/// Collection name can not be longer than 63 char.77		CollectionNameLimitExceeded,78		/// Collection description can not be longer than 255 char.79		CollectionDescriptionLimitExceeded,80		/// Token prefix can not be longer than 15 char.81		CollectionTokenPrefixLimitExceeded,82		/// This collection does not exist.83		CollectionNotFound,84		/// Item not exists.85		TokenNotFound,86		/// Admin not found87		AdminNotFound,88		/// Arithmetic calculation overflow.89		NumOverflow,90		/// Account already has admin role.91		AlreadyAdmin,92		/// You do not own this collection.93		NoPermission,94		/// This address is not set as sponsor, use setCollectionSponsor first.95		ConfirmUnsetSponsorFail,96		/// Collection is not in mint mode.97		PublicMintingNotAllowed,98		/// Sender parameter and item owner must be equal.99		MustBeTokenOwner,100		/// Item balance not enough.101		TokenValueTooLow,102		/// Size of item is too large.103		NftSizeLimitExceeded,104		/// No approve found105		ApproveNotFound,106		/// Requested value more than approved.107		TokenValueNotEnough,108		/// Only approved addresses can call this method.109		ApproveRequired,110		/// Address is not in white list.111		AddresNotInWhiteList,112		/// Number of collection admins bound exceeded.113		CollectionAdminsLimitExceeded,114		/// Owned tokens by a single address bound exceeded.115		AddressOwnershipLimitExceeded,116		/// Length of items properties must be greater than 0.117		EmptyArgument,118		/// const_data exceeded data limit.119		TokenConstDataLimitExceeded,120		/// variable_data exceeded data limit.121		TokenVariableDataLimitExceeded,122		/// Not NFT item data used to mint in NFT collection.123		NotNftDataUsedToMintNftCollectionToken,124		/// Not Fungible item data used to mint in Fungible collection.125		NotFungibleDataUsedToMintFungibleCollectionToken,126		/// Not Re Fungible item data used to mint in Re Fungible collection.127		NotReFungibleDataUsedToMintReFungibleCollectionToken,128		/// Unexpected collection type.129		UnexpectedCollectionType,130		/// Can't store metadata in fungible tokens.131		CantStoreMetadataInFungibleTokens,132		/// Collection token limit exceeded133		CollectionTokenLimitExceeded,134		/// Account token limit exceeded per collection135		AccountTokenLimitExceeded,136		/// Collection limit bounds per collection exceeded137		CollectionLimitBoundsExceeded,138		/// Tried to enable permissions which are only permitted to be disabled139		OwnerPermissionsCantBeReverted,140		/// Schema data size limit bound exceeded141		SchemaDataLimitExceeded,142		/// Maximum refungibility exceeded143		WrongRefungiblePieces,144		/// createRefungible should be called with one owner145		BadCreateRefungibleCall,146		/// Gas limit exceeded147		OutOfGas,148		/// Collection settings not allowing items transferring149		TransferNotAllowed,150	}151}152153#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]154pub struct CollectionHandle<T: Config> {155	pub id: CollectionId,156	collection: Collection<T>,157	recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,158}159impl<T: Config> CollectionHandle<T> {160	pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {161		<CollectionById<T>>::get(id).map(|collection| Self {162			id,163			collection,164			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(165				eth::collection_id_to_address(id),166				gas_limit,167			),168		})169	}170	pub fn get(id: CollectionId) -> Option<Self> {171		Self::get_with_gas_limit(id, u64::MAX)172	}173	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {174		self.recorder.log_sub(log)175	}176	fn consume_gas(&self, gas: u64) -> DispatchResult {177		self.recorder.consume_gas_sub(gas)178	}179	pub fn submit_logs(self) -> DispatchResult {180		self.recorder.submit_logs()181	}182	pub fn save(self) -> DispatchResult {183		self.recorder.submit_logs()?;184		<CollectionById<T>>::insert(self.id, self.collection);185		Ok(())186	}187}188impl<T: Config> Deref for CollectionHandle<T> {189	type Target = Collection<T>;190191	fn deref(&self) -> &Self::Target {192		&self.collection193	}194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197	fn deref_mut(&mut self) -> &mut Self::Target {198		&mut self.collection199	}200}201202pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {203	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;204205	/// Weight information for extrinsics in this pallet.206	type WeightInfo: WeightInfo;207208	type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;209	type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;210211	type CrossAccountId: CrossAccountId<Self::AccountId>;212	type Currency: Currency<Self::AccountId>;213	type CollectionCreationPrice: Get<214		<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,215	>;216	type TreasuryAccountId: Get<Self::AccountId>;217}218219type SelfWeightOf<T> = <T as Config>::WeightInfo;220221trait WeightInfoHelpers: WeightInfo {222	fn transfer() -> Weight {223		Self::transfer_nft()224			.max(Self::transfer_fungible())225			.max(Self::transfer_refungible())226	}227	fn transfer_from() -> Weight {228		Self::transfer_from_nft()229			.max(Self::transfer_from_fungible())230			.max(Self::transfer_from_refungible())231	}232	fn approve() -> Weight {233		// TODO: refungible, fungible234		Self::approve_nft()235	}236	fn set_variable_meta_data(data: u32) -> Weight {237		// TODO: refungible238		Self::set_variable_meta_data_nft(data)239	}240	fn create_item(data: u32) -> Weight {241		Self::create_item_nft(data)242			.max(Self::create_item_fungible())243			.max(Self::create_item_refungible(data))244	}245	fn burn_item() -> Weight {246		// TODO: refungible, fungible247		Self::burn_item_nft()248	}249}250impl<T: WeightInfo> WeightInfoHelpers for T {}251252// # Used definitions253//254// ## User control levels255//256// chain-controlled - key is uncontrolled by user257//                    i.e autoincrementing index258//                    can use non-cryptographic hash259// real - key is controlled by user260//        but it is hard to generate enough colliding values, i.e owner of signed txs261//        can use non-cryptographic hash262// controlled - key is completly controlled by users263//              i.e maps with mutable keys264//              should use cryptographic hash265//266// ## User control level downgrade reasons267//268// ?1 - chain-controlled -> controlled269//      collections/tokens can be destroyed, resulting in massive holes270// ?2 - chain-controlled -> controlled271//      same as ?1, but can be only added, resulting in easier exploitation272// ?3 - real -> controlled273//      no confirmation required, so addresses can be easily generated274decl_storage! {275	trait Store for Module<T: Config> as Nft {276277		//#region Private members278		/// Id of next collection279		CreatedCollectionCount: u32;280		/// Used for migrations281		ChainVersion: u64;282		/// Id of last collection token283		/// Collection id (controlled?1)284		ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;285		//#endregion286287		//#region Bound counters288		/// Amount of collections destroyed, used for total amount tracking with289		/// CreatedCollectionCount290		DestroyedCollectionCount: u32;291		/// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)292		/// Account id (real)293		pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294		//#endregion295296		//#region Basic collections297		/// Collection info298		/// Collection id (controlled?1)299		pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300		/// List of collection admins301		/// Collection id (controlled?2)302		pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303		/// Whitelisted collection users304		/// Collection id (controlled?2), user id (controlled?3)305		pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306		//#endregion307308		/// How many of collection items user have309		/// Collection id (controlled?2), account id (real)310		pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312		/// Amount of items which spender can transfer out of owners account (via transferFrom)313		/// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))314		/// TODO: Off chain worker should remove from this map when token gets removed315		pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317		//#region Item collections318		/// Collection id (controlled?2), token id (controlled?1)319		pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320		/// Collection id (controlled?2), owner (controlled?2)321		pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322		/// Collection id (controlled?2), token id (controlled?1)323		pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324		//#endregion325326		//#region Index list327		/// Collection id (controlled?2), tokens owner (controlled?2)328		pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329		//#endregion330331		//#region Tokens transfer rate limit baskets332		/// (Collection id (controlled?2), who created (real))333		/// TODO: Off chain worker should remove from this map when collection gets removed334		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335		/// Collection id (controlled?2), token id (controlled?2)336		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337		/// Collection id (controlled?2), owning user (real)338		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339		/// Collection id (controlled?2), token id (controlled?2)340		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341		//#endregion342343		/// Variable metadata sponsoring344		/// Collection id (controlled?2), token id (controlled?2)345		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346	}347	add_extra_genesis {348		build(|config: &GenesisConfig<T>| {349			// Modification of storage350			for (_num, _c) in &config.collection_id {351				<Module<T>>::init_collection(_c);352			}353354			for (_num, _c, _i) in &config.nft_item_id {355				<Module<T>>::init_nft_token(*_c, _i);356			}357358			for (collection_id, account_id, fungible_item) in &config.fungible_item_id {359				<Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);360			}361362			for (_num, _c, _i) in &config.refungible_item_id {363				<Module<T>>::init_refungible_token(*_c, _i);364			}365		})366	}367}368369decl_event!(370	pub enum Event<T>371	where372		AccountId = <T as frame_system::Config>::AccountId,373		CrossAccountId = <T as Config>::CrossAccountId,374	{375		/// New collection was created376		///377		/// # Arguments378		///379		/// * collection_id: Globally unique identifier of newly created collection.380		///381		/// * mode: [CollectionMode] converted into u8.382		///383		/// * account_id: Collection owner.384		CollectionCreated(CollectionId, u8, AccountId),385386		/// New item was created.387		///388		/// # Arguments389		///390		/// * collection_id: Id of the collection where item was created.391		///392		/// * item_id: Id of an item. Unique within the collection.393		///394		/// * recipient: Owner of newly created item395		ItemCreated(CollectionId, TokenId, CrossAccountId),396397		/// Collection item was burned.398		///399		/// # Arguments400		///401		/// collection_id.402		///403		/// item_id: Identifier of burned NFT.404		ItemDestroyed(CollectionId, TokenId),405406		/// Item was transferred407		///408		/// * collection_id: Id of collection to which item is belong409		///410		/// * item_id: Id of an item411		///412		/// * sender: Original owner of item413		///414		/// * recipient: New owner of item415		///416		/// * amount: Always 1 for NFT417		Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),418419		/// * collection_id420		///421		/// * item_id422		///423		/// * sender424		///425		/// * spender426		///427		/// * amount428		Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),429	}430);431432decl_module! {433	pub struct Module<T: Config> for enum Call434	where435		origin: T::Origin436	{437		fn deposit_event() = default;438		const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;439		type Error = Error<T>;440441		fn on_initialize(_now: T::BlockNumber) -> Weight {442			0443		}444445		/// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.446		///447		/// # Permissions448		///449		/// * Anyone.450		///451		/// # Arguments452		///453		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.454		///455		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.456		///457		/// * token_prefix: UTF-8 string with token prefix.458		///459		/// * mode: [CollectionMode] collection type and type dependent data.460		// returns collection ID461		#[weight = <SelfWeightOf<T>>::create_collection()]462		#[transactional]463		pub fn create_collection(origin,464								 collection_name: Vec<u16>,465								 collection_description: Vec<u16>,466								 token_prefix: Vec<u8>,467								 mode: CollectionMode) -> DispatchResult {468469			// Anyone can create a collection470			let who = ensure_signed(origin)?;471472			// Take a (non-refundable) deposit of collection creation473			let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();474			imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(475				&T::TreasuryAccountId::get(),476				T::CollectionCreationPrice::get(),477			));478			<T as Config>::Currency::settle(479				&who,480				imbalance,481				WithdrawReasons::TRANSFER,482				ExistenceRequirement::KeepAlive,483			).map_err(|_| Error::<T>::NoPermission)?;484485			let decimal_points = match mode {486				CollectionMode::Fungible(points) => points,487				_ => 0488			};489490			let created_count = CreatedCollectionCount::get();491			let destroyed_count = DestroyedCollectionCount::get();492493			// bound Total number of collections494			ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);495496			// check params497			ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);498			ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);499			ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);500			ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);501502			// Generate next collection ID503			let next_id = created_count504				.checked_add(1)505				.ok_or(Error::<T>::NumOverflow)?;506507			CreatedCollectionCount::put(next_id);508509			let limits = CollectionLimits {510				sponsored_data_size: CUSTOM_DATA_LIMIT,511				..Default::default()512			};513514			// Create new collection515			let new_collection = Collection {516				owner: who.clone(),517				name: collection_name,518				mode: mode.clone(),519				mint_mode: false,520				access: AccessMode::Normal,521				description: collection_description,522				decimal_points,523				token_prefix,524				offchain_schema: Vec::new(),525				schema_version: SchemaVersion::ImageURL,526				sponsorship: SponsorshipState::Disabled,527				variable_on_chain_schema: Vec::new(),528				const_on_chain_schema: Vec::new(),529				limits,530				transfers_enabled: true,531			};532533			// Add new collection to map534			<CollectionById<T>>::insert(next_id, new_collection);535536			// call event537			Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));538539			Ok(())540		}541542		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.543		///544		/// # Permissions545		///546		/// * Collection Owner.547		///548		/// # Arguments549		///550		/// * collection_id: collection to destroy.551		#[weight = <SelfWeightOf<T>>::destroy_collection()]552		#[transactional]553		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {554555			let sender = ensure_signed(origin)?;556			let collection = Self::get_collection(collection_id)?;557			Self::check_owner_permissions(&collection, &sender)?;558			if !collection.limits.owner_can_destroy {559				fail!(Error::<T>::NoPermission);560			}561562			<AddressTokens<T>>::remove_prefix(collection_id, None);563			<Allowances<T>>::remove_prefix(collection_id, None);564			<Balance<T>>::remove_prefix(collection_id, None);565			<ItemListIndex>::remove(collection_id);566			<AdminList<T>>::remove(collection_id);567			<CollectionById<T>>::remove(collection_id);568			<WhiteList<T>>::remove_prefix(collection_id, None);569570			<NftItemList<T>>::remove_prefix(collection_id, None);571			<FungibleItemList<T>>::remove_prefix(collection_id, None);572			<ReFungibleItemList<T>>::remove_prefix(collection_id, None);573574			<NftTransferBasket<T>>::remove_prefix(collection_id, None);575			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);576			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);577578			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);579580			DestroyedCollectionCount::put(DestroyedCollectionCount::get()581				.checked_add(1)582				.ok_or(Error::<T>::NumOverflow)?);583584			Ok(())585		}586587		/// Add an address to white list.588		///589		/// # Permissions590		///591		/// * Collection Owner592		/// * Collection Admin593		///594		/// # Arguments595		///596		/// * collection_id.597		///598		/// * address.599		#[weight = <SelfWeightOf<T>>::add_to_white_list()]600		#[transactional]601		pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{602603			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);604			let collection = Self::get_collection(collection_id)?;605606			Self::toggle_white_list_internal(607				&sender,608				&collection,609				&address,610				true,611			)?;612613			Ok(())614		}615616		/// Remove an address from white list.617		///618		/// # Permissions619		///620		/// * Collection Owner621		/// * Collection Admin622		///623		/// # Arguments624		///625		/// * collection_id.626		///627		/// * address.628		#[weight = <SelfWeightOf<T>>::remove_from_white_list()]629		#[transactional]630		pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{631632			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633			let collection = Self::get_collection(collection_id)?;634635			Self::toggle_white_list_internal(636				&sender,637				&collection,638				&address,639				false,640			)?;641642			Ok(())643		}644645		/// Toggle between normal and white list access for the methods with access for `Anyone`.646		///647		/// # Permissions648		///649		/// * Collection Owner.650		///651		/// # Arguments652		///653		/// * collection_id.654		///655		/// * mode: [AccessMode]656		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]657		#[transactional]658		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult659		{660			let sender = ensure_signed(origin)?;661662			let mut target_collection = Self::get_collection(collection_id)?;663			Self::check_owner_permissions(&target_collection, &sender)?;664			target_collection.access = mode;665			target_collection.save()666		}667668		/// Allows Anyone to create tokens if:669		/// * White List is enabled, and670		/// * Address is added to white list, and671		/// * This method was called with True parameter672		///673		/// # Permissions674		/// * Collection Owner675		///676		/// # Arguments677		///678		/// * collection_id.679		///680		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.681		#[weight = <SelfWeightOf<T>>::set_mint_permission()]682		#[transactional]683		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult684		{685			let sender = ensure_signed(origin)?;686687			let mut target_collection = Self::get_collection(collection_id)?;688			Self::check_owner_permissions(&target_collection, &sender)?;689			target_collection.mint_mode = mint_permission;690			target_collection.save()691		}692693		/// Change the owner of the collection.694		///695		/// # Permissions696		///697		/// * Collection Owner.698		///699		/// # Arguments700		///701		/// * collection_id.702		///703		/// * new_owner.704		#[weight = <SelfWeightOf<T>>::change_collection_owner()]705		#[transactional]706		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {707708			let sender = ensure_signed(origin)?;709			let mut target_collection = Self::get_collection(collection_id)?;710			Self::check_owner_permissions(&target_collection, &sender)?;711			target_collection.owner = new_owner;712			target_collection.save()713		}714715		/// Adds an admin of the Collection.716		/// 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.717		///718		/// # Permissions719		///720		/// * Collection Owner.721		/// * Collection Admin.722		///723		/// # Arguments724		///725		/// * collection_id: ID of the Collection to add admin for.726		///727		/// * new_admin_id: Address of new admin to add.728		#[weight = <SelfWeightOf<T>>::add_collection_admin()]729		#[transactional]730		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {731			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);732			let collection = Self::get_collection(collection_id)?;733			Self::check_owner_or_admin_permissions(&collection, &sender)?;734			let mut admin_arr = <AdminList<T>>::get(collection_id);735736			match admin_arr.binary_search(&new_admin_id) {737				Ok(_) => {},738				Err(idx) => {739					ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);740					admin_arr.insert(idx, new_admin_id);741					<AdminList<T>>::insert(collection_id, admin_arr);742				}743			}744			Ok(())745		}746747		/// 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.748		///749		/// # Permissions750		///751		/// * Collection Owner.752		/// * Collection Admin.753		///754		/// # Arguments755		///756		/// * collection_id: ID of the Collection to remove admin for.757		///758		/// * account_id: Address of admin to remove.759		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]760		#[transactional]761		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {762			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763			let collection = Self::get_collection(collection_id)?;764			Self::check_owner_or_admin_permissions(&collection, &sender)?;765			let mut admin_arr = <AdminList<T>>::get(collection_id);766767			if let Ok(idx) = admin_arr.binary_search(&account_id) {768				admin_arr.remove(idx);769				<AdminList<T>>::insert(collection_id, admin_arr);770			}771			Ok(())772		}773774		/// # Permissions775		///776		/// * Collection Owner777		///778		/// # Arguments779		///780		/// * collection_id.781		///782		/// * new_sponsor.783		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]784		#[transactional]785		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {786			let sender = ensure_signed(origin)?;787			let mut target_collection = Self::get_collection(collection_id)?;788			Self::check_owner_permissions(&target_collection, &sender)?;789790			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);791			target_collection.save()792		}793794		/// # Permissions795		///796		/// * Sponsor.797		///798		/// # Arguments799		///800		/// * collection_id.801		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]802		#[transactional]803		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {804			let sender = ensure_signed(origin)?;805806			let mut target_collection = Self::get_collection(collection_id)?;807			ensure!(808				target_collection.sponsorship.pending_sponsor() == Some(&sender),809				Error::<T>::ConfirmUnsetSponsorFail810			);811812			target_collection.sponsorship = SponsorshipState::Confirmed(sender);813			target_collection.save()814		}815816		/// Switch back to pay-per-own-transaction model.817		///818		/// # Permissions819		///820		/// * Collection owner.821		///822		/// # Arguments823		///824		/// * collection_id.825		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]826		#[transactional]827		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {828			let sender = ensure_signed(origin)?;829830			let mut target_collection = Self::get_collection(collection_id)?;831			Self::check_owner_permissions(&target_collection, &sender)?;832833			target_collection.sponsorship = SponsorshipState::Disabled;834			target_collection.save()835		}836837		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.838		///839		/// # Permissions840		///841		/// * Collection Owner.842		/// * Collection Admin.843		/// * Anyone if844		///     * White List is enabled, and845		///     * Address is added to white list, and846		///     * MintPermission is enabled (see SetMintPermission method)847		///848		/// # Arguments849		///850		/// * collection_id: ID of the collection.851		///852		/// * owner: Address, initial owner of the NFT.853		///854		/// * data: Token data to store on chain.855		// #[weight =856		// (130_000_000 as Weight)857		// .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))858		// .saturating_add(RocksDbWeight::get().reads(10 as Weight))859		// .saturating_add(RocksDbWeight::get().writes(8 as Weight))]860861		#[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]862		#[transactional]863		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {864			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);865			let collection = Self::get_collection(collection_id)?;866867			Self::create_item_internal(&sender, &collection, &owner, data)?;868869			collection.submit_logs()870		}871872		/// This method creates multiple items in a collection created with CreateCollection method.873		///874		/// # Permissions875		///876		/// * Collection Owner.877		/// * Collection Admin.878		/// * Anyone if879		///     * White List is enabled, and880		///     * Address is added to white list, and881		///     * MintPermission is enabled (see SetMintPermission method)882		///883		/// # Arguments884		///885		/// * collection_id: ID of the collection.886		///887		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].888		///889		/// * owner: Address, initial owner of the NFT.890		#[weight = <SelfWeightOf<T>>::create_item(items_data.iter()891							   .map(|data| { data.data_size() as u32 })892							   .sum())]893		#[transactional]894		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {895896			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);897			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);898			let collection = Self::get_collection(collection_id)?;899900			Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;901902			collection.submit_logs()903		}904905		// TODO! transaction weight906907		/// Set transfers_enabled value for particular collection908		///909		/// # Permissions910		///911		/// * Collection Owner.912		///913		/// # Arguments914		///915		/// * collection_id: ID of the collection.916		///917		/// * value: New flag value.918		#[weight = <SelfWeightOf<T>>::burn_item()]919		#[transactional]920		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {921922			let sender = ensure_signed(origin)?;923			let mut target_collection = Self::get_collection(collection_id)?;924925			Self::check_owner_permissions(&target_collection, &sender)?;926927			target_collection.transfers_enabled = value;928			target_collection.save()929		}930931		/// Destroys a concrete instance of NFT.932		///933		/// # Permissions934		///935		/// * Collection Owner.936		/// * Collection Admin.937		/// * Current NFT Owner.938		///939		/// # Arguments940		///941		/// * collection_id: ID of the collection.942		///943		/// * item_id: ID of NFT to burn.944		#[weight = <SelfWeightOf<T>>::burn_item()]945		#[transactional]946		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {947948			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);949			let target_collection = Self::get_collection(collection_id)?;950951			Self::burn_item_internal(&sender, &target_collection, item_id, value)?;952953			target_collection.submit_logs()954		}955956		/// Change ownership of the token.957		///958		/// # Permissions959		///960		/// * Collection Owner961		/// * Collection Admin962		/// * Current NFT owner963		///964		/// # Arguments965		///966		/// * recipient: Address of token recipient.967		///968		/// * collection_id.969		///970		/// * item_id: ID of the item971		///     * Non-Fungible Mode: Required.972		///     * Fungible Mode: Ignored.973		///     * Re-Fungible Mode: Required.974		///975		/// * value: Amount to transfer.976		///     * Non-Fungible Mode: Ignored977		///     * Fungible Mode: Must specify transferred amount978		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)979		#[weight = <SelfWeightOf<T>>::transfer()]980		#[transactional]981		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {982			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);983			let collection = Self::get_collection(collection_id)?;984985			Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;986987			collection.submit_logs()988		}989990		/// Set, change, or remove approved address to transfer the ownership of the NFT.991		///992		/// # Permissions993		///994		/// * Collection Owner995		/// * Collection Admin996		/// * Current NFT owner997		///998		/// # Arguments999		///1000		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1001		///1002		/// * collection_id.1003		///1004		/// * item_id: ID of the item.1005		#[weight = <SelfWeightOf<T>>::approve()]1006		#[transactional]1007		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1008			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1009			let collection = Self::get_collection(collection_id)?;10101011			Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10121013			collection.submit_logs()1014		}10151016		/// 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.1017		///1018		/// # Permissions1019		/// * Collection Owner1020		/// * Collection Admin1021		/// * Current NFT owner1022		/// * Address approved by current NFT owner1023		///1024		/// # Arguments1025		///1026		/// * from: Address that owns token.1027		///1028		/// * recipient: Address of token recipient.1029		///1030		/// * collection_id.1031		///1032		/// * item_id: ID of the item.1033		///1034		/// * value: Amount to transfer.1035		#[weight = <SelfWeightOf<T>>::transfer_from()]1036		#[transactional]1037		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1038			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039			let collection = Self::get_collection(collection_id)?;10401041			Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10421043			collection.submit_logs()1044		}1045		// #[weight = 0]1046		//     // let no_perm_mes = "You do not have permissions to modify this collection";1047		//     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1048		//     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1049		//     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10501051		//     // // on_nft_received  call10521053		//     // Self::transfer(origin, collection_id, item_id, new_owner)?;10541055		//     Ok(())1056		// }10571058		/// Set off-chain data schema.1059		///1060		/// # Permissions1061		///1062		/// * Collection Owner1063		/// * Collection Admin1064		///1065		/// # Arguments1066		///1067		/// * collection_id.1068		///1069		/// * schema: String representing the offchain data schema.1070		#[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1071		#[transactional]1072		pub fn set_variable_meta_data (1073			origin,1074			collection_id: CollectionId,1075			item_id: TokenId,1076			data: Vec<u8>1077		) -> DispatchResult {1078			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10791080			let collection = Self::get_collection(collection_id)?;10811082			Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10831084			Ok(())1085		}10861087		/// Set schema standard1088		/// ImageURL1089		/// Unique1090		///1091		/// # Permissions1092		///1093		/// * Collection Owner1094		/// * Collection Admin1095		///1096		/// # Arguments1097		///1098		/// * collection_id.1099		///1100		/// * schema: SchemaVersion: enum1101		#[weight = <SelfWeightOf<T>>::set_schema_version()]1102		#[transactional]1103		pub fn set_schema_version(1104			origin,1105			collection_id: CollectionId,1106			version: SchemaVersion1107		) -> DispatchResult {1108			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1109			let mut target_collection = Self::get_collection(collection_id)?;1110			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1111			target_collection.schema_version = version;1112			target_collection.save()1113		}11141115		/// Set off-chain data schema.1116		///1117		/// # Permissions1118		///1119		/// * Collection Owner1120		/// * Collection Admin1121		///1122		/// # Arguments1123		///1124		/// * collection_id.1125		///1126		/// * schema: String representing the offchain data schema.1127		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1128		#[transactional]1129		pub fn set_offchain_schema(1130			origin,1131			collection_id: CollectionId,1132			schema: Vec<u8>1133		) -> DispatchResult {1134			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1135			let mut target_collection = Self::get_collection(collection_id)?;1136			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11371138			// check schema limit1139			ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11401141			target_collection.offchain_schema = schema;1142			target_collection.save()1143		}11441145		/// Set const on-chain data schema.1146		///1147		/// # Permissions1148		///1149		/// * Collection Owner1150		/// * Collection Admin1151		///1152		/// # Arguments1153		///1154		/// * collection_id.1155		///1156		/// * schema: String representing the const on-chain data schema.1157		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1158		#[transactional]1159		pub fn set_const_on_chain_schema (1160			origin,1161			collection_id: CollectionId,1162			schema: Vec<u8>1163		) -> DispatchResult {1164			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1165			let mut target_collection = Self::get_collection(collection_id)?;1166			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11671168			// check schema limit1169			ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11701171			target_collection.const_on_chain_schema = schema;1172			target_collection.save()1173		}11741175		/// Set variable on-chain data schema.1176		///1177		/// # Permissions1178		///1179		/// * Collection Owner1180		/// * Collection Admin1181		///1182		/// # Arguments1183		///1184		/// * collection_id.1185		///1186		/// * schema: String representing the variable on-chain data schema.1187		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1188		#[transactional]1189		pub fn set_variable_on_chain_schema (1190			origin,1191			collection_id: CollectionId,1192			schema: Vec<u8>1193		) -> DispatchResult {1194			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1195			let mut target_collection = Self::get_collection(collection_id)?;1196			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11971198			// check schema limit1199			ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12001201			target_collection.variable_on_chain_schema = schema;1202			target_collection.save()1203		}12041205		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1206		#[transactional]1207		pub fn set_collection_limits(1208			origin,1209			collection_id: u32,1210			new_limits: CollectionLimits<T::BlockNumber>,1211		) -> DispatchResult {1212			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1213			let mut target_collection = Self::get_collection(collection_id)?;1214			Self::check_owner_permissions(&target_collection, sender.as_sub())?;1215			let old_limits = &target_collection.limits;12161217			// collection bounds1218			ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1219				new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1220				new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1221				Error::<T>::CollectionLimitBoundsExceeded);12221223			// token_limit   check  prev1224			ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1225			ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12261227			ensure!(1228				(old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1229				(old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1230				Error::<T>::OwnerPermissionsCantBeReverted,1231			);12321233			target_collection.limits = new_limits;12341235			target_collection.save()1236		}1237	}1238}12391240impl<T: Config> Module<T> {1241	pub fn create_item_internal(1242		sender: &T::CrossAccountId,1243		collection: &CollectionHandle<T>,1244		owner: &T::CrossAccountId,1245		data: CreateItemData,1246	) -> DispatchResult {1247		Self::can_create_items_in_collection(collection, sender, owner, 1)?;1248		Self::validate_create_item_args(collection, &data)?;1249		Self::create_item_no_validation(collection, owner, data)?;12501251		Ok(())1252	}12531254	pub fn transfer_internal(1255		sender: &T::CrossAccountId,1256		recipient: &T::CrossAccountId,1257		target_collection: &CollectionHandle<T>,1258		item_id: TokenId,1259		value: u128,1260	) -> DispatchResult {1261		target_collection.consume_gas(2000000)?;1262		// Limits check1263		Self::is_correct_transfer(target_collection, recipient)?;12641265		// Transfer permissions check1266		ensure!(1267			Self::is_item_owner(sender, target_collection, item_id)1268				|| Self::is_owner_or_admin_permissions(target_collection, sender),1269			Error::<T>::NoPermission1270		);12711272		if target_collection.access == AccessMode::WhiteList {1273			Self::check_white_list(target_collection, sender)?;1274			Self::check_white_list(target_collection, recipient)?;1275		}12761277		match target_collection.mode {1278			CollectionMode::NFT => Self::transfer_nft(1279				target_collection,1280				item_id,1281				sender.clone(),1282				recipient.clone(),1283			)?,1284			CollectionMode::Fungible(_) => {1285				Self::transfer_fungible(target_collection, value, sender, recipient)?1286			}1287			CollectionMode::ReFungible => Self::transfer_refungible(1288				target_collection,1289				item_id,1290				value,1291				sender.clone(),1292				recipient.clone(),1293			)?,1294			_ => (),1295		};12961297		Self::deposit_event(RawEvent::Transfer(1298			target_collection.id,1299			item_id,1300			sender.clone(),1301			recipient.clone(),1302			value,1303		));13041305		Ok(())1306	}13071308	pub fn approve_internal(1309		sender: &T::CrossAccountId,1310		spender: &T::CrossAccountId,1311		collection: &CollectionHandle<T>,1312		item_id: TokenId,1313		amount: u128,1314	) -> DispatchResult {1315		collection.consume_gas(2000000)?;1316		Self::token_exists(collection, item_id)?;13171318		// Transfer permissions check1319		let bypasses_limits = collection.limits.owner_can_transfer1320			&& Self::is_owner_or_admin_permissions(collection, sender);13211322		let allowance_limit = if bypasses_limits {1323			None1324		} else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1325			Some(amount)1326		} else {1327			fail!(Error::<T>::NoPermission);1328		};13291330		if collection.access == AccessMode::WhiteList {1331			Self::check_white_list(collection, sender)?;1332			Self::check_white_list(collection, spender)?;1333		}13341335		let allowance: u128 = amount1336			.checked_add(<Allowances<T>>::get(1337				collection.id,1338				(item_id, sender.as_sub(), spender.as_sub()),1339			))1340			.ok_or(Error::<T>::NumOverflow)?;1341		if let Some(limit) = allowance_limit {1342			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1343		}1344		<Allowances<T>>::insert(1345			collection.id,1346			(item_id, sender.as_sub(), spender.as_sub()),1347			allowance,1348		);13491350		if matches!(collection.mode, CollectionMode::NFT) {1351			// TODO: NFT: only one owner may exist for token in ERC7211352			collection.log(ERC721Events::Approval {1353				owner: *sender.as_eth(),1354				approved: *spender.as_eth(),1355				token_id: item_id.into(),1356			})?;1357		}13581359		if matches!(collection.mode, CollectionMode::Fungible(_)) {1360			// TODO: NFT: only one owner may exist for token in ERC201361			collection.log(ERC20Events::Approval {1362				owner: *sender.as_eth(),1363				spender: *spender.as_eth(),1364				value: allowance.into(),1365			})?;1366		}13671368		Self::deposit_event(RawEvent::Approved(1369			collection.id,1370			item_id,1371			sender.clone(),1372			spender.clone(),1373			allowance,1374		));1375		Ok(())1376	}13771378	pub fn transfer_from_internal(1379		sender: &T::CrossAccountId,1380		from: &T::CrossAccountId,1381		recipient: &T::CrossAccountId,1382		collection: &CollectionHandle<T>,1383		item_id: TokenId,1384		amount: u128,1385	) -> DispatchResult {1386		collection.consume_gas(2000000)?;1387		// Check approval1388		let approval: u128 =1389			<Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13901391		// Limits check1392		Self::is_correct_transfer(collection, recipient)?;13931394		// Transfer permissions check1395		ensure!(1396			approval >= amount1397				|| (collection.limits.owner_can_transfer1398					&& Self::is_owner_or_admin_permissions(collection, sender)),1399			Error::<T>::NoPermission1400		);14011402		if collection.access == AccessMode::WhiteList {1403			Self::check_white_list(collection, sender)?;1404			Self::check_white_list(collection, recipient)?;1405		}14061407		// Reduce approval by transferred amount or remove if remaining approval drops to 01408		let allowance = approval.saturating_sub(amount);1409		if allowance > 0 {1410			<Allowances<T>>::insert(1411				collection.id,1412				(item_id, from.as_sub(), sender.as_sub()),1413				allowance,1414			);1415		} else {1416			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1417		}14181419		match collection.mode {1420			CollectionMode::NFT => {1421				Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1422			}1423			CollectionMode::Fungible(_) => {1424				Self::transfer_fungible(collection, amount, from, recipient)?1425			}1426			CollectionMode::ReFungible => Self::transfer_refungible(1427				collection,1428				item_id,1429				amount,1430				from.clone(),1431				recipient.clone(),1432			)?,1433			_ => (),1434		};14351436		if matches!(collection.mode, CollectionMode::Fungible(_)) {1437			collection.log(ERC20Events::Approval {1438				owner: *from.as_eth(),1439				spender: *sender.as_eth(),1440				value: allowance.into(),1441			})?;1442		}14431444		Ok(())1445	}14461447	pub fn set_variable_meta_data_internal(1448		sender: &T::CrossAccountId,1449		collection: &CollectionHandle<T>,1450		item_id: TokenId,1451		data: Vec<u8>,1452	) -> DispatchResult {1453		Self::token_exists(collection, item_id)?;14541455		ensure!(1456			CUSTOM_DATA_LIMIT >= data.len() as u32,1457			Error::<T>::TokenVariableDataLimitExceeded1458		);14591460		// Modify permissions check1461		ensure!(1462			Self::is_item_owner(sender, collection, item_id)1463				|| Self::is_owner_or_admin_permissions(collection, sender),1464			Error::<T>::NoPermission1465		);14661467		match collection.mode {1468			CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1469			CollectionMode::ReFungible => {1470				Self::set_re_fungible_variable_data(collection, item_id, data)?1471			}1472			CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1473			_ => fail!(Error::<T>::UnexpectedCollectionType),1474		};14751476		Ok(())1477	}14781479	pub fn create_multiple_items_internal(1480		sender: &T::CrossAccountId,1481		collection: &CollectionHandle<T>,1482		owner: &T::CrossAccountId,1483		items_data: Vec<CreateItemData>,1484	) -> DispatchResult {1485		Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14861487		for data in &items_data {1488			Self::validate_create_item_args(collection, data)?;1489		}1490		for data in &items_data {1491			Self::create_item_no_validation(collection, owner, data.clone())?;1492		}14931494		Ok(())1495	}14961497	pub fn burn_item_internal(1498		sender: &T::CrossAccountId,1499		collection: &CollectionHandle<T>,1500		item_id: TokenId,1501		value: u128,1502	) -> DispatchResult {1503		ensure!(1504			Self::is_item_owner(sender, collection, item_id)1505				|| (collection.limits.owner_can_transfer1506					&& Self::is_owner_or_admin_permissions(collection, sender)),1507			Error::<T>::NoPermission1508		);15091510		if collection.access == AccessMode::WhiteList {1511			Self::check_white_list(collection, sender)?;1512		}15131514		match collection.mode {1515			CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1516			CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1517			CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1518			_ => (),1519		};15201521		Ok(())1522	}15231524	pub fn toggle_white_list_internal(1525		sender: &T::CrossAccountId,1526		collection: &CollectionHandle<T>,1527		address: &T::CrossAccountId,1528		whitelisted: bool,1529	) -> DispatchResult {1530		Self::check_owner_or_admin_permissions(collection, sender)?;15311532		if whitelisted {1533			<WhiteList<T>>::insert(collection.id, address.as_sub(), true);1534		} else {1535			<WhiteList<T>>::remove(collection.id, address.as_sub());1536		}15371538		Ok(())1539	}15401541	fn is_correct_transfer(1542		collection: &CollectionHandle<T>,1543		recipient: &T::CrossAccountId,1544	) -> DispatchResult {1545		let collection_id = collection.id;15461547		// check token limit and account token limit1548		let account_items: u32 =1549			<AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1550		ensure!(1551			collection.limits.account_token_ownership_limit > account_items,1552			Error::<T>::AccountTokenLimitExceeded1553		);15541555		// preliminary transfer check1556		ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15571558		Ok(())1559	}15601561	fn can_create_items_in_collection(1562		collection: &CollectionHandle<T>,1563		sender: &T::CrossAccountId,1564		owner: &T::CrossAccountId,1565		amount: u32,1566	) -> DispatchResult {1567		let collection_id = collection.id;15681569		// check token limit and account token limit1570		let total_items: u32 = ItemListIndex::get(collection_id)1571			.checked_add(amount)1572			.ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1573		let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1574			as u32)1575			.checked_add(amount)1576			.ok_or(Error::<T>::AccountTokenLimitExceeded)?;1577		ensure!(1578			collection.limits.token_limit >= total_items,1579			Error::<T>::CollectionTokenLimitExceeded1580		);1581		ensure!(1582			collection.limits.account_token_ownership_limit >= account_items,1583			Error::<T>::AccountTokenLimitExceeded1584		);15851586		if !Self::is_owner_or_admin_permissions(collection, sender) {1587			ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1588			Self::check_white_list(collection, owner)?;1589			Self::check_white_list(collection, sender)?;1590		}15911592		Ok(())1593	}15941595	fn validate_create_item_args(1596		target_collection: &CollectionHandle<T>,1597		data: &CreateItemData,1598	) -> DispatchResult {1599		match target_collection.mode {1600			CollectionMode::NFT => {1601				if !matches!(data, CreateItemData::NFT(_)) {1602					fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1603				}1604			}1605			CollectionMode::Fungible(_) => {1606				if !matches!(data, CreateItemData::Fungible(_)) {1607					fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1608				}1609			}1610			CollectionMode::ReFungible => {1611				if let CreateItemData::ReFungible(data) = data {1612					// Check refungibility limits1613					ensure!(1614						data.pieces <= MAX_REFUNGIBLE_PIECES,1615						Error::<T>::WrongRefungiblePieces1616					);1617					ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1618				} else {1619					fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1620				}1621			}1622			_ => {1623				fail!(Error::<T>::UnexpectedCollectionType);1624			}1625		};16261627		Ok(())1628	}16291630	fn create_item_no_validation(1631		collection: &CollectionHandle<T>,1632		owner: &T::CrossAccountId,1633		data: CreateItemData,1634	) -> DispatchResult {1635		match data {1636			CreateItemData::NFT(data) => {1637				let item = NftItemType {1638					owner: owner.clone(),1639					const_data: data.const_data.into_inner(),1640					variable_data: data.variable_data.into_inner(),1641				};16421643				Self::add_nft_item(collection, item)?;1644			}1645			CreateItemData::Fungible(data) => {1646				Self::add_fungible_item(collection, owner, data.value)?;1647			}1648			CreateItemData::ReFungible(data) => {1649				let owner_list = vec![Ownership {1650					owner: owner.clone(),1651					fraction: data.pieces,1652				}];16531654				let item = ReFungibleItemType {1655					owner: owner_list,1656					const_data: data.const_data.into_inner(),1657					variable_data: data.variable_data.into_inner(),1658				};16591660				Self::add_refungible_item(collection, item)?;1661			}1662		};16631664		Ok(())1665	}16661667	fn add_fungible_item(1668		collection: &CollectionHandle<T>,1669		owner: &T::CrossAccountId,1670		value: u128,1671	) -> DispatchResult {1672		let collection_id = collection.id;16731674		// Does new owner already have an account?1675		let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16761677		// Mint1678		let item = FungibleItemType {1679			value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1680		};1681		<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16821683		// Update balance1684		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1685			.checked_add(value)1686			.ok_or(Error::<T>::NumOverflow)?;1687		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16881689		Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1690		Ok(())1691	}16921693	fn add_refungible_item(1694		collection: &CollectionHandle<T>,1695		item: ReFungibleItemType<T::CrossAccountId>,1696	) -> DispatchResult {1697		let collection_id = collection.id;16981699		let current_index = <ItemListIndex>::get(collection_id)1700			.checked_add(1)1701			.ok_or(Error::<T>::NumOverflow)?;1702		let itemcopy = item.clone();17031704		ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1705		let item_owner = item.owner.first().expect("only one owner is defined");17061707		let value = item_owner.fraction;1708		let owner = item_owner.owner.clone();17091710		Self::add_token_index(collection_id, current_index, &owner)?;17111712		<ItemListIndex>::insert(collection_id, current_index);1713		<ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17141715		// Update balance1716		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1717			.checked_add(value)1718			.ok_or(Error::<T>::NumOverflow)?;1719		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17201721		Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1722		Ok(())1723	}17241725	fn add_nft_item(1726		collection: &CollectionHandle<T>,1727		item: NftItemType<T::CrossAccountId>,1728	) -> DispatchResult {1729		let collection_id = collection.id;17301731		let current_index = <ItemListIndex>::get(collection_id)1732			.checked_add(1)1733			.ok_or(Error::<T>::NumOverflow)?;17341735		let item_owner = item.owner.clone();1736		Self::add_token_index(collection_id, current_index, &item.owner)?;17371738		<ItemListIndex>::insert(collection_id, current_index);1739		<NftItemList<T>>::insert(collection_id, current_index, item);17401741		// Update balance1742		let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1743			.checked_add(1)1744			.ok_or(Error::<T>::NumOverflow)?;1745		<Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17461747		collection.log(ERC721Events::Transfer {1748			from: H160::default(),1749			to: *item_owner.as_eth(),1750			token_id: current_index.into(),1751		})?;1752		Self::deposit_event(RawEvent::ItemCreated(1753			collection_id,1754			current_index,1755			item_owner,1756		));1757		Ok(())1758	}17591760	fn burn_refungible_item(1761		collection: &CollectionHandle<T>,1762		item_id: TokenId,1763		owner: &T::CrossAccountId,1764	) -> DispatchResult {1765		let collection_id = collection.id;17661767		let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1768			.ok_or(Error::<T>::TokenNotFound)?;1769		let rft_balance = token1770			.owner1771			.iter()1772			.find(|&i| i.owner == *owner)1773			.ok_or(Error::<T>::TokenNotFound)?;1774		Self::remove_token_index(collection_id, item_id, owner)?;17751776		// update balance1777		let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1778			.checked_sub(rft_balance.fraction)1779			.ok_or(Error::<T>::NumOverflow)?;1780		<Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17811782		// Re-create owners list with sender removed1783		let index = token1784			.owner1785			.iter()1786			.position(|i| i.owner == *owner)1787			.expect("owned item is exists");1788		token.owner.remove(index);1789		let owner_count = token.owner.len();17901791		// Burn the token completely if this was the last (only) owner1792		if owner_count == 0 {1793			<ReFungibleItemList<T>>::remove(collection_id, item_id);1794			<VariableMetaDataBasket<T>>::remove(collection_id, item_id);1795		} else {1796			<ReFungibleItemList<T>>::insert(collection_id, item_id, token);1797		}17981799		Ok(())1800	}18011802	fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1803		let collection_id = collection.id;18041805		let item =1806			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1807		Self::remove_token_index(collection_id, item_id, &item.owner)?;18081809		// update balance1810		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1811			.checked_sub(1)1812			.ok_or(Error::<T>::NumOverflow)?;1813		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1814		<NftItemList<T>>::remove(collection_id, item_id);1815		<VariableMetaDataBasket<T>>::remove(collection_id, item_id);18161817		Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1818		Ok(())1819	}18201821	fn burn_fungible_item(1822		owner: &T::CrossAccountId,1823		collection: &CollectionHandle<T>,1824		value: u128,1825	) -> DispatchResult {1826		let collection_id = collection.id;18271828		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1829		ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18301831		// update balance1832		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1833			.checked_sub(value)1834			.ok_or(Error::<T>::NumOverflow)?;1835		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18361837		if balance.value - value > 0 {1838			balance.value -= value;1839			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1840		} else {1841			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1842		}18431844		collection.log(ERC20Events::Transfer {1845			from: *owner.as_eth(),1846			to: H160::default(),1847			value: value.into(),1848		})?;1849		Ok(())1850	}18511852	pub fn get_collection(1853		collection_id: CollectionId,1854	) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1855		Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1856	}18571858	fn check_owner_permissions(1859		target_collection: &CollectionHandle<T>,1860		subject: &T::AccountId,1861	) -> DispatchResult {1862		ensure!(1863			*subject == target_collection.owner,1864			Error::<T>::NoPermission1865		);18661867		Ok(())1868	}18691870	fn is_owner_or_admin_permissions(1871		collection: &CollectionHandle<T>,1872		subject: &T::CrossAccountId,1873	) -> bool {1874		*subject.as_sub() == collection.owner1875			|| <AdminList<T>>::get(collection.id).contains(subject)1876	}18771878	fn check_owner_or_admin_permissions(1879		collection: &CollectionHandle<T>,1880		subject: &T::CrossAccountId,1881	) -> DispatchResult {1882		ensure!(1883			Self::is_owner_or_admin_permissions(collection, subject),1884			Error::<T>::NoPermission1885		);18861887		Ok(())1888	}18891890	fn owned_amount(1891		subject: &T::CrossAccountId,1892		target_collection: &CollectionHandle<T>,1893		item_id: TokenId,1894	) -> Option<u128> {1895		let collection_id = target_collection.id;18961897		match target_collection.mode {1898			CollectionMode::NFT => {1899				(<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1900			}1901			CollectionMode::Fungible(_) => {1902				Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1903			}1904			CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1905				.owner1906				.iter()1907				.find(|i| i.owner == *subject)1908				.map(|i| i.fraction),1909			CollectionMode::Invalid => None,1910		}1911	}19121913	fn is_item_owner(1914		subject: &T::CrossAccountId,1915		target_collection: &CollectionHandle<T>,1916		item_id: TokenId,1917	) -> bool {1918		match target_collection.mode {1919			CollectionMode::Fungible(_) => true,1920			_ => Self::owned_amount(subject, target_collection, item_id).is_some(),1921		}1922	}19231924	fn check_white_list(1925		collection: &CollectionHandle<T>,1926		address: &T::CrossAccountId,1927	) -> DispatchResult {1928		let collection_id = collection.id;19291930		let mes = Error::<T>::AddresNotInWhiteList;1931		ensure!(1932			<WhiteList<T>>::contains_key(collection_id, address.as_sub()),1933			mes1934		);19351936		Ok(())1937	}19381939	/// Check if token exists. In case of Fungible, check if there is an entry for1940	/// the owner in fungible balances double map1941	fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1942		let collection_id = target_collection.id;1943		let exists = match target_collection.mode {1944			CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1945			CollectionMode::Fungible(_) => true,1946			CollectionMode::ReFungible => {1947				<ReFungibleItemList<T>>::contains_key(collection_id, item_id)1948			}1949			_ => false,1950		};19511952		ensure!(exists, Error::<T>::TokenNotFound);1953		Ok(())1954	}19551956	fn transfer_fungible(1957		collection: &CollectionHandle<T>,1958		value: u128,1959		owner: &T::CrossAccountId,1960		recipient: &T::CrossAccountId,1961	) -> DispatchResult {1962		let collection_id = collection.id;19631964		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1965		ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19661967		// Send balance to recipient (updates balanceOf of recipient)1968		Self::add_fungible_item(collection, recipient, value)?;19691970		// update balanceOf of sender1971		<Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19721973		// Reduce or remove sender1974		if balance.value == value {1975			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1976		} else {1977			balance.value -= value;1978			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1979		}19801981		collection.log(ERC20Events::Transfer {1982			from: *owner.as_eth(),1983			to: *recipient.as_eth(),1984			value: value.into(),1985		})?;1986		Self::deposit_event(RawEvent::Transfer(1987			collection.id,1988			1,1989			owner.clone(),1990			recipient.clone(),1991			value,1992		));19931994		Ok(())1995	}19961997	fn transfer_refungible(1998		collection: &CollectionHandle<T>,1999		item_id: TokenId,2000		value: u128,2001		owner: T::CrossAccountId,2002		new_owner: T::CrossAccountId,2003	) -> DispatchResult {2004		let collection_id = collection.id;2005		let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2006			.ok_or(Error::<T>::TokenNotFound)?;20072008		let item = full_item2009			.owner2010			.iter()2011			.find(|i| i.owner == owner)2012			.ok_or(Error::<T>::TokenNotFound)?;2013		let amount = item.fraction;20142015		ensure!(amount >= value, Error::<T>::TokenValueTooLow);20162017		// update balance2018		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2019			.checked_sub(value)2020			.ok_or(Error::<T>::NumOverflow)?;2021		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20222023		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2024			.checked_add(value)2025			.ok_or(Error::<T>::NumOverflow)?;2026		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20272028		let old_owner = item.owner.clone();2029		let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20302031		let mut new_full_item = full_item.clone();2032		// transfer2033		if amount == value && !new_owner_has_account {2034			// change owner2035			// new owner do not have account2036			new_full_item2037				.owner2038				.iter_mut()2039				.find(|i| i.owner == owner)2040				.expect("old owner does present in refungible")2041				.owner = new_owner.clone();2042			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20432044			// update index collection2045			Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2046		} else {2047			new_full_item2048				.owner2049				.iter_mut()2050				.find(|i| i.owner == owner)2051				.expect("old owner does present in refungible")2052				.fraction -= value;20532054			// separate amount2055			if new_owner_has_account {2056				// new owner has account2057				new_full_item2058					.owner2059					.iter_mut()2060					.find(|i| i.owner == new_owner)2061					.expect("new owner has account")2062					.fraction += value;2063			} else {2064				// new owner do not have account2065				new_full_item.owner.push(Ownership {2066					owner: new_owner.clone(),2067					fraction: value,2068				});2069				Self::add_token_index(collection_id, item_id, &new_owner)?;2070			}20712072			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2073		}20742075		Self::deposit_event(RawEvent::Transfer(2076			collection.id,2077			item_id,2078			owner,2079			new_owner,2080			amount,2081		));20822083		Ok(())2084	}20852086	fn transfer_nft(2087		collection: &CollectionHandle<T>,2088		item_id: TokenId,2089		sender: T::CrossAccountId,2090		new_owner: T::CrossAccountId,2091	) -> DispatchResult {2092		let collection_id = collection.id;2093		let mut item =2094			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20952096		ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20972098		// update balance2099		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2100			.checked_sub(1)2101			.ok_or(Error::<T>::NumOverflow)?;2102		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21032104		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2105			.checked_add(1)2106			.ok_or(Error::<T>::NumOverflow)?;2107		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21082109		// change owner2110		let old_owner = item.owner.clone();2111		item.owner = new_owner.clone();2112		<NftItemList<T>>::insert(collection_id, item_id, item);21132114		// update index collection2115		Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21162117		collection.log(ERC721Events::Transfer {2118			from: *sender.as_eth(),2119			to: *new_owner.as_eth(),2120			token_id: item_id.into(),2121		})?;2122		Self::deposit_event(RawEvent::Transfer(2123			collection.id,2124			item_id,2125			sender,2126			new_owner,2127			1,2128		));21292130		Ok(())2131	}21322133	fn set_re_fungible_variable_data(2134		collection: &CollectionHandle<T>,2135		item_id: TokenId,2136		data: Vec<u8>,2137	) -> DispatchResult {2138		let collection_id = collection.id;2139		let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2140			.ok_or(Error::<T>::TokenNotFound)?;21412142		item.variable_data = data;21432144		<ReFungibleItemList<T>>::insert(collection_id, item_id, item);21452146		Ok(())2147	}21482149	fn set_nft_variable_data(2150		collection: &CollectionHandle<T>,2151		item_id: TokenId,2152		data: Vec<u8>,2153	) -> DispatchResult {2154		let collection_id = collection.id;2155		let mut item =2156			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21572158		item.variable_data = data;21592160		<NftItemList<T>>::insert(collection_id, item_id, item);21612162		Ok(())2163	}21642165	#[allow(dead_code)]2166	fn init_collection(item: &Collection<T>) {2167		// check params2168		assert!(2169			item.decimal_points <= MAX_DECIMAL_POINTS,2170			"decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2171		);2172		assert!(2173			item.name.len() <= 64,2174			"Collection name can not be longer than 63 char"2175		);2176		assert!(2177			item.name.len() <= 256,2178			"Collection description can not be longer than 255 char"2179		);2180		assert!(2181			item.token_prefix.len() <= 16,2182			"Token prefix can not be longer than 15 char"2183		);21842185		// Generate next collection ID2186		let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21872188		CreatedCollectionCount::put(next_id);2189	}21902191	#[allow(dead_code)]2192	fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2193		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21942195		Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21962197		<ItemListIndex>::insert(collection_id, current_index);21982199		// Update balance2200		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2201			.checked_add(1)2202			.unwrap();2203		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2204	}22052206	#[allow(dead_code)]2207	fn init_fungible_token(2208		collection_id: CollectionId,2209		owner: &T::CrossAccountId,2210		item: &FungibleItemType,2211	) {2212		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22132214		Self::add_token_index(collection_id, current_index, owner).unwrap();22152216		<ItemListIndex>::insert(collection_id, current_index);22172218		// Update balance2219		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2220			.checked_add(item.value)2221			.unwrap();2222		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2223	}22242225	#[allow(dead_code)]2226	fn init_refungible_token(2227		collection_id: CollectionId,2228		item: &ReFungibleItemType<T::CrossAccountId>,2229	) {2230		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22312232		let value = item.owner.first().unwrap().fraction;2233		let owner = item.owner.first().unwrap().owner.clone();22342235		Self::add_token_index(collection_id, current_index, &owner).unwrap();22362237		<ItemListIndex>::insert(collection_id, current_index);22382239		// Update balance2240		let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2241			.checked_add(value)2242			.unwrap();2243		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2244	}22452246	fn add_token_index(2247		collection_id: CollectionId,2248		item_index: TokenId,2249		owner: &T::CrossAccountId,2250	) -> DispatchResult {2251		// add to account limit2252		if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2253			// bound Owned tokens by a single address2254			let count = <AccountItemCount<T>>::get(owner.as_sub());2255			ensure!(2256				count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2257				Error::<T>::AddressOwnershipLimitExceeded2258			);22592260			<AccountItemCount<T>>::insert(2261				owner.as_sub(),2262				count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2263			);2264		} else {2265			<AccountItemCount<T>>::insert(owner.as_sub(), 1);2266		}22672268		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2269		if list_exists {2270			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2271			let item_contains = list.contains(&item_index.clone());22722273			if !item_contains {2274				list.push(item_index);2275			}22762277			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2278		} else {2279			let itm = vec![item_index];2280			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2281		}22822283		Ok(())2284	}22852286	fn remove_token_index(2287		collection_id: CollectionId,2288		item_index: TokenId,2289		owner: &T::CrossAccountId,2290	) -> DispatchResult {2291		// update counter2292		<AccountItemCount<T>>::insert(2293			owner.as_sub(),2294			<AccountItemCount<T>>::get(owner.as_sub())2295				.checked_sub(1)2296				.ok_or(Error::<T>::NumOverflow)?,2297		);22982299		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2300		if list_exists {2301			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2302			let item_contains = list.contains(&item_index.clone());23032304			if item_contains {2305				list.retain(|&item| item != item_index);2306				<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2307			}2308		}23092310		Ok(())2311	}23122313	fn move_token_index(2314		collection_id: CollectionId,2315		item_index: TokenId,2316		old_owner: &T::CrossAccountId,2317		new_owner: &T::CrossAccountId,2318	) -> DispatchResult {2319		Self::remove_token_index(collection_id, item_index, old_owner)?;2320		Self::add_token_index(collection_id, item_index, new_owner)?;23212322		Ok(())2323	}2324}23252326sp_api::decl_runtime_apis! {2327	pub trait NftApi {2328		/// Used for ethereum integration2329		fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2330	}2331}
addedpallets/nft/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft/src/weights.rs
@@ -0,0 +1,362 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_nft
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
+//! DATE: 2021-08-30, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128
+
+// Executed Command:
+// target/release/nft
+// benchmark
+// --pallet
+// pallet-nft
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=20
+// --output=./pallets/nft/src/weights.rs
+
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_nft.
+pub trait WeightInfo {
+	fn create_collection() -> Weight;
+	fn destroy_collection() -> Weight;
+	fn add_to_white_list() -> Weight;
+	fn remove_from_white_list() -> Weight;
+	fn set_public_access_mode() -> Weight;
+	fn set_mint_permission() -> Weight;
+	fn change_collection_owner() -> Weight;
+	fn add_collection_admin() -> Weight;
+	fn remove_collection_admin() -> Weight;
+	fn set_collection_sponsor() -> Weight;
+	fn confirm_sponsorship() -> Weight;
+	fn remove_collection_sponsor() -> Weight;
+	fn create_item_nft(b: u32, ) -> Weight;
+	fn create_item_fungible() -> Weight;
+	fn create_item_refungible(b: u32, ) -> Weight;
+	fn burn_item_nft() -> Weight;
+	fn transfer_nft() -> Weight;
+	fn transfer_fungible() -> Weight;
+	fn transfer_refungible() -> Weight;
+	fn approve_nft() -> Weight;
+	fn transfer_from_nft() -> Weight;
+	fn transfer_from_fungible() -> Weight;
+	fn transfer_from_refungible() -> Weight;
+	fn set_offchain_schema(b: u32, ) -> Weight;
+	fn set_const_on_chain_schema(b: u32, ) -> Weight;
+	fn set_variable_on_chain_schema(b: u32, ) -> Weight;
+	fn set_variable_meta_data_nft(b: u32, ) -> Weight;
+	fn set_schema_version() -> Weight;
+	fn set_collection_limits() -> Weight;
+}
+
+/// Weights for pallet_nft using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	fn create_collection() -> Weight {
+		(25_851_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(8 as Weight))
+			.saturating_add(T::DbWeight::get().writes(6 as Weight))
+	}
+	fn destroy_collection() -> Weight {
+		(28_737_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn add_to_white_list() -> Weight {
+		(6_237_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_from_white_list() -> Weight {
+		(6_252_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_public_access_mode() -> Weight {
+		(6_691_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_mint_permission() -> Weight {
+		(6_630_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn change_collection_owner() -> Weight {
+		(6_521_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn add_collection_admin() -> Weight {
+		(8_057_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_admin() -> Weight {
+		(8_307_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_sponsor() -> Weight {
+		(6_484_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn confirm_sponsorship() -> Weight {
+		(6_530_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_sponsor() -> Weight {
+		(6_733_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn create_item_nft(_b: u32, ) -> Weight {
+		(167_909_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn create_item_fungible() -> Weight {
+		(22_331_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn create_item_refungible(_b: u32, ) -> Weight {
+		(26_293_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn burn_item_nft() -> Weight {
+		(32_237_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_nft() -> Weight {
+		(192_578_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(14 as Weight))
+			.saturating_add(T::DbWeight::get().writes(10 as Weight))
+	}
+	fn transfer_fungible() -> Weight {
+		(170_749_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_refungible() -> Weight {
+		(35_949_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(10 as Weight))
+			.saturating_add(T::DbWeight::get().writes(7 as Weight))
+	}
+	fn approve_nft() -> Weight {
+		(169_825_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+	}
+	fn transfer_from_nft() -> Weight {
+		(197_912_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(15 as Weight))
+			.saturating_add(T::DbWeight::get().writes(11 as Weight))
+	}
+	fn transfer_from_fungible() -> Weight {
+		(183_789_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(12 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn transfer_from_refungible() -> Weight {
+		(37_149_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(11 as Weight))
+			.saturating_add(T::DbWeight::get().writes(8 as Weight))
+	}
+	fn set_offchain_schema(_b: u32, ) -> Weight {
+		(6_435_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+		(6_646_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+		(6_542_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+		(14_697_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_schema_version() -> Weight {
+		(6_566_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_limits() -> Weight {
+		(6_349_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn create_collection() -> Weight {
+		(25_851_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(8 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(6 as Weight))
+	}
+	fn destroy_collection() -> Weight {
+		(28_737_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn add_to_white_list() -> Weight {
+		(6_237_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_from_white_list() -> Weight {
+		(6_252_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_public_access_mode() -> Weight {
+		(6_691_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_mint_permission() -> Weight {
+		(6_630_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn change_collection_owner() -> Weight {
+		(6_521_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn add_collection_admin() -> Weight {
+		(8_057_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_admin() -> Weight {
+		(8_307_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_sponsor() -> Weight {
+		(6_484_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn confirm_sponsorship() -> Weight {
+		(6_530_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn remove_collection_sponsor() -> Weight {
+		(6_733_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn create_item_nft(_b: u32, ) -> Weight {
+		(167_909_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn create_item_fungible() -> Weight {
+		(22_331_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn create_item_refungible(_b: u32, ) -> Weight {
+		(26_293_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn burn_item_nft() -> Weight {
+		(32_237_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_nft() -> Weight {
+		(192_578_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(14 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(10 as Weight))
+	}
+	fn transfer_fungible() -> Weight {
+		(170_749_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn transfer_refungible() -> Weight {
+		(35_949_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(10 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(7 as Weight))
+	}
+	fn approve_nft() -> Weight {
+		(169_825_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+	}
+	fn transfer_from_nft() -> Weight {
+		(197_912_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(15 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(11 as Weight))
+	}
+	fn transfer_from_fungible() -> Weight {
+		(183_789_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(12 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn transfer_from_refungible() -> Weight {
+		(37_149_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(11 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(8 as Weight))
+	}
+	fn set_offchain_schema(_b: u32, ) -> Weight {
+		(6_435_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+		(6_646_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+		(6_542_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+		(14_697_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_schema_version() -> Weight {
+		(6_566_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	fn set_collection_limits() -> Weight {
+		(6_349_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+}
\ No newline at end of file
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -55,6 +55,10 @@
 pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
 pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
 
+pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;
+pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;
+pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;
+
 /// How much items can be created per single
 /// create_many call
 pub const MAX_ITEMS_PER_BATCH: u32 = 200;
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -119,8 +119,6 @@
 /// Digest item type.
 pub type DigestItem = generic::DigestItem<Hash>;
 
-mod nft_weights;
-
 /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
 /// the specifics of the runtime. They can then be made to be agnostic over specific formats
 /// of data like extrinsics, allowing for them to continue syncing the network through upgrades
@@ -695,7 +693,7 @@
 /// Used for the pallet nft in `./nft.rs`
 impl pallet_nft::Config for Runtime {
 	type Event = Event;
-	type WeightInfo = nft_weights::WeightInfo;
+	type WeightInfo = pallet_nft::weights::SubstrateWeight<Self>;
 
 	type EvmBackwardsAddressMapping = pallet_nft::MapBackwardsAddressTruncated;
 	type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
deletedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-// This file is subject to the terms and conditions defined in
-// file 'LICENSE', which is part of this source code package.
-//
-
-use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
-
-pub struct WeightInfo;
-impl pallet_nft::WeightInfo for WeightInfo {
-	fn create_collection() -> Weight {
-		70_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn destroy_collection() -> Weight {
-		90_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(5_u64))
-	}
-	fn add_to_white_list() -> Weight {
-		30_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_from_white_list() -> Weight {
-		35_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_public_access_mode() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_mint_permission() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn change_collection_owner() -> Weight {
-		27_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn add_collection_admin() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_admin() -> Weight {
-		50_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_collection_sponsor() -> Weight {
-		32_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn confirm_sponsorship() -> Weight {
-		22_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn remove_collection_sponsor() -> Weight {
-		24_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn create_item(s: usize) -> Weight {
-		130_000_000_u64
-			.saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temparary multiplier, fee for storage
-			.saturating_add(DbWeight::get().reads(10_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn burn_item() -> Weight {
-		170_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn transfer() -> Weight {
-		125_000_000_u64
-			.saturating_add(DbWeight::get().reads(7_u64))
-			.saturating_add(DbWeight::get().writes(7_u64))
-	}
-	fn approve() -> Weight {
-		45_000_000_u64
-			.saturating_add(DbWeight::get().reads(3_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn transfer_from() -> Weight {
-		150_000_000_u64
-			.saturating_add(DbWeight::get().reads(9_u64))
-			.saturating_add(DbWeight::get().writes(8_u64))
-	}
-	fn set_offchain_schema() -> Weight {
-		33_000_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_const_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_on_chain_schema() -> Weight {
-		11_100_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_variable_meta_data() -> Weight {
-		17_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn enable_contract_sponsoring() -> Weight {
-		13_000_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_schema_version() -> Weight {
-		8_500_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-	fn set_contract_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
-		3_500_000_u64
-			.saturating_add(DbWeight::get().reads(1_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn toggle_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn add_to_contract_white_list() -> Weight {
-		3_000_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn remove_from_contract_white_list() -> Weight {
-		3_200_000_u64
-			.saturating_add(DbWeight::get().reads(0_u64))
-			.saturating_add(DbWeight::get().writes(2_u64))
-	}
-	fn set_collection_limits() -> Weight {
-		8_900_000_u64
-			.saturating_add(DbWeight::get().reads(2_u64))
-			.saturating_add(DbWeight::get().writes(1_u64))
-	}
-}