git.delta.rocks / unique-network / refs/commits / 7fd36cea2f6e

difftreelog

refactor remove `#[transactional]` from extrinsics

Yaroslav Bolyukin2022-07-21parent: #e198017.patch.diff
in: master
Every extrinsic now runs in transaction implicitly, and
`#[transactional]` on pallet dispatchable is now meaningless

4 files changed

modifiedpallets/evm-migration/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-migration/src/lib.rs
+++ b/pallets/evm-migration/src/lib.rs
@@ -23,7 +23,7 @@
 
 #[frame_support::pallet]
 pub mod pallet {
-	use frame_support::{pallet_prelude::*, transactional};
+	use frame_support::pallet_prelude::*;
 	use frame_system::pallet_prelude::*;
 	use sp_core::{H160, H256};
 	use sp_std::vec::Vec;
@@ -84,7 +84,6 @@
 		}
 
 		#[pallet::weight(<SelfWeightOf<T>>::finish(code.len() as u32))]
-		#[transactional]
 		pub fn finish(origin: OriginFor<T>, address: H160, code: Vec<u8>) -> DispatchResult {
 			ensure_root(origin)?;
 			ensure!(
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -145,7 +145,7 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{pallet_prelude::*, BoundedVec, dispatch::DispatchResult};
 use frame_system::{pallet_prelude::*, ensure_signed};
 use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
 use sp_std::{
@@ -350,11 +350,11 @@
 		/// * Anyone - will be assigned as the issuer of the collection.
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `metadata`: Metadata describing the collection, e.g. IPFS hash. Cannot be changed.
 		/// - `max`: Optional maximum number of tokens.
 		/// - `symbol`: UTF-8 string with token prefix, by which to represent the token in wallets and UIs.
 		/// Analogous to Unique's [`token_prefix`](up_data_structs::Collection). Cannot be changed.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]
 		pub fn create_collection(
 			origin: OriginFor<T>,
@@ -426,8 +426,8 @@
 		/// * Collection issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `collection_id`: RMRK ID of the collection to destroy.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]
 		pub fn destroy_collection(
 			origin: OriginFor<T>,
@@ -459,9 +459,9 @@
 		/// * Collection issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `collection_id`: RMRK collection ID to change the issuer of.
 		/// - `new_issuer`: Collection's new issuer.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]
 		pub fn change_collection_issuer(
 			origin: OriginFor<T>,
@@ -497,8 +497,8 @@
 		/// * Collection issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `collection_id`: RMRK ID of the collection to lock.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::lock_collection())]
 		pub fn lock_collection(
 			origin: OriginFor<T>,
@@ -535,6 +535,7 @@
 		/// * Collection issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `owner`: Owner account of the NFT. If set to None, defaults to the sender (collection issuer).
 		/// - `collection_id`: RMRK collection ID for the NFT to be minted within. Cannot be changed.
 		/// - `recipient`: Receiver account of the royalty. Has no effect if the `royalty_amount` is not set. Cannot be changed.
@@ -542,7 +543,6 @@
 		/// - `metadata`: Arbitrary data about an NFT, e.g. IPFS hash. Cannot be changed.
 		/// - `transferable`: Can this NFT be transferred? Cannot be changed.
 		/// - `resources`: Resource data to be added to the NFT immediately after minting.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]
 		pub fn mint_nft(
 			origin: OriginFor<T>,
@@ -620,12 +620,12 @@
 		/// * Token owner
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `collection_id`: RMRK ID of the collection in which the NFT to burn belongs to.
 		/// - `nft_id`: ID of the NFT to be destroyed.
 		/// - `max_burns`: Maximum number of tokens to burn, assuming nesting. The transaction
 		/// is reverted if there are more tokens to burn in the nesting tree than this number.
 		/// This is primarily a mechanism of transaction weight control.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]
 		pub fn burn_nft(
 			origin: OriginFor<T>,
@@ -669,10 +669,10 @@
 		/// - Token owner
 		///
 		/// # Arguments:
-		/// - `collection_id`: RMRK ID of the collection of the NFT to be transferred.
-		/// - `nft_id`: ID of the NFT to be transferred.
+		/// - `origin`: sender of the transaction
+		/// - `rmrk_collection_id`: RMRK ID of the collection of the NFT to be transferred.
+		/// - `rmrk_nft_id`: ID of the NFT to be transferred.
 		/// - `new_owner`: New owner of the nft which can be either an account or a NFT.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::send())]
 		pub fn send(
 			origin: OriginFor<T>,
@@ -793,11 +793,11 @@
 		/// - Token-owner-to-be
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT to be accepted.
 		/// - `rmrk_nft_id`: ID of the NFT to be accepted.
 		/// - `new_owner`: Either the sender's account ID or a sender-owned NFT,
 		/// whichever the accepted NFT was sent to.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::accept_nft())]
 		pub fn accept_nft(
 			origin: OriginFor<T>,
@@ -885,9 +885,9 @@
 		/// - Token-owner-to-be-not
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK ID of the NFT to be rejected.
 		/// - `rmrk_nft_id`: ID of the NFT to be rejected.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::reject_nft())]
 		pub fn reject_nft(
 			origin: OriginFor<T>,
@@ -950,10 +950,11 @@
 		/// - Token owner
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `rmrk_nft_id`: ID of the NFT with a pending resource to be accepted.
 		/// - `resource_id`: ID of the newly created pending resource.
-		#[transactional]
+		/// accept the addition of a new resource to an existing NFT
 		#[pallet::weight(<SelfWeightOf<T>>::accept_resource())]
 		pub fn accept_resource(
 			origin: OriginFor<T>,
@@ -1004,10 +1005,10 @@
 		/// - Token owner
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `rmrk_nft_id`: ID of the NFT with a resource to be removed.
 		/// - `resource_id`: ID of the removal-pending resource.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]
 		pub fn accept_resource_removal(
 			origin: OriginFor<T>,
@@ -1084,11 +1085,11 @@
 		/// - Token owner - in case of NFT property
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID.
 		/// - `maybe_nft_id`: Optional ID of the NFT. If left empty, then the property is set for the collection.
 		/// - `key`: Key of the custom property to be referenced by.
 		/// - `value`: Value of the custom property to be stored.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::set_property())]
 		pub fn set_property(
 			origin: OriginFor<T>,
@@ -1158,10 +1159,10 @@
 		/// - Token owner
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `rmrk_nft_id`: ID of the NFT to rearrange resource priorities for.
 		/// - `priorities`: Ordered vector of resource IDs.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::set_priority())]
 		pub fn set_priority(
 			origin: OriginFor<T>,
@@ -1209,10 +1210,10 @@
 		/// the owner's [acceptance](Pallet::accept_resource).
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `nft_id`: ID of the NFT to assign a resource to.
 		/// - `resource`: Data of the resource to be created.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]
 		pub fn add_basic_resource(
 			origin: OriginFor<T>,
@@ -1251,10 +1252,10 @@
 		/// the owner's [acceptance](Pallet::accept_resource).
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `nft_id`: ID of the NFT to assign a resource to.
 		/// - `resource`: Data of the resource to be created.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]
 		pub fn add_composable_resource(
 			origin: OriginFor<T>,
@@ -1313,10 +1314,10 @@
 		/// the owner's [acceptance](Pallet::accept_resource).
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
 		/// - `nft_id`: ID of the NFT to assign a resource to.
 		/// - `resource`: Data of the resource to be created.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]
 		pub fn add_slot_resource(
 			origin: OriginFor<T>,
@@ -1354,10 +1355,10 @@
 		/// - Collection issuer
 		///
 		/// # Arguments
-		/// - `collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
+		/// - `origin`: sender of the transaction
+		/// - `rmrk_collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
 		/// - `nft_id`: ID of the NFT with a resource to be removed.
 		/// - `resource_id`: ID of the resource to be removed.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::remove_resource())]
 		pub fn remove_resource(
 			origin: OriginFor<T>,
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -133,7 +133,7 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{pallet_prelude::*, BoundedVec, dispatch::DispatchResult};
 use frame_system::{pallet_prelude::*, ensure_signed};
 use sp_runtime::DispatchError;
 use up_data_structs::*;
@@ -226,11 +226,11 @@
 		/// - Anyone - will be assigned as the issuer of the Base.
 		///
 		/// # Arguments:
+		/// - `origin`: Caller, will be assigned as the issuer of the Base
 		/// - `base_type`: Arbitrary media type, e.g. "svg".
 		/// - `symbol`: Arbitrary client-chosen symbol.
 		/// - `parts`: Array of Fixed and Slot Parts composing the Base,
 		/// confined in length by [`RmrkPartsLimit`](up_data_structs::RmrkPartsLimit).
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]
 		pub fn create_base(
 			origin: OriginFor<T>,
@@ -295,13 +295,13 @@
 		/// - Base issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `base_id`: Base ID containing the Theme to be updated.
 		/// - `theme`: Theme to add to the Base.  A Theme has a name and properties, which are an
 		///   array of [key, value, inherit].
 		///   - `key`: Arbitrary BoundedString, defined by client.
 		///   - `value`: Arbitrary BoundedString, defined by client.
 		///   - `inherit`: Optional bool.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]
 		pub fn theme_add(
 			origin: OriginFor<T>,
@@ -359,10 +359,10 @@
 		/// - Base issuer
 		///
 		/// # Arguments:
+		/// - `origin`: sender of the transaction
 		/// - `base_id`: Base containing the Slot Part to be updated.
-		/// - `part_id`: Slot Part whose Equippable List is being updated.
+		/// - `slot_id`: Slot Part whose Equippable List is being updated .
 		/// - `equippables`: List of equippables that will override the current Equippables list.
-		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::equippable())]
 		pub fn equippable(
 			origin: OriginFor<T>,
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
before · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # 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::{77	decl_module, decl_storage, decl_error, decl_event,78	dispatch::DispatchResult,79	ensure, fail,80	weights::{Weight},81	transactional,82	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},83	BoundedVec,84};85use scale_info::TypeInfo;86use frame_system::{self as system, ensure_signed};87use sp_runtime::{sp_std::prelude::Vec};88use up_data_structs::{89	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,90	CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,91	SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,92	PropertyKeyPermission,93};94use pallet_evm::account::CrossAccountId;95use pallet_common::{96	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,97	dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,98};99pub mod eth;100101#[cfg(feature = "runtime-benchmarks")]102mod benchmarking;103pub mod weights;104use weights::WeightInfo;105106/// Maximum number of levels of depth in the token nesting tree.107pub const NESTING_BUDGET: u32 = 5;108109decl_error! {110	/// Errors for the common Unique transactions.111	pub enum Error for Module<T: Config> {112		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].113		CollectionDecimalPointLimitExceeded,114		/// This address is not set as sponsor, use setCollectionSponsor first.115		ConfirmUnsetSponsorFail,116		/// Length of items properties must be greater than 0.117		EmptyArgument,118		/// Repertition is only supported by refungible collection.119		RepartitionCalledOnNonRefungibleCollection,120	}121}122123/// Configuration trait of this pallet.124pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {125	/// Overarching event type.126	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;127128	/// 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	/// Weight info information for extra refungible pallet operations.135	type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;136}137138decl_event! {139	pub enum Event<T>140	where141		<T as frame_system::Config>::AccountId,142		<T as pallet_evm::account::Config>::CrossAccountId,143	{144		/// Collection sponsor was removed145		///146		/// # Arguments147		/// * collection_id: ID of the affected collection.148		CollectionSponsorRemoved(CollectionId),149150		/// Collection admin was added151		///152		/// # Arguments153		/// * collection_id: ID of the affected collection.154		/// * admin: Admin address.155		CollectionAdminAdded(CollectionId, CrossAccountId),156157		/// Collection owned was changed158		///159		/// # Arguments160		/// * collection_id: ID of the affected collection.161		/// * owner: New owner address.162		CollectionOwnedChanged(CollectionId, AccountId),163164		/// Collection sponsor was set165		///166		/// # Arguments167		/// * collection_id: ID of the affected collection.168		/// * owner: New sponsor address.169		CollectionSponsorSet(CollectionId, AccountId),170171		/// New sponsor was confirm172		///173		/// # Arguments174		/// * collection_id: ID of the affected collection.175		/// * sponsor: New sponsor address.176		SponsorshipConfirmed(CollectionId, AccountId),177178		/// Collection admin was removed179		///180		/// # Arguments181		/// * collection_id: ID of the affected collection.182		/// * admin: Removed admin address.183		CollectionAdminRemoved(CollectionId, CrossAccountId),184185		/// Address was removed from the allow list186		///187		/// # Arguments188		/// * collection_id: ID of the affected collection.189		/// * user: Address of the removed account.190		AllowListAddressRemoved(CollectionId, CrossAccountId),191192		/// Address was added to the allow list193		///194		/// # Arguments195		/// * collection_id: ID of the affected collection.196		/// * user: Address of the added account.197		AllowListAddressAdded(CollectionId, CrossAccountId),198199		/// Collection limits were set200		///201		/// # Arguments202		/// * collection_id: ID of the affected collection.203		CollectionLimitSet(CollectionId),204205		/// Collection permissions were set206		///207		/// # Arguments208		/// * collection_id: ID of the affected collection.209		CollectionPermissionSet(CollectionId),210	}211}212213type SelfWeightOf<T> = <T as Config>::WeightInfo;214215// # Used definitions216//217// ## User control levels218//219// chain-controlled - key is uncontrolled by user220//                    i.e autoincrementing index221//                    can use non-cryptographic hash222// real - key is controlled by user223//        but it is hard to generate enough colliding values, i.e owner of signed txs224//        can use non-cryptographic hash225// controlled - key is completly controlled by users226//              i.e maps with mutable keys227//              should use cryptographic hash228//229// ## User control level downgrade reasons230//231// ?1 - chain-controlled -> controlled232//      collections/tokens can be destroyed, resulting in massive holes233// ?2 - chain-controlled -> controlled234//      same as ?1, but can be only added, resulting in easier exploitation235// ?3 - real -> controlled236//      no confirmation required, so addresses can be easily generated237decl_storage! {238	trait Store for Module<T: Config> as Unique {239240		//#region Private members241		/// Used for migrations242		ChainVersion: u64;243		//#endregion244245		//#region Tokens transfer sponosoring rate limit baskets246		/// (Collection id (controlled?2), who created (real))247		/// TODO: Off chain worker should remove from this map when collection gets removed248		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;249		/// Collection id (controlled?2), token id (controlled?2)250		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;251		/// Collection id (controlled?2), owning user (real)252		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;253		/// Collection id (controlled?2), token id (controlled?2)254		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;255		//#endregion256257		/// Variable metadata sponsoring258		/// Collection id (controlled?2), token id (controlled?2)259		#[deprecated]260		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;261		/// Last sponsoring of token property setting // todo:doc rephrase this and the following262		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;263264		/// Last sponsoring of NFT approval in a collection265		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;266		/// Last sponsoring of fungible tokens approval in a collection267		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;268		/// Last sponsoring of RFT approval in a collection269		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;270	}271}272273decl_module! {274	/// Type alias to Pallet, to be used by construct_runtime.275	pub struct Module<T: Config> for enum Call276	where277		origin: T::Origin278	{279		type Error = Error<T>;280281		fn deposit_event() = default;282283		fn on_initialize(_now: T::BlockNumber) -> Weight {284			0285		}286287		fn on_runtime_upgrade() -> Weight {288			0289		}290291		/// Create a collection of tokens.292		///293		/// Each Token may have multiple properties encoded as an array of bytes294		/// of certain length. The initial owner of the collection is set295		/// to the address that signed the transaction and can be changed later.296		///297		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.298		///299		/// # Permissions300		///301		/// * Anyone - becomes the owner of the new collection.302		///303		/// # Arguments304		///305		/// * `collection_name`: Wide-character string with collection name306		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).307		/// * `collection_description`: Wide-character string with collection description308		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).309		/// * `token_prefix`: Byte string containing the token prefix to mark a collection310		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).311		/// * `mode`: Type of items stored in the collection and type dependent data.312		// returns collection ID313		#[weight = <SelfWeightOf<T>>::create_collection()]314		#[transactional]315		#[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]316		pub fn create_collection(317			origin,318			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,319			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,320			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,321			mode: CollectionMode322		) -> DispatchResult {323			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {324				name: collection_name,325				description: collection_description,326				token_prefix,327				mode,328				..Default::default()329			};330			Self::create_collection_ex(origin, data)331		}332333		/// Create a collection with explicit parameters.334		///335		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.336		///337		/// # Permissions338		///339		/// * Anyone - becomes the owner of the new collection.340		///341		/// # Arguments342		///343		/// * `data`: Explicit data of a collection used for its creation.344		#[weight = <SelfWeightOf<T>>::create_collection()]345		#[transactional]346		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {347			let sender = ensure_signed(origin)?;348349			// =========350351			let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;352353			Ok(())354		}355356		/// Destroy a collection if no tokens exist within.357		///358		/// # Permissions359		///360		/// * Collection owner361		///362		/// # Arguments363		///364		/// * `collection_id`: Collection to destroy.365		#[weight = <SelfWeightOf<T>>::destroy_collection()]366		#[transactional]367		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {368			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);369			let collection = <CollectionHandle<T>>::try_get(collection_id)?;370			collection.check_is_internal()?;371372			// =========373374			T::CollectionDispatch::destroy(sender, collection)?;375376			// TODO: basket cleanup should be moved elsewhere377			// Maybe runtime dispatch.rs should perform it?378379			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);384			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);385			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);386387			Ok(())388		}389390		/// Add an address to allow list.391		///392		/// # Permissions393		///394		/// * Collection owner395		/// * Collection admin396		///397		/// # Arguments398		///399		/// * `collection_id`: ID of the modified collection.400		/// * `address`: ID of the address to be added to the allowlist.401		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]402		#[transactional]403		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{404405			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);406			let collection = <CollectionHandle<T>>::try_get(collection_id)?;407			collection.check_is_internal()?;408409			<PalletCommon<T>>::toggle_allowlist(410				&collection,411				&sender,412				&address,413				true,414			)?;415416			Self::deposit_event(Event::<T>::AllowListAddressAdded(417				collection_id,418				address419			));420421			Ok(())422		}423424		/// Remove an address from allow list.425		///426		/// # Permissions427		///428		/// * Collection owner429		/// * Collection admin430		///431		/// # Arguments432		///433		/// * `collection_id`: ID of the modified collection.434		/// * `address`: ID of the address to be removed from the allowlist.435		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]436		#[transactional]437		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{438439			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);440			let collection = <CollectionHandle<T>>::try_get(collection_id)?;441			collection.check_is_internal()?;442443			<PalletCommon<T>>::toggle_allowlist(444				&collection,445				&sender,446				&address,447				false,448			)?;449450			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(451				collection_id,452				address453			));454455			Ok(())456		}457458		/// Change the owner of the collection.459		///460		/// # Permissions461		///462		/// * Collection owner463		///464		/// # Arguments465		///466		/// * `collection_id`: ID of the modified collection.467		/// * `new_owner`: ID of the account that will become the owner.468		#[weight = <SelfWeightOf<T>>::change_collection_owner()]469		#[transactional]470		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {471472			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473474			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;475			target_collection.check_is_internal()?;476			target_collection.check_is_owner(&sender)?;477478			target_collection.owner = new_owner.clone();479			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(480				collection_id,481				new_owner482			));483484			target_collection.save()485		}486487		/// Add an admin to a collection.488		///489		/// NFT Collection can be controlled by multiple admin addresses490		/// (some which can also be servers, for example). Admins can issue491		/// and burn NFTs, as well as add and remove other admins,492		/// but cannot change NFT or Collection ownership.493		///494		/// # Permissions495		///496		/// * Collection owner497		/// * Collection admin498		///499		/// # Arguments500		///501		/// * `collection_id`: ID of the Collection to add an admin for.502		/// * `new_admin`: Address of new admin to add.503		#[weight = <SelfWeightOf<T>>::add_collection_admin()]504		#[transactional]505		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {506			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);507			let collection = <CollectionHandle<T>>::try_get(collection_id)?;508			collection.check_is_internal()?;509510			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(511				collection_id,512				new_admin.clone()513			));514515			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)516		}517518		/// Remove admin of a collection.519		///520		/// An admin address can remove itself. List of admins may become empty,521		/// in which case only Collection Owner will be able to add an Admin.522		///523		/// # Permissions524		///525		/// * Collection owner526		/// * Collection admin527		///528		/// # Arguments529		///530		/// * `collection_id`: ID of the collection to remove the admin for.531		/// * `account_id`: Address of the admin to remove.532		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]533		#[transactional]534		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {535			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);536			let collection = <CollectionHandle<T>>::try_get(collection_id)?;537			collection.check_is_internal()?;538539			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(540				collection_id,541				account_id.clone()542			));543544			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)545		}546547		/// Set (invite) a new collection sponsor.548		///549		/// If successful, confirmation from the sponsor-to-be will be pending.550		///551		/// # Permissions552		///553		/// * Collection owner554		/// * Collection admin555		///556		/// # Arguments557		///558		/// * `collection_id`: ID of the modified collection.559		/// * `new_sponsor`: ID of the account of the sponsor-to-be.560		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]561		#[transactional]562		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {563			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);564565			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;566			target_collection.check_is_owner_or_admin(&sender)?;567			target_collection.check_is_internal()?;568569			target_collection.set_sponsor(new_sponsor.clone())?;570571			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(572				collection_id,573				new_sponsor574			));575576			target_collection.save()577		}578579		/// Confirm own sponsorship of a collection, becoming the sponsor.580		///581		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].582		/// Sponsor can pay the fees of a transaction instead of the sender,583		/// but only within specified limits.584		///585		/// # Permissions586		///587		/// * Sponsor-to-be588		///589		/// # Arguments590		///591		/// * `collection_id`: ID of the collection with the pending sponsor.592		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]593		#[transactional]594		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {595			let sender = ensure_signed(origin)?;596597			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;598			target_collection.check_is_internal()?;599			ensure!(600				target_collection.confirm_sponsorship(&sender)?,601				Error::<T>::ConfirmUnsetSponsorFail602			);603604			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(605				collection_id,606				sender607			));608609			target_collection.save()610		}611612		/// Remove a collection's a sponsor, making everyone pay for their own transactions.613		///614		/// # Permissions615		///616		/// * Collection owner617		///618		/// # Arguments619		///620		/// * `collection_id`: ID of the collection with the sponsor to remove.621		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]622		#[transactional]623		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {624			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);625626			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;627			target_collection.check_is_internal()?;628			target_collection.check_is_owner(&sender)?;629630			target_collection.sponsorship = SponsorshipState::Disabled;631632			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(633				collection_id634			));635			target_collection.save()636		}637638		/// Mint an item within a collection.639		///640		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].641		///642		/// # Permissions643		///644		/// * Collection owner645		/// * Collection admin646		/// * Anyone if647		///     * Allow List is enabled, and648		///     * Address is added to allow list, and649		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])650		///651		/// # Arguments652		///653		/// * `collection_id`: ID of the collection to which an item would belong.654		/// * `owner`: Address of the initial owner of the item.655		/// * `data`: Token data describing the item to store on chain.656		#[weight = T::CommonWeightInfo::create_item()]657		#[transactional]658		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {659			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);660			let budget = budget::Value::new(NESTING_BUDGET);661662			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))663		}664665		/// Create multiple items within a collection.666		///667		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].668		///669		/// # Permissions670		///671		/// * Collection owner672		/// * Collection admin673		/// * Anyone if674		///     * Allow List is enabled, and675		///     * Address is added to the allow list, and676		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])677		///678		/// # Arguments679		///680		/// * `collection_id`: ID of the collection to which the tokens would belong.681		/// * `owner`: Address of the initial owner of the tokens.682		/// * `items_data`: Vector of data describing each item to be created.683		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]684		#[transactional]685		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {686			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);687			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688			let budget = budget::Value::new(NESTING_BUDGET);689690			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))691		}692693		/// Add or change collection properties.694		///695		/// # Permissions696		///697		/// * Collection owner698		/// * Collection admin699		///700		/// # Arguments701		///702		/// * `collection_id`: ID of the modified collection.703		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.704		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.705		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]706		#[transactional]707		pub fn set_collection_properties(708			origin,709			collection_id: CollectionId,710			properties: Vec<Property>711		) -> DispatchResultWithPostInfo {712			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);713714			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);715716			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))717		}718719		/// Delete specified collection properties.720		///721		/// # Permissions722		///723		/// * Collection Owner724		/// * Collection Admin725		///726		/// # Arguments727		///728		/// * `collection_id`: ID of the modified collection.729		/// * `property_keys`: Vector of keys of the properties to be deleted.730		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.731		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]732		#[transactional]733		pub fn delete_collection_properties(734			origin,735			collection_id: CollectionId,736			property_keys: Vec<PropertyKey>,737		) -> DispatchResultWithPostInfo {738			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);739740			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);741742			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))743		}744745		/// Add or change token properties according to collection's permissions.746		/// Currently properties only work with NFTs.747		///748		/// # Permissions749		///750		/// * Depends on collection's token property permissions and specified property mutability:751		/// 	* Collection owner752		/// 	* Collection admin753		/// 	* Token owner754		///755		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].756		///757		/// # Arguments758		///759		/// * `collection_id: ID of the collection to which the token belongs.760		/// * `token_id`: ID of the modified token.761		/// * `properties`: Vector of key-value pairs stored as the token's metadata.762		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.763		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]764		#[transactional]765		pub fn set_token_properties(766			origin,767			collection_id: CollectionId,768			token_id: TokenId,769			properties: Vec<Property>770		) -> DispatchResultWithPostInfo {771			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);772773			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);774			let budget = budget::Value::new(NESTING_BUDGET);775776			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))777		}778779		/// Delete specified token properties. Currently properties only work with NFTs.780		///781		/// # Permissions782		///783		/// * Depends on collection's token property permissions and specified property mutability:784		/// 	* Collection owner785		/// 	* Collection admin786		/// 	* Token owner787		///788		/// # Arguments789		///790		/// * `collection_id`: ID of the collection to which the token belongs.791		/// * `token_id`: ID of the modified token.792		/// * `property_keys`: Vector of keys of the properties to be deleted.793		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.794		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]795		#[transactional]796		pub fn delete_token_properties(797			origin,798			collection_id: CollectionId,799			token_id: TokenId,800			property_keys: Vec<PropertyKey>801		) -> DispatchResultWithPostInfo {802			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);803804			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);805			let budget = budget::Value::new(NESTING_BUDGET);806807			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))808		}809810		/// Add or change token property permissions of a collection.811		///812		/// Without a permission for a particular key, a property with that key813		/// cannot be created in a token.814		///815		/// # Permissions816		///817		/// * Collection owner818		/// * Collection admin819		///820		/// # Arguments821		///822		/// * `collection_id`: ID of the modified collection.823		/// * `property_permissions`: Vector of permissions for property keys.824		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.825		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]826		#[transactional]827		pub fn set_token_property_permissions(828			origin,829			collection_id: CollectionId,830			property_permissions: Vec<PropertyKeyPermission>,831		) -> DispatchResultWithPostInfo {832			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);833834			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);835836			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))837		}838839		/// Create multiple items within a collection with explicitly specified initial parameters.840		///841		/// # Permissions842		///843		/// * Collection owner844		/// * Collection admin845		/// * Anyone if846		///     * Allow List is enabled, and847		///     * Address is added to allow list, and848		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])849		///850		/// # Arguments851		///852		/// * `collection_id`: ID of the collection to which the tokens would belong.853		/// * `data`: Explicit item creation data.854		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]855		#[transactional]856		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {857			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858			let budget = budget::Value::new(NESTING_BUDGET);859860			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))861		}862863		/// Completely allow or disallow transfers for a particular collection.864		///865		/// # Permissions866		///867		/// * Collection owner868		///869		/// # Arguments870		///871		/// * `collection_id`: ID of the collection.872		/// * `value`: New value of the flag, are transfers allowed?873		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]874		#[transactional]875		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {876			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);877			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;878			target_collection.check_is_internal()?;879			target_collection.check_is_owner(&sender)?;880881			// =========882883			target_collection.limits.transfers_enabled = Some(value);884			target_collection.save()885		}886887		/// Destroy an item.888		///889		/// # Permissions890		///891		/// * Collection owner892		/// * Collection admin893		/// * Current item owner894		///895		/// # Arguments896		///897		/// * `collection_id`: ID of the collection to which the item belongs.898		/// * `item_id`: ID of item to burn.899		/// * `value`: Number of pieces of the item to destroy.900		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.901		///     * Fungible Mode: The desired number of pieces to burn.902		///     * Re-Fungible Mode: The desired number of pieces to burn.903		#[weight = T::CommonWeightInfo::burn_item()]904		#[transactional]905		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {906			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);907908			let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;909			if value == 1 {910				<NftTransferBasket<T>>::remove(collection_id, item_id);911				<NftApproveBasket<T>>::remove(collection_id, item_id);912			}913			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?914			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());915			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));916			Ok(post_info)917		}918919		/// Destroy a token on behalf of the owner as a non-owner account.920		///921		/// See also: [`approve`][`Pallet::approve`].922		///923		/// After this method executes, one approval is removed from the total so that924		/// the approved address will not be able to transfer this item again from this owner.925		///926		/// # Permissions927		///928		/// * Collection owner929		/// * Collection admin930		/// * Current token owner931		/// * Address approved by current item owner932		///933		/// # Arguments934		///935		/// * `from`: The owner of the burning item.936		/// * `collection_id`: ID of the collection to which the item belongs.937		/// * `item_id`: ID of item to burn.938		/// * `value`: Number of pieces to burn.939		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.940		///     * Fungible Mode: The desired number of pieces to burn.941		///     * Re-Fungible Mode: The desired number of pieces to burn.942		#[weight = T::CommonWeightInfo::burn_from()]943		#[transactional]944		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {945			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946			let budget = budget::Value::new(NESTING_BUDGET);947948			dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))949		}950951		/// Change ownership of the token.952		///953		/// # Permissions954		///955		/// * Collection owner956		/// * Collection admin957		/// * Current token owner958		///959		/// # Arguments960		///961		/// * `recipient`: Address of token recipient.962		/// * `collection_id`: ID of the collection the item belongs to.963		/// * `item_id`: ID of the item.964		///     * Non-Fungible Mode: Required.965		///     * Fungible Mode: Ignored.966		///     * Re-Fungible Mode: Required.967		///968		/// * `value`: Amount to transfer.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 transfer.971		///     * Re-Fungible Mode: The desired number of pieces to transfer.972		#[weight = T::CommonWeightInfo::transfer()]973		#[transactional]974		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {975			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);976			let budget = budget::Value::new(NESTING_BUDGET);977978			dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))979		}980981		/// Allow a non-permissioned address to transfer or burn an item.982		///983		/// # Permissions984		///985		/// * Collection owner986		/// * Collection admin987		/// * Current item owner988		///989		/// # Arguments990		///991		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.992		/// * `collection_id`: ID of the collection the item belongs to.993		/// * `item_id`: ID of the item transactions on which are now approved.994		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).995		/// Set to 0 to revoke the approval.996		#[weight = T::CommonWeightInfo::approve()]997		#[transactional]998		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {999			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10001001			dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))1002		}10031004		/// Change ownership of an item on behalf of the owner as a non-owner account.1005		///1006		/// See the [`approve`][`Pallet::approve`] method for additional information.1007		///1008		/// After this method executes, one approval is removed from the total so that1009		/// the approved address will not be able to transfer this item again from this owner.1010		///1011		/// # Permissions1012		///1013		/// * Collection owner1014		/// * Collection admin1015		/// * Current item owner1016		/// * Address approved by current item owner1017		///1018		/// # Arguments1019		///1020		/// * `from`: Address that currently owns the token.1021		/// * `recipient`: Address of the new token-owner-to-be.1022		/// * `collection_id`: ID of the collection the item.1023		/// * `item_id`: ID of the item to be transferred.1024		/// * `value`: Amount to transfer.1025		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1026		///     * Fungible Mode: The desired number of pieces to transfer.1027		///     * Re-Fungible Mode: The desired number of pieces to transfer.1028		#[weight = T::CommonWeightInfo::transfer_from()]1029		#[transactional]1030		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1031			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1032			let budget = budget::Value::new(NESTING_BUDGET);10331034			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1035		}10361037		/// Set specific limits of a collection. Empty, or None fields mean chain default.1038		///1039		/// # Permissions1040		///1041		/// * Collection owner1042		/// * Collection admin1043		///1044		/// # Arguments1045		///1046		/// * `collection_id`: ID of the modified collection.1047		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1048		/// will not overwrite the old ones.1049		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1050		#[transactional]1051		pub fn set_collection_limits(1052			origin,1053			collection_id: CollectionId,1054			new_limit: CollectionLimits,1055		) -> DispatchResult {1056			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1057			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1058			target_collection.check_is_internal()?;1059			target_collection.check_is_owner_or_admin(&sender)?;1060			let old_limit = &target_collection.limits;10611062			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10631064			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1065				collection_id1066			));10671068			target_collection.save()1069		}10701071		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1072		///1073		/// # Permissions1074		///1075		/// * Collection owner1076		/// * Collection admin1077		///1078		/// # Arguments1079		///1080		/// * `collection_id`: ID of the modified collection.1081		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1082		/// will not overwrite the old ones.1083		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1084		#[transactional]1085		pub fn set_collection_permissions(1086			origin,1087			collection_id: CollectionId,1088			new_permission: CollectionPermissions,1089		) -> DispatchResult {1090			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1091			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1092			target_collection.check_is_internal()?;1093			target_collection.check_is_owner_or_admin(&sender)?;1094			let old_limit = &target_collection.permissions;10951096			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10971098			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1099				collection_id1100			));11011102			target_collection.save()1103		}11041105		/// Re-partition a refungible token, while owning all of its parts/pieces.1106		///1107		/// # Permissions1108		///1109		/// * Token owner (must own every part)1110		///1111		/// # Arguments1112		///1113		/// * `collection_id`: ID of the collection the RFT belongs to.1114		/// * `token_id`: ID of the RFT.1115		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1116		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]1117		#[transactional]1118		pub fn repartition(1119			origin,1120			collection_id: CollectionId,1121			token_id: TokenId,1122			amount: u128,1123		) -> DispatchResultWithPostInfo {1124			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1125			dispatch_tx::<T, _>(collection_id, |d| {1126				if let Some(refungible_extensions) = d.refungible_extensions() {1127					refungible_extensions.repartition(&sender, token_id, amount)1128				} else {1129					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1130				}1131			})1132		}1133	}1134}
after · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # 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::{77	decl_module, decl_storage, decl_error, decl_event,78	dispatch::DispatchResult,79	ensure, fail,80	weights::{Weight},81	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},82	BoundedVec,83};84use scale_info::TypeInfo;85use frame_system::{self as system, ensure_signed};86use sp_runtime::{sp_std::prelude::Vec};87use up_data_structs::{88	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,89	CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,90	SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,91	PropertyKeyPermission,92};93use pallet_evm::account::CrossAccountId;94use pallet_common::{95	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,96	dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,97};98pub mod eth;99100#[cfg(feature = "runtime-benchmarks")]101mod benchmarking;102pub mod weights;103use weights::WeightInfo;104105/// Maximum number of levels of depth in the token nesting tree.106pub const NESTING_BUDGET: u32 = 5;107108decl_error! {109	/// Errors for the common Unique transactions.110	pub enum Error for Module<T: Config> {111		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].112		CollectionDecimalPointLimitExceeded,113		/// This address is not set as sponsor, use setCollectionSponsor first.114		ConfirmUnsetSponsorFail,115		/// Length of items properties must be greater than 0.116		EmptyArgument,117		/// Repertition is only supported by refungible collection.118		RepartitionCalledOnNonRefungibleCollection,119	}120}121122/// Configuration trait of this pallet.123pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {124	/// Overarching event type.125	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;126127	/// Weight information for extrinsics in this pallet.128	type WeightInfo: WeightInfo;129130	/// Weight information for common pallet operations.131	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;132133	/// Weight info information for extra refungible pallet operations.134	type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;135}136137decl_event! {138	pub enum Event<T>139	where140		<T as frame_system::Config>::AccountId,141		<T as pallet_evm::account::Config>::CrossAccountId,142	{143		/// Collection sponsor was removed144		///145		/// # Arguments146		/// * collection_id: ID of the affected collection.147		CollectionSponsorRemoved(CollectionId),148149		/// Collection admin was added150		///151		/// # Arguments152		/// * collection_id: ID of the affected collection.153		/// * admin: Admin address.154		CollectionAdminAdded(CollectionId, CrossAccountId),155156		/// Collection owned was changed157		///158		/// # Arguments159		/// * collection_id: ID of the affected collection.160		/// * owner: New owner address.161		CollectionOwnedChanged(CollectionId, AccountId),162163		/// Collection sponsor was set164		///165		/// # Arguments166		/// * collection_id: ID of the affected collection.167		/// * owner: New sponsor address.168		CollectionSponsorSet(CollectionId, AccountId),169170		/// New sponsor was confirm171		///172		/// # Arguments173		/// * collection_id: ID of the affected collection.174		/// * sponsor: New sponsor address.175		SponsorshipConfirmed(CollectionId, AccountId),176177		/// Collection admin was removed178		///179		/// # Arguments180		/// * collection_id: ID of the affected collection.181		/// * admin: Removed admin address.182		CollectionAdminRemoved(CollectionId, CrossAccountId),183184		/// Address was removed from the allow list185		///186		/// # Arguments187		/// * collection_id: ID of the affected collection.188		/// * user: Address of the removed account.189		AllowListAddressRemoved(CollectionId, CrossAccountId),190191		/// Address was added to the allow list192		///193		/// # Arguments194		/// * collection_id: ID of the affected collection.195		/// * user: Address of the added account.196		AllowListAddressAdded(CollectionId, CrossAccountId),197198		/// Collection limits were set199		///200		/// # Arguments201		/// * collection_id: ID of the affected collection.202		CollectionLimitSet(CollectionId),203204		/// Collection permissions were set205		///206		/// # Arguments207		/// * collection_id: ID of the affected collection.208		CollectionPermissionSet(CollectionId),209	}210}211212type SelfWeightOf<T> = <T as Config>::WeightInfo;213214// # Used definitions215//216// ## User control levels217//218// chain-controlled - key is uncontrolled by user219//                    i.e autoincrementing index220//                    can use non-cryptographic hash221// real - key is controlled by user222//        but it is hard to generate enough colliding values, i.e owner of signed txs223//        can use non-cryptographic hash224// controlled - key is completly controlled by users225//              i.e maps with mutable keys226//              should use cryptographic hash227//228// ## User control level downgrade reasons229//230// ?1 - chain-controlled -> controlled231//      collections/tokens can be destroyed, resulting in massive holes232// ?2 - chain-controlled -> controlled233//      same as ?1, but can be only added, resulting in easier exploitation234// ?3 - real -> controlled235//      no confirmation required, so addresses can be easily generated236decl_storage! {237	trait Store for Module<T: Config> as Unique {238239		//#region Private members240		/// Used for migrations241		ChainVersion: u64;242		//#endregion243244		//#region Tokens transfer sponosoring rate limit baskets245		/// (Collection id (controlled?2), who created (real))246		/// TODO: Off chain worker should remove from this map when collection gets removed247		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;248		/// Collection id (controlled?2), token id (controlled?2)249		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250		/// Collection id (controlled?2), owning user (real)251		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;252		/// Collection id (controlled?2), token id (controlled?2)253		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254		//#endregion255256		/// Variable metadata sponsoring257		/// Collection id (controlled?2), token id (controlled?2)258		#[deprecated]259		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;260		/// Last sponsoring of token property setting // todo:doc rephrase this and the following261		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;262263		/// Last sponsoring of NFT approval in a collection264		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;265		/// Last sponsoring of fungible tokens approval in a collection266		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;267		/// Last sponsoring of RFT approval in a collection268		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;269	}270}271272decl_module! {273	/// Type alias to Pallet, to be used by construct_runtime.274	pub struct Module<T: Config> for enum Call275	where276		origin: T::Origin277	{278		type Error = Error<T>;279280		fn deposit_event() = default;281282		fn on_initialize(_now: T::BlockNumber) -> Weight {283			0284		}285286		fn on_runtime_upgrade() -> Weight {287			0288		}289290		/// Create a collection of tokens.291		///292		/// Each Token may have multiple properties encoded as an array of bytes293		/// of certain length. The initial owner of the collection is set294		/// to the address that signed the transaction and can be changed later.295		///296		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.297		///298		/// # Permissions299		///300		/// * Anyone - becomes the owner of the new collection.301		///302		/// # Arguments303		///304		/// * `collection_name`: Wide-character string with collection name305		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).306		/// * `collection_description`: Wide-character string with collection description307		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).308		/// * `token_prefix`: Byte string containing the token prefix to mark a collection309		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).310		/// * `mode`: Type of items stored in the collection and type dependent data.311		// returns collection ID312		#[weight = <SelfWeightOf<T>>::create_collection()]313		#[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]314		pub fn create_collection(315			origin,316			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,317			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,318			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,319			mode: CollectionMode320		) -> DispatchResult {321			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {322				name: collection_name,323				description: collection_description,324				token_prefix,325				mode,326				..Default::default()327			};328			Self::create_collection_ex(origin, data)329		}330331		/// Create a collection with explicit parameters.332		///333		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.334		///335		/// # Permissions336		///337		/// * Anyone - becomes the owner of the new collection.338		///339		/// # Arguments340		///341		/// * `data`: Explicit data of a collection used for its creation.342		#[weight = <SelfWeightOf<T>>::create_collection()]343		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {344			let sender = ensure_signed(origin)?;345346			// =========347348			let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;349350			Ok(())351		}352353		/// Destroy a collection if no tokens exist within.354		///355		/// # Permissions356		///357		/// * Collection owner358		///359		/// # Arguments360		///361		/// * `collection_id`: Collection to destroy.362		#[weight = <SelfWeightOf<T>>::destroy_collection()]363		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365			let collection = <CollectionHandle<T>>::try_get(collection_id)?;366			collection.check_is_internal()?;367368			// =========369370			T::CollectionDispatch::destroy(sender, collection)?;371372			// TODO: basket cleanup should be moved elsewhere373			// Maybe runtime dispatch.rs should perform it?374375			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);376			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);377			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);378379			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383			Ok(())384		}385386		/// Add an address to allow list.387		///388		/// # Permissions389		///390		/// * Collection owner391		/// * Collection admin392		///393		/// # Arguments394		///395		/// * `collection_id`: ID of the modified collection.396		/// * `address`: ID of the address to be added to the allowlist.397		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]398		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{399400			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);401			let collection = <CollectionHandle<T>>::try_get(collection_id)?;402			collection.check_is_internal()?;403404			<PalletCommon<T>>::toggle_allowlist(405				&collection,406				&sender,407				&address,408				true,409			)?;410411			Self::deposit_event(Event::<T>::AllowListAddressAdded(412				collection_id,413				address414			));415416			Ok(())417		}418419		/// Remove an address from allow list.420		///421		/// # Permissions422		///423		/// * Collection owner424		/// * Collection admin425		///426		/// # Arguments427		///428		/// * `collection_id`: ID of the modified collection.429		/// * `address`: ID of the address to be removed from the allowlist.430		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]431		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{432433			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);434			let collection = <CollectionHandle<T>>::try_get(collection_id)?;435			collection.check_is_internal()?;436437			<PalletCommon<T>>::toggle_allowlist(438				&collection,439				&sender,440				&address,441				false,442			)?;443444			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(445				collection_id,446				address447			));448449			Ok(())450		}451452		/// Change the owner of the collection.453		///454		/// # Permissions455		///456		/// * Collection owner457		///458		/// # Arguments459		///460		/// * `collection_id`: ID of the modified collection.461		/// * `new_owner`: ID of the account that will become the owner.462		#[weight = <SelfWeightOf<T>>::change_collection_owner()]463		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {464465			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);466467			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;468			target_collection.check_is_internal()?;469			target_collection.check_is_owner(&sender)?;470471			target_collection.owner = new_owner.clone();472			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(473				collection_id,474				new_owner475			));476477			target_collection.save()478		}479480		/// Add an admin to a collection.481		///482		/// NFT Collection can be controlled by multiple admin addresses483		/// (some which can also be servers, for example). Admins can issue484		/// and burn NFTs, as well as add and remove other admins,485		/// but cannot change NFT or Collection ownership.486		///487		/// # Permissions488		///489		/// * Collection owner490		/// * Collection admin491		///492		/// # Arguments493		///494		/// * `collection_id`: ID of the Collection to add an admin for.495		/// * `new_admin`: Address of new admin to add.496		#[weight = <SelfWeightOf<T>>::add_collection_admin()]497		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {498			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499			let collection = <CollectionHandle<T>>::try_get(collection_id)?;500			collection.check_is_internal()?;501502			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(503				collection_id,504				new_admin.clone()505			));506507			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)508		}509510		/// Remove admin of a collection.511		///512		/// An admin address can remove itself. List of admins may become empty,513		/// in which case only Collection Owner will be able to add an Admin.514		///515		/// # Permissions516		///517		/// * Collection owner518		/// * Collection admin519		///520		/// # Arguments521		///522		/// * `collection_id`: ID of the collection to remove the admin for.523		/// * `account_id`: Address of the admin to remove.524		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]525		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {526			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);527			let collection = <CollectionHandle<T>>::try_get(collection_id)?;528			collection.check_is_internal()?;529530			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(531				collection_id,532				account_id.clone()533			));534535			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)536		}537538		/// Set (invite) a new collection sponsor.539		///540		/// If successful, confirmation from the sponsor-to-be will be pending.541		///542		/// # Permissions543		///544		/// * Collection owner545		/// * Collection admin546		///547		/// # Arguments548		///549		/// * `collection_id`: ID of the modified collection.550		/// * `new_sponsor`: ID of the account of the sponsor-to-be.551		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]552		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {553			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);554555			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;556			target_collection.check_is_owner_or_admin(&sender)?;557			target_collection.check_is_internal()?;558559			target_collection.set_sponsor(new_sponsor.clone())?;560561			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(562				collection_id,563				new_sponsor564			));565566			target_collection.save()567		}568569		/// Confirm own sponsorship of a collection, becoming the sponsor.570		///571		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].572		/// Sponsor can pay the fees of a transaction instead of the sender,573		/// but only within specified limits.574		///575		/// # Permissions576		///577		/// * Sponsor-to-be578		///579		/// # Arguments580		///581		/// * `collection_id`: ID of the collection with the pending sponsor.582		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]583		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {584			let sender = ensure_signed(origin)?;585586			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;587			target_collection.check_is_internal()?;588			ensure!(589				target_collection.confirm_sponsorship(&sender)?,590				Error::<T>::ConfirmUnsetSponsorFail591			);592593			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(594				collection_id,595				sender596			));597598			target_collection.save()599		}600601		/// Remove a collection's a sponsor, making everyone pay for their own transactions.602		///603		/// # Permissions604		///605		/// * Collection owner606		///607		/// # Arguments608		///609		/// * `collection_id`: ID of the collection with the sponsor to remove.610		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]611		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {612			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613614			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;615			target_collection.check_is_internal()?;616			target_collection.check_is_owner(&sender)?;617618			target_collection.sponsorship = SponsorshipState::Disabled;619620			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(621				collection_id622			));623			target_collection.save()624		}625626		/// Mint an item within a collection.627		///628		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].629		///630		/// # Permissions631		///632		/// * Collection owner633		/// * Collection admin634		/// * Anyone if635		///     * Allow List is enabled, and636		///     * Address is added to allow list, and637		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])638		///639		/// # Arguments640		///641		/// * `collection_id`: ID of the collection to which an item would belong.642		/// * `owner`: Address of the initial owner of the item.643		/// * `data`: Token data describing the item to store on chain.644		#[weight = T::CommonWeightInfo::create_item()]645		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {646			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);647			let budget = budget::Value::new(NESTING_BUDGET);648649			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))650		}651652		/// Create multiple items within a collection.653		///654		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].655		///656		/// # Permissions657		///658		/// * Collection owner659		/// * Collection admin660		/// * Anyone if661		///     * Allow List is enabled, and662		///     * Address is added to the allow list, and663		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])664		///665		/// # Arguments666		///667		/// * `collection_id`: ID of the collection to which the tokens would belong.668		/// * `owner`: Address of the initial owner of the tokens.669		/// * `items_data`: Vector of data describing each item to be created.670		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]671		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {672			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);673			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);674			let budget = budget::Value::new(NESTING_BUDGET);675676			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))677		}678679		/// Add or change collection properties.680		///681		/// # Permissions682		///683		/// * Collection owner684		/// * Collection admin685		///686		/// # Arguments687		///688		/// * `collection_id`: ID of the modified collection.689		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.690		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.691		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]692		pub fn set_collection_properties(693			origin,694			collection_id: CollectionId,695			properties: Vec<Property>696		) -> DispatchResultWithPostInfo {697			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);698699			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);700701			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))702		}703704		/// Delete specified collection properties.705		///706		/// # Permissions707		///708		/// * Collection Owner709		/// * Collection Admin710		///711		/// # Arguments712		///713		/// * `collection_id`: ID of the modified collection.714		/// * `property_keys`: Vector of keys of the properties to be deleted.715		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.716		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]717		pub fn delete_collection_properties(718			origin,719			collection_id: CollectionId,720			property_keys: Vec<PropertyKey>,721		) -> DispatchResultWithPostInfo {722			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);723724			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);725726			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))727		}728729		/// Add or change token properties according to collection's permissions.730		/// Currently properties only work with NFTs.731		///732		/// # Permissions733		///734		/// * Depends on collection's token property permissions and specified property mutability:735		/// 	* Collection owner736		/// 	* Collection admin737		/// 	* Token owner738		///739		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].740		///741		/// # Arguments742		///743		/// * `collection_id: ID of the collection to which the token belongs.744		/// * `token_id`: ID of the modified token.745		/// * `properties`: Vector of key-value pairs stored as the token's metadata.746		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.747		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]748		pub fn set_token_properties(749			origin,750			collection_id: CollectionId,751			token_id: TokenId,752			properties: Vec<Property>753		) -> DispatchResultWithPostInfo {754			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);755756			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757			let budget = budget::Value::new(NESTING_BUDGET);758759			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))760		}761762		/// Delete specified token properties. Currently properties only work with NFTs.763		///764		/// # Permissions765		///766		/// * Depends on collection's token property permissions and specified property mutability:767		/// 	* Collection owner768		/// 	* Collection admin769		/// 	* Token owner770		///771		/// # Arguments772		///773		/// * `collection_id`: ID of the collection to which the token belongs.774		/// * `token_id`: ID of the modified token.775		/// * `property_keys`: Vector of keys of the properties to be deleted.776		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.777		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]778		pub fn delete_token_properties(779			origin,780			collection_id: CollectionId,781			token_id: TokenId,782			property_keys: Vec<PropertyKey>783		) -> DispatchResultWithPostInfo {784			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);785786			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);787			let budget = budget::Value::new(NESTING_BUDGET);788789			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))790		}791792		/// Add or change token property permissions of a collection.793		///794		/// Without a permission for a particular key, a property with that key795		/// cannot be created in a token.796		///797		/// # Permissions798		///799		/// * Collection owner800		/// * Collection admin801		///802		/// # Arguments803		///804		/// * `collection_id`: ID of the modified collection.805		/// * `property_permissions`: Vector of permissions for property keys.806		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.807		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]808		pub fn set_token_property_permissions(809			origin,810			collection_id: CollectionId,811			property_permissions: Vec<PropertyKeyPermission>,812		) -> DispatchResultWithPostInfo {813			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);814815			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);816817			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))818		}819820		/// Create multiple items within a collection with explicitly specified initial parameters.821		///822		/// # Permissions823		///824		/// * Collection owner825		/// * Collection admin826		/// * Anyone if827		///     * Allow List is enabled, and828		///     * Address is added to allow list, and829		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])830		///831		/// # Arguments832		///833		/// * `collection_id`: ID of the collection to which the tokens would belong.834		/// * `data`: Explicit item creation data.835		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]836		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {837			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);838			let budget = budget::Value::new(NESTING_BUDGET);839840			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))841		}842843		/// Completely allow or disallow transfers for a particular collection.844		///845		/// # Permissions846		///847		/// * Collection owner848		///849		/// # Arguments850		///851		/// * `collection_id`: ID of the collection.852		/// * `value`: New value of the flag, are transfers allowed?853		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]854		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {855			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;857			target_collection.check_is_internal()?;858			target_collection.check_is_owner(&sender)?;859860			// =========861862			target_collection.limits.transfers_enabled = Some(value);863			target_collection.save()864		}865866		/// Destroy an item.867		///868		/// # Permissions869		///870		/// * Collection owner871		/// * Collection admin872		/// * Current item owner873		///874		/// # Arguments875		///876		/// * `collection_id`: ID of the collection to which the item belongs.877		/// * `item_id`: ID of item to burn.878		/// * `value`: Number of pieces of the item to destroy.879		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.880		///     * Fungible Mode: The desired number of pieces to burn.881		///     * Re-Fungible Mode: The desired number of pieces to burn.882		#[weight = T::CommonWeightInfo::burn_item()]883		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {884			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);885886			let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;887			if value == 1 {888				<NftTransferBasket<T>>::remove(collection_id, item_id);889				<NftApproveBasket<T>>::remove(collection_id, item_id);890			}891			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?892			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());893			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));894			Ok(post_info)895		}896897		/// Destroy a token on behalf of the owner as a non-owner account.898		///899		/// See also: [`approve`][`Pallet::approve`].900		///901		/// After this method executes, one approval is removed from the total so that902		/// the approved address will not be able to transfer this item again from this owner.903		///904		/// # Permissions905		///906		/// * Collection owner907		/// * Collection admin908		/// * Current token owner909		/// * Address approved by current item owner910		///911		/// # Arguments912		///913		/// * `from`: The owner of the burning item.914		/// * `collection_id`: ID of the collection to which the item belongs.915		/// * `item_id`: ID of item to burn.916		/// * `value`: Number of pieces to burn.917		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.918		///     * Fungible Mode: The desired number of pieces to burn.919		///     * Re-Fungible Mode: The desired number of pieces to burn.920		#[weight = T::CommonWeightInfo::burn_from()]921		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {922			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);923			let budget = budget::Value::new(NESTING_BUDGET);924925			dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))926		}927928		/// Change ownership of the token.929		///930		/// # Permissions931		///932		/// * Collection owner933		/// * Collection admin934		/// * Current token owner935		///936		/// # Arguments937		///938		/// * `recipient`: Address of token recipient.939		/// * `collection_id`: ID of the collection the item belongs to.940		/// * `item_id`: ID of the item.941		///     * Non-Fungible Mode: Required.942		///     * Fungible Mode: Ignored.943		///     * Re-Fungible Mode: Required.944		///945		/// * `value`: Amount to transfer.946		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.947		///     * Fungible Mode: The desired number of pieces to transfer.948		///     * Re-Fungible Mode: The desired number of pieces to transfer.949		#[weight = T::CommonWeightInfo::transfer()]950		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {951			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952			let budget = budget::Value::new(NESTING_BUDGET);953954			dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))955		}956957		/// Allow a non-permissioned address to transfer or burn an item.958		///959		/// # Permissions960		///961		/// * Collection owner962		/// * Collection admin963		/// * Current item owner964		///965		/// # Arguments966		///967		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.968		/// * `collection_id`: ID of the collection the item belongs to.969		/// * `item_id`: ID of the item transactions on which are now approved.970		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).971		/// Set to 0 to revoke the approval.972		#[weight = T::CommonWeightInfo::approve()]973		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {974			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);975976			dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))977		}978979		/// Change ownership of an item on behalf of the owner as a non-owner account.980		///981		/// See the [`approve`][`Pallet::approve`] method for additional information.982		///983		/// After this method executes, one approval is removed from the total so that984		/// the approved address will not be able to transfer this item again from this owner.985		///986		/// # Permissions987		///988		/// * Collection owner989		/// * Collection admin990		/// * Current item owner991		/// * Address approved by current item owner992		///993		/// # Arguments994		///995		/// * `from`: Address that currently owns the token.996		/// * `recipient`: Address of the new token-owner-to-be.997		/// * `collection_id`: ID of the collection the item.998		/// * `item_id`: ID of the item to be transferred.999		/// * `value`: Amount to transfer.1000		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1001		///     * Fungible Mode: The desired number of pieces to transfer.1002		///     * Re-Fungible Mode: The desired number of pieces to transfer.1003		#[weight = T::CommonWeightInfo::transfer_from()]1004		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1005			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006			let budget = budget::Value::new(NESTING_BUDGET);10071008			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1009		}10101011		/// Set specific limits of a collection. Empty, or None fields mean chain default.1012		///1013		/// # Permissions1014		///1015		/// * Collection owner1016		/// * Collection admin1017		///1018		/// # Arguments1019		///1020		/// * `collection_id`: ID of the modified collection.1021		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1022		/// will not overwrite the old ones.1023		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1024		pub fn set_collection_limits(1025			origin,1026			collection_id: CollectionId,1027			new_limit: CollectionLimits,1028		) -> DispatchResult {1029			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1031			target_collection.check_is_internal()?;1032			target_collection.check_is_owner_or_admin(&sender)?;1033			let old_limit = &target_collection.limits;10341035			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10361037			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1038				collection_id1039			));10401041			target_collection.save()1042		}10431044		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1045		///1046		/// # Permissions1047		///1048		/// * Collection owner1049		/// * Collection admin1050		///1051		/// # Arguments1052		///1053		/// * `collection_id`: ID of the modified collection.1054		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1055		/// will not overwrite the old ones.1056		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1057		pub fn set_collection_permissions(1058			origin,1059			collection_id: CollectionId,1060			new_permission: CollectionPermissions,1061		) -> DispatchResult {1062			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1063			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1064			target_collection.check_is_internal()?;1065			target_collection.check_is_owner_or_admin(&sender)?;1066			let old_limit = &target_collection.permissions;10671068			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10691070			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1071				collection_id1072			));10731074			target_collection.save()1075		}10761077		/// Re-partition a refungible token, while owning all of its parts/pieces.1078		///1079		/// # Permissions1080		///1081		/// * Token owner (must own every part)1082		///1083		/// # Arguments1084		///1085		/// * `collection_id`: ID of the collection the RFT belongs to.1086		/// * `token_id`: ID of the RFT.1087		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1088		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]1089		pub fn repartition(1090			origin,1091			collection_id: CollectionId,1092			token_id: TokenId,1093			amount: u128,1094		) -> DispatchResultWithPostInfo {1095			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1096			dispatch_tx::<T, _>(collection_id, |d| {1097				if let Some(refungible_extensions) = d.refungible_extensions() {1098					refungible_extensions.repartition(&sender, token_id, amount)1099				} else {1100					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1101				}1102			})1103		}1104	}1105}