git.delta.rocks / unique-network / refs/commits / 437764b749e7

difftreelog

source

pallets/unique/src/lib.rs47.2 KiBsourcehistory
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//! # Unique Pallet18//!19//! A pallet governing Unique transactions.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Pallet`]24//!25//! ## Overview26//!27//! The Unique pallet's purpose is to be the primary interface between28//! external users and the inner structure of the Unique chains.29//!30//! It also contains an implementation of [`CollectionHelpers`][`eth`],31//! an Ethereum contract dealing with collection operations.32//!33//! ## Interface34//!35//! ### Dispatchables36//!37//! - `create_collection` - Create a collection of tokens. **Deprecated**, use `create_collection_ex`.38//! - `create_collection_ex` - Create a collection of tokens with explicit parameters.39//! - `destroy_collection` - Destroy a collection if no tokens exist within.40//! - `add_to_allow_list` - Add an address to allow list.41//! - `remove_from_allow_list` - Remove an address from allow list.42//! - `change_collection_owner` - Change the owner of the collection.43//! - `add_collection_admin` - Add an admin to a collection.44//! - `remove_collection_admin` - Remove admin of a collection.45//! - `set_collection_sponsor` - Invite a new collection sponsor.46//! - `confirm_sponsorship` - Confirm own sponsorship of a collection, becoming the sponsor.47//! - `remove_collection_sponsor` - Remove a sponsor from a collection.48//! - `create_item` - Create an item within a collection.49//! - `create_multiple_items` - Create multiple items within a collection.50//! - `set_collection_properties` - Add or change collection properties.51//! - `delete_collection_properties` - Delete specified collection properties.52//! - `set_token_properties` - Add or change token properties.53//! - `delete_token_properties` - Delete token properties.54//! - `set_token_property_permissions` - Add or change token property permissions of a collection.55//! - `create_multiple_items_ex` - Create multiple items within a collection with explicitly specified initial parameters.56//! - `set_transfers_enabled_flag` - Completely allow or disallow transfers for a particular collection.57//! - `burn_item` - Destroy an item.58//! - `burn_from` - Destroy an item on behalf of the owner as a non-owner account.59//! - `transfer` - Change ownership of the token.60//! - `transfer_from` - Change ownership of the token on behalf of the owner as a non-owner account.61//! - `approve` - Allow a non-permissioned address to transfer or burn an item.62//! - `set_collection_limits` - Set specific limits of a collection.63//! - `set_collection_permissions` - Set specific permissions of a collection.64//! - `repartition` - Re-partition a refungible token, while owning all of its parts.6566#![recursion_limit = "1024"]67#![cfg_attr(not(feature = "std"), no_std)]68#![allow(69	clippy::too_many_arguments,70	clippy::unnecessary_mut_passed,71	clippy::unused_unit72)]7374extern crate alloc;7576use frame_support::pallet_prelude::*;77use frame_system::pallet_prelude::*;78pub use pallet::*;79pub mod eth;8081#[cfg(feature = "runtime-benchmarks")]82pub mod benchmarking;83pub mod weights;8485#[frame_support::pallet]86pub mod pallet {87	use frame_support::{88		dispatch::{DispatchErrorWithPostInfo, DispatchResult, PostDispatchInfo},89		ensure, fail,90		storage::Key,91		BoundedVec,92	};93	use frame_system::{ensure_root, ensure_signed};94	use pallet_common::{95		dispatch::{dispatch_tx, CollectionDispatch},96		CollectionHandle, CommonCollectionOperations, CommonWeightInfo, Pallet as PalletCommon,97		RefungibleExtensionsWeightInfo,98	};99	use pallet_evm::account::CrossAccountId;100	use pallet_structure::weights::WeightInfo as StructureWeightInfo;101	use scale_info::TypeInfo;102	use sp_std::{vec, vec::Vec};103	use up_data_structs::{104		budget, CollectionId, CollectionLimits, CollectionMode, CollectionPermissions,105		CreateCollectionData, CreateItemData, CreateItemExData, Property, PropertyKey,106		PropertyKeyPermission, TokenId, COLLECTION_ADMINS_LIMIT, MAX_COLLECTION_DESCRIPTION_LENGTH,107		MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_PROPERTIES_SIZE, MAX_PROPERTIES_PER_ITEM,108		MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH, MAX_TOKEN_PREFIX_LENGTH,109		MAX_TOKEN_PROPERTIES_SIZE,110	};111	use weights::WeightInfo;112113	use super::*;114115	/// Errors for the common Unique transactions.116	#[pallet::error]117	pub enum Error<T> {118		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].119		CollectionDecimalPointLimitExceeded,120		/// Length of items properties must be greater than 0.121		EmptyArgument,122		/// Repertition is only supported by refungible collection.123		RepartitionCalledOnNonRefungibleCollection,124	}125126	/// Configuration trait of this pallet.127	#[pallet::config]128	pub trait Config: frame_system::Config + pallet_common::Config + Sized + TypeInfo {129		/// Weight information for extrinsics in this pallet.130		type WeightInfo: WeightInfo;131132		/// Weight information for common pallet operations.133		type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;134135		type StructureWeightInfo: StructureWeightInfo;136137		/// Weight info information for extra refungible pallet operations.138		type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;139	}140141	#[pallet::pallet]142	pub struct Pallet<T>(_);143144	pub type SelfWeightOf<T> = <T as Config>::WeightInfo;145146	// # Used definitions147	//148	// ## User control levels149	//150	// chain-controlled - key is uncontrolled by user151	//                    i.e autoincrementing index152	//                    can use non-cryptographic hash153	// real - key is controlled by user154	//        but it is hard to generate enough colliding values, i.e owner of signed txs155	//        can use non-cryptographic hash156	// controlled - key is completly controlled by users157	//              i.e maps with mutable keys158	//              should use cryptographic hash159	//160	// ## User control level downgrade reasons161	//162	// ?1 - chain-controlled -> controlled163	//      collections/tokens can be destroyed, resulting in massive holes164	// ?2 - chain-controlled -> controlled165	//      same as ?1, but can be only added, resulting in easier exploitation166	// ?3 - real -> controlled167	//      no confirmation required, so addresses can be easily generated168169	//#region Private members170	/// Used for migrations171	#[pallet::storage]172	pub type ChainVersion<T> = StorageValue<_, u64, ValueQuery>;173	//#endregion174175	//#region Tokens transfer sponosoring rate limit baskets176	/// (Collection id (controlled?2), who created (real))177	/// TODO: Off chain worker should remove from this map when collection gets removed178	#[pallet::storage]179	#[pallet::getter(fn create_item_busket)]180	pub type CreateItemBasket<T: Config> = StorageMap<181		Hasher = Blake2_128Concat,182		Key = (CollectionId, T::AccountId),183		Value = BlockNumberFor<T>,184		QueryKind = OptionQuery,185	>;186	/// Collection id (controlled?2), token id (controlled?2)187	#[pallet::storage]188	#[pallet::getter(fn nft_transfer_basket)]189	pub type NftTransferBasket<T: Config> = StorageDoubleMap<190		Hasher1 = Blake2_128Concat,191		Key1 = CollectionId,192		Hasher2 = Blake2_128Concat,193		Key2 = TokenId,194		Value = BlockNumberFor<T>,195		QueryKind = OptionQuery,196	>;197	/// Collection id (controlled?2), owning user (real)198	#[pallet::storage]199	#[pallet::getter(fn fungible_transfer_basket)]200	pub type FungibleTransferBasket<T: Config> = StorageDoubleMap<201		Hasher1 = Blake2_128Concat,202		Key1 = CollectionId,203		Hasher2 = Twox64Concat,204		Key2 = T::AccountId,205		Value = BlockNumberFor<T>,206		QueryKind = OptionQuery,207	>;208	/// Collection id (controlled?2), token id (controlled?2)209	#[pallet::storage]210	#[pallet::getter(fn refungible_transfer_basket)]211	pub type ReFungibleTransferBasket<T: Config> = StorageNMap<212		Key = (213			Key<Blake2_128Concat, CollectionId>,214			Key<Blake2_128Concat, TokenId>,215			Key<Twox64Concat, T::AccountId>,216		),217		Value = BlockNumberFor<T>,218		QueryKind = OptionQuery,219	>;220	//#endregion221222	/// Last sponsoring of token property setting // todo:doc rephrase this and the following223	#[pallet::storage]224	#[pallet::getter(fn token_property_basket)]225	pub type TokenPropertyBasket<T: Config> = StorageDoubleMap<226		Hasher1 = Blake2_128Concat,227		Key1 = CollectionId,228		Hasher2 = Blake2_128Concat,229		Key2 = TokenId,230		Value = BlockNumberFor<T>,231		QueryKind = OptionQuery,232	>;233234	/// Last sponsoring of NFT approval in a collection235	#[pallet::storage]236	#[pallet::getter(fn nft_approve_basket)]237	pub type NftApproveBasket<T: Config> = StorageDoubleMap<238		Hasher1 = Blake2_128Concat,239		Key1 = CollectionId,240		Hasher2 = Blake2_128Concat,241		Key2 = TokenId,242		Value = BlockNumberFor<T>,243		QueryKind = OptionQuery,244	>;245	/// Last sponsoring of fungible tokens approval in a collection246	#[pallet::storage]247	#[pallet::getter(fn fungible_approve_basket)]248	pub type FungibleApproveBasket<T: Config> = StorageDoubleMap<249		Hasher1 = Blake2_128Concat,250		Key1 = CollectionId,251		Hasher2 = Twox64Concat,252		Key2 = T::AccountId,253		Value = BlockNumberFor<T>,254		QueryKind = OptionQuery,255	>;256	/// Last sponsoring of RFT approval in a collection257	#[pallet::storage]258	#[pallet::getter(fn refungible_approve_basket)]259	pub type RefungibleApproveBasket<T: Config> = StorageNMap<260		Key = (261			Key<Blake2_128Concat, CollectionId>,262			Key<Blake2_128Concat, TokenId>,263			Key<Twox64Concat, T::AccountId>,264		),265		Value = BlockNumberFor<T>,266		QueryKind = OptionQuery,267	>;268269	#[pallet::extra_constants]270	impl<T: Config> Pallet<T> {271		/// A maximum number of levels of depth in the token nesting tree.272		fn nesting_budget() -> u32 {273			5274		}275276		/// Maximal length of a collection name.277		fn max_collection_name_length() -> u32 {278			MAX_COLLECTION_NAME_LENGTH279		}280281		/// Maximal length of a collection description.282		fn max_collection_description_length() -> u32 {283			MAX_COLLECTION_DESCRIPTION_LENGTH284		}285286		/// Maximal length of a token prefix.287		fn max_token_prefix_length() -> u32 {288			MAX_TOKEN_PREFIX_LENGTH289		}290291		/// Maximum admins per collection.292		fn collection_admins_limit() -> u32 {293			COLLECTION_ADMINS_LIMIT294		}295296		/// Maximal length of a property key.297		fn max_property_key_length() -> u32 {298			MAX_PROPERTY_KEY_LENGTH299		}300301		/// Maximal length of a property value.302		fn max_property_value_length() -> u32 {303			MAX_PROPERTY_VALUE_LENGTH304		}305306		/// A maximum number of token properties.307		fn max_properties_per_item() -> u32 {308			MAX_PROPERTIES_PER_ITEM309		}310311		/// Maximum size for all collection properties.312		fn max_collection_properties_size() -> u32 {313			MAX_COLLECTION_PROPERTIES_SIZE314		}315316		/// Maximum size of all token properties.317		fn max_token_properties_size() -> u32 {318			MAX_TOKEN_PROPERTIES_SIZE319		}320321		/// Default NFT collection limit.322		fn nft_default_collection_limits() -> CollectionLimits {323			CollectionLimits::with_default_limits(CollectionMode::NFT)324		}325326		/// Default RFT collection limit.327		fn rft_default_collection_limits() -> CollectionLimits {328			CollectionLimits::with_default_limits(CollectionMode::ReFungible)329		}330331		/// Default FT collection limit.332		fn ft_default_collection_limits() -> CollectionLimits {333			CollectionLimits::with_default_limits(CollectionMode::Fungible(0))334		}335	}336337	/// Type alias to Pallet, to be used by construct_runtime.338	#[pallet::call]339	impl<T: Config> Pallet<T> {340		/// Create a collection of tokens.341		///342		/// Each Token may have multiple properties encoded as an array of bytes343		/// of certain length. The initial owner of the collection is set344		/// to the address that signed the transaction and can be changed later.345		///346		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.347		///348		/// # Permissions349		///350		/// * Anyone - becomes the owner of the new collection.351		///352		/// # Arguments353		///354		/// * `collection_name`: Wide-character string with collection name355		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).356		/// * `collection_description`: Wide-character string with collection description357		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).358		/// * `token_prefix`: Byte string containing the token prefix to mark a collection359		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).360		/// * `mode`: Type of items stored in the collection and type dependent data.361		///362		/// returns collection ID363		///364		/// Deprecated: `create_collection_ex` is more up-to-date and advanced, prefer it instead.365		#[pallet::call_index(0)]366		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]367		pub fn create_collection(368			origin: OriginFor<T>,369			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,370			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,371			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,372			mode: CollectionMode,373		) -> DispatchResult {374			let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {375				name: collection_name,376				description: collection_description,377				token_prefix,378				mode,379				..Default::default()380			};381			Self::create_collection_ex(origin, data)382		}383384		/// Create a collection with explicit parameters.385		///386		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.387		///388		/// # Permissions389		///390		/// * Anyone - becomes the owner of the new collection.391		///392		/// # Arguments393		///394		/// * `data`: Explicit data of a collection used for its creation.395		#[pallet::call_index(1)]396		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]397		pub fn create_collection_ex(398			origin: OriginFor<T>,399			data: CreateCollectionData<T::CrossAccountId>,400		) -> DispatchResult {401			let sender = ensure_signed(origin)?;402403			// =========404			let sender = T::CrossAccountId::from_sub(sender);405			let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;406407			Ok(())408		}409410		/// Destroy a collection if no tokens exist within.411		///412		/// # Permissions413		///414		/// * Collection owner415		///416		/// # Arguments417		///418		/// * `collection_id`: Collection to destroy.419		#[pallet::call_index(2)]420		#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]421		pub fn destroy_collection(422			origin: OriginFor<T>,423			collection_id: CollectionId,424		) -> DispatchResult {425			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);426427			Self::destroy_collection_internal(sender, collection_id)428		}429430		/// Add an address to allow list.431		///432		/// # Permissions433		///434		/// * Collection owner435		/// * Collection admin436		///437		/// # Arguments438		///439		/// * `collection_id`: ID of the modified collection.440		/// * `address`: ID of the address to be added to the allowlist.441		#[pallet::call_index(3)]442		#[pallet::weight(<SelfWeightOf<T>>::add_to_allow_list())]443		pub fn add_to_allow_list(444			origin: OriginFor<T>,445			collection_id: CollectionId,446			address: T::CrossAccountId,447		) -> DispatchResult {448			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {449				fail!(<pallet_common::Error<T>>::UnsupportedOperation);450			}451452			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);453			let collection = <CollectionHandle<T>>::try_get(collection_id)?;454			collection.check_is_internal()?;455456			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, true)?;457458			Ok(())459		}460461		/// Remove an address from allow list.462		///463		/// # Permissions464		///465		/// * Collection owner466		/// * Collection admin467		///468		/// # Arguments469		///470		/// * `collection_id`: ID of the modified collection.471		/// * `address`: ID of the address to be removed from the allowlist.472		#[pallet::call_index(4)]473		#[pallet::weight(<SelfWeightOf<T>>::remove_from_allow_list())]474		pub fn remove_from_allow_list(475			origin: OriginFor<T>,476			collection_id: CollectionId,477			address: T::CrossAccountId,478		) -> DispatchResult {479			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {480				fail!(<pallet_common::Error<T>>::UnsupportedOperation);481			}482483			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);484			let collection = <CollectionHandle<T>>::try_get(collection_id)?;485			collection.check_is_internal()?;486487			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, false)?;488489			Ok(())490		}491492		/// Change the owner of the collection.493		///494		/// # Permissions495		///496		/// * Collection owner497		///498		/// # Arguments499		///500		/// * `collection_id`: ID of the modified collection.501		/// * `new_owner`: ID of the account that will become the owner.502		#[pallet::call_index(5)]503		#[pallet::weight(<SelfWeightOf<T>>::change_collection_owner())]504		pub fn change_collection_owner(505			origin: OriginFor<T>,506			collection_id: CollectionId,507			new_owner: T::AccountId,508		) -> DispatchResult {509			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {510				fail!(<pallet_common::Error<T>>::UnsupportedOperation);511			}512			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);513			let new_owner = T::CrossAccountId::from_sub(new_owner);514			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;515			target_collection.change_owner(sender, new_owner)516		}517518		/// Add an admin to a collection.519		///520		/// NFT Collection can be controlled by multiple admin addresses521		/// (some which can also be servers, for example). Admins can issue522		/// and burn NFTs, as well as add and remove other admins,523		/// but cannot change NFT or Collection ownership.524		///525		/// # Permissions526		///527		/// * Collection owner528		/// * Collection admin529		///530		/// # Arguments531		///532		/// * `collection_id`: ID of the Collection to add an admin for.533		/// * `new_admin`: Address of new admin to add.534		#[pallet::call_index(6)]535		#[pallet::weight(<SelfWeightOf<T>>::add_collection_admin())]536		pub fn add_collection_admin(537			origin: OriginFor<T>,538			collection_id: CollectionId,539			new_admin_id: T::CrossAccountId,540		) -> DispatchResult {541			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {542				fail!(<pallet_common::Error<T>>::UnsupportedOperation);543			}544			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);545			let collection = <CollectionHandle<T>>::try_get(collection_id)?;546			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)547		}548549		/// Remove admin of a collection.550		///551		/// An admin address can remove itself. List of admins may become empty,552		/// in which case only Collection Owner will be able to add an Admin.553		///554		/// # Permissions555		///556		/// * Collection owner557		/// * Collection admin558		///559		/// # Arguments560		///561		/// * `collection_id`: ID of the collection to remove the admin for.562		/// * `account_id`: Address of the admin to remove.563		#[pallet::call_index(7)]564		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_admin())]565		pub fn remove_collection_admin(566			origin: OriginFor<T>,567			collection_id: CollectionId,568			account_id: T::CrossAccountId,569		) -> DispatchResult {570			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {571				fail!(<pallet_common::Error<T>>::UnsupportedOperation);572			}573			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);574			let collection = <CollectionHandle<T>>::try_get(collection_id)?;575			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)576		}577578		/// Set (invite) a new collection sponsor.579		///580		/// If successful, confirmation from the sponsor-to-be will be pending.581		///582		/// # Permissions583		///584		/// * Collection owner585		/// * Collection admin586		///587		/// # Arguments588		///589		/// * `collection_id`: ID of the modified collection.590		/// * `new_sponsor`: ID of the account of the sponsor-to-be.591		#[pallet::call_index(8)]592		#[pallet::weight(<SelfWeightOf<T>>::set_collection_sponsor())]593		pub fn set_collection_sponsor(594			origin: OriginFor<T>,595			collection_id: CollectionId,596			new_sponsor: T::AccountId,597		) -> DispatchResult {598			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {599				fail!(<pallet_common::Error<T>>::UnsupportedOperation);600			}601			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);602			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;603			target_collection.set_sponsor(&sender, new_sponsor.clone())604		}605606		/// Confirm own sponsorship of a collection, becoming the sponsor.607		///608		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].609		/// Sponsor can pay the fees of a transaction instead of the sender,610		/// but only within specified limits.611		///612		/// # Permissions613		///614		/// * Sponsor-to-be615		///616		/// # Arguments617		///618		/// * `collection_id`: ID of the collection with the pending sponsor.619		#[pallet::call_index(9)]620		#[pallet::weight(<SelfWeightOf<T>>::confirm_sponsorship())]621		pub fn confirm_sponsorship(622			origin: OriginFor<T>,623			collection_id: CollectionId,624		) -> DispatchResult {625			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {626				fail!(<pallet_common::Error<T>>::UnsupportedOperation);627			}628			let sender = ensure_signed(origin)?;629			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;630			target_collection.confirm_sponsorship(&sender)631		}632633		/// Remove a collection's a sponsor, making everyone pay for their own transactions.634		///635		/// # Permissions636		///637		/// * Collection owner638		///639		/// # Arguments640		///641		/// * `collection_id`: ID of the collection with the sponsor to remove.642		#[pallet::call_index(10)]643		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_sponsor())]644		pub fn remove_collection_sponsor(645			origin: OriginFor<T>,646			collection_id: CollectionId,647		) -> DispatchResult {648			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {649				fail!(<pallet_common::Error<T>>::UnsupportedOperation);650			}651			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);652			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;653			target_collection.remove_sponsor(&sender)654		}655656		/// Mint an item within a collection.657		///658		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].659		///660		/// # Permissions661		///662		/// * Collection owner663		/// * Collection admin664		/// * Anyone if665		///     * Allow List is enabled, and666		///     * Address is added to allow list, and667		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])668		///669		/// # Arguments670		///671		/// * `collection_id`: ID of the collection to which an item would belong.672		/// * `owner`: Address of the initial owner of the item.673		/// * `data`: Token data describing the item to store on chain.674		#[pallet::call_index(11)]675		#[pallet::weight(T::CommonWeightInfo::create_item(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]676		pub fn create_item(677			origin: OriginFor<T>,678			collection_id: CollectionId,679			owner: T::CrossAccountId,680			data: CreateItemData,681		) -> DispatchResultWithPostInfo {682			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);683			let budget = Self::structure_nesting_budget();684685			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {686				d.create_item(sender, owner, data, &budget)687			})688		}689690		/// Create multiple items within a collection.691		///692		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].693		///694		/// # Permissions695		///696		/// * Collection owner697		/// * Collection admin698		/// * Anyone if699		///     * Allow List is enabled, and700		///     * Address is added to the allow list, and701		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])702		///703		/// # Arguments704		///705		/// * `collection_id`: ID of the collection to which the tokens would belong.706		/// * `owner`: Address of the initial owner of the tokens.707		/// * `items_data`: Vector of data describing each item to be created.708		#[pallet::call_index(12)]709		#[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data) + <Pallet<T>>::nesting_budget_predispatch_weight())]710		pub fn create_multiple_items(711			origin: OriginFor<T>,712			collection_id: CollectionId,713			owner: T::CrossAccountId,714			items_data: Vec<CreateItemData>,715		) -> DispatchResultWithPostInfo {716			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);717			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);718			let budget = Self::structure_nesting_budget();719720			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {721				d.create_multiple_items(sender, owner, items_data, &budget)722			})723		}724725		/// Add or change collection properties.726		///727		/// # Permissions728		///729		/// * Collection owner730		/// * Collection admin731		///732		/// # Arguments733		///734		/// * `collection_id`: ID of the modified collection.735		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.736		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.737		#[pallet::call_index(13)]738		#[pallet::weight(T::CommonWeightInfo::set_collection_properties(properties.len() as u32))]739		pub fn set_collection_properties(740			origin: OriginFor<T>,741			collection_id: CollectionId,742			properties: Vec<Property>,743		) -> DispatchResultWithPostInfo {744			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);745746			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);747748			dispatch_tx::<T, _>(collection_id, |d| {749				d.set_collection_properties(sender, properties)750			})751		}752753		/// Delete specified collection properties.754		///755		/// # Permissions756		///757		/// * Collection Owner758		/// * Collection Admin759		///760		/// # Arguments761		///762		/// * `collection_id`: ID of the modified collection.763		/// * `property_keys`: Vector of keys of the properties to be deleted.764		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.765		#[pallet::call_index(14)]766		#[pallet::weight(T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32))]767		pub fn delete_collection_properties(768			origin: OriginFor<T>,769			collection_id: CollectionId,770			property_keys: Vec<PropertyKey>,771		) -> DispatchResultWithPostInfo {772			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);773774			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);775776			dispatch_tx::<T, _>(collection_id, |d| {777				d.delete_collection_properties(&sender, property_keys)778			})779		}780781		/// Add or change token properties according to collection's permissions.782		/// Currently properties only work with NFTs.783		///784		/// # Permissions785		///786		/// * Depends on collection's token property permissions and specified property mutability:787		/// 	* Collection owner788		/// 	* Collection admin789		/// 	* Token owner790		///791		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].792		///793		/// # Arguments794		///795		/// * `collection_id: ID of the collection to which the token belongs.796		/// * `token_id`: ID of the modified token.797		/// * `properties`: Vector of key-value pairs stored as the token's metadata.798		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.799		#[pallet::call_index(15)]800		#[pallet::weight(T::CommonWeightInfo::set_token_properties(properties.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]801		pub fn set_token_properties(802			origin: OriginFor<T>,803			collection_id: CollectionId,804			token_id: TokenId,805			properties: Vec<Property>,806		) -> DispatchResultWithPostInfo {807			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);808809			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);810			let budget = Self::structure_nesting_budget();811812			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {813				d.set_token_properties(sender, token_id, properties, &budget)814			})815		}816817		/// Delete specified token properties. Currently properties only work with NFTs.818		///819		/// # Permissions820		///821		/// * Depends on collection's token property permissions and specified property mutability:822		/// 	* Collection owner823		/// 	* Collection admin824		/// 	* Token owner825		///826		/// # Arguments827		///828		/// * `collection_id`: ID of the collection to which the token belongs.829		/// * `token_id`: ID of the modified token.830		/// * `property_keys`: Vector of keys of the properties to be deleted.831		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.832		#[pallet::call_index(16)]833		#[pallet::weight(T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]834		pub fn delete_token_properties(835			origin: OriginFor<T>,836			collection_id: CollectionId,837			token_id: TokenId,838			property_keys: Vec<PropertyKey>,839		) -> DispatchResultWithPostInfo {840			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);841842			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);843			let budget = Self::structure_nesting_budget();844845			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {846				d.delete_token_properties(sender, token_id, property_keys, &budget)847			})848		}849850		/// Add or change token property permissions of a collection.851		///852		/// Without a permission for a particular key, a property with that key853		/// cannot be created in a token.854		///855		/// # Permissions856		///857		/// * Collection owner858		/// * Collection admin859		///860		/// # Arguments861		///862		/// * `collection_id`: ID of the modified collection.863		/// * `property_permissions`: Vector of permissions for property keys.864		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.865		#[pallet::call_index(17)]866		#[pallet::weight(T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32))]867		pub fn set_token_property_permissions(868			origin: OriginFor<T>,869			collection_id: CollectionId,870			property_permissions: Vec<PropertyKeyPermission>,871		) -> DispatchResultWithPostInfo {872			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);873874			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);875876			dispatch_tx::<T, _>(collection_id, |d| {877				d.set_token_property_permissions(&sender, property_permissions)878			})879		}880881		/// Create multiple items within a collection with explicitly specified initial parameters.882		///883		/// # Permissions884		///885		/// * Collection owner886		/// * Collection admin887		/// * Anyone if888		///     * Allow List is enabled, and889		///     * Address is added to allow list, and890		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])891		///892		/// # Arguments893		///894		/// * `collection_id`: ID of the collection to which the tokens would belong.895		/// * `data`: Explicit item creation data.896		#[pallet::call_index(18)]897		#[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]898		pub fn create_multiple_items_ex(899			origin: OriginFor<T>,900			collection_id: CollectionId,901			data: CreateItemExData<T::CrossAccountId>,902		) -> DispatchResultWithPostInfo {903			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);904			let budget = Self::structure_nesting_budget();905906			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {907				d.create_multiple_items_ex(sender, data, &budget)908			})909		}910911		/// Completely allow or disallow transfers for a particular collection.912		///913		/// # Permissions914		///915		/// * Collection owner916		///917		/// # Arguments918		///919		/// * `collection_id`: ID of the collection.920		/// * `value`: New value of the flag, are transfers allowed?921		#[pallet::call_index(19)]922		#[pallet::weight(<SelfWeightOf<T>>::set_transfers_enabled_flag())]923		pub fn set_transfers_enabled_flag(924			origin: OriginFor<T>,925			collection_id: CollectionId,926			value: bool,927		) -> DispatchResult {928			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {929				fail!(<pallet_common::Error<T>>::UnsupportedOperation);930			}931			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);932			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;933			target_collection.check_is_internal()?;934			target_collection.check_is_owner(&sender)?;935936			// =========937938			target_collection.limits.transfers_enabled = Some(value);939			target_collection.save()940		}941942		/// Destroy an item.943		///944		/// # Permissions945		///946		/// * Collection owner947		/// * Collection admin948		/// * Current item owner949		///950		/// # Arguments951		///952		/// * `collection_id`: ID of the collection to which the item belongs.953		/// * `item_id`: ID of item to burn.954		/// * `value`: Number of pieces of the item to destroy.955		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.956		///     * Fungible Mode: The desired number of pieces to burn.957		///     * Re-Fungible Mode: The desired number of pieces to burn.958		#[pallet::call_index(20)]959		#[pallet::weight(T::CommonWeightInfo::burn_item())]960		pub fn burn_item(961			origin: OriginFor<T>,962			collection_id: CollectionId,963			item_id: TokenId,964			value: u128,965		) -> DispatchResultWithPostInfo {966			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);967968			let post_info =969				dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;970			if value == 1 {971				<NftTransferBasket<T>>::remove(collection_id, item_id);972				<NftApproveBasket<T>>::remove(collection_id, item_id);973			}974			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?975			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());976			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));977			Ok(post_info)978		}979980		/// Destroy a token on behalf of the owner as a non-owner account.981		///982		/// See also: [`approve`][`Pallet::approve`].983		///984		/// After this method executes, one approval is removed from the total so that985		/// the approved address will not be able to transfer this item again from this owner.986		///987		/// # Permissions988		///989		/// * Collection owner990		/// * Collection admin991		/// * Current token owner992		/// * Address approved by current item owner993		///994		/// # Arguments995		///996		/// * `from`: The owner of the burning item.997		/// * `collection_id`: ID of the collection to which the item belongs.998		/// * `item_id`: ID of item to burn.999		/// * `value`: Number of pieces to burn.1000		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1001		///     * Fungible Mode: The desired number of pieces to burn.1002		///     * Re-Fungible Mode: The desired number of pieces to burn.1003		#[pallet::call_index(21)]1004		#[pallet::weight(T::CommonWeightInfo::burn_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1005		pub fn burn_from(1006			origin: OriginFor<T>,1007			collection_id: CollectionId,1008			from: T::CrossAccountId,1009			item_id: TokenId,1010			value: u128,1011		) -> DispatchResultWithPostInfo {1012			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013			let budget = Self::structure_nesting_budget();10141015			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {1016				d.burn_from(sender, from, item_id, value, &budget)1017			})1018		}10191020		/// Change ownership of the token.1021		///1022		/// # Permissions1023		///1024		/// * Collection owner1025		/// * Collection admin1026		/// * Current token owner1027		///1028		/// # Arguments1029		///1030		/// * `recipient`: Address of token recipient.1031		/// * `collection_id`: ID of the collection the item belongs to.1032		/// * `item_id`: ID of the item.1033		///     * Non-Fungible Mode: Required.1034		///     * Fungible Mode: Ignored.1035		///     * Re-Fungible Mode: Required.1036		///1037		/// * `value`: Amount to transfer.1038		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1039		///     * Fungible Mode: The desired number of pieces to transfer.1040		///     * Re-Fungible Mode: The desired number of pieces to transfer.1041		#[pallet::call_index(22)]1042		#[pallet::weight(T::CommonWeightInfo::transfer() + <Pallet<T>>::nesting_budget_predispatch_weight())]1043		pub fn transfer(1044			origin: OriginFor<T>,1045			recipient: T::CrossAccountId,1046			collection_id: CollectionId,1047			item_id: TokenId,1048			value: u128,1049		) -> DispatchResultWithPostInfo {1050			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1051			let budget = Self::structure_nesting_budget();10521053			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {1054				d.transfer(sender, recipient, item_id, value, &budget)1055			})1056		}10571058		/// Allow a non-permissioned address to transfer or burn an item.1059		///1060		/// # Permissions1061		///1062		/// * Collection owner1063		/// * Collection admin1064		/// * Current item owner1065		///1066		/// # Arguments1067		///1068		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.1069		/// * `collection_id`: ID of the collection the item belongs to.1070		/// * `item_id`: ID of the item transactions on which are now approved.1071		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1072		/// Set to 0 to revoke the approval.1073		#[pallet::call_index(23)]1074		#[pallet::weight(T::CommonWeightInfo::approve())]1075		pub fn approve(1076			origin: OriginFor<T>,1077			spender: T::CrossAccountId,1078			collection_id: CollectionId,1079			item_id: TokenId,1080			amount: u128,1081		) -> DispatchResultWithPostInfo {1082			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10831084			dispatch_tx::<T, _>(collection_id, |d| {1085				d.approve(sender, spender, item_id, amount)1086			})1087		}10881089		/// Allow a non-permissioned address to transfer or burn an item from owner's eth mirror.1090		///1091		/// # Permissions1092		///1093		/// * Collection owner1094		/// * Collection admin1095		/// * Current item owner1096		///1097		/// # Arguments1098		///1099		/// * `from`: Owner's account eth mirror1100		/// * `to`: Account to be approved to make specific transactions on non-owned tokens.1101		/// * `collection_id`: ID of the collection the item belongs to.1102		/// * `item_id`: ID of the item transactions on which are now approved.1103		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1104		/// Set to 0 to revoke the approval.1105		#[pallet::call_index(24)]1106		#[pallet::weight(T::CommonWeightInfo::approve_from())]1107		pub fn approve_from(1108			origin: OriginFor<T>,1109			from: T::CrossAccountId,1110			to: T::CrossAccountId,1111			collection_id: CollectionId,1112			item_id: TokenId,1113			amount: u128,1114		) -> DispatchResultWithPostInfo {1115			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11161117			dispatch_tx::<T, _>(collection_id, |d| {1118				d.approve_from(sender, from, to, item_id, amount)1119			})1120		}11211122		/// Change ownership of an item on behalf of the owner as a non-owner account.1123		///1124		/// See the [`approve`][`Pallet::approve`] method for additional information.1125		///1126		/// After this method executes, one approval is removed from the total so that1127		/// the approved address will not be able to transfer this item again from this owner.1128		///1129		/// # Permissions1130		///1131		/// * Collection owner1132		/// * Collection admin1133		/// * Current item owner1134		/// * Address approved by current item owner1135		///1136		/// # Arguments1137		///1138		/// * `from`: Address that currently owns the token.1139		/// * `recipient`: Address of the new token-owner-to-be.1140		/// * `collection_id`: ID of the collection the item.1141		/// * `item_id`: ID of the item to be transferred.1142		/// * `value`: Amount to transfer.1143		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1144		///     * Fungible Mode: The desired number of pieces to transfer.1145		///     * Re-Fungible Mode: The desired number of pieces to transfer.1146		#[pallet::call_index(25)]1147		#[pallet::weight(T::CommonWeightInfo::transfer_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1148		pub fn transfer_from(1149			origin: OriginFor<T>,1150			from: T::CrossAccountId,1151			recipient: T::CrossAccountId,1152			collection_id: CollectionId,1153			item_id: TokenId,1154			value: u128,1155		) -> DispatchResultWithPostInfo {1156			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1157			let budget = Self::structure_nesting_budget();11581159			Self::dispatch_tx_with_nesting_budget(collection_id, &budget, |d| {1160				d.transfer_from(sender, from, recipient, item_id, value, &budget)1161			})1162		}11631164		/// Set specific limits of a collection. Empty, or None fields mean chain default.1165		///1166		/// # Permissions1167		///1168		/// * Collection owner1169		/// * Collection admin1170		///1171		/// # Arguments1172		///1173		/// * `collection_id`: ID of the modified collection.1174		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1175		/// will not overwrite the old ones.1176		#[pallet::call_index(26)]1177		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1178		pub fn set_collection_limits(1179			origin: OriginFor<T>,1180			collection_id: CollectionId,1181			new_limit: CollectionLimits,1182		) -> DispatchResult {1183			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1184				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1185			}1186			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1187			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1188			<PalletCommon<T>>::update_limits(&sender, &mut target_collection, new_limit)1189		}11901191		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1192		///1193		/// # Permissions1194		///1195		/// * Collection owner1196		/// * Collection admin1197		///1198		/// # Arguments1199		///1200		/// * `collection_id`: ID of the modified collection.1201		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1202		/// will not overwrite the old ones.1203		#[pallet::call_index(27)]1204		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1205		pub fn set_collection_permissions(1206			origin: OriginFor<T>,1207			collection_id: CollectionId,1208			new_permission: CollectionPermissions,1209		) -> DispatchResult {1210			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1211				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1212			}1213			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1214			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1215			<PalletCommon<T>>::update_permissions(&sender, &mut target_collection, new_permission)1216		}12171218		/// Re-partition a refungible token, while owning all of its parts/pieces.1219		///1220		/// # Permissions1221		///1222		/// * Token owner (must own every part)1223		///1224		/// # Arguments1225		///1226		/// * `collection_id`: ID of the collection the RFT belongs to.1227		/// * `token_id`: ID of the RFT.1228		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1229		#[pallet::call_index(28)]1230		#[pallet::weight(T::RefungibleExtensionsWeightInfo::repartition())]1231		pub fn repartition(1232			origin: OriginFor<T>,1233			collection_id: CollectionId,1234			token_id: TokenId,1235			amount: u128,1236		) -> DispatchResultWithPostInfo {1237			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1238			dispatch_tx::<T, _>(collection_id, |d| {1239				if let Some(refungible_extensions) = d.refungible_extensions() {1240					refungible_extensions.repartition(&sender, token_id, amount)1241				} else {1242					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1243				}1244			})1245		}12461247		/// Sets or unsets the approval of a given operator.1248		///1249		/// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.1250		///1251		/// # Arguments1252		///1253		/// * `owner`: Token owner1254		/// * `operator`: Operator1255		/// * `approve`: Should operator status be granted or revoked?1256		#[pallet::call_index(29)]1257		#[pallet::weight(T::CommonWeightInfo::set_allowance_for_all())]1258		pub fn set_allowance_for_all(1259			origin: OriginFor<T>,1260			collection_id: CollectionId,1261			operator: T::CrossAccountId,1262			approve: bool,1263		) -> DispatchResultWithPostInfo {1264			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1265			dispatch_tx::<T, _>(collection_id, |d| {1266				d.set_allowance_for_all(sender, operator, approve)1267			})1268		}12691270		/// Repairs a collection if the data was somehow corrupted.1271		///1272		/// # Arguments1273		///1274		/// * `collection_id`: ID of the collection to repair.1275		#[pallet::call_index(30)]1276		#[pallet::weight(<SelfWeightOf<T>>::force_repair_collection())]1277		pub fn force_repair_collection(1278			origin: OriginFor<T>,1279			collection_id: CollectionId,1280		) -> DispatchResult {1281			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1282				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1283			}1284			ensure_root(origin)?;1285			<PalletCommon<T>>::repair_collection(collection_id)1286		}12871288		/// Repairs a token if the data was somehow corrupted.1289		///1290		/// # Arguments1291		///1292		/// * `collection_id`: ID of the collection the item belongs to.1293		/// * `item_id`: ID of the item.1294		#[pallet::call_index(31)]1295		#[pallet::weight(T::CommonWeightInfo::force_repair_item())]1296		pub fn force_repair_item(1297			origin: OriginFor<T>,1298			collection_id: CollectionId,1299			item_id: TokenId,1300		) -> DispatchResultWithPostInfo {1301			ensure_root(origin)?;1302			dispatch_tx::<T, _>(collection_id, |d| d.repair_item(item_id))1303		}1304	}13051306	impl<T: Config> Pallet<T> {1307		/// Force set `sponsor` for `collection`.1308		///1309		/// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation1310		/// from the `sponsor` is not required.1311		///1312		/// # Arguments1313		///1314		/// * `sponsor`: ID of the account of the sponsor-to-be.1315		/// * `collection_id`: ID of the modified collection.1316		pub fn force_set_sponsor(1317			sponsor: T::AccountId,1318			collection_id: CollectionId,1319		) -> DispatchResult {1320			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1321			target_collection.force_set_sponsor(sponsor)1322		}13231324		/// Force remove `sponsor` for `collection`.1325		///1326		/// Differs from `remove_sponsor` in that1327		/// it doesn't require consent from the `owner` of the collection.1328		///1329		/// # Arguments1330		///1331		/// * `collection_id`: ID of the modified collection.1332		pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1333			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1334			target_collection.force_remove_sponsor()1335		}13361337		#[inline(always)]1338		pub(crate) fn destroy_collection_internal(1339			sender: T::CrossAccountId,1340			collection_id: CollectionId,1341		) -> DispatchResult {1342			T::CollectionDispatch::destroy(sender, collection_id)?;13431344			// TODO: basket cleanup should be moved elsewhere1345			// Maybe runtime dispatch.rs should perform it?13461347			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1348			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1349			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13501351			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1352			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1353			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13541355			Ok(())1356		}13571358		fn structure_nesting_budget() -> budget::Value {1359			budget::Value::new(Self::nesting_budget())1360		}13611362		fn nesting_budget_weight(value: &budget::Value) -> Weight {1363			T::StructureWeightInfo::find_parent().saturating_mul(value.remaining() as u64)1364		}13651366		fn nesting_budget_predispatch_weight() -> Weight {1367			Self::nesting_budget_weight(&Self::structure_nesting_budget())1368		}13691370		pub fn dispatch_tx_with_nesting_budget<1371			C: FnOnce(&dyn CommonCollectionOperations<T>) -> DispatchResultWithPostInfo,1372		>(1373			collection: CollectionId,1374			budget: &budget::Value,1375			call: C,1376		) -> DispatchResultWithPostInfo {1377			let mut result = dispatch_tx::<T, _>(collection, call);13781379			match &mut result {1380				Ok(PostDispatchInfo {1381					actual_weight: Some(weight),1382					..1383				})1384				| Err(DispatchErrorWithPostInfo {1385					post_info: PostDispatchInfo {1386						actual_weight: Some(weight),1387						..1388					},1389					..1390				}) => *weight += Self::nesting_budget_weight(budget),1391				_ => {}1392			}13931394			result1395		}1396	}1397}