git.delta.rocks / unique-network / refs/commits / 77bb9d1b095d

difftreelog

cargo fmt

Trubnikov Sergey2022-03-22parent: #310b60e.patch.diff
in: master

10 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -168,7 +168,9 @@
 	use scale_info::TypeInfo;
 
 	#[pallet::config]
-	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config {
+	pub trait Config:
+		frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config
+	{
 		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
 
 		type Currency: Currency<Self::AccountId>;
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -18,7 +18,8 @@
 use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use pallet_evm::{
-	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, account::CrossAccountId
+	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,
+	account::CrossAccountId,
 };
 use sp_core::H160;
 use crate::{
@@ -179,7 +180,9 @@
 }
 
 pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
+impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
+	for HelpersContractSponsoring<T>
+{
 	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
 		let mode = <Pallet<T>>::sponsoring_mode(call.0);
 		if mode == SponsoringModeT::Disabled {
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -30,7 +30,9 @@
 	use sp_core::H160;
 
 	#[pallet::config]
-	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config {
+	pub trait Config:
+		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config
+	{
 		type ContractAddress: Get<H160>;
 		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
 	}
modifiedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-transaction-payment/src/lib.rs
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -87,7 +87,8 @@
 		fee: U256,
 	) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
 		let who_pays_fee = if let WithdrawReason::Call { target, input } = &reason {
-			T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone())).unwrap_or(who.clone())
+			T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
+				.unwrap_or(who.clone())
 		} else {
 			who.clone()
 		};
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -19,9 +19,7 @@
 use core::ops::Deref;
 use frame_support::{ensure};
 use up_data_structs::{AccessMode, CollectionId, TokenId, CreateCollectionData};
-use pallet_common::{
-	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
-};
+use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
 use pallet_evm::account::CrossAccountId;
 use pallet_evm_coder_substrate::WithRecorder;
 use sp_core::H160;
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -21,9 +21,7 @@
 use up_data_structs::{
 	AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
 };
-use pallet_common::{
-	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent,
-};
+use pallet_common::{Error as CommonError, Pallet as PalletCommon, Event as CommonEvent};
 use pallet_evm::account::CrossAccountId;
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use sp_core::H160;
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -21,9 +21,7 @@
 	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
 	CreateCollectionData, CreateRefungibleExData,
 };
-use pallet_common::{
-	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
-};
+use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
 use pallet_evm::account::CrossAccountId;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
 use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
modifiedpallets/unique/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/sponsoring.rs
+++ b/pallets/unique/src/eth/sponsoring.rs
@@ -32,7 +32,9 @@
 use up_data_structs::{CreateItemData, CreateNftData};
 
 pub struct UniqueEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T> {
+impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
+	for UniqueEthSponsorshipHandler<T>
+{
 	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
 		let collection_id = map_eth_to_id(&call.0)?;
 		let collection = <CollectionHandle<T>>::new(collection_id)?;
@@ -59,16 +61,17 @@
 							.map(|()| sponsor)
 					}
 					UniqueNFTCall::ERC721Mintable(call) => match call {
-						pallet_nonfungible::erc::ERC721MintableCall::Mint { .. } |
-						pallet_nonfungible::erc::ERC721MintableCall::MintWithTokenUri { .. } => {
-							withdraw_create_item(
-								&collection, 
-								who.as_sub(), 
-								&CreateItemData::NFT(CreateNftData::default()))
-							.map(|()| sponsor)
-						}
+						pallet_nonfungible::erc::ERC721MintableCall::Mint { .. }
+						| pallet_nonfungible::erc::ERC721MintableCall::MintWithTokenUri {
+							..
+						} => withdraw_create_item(
+							&collection,
+							who.as_sub(),
+							&CreateItemData::NFT(CreateNftData::default()),
+						)
+						.map(|()| sponsor),
 						_ => None,
-    				}
+					},
 					_ => None,
 				}
 			}
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
before · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20	clippy::too_many_arguments,21	clippy::unnecessary_mut_passed,22	clippy::unused_unit23)]2425extern crate alloc;2627pub use serde::{Serialize, Deserialize};2829pub use frame_support::{30	construct_runtime, decl_module, decl_storage, decl_error, decl_event,31	dispatch::DispatchResult,32	ensure, fail, parameter_types,33	traits::{34		ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,35		IsSubType, WithdrawReasons,36	},37	weights::{38		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},39		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,40		WeightToFeePolynomial, DispatchClass,41	},42	StorageValue, transactional,43	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},44	BoundedVec,45};46use scale_info::TypeInfo;47use frame_system::{self as system, ensure_signed};48use sp_runtime::{sp_std::prelude::Vec};49use up_data_structs::{50	MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,51	OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,52	MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,53	CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,54	CreateCollectionData, CustomDataLimit, CreateItemExData,55};56use pallet_common::{57	CollectionHandle, Pallet as PalletCommon, Error as CommonError,58	CommonWeightInfo,59};60use pallet_evm::account::CrossAccountId;61use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};62use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};63use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};6465#[cfg(test)]66mod mock;6768#[cfg(test)]69mod tests;7071mod eth;72mod sponsorship;73pub use sponsorship::UniqueSponsorshipHandler;74pub use eth::sponsoring::UniqueEthSponsorshipHandler;7576pub use eth::UniqueErcSupport;7778pub mod common;79use common::CommonWeights;80pub mod dispatch;81use dispatch::dispatch_call;8283#[cfg(feature = "runtime-benchmarks")]84mod benchmarking;85pub mod weights;86use weights::WeightInfo;8788decl_error! {89	/// Error for non-fungible-token module.90	pub enum Error for Module<T: Config> {91		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.92		CollectionDecimalPointLimitExceeded,93		/// This address is not set as sponsor, use setCollectionSponsor first.94		ConfirmUnsetSponsorFail,95		/// Length of items properties must be greater than 0.96		EmptyArgument,97	}98}99100pub trait Config:101	system::Config102	+ pallet_evm_coder_substrate::Config103	+ pallet_common::Config104	+ pallet_nonfungible::Config105	+ pallet_refungible::Config106	+ pallet_fungible::Config107	+ Sized108	+ TypeInfo109{110	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;111112	/// Weight information for extrinsics in this pallet.113	type WeightInfo: WeightInfo;114}115116decl_event! {117	pub enum Event<T>118	where119		<T as frame_system::Config>::AccountId,120		<T as pallet_evm::account::Config>::CrossAccountId,121	{122		/// Collection sponsor was removed123		///124		/// # Arguments125		///126		/// * collection_id: Globally unique collection identifier.127		CollectionSponsorRemoved(CollectionId),128129		/// Collection admin was added130		///131		/// # Arguments132		///133		/// * collection_id: Globally unique collection identifier.134		///135		/// * admin:  Admin address.136		CollectionAdminAdded(CollectionId, CrossAccountId),137138		/// Collection owned was change139		///140		/// # Arguments141		///142		/// * collection_id: Globally unique collection identifier.143		///144		/// * owner:  New owner address.145		CollectionOwnedChanged(CollectionId, AccountId),146147		/// Collection sponsor was set148		///149		/// # Arguments150		///151		/// * collection_id: Globally unique collection identifier.152		///153		/// * owner:  New sponsor address.154		CollectionSponsorSet(CollectionId, AccountId),155156		/// const on chain schema was set157		///158		/// # Arguments159		///160		/// * collection_id: Globally unique collection identifier.161		ConstOnChainSchemaSet(CollectionId),162163		/// New sponsor was confirm164		///165		/// # Arguments166		///167		/// * collection_id: Globally unique collection identifier.168		///169		/// * sponsor:  New sponsor address.170		SponsorshipConfirmed(CollectionId, AccountId),171172		/// Collection admin was removed173		///174		/// # Arguments175		///176		/// * collection_id: Globally unique collection identifier.177		///178		/// * admin:  Admin address.179		CollectionAdminRemoved(CollectionId, CrossAccountId),180181		/// Address was remove from allow list182		///183		/// # Arguments184		///185		/// * collection_id: Globally unique collection identifier.186		///187		/// * user:  Address.188		AllowListAddressRemoved(CollectionId, CrossAccountId),189190		/// Address was add to allow list191		///192		/// # Arguments193		///194		/// * collection_id: Globally unique collection identifier.195		///196		/// * user:  Address.197		AllowListAddressAdded(CollectionId, CrossAccountId),198199		/// Collection limits was set200		///201		/// # Arguments202		///203		/// * collection_id: Globally unique collection identifier.204		CollectionLimitSet(CollectionId),205206		/// Mint permission	was set207		///208		/// # Arguments209		///210		/// * collection_id: Globally unique collection identifier.211		MintPermissionSet(CollectionId),212213		/// Offchain schema was set214		///215		/// # Arguments216		///217		/// * collection_id: Globally unique collection identifier.218		OffchainSchemaSet(CollectionId),219220		/// Public access mode was set221		///222		/// # Arguments223		///224		/// * collection_id: Globally unique collection identifier.225		///226		/// * mode: New access state.227		PublicAccessModeSet(CollectionId, AccessMode),228229		/// Schema version was set230		///231		/// # Arguments232		///233		/// * collection_id: Globally unique collection identifier.234		SchemaVersionSet(CollectionId),235236		/// Variable on chain schema was set237		///238		/// # Arguments239		///240		/// * collection_id: Globally unique collection identifier.241		VariableOnChainSchemaSet(CollectionId),242	}243}244245type SelfWeightOf<T> = <T as Config>::WeightInfo;246247// # Used definitions248//249// ## User control levels250//251// chain-controlled - key is uncontrolled by user252//                    i.e autoincrementing index253//                    can use non-cryptographic hash254// real - key is controlled by user255//        but it is hard to generate enough colliding values, i.e owner of signed txs256//        can use non-cryptographic hash257// controlled - key is completly controlled by users258//              i.e maps with mutable keys259//              should use cryptographic hash260//261// ## User control level downgrade reasons262//263// ?1 - chain-controlled -> controlled264//      collections/tokens can be destroyed, resulting in massive holes265// ?2 - chain-controlled -> controlled266//      same as ?1, but can be only added, resulting in easier exploitation267// ?3 - real -> controlled268//      no confirmation required, so addresses can be easily generated269decl_storage! {270	trait Store for Module<T: Config> as Unique {271272		//#region Private members273		/// Used for migrations274		ChainVersion: u64;275		//#endregion276277		//#region Tokens transfer rate limit baskets278		/// (Collection id (controlled?2), who created (real))279		/// TODO: Off chain worker should remove from this map when collection gets removed280		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;281		/// Collection id (controlled?2), token id (controlled?2)282		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;283		/// Collection id (controlled?2), owning user (real)284		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;285		/// Collection id (controlled?2), token id (controlled?2)286		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;287		//#endregion288289		/// Variable metadata sponsoring290		/// Collection id (controlled?2), token id (controlled?2)291		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;292		/// Approval sponsoring293		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;294		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;295		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;296	}297}298299decl_module! {300	pub struct Module<T: Config> for enum Call301	where302		origin: T::Origin303	{304		type Error = Error<T>;305306		fn deposit_event() = default;307308		fn on_initialize(_now: T::BlockNumber) -> Weight {309			0310		}311312		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.313		///314		/// # Permissions315		///316		/// * Anyone.317		///318		/// # Arguments319		///320		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.321		///322		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.323		///324		/// * token_prefix: UTF-8 string with token prefix.325		///326		/// * mode: [CollectionMode] collection type and type dependent data.327		// returns collection ID328		#[weight = <SelfWeightOf<T>>::create_collection()]329		#[transactional]330		#[deprecated]331		pub fn create_collection(origin,332								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,333								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,334								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,335								 mode: CollectionMode) -> DispatchResult  {336			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {337				name: collection_name,338				description: collection_description,339				token_prefix,340				mode,341				..Default::default()342			};343			Self::create_collection_ex(origin, data)344		}345346		/// This method creates a collection347		///348		/// Prefer it to deprecated [`created_collection`] method349		#[weight = <SelfWeightOf<T>>::create_collection()]350		#[transactional]351		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {352			let owner = ensure_signed(origin)?;353354			let _id = match data.mode {355				CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},356				CollectionMode::Fungible(decimal_points) => {357					// check params358					ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);359					<PalletFungible<T>>::init_collection(owner, data)?360				}361				CollectionMode::ReFungible => {362					<PalletRefungible<T>>::init_collection(owner, data)?363				}364			};365366			Ok(())367		}368369		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.370		///371		/// # Permissions372		///373		/// * Collection Owner.374		///375		/// # Arguments376		///377		/// * collection_id: collection to destroy.378		#[weight = <SelfWeightOf<T>>::destroy_collection()]379		#[transactional]380		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {381			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);382383			let collection = <CollectionHandle<T>>::try_get(collection_id)?;384			collection.check_is_owner(&sender)?;385386			// =========387388			match collection.mode {389				CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,390				CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,391				CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,392			}393394			<NftTransferBasket<T>>::remove_prefix(collection_id, None);395			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);396			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);397398			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);399			<NftApproveBasket<T>>::remove_prefix(collection_id, None);400			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);401			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);402403			Ok(())404		}405406		/// Add an address to allow list.407		///408		/// # Permissions409		///410		/// * Collection Owner411		/// * Collection Admin412		///413		/// # Arguments414		///415		/// * collection_id.416		///417		/// * address.418		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]419		#[transactional]420		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{421422			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);423			let collection = <CollectionHandle<T>>::try_get(collection_id)?;424425			<PalletCommon<T>>::toggle_allowlist(426				&collection,427				&sender,428				&address,429				true,430			)?;431432			Self::deposit_event(Event::<T>::AllowListAddressAdded(433				collection_id,434				address435			));436437			Ok(())438		}439440		/// Remove an address from allow list.441		///442		/// # Permissions443		///444		/// * Collection Owner445		/// * Collection Admin446		///447		/// # Arguments448		///449		/// * collection_id.450		///451		/// * address.452		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]453		#[transactional]454		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{455456			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);457			let collection = <CollectionHandle<T>>::try_get(collection_id)?;458459			<PalletCommon<T>>::toggle_allowlist(460				&collection,461				&sender,462				&address,463				false,464			)?;465466			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(467				collection_id,468				address469			));470471			Ok(())472		}473474		/// Toggle between normal and allow list access for the methods with access for `Anyone`.475		///476		/// # Permissions477		///478		/// * Collection Owner.479		///480		/// # Arguments481		///482		/// * collection_id.483		///484		/// * mode: [AccessMode]485		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]486		#[transactional]487		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult488		{489			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);490491			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;492			target_collection.check_is_owner(&sender)?;493494			target_collection.access = mode.clone();495496			<Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(497				collection_id,498				mode499			));500501			target_collection.save()502		}503504		/// Allows Anyone to create tokens if:505		/// * Allow List is enabled, and506		/// * Address is added to allow list, and507		/// * This method was called with True parameter508		///509		/// # Permissions510		/// * Collection Owner511		///512		/// # Arguments513		///514		/// * collection_id.515		///516		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.517		#[weight = <SelfWeightOf<T>>::set_mint_permission()]518		#[transactional]519		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult520		{521			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);522523			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;524			target_collection.check_is_owner(&sender)?;525526			target_collection.mint_mode = mint_permission;527528			<Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(529				collection_id530			));531532			target_collection.save()533		}534535		/// Change the owner of the collection.536		///537		/// # Permissions538		///539		/// * Collection Owner.540		///541		/// # Arguments542		///543		/// * collection_id.544		///545		/// * new_owner.546		#[weight = <SelfWeightOf<T>>::change_collection_owner()]547		#[transactional]548		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {549550			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);551552			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;553			target_collection.check_is_owner(&sender)?;554555			target_collection.owner = new_owner.clone();556			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(557				collection_id,558				new_owner559			));560561			target_collection.save()562		}563564		/// Adds an admin of the Collection.565		/// 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.566		///567		/// # Permissions568		///569		/// * Collection Owner.570		/// * Collection Admin.571		///572		/// # Arguments573		///574		/// * collection_id: ID of the Collection to add admin for.575		///576		/// * new_admin_id: Address of new admin to add.577		#[weight = <SelfWeightOf<T>>::add_collection_admin()]578		#[transactional]579		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {580			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);581			let collection = <CollectionHandle<T>>::try_get(collection_id)?;582583			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(584				collection_id,585				new_admin_id.clone()586			));587588			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)589		}590591		/// 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.592		///593		/// # Permissions594		///595		/// * Collection Owner.596		/// * Collection Admin.597		///598		/// # Arguments599		///600		/// * collection_id: ID of the Collection to remove admin for.601		///602		/// * account_id: Address of admin to remove.603		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]604		#[transactional]605		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {606			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);607			let collection = <CollectionHandle<T>>::try_get(collection_id)?;608609			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(610				collection_id,611				account_id.clone()612			));613614			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)615		}616617		/// # Permissions618		///619		/// * Collection Owner620		///621		/// # Arguments622		///623		/// * collection_id.624		///625		/// * new_sponsor.626		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]627		#[transactional]628		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {629			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630631			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;632			target_collection.check_is_owner(&sender)?;633634			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());635636			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(637				collection_id,638				new_sponsor639			));640641			target_collection.save()642		}643644		/// # Permissions645		///646		/// * Sponsor.647		///648		/// # Arguments649		///650		/// * collection_id.651		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]652		#[transactional]653		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {654			let sender = ensure_signed(origin)?;655656			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;657			ensure!(658				target_collection.sponsorship.pending_sponsor() == Some(&sender),659				Error::<T>::ConfirmUnsetSponsorFail660			);661662			target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());663664			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(665				collection_id,666				sender667			));668669			target_collection.save()670		}671672		/// Switch back to pay-per-own-transaction model.673		///674		/// # Permissions675		///676		/// * Collection owner.677		///678		/// # Arguments679		///680		/// * collection_id.681		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]682		#[transactional]683		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {684			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);685686			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;687			target_collection.check_is_owner(&sender)?;688689			target_collection.sponsorship = SponsorshipState::Disabled;690691			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(692				collection_id693			));694			target_collection.save()695		}696697		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.698		///699		/// # Permissions700		///701		/// * Collection Owner.702		/// * Collection Admin.703		/// * Anyone if704		///     * Allow List is enabled, and705		///     * Address is added to allow list, and706		///     * MintPermission is enabled (see SetMintPermission method)707		///708		/// # Arguments709		///710		/// * collection_id: ID of the collection.711		///712		/// * owner: Address, initial owner of the NFT.713		///714		/// * data: Token data to store on chain.715		#[weight = <CommonWeights<T>>::create_item()]716		#[transactional]717		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {718			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);719720			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))721		}722723		/// This method creates multiple items in a collection created with CreateCollection method.724		///725		/// # Permissions726		///727		/// * Collection Owner.728		/// * Collection Admin.729		/// * Anyone if730		///     * Allow List is enabled, and731		///     * Address is added to allow list, and732		///     * MintPermission is enabled (see SetMintPermission method)733		///734		/// # Arguments735		///736		/// * collection_id: ID of the collection.737		///738		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].739		///740		/// * owner: Address, initial owner of the NFT.741		#[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]742		#[transactional]743		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {744			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);745			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);746747			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))748		}749750		#[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]751		#[transactional]752		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {753			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);754755			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))756		}757758		// TODO! transaction weight759760		/// Set transfers_enabled value for particular collection761		///762		/// # Permissions763		///764		/// * Collection Owner.765		///766		/// # Arguments767		///768		/// * collection_id: ID of the collection.769		///770		/// * value: New flag value.771		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]772		#[transactional]773		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {774			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);775			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;776			target_collection.check_is_owner(&sender)?;777778			// =========779780			target_collection.limits.transfers_enabled = Some(value);781			target_collection.save()782		}783784		/// Destroys a concrete instance of NFT.785		///786		/// # Permissions787		///788		/// * Collection Owner.789		/// * Collection Admin.790		/// * Current NFT Owner.791		///792		/// # Arguments793		///794		/// * collection_id: ID of the collection.795		///796		/// * item_id: ID of NFT to burn.797		#[weight = <CommonWeights<T>>::burn_item()]798		#[transactional]799		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {800			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);801802			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;803			if value == 1 {804				<NftTransferBasket<T>>::remove(collection_id, item_id);805				<NftApproveBasket<T>>::remove(collection_id, item_id);806			}807			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?808			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());809			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));810			Ok(post_info)811		}812813		/// Destroys a concrete instance of NFT on behalf of the owner814		/// See also: [`approve`]815		///816		/// # Permissions817		///818		/// * Collection Owner.819		/// * Collection Admin.820		/// * Current NFT Owner.821		///822		/// # Arguments823		///824		/// * collection_id: ID of the collection.825		///826		/// * item_id: ID of NFT to burn.827		///828		/// * from: owner of item829		#[weight = <CommonWeights<T>>::burn_from()]830		#[transactional]831		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {832			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);833834			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))835		}836837		/// Change ownership of the token.838		///839		/// # Permissions840		///841		/// * Collection Owner842		/// * Collection Admin843		/// * Current NFT owner844		///845		/// # Arguments846		///847		/// * recipient: Address of token recipient.848		///849		/// * collection_id.850		///851		/// * item_id: ID of the item852		///     * Non-Fungible Mode: Required.853		///     * Fungible Mode: Ignored.854		///     * Re-Fungible Mode: Required.855		///856		/// * value: Amount to transfer.857		///     * Non-Fungible Mode: Ignored858		///     * Fungible Mode: Must specify transferred amount859		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)860		#[weight = <CommonWeights<T>>::transfer()]861		#[transactional]862		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {863			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);864865			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))866		}867868		/// Set, change, or remove approved address to transfer the ownership of the NFT.869		///870		/// # Permissions871		///872		/// * Collection Owner873		/// * Collection Admin874		/// * Current NFT owner875		///876		/// # Arguments877		///878		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).879		///880		/// * collection_id.881		///882		/// * item_id: ID of the item.883		#[weight = <CommonWeights<T>>::approve()]884		#[transactional]885		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {886			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);887888			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))889		}890891		/// 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.892		///893		/// # Permissions894		/// * Collection Owner895		/// * Collection Admin896		/// * Current NFT owner897		/// * Address approved by current NFT owner898		///899		/// # Arguments900		///901		/// * from: Address that owns token.902		///903		/// * recipient: Address of token recipient.904		///905		/// * collection_id.906		///907		/// * item_id: ID of the item.908		///909		/// * value: Amount to transfer.910		#[weight = <CommonWeights<T>>::transfer_from()]911		#[transactional]912		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {913			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);914915			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))916		}917918		/// Set off-chain data schema.919		///920		/// # Permissions921		///922		/// * Collection Owner923		/// * Collection Admin924		///925		/// # Arguments926		///927		/// * collection_id.928		///929		/// * schema: String representing the offchain data schema.930		#[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]931		#[transactional]932		pub fn set_variable_meta_data (933			origin,934			collection_id: CollectionId,935			item_id: TokenId,936			data: BoundedVec<u8, CustomDataLimit>,937		) -> DispatchResultWithPostInfo {938			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);939940			dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))941		}942943		/// Set meta_update_permission value for particular collection944		///945		/// # Permissions946		///947		/// * Collection Owner.948		///949		/// # Arguments950		///951		/// * collection_id: ID of the collection.952		///953		/// * value: New flag value.954		#[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]955		#[transactional]956		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {957			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);958			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;959960			ensure!(961				target_collection.meta_update_permission != MetaUpdatePermission::None,962				<CommonError<T>>::MetadataFlagFrozen,963			);964			target_collection.check_is_owner(&sender)?;965966			target_collection.meta_update_permission = value;967968			target_collection.save()969		}970971		/// Set schema standard972		/// ImageURL973		/// Unique974		///975		/// # Permissions976		///977		/// * Collection Owner978		/// * Collection Admin979		///980		/// # Arguments981		///982		/// * collection_id.983		///984		/// * schema: SchemaVersion: enum985		#[weight = <SelfWeightOf<T>>::set_schema_version()]986		#[transactional]987		pub fn set_schema_version(988			origin,989			collection_id: CollectionId,990			version: SchemaVersion991		) -> DispatchResult {992			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);993			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;994			target_collection.check_is_owner_or_admin(&sender)?;995			target_collection.schema_version = version;996997			<Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(998				collection_id999			));10001001			target_collection.save()1002		}10031004		/// Set off-chain data schema.1005		///1006		/// # Permissions1007		///1008		/// * Collection Owner1009		/// * Collection Admin1010		///1011		/// # Arguments1012		///1013		/// * collection_id.1014		///1015		/// * schema: String representing the offchain data schema.1016		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1017		#[transactional]1018		pub fn set_offchain_schema(1019			origin,1020			collection_id: CollectionId,1021			schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1022		) -> DispatchResult {1023			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1024			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1025			target_collection.check_is_owner_or_admin(&sender)?;10261027			target_collection.offchain_schema = schema;10281029			<Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1030				collection_id1031			));10321033			target_collection.save()1034		}10351036		/// Set const on-chain data schema.1037		///1038		/// # Permissions1039		///1040		/// * Collection Owner1041		/// * Collection Admin1042		///1043		/// # Arguments1044		///1045		/// * collection_id.1046		///1047		/// * schema: String representing the const on-chain data schema.1048		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1049		#[transactional]1050		pub fn set_const_on_chain_schema (1051			origin,1052			collection_id: CollectionId,1053			schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1054		) -> DispatchResult {1055			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1056			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1057			target_collection.check_is_owner_or_admin(&sender)?;10581059			target_collection.const_on_chain_schema = schema;10601061			<Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1062				collection_id1063			));10641065			target_collection.save()1066		}10671068		/// Set variable on-chain data schema.1069		///1070		/// # Permissions1071		///1072		/// * Collection Owner1073		/// * Collection Admin1074		///1075		/// # Arguments1076		///1077		/// * collection_id.1078		///1079		/// * schema: String representing the variable on-chain data schema.1080		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1081		#[transactional]1082		pub fn set_variable_on_chain_schema (1083			origin,1084			collection_id: CollectionId,1085			schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1086		) -> DispatchResult {1087			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1088			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1089			target_collection.check_is_owner_or_admin(&sender)?;10901091			target_collection.variable_on_chain_schema = schema;10921093			<Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1094				collection_id1095			));10961097			target_collection.save()1098		}10991100		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1101		#[transactional]1102		pub fn set_collection_limits(1103			origin,1104			collection_id: CollectionId,1105			new_limit: CollectionLimits,1106		) -> DispatchResult {1107			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1108			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1109			target_collection.check_is_owner(&sender)?;1110			let old_limit = &target_collection.limits;11111112			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;11131114			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1115				collection_id1116			));11171118			target_collection.save()1119		}1120	}1121}
after · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20	clippy::too_many_arguments,21	clippy::unnecessary_mut_passed,22	clippy::unused_unit23)]2425extern crate alloc;2627pub use serde::{Serialize, Deserialize};2829pub use frame_support::{30	construct_runtime, decl_module, decl_storage, decl_error, decl_event,31	dispatch::DispatchResult,32	ensure, fail, parameter_types,33	traits::{34		ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,35		IsSubType, WithdrawReasons,36	},37	weights::{38		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},39		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,40		WeightToFeePolynomial, DispatchClass,41	},42	StorageValue, transactional,43	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},44	BoundedVec,45};46use scale_info::TypeInfo;47use frame_system::{self as system, ensure_signed};48use sp_runtime::{sp_std::prelude::Vec};49use up_data_structs::{50	MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,51	OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,52	MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,53	CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,54	CreateCollectionData, CustomDataLimit, CreateItemExData,55};56use pallet_common::{CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo};57use pallet_evm::account::CrossAccountId;58use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};59use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};60use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};6162#[cfg(test)]63mod mock;6465#[cfg(test)]66mod tests;6768mod eth;69mod sponsorship;70pub use sponsorship::UniqueSponsorshipHandler;71pub use eth::sponsoring::UniqueEthSponsorshipHandler;7273pub use eth::UniqueErcSupport;7475pub mod common;76use common::CommonWeights;77pub mod dispatch;78use dispatch::dispatch_call;7980#[cfg(feature = "runtime-benchmarks")]81mod benchmarking;82pub mod weights;83use weights::WeightInfo;8485decl_error! {86	/// Error for non-fungible-token module.87	pub enum Error for Module<T: Config> {88		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.89		CollectionDecimalPointLimitExceeded,90		/// This address is not set as sponsor, use setCollectionSponsor first.91		ConfirmUnsetSponsorFail,92		/// Length of items properties must be greater than 0.93		EmptyArgument,94	}95}9697pub trait Config:98	system::Config99	+ pallet_evm_coder_substrate::Config100	+ pallet_common::Config101	+ pallet_nonfungible::Config102	+ pallet_refungible::Config103	+ pallet_fungible::Config104	+ Sized105	+ TypeInfo106{107	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;108109	/// Weight information for extrinsics in this pallet.110	type WeightInfo: WeightInfo;111}112113decl_event! {114	pub enum Event<T>115	where116		<T as frame_system::Config>::AccountId,117		<T as pallet_evm::account::Config>::CrossAccountId,118	{119		/// Collection sponsor was removed120		///121		/// # Arguments122		///123		/// * collection_id: Globally unique collection identifier.124		CollectionSponsorRemoved(CollectionId),125126		/// Collection admin was added127		///128		/// # Arguments129		///130		/// * collection_id: Globally unique collection identifier.131		///132		/// * admin:  Admin address.133		CollectionAdminAdded(CollectionId, CrossAccountId),134135		/// Collection owned was change136		///137		/// # Arguments138		///139		/// * collection_id: Globally unique collection identifier.140		///141		/// * owner:  New owner address.142		CollectionOwnedChanged(CollectionId, AccountId),143144		/// Collection sponsor was set145		///146		/// # Arguments147		///148		/// * collection_id: Globally unique collection identifier.149		///150		/// * owner:  New sponsor address.151		CollectionSponsorSet(CollectionId, AccountId),152153		/// const on chain schema was set154		///155		/// # Arguments156		///157		/// * collection_id: Globally unique collection identifier.158		ConstOnChainSchemaSet(CollectionId),159160		/// New sponsor was confirm161		///162		/// # Arguments163		///164		/// * collection_id: Globally unique collection identifier.165		///166		/// * sponsor:  New sponsor address.167		SponsorshipConfirmed(CollectionId, AccountId),168169		/// Collection admin was removed170		///171		/// # Arguments172		///173		/// * collection_id: Globally unique collection identifier.174		///175		/// * admin:  Admin address.176		CollectionAdminRemoved(CollectionId, CrossAccountId),177178		/// Address was remove from allow list179		///180		/// # Arguments181		///182		/// * collection_id: Globally unique collection identifier.183		///184		/// * user:  Address.185		AllowListAddressRemoved(CollectionId, CrossAccountId),186187		/// Address was add to allow list188		///189		/// # Arguments190		///191		/// * collection_id: Globally unique collection identifier.192		///193		/// * user:  Address.194		AllowListAddressAdded(CollectionId, CrossAccountId),195196		/// Collection limits was set197		///198		/// # Arguments199		///200		/// * collection_id: Globally unique collection identifier.201		CollectionLimitSet(CollectionId),202203		/// Mint permission	was set204		///205		/// # Arguments206		///207		/// * collection_id: Globally unique collection identifier.208		MintPermissionSet(CollectionId),209210		/// Offchain schema was set211		///212		/// # Arguments213		///214		/// * collection_id: Globally unique collection identifier.215		OffchainSchemaSet(CollectionId),216217		/// Public access mode was set218		///219		/// # Arguments220		///221		/// * collection_id: Globally unique collection identifier.222		///223		/// * mode: New access state.224		PublicAccessModeSet(CollectionId, AccessMode),225226		/// Schema version was set227		///228		/// # Arguments229		///230		/// * collection_id: Globally unique collection identifier.231		SchemaVersionSet(CollectionId),232233		/// Variable on chain schema was set234		///235		/// # Arguments236		///237		/// * collection_id: Globally unique collection identifier.238		VariableOnChainSchemaSet(CollectionId),239	}240}241242type SelfWeightOf<T> = <T as Config>::WeightInfo;243244// # Used definitions245//246// ## User control levels247//248// chain-controlled - key is uncontrolled by user249//                    i.e autoincrementing index250//                    can use non-cryptographic hash251// real - key is controlled by user252//        but it is hard to generate enough colliding values, i.e owner of signed txs253//        can use non-cryptographic hash254// controlled - key is completly controlled by users255//              i.e maps with mutable keys256//              should use cryptographic hash257//258// ## User control level downgrade reasons259//260// ?1 - chain-controlled -> controlled261//      collections/tokens can be destroyed, resulting in massive holes262// ?2 - chain-controlled -> controlled263//      same as ?1, but can be only added, resulting in easier exploitation264// ?3 - real -> controlled265//      no confirmation required, so addresses can be easily generated266decl_storage! {267	trait Store for Module<T: Config> as Unique {268269		//#region Private members270		/// Used for migrations271		ChainVersion: u64;272		//#endregion273274		//#region Tokens transfer rate limit baskets275		/// (Collection id (controlled?2), who created (real))276		/// TODO: Off chain worker should remove from this map when collection gets removed277		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;278		/// Collection id (controlled?2), token id (controlled?2)279		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;280		/// Collection id (controlled?2), owning user (real)281		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;282		/// Collection id (controlled?2), token id (controlled?2)283		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;284		//#endregion285286		/// Variable metadata sponsoring287		/// Collection id (controlled?2), token id (controlled?2)288		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;289		/// Approval sponsoring290		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;291		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;292		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;293	}294}295296decl_module! {297	pub struct Module<T: Config> for enum Call298	where299		origin: T::Origin300	{301		type Error = Error<T>;302303		fn deposit_event() = default;304305		fn on_initialize(_now: T::BlockNumber) -> Weight {306			0307		}308309		/// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.310		///311		/// # Permissions312		///313		/// * Anyone.314		///315		/// # Arguments316		///317		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.318		///319		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.320		///321		/// * token_prefix: UTF-8 string with token prefix.322		///323		/// * mode: [CollectionMode] collection type and type dependent data.324		// returns collection ID325		#[weight = <SelfWeightOf<T>>::create_collection()]326		#[transactional]327		#[deprecated]328		pub fn create_collection(origin,329								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,330								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,331								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,332								 mode: CollectionMode) -> DispatchResult  {333			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {334				name: collection_name,335				description: collection_description,336				token_prefix,337				mode,338				..Default::default()339			};340			Self::create_collection_ex(origin, data)341		}342343		/// This method creates a collection344		///345		/// Prefer it to deprecated [`created_collection`] method346		#[weight = <SelfWeightOf<T>>::create_collection()]347		#[transactional]348		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {349			let owner = ensure_signed(origin)?;350351			let _id = match data.mode {352				CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},353				CollectionMode::Fungible(decimal_points) => {354					// check params355					ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);356					<PalletFungible<T>>::init_collection(owner, data)?357				}358				CollectionMode::ReFungible => {359					<PalletRefungible<T>>::init_collection(owner, data)?360				}361			};362363			Ok(())364		}365366		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.367		///368		/// # Permissions369		///370		/// * Collection Owner.371		///372		/// # Arguments373		///374		/// * collection_id: collection to destroy.375		#[weight = <SelfWeightOf<T>>::destroy_collection()]376		#[transactional]377		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {378			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);379380			let collection = <CollectionHandle<T>>::try_get(collection_id)?;381			collection.check_is_owner(&sender)?;382383			// =========384385			match collection.mode {386				CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,387				CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,388				CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,389			}390391			<NftTransferBasket<T>>::remove_prefix(collection_id, None);392			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);393			<ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);394395			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);396			<NftApproveBasket<T>>::remove_prefix(collection_id, None);397			<FungibleApproveBasket<T>>::remove_prefix(collection_id, None);398			<RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);399400			Ok(())401		}402403		/// Add an address to allow list.404		///405		/// # Permissions406		///407		/// * Collection Owner408		/// * Collection Admin409		///410		/// # Arguments411		///412		/// * collection_id.413		///414		/// * address.415		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]416		#[transactional]417		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{418419			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);420			let collection = <CollectionHandle<T>>::try_get(collection_id)?;421422			<PalletCommon<T>>::toggle_allowlist(423				&collection,424				&sender,425				&address,426				true,427			)?;428429			Self::deposit_event(Event::<T>::AllowListAddressAdded(430				collection_id,431				address432			));433434			Ok(())435		}436437		/// Remove an address from allow list.438		///439		/// # Permissions440		///441		/// * Collection Owner442		/// * Collection Admin443		///444		/// # Arguments445		///446		/// * collection_id.447		///448		/// * address.449		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]450		#[transactional]451		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{452453			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);454			let collection = <CollectionHandle<T>>::try_get(collection_id)?;455456			<PalletCommon<T>>::toggle_allowlist(457				&collection,458				&sender,459				&address,460				false,461			)?;462463			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(464				collection_id,465				address466			));467468			Ok(())469		}470471		/// Toggle between normal and allow list access for the methods with access for `Anyone`.472		///473		/// # Permissions474		///475		/// * Collection Owner.476		///477		/// # Arguments478		///479		/// * collection_id.480		///481		/// * mode: [AccessMode]482		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]483		#[transactional]484		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult485		{486			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);487488			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;489			target_collection.check_is_owner(&sender)?;490491			target_collection.access = mode.clone();492493			<Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(494				collection_id,495				mode496			));497498			target_collection.save()499		}500501		/// Allows Anyone to create tokens if:502		/// * Allow List is enabled, and503		/// * Address is added to allow list, and504		/// * This method was called with True parameter505		///506		/// # Permissions507		/// * Collection Owner508		///509		/// # Arguments510		///511		/// * collection_id.512		///513		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.514		#[weight = <SelfWeightOf<T>>::set_mint_permission()]515		#[transactional]516		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult517		{518			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);519520			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;521			target_collection.check_is_owner(&sender)?;522523			target_collection.mint_mode = mint_permission;524525			<Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(526				collection_id527			));528529			target_collection.save()530		}531532		/// Change the owner of the collection.533		///534		/// # Permissions535		///536		/// * Collection Owner.537		///538		/// # Arguments539		///540		/// * collection_id.541		///542		/// * new_owner.543		#[weight = <SelfWeightOf<T>>::change_collection_owner()]544		#[transactional]545		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {546547			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);548549			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;550			target_collection.check_is_owner(&sender)?;551552			target_collection.owner = new_owner.clone();553			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(554				collection_id,555				new_owner556			));557558			target_collection.save()559		}560561		/// Adds an admin of the Collection.562		/// 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.563		///564		/// # Permissions565		///566		/// * Collection Owner.567		/// * Collection Admin.568		///569		/// # Arguments570		///571		/// * collection_id: ID of the Collection to add admin for.572		///573		/// * new_admin_id: Address of new admin to add.574		#[weight = <SelfWeightOf<T>>::add_collection_admin()]575		#[transactional]576		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {577			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);578			let collection = <CollectionHandle<T>>::try_get(collection_id)?;579580			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(581				collection_id,582				new_admin_id.clone()583			));584585			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)586		}587588		/// 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.589		///590		/// # Permissions591		///592		/// * Collection Owner.593		/// * Collection Admin.594		///595		/// # Arguments596		///597		/// * collection_id: ID of the Collection to remove admin for.598		///599		/// * account_id: Address of admin to remove.600		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]601		#[transactional]602		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {603			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);604			let collection = <CollectionHandle<T>>::try_get(collection_id)?;605606			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(607				collection_id,608				account_id.clone()609			));610611			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)612		}613614		/// # Permissions615		///616		/// * Collection Owner617		///618		/// # Arguments619		///620		/// * collection_id.621		///622		/// * new_sponsor.623		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]624		#[transactional]625		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {626			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);627628			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;629			target_collection.check_is_owner(&sender)?;630631			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());632633			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(634				collection_id,635				new_sponsor636			));637638			target_collection.save()639		}640641		/// # Permissions642		///643		/// * Sponsor.644		///645		/// # Arguments646		///647		/// * collection_id.648		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]649		#[transactional]650		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {651			let sender = ensure_signed(origin)?;652653			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;654			ensure!(655				target_collection.sponsorship.pending_sponsor() == Some(&sender),656				Error::<T>::ConfirmUnsetSponsorFail657			);658659			target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());660661			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(662				collection_id,663				sender664			));665666			target_collection.save()667		}668669		/// Switch back to pay-per-own-transaction model.670		///671		/// # Permissions672		///673		/// * Collection owner.674		///675		/// # Arguments676		///677		/// * collection_id.678		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]679		#[transactional]680		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {681			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682683			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;684			target_collection.check_is_owner(&sender)?;685686			target_collection.sponsorship = SponsorshipState::Disabled;687688			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(689				collection_id690			));691			target_collection.save()692		}693694		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.695		///696		/// # Permissions697		///698		/// * Collection Owner.699		/// * Collection Admin.700		/// * Anyone if701		///     * Allow List is enabled, and702		///     * Address is added to allow list, and703		///     * MintPermission is enabled (see SetMintPermission method)704		///705		/// # Arguments706		///707		/// * collection_id: ID of the collection.708		///709		/// * owner: Address, initial owner of the NFT.710		///711		/// * data: Token data to store on chain.712		#[weight = <CommonWeights<T>>::create_item()]713		#[transactional]714		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {715			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);716717			dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))718		}719720		/// This method creates multiple items in a collection created with CreateCollection method.721		///722		/// # Permissions723		///724		/// * Collection Owner.725		/// * Collection Admin.726		/// * Anyone if727		///     * Allow List is enabled, and728		///     * Address is added to allow list, and729		///     * MintPermission is enabled (see SetMintPermission method)730		///731		/// # Arguments732		///733		/// * collection_id: ID of the collection.734		///735		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].736		///737		/// * owner: Address, initial owner of the NFT.738		#[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]739		#[transactional]740		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {741			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);742			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);743744			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))745		}746747		#[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]748		#[transactional]749		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {750			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);751752			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))753		}754755		// TODO! transaction weight756757		/// Set transfers_enabled value for particular collection758		///759		/// # Permissions760		///761		/// * Collection Owner.762		///763		/// # Arguments764		///765		/// * collection_id: ID of the collection.766		///767		/// * value: New flag value.768		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]769		#[transactional]770		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {771			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);772			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;773			target_collection.check_is_owner(&sender)?;774775			// =========776777			target_collection.limits.transfers_enabled = Some(value);778			target_collection.save()779		}780781		/// Destroys a concrete instance of NFT.782		///783		/// # Permissions784		///785		/// * Collection Owner.786		/// * Collection Admin.787		/// * Current NFT Owner.788		///789		/// # Arguments790		///791		/// * collection_id: ID of the collection.792		///793		/// * item_id: ID of NFT to burn.794		#[weight = <CommonWeights<T>>::burn_item()]795		#[transactional]796		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {797			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);798799			let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;800			if value == 1 {801				<NftTransferBasket<T>>::remove(collection_id, item_id);802				<NftApproveBasket<T>>::remove(collection_id, item_id);803			}804			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?805			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());806			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));807			Ok(post_info)808		}809810		/// Destroys a concrete instance of NFT on behalf of the owner811		/// See also: [`approve`]812		///813		/// # Permissions814		///815		/// * Collection Owner.816		/// * Collection Admin.817		/// * Current NFT Owner.818		///819		/// # Arguments820		///821		/// * collection_id: ID of the collection.822		///823		/// * item_id: ID of NFT to burn.824		///825		/// * from: owner of item826		#[weight = <CommonWeights<T>>::burn_from()]827		#[transactional]828		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {829			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);830831			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))832		}833834		/// Change ownership of the token.835		///836		/// # Permissions837		///838		/// * Collection Owner839		/// * Collection Admin840		/// * Current NFT owner841		///842		/// # Arguments843		///844		/// * recipient: Address of token recipient.845		///846		/// * collection_id.847		///848		/// * item_id: ID of the item849		///     * Non-Fungible Mode: Required.850		///     * Fungible Mode: Ignored.851		///     * Re-Fungible Mode: Required.852		///853		/// * value: Amount to transfer.854		///     * Non-Fungible Mode: Ignored855		///     * Fungible Mode: Must specify transferred amount856		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)857		#[weight = <CommonWeights<T>>::transfer()]858		#[transactional]859		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {860			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);861862			dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))863		}864865		/// Set, change, or remove approved address to transfer the ownership of the NFT.866		///867		/// # Permissions868		///869		/// * Collection Owner870		/// * Collection Admin871		/// * Current NFT owner872		///873		/// # Arguments874		///875		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).876		///877		/// * collection_id.878		///879		/// * item_id: ID of the item.880		#[weight = <CommonWeights<T>>::approve()]881		#[transactional]882		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {883			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);884885			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))886		}887888		/// 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.889		///890		/// # Permissions891		/// * Collection Owner892		/// * Collection Admin893		/// * Current NFT owner894		/// * Address approved by current NFT owner895		///896		/// # Arguments897		///898		/// * from: Address that owns token.899		///900		/// * recipient: Address of token recipient.901		///902		/// * collection_id.903		///904		/// * item_id: ID of the item.905		///906		/// * value: Amount to transfer.907		#[weight = <CommonWeights<T>>::transfer_from()]908		#[transactional]909		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {910			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);911912			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))913		}914915		/// Set off-chain data schema.916		///917		/// # Permissions918		///919		/// * Collection Owner920		/// * Collection Admin921		///922		/// # Arguments923		///924		/// * collection_id.925		///926		/// * schema: String representing the offchain data schema.927		#[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]928		#[transactional]929		pub fn set_variable_meta_data (930			origin,931			collection_id: CollectionId,932			item_id: TokenId,933			data: BoundedVec<u8, CustomDataLimit>,934		) -> DispatchResultWithPostInfo {935			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);936937			dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))938		}939940		/// Set meta_update_permission value for particular collection941		///942		/// # Permissions943		///944		/// * Collection Owner.945		///946		/// # Arguments947		///948		/// * collection_id: ID of the collection.949		///950		/// * value: New flag value.951		#[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]952		#[transactional]953		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {954			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);955			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;956957			ensure!(958				target_collection.meta_update_permission != MetaUpdatePermission::None,959				<CommonError<T>>::MetadataFlagFrozen,960			);961			target_collection.check_is_owner(&sender)?;962963			target_collection.meta_update_permission = value;964965			target_collection.save()966		}967968		/// Set schema standard969		/// ImageURL970		/// Unique971		///972		/// # Permissions973		///974		/// * Collection Owner975		/// * Collection Admin976		///977		/// # Arguments978		///979		/// * collection_id.980		///981		/// * schema: SchemaVersion: enum982		#[weight = <SelfWeightOf<T>>::set_schema_version()]983		#[transactional]984		pub fn set_schema_version(985			origin,986			collection_id: CollectionId,987			version: SchemaVersion988		) -> DispatchResult {989			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);990			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;991			target_collection.check_is_owner_or_admin(&sender)?;992			target_collection.schema_version = version;993994			<Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(995				collection_id996			));997998			target_collection.save()999		}10001001		/// Set off-chain data schema.1002		///1003		/// # Permissions1004		///1005		/// * Collection Owner1006		/// * Collection Admin1007		///1008		/// # Arguments1009		///1010		/// * collection_id.1011		///1012		/// * schema: String representing the offchain data schema.1013		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1014		#[transactional]1015		pub fn set_offchain_schema(1016			origin,1017			collection_id: CollectionId,1018			schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1019		) -> DispatchResult {1020			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1021			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1022			target_collection.check_is_owner_or_admin(&sender)?;10231024			target_collection.offchain_schema = schema;10251026			<Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1027				collection_id1028			));10291030			target_collection.save()1031		}10321033		/// Set const on-chain data schema.1034		///1035		/// # Permissions1036		///1037		/// * Collection Owner1038		/// * Collection Admin1039		///1040		/// # Arguments1041		///1042		/// * collection_id.1043		///1044		/// * schema: String representing the const on-chain data schema.1045		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1046		#[transactional]1047		pub fn set_const_on_chain_schema (1048			origin,1049			collection_id: CollectionId,1050			schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1051		) -> DispatchResult {1052			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1053			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1054			target_collection.check_is_owner_or_admin(&sender)?;10551056			target_collection.const_on_chain_schema = schema;10571058			<Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1059				collection_id1060			));10611062			target_collection.save()1063		}10641065		/// Set variable on-chain data schema.1066		///1067		/// # Permissions1068		///1069		/// * Collection Owner1070		/// * Collection Admin1071		///1072		/// # Arguments1073		///1074		/// * collection_id.1075		///1076		/// * schema: String representing the variable on-chain data schema.1077		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1078		#[transactional]1079		pub fn set_variable_on_chain_schema (1080			origin,1081			collection_id: CollectionId,1082			schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1083		) -> DispatchResult {1084			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1085			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1086			target_collection.check_is_owner_or_admin(&sender)?;10871088			target_collection.variable_on_chain_schema = schema;10891090			<Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1091				collection_id1092			));10931094			target_collection.save()1095		}10961097		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1098		#[transactional]1099		pub fn set_collection_limits(1100			origin,1101			collection_id: CollectionId,1102			new_limit: CollectionLimits,1103		) -> DispatchResult {1104			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1105			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1106			target_collection.check_is_owner(&sender)?;1107			let old_limit = &target_collection.limits;11081109			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;11101111			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1112				collection_id1113			));11141115			target_collection.save()1116		}1117	}1118}
modifiedpallets/unique/src/tests.rsdiffbeforeafterboth
--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -2865,18 +2865,49 @@
 		let origin1 = Origin::signed(user1);
 		let origin2 = Origin::signed(user2);
 		let account2 = account(user2);
-		
-		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
-		assert_ok!(TemplateModule::set_collection_sponsor(origin1.clone(), collection_id, user1));
-		assert_ok!(TemplateModule::confirm_sponsorship(origin1.clone(), collection_id));
 
+		let collection_id =
+			create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
+		assert_ok!(TemplateModule::set_collection_sponsor(
+			origin1.clone(),
+			collection_id,
+			user1
+		));
+		assert_ok!(TemplateModule::confirm_sponsorship(
+			origin1.clone(),
+			collection_id
+		));
+
 		// Expect error while have no permissions
-		assert!(TemplateModule::create_item(origin2.clone(), collection_id, account2.clone(), default_nft_data().into()).is_err());
+		assert!(TemplateModule::create_item(
+			origin2.clone(),
+			collection_id,
+			account2.clone(),
+			default_nft_data().into()
+		)
+		.is_err());
 
-		assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), collection_id, AccessMode::AllowList));
-		assert_ok!(TemplateModule::add_to_allow_list(origin1.clone(), collection_id, account2.clone()));
-		assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), collection_id, true));
-		
-		assert_ok!(TemplateModule::create_item(origin2, collection_id, account2, default_nft_data().into()));
+		assert_ok!(TemplateModule::set_public_access_mode(
+			origin1.clone(),
+			collection_id,
+			AccessMode::AllowList
+		));
+		assert_ok!(TemplateModule::add_to_allow_list(
+			origin1.clone(),
+			collection_id,
+			account2.clone()
+		));
+		assert_ok!(TemplateModule::set_mint_permission(
+			origin1.clone(),
+			collection_id,
+			true
+		));
+
+		assert_ok!(TemplateModule::create_item(
+			origin2,
+			collection_id,
+			account2,
+			default_nft_data().into()
+		));
 	});
-}
\ No newline at end of file
+}