git.delta.rocks / unique-network / refs/commits / 2e72efeb8353

difftreelog

source

pallets/unique/src/lib.rs47.3 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, CollectionIssuer, 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(406				sender.clone(),407				CollectionIssuer::User(sender),408				data,409			)?;410411			Ok(())412		}413414		/// Destroy a collection if no tokens exist within.415		///416		/// # Permissions417		///418		/// * Collection owner419		///420		/// # Arguments421		///422		/// * `collection_id`: Collection to destroy.423		#[pallet::call_index(2)]424		#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]425		pub fn destroy_collection(426			origin: OriginFor<T>,427			collection_id: CollectionId,428		) -> DispatchResult {429			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);430431			Self::destroy_collection_internal(sender, collection_id)432		}433434		/// Add an address to allow list.435		///436		/// # Permissions437		///438		/// * Collection owner439		/// * Collection admin440		///441		/// # Arguments442		///443		/// * `collection_id`: ID of the modified collection.444		/// * `address`: ID of the address to be added to the allowlist.445		#[pallet::call_index(3)]446		#[pallet::weight(<SelfWeightOf<T>>::add_to_allow_list())]447		pub fn add_to_allow_list(448			origin: OriginFor<T>,449			collection_id: CollectionId,450			address: T::CrossAccountId,451		) -> DispatchResult {452			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {453				fail!(<pallet_common::Error<T>>::UnsupportedOperation);454			}455456			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);457			let collection = <CollectionHandle<T>>::try_get(collection_id)?;458			collection.check_is_internal()?;459460			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, true)?;461462			Ok(())463		}464465		/// Remove an address from allow list.466		///467		/// # Permissions468		///469		/// * Collection owner470		/// * Collection admin471		///472		/// # Arguments473		///474		/// * `collection_id`: ID of the modified collection.475		/// * `address`: ID of the address to be removed from the allowlist.476		#[pallet::call_index(4)]477		#[pallet::weight(<SelfWeightOf<T>>::remove_from_allow_list())]478		pub fn remove_from_allow_list(479			origin: OriginFor<T>,480			collection_id: CollectionId,481			address: T::CrossAccountId,482		) -> DispatchResult {483			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {484				fail!(<pallet_common::Error<T>>::UnsupportedOperation);485			}486487			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);488			let collection = <CollectionHandle<T>>::try_get(collection_id)?;489			collection.check_is_internal()?;490491			<PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, false)?;492493			Ok(())494		}495496		/// Change the owner of the collection.497		///498		/// # Permissions499		///500		/// * Collection owner501		///502		/// # Arguments503		///504		/// * `collection_id`: ID of the modified collection.505		/// * `new_owner`: ID of the account that will become the owner.506		#[pallet::call_index(5)]507		#[pallet::weight(<SelfWeightOf<T>>::change_collection_owner())]508		pub fn change_collection_owner(509			origin: OriginFor<T>,510			collection_id: CollectionId,511			new_owner: T::AccountId,512		) -> DispatchResult {513			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {514				fail!(<pallet_common::Error<T>>::UnsupportedOperation);515			}516			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);517			let new_owner = T::CrossAccountId::from_sub(new_owner);518			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;519			target_collection.change_owner(sender, new_owner)520		}521522		/// Add an admin to a collection.523		///524		/// NFT Collection can be controlled by multiple admin addresses525		/// (some which can also be servers, for example). Admins can issue526		/// and burn NFTs, as well as add and remove other admins,527		/// but cannot change NFT or Collection ownership.528		///529		/// # Permissions530		///531		/// * Collection owner532		/// * Collection admin533		///534		/// # Arguments535		///536		/// * `collection_id`: ID of the Collection to add an admin for.537		/// * `new_admin`: Address of new admin to add.538		#[pallet::call_index(6)]539		#[pallet::weight(<SelfWeightOf<T>>::add_collection_admin())]540		pub fn add_collection_admin(541			origin: OriginFor<T>,542			collection_id: CollectionId,543			new_admin_id: T::CrossAccountId,544		) -> DispatchResult {545			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {546				fail!(<pallet_common::Error<T>>::UnsupportedOperation);547			}548			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);549			let collection = <CollectionHandle<T>>::try_get(collection_id)?;550			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)551		}552553		/// Remove admin of a collection.554		///555		/// An admin address can remove itself. List of admins may become empty,556		/// in which case only Collection Owner will be able to add an Admin.557		///558		/// # Permissions559		///560		/// * Collection owner561		/// * Collection admin562		///563		/// # Arguments564		///565		/// * `collection_id`: ID of the collection to remove the admin for.566		/// * `account_id`: Address of the admin to remove.567		#[pallet::call_index(7)]568		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_admin())]569		pub fn remove_collection_admin(570			origin: OriginFor<T>,571			collection_id: CollectionId,572			account_id: T::CrossAccountId,573		) -> DispatchResult {574			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {575				fail!(<pallet_common::Error<T>>::UnsupportedOperation);576			}577			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);578			let collection = <CollectionHandle<T>>::try_get(collection_id)?;579			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)580		}581582		/// Set (invite) a new collection sponsor.583		///584		/// If successful, confirmation from the sponsor-to-be will be pending.585		///586		/// # Permissions587		///588		/// * Collection owner589		/// * Collection admin590		///591		/// # Arguments592		///593		/// * `collection_id`: ID of the modified collection.594		/// * `new_sponsor`: ID of the account of the sponsor-to-be.595		#[pallet::call_index(8)]596		#[pallet::weight(<SelfWeightOf<T>>::set_collection_sponsor())]597		pub fn set_collection_sponsor(598			origin: OriginFor<T>,599			collection_id: CollectionId,600			new_sponsor: T::AccountId,601		) -> DispatchResult {602			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {603				fail!(<pallet_common::Error<T>>::UnsupportedOperation);604			}605			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);606			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;607			target_collection.set_sponsor(&sender, new_sponsor.clone())608		}609610		/// Confirm own sponsorship of a collection, becoming the sponsor.611		///612		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].613		/// Sponsor can pay the fees of a transaction instead of the sender,614		/// but only within specified limits.615		///616		/// # Permissions617		///618		/// * Sponsor-to-be619		///620		/// # Arguments621		///622		/// * `collection_id`: ID of the collection with the pending sponsor.623		#[pallet::call_index(9)]624		#[pallet::weight(<SelfWeightOf<T>>::confirm_sponsorship())]625		pub fn confirm_sponsorship(626			origin: OriginFor<T>,627			collection_id: CollectionId,628		) -> DispatchResult {629			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {630				fail!(<pallet_common::Error<T>>::UnsupportedOperation);631			}632			let sender = ensure_signed(origin)?;633			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;634			target_collection.confirm_sponsorship(&sender)635		}636637		/// Remove a collection's a sponsor, making everyone pay for their own transactions.638		///639		/// # Permissions640		///641		/// * Collection owner642		///643		/// # Arguments644		///645		/// * `collection_id`: ID of the collection with the sponsor to remove.646		#[pallet::call_index(10)]647		#[pallet::weight(<SelfWeightOf<T>>::remove_collection_sponsor())]648		pub fn remove_collection_sponsor(649			origin: OriginFor<T>,650			collection_id: CollectionId,651		) -> DispatchResult {652			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {653				fail!(<pallet_common::Error<T>>::UnsupportedOperation);654			}655			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);656			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;657			target_collection.remove_sponsor(&sender)658		}659660		/// Mint an item within a collection.661		///662		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].663		///664		/// # Permissions665		///666		/// * Collection owner667		/// * Collection admin668		/// * Anyone if669		///     * Allow List is enabled, and670		///     * Address is added to allow list, and671		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])672		///673		/// # Arguments674		///675		/// * `collection_id`: ID of the collection to which an item would belong.676		/// * `owner`: Address of the initial owner of the item.677		/// * `data`: Token data describing the item to store on chain.678		#[pallet::call_index(11)]679		#[pallet::weight(T::CommonWeightInfo::create_item(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]680		pub fn create_item(681			origin: OriginFor<T>,682			collection_id: CollectionId,683			owner: T::CrossAccountId,684			data: CreateItemData,685		) -> DispatchResultWithPostInfo {686			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);687			let budget = Self::structure_nesting_budget();688689			Self::refund_nesting_budget(690				dispatch_tx::<T, _>(collection_id, |d| {691					d.create_item(sender, owner, data, &budget)692				}),693				budget,694			)695		}696697		/// Create multiple items within a collection.698		///699		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].700		///701		/// # Permissions702		///703		/// * Collection owner704		/// * Collection admin705		/// * Anyone if706		///     * Allow List is enabled, and707		///     * Address is added to the allow list, and708		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])709		///710		/// # Arguments711		///712		/// * `collection_id`: ID of the collection to which the tokens would belong.713		/// * `owner`: Address of the initial owner of the tokens.714		/// * `items_data`: Vector of data describing each item to be created.715		#[pallet::call_index(12)]716		#[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data) + <Pallet<T>>::nesting_budget_predispatch_weight())]717		pub fn create_multiple_items(718			origin: OriginFor<T>,719			collection_id: CollectionId,720			owner: T::CrossAccountId,721			items_data: Vec<CreateItemData>,722		) -> DispatchResultWithPostInfo {723			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);724			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);725			let budget = Self::structure_nesting_budget();726727			Self::refund_nesting_budget(728				dispatch_tx::<T, _>(collection_id, |d| {729					d.create_multiple_items(sender, owner, items_data, &budget)730				}),731				budget,732			)733		}734735		/// Add or change collection properties.736		///737		/// # Permissions738		///739		/// * Collection owner740		/// * Collection admin741		///742		/// # Arguments743		///744		/// * `collection_id`: ID of the modified collection.745		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.746		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.747		#[pallet::call_index(13)]748		#[pallet::weight(T::CommonWeightInfo::set_collection_properties(properties.len() as u32))]749		pub fn set_collection_properties(750			origin: OriginFor<T>,751			collection_id: CollectionId,752			properties: Vec<Property>,753		) -> DispatchResultWithPostInfo {754			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);755756			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757758			dispatch_tx::<T, _>(collection_id, |d| {759				d.set_collection_properties(sender, properties)760			})761		}762763		/// Delete specified collection properties.764		///765		/// # Permissions766		///767		/// * Collection Owner768		/// * Collection Admin769		///770		/// # Arguments771		///772		/// * `collection_id`: ID of the modified collection.773		/// * `property_keys`: Vector of keys of the properties to be deleted.774		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.775		#[pallet::call_index(14)]776		#[pallet::weight(T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32))]777		pub fn delete_collection_properties(778			origin: OriginFor<T>,779			collection_id: CollectionId,780			property_keys: Vec<PropertyKey>,781		) -> DispatchResultWithPostInfo {782			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);783784			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);785786			dispatch_tx::<T, _>(collection_id, |d| {787				d.delete_collection_properties(&sender, property_keys)788			})789		}790791		/// Add or change token properties according to collection's permissions.792		/// Currently properties only work with NFTs.793		///794		/// # Permissions795		///796		/// * Depends on collection's token property permissions and specified property mutability:797		/// 	* Collection owner798		/// 	* Collection admin799		/// 	* Token owner800		///801		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].802		///803		/// # Arguments804		///805		/// * `collection_id: ID of the collection to which the token belongs.806		/// * `token_id`: ID of the modified token.807		/// * `properties`: Vector of key-value pairs stored as the token's metadata.808		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.809		#[pallet::call_index(15)]810		#[pallet::weight(T::CommonWeightInfo::set_token_properties(properties.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]811		pub fn set_token_properties(812			origin: OriginFor<T>,813			collection_id: CollectionId,814			token_id: TokenId,815			properties: Vec<Property>,816		) -> DispatchResultWithPostInfo {817			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);818819			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);820			let budget = Self::structure_nesting_budget();821822			Self::refund_nesting_budget(823				dispatch_tx::<T, _>(collection_id, |d| {824					d.set_token_properties(sender, token_id, properties, &budget)825				}),826				budget,827			)828		}829830		/// Delete specified token properties. Currently properties only work with NFTs.831		///832		/// # Permissions833		///834		/// * Depends on collection's token property permissions and specified property mutability:835		/// 	* Collection owner836		/// 	* Collection admin837		/// 	* Token owner838		///839		/// # Arguments840		///841		/// * `collection_id`: ID of the collection to which the token belongs.842		/// * `token_id`: ID of the modified token.843		/// * `property_keys`: Vector of keys of the properties to be deleted.844		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.845		#[pallet::call_index(16)]846		#[pallet::weight(T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]847		pub fn delete_token_properties(848			origin: OriginFor<T>,849			collection_id: CollectionId,850			token_id: TokenId,851			property_keys: Vec<PropertyKey>,852		) -> DispatchResultWithPostInfo {853			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);854855			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856			let budget = Self::structure_nesting_budget();857858			Self::refund_nesting_budget(859				dispatch_tx::<T, _>(collection_id, |d| {860					d.delete_token_properties(sender, token_id, property_keys, &budget)861				}),862				budget,863			)864		}865866		/// Add or change token property permissions of a collection.867		///868		/// Without a permission for a particular key, a property with that key869		/// cannot be created in a token.870		///871		/// # Permissions872		///873		/// * Collection owner874		/// * Collection admin875		///876		/// # Arguments877		///878		/// * `collection_id`: ID of the modified collection.879		/// * `property_permissions`: Vector of permissions for property keys.880		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.881		#[pallet::call_index(17)]882		#[pallet::weight(T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32))]883		pub fn set_token_property_permissions(884			origin: OriginFor<T>,885			collection_id: CollectionId,886			property_permissions: Vec<PropertyKeyPermission>,887		) -> DispatchResultWithPostInfo {888			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);889890			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);891892			dispatch_tx::<T, _>(collection_id, |d| {893				d.set_token_property_permissions(&sender, property_permissions)894			})895		}896897		/// Create multiple items within a collection with explicitly specified initial parameters.898		///899		/// # Permissions900		///901		/// * Collection owner902		/// * Collection admin903		/// * Anyone if904		///     * Allow List is enabled, and905		///     * Address is added to allow list, and906		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])907		///908		/// # Arguments909		///910		/// * `collection_id`: ID of the collection to which the tokens would belong.911		/// * `data`: Explicit item creation data.912		#[pallet::call_index(18)]913		#[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]914		pub fn create_multiple_items_ex(915			origin: OriginFor<T>,916			collection_id: CollectionId,917			data: CreateItemExData<T::CrossAccountId>,918		) -> DispatchResultWithPostInfo {919			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);920			let budget = Self::structure_nesting_budget();921922			Self::refund_nesting_budget(923				dispatch_tx::<T, _>(collection_id, |d| {924					d.create_multiple_items_ex(sender, data, &budget)925				}),926				budget,927			)928		}929930		/// Completely allow or disallow transfers for a particular collection.931		///932		/// # Permissions933		///934		/// * Collection owner935		///936		/// # Arguments937		///938		/// * `collection_id`: ID of the collection.939		/// * `value`: New value of the flag, are transfers allowed?940		#[pallet::call_index(19)]941		#[pallet::weight(<SelfWeightOf<T>>::set_transfers_enabled_flag())]942		pub fn set_transfers_enabled_flag(943			origin: OriginFor<T>,944			collection_id: CollectionId,945			value: bool,946		) -> DispatchResult {947			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {948				fail!(<pallet_common::Error<T>>::UnsupportedOperation);949			}950			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);951			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;952			target_collection.check_is_internal()?;953			target_collection.check_is_owner(&sender)?;954955			// =========956957			target_collection.limits.transfers_enabled = Some(value);958			target_collection.save()959		}960961		/// Destroy an item.962		///963		/// # Permissions964		///965		/// * Collection owner966		/// * Collection admin967		/// * Current item owner968		///969		/// # Arguments970		///971		/// * `collection_id`: ID of the collection to which the item belongs.972		/// * `item_id`: ID of item to burn.973		/// * `value`: Number of pieces of the item to destroy.974		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.975		///     * Fungible Mode: The desired number of pieces to burn.976		///     * Re-Fungible Mode: The desired number of pieces to burn.977		#[pallet::call_index(20)]978		#[pallet::weight(T::CommonWeightInfo::burn_item())]979		pub fn burn_item(980			origin: OriginFor<T>,981			collection_id: CollectionId,982			item_id: TokenId,983			value: u128,984		) -> DispatchResultWithPostInfo {985			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);986987			let post_info =988				dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;989			if value == 1 {990				<NftTransferBasket<T>>::remove(collection_id, item_id);991				<NftApproveBasket<T>>::remove(collection_id, item_id);992			}993			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?994			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());995			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));996			Ok(post_info)997		}998999		/// Destroy a token on behalf of the owner as a non-owner account.1000		///1001		/// See also: [`approve`][`Pallet::approve`].1002		///1003		/// After this method executes, one approval is removed from the total so that1004		/// the approved address will not be able to transfer this item again from this owner.1005		///1006		/// # Permissions1007		///1008		/// * Collection owner1009		/// * Collection admin1010		/// * Current token owner1011		/// * Address approved by current item owner1012		///1013		/// # Arguments1014		///1015		/// * `from`: The owner of the burning item.1016		/// * `collection_id`: ID of the collection to which the item belongs.1017		/// * `item_id`: ID of item to burn.1018		/// * `value`: Number of pieces to burn.1019		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1020		///     * Fungible Mode: The desired number of pieces to burn.1021		///     * Re-Fungible Mode: The desired number of pieces to burn.1022		#[pallet::call_index(21)]1023		#[pallet::weight(T::CommonWeightInfo::burn_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1024		pub fn burn_from(1025			origin: OriginFor<T>,1026			collection_id: CollectionId,1027			from: T::CrossAccountId,1028			item_id: TokenId,1029			value: u128,1030		) -> DispatchResultWithPostInfo {1031			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1032			let budget = Self::structure_nesting_budget();10331034			Self::refund_nesting_budget(1035				dispatch_tx::<T, _>(collection_id, |d| {1036					d.burn_from(sender, from, item_id, value, &budget)1037				}),1038				budget,1039			)1040		}10411042		/// Change ownership of the token.1043		///1044		/// # Permissions1045		///1046		/// * Collection owner1047		/// * Collection admin1048		/// * Current token owner1049		///1050		/// # Arguments1051		///1052		/// * `recipient`: Address of token recipient.1053		/// * `collection_id`: ID of the collection the item belongs to.1054		/// * `item_id`: ID of the item.1055		///     * Non-Fungible Mode: Required.1056		///     * Fungible Mode: Ignored.1057		///     * Re-Fungible Mode: Required.1058		///1059		/// * `value`: Amount to transfer.1060		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1061		///     * Fungible Mode: The desired number of pieces to transfer.1062		///     * Re-Fungible Mode: The desired number of pieces to transfer.1063		#[pallet::call_index(22)]1064		#[pallet::weight(T::CommonWeightInfo::transfer() + <Pallet<T>>::nesting_budget_predispatch_weight())]1065		pub fn transfer(1066			origin: OriginFor<T>,1067			recipient: T::CrossAccountId,1068			collection_id: CollectionId,1069			item_id: TokenId,1070			value: u128,1071		) -> DispatchResultWithPostInfo {1072			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1073			let budget = Self::structure_nesting_budget();10741075			Self::refund_nesting_budget(1076				dispatch_tx::<T, _>(collection_id, |d| {1077					d.transfer(sender, recipient, item_id, value, &budget)1078				}),1079				budget,1080			)1081		}10821083		/// Allow a non-permissioned address to transfer or burn an item.1084		///1085		/// # Permissions1086		///1087		/// * Collection owner1088		/// * Collection admin1089		/// * Current item owner1090		///1091		/// # Arguments1092		///1093		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.1094		/// * `collection_id`: ID of the collection the item belongs to.1095		/// * `item_id`: ID of the item transactions on which are now approved.1096		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1097		/// Set to 0 to revoke the approval.1098		#[pallet::call_index(23)]1099		#[pallet::weight(T::CommonWeightInfo::approve())]1100		pub fn approve(1101			origin: OriginFor<T>,1102			spender: T::CrossAccountId,1103			collection_id: CollectionId,1104			item_id: TokenId,1105			amount: u128,1106		) -> DispatchResultWithPostInfo {1107			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11081109			dispatch_tx::<T, _>(collection_id, |d| {1110				d.approve(sender, spender, item_id, amount)1111			})1112		}11131114		/// Allow a non-permissioned address to transfer or burn an item from owner's eth mirror.1115		///1116		/// # Permissions1117		///1118		/// * Collection owner1119		/// * Collection admin1120		/// * Current item owner1121		///1122		/// # Arguments1123		///1124		/// * `from`: Owner's account eth mirror1125		/// * `to`: Account to be approved to make specific transactions on non-owned tokens.1126		/// * `collection_id`: ID of the collection the item belongs to.1127		/// * `item_id`: ID of the item transactions on which are now approved.1128		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1129		/// Set to 0 to revoke the approval.1130		#[pallet::call_index(24)]1131		#[pallet::weight(T::CommonWeightInfo::approve_from())]1132		pub fn approve_from(1133			origin: OriginFor<T>,1134			from: T::CrossAccountId,1135			to: T::CrossAccountId,1136			collection_id: CollectionId,1137			item_id: TokenId,1138			amount: u128,1139		) -> DispatchResultWithPostInfo {1140			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11411142			dispatch_tx::<T, _>(collection_id, |d| {1143				d.approve_from(sender, from, to, item_id, amount)1144			})1145		}11461147		/// Change ownership of an item on behalf of the owner as a non-owner account.1148		///1149		/// See the [`approve`][`Pallet::approve`] method for additional information.1150		///1151		/// After this method executes, one approval is removed from the total so that1152		/// the approved address will not be able to transfer this item again from this owner.1153		///1154		/// # Permissions1155		///1156		/// * Collection owner1157		/// * Collection admin1158		/// * Current item owner1159		/// * Address approved by current item owner1160		///1161		/// # Arguments1162		///1163		/// * `from`: Address that currently owns the token.1164		/// * `recipient`: Address of the new token-owner-to-be.1165		/// * `collection_id`: ID of the collection the item.1166		/// * `item_id`: ID of the item to be transferred.1167		/// * `value`: Amount to transfer.1168		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1169		///     * Fungible Mode: The desired number of pieces to transfer.1170		///     * Re-Fungible Mode: The desired number of pieces to transfer.1171		#[pallet::call_index(25)]1172		#[pallet::weight(T::CommonWeightInfo::transfer_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1173		pub fn transfer_from(1174			origin: OriginFor<T>,1175			from: T::CrossAccountId,1176			recipient: T::CrossAccountId,1177			collection_id: CollectionId,1178			item_id: TokenId,1179			value: u128,1180		) -> DispatchResultWithPostInfo {1181			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1182			let budget = Self::structure_nesting_budget();11831184			Self::refund_nesting_budget(1185				dispatch_tx::<T, _>(collection_id, |d| {1186					d.transfer_from(sender, from, recipient, item_id, value, &budget)1187				}),1188				budget,1189			)1190		}11911192		/// Set specific limits of a collection. Empty, or None fields mean chain default.1193		///1194		/// # Permissions1195		///1196		/// * Collection owner1197		/// * Collection admin1198		///1199		/// # Arguments1200		///1201		/// * `collection_id`: ID of the modified collection.1202		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1203		/// will not overwrite the old ones.1204		#[pallet::call_index(26)]1205		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1206		pub fn set_collection_limits(1207			origin: OriginFor<T>,1208			collection_id: CollectionId,1209			new_limit: CollectionLimits,1210		) -> DispatchResult {1211			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1212				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1213			}1214			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1215			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1216			<PalletCommon<T>>::update_limits(&sender, &mut target_collection, new_limit)1217		}12181219		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1220		///1221		/// # Permissions1222		///1223		/// * Collection owner1224		/// * Collection admin1225		///1226		/// # Arguments1227		///1228		/// * `collection_id`: ID of the modified collection.1229		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1230		/// will not overwrite the old ones.1231		#[pallet::call_index(27)]1232		#[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1233		pub fn set_collection_permissions(1234			origin: OriginFor<T>,1235			collection_id: CollectionId,1236			new_permission: CollectionPermissions,1237		) -> DispatchResult {1238			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1239				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1240			}1241			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1242			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1243			<PalletCommon<T>>::update_permissions(&sender, &mut target_collection, new_permission)1244		}12451246		/// Re-partition a refungible token, while owning all of its parts/pieces.1247		///1248		/// # Permissions1249		///1250		/// * Token owner (must own every part)1251		///1252		/// # Arguments1253		///1254		/// * `collection_id`: ID of the collection the RFT belongs to.1255		/// * `token_id`: ID of the RFT.1256		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1257		#[pallet::call_index(28)]1258		#[pallet::weight(T::RefungibleExtensionsWeightInfo::repartition())]1259		pub fn repartition(1260			origin: OriginFor<T>,1261			collection_id: CollectionId,1262			token_id: TokenId,1263			amount: u128,1264		) -> DispatchResultWithPostInfo {1265			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1266			dispatch_tx::<T, _>(collection_id, |d| {1267				if let Some(refungible_extensions) = d.refungible_extensions() {1268					refungible_extensions.repartition(&sender, token_id, amount)1269				} else {1270					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1271				}1272			})1273		}12741275		/// Sets or unsets the approval of a given operator.1276		///1277		/// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.1278		///1279		/// # Arguments1280		///1281		/// * `owner`: Token owner1282		/// * `operator`: Operator1283		/// * `approve`: Should operator status be granted or revoked?1284		#[pallet::call_index(29)]1285		#[pallet::weight(T::CommonWeightInfo::set_allowance_for_all())]1286		pub fn set_allowance_for_all(1287			origin: OriginFor<T>,1288			collection_id: CollectionId,1289			operator: T::CrossAccountId,1290			approve: bool,1291		) -> DispatchResultWithPostInfo {1292			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1293			dispatch_tx::<T, _>(collection_id, |d| {1294				d.set_allowance_for_all(sender, operator, approve)1295			})1296		}12971298		/// Repairs a collection if the data was somehow corrupted.1299		///1300		/// # Arguments1301		///1302		/// * `collection_id`: ID of the collection to repair.1303		#[pallet::call_index(30)]1304		#[pallet::weight(<SelfWeightOf<T>>::force_repair_collection())]1305		pub fn force_repair_collection(1306			origin: OriginFor<T>,1307			collection_id: CollectionId,1308		) -> DispatchResult {1309			if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1310				fail!(<pallet_common::Error<T>>::UnsupportedOperation);1311			}1312			ensure_root(origin)?;1313			<PalletCommon<T>>::repair_collection(collection_id)1314		}13151316		/// Repairs a token if the data was somehow corrupted.1317		///1318		/// # Arguments1319		///1320		/// * `collection_id`: ID of the collection the item belongs to.1321		/// * `item_id`: ID of the item.1322		#[pallet::call_index(31)]1323		#[pallet::weight(T::CommonWeightInfo::force_repair_item())]1324		pub fn force_repair_item(1325			origin: OriginFor<T>,1326			collection_id: CollectionId,1327			item_id: TokenId,1328		) -> DispatchResultWithPostInfo {1329			ensure_root(origin)?;1330			dispatch_tx::<T, _>(collection_id, |d| d.repair_item(item_id))1331		}1332	}13331334	impl<T: Config> Pallet<T> {1335		/// Force set `sponsor` for `collection`.1336		///1337		/// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation1338		/// from the `sponsor` is not required.1339		///1340		/// # Arguments1341		///1342		/// * `sponsor`: ID of the account of the sponsor-to-be.1343		/// * `collection_id`: ID of the modified collection.1344		pub fn force_set_sponsor(1345			sponsor: T::AccountId,1346			collection_id: CollectionId,1347		) -> DispatchResult {1348			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1349			target_collection.force_set_sponsor(sponsor)1350		}13511352		/// Force remove `sponsor` for `collection`.1353		///1354		/// Differs from `remove_sponsor` in that1355		/// it doesn't require consent from the `owner` of the collection.1356		///1357		/// # Arguments1358		///1359		/// * `collection_id`: ID of the modified collection.1360		pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1361			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1362			target_collection.force_remove_sponsor()1363		}13641365		#[inline(always)]1366		pub(crate) fn destroy_collection_internal(1367			sender: T::CrossAccountId,1368			collection_id: CollectionId,1369		) -> DispatchResult {1370			T::CollectionDispatch::destroy(sender, collection_id)?;13711372			// TODO: basket cleanup should be moved elsewhere1373			// Maybe runtime dispatch.rs should perform it?13741375			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1376			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1377			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13781379			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1380			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1381			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13821383			Ok(())1384		}13851386		fn structure_nesting_budget() -> budget::Value {1387			budget::Value::new(Self::nesting_budget())1388		}13891390		fn nesting_budget_predispatch_weight() -> Weight {1391			T::StructureWeightInfo::find_parent().saturating_mul(Self::nesting_budget() as u64)1392		}13931394		pub fn refund_nesting_budget(1395			mut result: DispatchResultWithPostInfo,1396			budget: budget::Value,1397		) -> DispatchResultWithPostInfo {1398			let refund_amount = budget.refund_amount();1399			let consumed = Self::nesting_budget() - refund_amount;14001401			match &mut result {1402				Ok(PostDispatchInfo {1403					actual_weight: Some(weight),1404					..1405				})1406				| Err(DispatchErrorWithPostInfo {1407					post_info: PostDispatchInfo {1408						actual_weight: Some(weight),1409						..1410					},1411					..1412				}) => {1413					*weight += T::StructureWeightInfo::find_parent().saturating_mul(consumed as u64)1414				}1415				_ => {}1416			}14171418			result1419		}1420	}1421}