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

difftreelog

source

pallets/common/src/lib.rs55.2 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Common pallet18//!19//! The Common pallet provides functionality for handling collections.20//!21//! ## Overview22//!23//! The Common pallet provides an interface for common collection operations for different collection types24//! (see [CommonCollectionOperations]), as well as a generic dispatcher for these, see [dispatch] module.25//! It also provides this functionality to EVM, see [erc] and [eth] modules.26//!27//! The Common pallet provides functions for:28//!29//! - Setting and approving collection sponsor.30//! - Get\set\delete allow list.31//! - Get\set\delete collection properties.32//! - Get\set\delete collection property permissions.33//! - Get\set\delete token property permissions.34//! - Get\set\delete collection administrators.35//! - Checking access permissions.36//!37//! ### Terminology38//! **Collection sponsor** - For the collection, you can set a sponsor, at whose expense it will39//! be possible to mint tokens.40//!41//! **Allow list** - List of users who have the right to minting tokens.42//!43//! **Collection properties** - Collection properties are simply key-value stores where various44//! metadata can be placed.45//!46//! **Permissions on token properties** - For each property in the token can be set permission47//! to change, see [`PropertyPermission`].48//!49//! **Collection administrator** - For a collection, you can set administrators who have the right50//! to most actions on the collection.5152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63	ensure,64	traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65	dispatch::Pays,66	transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70	COLLECTION_NUMBER_LIMIT,71	Collection,72	RpcCollection,73	CollectionFlags,74	RpcCollectionFlags,75	CollectionId,76	CreateItemData,77	MAX_TOKEN_PREFIX_LENGTH,78	COLLECTION_ADMINS_LIMIT,79	TokenId,80	TokenChild,81	CollectionStats,82	MAX_TOKEN_OWNERSHIP,83	CollectionMode,84	NFT_SPONSOR_TRANSFER_TIMEOUT,85	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87	MAX_SPONSOR_TIMEOUT,88	CUSTOM_DATA_LIMIT,89	CollectionLimits,90	CreateCollectionData,91	SponsorshipState,92	CreateItemExData,93	SponsoringRateLimit,94	budget::Budget,95	PhantomType,96	Property,97	Properties,98	PropertiesPermissionMap,99	PropertyKey,100	PropertyValue,101	PropertyPermission,102	PropertiesError,103	PropertyKeyPermission,104	TokenData,105	TrySetProperty,106	PropertyScope,107	// RMRK108	RmrkCollectionInfo,109	RmrkInstanceInfo,110	RmrkResourceInfo,111	RmrkPropertyInfo,112	RmrkBaseInfo,113	RmrkPartType,114	RmrkBoundedTheme,115	RmrkNftChild,116	CollectionPermissions,117};118119pub use pallet::*;120use sp_core::H160;121use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};122#[cfg(feature = "runtime-benchmarks")]123pub mod benchmarking;124pub mod dispatch;125pub mod erc;126pub mod eth;127pub mod weights;128129/// Weight info.130pub type SelfWeightOf<T> = <T as Config>::WeightInfo;131132/// Collection handle contains information about collection data and id.133/// Also provides functionality to count consumed gas.134///135/// CollectionHandle is used as a generic wrapper for collections of all types.136/// It allows to perform common operations and queries on any collection type,137/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].138#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]139pub struct CollectionHandle<T: Config> {140	/// Collection id141	pub id: CollectionId,142	collection: Collection<T::AccountId>,143	/// Substrate recorder for counting consumed gas144	pub recorder: SubstrateRecorder<T>,145}146147impl<T: Config> WithRecorder<T> for CollectionHandle<T> {148	fn recorder(&self) -> &SubstrateRecorder<T> {149		&self.recorder150	}151	fn into_recorder(self) -> SubstrateRecorder<T> {152		self.recorder153	}154}155156impl<T: Config> CollectionHandle<T> {157	/// Same as [CollectionHandle::new] but with an explicit gas limit.158	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {159		<CollectionById<T>>::get(id).map(|collection| Self {160			id,161			collection,162			recorder: SubstrateRecorder::new(gas_limit),163		})164	}165166	/// Same as [CollectionHandle::new] but with an existed [`SubstrateRecorder`].167	pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {168		<CollectionById<T>>::get(id).map(|collection| Self {169			id,170			collection,171			recorder,172		})173	}174175	/// Retrives collection data from storage and creates collection handle with default parameters.176	/// If collection not found return `None`177	pub fn new(id: CollectionId) -> Option<Self> {178		Self::new_with_gas_limit(id, u64::MAX)179	}180181	/// Same as [`CollectionHandle::new`] but if collection not found [CollectionNotFound](Error::CollectionNotFound) returned.182	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {183		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)184	}185186	/// Consume gas for reading.187	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {188		self.recorder189			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(190				<T as frame_system::Config>::DbWeight::get()191					.read192					.saturating_mul(reads),193			)))194	}195196	/// Consume gas for writing.197	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {198		self.recorder199			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(200				<T as frame_system::Config>::DbWeight::get()201					.write202					.saturating_mul(writes),203			)))204	}205206	/// Consume gas for reading and writing.207	pub fn consume_store_reads_and_writes(208		&self,209		reads: u64,210		writes: u64,211	) -> evm_coder::execution::Result<()> {212		let weight = <T as frame_system::Config>::DbWeight::get();213		let reads = weight.read.saturating_mul(reads);214		let writes = weight.read.saturating_mul(writes);215		self.recorder216			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(217				reads.saturating_add(writes),218			)))219	}220221	/// Save collection to storage.222	pub fn save(&self) -> DispatchResult {223		<CollectionById<T>>::insert(self.id, &self.collection);224		Ok(())225	}226227	/// Set collection sponsor.228	///229	/// Unique collections allows sponsoring for certain actions.230	/// This method allows you to set the sponsor of the collection.231	/// In order for sponsorship to become active, it must be confirmed through [`Self::confirm_sponsorship`].232	pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {233		self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);234		Ok(())235	}236237	/// Confirm sponsorship238	///239	/// In order for the sponsorship to become active, the user set as the sponsor must confirm their participation.240	/// Before confirming sponsorship, the user must be specified as the sponsor of the collection via [`Self::set_sponsor`].241	pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {242		if self.collection.sponsorship.pending_sponsor() != Some(sender) {243			return Ok(false);244		}245246		self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());247		Ok(true)248	}249250	/// Remove collection sponsor.251	pub fn remove_sponsor(&mut self) -> DispatchResult {252		self.collection.sponsorship = SponsorshipState::Disabled;253		Ok(())254	}255256	/// Checks that the collection was created with, and must be operated upon through **Unique API**.257	/// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error.258	pub fn check_is_internal(&self) -> DispatchResult {259		if self.flags.external {260			return Err(<Error<T>>::CollectionIsExternal)?;261		}262263		Ok(())264	}265266	/// Checks that the collection was created with, and must be operated upon through an **assimilated API**.267	/// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error.268	pub fn check_is_external(&self) -> DispatchResult {269		if !self.flags.external {270			return Err(<Error<T>>::CollectionIsInternal)?;271		}272273		Ok(())274	}275}276277impl<T: Config> Deref for CollectionHandle<T> {278	type Target = Collection<T::AccountId>;279280	fn deref(&self) -> &Self::Target {281		&self.collection282	}283}284285impl<T: Config> DerefMut for CollectionHandle<T> {286	fn deref_mut(&mut self) -> &mut Self::Target {287		&mut self.collection288	}289}290291impl<T: Config> CollectionHandle<T> {292	/// Checks if the `user` is the owner of the collection.293	pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {294		ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);295		Ok(())296	}297298	/// Returns **true** if the `user` is the owner or administrator of the collection.299	pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {300		*user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))301	}302303	/// Checks if the `user` is the owner or administrator of the collection.304	pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {305		ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);306		Ok(())307	}308309	/// Return **true** if `user` was not allowed to have tokens, and he can ignore such restrictions.310	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {311		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)312	}313314	/// Return **true** if `user` does not have enough token parts, and he can ignore such restrictions.315	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {316		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)317	}318319	/// Checks if the user is in the allow list. If not [Error::AddressNotInAllowlist] returns.320	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {321		ensure!(322			<Allowlist<T>>::get((self.id, user)),323			<Error<T>>::AddressNotInAllowlist324		);325		Ok(())326	}327328	/// Changes collection owner to another account329	/// #### Store read/writes330	/// 1 writes331	fn set_owner_internal(332		&mut self,333		caller: T::CrossAccountId,334		new_owner: T::CrossAccountId,335	) -> DispatchResult {336		self.check_is_owner(&caller)?;337		self.collection.owner = new_owner.as_sub().clone();338		self.save()339	}340}341342#[frame_support::pallet]343pub mod pallet {344	use super::*;345	use dispatch::CollectionDispatch;346	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};347	use frame_system::pallet_prelude::*;348	use frame_support::traits::Currency;349	use up_data_structs::{TokenId, mapping::TokenAddressMapping};350	use scale_info::TypeInfo;351	use weights::WeightInfo;352353	#[pallet::config]354	pub trait Config:355		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo356	{357		/// Weight information for functions of this pallet.358		type WeightInfo: WeightInfo;359360		/// Events compatible with [`frame_system::Config::Event`].361		type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;362363		/// Handler of accounts and payment.364		type Currency: Currency<Self::AccountId>;365366		/// Set price to create a collection.367		#[pallet::constant]368		type CollectionCreationPrice: Get<369			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,370		>;371372		/// Dispatcher of operations on collections.373		type CollectionDispatch: CollectionDispatch<Self>;374375		/// Account which holds the chain's treasury.376		type TreasuryAccountId: Get<Self::AccountId>;377378		/// Address under which the CollectionHelper contract would be available.379		#[pallet::constant]380		type ContractAddress: Get<H160>;381382		/// Mapper for token addresses to Ethereum addresses.383		type EvmTokenAddressMapping: TokenAddressMapping<H160>;384385		/// Mapper for token addresses to [`CrossAccountId`].386		type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;387	}388389	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);390391	#[pallet::pallet]392	#[pallet::storage_version(STORAGE_VERSION)]393	#[pallet::generate_store(pub(super) trait Store)]394	pub struct Pallet<T>(_);395396	#[pallet::extra_constants]397	impl<T: Config> Pallet<T> {398		/// Maximum admins per collection.399		pub fn collection_admins_limit() -> u32 {400			COLLECTION_ADMINS_LIMIT401		}402	}403404	#[pallet::event]405	#[pallet::generate_deposit(pub fn deposit_event)]406	pub enum Event<T: Config> {407		/// New collection was created408		CollectionCreated(409			/// Globally unique identifier of newly created collection.410			CollectionId,411			/// [`CollectionMode`] converted into _u8_.412			u8,413			/// Collection owner.414			T::AccountId,415		),416417		/// New collection was destroyed418		CollectionDestroyed(419			/// Globally unique identifier of collection.420			CollectionId,421		),422423		/// New item was created.424		ItemCreated(425			/// Id of the collection where item was created.426			CollectionId,427			/// Id of an item. Unique within the collection.428			TokenId,429			/// Owner of newly created item430			T::CrossAccountId,431			/// Always 1 for NFT432			u128,433		),434435		/// Collection item was burned.436		ItemDestroyed(437			/// Id of the collection where item was destroyed.438			CollectionId,439			/// Identifier of burned NFT.440			TokenId,441			/// Which user has destroyed its tokens.442			T::CrossAccountId,443			/// Amount of token pieces destroed. Always 1 for NFT.444			u128,445		),446447		/// Item was transferred448		Transfer(449			/// Id of collection to which item is belong.450			CollectionId,451			/// Id of an item.452			TokenId,453			/// Original owner of item.454			T::CrossAccountId,455			/// New owner of item.456			T::CrossAccountId,457			/// Amount of token pieces transfered. Always 1 for NFT.458			u128,459		),460461		/// Amount pieces of token owned by `sender` was approved for `spender`.462		Approved(463			/// Id of collection to which item is belong.464			CollectionId,465			/// Id of an item.466			TokenId,467			/// Original owner of item.468			T::CrossAccountId,469			/// Id for which the approval was granted.470			T::CrossAccountId,471			/// Amount of token pieces transfered. Always 1 for NFT.472			u128,473		),474475		/// The colletion property has been added or edited.476		CollectionPropertySet(477			/// Id of collection to which property has been set.478			CollectionId,479			/// The property that was set.480			PropertyKey,481		),482483		/// The property has been deleted.484		CollectionPropertyDeleted(485			/// Id of collection to which property has been deleted.486			CollectionId,487			/// The property that was deleted.488			PropertyKey,489		),490491		/// The token property has been added or edited.492		TokenPropertySet(493			/// Identifier of the collection whose token has the property set.494			CollectionId,495			/// The token for which the property was set.496			TokenId,497			/// The property that was set.498			PropertyKey,499		),500501		/// The token property has been deleted.502		TokenPropertyDeleted(503			/// Identifier of the collection whose token has the property deleted.504			CollectionId,505			/// The token for which the property was deleted.506			TokenId,507			/// The property that was deleted.508			PropertyKey,509		),510511		/// The token property permission of a collection has been set.512		PropertyPermissionSet(513			/// ID of collection to which property permission has been set.514			CollectionId,515			/// The property permission that was set.516			PropertyKey,517		),518	}519520	#[pallet::error]521	pub enum Error<T> {522		/// This collection does not exist.523		CollectionNotFound,524		/// Sender parameter and item owner must be equal.525		MustBeTokenOwner,526		/// No permission to perform action527		NoPermission,528		/// Destroying only empty collections is allowed529		CantDestroyNotEmptyCollection,530		/// Collection is not in mint mode.531		PublicMintingNotAllowed,532		/// Address is not in allow list.533		AddressNotInAllowlist,534535		/// Collection name can not be longer than 63 char.536		CollectionNameLimitExceeded,537		/// Collection description can not be longer than 255 char.538		CollectionDescriptionLimitExceeded,539		/// Token prefix can not be longer than 15 char.540		CollectionTokenPrefixLimitExceeded,541		/// Total collections bound exceeded.542		TotalCollectionsLimitExceeded,543		/// Exceeded max admin count544		CollectionAdminCountExceeded,545		/// Collection limit bounds per collection exceeded546		CollectionLimitBoundsExceeded,547		/// Tried to enable permissions which are only permitted to be disabled548		OwnerPermissionsCantBeReverted,549		/// Collection settings not allowing items transferring550		TransferNotAllowed,551		/// Account token limit exceeded per collection552		AccountTokenLimitExceeded,553		/// Collection token limit exceeded554		CollectionTokenLimitExceeded,555		/// Metadata flag frozen556		MetadataFlagFrozen,557558		/// Item does not exist559		TokenNotFound,560		/// Item is balance not enough561		TokenValueTooLow,562		/// Requested value is more than the approved563		ApprovedValueTooLow,564		/// Tried to approve more than owned565		CantApproveMoreThanOwned,566567		/// Can't transfer tokens to ethereum zero address568		AddressIsZero,569570		/// The operation is not supported571		UnsupportedOperation,572573		/// Insufficient funds to perform an action574		NotSufficientFounds,575576		/// User does not satisfy the nesting rule577		UserIsNotAllowedToNest,578		/// Only tokens from specific collections may nest tokens under this one579		SourceCollectionIsNotAllowedToNest,580581		/// Tried to store more data than allowed in collection field582		CollectionFieldSizeExceeded,583584		/// Tried to store more property data than allowed585		NoSpaceForProperty,586587		/// Tried to store more property keys than allowed588		PropertyLimitReached,589590		/// Property key is too long591		PropertyKeyIsTooLong,592593		/// Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed594		InvalidCharacterInPropertyKey,595596		/// Empty property keys are forbidden597		EmptyPropertyKey,598599		/// Tried to access an external collection with an internal API600		CollectionIsExternal,601602		/// Tried to access an internal collection with an external API603		CollectionIsInternal,604605		/// Transfer operation with zero amount606		ZeroTransferNotAllowed,607	}608609	/// Storage of the count of created collections. Essentially contains the last collection ID.610	#[pallet::storage]611	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;612613	/// Storage of the count of deleted collections.614	#[pallet::storage]615	pub type DestroyedCollectionCount<T> =616		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;617618	/// Storage of collection info.619	#[pallet::storage]620	pub type CollectionById<T> = StorageMap<621		Hasher = Blake2_128Concat,622		Key = CollectionId,623		Value = Collection<<T as frame_system::Config>::AccountId>,624		QueryKind = OptionQuery,625	>;626627	/// Storage of collection properties.628	#[pallet::storage]629	#[pallet::getter(fn collection_properties)]630	pub type CollectionProperties<T> = StorageMap<631		Hasher = Blake2_128Concat,632		Key = CollectionId,633		Value = Properties,634		QueryKind = ValueQuery,635		OnEmpty = up_data_structs::CollectionProperties,636	>;637638	/// Storage of token property permissions of a collection.639	#[pallet::storage]640	#[pallet::getter(fn property_permissions)]641	pub type CollectionPropertyPermissions<T> = StorageMap<642		Hasher = Blake2_128Concat,643		Key = CollectionId,644		Value = PropertiesPermissionMap,645		QueryKind = ValueQuery,646	>;647648	/// Storage of the amount of collection admins.649	#[pallet::storage]650	pub type AdminAmount<T> = StorageMap<651		Hasher = Blake2_128Concat,652		Key = CollectionId,653		Value = u32,654		QueryKind = ValueQuery,655	>;656657	/// List of collection admins.658	#[pallet::storage]659	pub type IsAdmin<T: Config> = StorageNMap<660		Key = (661			Key<Blake2_128Concat, CollectionId>,662			Key<Blake2_128Concat, T::CrossAccountId>,663		),664		Value = bool,665		QueryKind = ValueQuery,666	>;667668	/// Allowlisted collection users.669	#[pallet::storage]670	pub type Allowlist<T: Config> = StorageNMap<671		Key = (672			Key<Blake2_128Concat, CollectionId>,673			Key<Blake2_128Concat, T::CrossAccountId>,674		),675		Value = bool,676		QueryKind = ValueQuery,677	>;678679	/// Not used by code, exists only to provide some types to metadata.680	#[pallet::storage]681	pub type DummyStorageValue<T: Config> = StorageValue<682		Value = (683			CollectionStats,684			CollectionId,685			TokenId,686			TokenChild,687			PhantomType<(688				TokenData<T::CrossAccountId>,689				RpcCollection<T::AccountId>,690				// RMRK691				RmrkCollectionInfo<T::AccountId>,692				RmrkInstanceInfo<T::AccountId>,693				RmrkResourceInfo,694				RmrkPropertyInfo,695				RmrkBaseInfo<T::AccountId>,696				RmrkPartType,697				RmrkBoundedTheme,698				RmrkNftChild,699			)>,700		),701		QueryKind = OptionQuery,702	>;703704	#[pallet::hooks]705	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {706		fn on_runtime_upgrade() -> Weight {707			StorageVersion::new(1).put::<Pallet<T>>();708709			Weight::zero()710		}711	}712}713714impl<T: Config> Pallet<T> {715	/// Enshure that receiver address is correct.716	///717	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens.718	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {719		ensure!(720			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,721			<Error<T>>::AddressIsZero722		);723		Ok(())724	}725726	/// Get a vector of collection admins.727	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {728		<IsAdmin<T>>::iter_prefix((collection,))729			.map(|(a, _)| a)730			.collect()731	}732733	/// Get a vector of users allowed to mint tokens.734	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {735		<Allowlist<T>>::iter_prefix((collection,))736			.map(|(a, _)| a)737			.collect()738	}739740	/// Is `user` allowed to mint token in `collection`.741	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {742		<Allowlist<T>>::get((collection, user))743	}744745	/// Get statistics of collections.746	pub fn collection_stats() -> CollectionStats {747		let created = <CreatedCollectionCount<T>>::get();748		let destroyed = <DestroyedCollectionCount<T>>::get();749		CollectionStats {750			created: created.0,751			destroyed: destroyed.0,752			alive: created.0 - destroyed.0,753		}754	}755756	/// Get the effective limits for the collection.757	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {758		let collection = <CollectionById<T>>::get(collection)?;759		let limits = collection.limits;760		let effective_limits = CollectionLimits {761			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),762			sponsored_data_size: Some(limits.sponsored_data_size()),763			sponsored_data_rate_limit: Some(764				limits765					.sponsored_data_rate_limit766					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),767			),768			token_limit: Some(limits.token_limit()),769			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(770				match collection.mode {771					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,772					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,773					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,774				},775			)),776			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),777			owner_can_transfer: Some(limits.owner_can_transfer()),778			owner_can_destroy: Some(limits.owner_can_destroy()),779			transfers_enabled: Some(limits.transfers_enabled()),780		};781782		Some(effective_limits)783	}784785	/// Returns information about the `collection` adapted for rpc.786	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {787		let Collection {788			name,789			description,790			owner,791			mode,792			token_prefix,793			sponsorship,794			limits,795			permissions,796			flags,797		} = <CollectionById<T>>::get(collection)?;798799		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)800			.into_iter()801			.map(|(key, permission)| PropertyKeyPermission { key, permission })802			.collect();803804		let properties = <CollectionProperties<T>>::get(collection)805			.into_iter()806			.map(|(key, value)| Property { key, value })807			.collect();808809		let permissions = CollectionPermissions {810			access: Some(permissions.access()),811			mint_mode: Some(permissions.mint_mode()),812			nesting: Some(permissions.nesting().clone()),813		};814815		Some(RpcCollection {816			name: name.into_inner(),817			description: description.into_inner(),818			owner,819			mode,820			token_prefix: token_prefix.into_inner(),821			sponsorship,822			limits,823			permissions,824			token_property_permissions,825			properties,826			read_only: flags.external,827828			flags: RpcCollectionFlags {829				foreign: flags.foreign,830				erc721metadata: flags.erc721metadata,831			},832		})833	}834}835836macro_rules! limit_default {837	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{838		$(839			if let Some($new) = $new.$field {840				let $old = $old.$field($($arg)?);841				let _ = $new;842				let _ = $old;843				$check844			} else {845				$new.$field = $old.$field846			}847		)*848	}};849}850macro_rules! limit_default_clone {851	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{852		$(853			if let Some($new) = $new.$field.clone() {854				let $old = $old.$field($($arg)?);855				let _ = $new;856				let _ = $old;857				$check858			} else {859				$new.$field = $old.$field.clone()860			}861		)*862	}};863}864865impl<T: Config> Pallet<T> {866	/// Create new collection.867	///868	/// * `owner` - The owner of the collection.869	/// * `data` - Description of the created collection.870	/// * `flags` - Extra flags to store.871	pub fn init_collection(872		owner: T::CrossAccountId,873		payer: T::CrossAccountId,874		data: CreateCollectionData<T::AccountId>,875		flags: CollectionFlags,876	) -> Result<CollectionId, DispatchError> {877		{878			ensure!(879				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,880				Error::<T>::CollectionTokenPrefixLimitExceeded881			);882		}883884		let created_count = <CreatedCollectionCount<T>>::get()885			.0886			.checked_add(1)887			.ok_or(ArithmeticError::Overflow)?;888		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;889		let id = CollectionId(created_count);890891		// bound Total number of collections892		ensure!(893			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,894			<Error<T>>::TotalCollectionsLimitExceeded895		);896897		// =========898899		let collection = Collection {900			owner: owner.as_sub().clone(),901			name: data.name,902			mode: data.mode.clone(),903			description: data.description,904			token_prefix: data.token_prefix,905			sponsorship: data906				.pending_sponsor907				.map(SponsorshipState::Unconfirmed)908				.unwrap_or_default(),909			limits: data910				.limits911				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))912				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,913			permissions: data914				.permissions915				.map(|permissions| {916					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)917				})918				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,919			flags,920		};921922		let mut collection_properties = up_data_structs::CollectionProperties::get();923		collection_properties924			.try_set_from_iter(data.properties.into_iter())925			.map_err(<Error<T>>::from)?;926927		CollectionProperties::<T>::insert(id, collection_properties);928929		let mut token_props_permissions = PropertiesPermissionMap::new();930		token_props_permissions931			.try_set_from_iter(data.token_property_permissions.into_iter())932			.map_err(<Error<T>>::from)?;933934		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);935936		// Take a (non-refundable) deposit of collection creation937		{938			let mut imbalance =939				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();940			imbalance.subsume(941				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(942					&T::TreasuryAccountId::get(),943					T::CollectionCreationPrice::get(),944				),945			);946			<T as Config>::Currency::settle(947				payer.as_sub(),948				imbalance,949				WithdrawReasons::TRANSFER,950				ExistenceRequirement::KeepAlive,951			)952			.map_err(|_| Error::<T>::NotSufficientFounds)?;953		}954955		<CreatedCollectionCount<T>>::put(created_count);956		<Pallet<T>>::deposit_event(Event::CollectionCreated(957			id,958			data.mode.id(),959			owner.as_sub().clone(),960		));961		<PalletEvm<T>>::deposit_log(962			erc::CollectionHelpersEvents::CollectionCreated {963				owner: *owner.as_eth(),964				collection_id: eth::collection_id_to_address(id),965			}966			.to_log(T::ContractAddress::get()),967		);968		<CollectionById<T>>::insert(id, collection);969		Ok(id)970	}971972	/// Destroy collection.973	///974	/// * `collection` - Collection handler.975	/// * `sender` - The owner or administrator of the collection.976	pub fn destroy_collection(977		collection: CollectionHandle<T>,978		sender: &T::CrossAccountId,979	) -> DispatchResult {980		ensure!(981			collection.limits.owner_can_destroy(),982			<Error<T>>::NoPermission,983		);984		collection.check_is_owner(sender)?;985986		let destroyed_collections = <DestroyedCollectionCount<T>>::get()987			.0988			.checked_add(1)989			.ok_or(ArithmeticError::Overflow)?;990991		// =========992993		<DestroyedCollectionCount<T>>::put(destroyed_collections);994		<CollectionById<T>>::remove(collection.id);995		<AdminAmount<T>>::remove(collection.id);996		let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);997		let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);998		<CollectionProperties<T>>::remove(collection.id);9991000		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));10011002		<PalletEvm<T>>::deposit_log(1003			erc::CollectionHelpersEvents::CollectionDestroyed {1004				collection_id: eth::collection_id_to_address(collection.id),1005			}1006			.to_log(T::ContractAddress::get()),1007		);1008		Ok(())1009	}10101011	/// Set collection property.1012	///1013	/// * `collection` - Collection handler.1014	/// * `sender` - The owner or administrator of the collection.1015	/// * `property` - The property to set.1016	pub fn set_collection_property(1017		collection: &CollectionHandle<T>,1018		sender: &T::CrossAccountId,1019		property: Property,1020	) -> DispatchResult {1021		collection.check_is_owner_or_admin(sender)?;10221023		CollectionProperties::<T>::try_mutate(collection.id, |properties| {1024			let property = property.clone();1025			properties.try_set(property.key, property.value)1026		})1027		.map_err(<Error<T>>::from)?;10281029		Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));10301031		Ok(())1032	}10331034	/// Set scouped collection property.1035	///1036	/// * `collection_id` - ID of the collection for which the property is being set.1037	/// * `scope` - Property scope.1038	/// * `property` - The property to set.1039	pub fn set_scoped_collection_property(1040		collection_id: CollectionId,1041		scope: PropertyScope,1042		property: Property,1043	) -> DispatchResult {1044		CollectionProperties::<T>::try_mutate(collection_id, |properties| {1045			properties.try_scoped_set(scope, property.key, property.value)1046		})1047		.map_err(<Error<T>>::from)?;10481049		Ok(())1050	}10511052	/// Set scouped collection properties.1053	///1054	/// * `collection_id` - ID of the collection for which the properties is being set.1055	/// * `scope` - Property scope.1056	/// * `properties` - The properties to set.1057	pub fn set_scoped_collection_properties(1058		collection_id: CollectionId,1059		scope: PropertyScope,1060		properties: impl Iterator<Item = Property>,1061	) -> DispatchResult {1062		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1063			stored_properties.try_scoped_set_from_iter(scope, properties)1064		})1065		.map_err(<Error<T>>::from)?;10661067		Ok(())1068	}10691070	/// Set collection properties.1071	///1072	/// * `collection` - Collection handler.1073	/// * `sender` - The owner or administrator of the collection.1074	/// * `properties` - The properties to set.1075	#[transactional]1076	pub fn set_collection_properties(1077		collection: &CollectionHandle<T>,1078		sender: &T::CrossAccountId,1079		properties: Vec<Property>,1080	) -> DispatchResult {1081		for property in properties {1082			Self::set_collection_property(collection, sender, property)?;1083		}10841085		Ok(())1086	}10871088	/// Delete collection property.1089	///1090	/// * `collection` - Collection handler.1091	/// * `sender` - The owner or administrator of the collection.1092	/// * `property` - The property to delete.1093	pub fn delete_collection_property(1094		collection: &CollectionHandle<T>,1095		sender: &T::CrossAccountId,1096		property_key: PropertyKey,1097	) -> DispatchResult {1098		collection.check_is_owner_or_admin(sender)?;10991100		CollectionProperties::<T>::try_mutate(collection.id, |properties| {1101			properties.remove(&property_key)1102		})1103		.map_err(<Error<T>>::from)?;11041105		Self::deposit_event(Event::CollectionPropertyDeleted(1106			collection.id,1107			property_key,1108		));11091110		Ok(())1111	}11121113	/// Delete collection properties.1114	///1115	/// * `collection` - Collection handler.1116	/// * `sender` - The owner or administrator of the collection.1117	/// * `properties` - The properties to delete.1118	#[transactional]1119	pub fn delete_collection_properties(1120		collection: &CollectionHandle<T>,1121		sender: &T::CrossAccountId,1122		property_keys: Vec<PropertyKey>,1123	) -> DispatchResult {1124		for key in property_keys {1125			Self::delete_collection_property(collection, sender, key)?;1126		}11271128		Ok(())1129	}11301131	/// Set collection propetry permission without any checks.1132	///1133	/// Used for migrations.1134	///1135	/// * `collection` - Collection handler.1136	/// * `property_permissions` - Property permissions.1137	pub fn set_property_permission_unchecked(1138		collection: CollectionId,1139		property_permission: PropertyKeyPermission,1140	) -> DispatchResult {1141		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1142			permissions.try_set(property_permission.key, property_permission.permission)1143		})1144		.map_err(<Error<T>>::from)?;1145		Ok(())1146	}11471148	/// Set collection property permission.1149	///1150	/// * `collection` - Collection handler.1151	/// * `sender` - The owner or administrator of the collection.1152	/// * `property_permission` - Property permission.1153	pub fn set_property_permission(1154		collection: &CollectionHandle<T>,1155		sender: &T::CrossAccountId,1156		property_permission: PropertyKeyPermission,1157	) -> DispatchResult {1158		Self::set_scoped_property_permission(1159			collection,1160			sender,1161			PropertyScope::None,1162			property_permission,1163		)1164	}11651166	/// Set collection property permission with scope.1167	///1168	/// * `collection` - Collection handler.1169	/// * `sender` - The owner or administrator of the collection.1170	/// * `scope` - Property scope.1171	/// * `property_permission` - Property permission.1172	pub fn set_scoped_property_permission(1173		collection: &CollectionHandle<T>,1174		sender: &T::CrossAccountId,1175		scope: PropertyScope,1176		property_permission: PropertyKeyPermission,1177	) -> DispatchResult {1178		collection.check_is_owner_or_admin(sender)?;11791180		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1181		let current_permission = all_permissions.get(&property_permission.key);1182		if matches![1183			current_permission,1184			Some(PropertyPermission { mutable: false, .. })1185		] {1186			return Err(<Error<T>>::NoPermission.into());1187		}11881189		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1190			let property_permission = property_permission.clone();1191			permissions.try_scoped_set(1192				scope,1193				property_permission.key,1194				property_permission.permission,1195			)1196		})1197		.map_err(<Error<T>>::from)?;11981199		Self::deposit_event(Event::PropertyPermissionSet(1200			collection.id,1201			property_permission.key,1202		));12031204		Ok(())1205	}12061207	/// Set token property permission.1208	///1209	/// * `collection` - Collection handler.1210	/// * `sender` - The owner or administrator of the collection.1211	/// * `property_permissions` - Property permissions.1212	#[transactional]1213	pub fn set_token_property_permissions(1214		collection: &CollectionHandle<T>,1215		sender: &T::CrossAccountId,1216		property_permissions: Vec<PropertyKeyPermission>,1217	) -> DispatchResult {1218		Self::set_scoped_token_property_permissions(1219			collection,1220			sender,1221			PropertyScope::None,1222			property_permissions,1223		)1224	}12251226	/// Set token property permission with scope.1227	///1228	/// * `collection` - Collection handler.1229	/// * `sender` - The owner or administrator of the collection.1230	/// * `scope` - Property scope.1231	/// * `property_permissions` - Property permissions.1232	#[transactional]1233	pub fn set_scoped_token_property_permissions(1234		collection: &CollectionHandle<T>,1235		sender: &T::CrossAccountId,1236		scope: PropertyScope,1237		property_permissions: Vec<PropertyKeyPermission>,1238	) -> DispatchResult {1239		for prop_pemission in property_permissions {1240			Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1241		}12421243		Ok(())1244	}12451246	/// Get collection property.1247	pub fn get_collection_property(1248		collection_id: CollectionId,1249		key: &PropertyKey,1250	) -> Option<PropertyValue> {1251		Self::collection_properties(collection_id).get(key).cloned()1252	}12531254	/// Convert byte vector to property key vector.1255	pub fn bytes_keys_to_property_keys(1256		keys: Vec<Vec<u8>>,1257	) -> Result<Vec<PropertyKey>, DispatchError> {1258		keys.into_iter()1259			.map(|key| -> Result<PropertyKey, DispatchError> {1260				key.try_into()1261					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1262			})1263			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1264	}12651266	/// Get properties according to given keys.1267	pub fn filter_collection_properties(1268		collection_id: CollectionId,1269		keys: Option<Vec<PropertyKey>>,1270	) -> Result<Vec<Property>, DispatchError> {1271		let properties = Self::collection_properties(collection_id);12721273		let properties = keys1274			.map(|keys| {1275				keys.into_iter()1276					.filter_map(|key| {1277						properties.get(&key).map(|value| Property {1278							key,1279							value: value.clone(),1280						})1281					})1282					.collect()1283			})1284			.unwrap_or_else(|| {1285				properties1286					.into_iter()1287					.map(|(key, value)| Property { key, value })1288					.collect()1289			});12901291		Ok(properties)1292	}12931294	/// Get property permissions according to given keys.1295	pub fn filter_property_permissions(1296		collection_id: CollectionId,1297		keys: Option<Vec<PropertyKey>>,1298	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1299		let permissions = Self::property_permissions(collection_id);13001301		let key_permissions = keys1302			.map(|keys| {1303				keys.into_iter()1304					.filter_map(|key| {1305						permissions1306							.get(&key)1307							.map(|permission| PropertyKeyPermission {1308								key,1309								permission: permission.clone(),1310							})1311					})1312					.collect()1313			})1314			.unwrap_or_else(|| {1315				permissions1316					.into_iter()1317					.map(|(key, permission)| PropertyKeyPermission { key, permission })1318					.collect()1319			});13201321		Ok(key_permissions)1322	}13231324	/// Toggle `user` participation in the `collection`'s allow list.1325	/// #### Store read/writes1326	/// 1 writes1327	pub fn toggle_allowlist(1328		collection: &CollectionHandle<T>,1329		sender: &T::CrossAccountId,1330		user: &T::CrossAccountId,1331		allowed: bool,1332	) -> DispatchResult {1333		collection.check_is_owner_or_admin(sender)?;13341335		// =========13361337		if allowed {1338			<Allowlist<T>>::insert((collection.id, user), true);1339		} else {1340			<Allowlist<T>>::remove((collection.id, user));1341		}13421343		Ok(())1344	}13451346	/// Toggle `user` participation in the `collection`'s admin list.1347	/// #### Store read/writes1348	/// 2 writes1349	pub fn toggle_admin(1350		collection: &CollectionHandle<T>,1351		sender: &T::CrossAccountId,1352		user: &T::CrossAccountId,1353		admin: bool,1354	) -> DispatchResult {1355		collection.check_is_owner(sender)?;13561357		let was_admin = <IsAdmin<T>>::get((collection.id, user));1358		if was_admin == admin {1359			return Ok(());1360		}1361		let amount = <AdminAmount<T>>::get(collection.id);13621363		if admin {1364			let amount = amount1365				.checked_add(1)1366				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1367			ensure!(1368				amount <= Self::collection_admins_limit(),1369				<Error<T>>::CollectionAdminCountExceeded,1370			);13711372			// =========13731374			<AdminAmount<T>>::insert(collection.id, amount);1375			<IsAdmin<T>>::insert((collection.id, user), true);1376		} else {1377			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1378			<IsAdmin<T>>::remove((collection.id, user));1379		}13801381		Ok(())1382	}13831384	/// Merge set fields from `new_limit` to `old_limit`.1385	pub fn clamp_limits(1386		mode: CollectionMode,1387		old_limit: &CollectionLimits,1388		mut new_limit: CollectionLimits,1389	) -> Result<CollectionLimits, DispatchError> {1390		let limits = old_limit;1391		limit_default!(old_limit, new_limit,1392			account_token_ownership_limit => ensure!(1393				new_limit <= MAX_TOKEN_OWNERSHIP,1394				<Error<T>>::CollectionLimitBoundsExceeded,1395			),1396			sponsored_data_size => ensure!(1397				new_limit <= CUSTOM_DATA_LIMIT,1398				<Error<T>>::CollectionLimitBoundsExceeded,1399			),14001401			sponsored_data_rate_limit => {},1402			token_limit => ensure!(1403				old_limit >= new_limit && new_limit > 0,1404				<Error<T>>::CollectionTokenLimitExceeded1405			),14061407			sponsor_transfer_timeout(match mode {1408				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1409				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1410				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1411			}) => ensure!(1412				new_limit <= MAX_SPONSOR_TIMEOUT,1413				<Error<T>>::CollectionLimitBoundsExceeded,1414			),1415			sponsor_approve_timeout => {},1416			owner_can_transfer => ensure!(1417				!limits.owner_can_transfer_instaled() ||1418				old_limit || !new_limit,1419				<Error<T>>::OwnerPermissionsCantBeReverted,1420			),1421			owner_can_destroy => ensure!(1422				old_limit || !new_limit,1423				<Error<T>>::OwnerPermissionsCantBeReverted,1424			),1425			transfers_enabled => {},1426		);1427		Ok(new_limit)1428	}14291430	/// Merge set fields from `new_permission` to `old_permission`.1431	pub fn clamp_permissions(1432		_mode: CollectionMode,1433		old_permission: &CollectionPermissions,1434		mut new_permission: CollectionPermissions,1435	) -> Result<CollectionPermissions, DispatchError> {1436		limit_default_clone!(old_permission, new_permission,1437			access => {},1438			mint_mode => {},1439			nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },1440		);1441		Ok(new_permission)1442	}1443}14441445/// Indicates unsupported methods by returning [Error::UnsupportedOperation].1446#[macro_export]1447macro_rules! unsupported {1448	($runtime:path) => {1449		Err($crate::Error::<$runtime>::UnsupportedOperation.into())1450	};1451}14521453/// Return weights for various worst-case operations.1454pub trait CommonWeightInfo<CrossAccountId> {1455	/// Weight of item creation.1456	fn create_item() -> Weight;14571458	/// Weight of items creation.1459	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;14601461	/// Weight of items creation.1462	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;14631464	/// The weight of the burning item.1465	fn burn_item() -> Weight;14661467	/// Property setting weight.1468	///1469	/// * `amount`- The number of properties to set.1470	fn set_collection_properties(amount: u32) -> Weight;14711472	/// Collection property deletion weight.1473	///1474	/// * `amount`- The number of properties to set.1475	fn delete_collection_properties(amount: u32) -> Weight;14761477	/// Token property setting weight.1478	///1479	/// * `amount`- The number of properties to set.1480	fn set_token_properties(amount: u32) -> Weight;14811482	/// Token property deletion weight.1483	///1484	/// * `amount`- The number of properties to delete.1485	fn delete_token_properties(amount: u32) -> Weight;14861487	/// Token property permissions set weight.1488	///1489	/// * `amount`- The number of property permissions to set.1490	fn set_token_property_permissions(amount: u32) -> Weight;14911492	/// Transfer price of the token or its parts.1493	fn transfer() -> Weight;14941495	/// The price of setting the permission of the operation from another user.1496	fn approve() -> Weight;14971498	/// Transfer price from another user.1499	fn transfer_from() -> Weight;15001501	/// The price of burning a token from another user.1502	fn burn_from() -> Weight;15031504	/// Differs from burn_item in case of Fungible and Refungible, as it should burn1505	/// whole users's balance.1506	///1507	/// This method shouldn't be used directly, as it doesn't count breadth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1508	fn burn_recursively_self_raw() -> Weight;15091510	/// Cost of iterating over `amount` children while burning, without counting child burning itself.1511	///1512	/// This method shouldn't be used directly, as it doesn't count depth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1513	fn burn_recursively_breadth_raw(amount: u32) -> Weight;15141515	/// The price of recursive burning a token.1516	///1517	/// `max_selfs` - The maximum burning weight of the token itself.1518	/// `max_breadth` - The maximum number of nested tokens to burn.1519	fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1520		Self::burn_recursively_self_raw()1521			.saturating_mul(max_selfs.max(1) as u64)1522			.saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1523	}15241525	/// The price of retrieving token owner1526	fn token_owner() -> Weight;1527}15281529/// Weight info extension trait for refungible pallet.1530pub trait RefungibleExtensionsWeightInfo {1531	/// Weight of token repartition.1532	fn repartition() -> Weight;1533}15341535/// Common collection operations.1536///1537/// It wraps methods in Fungible, Nonfungible and Refungible pallets1538/// and adds weight info.1539pub trait CommonCollectionOperations<T: Config> {1540	/// Create token.1541	///1542	/// * `sender` - The user who mint the token and pays for the transaction.1543	/// * `to` - The user who will own the token.1544	/// * `data` - Token data.1545	/// * `nesting_budget` - A budget that can be spent on nesting tokens.1546	fn create_item(1547		&self,1548		sender: T::CrossAccountId,1549		to: T::CrossAccountId,1550		data: CreateItemData,1551		nesting_budget: &dyn Budget,1552	) -> DispatchResultWithPostInfo;15531554	/// Create multiple tokens.1555	///1556	/// * `sender` - The user who mint the token and pays for the transaction.1557	/// * `to` - The user who will own the token.1558	/// * `data` - Token data.1559	/// * `nesting_budget` - A budget that can be spent on nesting tokens.1560	fn create_multiple_items(1561		&self,1562		sender: T::CrossAccountId,1563		to: T::CrossAccountId,1564		data: Vec<CreateItemData>,1565		nesting_budget: &dyn Budget,1566	) -> DispatchResultWithPostInfo;15671568	/// Create multiple tokens.1569	///1570	/// * `sender` - The user who mint the token and pays for the transaction.1571	/// * `to` - The user who will own the token.1572	/// * `data` - Token data.1573	/// * `nesting_budget` - A budget that can be spent on nesting tokens.1574	fn create_multiple_items_ex(1575		&self,1576		sender: T::CrossAccountId,1577		data: CreateItemExData<T::CrossAccountId>,1578		nesting_budget: &dyn Budget,1579	) -> DispatchResultWithPostInfo;15801581	/// Burn token.1582	///1583	/// * `sender` - The user who owns the token.1584	/// * `token` - Token id that will burned.1585	/// * `amount` - The number of parts of the token that will be burned.1586	fn burn_item(1587		&self,1588		sender: T::CrossAccountId,1589		token: TokenId,1590		amount: u128,1591	) -> DispatchResultWithPostInfo;15921593	/// Burn token and all nested tokens recursievly.1594	///1595	/// * `sender` - The user who owns the token.1596	/// * `token` - Token id that will burned.1597	/// * `self_budget` - The budget that can be spent on burning tokens.1598	/// * `breadth_budget` - The budget that can be spent on burning nested tokens.1599	fn burn_item_recursively(1600		&self,1601		sender: T::CrossAccountId,1602		token: TokenId,1603		self_budget: &dyn Budget,1604		breadth_budget: &dyn Budget,1605	) -> DispatchResultWithPostInfo;16061607	/// Set collection properties.1608	///1609	/// * `sender` - Must be either the owner of the collection or its admin.1610	/// * `properties` - Properties to be set.1611	fn set_collection_properties(1612		&self,1613		sender: T::CrossAccountId,1614		properties: Vec<Property>,1615	) -> DispatchResultWithPostInfo;16161617	/// Delete collection properties.1618	///1619	/// * `sender` - Must be either the owner of the collection or its admin.1620	/// * `properties` - The properties to be removed.1621	fn delete_collection_properties(1622		&self,1623		sender: &T::CrossAccountId,1624		property_keys: Vec<PropertyKey>,1625	) -> DispatchResultWithPostInfo;16261627	/// Set token properties.1628	///1629	/// The appropriate [`PropertyPermission`] for the token property1630	/// must be set with [`Self::set_token_property_permissions`].1631	///1632	/// * `sender` - Must be either the owner of the token or its admin.1633	/// * `token_id` - The token for which the properties are being set.1634	/// * `properties` - Properties to be set.1635	/// * `budget` - Budget for setting properties.1636	fn set_token_properties(1637		&self,1638		sender: T::CrossAccountId,1639		token_id: TokenId,1640		properties: Vec<Property>,1641		budget: &dyn Budget,1642	) -> DispatchResultWithPostInfo;16431644	/// Remove token properties.1645	///1646	/// The appropriate [`PropertyPermission`] for the token property1647	/// must be set with [`Self::set_token_property_permissions`].1648	///1649	/// * `sender` - Must be either the owner of the token or its admin.1650	/// * `token_id` - The token for which the properties are being remove.1651	/// * `property_keys` - Keys to remove corresponding properties.1652	/// * `budget` - Budget for removing properties.1653	fn delete_token_properties(1654		&self,1655		sender: T::CrossAccountId,1656		token_id: TokenId,1657		property_keys: Vec<PropertyKey>,1658		budget: &dyn Budget,1659	) -> DispatchResultWithPostInfo;16601661	/// Set token property permissions.1662	///1663	/// * `sender` - Must be either the owner of the token or its admin.1664	/// * `token_id` - The token for which the properties are being set.1665	/// * `property_permissions` - Property permissions to be set.1666	/// * `budget` - Budget for setting properties.1667	fn set_token_property_permissions(1668		&self,1669		sender: &T::CrossAccountId,1670		property_permissions: Vec<PropertyKeyPermission>,1671	) -> DispatchResultWithPostInfo;16721673	/// Transfer amount of token pieces.1674	///1675	/// * `sender` - Donor user.1676	/// * `to` - Recepient user.1677	/// * `token` - The token of which parts are being sent.1678	/// * `amount` - The number of parts of the token that will be transferred.1679	/// * `budget` - The maximum budget that can be spent on the transfer.1680	fn transfer(1681		&self,1682		sender: T::CrossAccountId,1683		to: T::CrossAccountId,1684		token: TokenId,1685		amount: u128,1686		budget: &dyn Budget,1687	) -> DispatchResultWithPostInfo;16881689	/// Grant access to another account to transfer parts of the token owned by the calling user via [Self::transfer_from].1690	///1691	/// * `sender` - The user who grants access to the token.1692	/// * `spender` - The user to whom the rights are granted.1693	/// * `token` - The token to which access is granted.1694	/// * `amount` - The amount of pieces that another user can dispose of.1695	fn approve(1696		&self,1697		sender: T::CrossAccountId,1698		spender: T::CrossAccountId,1699		token: TokenId,1700		amount: u128,1701	) -> DispatchResultWithPostInfo;17021703	/// Send parts of a token owned by another user.1704	///1705	/// Before calling this method, you must grant rights to the calling user via [`Self::approve`].1706	///1707	/// * `sender` - The user who must have access to the token (see [`Self::approve`]).1708	/// * `from` - The user who owns the token.1709	/// * `to` - Recepient user.1710	/// * `token` - The token of which parts are being sent.1711	/// * `amount` - The number of parts of the token that will be transferred.1712	/// * `budget` - The maximum budget that can be spent on the transfer.1713	fn transfer_from(1714		&self,1715		sender: T::CrossAccountId,1716		from: T::CrossAccountId,1717		to: T::CrossAccountId,1718		token: TokenId,1719		amount: u128,1720		budget: &dyn Budget,1721	) -> DispatchResultWithPostInfo;17221723	/// Burn parts of a token owned by another user.1724	///1725	/// Before calling this method, you must grant rights to the calling user via [`Self::approve`].1726	///1727	/// * `sender` - The user who must have access to the token (see [`Self::approve`]).1728	/// * `from` - The user who owns the token.1729	/// * `token` - The token of which parts are being sent.1730	/// * `amount` - The number of parts of the token that will be transferred.1731	/// * `budget` - The maximum budget that can be spent on the burn.1732	fn burn_from(1733		&self,1734		sender: T::CrossAccountId,1735		from: T::CrossAccountId,1736		token: TokenId,1737		amount: u128,1738		budget: &dyn Budget,1739	) -> DispatchResultWithPostInfo;17401741	/// Check permission to nest token.1742	///1743	/// * `sender` - The user who initiated the check.1744	/// * `from` - The token that is checked for embedding.1745	/// * `under` - Token under which to check.1746	/// * `budget` - The maximum budget that can be spent on the check.1747	fn check_nesting(1748		&self,1749		sender: T::CrossAccountId,1750		from: (CollectionId, TokenId),1751		under: TokenId,1752		budget: &dyn Budget,1753	) -> DispatchResult;17541755	/// Nest one token into another.1756	///1757	/// * `under` - Token holder.1758	/// * `to_nest` - Nested token.1759	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));17601761	/// Unnest token.1762	///1763	/// * `under` - Token holder.1764	/// * `to_nest` - Token to unnest.1765	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));17661767	/// Get all user tokens.1768	///1769	/// * `account` - Account for which you need to get tokens.1770	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;17711772	/// Get all the tokens in the collection.1773	fn collection_tokens(&self) -> Vec<TokenId>;17741775	/// Check if the token exists.1776	///1777	/// * `token` - Id token to check.1778	fn token_exists(&self, token: TokenId) -> bool;17791780	/// Get the id of the last minted token.1781	fn last_token_id(&self) -> TokenId;17821783	/// Get the owner of the token.1784	///1785	/// * `token` - The token for which you need to find out the owner.1786	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;17871788	/// Returns 10 tokens owners in no particular order.1789	///1790	/// * `token` - The token for which you need to find out the owners.1791	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;17921793	/// Get the value of the token property by key.1794	///1795	/// * `token` - Token with the property to get.1796	/// * `key` - Property name.1797	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;17981799	/// Get a set of token properties by key vector.1800	///1801	/// * `token` - Token with the property to get.1802	/// * `keys` - Vector of property keys. If this parameter is [None](sp_std::result::Result),1803	/// then all properties are returned.1804	fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;18051806	/// Amount of unique collection tokens1807	fn total_supply(&self) -> u32;18081809	/// Amount of different tokens account has.1810	///1811	/// * `account` - The account for which need to get the balance.1812	fn account_balance(&self, account: T::CrossAccountId) -> u32;18131814	/// Amount of specific token account have.1815	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;18161817	/// Amount of token pieces1818	fn total_pieces(&self, token: TokenId) -> Option<u128>;18191820	/// Get the number of parts of the token that a trusted user can manage.1821	///1822	/// * `sender` - Trusted user.1823	/// * `spender` - Owner of the token.1824	/// * `token` - The token for which to get the value.1825	fn allowance(1826		&self,1827		sender: T::CrossAccountId,1828		spender: T::CrossAccountId,1829		token: TokenId,1830	) -> u128;18311832	/// Get extension for RFT collection.1833	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;1834}18351836/// Extension for RFT collection.1837pub trait RefungibleExtensions<T>1838where1839	T: Config,1840{1841	/// Change the number of parts of the token.1842	///1843	/// When the value changes down, this function is equivalent to burning parts of the token.1844	///1845	/// * `sender` - The user calling the repartition operation. Must be the owner of the token.1846	/// * `token` - The token for which you want to change the number of parts.1847	/// * `amount` - The new value of the parts of the token.1848	fn repartition(1849		&self,1850		sender: &T::CrossAccountId,1851		token: TokenId,1852		amount: u128,1853	) -> DispatchResultWithPostInfo;1854}18551856/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].1857///1858/// Used for [`CommonCollectionOperations`] implementations and flexible enough to do so.1859pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1860	let post_info = PostDispatchInfo {1861		actual_weight: Some(weight),1862		pays_fee: Pays::Yes,1863	};1864	match res {1865		Ok(()) => Ok(post_info),1866		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1867	}1868}18691870impl<T: Config> From<PropertiesError> for Error<T> {1871	fn from(error: PropertiesError) -> Self {1872		match error {1873			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1874			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1875			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1876			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1877			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1878		}1879	}1880}