git.delta.rocks / unique-network / refs/commits / 598fbed71948

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, CommonWeightInfo, Pallet as PalletCommon, RefungibleExtensionsWeightInfo,97	};98	use pallet_evm::account::CrossAccountId;99	use pallet_structure::weights::WeightInfo as StructureWeightInfo;100	use scale_info::TypeInfo;101	use sp_std::{vec, vec::Vec};102	use up_data_structs::{103		budget, CollectionId, CollectionLimits, CollectionMode, CollectionPermissions,104		CreateCollectionData, CreateItemData, CreateItemExData, Property, PropertyKey,105		PropertyKeyPermission, TokenId, COLLECTION_ADMINS_LIMIT, MAX_COLLECTION_DESCRIPTION_LENGTH,106		MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_PROPERTIES_SIZE, MAX_PROPERTIES_PER_ITEM,107		MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH, MAX_TOKEN_PREFIX_LENGTH,108		MAX_TOKEN_PROPERTIES_SIZE,109	};110	use weights::WeightInfo;111112	use super::*;113114	/// Errors for the common Unique transactions.115	#[pallet::error]116	pub enum Error<T> {117		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].118		CollectionDecimalPointLimitExceeded,119		/// Length of items properties must be greater than 0.120		EmptyArgument,121		/// Repertition is only supported by refungible collection.122		RepartitionCalledOnNonRefungibleCollection,123	}124125	/// Configuration trait of this pallet.126	#[pallet::config]127	pub trait Config: frame_system::Config + pallet_common::Config + Sized + TypeInfo {128		/// Weight information for extrinsics in this pallet.129		type WeightInfo: WeightInfo;130131		/// Weight information for common pallet operations.132		type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;133134		type StructureWeightInfo: StructureWeightInfo;135136		/// Weight info information for extra refungible pallet operations.137		type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;138	}139140	#[pallet::pallet]141	pub struct Pallet<T>(_);142143	pub type SelfWeightOf<T> = <T as Config>::WeightInfo;144145	// # Used definitions146	//147	// ## User control levels148	//149	// chain-controlled - key is uncontrolled by user150	//                    i.e autoincrementing index151	//                    can use non-cryptographic hash152	// real - key is controlled by user153	//        but it is hard to generate enough colliding values, i.e owner of signed txs154	//        can use non-cryptographic hash155	// controlled - key is completly controlled by users156	//              i.e maps with mutable keys157	//              should use cryptographic hash158	//159	// ## User control level downgrade reasons160	//161	// ?1 - chain-controlled -> controlled162	//      collections/tokens can be destroyed, resulting in massive holes163	// ?2 - chain-controlled -> controlled164	//      same as ?1, but can be only added, resulting in easier exploitation165	// ?3 - real -> controlled166	//      no confirmation required, so addresses can be easily generated167168	//#region Private members169	/// Used for migrations170	#[pallet::storage]171	pub type ChainVersion<T> = StorageValue<_, u64, ValueQuery>;172	//#endregion173174	//#region Tokens transfer sponosoring rate limit baskets175	/// (Collection id (controlled?2), who created (real))176	/// TODO: Off chain worker should remove from this map when collection gets removed177	#[pallet::storage]178	#[pallet::getter(fn create_item_busket)]179	pub type CreateItemBasket<T: Config> = StorageMap<180		Hasher = Blake2_128Concat,181		Key = (CollectionId, T::AccountId),182		Value = BlockNumberFor<T>,183		QueryKind = OptionQuery,184	>;185	/// Collection id (controlled?2), token id (controlled?2)186	#[pallet::storage]187	#[pallet::getter(fn nft_transfer_basket)]188	pub type NftTransferBasket<T: Config> = StorageDoubleMap<189		Hasher1 = Blake2_128Concat,190		Key1 = CollectionId,191		Hasher2 = Blake2_128Concat,192		Key2 = TokenId,193		Value = BlockNumberFor<T>,194		QueryKind = OptionQuery,195	>;196	/// Collection id (controlled?2), owning user (real)197	#[pallet::storage]198	#[pallet::getter(fn fungible_transfer_basket)]199	pub type FungibleTransferBasket<T: Config> = StorageDoubleMap<200		Hasher1 = Blake2_128Concat,201		Key1 = CollectionId,202		Hasher2 = Twox64Concat,203		Key2 = T::AccountId,204		Value = BlockNumberFor<T>,205		QueryKind = OptionQuery,206	>;207	/// Collection id (controlled?2), token id (controlled?2)208	#[pallet::storage]209	#[pallet::getter(fn refungible_transfer_basket)]210	pub type ReFungibleTransferBasket<T: Config> = StorageNMap<211		Key = (212			Key<Blake2_128Concat, CollectionId>,213			Key<Blake2_128Concat, TokenId>,214			Key<Twox64Concat, T::AccountId>,215		),216		Value = BlockNumberFor<T>,217		QueryKind = OptionQuery,218	>;219	//#endregion220221	/// Last sponsoring of token property setting // todo:doc rephrase this and the following222	#[pallet::storage]223	#[pallet::getter(fn token_property_basket)]224	pub type TokenPropertyBasket<T: Config> = StorageDoubleMap<225		Hasher1 = Blake2_128Concat,226		Key1 = CollectionId,227		Hasher2 = Blake2_128Concat,228		Key2 = TokenId,229		Value = BlockNumberFor<T>,230		QueryKind = OptionQuery,231	>;232233	/// Last sponsoring of NFT approval in a collection234	#[pallet::storage]235	#[pallet::getter(fn nft_approve_basket)]236	pub type NftApproveBasket<T: Config> = StorageDoubleMap<237		Hasher1 = Blake2_128Concat,238		Key1 = CollectionId,239		Hasher2 = Blake2_128Concat,240		Key2 = TokenId,241		Value = BlockNumberFor<T>,242		QueryKind = OptionQuery,243	>;244	/// Last sponsoring of fungible tokens approval in a collection245	#[pallet::storage]246	#[pallet::getter(fn fungible_approve_basket)]247	pub type FungibleApproveBasket<T: Config> = StorageDoubleMap<248		Hasher1 = Blake2_128Concat,249		Key1 = CollectionId,250		Hasher2 = Twox64Concat,251		Key2 = T::AccountId,252		Value = BlockNumberFor<T>,253		QueryKind = OptionQuery,254	>;255	/// Last sponsoring of RFT approval in a collection256	#[pallet::storage]257	#[pallet::getter(fn refungible_approve_basket)]258	pub type RefungibleApproveBasket<T: Config> = StorageNMap<259		Key = (260			Key<Blake2_128Concat, CollectionId>,261			Key<Blake2_128Concat, TokenId>,262			Key<Twox64Concat, T::AccountId>,263		),264		Value = BlockNumberFor<T>,265		QueryKind = OptionQuery,266	>;267268	#[pallet::extra_constants]269	impl<T: Config> Pallet<T> {270		/// A maximum number of levels of depth in the token nesting tree.271		fn nesting_budget() -> u32 {272			5273		}274275		/// Maximal length of a collection name.276		fn max_collection_name_length() -> u32 {277			MAX_COLLECTION_NAME_LENGTH278		}279280		/// Maximal length of a collection description.281		fn max_collection_description_length() -> u32 {282			MAX_COLLECTION_DESCRIPTION_LENGTH283		}284285		/// Maximal length of a token prefix.286		fn max_token_prefix_length() -> u32 {287			MAX_TOKEN_PREFIX_LENGTH288		}289290		/// Maximum admins per collection.291		fn collection_admins_limit() -> u32 {292			COLLECTION_ADMINS_LIMIT293		}294295		/// Maximal length of a property key.296		fn max_property_key_length() -> u32 {297			MAX_PROPERTY_KEY_LENGTH298		}299300		/// Maximal length of a property value.301		fn max_property_value_length() -> u32 {302			MAX_PROPERTY_VALUE_LENGTH303		}304305		/// A maximum number of token properties.306		fn max_properties_per_item() -> u32 {307			MAX_PROPERTIES_PER_ITEM308		}309310		/// Maximum size for all collection properties.311		fn max_collection_properties_size() -> u32 {312			MAX_COLLECTION_PROPERTIES_SIZE313		}314315		/// Maximum size of all token properties.316		fn max_token_properties_size() -> u32 {317			MAX_TOKEN_PROPERTIES_SIZE318		}319320		/// Default NFT collection limit.321		fn nft_default_collection_limits() -> CollectionLimits {322			CollectionLimits::with_default_limits(CollectionMode::NFT)323		}324325		/// Default RFT collection limit.326		fn rft_default_collection_limits() -> CollectionLimits {327			CollectionLimits::with_default_limits(CollectionMode::ReFungible)328		}329330		/// Default FT collection limit.331		fn ft_default_collection_limits() -> CollectionLimits {332			CollectionLimits::with_default_limits(CollectionMode::Fungible(0))333		}334	}335336	/// Type alias to Pallet, to be used by construct_runtime.337	#[pallet::call]338	impl<T: Config> Pallet<T> {339		/// Create a collection of tokens.340		///341		/// Each Token may have multiple properties encoded as an array of bytes342		/// of certain length. The initial owner of the collection is set343		/// to the address that signed the transaction and can be changed later.344		///345		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.346		///347		/// # Permissions348		///349		/// * Anyone - becomes the owner of the new collection.350		///351		/// # Arguments352		///353		/// * `collection_name`: Wide-character string with collection name354		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).355		/// * `collection_description`: Wide-character string with collection description356		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).357		/// * `token_prefix`: Byte string containing the token prefix to mark a collection358		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).359		/// * `mode`: Type of items stored in the collection and type dependent data.360		///361		/// returns collection ID362		///363		/// Deprecated: `create_collection_ex` is more up-to-date and advanced, prefer it instead.364		#[pallet::call_index(0)]365		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]366		pub fn create_collection(367			origin: OriginFor<T>,368			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,369			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,370			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,371			mode: CollectionMode,372		) -> DispatchResult {373			let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {374				name: collection_name,375				description: collection_description,376				token_prefix,377				mode,378				..Default::default()379			};380			Self::create_collection_ex(origin, data)381		}382383		/// Create a collection with explicit parameters.384		///385		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.386		///387		/// # Permissions388		///389		/// * Anyone - becomes the owner of the new collection.390		///391		/// # Arguments392		///393		/// * `data`: Explicit data of a collection used for its creation.394		#[pallet::call_index(1)]395		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]396		pub fn create_collection_ex(397			origin: OriginFor<T>,398			data: CreateCollectionData<T::CrossAccountId>,399		) -> DispatchResult {400			let sender = ensure_signed(origin)?;401402			// =========403			let sender = T::CrossAccountId::from_sub(sender);404			let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;405406			Ok(())407		}408409		/// Destroy a collection if no tokens exist within.410		///411		/// # Permissions412		///413		/// * Collection owner414		///415		/// # Arguments416		///417		/// * `collection_id`: Collection to destroy.418		#[pallet::call_index(2)]419		#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]420		pub fn destroy_collection(421			origin: OriginFor<T>,422			collection_id: CollectionId,423		) -> DispatchResult {424			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);425426			Self::destroy_collection_internal(sender, collection_id)427		}428429		/// Add an address to allow list.430		///431		/// # Permissions432		///433		/// * Collection owner434		/// * Collection admin435		///436		/// # Arguments437		///438		/// * `collection_id`: ID of the modified collection.439		/// * `address`: ID of the address to be added to the allowlist.440		#[pallet::call_index(3)]441		#[pallet::weight(<SelfWeightOf<T>>::add_to_allow_list())]442		pub fn add_to_allow_list(443			origin: OriginFor<T>,444			collection_id: CollectionId,445			address: T::CrossAccountId,446		) -> DispatchResult {447			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {448				fail!(<pallet_common::Error<T>>::UnsupportedOperation);449			}450451			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);452			let collection = <CollectionHandle<T>>::try_get(collection_id)?;453			collection.check_is_internal()?;454455			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, true)?;456457			Ok(())458		}459460		/// Remove an address from allow list.461		///462		/// # Permissions463		///464		/// * Collection owner465		/// * Collection admin466		///467		/// # Arguments468		///469		/// * `collection_id`: ID of the modified collection.470		/// * `address`: ID of the address to be removed from the allowlist.471		#[pallet::call_index(4)]472		#[pallet::weight(<SelfWeightOf<T>>::remove_from_allow_list())]473		pub fn remove_from_allow_list(474			origin: OriginFor<T>,475			collection_id: CollectionId,476			address: T::CrossAccountId,477		) -> DispatchResult {478			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {479				fail!(<pallet_common::Error<T>>::UnsupportedOperation);480			}481482			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);483			let collection = <CollectionHandle<T>>::try_get(collection_id)?;484			collection.check_is_internal()?;485486			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, false)?;487488			Ok(())489		}490491		/// Change the owner of the collection.492		///493		/// # Permissions494		///495		/// * Collection owner496		///497		/// # Arguments498		///499		/// * `collection_id`: ID of the modified collection.500		/// * `new_owner`: ID of the account that will become the owner.501		#[pallet::call_index(5)]502		#[pallet::weight(<SelfWeightOf<T>>::change_collection_owner())]503		pub fn change_collection_owner(504			origin: OriginFor<T>,505			collection_id: CollectionId,506			new_owner: T::AccountId,507		) -> DispatchResult {508			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {509				fail!(<pallet_common::Error<T>>::UnsupportedOperation);510			}511			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);512			let new_owner = T::CrossAccountId::from_sub(new_owner);513			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;514			target_collection.change_owner(sender, new_owner)515		}516517		/// Add an admin to a collection.518		///519		/// NFT Collection can be controlled by multiple admin addresses520		/// (some which can also be servers, for example). Admins can issue521		/// and burn NFTs, as well as add and remove other admins,522		/// but cannot change NFT or Collection ownership.523		///524		/// # Permissions525		///526		/// * Collection owner527		/// * Collection admin528		///529		/// # Arguments530		///531		/// * `collection_id`: ID of the Collection to add an admin for.532		/// * `new_admin`: Address of new admin to add.533		#[pallet::call_index(6)]534		#[pallet::weight(<SelfWeightOf<T>>::add_collection_admin())]535		pub fn add_collection_admin(536			origin: OriginFor<T>,537			collection_id: CollectionId,538			new_admin_id: T::CrossAccountId,539		) -> DispatchResult {540			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {541				fail!(<pallet_common::Error<T>>::UnsupportedOperation);542			}543			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);544			let collection = <CollectionHandle<T>>::try_get(collection_id)?;545			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)546		}547548		/// Remove admin of a collection.549		///550		/// An admin address can remove itself. List of admins may become empty,551		/// in which case only Collection Owner will be able to add an Admin.552		///553		/// # Permissions554		///555		/// * Collection owner556		/// * Collection admin557		///558		/// # Arguments559		///560		/// * `collection_id`: ID of the collection to remove the admin for.561		/// * `account_id`: Address of the admin to remove.562		#[pallet::call_index(7)]563		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_admin())]564		pub fn remove_collection_admin(565			origin: OriginFor<T>,566			collection_id: CollectionId,567			account_id: T::CrossAccountId,568		) -> DispatchResult {569			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {570				fail!(<pallet_common::Error<T>>::UnsupportedOperation);571			}572			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);573			let collection = <CollectionHandle<T>>::try_get(collection_id)?;574			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)575		}576577		/// Set (invite) a new collection sponsor.578		///579		/// If successful, confirmation from the sponsor-to-be will be pending.580		///581		/// # Permissions582		///583		/// * Collection owner584		/// * Collection admin585		///586		/// # Arguments587		///588		/// * `collection_id`: ID of the modified collection.589		/// * `new_sponsor`: ID of the account of the sponsor-to-be.590		#[pallet::call_index(8)]591		#[pallet::weight(<SelfWeightOf<T>>::set_collection_sponsor())]592		pub fn set_collection_sponsor(593			origin: OriginFor<T>,594			collection_id: CollectionId,595			new_sponsor: T::AccountId,596		) -> DispatchResult {597			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {598				fail!(<pallet_common::Error<T>>::UnsupportedOperation);599			}600			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;602			target_collection.set_sponsor(&sender, new_sponsor.clone())603		}604605		/// Confirm own sponsorship of a collection, becoming the sponsor.606		///607		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].608		/// Sponsor can pay the fees of a transaction instead of the sender,609		/// but only within specified limits.610		///611		/// # Permissions612		///613		/// * Sponsor-to-be614		///615		/// # Arguments616		///617		/// * `collection_id`: ID of the collection with the pending sponsor.618		#[pallet::call_index(9)]619		#[pallet::weight(<SelfWeightOf<T>>::confirm_sponsorship())]620		pub fn confirm_sponsorship(621			origin: OriginFor<T>,622			collection_id: CollectionId,623		) -> DispatchResult {624			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {625				fail!(<pallet_common::Error<T>>::UnsupportedOperation);626			}627			let sender = ensure_signed(origin)?;628			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;629			target_collection.confirm_sponsorship(&sender)630		}631632		/// Remove a collection's a sponsor, making everyone pay for their own transactions.633		///634		/// # Permissions635		///636		/// * Collection owner637		///638		/// # Arguments639		///640		/// * `collection_id`: ID of the collection with the sponsor to remove.641		#[pallet::call_index(10)]642		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_sponsor())]643		pub fn remove_collection_sponsor(644			origin: OriginFor<T>,645			collection_id: CollectionId,646		) -> DispatchResult {647			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {648				fail!(<pallet_common::Error<T>>::UnsupportedOperation);649			}650			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);651			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;652			target_collection.remove_sponsor(&sender)653		}654655		/// Mint an item within a collection.656		///657		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].658		///659		/// # Permissions660		///661		/// * Collection owner662		/// * Collection admin663		/// * Anyone if664		///     * Allow List is enabled, and665		///     * Address is added to allow list, and666		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])667		///668		/// # Arguments669		///670		/// * `collection_id`: ID of the collection to which an item would belong.671		/// * `owner`: Address of the initial owner of the item.672		/// * `data`: Token data describing the item to store on chain.673		#[pallet::call_index(11)]674		#[pallet::weight(T::CommonWeightInfo::create_item(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]675		pub fn create_item(676			origin: OriginFor<T>,677			collection_id: CollectionId,678			owner: T::CrossAccountId,679			data: CreateItemData,680		) -> DispatchResultWithPostInfo {681			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682			let budget = Self::structure_nesting_budget();683684			Self::refund_nesting_budget(685				dispatch_tx::<T, _>(collection_id, |d| {686					d.create_item(sender, owner, data, &budget)687				}),688				budget,689			)690		}691692		/// Create multiple items within a collection.693		///694		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].695		///696		/// # Permissions697		///698		/// * Collection owner699		/// * Collection admin700		/// * Anyone if701		///     * Allow List is enabled, and702		///     * Address is added to the allow list, and703		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])704		///705		/// # Arguments706		///707		/// * `collection_id`: ID of the collection to which the tokens would belong.708		/// * `owner`: Address of the initial owner of the tokens.709		/// * `items_data`: Vector of data describing each item to be created.710		#[pallet::call_index(12)]711		#[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data) + <Pallet<T>>::nesting_budget_predispatch_weight())]712		pub fn create_multiple_items(713			origin: OriginFor<T>,714			collection_id: CollectionId,715			owner: T::CrossAccountId,716			items_data: Vec<CreateItemData>,717		) -> DispatchResultWithPostInfo {718			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);719			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);720			let budget = Self::structure_nesting_budget();721722			Self::refund_nesting_budget(723				dispatch_tx::<T, _>(collection_id, |d| {724					d.create_multiple_items(sender, owner, items_data, &budget)725				}),726				budget,727			)728		}729730		/// Add or change collection properties.731		///732		/// # Permissions733		///734		/// * Collection owner735		/// * Collection admin736		///737		/// # Arguments738		///739		/// * `collection_id`: ID of the modified collection.740		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.741		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.742		#[pallet::call_index(13)]743		#[pallet::weight(T::CommonWeightInfo::set_collection_properties(properties.len() as u32))]744		pub fn set_collection_properties(745			origin: OriginFor<T>,746			collection_id: CollectionId,747			properties: Vec<Property>,748		) -> DispatchResultWithPostInfo {749			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);750751			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);752753			dispatch_tx::<T, _>(collection_id, |d| {754				d.set_collection_properties(sender, properties)755			})756		}757758		/// Delete specified collection properties.759		///760		/// # Permissions761		///762		/// * Collection Owner763		/// * Collection Admin764		///765		/// # Arguments766		///767		/// * `collection_id`: ID of the modified collection.768		/// * `property_keys`: Vector of keys of the properties to be deleted.769		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.770		#[pallet::call_index(14)]771		#[pallet::weight(T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32))]772		pub fn delete_collection_properties(773			origin: OriginFor<T>,774			collection_id: CollectionId,775			property_keys: Vec<PropertyKey>,776		) -> DispatchResultWithPostInfo {777			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);778779			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);780781			dispatch_tx::<T, _>(collection_id, |d| {782				d.delete_collection_properties(&sender, property_keys)783			})784		}785786		/// Add or change token properties according to collection's permissions.787		/// Currently properties only work with NFTs.788		///789		/// # Permissions790		///791		/// * Depends on collection's token property permissions and specified property mutability:792		/// 	* Collection owner793		/// 	* Collection admin794		/// 	* Token owner795		///796		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].797		///798		/// # Arguments799		///800		/// * `collection_id: ID of the collection to which the token belongs.801		/// * `token_id`: ID of the modified token.802		/// * `properties`: Vector of key-value pairs stored as the token's metadata.803		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.804		#[pallet::call_index(15)]805		#[pallet::weight(T::CommonWeightInfo::set_token_properties(properties.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]806		pub fn set_token_properties(807			origin: OriginFor<T>,808			collection_id: CollectionId,809			token_id: TokenId,810			properties: Vec<Property>,811		) -> DispatchResultWithPostInfo {812			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);813814			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);815			let budget = Self::structure_nesting_budget();816817			Self::refund_nesting_budget(818				dispatch_tx::<T, _>(collection_id, |d| {819					d.set_token_properties(sender, token_id, properties, &budget)820				}),821				budget,822			)823		}824825		/// Delete specified token properties. Currently properties only work with NFTs.826		///827		/// # Permissions828		///829		/// * Depends on collection's token property permissions and specified property mutability:830		/// 	* Collection owner831		/// 	* Collection admin832		/// 	* Token owner833		///834		/// # Arguments835		///836		/// * `collection_id`: ID of the collection to which the token belongs.837		/// * `token_id`: ID of the modified token.838		/// * `property_keys`: Vector of keys of the properties to be deleted.839		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.840		#[pallet::call_index(16)]841		#[pallet::weight(T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]842		pub fn delete_token_properties(843			origin: OriginFor<T>,844			collection_id: CollectionId,845			token_id: TokenId,846			property_keys: Vec<PropertyKey>,847		) -> DispatchResultWithPostInfo {848			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);849850			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851			let budget = Self::structure_nesting_budget();852853			Self::refund_nesting_budget(854				dispatch_tx::<T, _>(collection_id, |d| {855					d.delete_token_properties(sender, token_id, property_keys, &budget)856				}),857				budget,858			)859		}860861		/// Add or change token property permissions of a collection.862		///863		/// Without a permission for a particular key, a property with that key864		/// cannot be created in a token.865		///866		/// # Permissions867		///868		/// * Collection owner869		/// * Collection admin870		///871		/// # Arguments872		///873		/// * `collection_id`: ID of the modified collection.874		/// * `property_permissions`: Vector of permissions for property keys.875		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.876		#[pallet::call_index(17)]877		#[pallet::weight(T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32))]878		pub fn set_token_property_permissions(879			origin: OriginFor<T>,880			collection_id: CollectionId,881			property_permissions: Vec<PropertyKeyPermission>,882		) -> DispatchResultWithPostInfo {883			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);884885			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);886887			dispatch_tx::<T, _>(collection_id, |d| {888				d.set_token_property_permissions(&sender, property_permissions)889			})890		}891892		/// Create multiple items within a collection with explicitly specified initial parameters.893		///894		/// # Permissions895		///896		/// * Collection owner897		/// * Collection admin898		/// * Anyone if899		///     * Allow List is enabled, and900		///     * Address is added to allow list, and901		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])902		///903		/// # Arguments904		///905		/// * `collection_id`: ID of the collection to which the tokens would belong.906		/// * `data`: Explicit item creation data.907		#[pallet::call_index(18)]908		#[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]909		pub fn create_multiple_items_ex(910			origin: OriginFor<T>,911			collection_id: CollectionId,912			data: CreateItemExData<T::CrossAccountId>,913		) -> DispatchResultWithPostInfo {914			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);915			let budget = Self::structure_nesting_budget();916917			Self::refund_nesting_budget(918				dispatch_tx::<T, _>(collection_id, |d| {919					d.create_multiple_items_ex(sender, data, &budget)920				}),921				budget,922			)923		}924925		/// Completely allow or disallow transfers for a particular collection.926		///927		/// # Permissions928		///929		/// * Collection owner930		///931		/// # Arguments932		///933		/// * `collection_id`: ID of the collection.934		/// * `value`: New value of the flag, are transfers allowed?935		#[pallet::call_index(19)]936		#[pallet::weight(<SelfWeightOf<T>>::set_transfers_enabled_flag())]937		pub fn set_transfers_enabled_flag(938			origin: OriginFor<T>,939			collection_id: CollectionId,940			value: bool,941		) -> DispatchResult {942			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {943				fail!(<pallet_common::Error<T>>::UnsupportedOperation);944			}945			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;947			target_collection.check_is_internal()?;948			target_collection.check_is_owner(&sender)?;949950			// =========951952			target_collection.limits.transfers_enabled = Some(value);953			target_collection.save()954		}955956		/// Destroy an item.957		///958		/// # Permissions959		///960		/// * Collection owner961		/// * Collection admin962		/// * Current item owner963		///964		/// # Arguments965		///966		/// * `collection_id`: ID of the collection to which the item belongs.967		/// * `item_id`: ID of item to burn.968		/// * `value`: Number of pieces of the item to destroy.969		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.970		///     * Fungible Mode: The desired number of pieces to burn.971		///     * Re-Fungible Mode: The desired number of pieces to burn.972		#[pallet::call_index(20)]973		#[pallet::weight(T::CommonWeightInfo::burn_item())]974		pub fn burn_item(975			origin: OriginFor<T>,976			collection_id: CollectionId,977			item_id: TokenId,978			value: u128,979		) -> DispatchResultWithPostInfo {980			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981982			let post_info =983				dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;984			if value == 1 {985				<NftTransferBasket<T>>::remove(collection_id, item_id);986				<NftApproveBasket<T>>::remove(collection_id, item_id);987			}988			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?989			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());990			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));991			Ok(post_info)992		}993994		/// Destroy a token on behalf of the owner as a non-owner account.995		///996		/// See also: [`approve`][`Pallet::approve`].997		///998		/// After this method executes, one approval is removed from the total so that999		/// the approved address will not be able to transfer this item again from this owner.1000		///1001		/// # Permissions1002		///1003		/// * Collection owner1004		/// * Collection admin1005		/// * Current token owner1006		/// * Address approved by current item owner1007		///1008		/// # Arguments1009		///1010		/// * `from`: The owner of the burning item.1011		/// * `collection_id`: ID of the collection to which the item belongs.1012		/// * `item_id`: ID of item to burn.1013		/// * `value`: Number of pieces to burn.1014		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1015		///     * Fungible Mode: The desired number of pieces to burn.1016		///     * Re-Fungible Mode: The desired number of pieces to burn.1017		#[pallet::call_index(21)]1018		#[pallet::weight(T::CommonWeightInfo::burn_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1019		pub fn burn_from(1020			origin: OriginFor<T>,1021			collection_id: CollectionId,1022			from: T::CrossAccountId,1023			item_id: TokenId,1024			value: u128,1025		) -> DispatchResultWithPostInfo {1026			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1027			let budget = Self::structure_nesting_budget();10281029			Self::refund_nesting_budget(1030				dispatch_tx::<T, _>(collection_id, |d| {1031					d.burn_from(sender, from, item_id, value, &budget)1032				}),1033				budget,1034			)1035		}10361037		/// Change ownership of the token.1038		///1039		/// # Permissions1040		///1041		/// * Collection owner1042		/// * Collection admin1043		/// * Current token owner1044		///1045		/// # Arguments1046		///1047		/// * `recipient`: Address of token recipient.1048		/// * `collection_id`: ID of the collection the item belongs to.1049		/// * `item_id`: ID of the item.1050		///     * Non-Fungible Mode: Required.1051		///     * Fungible Mode: Ignored.1052		///     * Re-Fungible Mode: Required.1053		///1054		/// * `value`: Amount to transfer.1055		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1056		///     * Fungible Mode: The desired number of pieces to transfer.1057		///     * Re-Fungible Mode: The desired number of pieces to transfer.1058		#[pallet::call_index(22)]1059		#[pallet::weight(T::CommonWeightInfo::transfer() + <Pallet<T>>::nesting_budget_predispatch_weight())]1060		pub fn transfer(1061			origin: OriginFor<T>,1062			recipient: T::CrossAccountId,1063			collection_id: CollectionId,1064			item_id: TokenId,1065			value: u128,1066		) -> DispatchResultWithPostInfo {1067			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1068			let budget = Self::structure_nesting_budget();10691070			Self::refund_nesting_budget(1071				dispatch_tx::<T, _>(collection_id, |d| {1072					d.transfer(sender, recipient, item_id, value, &budget)1073				}),1074				budget,1075			)1076		}10771078		/// Allow a non-permissioned address to transfer or burn an item.1079		///1080		/// # Permissions1081		///1082		/// * Collection owner1083		/// * Collection admin1084		/// * Current item owner1085		///1086		/// # Arguments1087		///1088		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.1089		/// * `collection_id`: ID of the collection the item belongs to.1090		/// * `item_id`: ID of the item transactions on which are now approved.1091		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1092		/// Set to 0 to revoke the approval.1093		#[pallet::call_index(23)]1094		#[pallet::weight(T::CommonWeightInfo::approve())]1095		pub fn approve(1096			origin: OriginFor<T>,1097			spender: T::CrossAccountId,1098			collection_id: CollectionId,1099			item_id: TokenId,1100			amount: u128,1101		) -> DispatchResultWithPostInfo {1102			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11031104			dispatch_tx::<T, _>(collection_id, |d| {1105				d.approve(sender, spender, item_id, amount)1106			})1107		}11081109		/// Allow a non-permissioned address to transfer or burn an item from owner's eth mirror.1110		///1111		/// # Permissions1112		///1113		/// * Collection owner1114		/// * Collection admin1115		/// * Current item owner1116		///1117		/// # Arguments1118		///1119		/// * `from`: Owner's account eth mirror1120		/// * `to`: Account to be approved to make specific transactions on non-owned tokens.1121		/// * `collection_id`: ID of the collection the item belongs to.1122		/// * `item_id`: ID of the item transactions on which are now approved.1123		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1124		/// Set to 0 to revoke the approval.1125		#[pallet::call_index(24)]1126		#[pallet::weight(T::CommonWeightInfo::approve_from())]1127		pub fn approve_from(1128			origin: OriginFor<T>,1129			from: T::CrossAccountId,1130			to: T::CrossAccountId,1131			collection_id: CollectionId,1132			item_id: TokenId,1133			amount: u128,1134		) -> DispatchResultWithPostInfo {1135			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11361137			dispatch_tx::<T, _>(collection_id, |d| {1138				d.approve_from(sender, from, to, item_id, amount)1139			})1140		}11411142		/// Change ownership of an item on behalf of the owner as a non-owner account.1143		///1144		/// See the [`approve`][`Pallet::approve`] method for additional information.1145		///1146		/// After this method executes, one approval is removed from the total so that1147		/// the approved address will not be able to transfer this item again from this owner.1148		///1149		/// # Permissions1150		///1151		/// * Collection owner1152		/// * Collection admin1153		/// * Current item owner1154		/// * Address approved by current item owner1155		///1156		/// # Arguments1157		///1158		/// * `from`: Address that currently owns the token.1159		/// * `recipient`: Address of the new token-owner-to-be.1160		/// * `collection_id`: ID of the collection the item.1161		/// * `item_id`: ID of the item to be transferred.1162		/// * `value`: Amount to transfer.1163		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1164		///     * Fungible Mode: The desired number of pieces to transfer.1165		///     * Re-Fungible Mode: The desired number of pieces to transfer.1166		#[pallet::call_index(25)]1167		#[pallet::weight(T::CommonWeightInfo::transfer_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1168		pub fn transfer_from(1169			origin: OriginFor<T>,1170			from: T::CrossAccountId,1171			recipient: T::CrossAccountId,1172			collection_id: CollectionId,1173			item_id: TokenId,1174			value: u128,1175		) -> DispatchResultWithPostInfo {1176			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1177			let budget = Self::structure_nesting_budget();11781179			Self::refund_nesting_budget(1180				dispatch_tx::<T, _>(collection_id, |d| {1181					d.transfer_from(sender, from, recipient, item_id, value, &budget)1182				}),1183				budget,1184			)1185		}11861187		/// Set specific limits of a collection. Empty, or None fields mean chain default.1188		///1189		/// # Permissions1190		///1191		/// * Collection owner1192		/// * Collection admin1193		///1194		/// # Arguments1195		///1196		/// * `collection_id`: ID of the modified collection.1197		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1198		/// will not overwrite the old ones.1199		#[pallet::call_index(26)]1200		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1201		pub fn set_collection_limits(1202			origin: OriginFor<T>,1203			collection_id: CollectionId,1204			new_limit: CollectionLimits,1205		) -> DispatchResult {1206			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1207				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1208			}1209			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1211			<PalletCommon<T>>::update_limits(&sender, &mut target_collection, new_limit)1212		}12131214		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1215		///1216		/// # Permissions1217		///1218		/// * Collection owner1219		/// * Collection admin1220		///1221		/// # Arguments1222		///1223		/// * `collection_id`: ID of the modified collection.1224		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1225		/// will not overwrite the old ones.1226		#[pallet::call_index(27)]1227		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1228		pub fn set_collection_permissions(1229			origin: OriginFor<T>,1230			collection_id: CollectionId,1231			new_permission: CollectionPermissions,1232		) -> DispatchResult {1233			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1234				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1235			}1236			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1237			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1238			<PalletCommon<T>>::update_permissions(&sender, &mut target_collection, new_permission)1239		}12401241		/// Re-partition a refungible token, while owning all of its parts/pieces.1242		///1243		/// # Permissions1244		///1245		/// * Token owner (must own every part)1246		///1247		/// # Arguments1248		///1249		/// * `collection_id`: ID of the collection the RFT belongs to.1250		/// * `token_id`: ID of the RFT.1251		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1252		#[pallet::call_index(28)]1253		#[pallet::weight(T::RefungibleExtensionsWeightInfo::repartition())]1254		pub fn repartition(1255			origin: OriginFor<T>,1256			collection_id: CollectionId,1257			token_id: TokenId,1258			amount: u128,1259		) -> DispatchResultWithPostInfo {1260			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1261			dispatch_tx::<T, _>(collection_id, |d| {1262				if let Some(refungible_extensions) = d.refungible_extensions() {1263					refungible_extensions.repartition(&sender, token_id, amount)1264				} else {1265					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1266				}1267			})1268		}12691270		/// Sets or unsets the approval of a given operator.1271		///1272		/// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.1273		///1274		/// # Arguments1275		///1276		/// * `owner`: Token owner1277		/// * `operator`: Operator1278		/// * `approve`: Should operator status be granted or revoked?1279		#[pallet::call_index(29)]1280		#[pallet::weight(T::CommonWeightInfo::set_allowance_for_all())]1281		pub fn set_allowance_for_all(1282			origin: OriginFor<T>,1283			collection_id: CollectionId,1284			operator: T::CrossAccountId,1285			approve: bool,1286		) -> DispatchResultWithPostInfo {1287			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1288			dispatch_tx::<T, _>(collection_id, |d| {1289				d.set_allowance_for_all(sender, operator, approve)1290			})1291		}12921293		/// Repairs a collection if the data was somehow corrupted.1294		///1295		/// # Arguments1296		///1297		/// * `collection_id`: ID of the collection to repair.1298		#[pallet::call_index(30)]1299		#[pallet::weight(<SelfWeightOf<T>>::force_repair_collection())]1300		pub fn force_repair_collection(1301			origin: OriginFor<T>,1302			collection_id: CollectionId,1303		) -> DispatchResult {1304			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1305				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1306			}1307			ensure_root(origin)?;1308			<PalletCommon<T>>::repair_collection(collection_id)1309		}13101311		/// Repairs a token if the data was somehow corrupted.1312		///1313		/// # Arguments1314		///1315		/// * `collection_id`: ID of the collection the item belongs to.1316		/// * `item_id`: ID of the item.1317		#[pallet::call_index(31)]1318		#[pallet::weight(T::CommonWeightInfo::force_repair_item())]1319		pub fn force_repair_item(1320			origin: OriginFor<T>,1321			collection_id: CollectionId,1322			item_id: TokenId,1323		) -> DispatchResultWithPostInfo {1324			ensure_root(origin)?;1325			dispatch_tx::<T, _>(collection_id, |d| d.repair_item(item_id))1326		}1327	}13281329	impl<T: Config> Pallet<T> {1330		/// Force set `sponsor` for `collection`.1331		///1332		/// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation1333		/// from the `sponsor` is not required.1334		///1335		/// # Arguments1336		///1337		/// * `sponsor`: ID of the account of the sponsor-to-be.1338		/// * `collection_id`: ID of the modified collection.1339		pub fn force_set_sponsor(1340			sponsor: T::AccountId,1341			collection_id: CollectionId,1342		) -> DispatchResult {1343			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1344			target_collection.force_set_sponsor(sponsor)1345		}13461347		/// Force remove `sponsor` for `collection`.1348		///1349		/// Differs from `remove_sponsor` in that1350		/// it doesn't require consent from the `owner` of the collection.1351		///1352		/// # Arguments1353		///1354		/// * `collection_id`: ID of the modified collection.1355		pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1356			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1357			target_collection.force_remove_sponsor()1358		}13591360		#[inline(always)]1361		pub(crate) fn destroy_collection_internal(1362			sender: T::CrossAccountId,1363			collection_id: CollectionId,1364		) -> DispatchResult {1365			T::CollectionDispatch::destroy(sender, collection_id)?;13661367			// TODO: basket cleanup should be moved elsewhere1368			// Maybe runtime dispatch.rs should perform it?13691370			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1371			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1372			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13731374			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1375			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1376			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13771378			Ok(())1379		}13801381		fn structure_nesting_budget() -> budget::Value {1382			budget::Value::new(Self::nesting_budget())1383		}13841385		fn nesting_budget_predispatch_weight() -> Weight {1386			T::StructureWeightInfo::find_parent().saturating_mul(Self::nesting_budget() as u64)1387		}13881389		pub fn refund_nesting_budget(1390			mut result: DispatchResultWithPostInfo,1391			budget: budget::Value,1392		) -> DispatchResultWithPostInfo {1393			let refund_amount = budget.refund_amount();1394			let consumed = Self::nesting_budget() - refund_amount;13951396			match &mut result {1397				Ok(PostDispatchInfo {1398					actual_weight: Some(weight),1399					..1400				})1401				| Err(DispatchErrorWithPostInfo {1402					post_info: PostDispatchInfo {1403						actual_weight: Some(weight),1404						..1405					},1406					..1407				}) => {1408					*weight += T::StructureWeightInfo::find_parent().saturating_mul(consumed as u64)1409				}1410				_ => {}1411			}14121413			result1414		}1415	}1416}