git.delta.rocks / unique-network / refs/commits / d11053909f47

difftreelog

source

pallets/common/src/lib.rs70.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::{57	ops::{Deref, DerefMut},58	slice::from_ref,59};60use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use sp_std::vec::Vec;62use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};63use evm_coder::ToLog;64use frame_support::{65	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},66	ensure,67	traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},68	dispatch::Pays,69	transactional, fail,70};71use pallet_evm::GasWeightMapping;72use up_data_structs::{73	AccessMode,74	COLLECTION_NUMBER_LIMIT,75	Collection,76	RpcCollection,77	CollectionFlags,78	RpcCollectionFlags,79	CollectionId,80	CreateItemData,81	MAX_TOKEN_PREFIX_LENGTH,82	COLLECTION_ADMINS_LIMIT,83	TokenId,84	TokenChild,85	CollectionStats,86	MAX_TOKEN_OWNERSHIP,87	CollectionMode,88	NFT_SPONSOR_TRANSFER_TIMEOUT,89	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,90	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,91	MAX_SPONSOR_TIMEOUT,92	CUSTOM_DATA_LIMIT,93	CollectionLimits,94	CreateCollectionData,95	SponsorshipState,96	CreateItemExData,97	SponsoringRateLimit,98	budget::Budget,99	PhantomType,100	Property,101	Properties,102	PropertiesPermissionMap,103	PropertyKey,104	PropertyValue,105	PropertyPermission,106	PropertiesError,107	TokenOwnerError,108	PropertyKeyPermission,109	TokenData,110	TrySetProperty,111	PropertyScope,112	// RMRK113	RmrkCollectionInfo,114	RmrkInstanceInfo,115	RmrkResourceInfo,116	RmrkPropertyInfo,117	RmrkBaseInfo,118	RmrkPartType,119	RmrkBoundedTheme,120	RmrkNftChild,121	CollectionPermissions,122};123use up_pov_estimate_rpc::PovInfo;124125pub use pallet::*;126use sp_core::H160;127use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};128129use crate::erc::CollectionHelpersEvents;130#[cfg(feature = "runtime-benchmarks")]131pub mod benchmarking;132pub mod dispatch;133pub mod erc;134pub mod eth;135pub mod weights;136137/// Weight info.138pub type SelfWeightOf<T> = <T as Config>::WeightInfo;139140/// Collection handle contains information about collection data and id.141/// Also provides functionality to count consumed gas.142///143/// CollectionHandle is used as a generic wrapper for collections of all types.144/// It allows to perform common operations and queries on any collection type,145/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].146#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]147pub struct CollectionHandle<T: Config> {148	/// Collection id149	pub id: CollectionId,150	collection: Collection<T::AccountId>,151	/// Substrate recorder for counting consumed gas152	pub recorder: SubstrateRecorder<T>,153}154155impl<T: Config> WithRecorder<T> for CollectionHandle<T> {156	fn recorder(&self) -> &SubstrateRecorder<T> {157		&self.recorder158	}159	fn into_recorder(self) -> SubstrateRecorder<T> {160		self.recorder161	}162}163164impl<T: Config> CollectionHandle<T> {165	/// Same as [CollectionHandle::new] but with an explicit gas limit.166	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {167		<CollectionById<T>>::get(id).map(|collection| Self {168			id,169			collection,170			recorder: SubstrateRecorder::new(gas_limit),171		})172	}173174	/// Same as [CollectionHandle::new] but with an existed [`SubstrateRecorder`].175	pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {176		<CollectionById<T>>::get(id).map(|collection| Self {177			id,178			collection,179			recorder,180		})181	}182183	/// Retrives collection data from storage and creates collection handle with default parameters.184	/// If collection not found return `None`185	pub fn new(id: CollectionId) -> Option<Self> {186		Self::new_with_gas_limit(id, u64::MAX)187	}188189	/// Same as [`CollectionHandle::new`] but if collection not found [CollectionNotFound](Error::CollectionNotFound) returned.190	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {191		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)192	}193194	/// Consume gas for reading.195	pub fn consume_store_reads(196		&self,197		reads: u64,198	) -> pallet_evm_coder_substrate::execution::Result<()> {199		self.recorder200			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201				<T as frame_system::Config>::DbWeight::get()202					.read203					.saturating_mul(reads),204			)))205	}206207	/// Consume gas for writing.208	pub fn consume_store_writes(209		&self,210		writes: u64,211	) -> pallet_evm_coder_substrate::execution::Result<()> {212		self.recorder213			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(214				<T as frame_system::Config>::DbWeight::get()215					.write216					.saturating_mul(writes),217			)))218	}219220	/// Consume gas for reading and writing.221	pub fn consume_store_reads_and_writes(222		&self,223		reads: u64,224		writes: u64,225	) -> pallet_evm_coder_substrate::execution::Result<()> {226		let weight = <T as frame_system::Config>::DbWeight::get();227		let reads = weight.read.saturating_mul(reads);228		let writes = weight.read.saturating_mul(writes);229		self.recorder230			.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(231				reads.saturating_add(writes),232			)))233	}234235	/// Save collection to storage.236	pub fn save(&self) -> DispatchResult {237		<CollectionById<T>>::insert(self.id, &self.collection);238		Ok(())239	}240241	/// Set collection sponsor.242	///243	/// Unique collections allows sponsoring for certain actions.244	/// This method allows you to set the sponsor of the collection.245	/// In order for sponsorship to become active, it must be confirmed through [`Self::confirm_sponsorship`].246	pub fn set_sponsor(247		&mut self,248		sender: &T::CrossAccountId,249		sponsor: T::AccountId,250	) -> DispatchResult {251		self.check_is_internal()?;252		self.check_is_owner_or_admin(sender)?;253254		self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());255256		<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));257		<PalletEvm<T>>::deposit_log(258			erc::CollectionHelpersEvents::CollectionChanged {259				collection_id: eth::collection_id_to_address(self.id),260			}261			.to_log(T::ContractAddress::get()),262		);263264		self.save()265	}266267	/// Force set `sponsor`.268	///269	/// Differs from [`set_sponsor`][`Self::set_sponsor`] in that confirmation270	/// from the `sponsor` is not required.271	///272	/// # Arguments273	///274	/// * `sender`: Caller's account.275	/// * `sponsor`: ID of the account of the sponsor-to-be.276	pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {277		self.check_is_internal()?;278279		self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());280281		<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));282		<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));283		<PalletEvm<T>>::deposit_log(284			erc::CollectionHelpersEvents::CollectionChanged {285				collection_id: eth::collection_id_to_address(self.id),286			}287			.to_log(T::ContractAddress::get()),288		);289290		self.save()291	}292293	/// Confirm sponsorship294	///295	/// In order for the sponsorship to become active, the user set as the sponsor must confirm their participation.296	/// Before confirming sponsorship, the user must be specified as the sponsor of the collection via [`Self::set_sponsor`].297	pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {298		self.check_is_internal()?;299		ensure!(300			self.collection.sponsorship.pending_sponsor() == Some(sender),301			Error::<T>::ConfirmSponsorshipFail302		);303304		self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());305306		<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));307		<PalletEvm<T>>::deposit_log(308			erc::CollectionHelpersEvents::CollectionChanged {309				collection_id: eth::collection_id_to_address(self.id),310			}311			.to_log(T::ContractAddress::get()),312		);313314		self.save()315	}316317	/// Remove collection sponsor.318	pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {319		self.check_is_internal()?;320		self.check_is_owner_or_admin(sender)?;321322		self.collection.sponsorship = SponsorshipState::Disabled;323324		<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));325		<PalletEvm<T>>::deposit_log(326			erc::CollectionHelpersEvents::CollectionChanged {327				collection_id: eth::collection_id_to_address(self.id),328			}329			.to_log(T::ContractAddress::get()),330		);331		self.save()332	}333334	/// Force remove `sponsor`.335	///336	/// Differs from `remove_sponsor` in that337	/// it doesn't require consent from the `owner` of the collection.338	pub fn force_remove_sponsor(&mut self) -> DispatchResult {339		self.check_is_internal()?;340341		self.collection.sponsorship = SponsorshipState::Disabled;342343		<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));344		<PalletEvm<T>>::deposit_log(345			erc::CollectionHelpersEvents::CollectionChanged {346				collection_id: eth::collection_id_to_address(self.id),347			}348			.to_log(T::ContractAddress::get()),349		);350		self.save()351	}352353	/// Checks that the collection was created with, and must be operated upon through **Unique API**.354	/// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error.355	pub fn check_is_internal(&self) -> DispatchResult {356		if self.flags.external {357			return Err(<Error<T>>::CollectionIsExternal)?;358		}359360		Ok(())361	}362363	/// Checks that the collection was created with, and must be operated upon through an **assimilated API**.364	/// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error.365	pub fn check_is_external(&self) -> DispatchResult {366		if !self.flags.external {367			return Err(<Error<T>>::CollectionIsInternal)?;368		}369370		Ok(())371	}372}373374impl<T: Config> Deref for CollectionHandle<T> {375	type Target = Collection<T::AccountId>;376377	fn deref(&self) -> &Self::Target {378		&self.collection379	}380}381382impl<T: Config> DerefMut for CollectionHandle<T> {383	fn deref_mut(&mut self) -> &mut Self::Target {384		&mut self.collection385	}386}387388impl<T: Config> CollectionHandle<T> {389	/// Checks if the `user` is the owner of the collection.390	pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {391		ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);392		Ok(())393	}394395	/// Returns **true** if the `user` is the owner or administrator of the collection.396	pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {397		*user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))398	}399400	/// Checks if the `user` is the owner or administrator of the collection.401	pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {402		ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);403		Ok(())404	}405406	/// Returns **true** if407	/// * the `user`is a collection owner or admin408	/// * the collection limits allow the owner/admins to transfer/burn any collection token409	pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {410		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)411	}412413	/// Return **true** if `user` does not have enough token parts, and he can ignore such restrictions.414	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {415		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)416	}417418	/// Checks if the user is in the allow list. If not [Error::AddressNotInAllowlist] returns.419	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {420		ensure!(421			<Allowlist<T>>::get((self.id, user)),422			<Error<T>>::AddressNotInAllowlist423		);424		Ok(())425	}426427	/// Changes collection owner to another account428	/// #### Store read/writes429	/// 1 writes430	pub fn change_owner(431		&mut self,432		caller: T::CrossAccountId,433		new_owner: T::CrossAccountId,434	) -> DispatchResult {435		self.check_is_internal()?;436		self.check_is_owner(&caller)?;437		self.collection.owner = new_owner.as_sub().clone();438439		<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(440			self.id,441			new_owner.as_sub().clone(),442		));443		<PalletEvm<T>>::deposit_log(444			erc::CollectionHelpersEvents::CollectionChanged {445				collection_id: eth::collection_id_to_address(self.id),446			}447			.to_log(T::ContractAddress::get()),448		);449450		self.save()451	}452}453454#[frame_support::pallet]455pub mod pallet {456	use super::*;457	use dispatch::CollectionDispatch;458	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};459	use frame_system::pallet_prelude::*;460	use frame_support::traits::Currency;461	use up_data_structs::{TokenId, mapping::TokenAddressMapping};462	use scale_info::TypeInfo;463	use weights::WeightInfo;464465	#[pallet::config]466	pub trait Config:467		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo468	{469		/// Weight information for functions of this pallet.470		type WeightInfo: WeightInfo;471472		/// Events compatible with [`frame_system::Config::Event`].473		type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;474475		/// Handler of accounts and payment.476		type Currency: Currency<Self::AccountId>;477478		/// Set price to create a collection.479		#[pallet::constant]480		type CollectionCreationPrice: Get<481			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,482		>;483484		/// Dispatcher of operations on collections.485		type CollectionDispatch: CollectionDispatch<Self>;486487		/// Account which holds the chain's treasury.488		type TreasuryAccountId: Get<Self::AccountId>;489490		/// Address under which the CollectionHelper contract would be available.491		#[pallet::constant]492		type ContractAddress: Get<H160>;493494		/// Mapper for token addresses to Ethereum addresses.495		type EvmTokenAddressMapping: TokenAddressMapping<H160>;496497		/// Mapper for token addresses to [`CrossAccountId`].498		type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;499	}500501	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);502503	#[pallet::pallet]504	#[pallet::storage_version(STORAGE_VERSION)]505	#[pallet::generate_store(pub(super) trait Store)]506	pub struct Pallet<T>(_);507508	#[pallet::extra_constants]509	impl<T: Config> Pallet<T> {510		/// Maximum admins per collection.511		pub fn collection_admins_limit() -> u32 {512			COLLECTION_ADMINS_LIMIT513		}514	}515516	impl<T: Config> Pallet<T> {517		/// Helper function that handles deposit events518		pub fn deposit_event(event: Event<T>) {519			let event = <T as Config>::RuntimeEvent::from(event);520			let event = event.into();521			<frame_system::Pallet<T>>::deposit_event(event)522		}523	}524525	#[pallet::event]526	pub enum Event<T: Config> {527		/// New collection was created528		CollectionCreated(529			/// Globally unique identifier of newly created collection.530			CollectionId,531			/// [`CollectionMode`] converted into _u8_.532			u8,533			/// Collection owner.534			T::AccountId,535		),536537		/// New collection was destroyed538		CollectionDestroyed(539			/// Globally unique identifier of collection.540			CollectionId,541		),542543		/// New item was created.544		ItemCreated(545			/// Id of the collection where item was created.546			CollectionId,547			/// Id of an item. Unique within the collection.548			TokenId,549			/// Owner of newly created item550			T::CrossAccountId,551			/// Always 1 for NFT552			u128,553		),554555		/// Collection item was burned.556		ItemDestroyed(557			/// Id of the collection where item was destroyed.558			CollectionId,559			/// Identifier of burned NFT.560			TokenId,561			/// Which user has destroyed its tokens.562			T::CrossAccountId,563			/// Amount of token pieces destroed. Always 1 for NFT.564			u128,565		),566567		/// Item was transferred568		Transfer(569			/// Id of collection to which item is belong.570			CollectionId,571			/// Id of an item.572			TokenId,573			/// Original owner of item.574			T::CrossAccountId,575			/// New owner of item.576			T::CrossAccountId,577			/// Amount of token pieces transfered. Always 1 for NFT.578			u128,579		),580581		/// Amount pieces of token owned by `sender` was approved for `spender`.582		Approved(583			/// Id of collection to which item is belong.584			CollectionId,585			/// Id of an item.586			TokenId,587			/// Original owner of item.588			T::CrossAccountId,589			/// Id for which the approval was granted.590			T::CrossAccountId,591			/// Amount of token pieces transfered. Always 1 for NFT.592			u128,593		),594595		/// A `sender` approves operations on all owned tokens for `spender`.596		ApprovedForAll(597			/// Id of collection to which item is belong.598			CollectionId,599			/// Owner of a wallet.600			T::CrossAccountId,601			/// Id for which operator status was granted or rewoked.602			T::CrossAccountId,603			/// Is operator status granted or revoked?604			bool,605		),606607		/// The colletion property has been added or edited.608		CollectionPropertySet(609			/// Id of collection to which property has been set.610			CollectionId,611			/// The property that was set.612			PropertyKey,613		),614615		/// The property has been deleted.616		CollectionPropertyDeleted(617			/// Id of collection to which property has been deleted.618			CollectionId,619			/// The property that was deleted.620			PropertyKey,621		),622623		/// The token property has been added or edited.624		TokenPropertySet(625			/// Identifier of the collection whose token has the property set.626			CollectionId,627			/// The token for which the property was set.628			TokenId,629			/// The property that was set.630			PropertyKey,631		),632633		/// The token property has been deleted.634		TokenPropertyDeleted(635			/// Identifier of the collection whose token has the property deleted.636			CollectionId,637			/// The token for which the property was deleted.638			TokenId,639			/// The property that was deleted.640			PropertyKey,641		),642643		/// The token property permission of a collection has been set.644		PropertyPermissionSet(645			/// ID of collection to which property permission has been set.646			CollectionId,647			/// The property permission that was set.648			PropertyKey,649		),650651		/// Address was added to the allow list.652		AllowListAddressAdded(653			/// ID of the affected collection.654			CollectionId,655			/// Address of the added account.656			T::CrossAccountId,657		),658659		/// Address was removed from the allow list.660		AllowListAddressRemoved(661			/// ID of the affected collection.662			CollectionId,663			/// Address of the removed account.664			T::CrossAccountId,665		),666667		/// Collection admin was added.668		CollectionAdminAdded(669			/// ID of the affected collection.670			CollectionId,671			/// Admin address.672			T::CrossAccountId,673		),674675		/// Collection admin was removed.676		CollectionAdminRemoved(677			/// ID of the affected collection.678			CollectionId,679			/// Removed admin address.680			T::CrossAccountId,681		),682683		/// Collection limits were set.684		CollectionLimitSet(685			/// ID of the affected collection.686			CollectionId,687		),688689		/// Collection owned was changed.690		CollectionOwnerChanged(691			/// ID of the affected collection.692			CollectionId,693			/// New owner address.694			T::AccountId,695		),696697		/// Collection permissions were set.698		CollectionPermissionSet(699			/// ID of the affected collection.700			CollectionId,701		),702703		/// Collection sponsor was set.704		CollectionSponsorSet(705			/// ID of the affected collection.706			CollectionId,707			/// New sponsor address.708			T::AccountId,709		),710711		/// New sponsor was confirm.712		SponsorshipConfirmed(713			/// ID of the affected collection.714			CollectionId,715			/// New sponsor address.716			T::AccountId,717		),718719		/// Collection sponsor was removed.720		CollectionSponsorRemoved(721			/// ID of the affected collection.722			CollectionId,723		),724	}725726	#[pallet::error]727	pub enum Error<T> {728		/// This collection does not exist.729		CollectionNotFound,730		/// Sender parameter and item owner must be equal.731		MustBeTokenOwner,732		/// No permission to perform action733		NoPermission,734		/// Destroying only empty collections is allowed735		CantDestroyNotEmptyCollection,736		/// Collection is not in mint mode.737		PublicMintingNotAllowed,738		/// Address is not in allow list.739		AddressNotInAllowlist,740741		/// Collection name can not be longer than 63 char.742		CollectionNameLimitExceeded,743		/// Collection description can not be longer than 255 char.744		CollectionDescriptionLimitExceeded,745		/// Token prefix can not be longer than 15 char.746		CollectionTokenPrefixLimitExceeded,747		/// Total collections bound exceeded.748		TotalCollectionsLimitExceeded,749		/// Exceeded max admin count750		CollectionAdminCountExceeded,751		/// Collection limit bounds per collection exceeded752		CollectionLimitBoundsExceeded,753		/// Tried to enable permissions which are only permitted to be disabled754		OwnerPermissionsCantBeReverted,755		/// Collection settings not allowing items transferring756		TransferNotAllowed,757		/// Account token limit exceeded per collection758		AccountTokenLimitExceeded,759		/// Collection token limit exceeded760		CollectionTokenLimitExceeded,761		/// Metadata flag frozen762		MetadataFlagFrozen,763764		/// Item does not exist765		TokenNotFound,766		/// Item is balance not enough767		TokenValueTooLow,768		/// Requested value is more than the approved769		ApprovedValueTooLow,770		/// Tried to approve more than owned771		CantApproveMoreThanOwned,772		/// Only spending from eth mirror could be approved773		AddressIsNotEthMirror,774775		/// Can't transfer tokens to ethereum zero address776		AddressIsZero,777778		/// The operation is not supported779		UnsupportedOperation,780781		/// Insufficient funds to perform an action782		NotSufficientFounds,783784		/// User does not satisfy the nesting rule785		UserIsNotAllowedToNest,786		/// Only tokens from specific collections may nest tokens under this one787		SourceCollectionIsNotAllowedToNest,788789		/// Tried to store more data than allowed in collection field790		CollectionFieldSizeExceeded,791792		/// Tried to store more property data than allowed793		NoSpaceForProperty,794795		/// Tried to store more property keys than allowed796		PropertyLimitReached,797798		/// Property key is too long799		PropertyKeyIsTooLong,800801		/// Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed802		InvalidCharacterInPropertyKey,803804		/// Empty property keys are forbidden805		EmptyPropertyKey,806807		/// Tried to access an external collection with an internal API808		CollectionIsExternal,809810		/// Tried to access an internal collection with an external API811		CollectionIsInternal,812813		/// This address is not set as sponsor, use setCollectionSponsor first.814		ConfirmSponsorshipFail,815816		/// The user is not an administrator.817		UserIsNotCollectionAdmin,818	}819820	/// Storage of the count of created collections. Essentially contains the last collection ID.821	#[pallet::storage]822	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;823824	/// Storage of the count of deleted collections.825	#[pallet::storage]826	pub type DestroyedCollectionCount<T> =827		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;828829	/// Storage of collection info.830	#[pallet::storage]831	pub type CollectionById<T> = StorageMap<832		Hasher = Blake2_128Concat,833		Key = CollectionId,834		Value = Collection<<T as frame_system::Config>::AccountId>,835		QueryKind = OptionQuery,836	>;837838	/// Storage of collection properties.839	#[pallet::storage]840	#[pallet::getter(fn collection_properties)]841	pub type CollectionProperties<T> = StorageMap<842		Hasher = Blake2_128Concat,843		Key = CollectionId,844		Value = Properties,845		QueryKind = ValueQuery,846		OnEmpty = up_data_structs::CollectionProperties,847	>;848849	/// Storage of token property permissions of a collection.850	#[pallet::storage]851	#[pallet::getter(fn property_permissions)]852	pub type CollectionPropertyPermissions<T> = StorageMap<853		Hasher = Blake2_128Concat,854		Key = CollectionId,855		Value = PropertiesPermissionMap,856		QueryKind = ValueQuery,857	>;858859	/// Storage of the amount of collection admins.860	#[pallet::storage]861	pub type AdminAmount<T> = StorageMap<862		Hasher = Blake2_128Concat,863		Key = CollectionId,864		Value = u32,865		QueryKind = ValueQuery,866	>;867868	/// List of collection admins.869	#[pallet::storage]870	pub type IsAdmin<T: Config> = StorageNMap<871		Key = (872			Key<Blake2_128Concat, CollectionId>,873			Key<Blake2_128Concat, T::CrossAccountId>,874		),875		Value = bool,876		QueryKind = ValueQuery,877	>;878879	/// Allowlisted collection users.880	#[pallet::storage]881	pub type Allowlist<T: Config> = StorageNMap<882		Key = (883			Key<Blake2_128Concat, CollectionId>,884			Key<Blake2_128Concat, T::CrossAccountId>,885		),886		Value = bool,887		QueryKind = ValueQuery,888	>;889890	/// Not used by code, exists only to provide some types to metadata.891	#[pallet::storage]892	pub type DummyStorageValue<T: Config> = StorageValue<893		Value = (894			CollectionStats,895			CollectionId,896			TokenId,897			TokenChild,898			PhantomType<(899				TokenData<T::CrossAccountId>,900				RpcCollection<T::AccountId>,901				// RMRK902				RmrkCollectionInfo<T::AccountId>,903				RmrkInstanceInfo<T::AccountId>,904				RmrkResourceInfo,905				RmrkPropertyInfo,906				RmrkBaseInfo<T::AccountId>,907				RmrkPartType,908				RmrkBoundedTheme,909				RmrkNftChild,910				// PoV Estimate Info911				PovInfo,912			)>,913		),914		QueryKind = OptionQuery,915	>;916917	#[pallet::hooks]918	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {919		fn on_runtime_upgrade() -> Weight {920			StorageVersion::new(1).put::<Pallet<T>>();921922			Weight::zero()923		}924	}925}926927impl<T: Config> Pallet<T> {928	/// Enshure that receiver address is correct.929	///930	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens.931	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {932		ensure!(933			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,934			<Error<T>>::AddressIsZero935		);936		Ok(())937	}938939	/// Get a vector of collection admins.940	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {941		<IsAdmin<T>>::iter_prefix((collection,))942			.map(|(a, _)| a)943			.collect()944	}945946	/// Get a vector of users allowed to mint tokens.947	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {948		<Allowlist<T>>::iter_prefix((collection,))949			.map(|(a, _)| a)950			.collect()951	}952953	/// Is `user` allowed to mint token in `collection`.954	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {955		<Allowlist<T>>::get((collection, user))956	}957958	/// Get statistics of collections.959	pub fn collection_stats() -> CollectionStats {960		let created = <CreatedCollectionCount<T>>::get();961		let destroyed = <DestroyedCollectionCount<T>>::get();962		CollectionStats {963			created: created.0,964			destroyed: destroyed.0,965			alive: created.0 - destroyed.0,966		}967	}968969	/// Get the effective limits for the collection.970	pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {971		let collection = <CollectionById<T>>::get(collection)?;972		let limits = collection.limits;973		let effective_limits = CollectionLimits {974			account_token_ownership_limit: Some(limits.account_token_ownership_limit()),975			sponsored_data_size: Some(limits.sponsored_data_size()),976			sponsored_data_rate_limit: Some(977				limits978					.sponsored_data_rate_limit979					.unwrap_or(SponsoringRateLimit::SponsoringDisabled),980			),981			token_limit: Some(limits.token_limit()),982			sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(983				match collection.mode {984					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,985					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,986					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,987				},988			)),989			sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),990			owner_can_transfer: Some(limits.owner_can_transfer()),991			owner_can_destroy: Some(limits.owner_can_destroy()),992			transfers_enabled: Some(limits.transfers_enabled()),993		};994995		Some(effective_limits)996	}997998	/// Returns information about the `collection` adapted for rpc.999	pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {1000		let Collection {1001			name,1002			description,1003			owner,1004			mode,1005			token_prefix,1006			sponsorship,1007			limits,1008			permissions,1009			flags,1010		} = <CollectionById<T>>::get(collection)?;10111012		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1013			.into_iter()1014			.map(|(key, permission)| PropertyKeyPermission { key, permission })1015			.collect();10161017		let properties = <CollectionProperties<T>>::get(collection)1018			.into_iter()1019			.map(|(key, value)| Property { key, value })1020			.collect();10211022		let permissions = CollectionPermissions {1023			access: Some(permissions.access()),1024			mint_mode: Some(permissions.mint_mode()),1025			nesting: Some(permissions.nesting().clone()),1026		};10271028		Some(RpcCollection {1029			name: name.into_inner(),1030			description: description.into_inner(),1031			owner,1032			mode,1033			token_prefix: token_prefix.into_inner(),1034			sponsorship,1035			limits,1036			permissions,1037			token_property_permissions,1038			properties,1039			read_only: flags.external,10401041			flags: RpcCollectionFlags {1042				foreign: flags.foreign,1043				erc721metadata: flags.erc721metadata,1044			},1045		})1046	}1047}10481049macro_rules! limit_default {1050	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1051		$(1052			if let Some($new) = $new.$field {1053				let $old = $old.$field($($arg)?);1054				let _ = $new;1055				let _ = $old;1056				$check1057			} else {1058				$new.$field = $old.$field1059			}1060		)*1061	}};1062}1063macro_rules! limit_default_clone {1064	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1065		$(1066			if let Some($new) = $new.$field.clone() {1067				let $old = $old.$field($($arg)?);1068				let _ = $new;1069				let _ = $old;1070				$check1071			} else {1072				$new.$field = $old.$field.clone()1073			}1074		)*1075	}};1076}10771078impl<T: Config> Pallet<T> {1079	/// Create new collection.1080	///1081	/// * `owner` - The owner of the collection.1082	/// * `data` - Description of the created collection.1083	/// * `flags` - Extra flags to store.1084	pub fn init_collection(1085		owner: T::CrossAccountId,1086		payer: T::CrossAccountId,1087		data: CreateCollectionData<T::AccountId>,1088		flags: CollectionFlags,1089	) -> Result<CollectionId, DispatchError> {1090		{1091			ensure!(1092				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1093				Error::<T>::CollectionTokenPrefixLimitExceeded1094			);1095		}10961097		let created_count = <CreatedCollectionCount<T>>::get()1098			.01099			.checked_add(1)1100			.ok_or(ArithmeticError::Overflow)?;1101		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1102		let id = CollectionId(created_count);11031104		// bound Total number of collections1105		ensure!(1106			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1107			<Error<T>>::TotalCollectionsLimitExceeded1108		);11091110		// =========11111112		let collection = Collection {1113			owner: owner.as_sub().clone(),1114			name: data.name,1115			mode: data.mode.clone(),1116			description: data.description,1117			token_prefix: data.token_prefix,1118			sponsorship: data1119				.pending_sponsor1120				.map(SponsorshipState::Unconfirmed)1121				.unwrap_or_default(),1122			limits: data1123				.limits1124				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1125				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,1126			permissions: data1127				.permissions1128				.map(|permissions| {1129					Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1130				})1131				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1132			flags,1133		};11341135		let mut collection_properties = up_data_structs::CollectionProperties::get();1136		collection_properties1137			.try_set_from_iter(data.properties.into_iter())1138			.map_err(<Error<T>>::from)?;11391140		CollectionProperties::<T>::insert(id, collection_properties);11411142		let mut token_props_permissions = PropertiesPermissionMap::new();1143		token_props_permissions1144			.try_set_from_iter(data.token_property_permissions.into_iter())1145			.map_err(<Error<T>>::from)?;11461147		CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11481149		// Take a (non-refundable) deposit of collection creation1150		{1151			let mut imbalance =1152				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1153			imbalance.subsume(1154				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1155					&T::TreasuryAccountId::get(),1156					T::CollectionCreationPrice::get(),1157				),1158			);1159			<T as Config>::Currency::settle(1160				payer.as_sub(),1161				imbalance,1162				WithdrawReasons::TRANSFER,1163				ExistenceRequirement::KeepAlive,1164			)1165			.map_err(|_| Error::<T>::NotSufficientFounds)?;1166		}11671168		<CreatedCollectionCount<T>>::put(created_count);1169		<Pallet<T>>::deposit_event(Event::CollectionCreated(1170			id,1171			data.mode.id(),1172			owner.as_sub().clone(),1173		));1174		<PalletEvm<T>>::deposit_log(1175			erc::CollectionHelpersEvents::CollectionCreated {1176				owner: *owner.as_eth(),1177				collection_id: eth::collection_id_to_address(id),1178			}1179			.to_log(T::ContractAddress::get()),1180		);1181		<CollectionById<T>>::insert(id, collection);1182		Ok(id)1183	}11841185	/// Destroy collection.1186	///1187	/// * `collection` - Collection handler.1188	/// * `sender` - The owner or administrator of the collection.1189	pub fn destroy_collection(1190		collection: CollectionHandle<T>,1191		sender: &T::CrossAccountId,1192	) -> DispatchResult {1193		ensure!(1194			collection.limits.owner_can_destroy(),1195			<Error<T>>::NoPermission,1196		);1197		collection.check_is_owner(sender)?;11981199		let destroyed_collections = <DestroyedCollectionCount<T>>::get()1200			.01201			.checked_add(1)1202			.ok_or(ArithmeticError::Overflow)?;12031204		// =========12051206		<DestroyedCollectionCount<T>>::put(destroyed_collections);1207		<CollectionById<T>>::remove(collection.id);1208		<AdminAmount<T>>::remove(collection.id);1209		let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1210		let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1211		<CollectionProperties<T>>::remove(collection.id);12121213		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12141215		<PalletEvm<T>>::deposit_log(1216			erc::CollectionHelpersEvents::CollectionDestroyed {1217				collection_id: eth::collection_id_to_address(collection.id),1218			}1219			.to_log(T::ContractAddress::get()),1220		);1221		Ok(())1222	}12231224	/// This function sets or removes a collection properties according to1225	/// `properties_updates` contents:1226	/// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1227	/// * removes a property under the <key> if the value is `None` `(<key>, None)`.1228	///1229	/// This function fires an event for each property change.1230	/// In case of an error, all the changes (including the events) will be reverted1231	/// since the function is transactional.1232	#[transactional]1233	fn modify_collection_properties(1234		collection: &CollectionHandle<T>,1235		sender: &T::CrossAccountId,1236		properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1237	) -> DispatchResult {1238		collection.check_is_owner_or_admin(sender)?;12391240		let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12411242		for (key, value) in properties_updates {1243			match value {1244				Some(value) => {1245					stored_properties1246						.try_set(key.clone(), value)1247						.map_err(<Error<T>>::from)?;12481249					Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1250					<PalletEvm<T>>::deposit_log(1251						erc::CollectionHelpersEvents::CollectionChanged {1252							collection_id: eth::collection_id_to_address(collection.id),1253						}1254						.to_log(T::ContractAddress::get()),1255					);1256				}1257				None => {1258					stored_properties.remove(&key).map_err(<Error<T>>::from)?;12591260					Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1261					<PalletEvm<T>>::deposit_log(1262						erc::CollectionHelpersEvents::CollectionChanged {1263							collection_id: eth::collection_id_to_address(collection.id),1264						}1265						.to_log(T::ContractAddress::get()),1266					);1267				}1268			}1269		}12701271		<CollectionProperties<T>>::set(collection.id, stored_properties);12721273		Ok(())1274	}12751276	/// A batch operation to add, edit or remove properties for a token.1277	/// It sets or removes a token's properties according to1278	/// `properties_updates` contents:1279	/// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1280	/// * removes a property under the <key> if the value is `None` `(<key>, None)`.1281	///1282	/// - `nesting_budget`: Limit for searching parents in-depth to check ownership.1283	/// - `is_token_create`: Indicates that method is called during token initialization.1284	///   Allows to bypass ownership check.1285	///1286	/// All affected properties should have `mutable` permission1287	/// to be **deleted** or to be **set more than once**,1288	/// and the sender should have permission to edit those properties.1289	///1290	/// This function fires an event for each property change.1291	/// In case of an error, all the changes (including the events) will be reverted1292	/// since the function is transactional.1293	pub fn modify_token_properties(1294		collection: &CollectionHandle<T>,1295		sender: &T::CrossAccountId,1296		token_id: TokenId,1297		properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1298		is_token_create: bool,1299		mut stored_properties: Properties,1300		is_token_owner: impl Fn() -> Result<bool, DispatchError>,1301		set_token_properties: impl FnOnce(Properties),1302	) -> DispatchResult {1303		let is_collection_admin = collection.is_owner_or_admin(sender);1304		let permissions = Self::property_permissions(collection.id);13051306		let mut token_owner_result = None;1307		let mut is_token_owner = || -> Result<bool, DispatchError> {1308			*token_owner_result.get_or_insert_with(&is_token_owner)1309		};13101311		for (key, value) in properties_updates {1312			let permission = permissions1313				.get(&key)1314				.cloned()1315				.unwrap_or_else(PropertyPermission::none);13161317			let is_property_exists = stored_properties.get(&key).is_some();13181319			match permission {1320				PropertyPermission { mutable: false, .. } if is_property_exists => {1321					return Err(<Error<T>>::NoPermission.into());1322				}13231324				PropertyPermission {1325					collection_admin,1326					token_owner,1327					..1328				} => {1329					//TODO: investigate threats during public minting.1330					let is_token_create =1331						is_token_create && (collection_admin || token_owner) && value.is_some();1332					if !(is_token_create1333						|| (collection_admin && is_collection_admin)1334						|| (token_owner && is_token_owner()?))1335					{1336						fail!(<Error<T>>::NoPermission);1337					}1338				}1339			}13401341			match value {1342				Some(value) => {1343					stored_properties1344						.try_set(key.clone(), value)1345						.map_err(<Error<T>>::from)?;13461347					Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1348				}1349				None => {1350					stored_properties.remove(&key).map_err(<Error<T>>::from)?;13511352					Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1353				}1354			}13551356			<PalletEvm<T>>::deposit_log(1357				CollectionHelpersEvents::TokenChanged {1358					collection_id: eth::collection_id_to_address(collection.id),1359					token_id: token_id.into(),1360				}1361				.to_log(T::ContractAddress::get()),1362			);1363		}13641365		set_token_properties(stored_properties);13661367		Ok(())1368	}13691370	/// Sets or unsets the approval of a given operator.1371	///1372	/// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.1373	/// - `owner`: Token owner1374	/// - `operator`: Operator1375	/// - `approve`: Should operator status be granted or revoked?1376	pub fn set_allowance_for_all(1377		collection: &CollectionHandle<T>,1378		owner: &T::CrossAccountId,1379		operator: &T::CrossAccountId,1380		approve: bool,1381		set_allowance: impl FnOnce(),1382		log: evm_coder::ethereum::Log,1383	) -> DispatchResult {1384		if collection.permissions.access() == AccessMode::AllowList {1385			collection.check_allowlist(owner)?;1386			collection.check_allowlist(operator)?;1387		}13881389		Self::ensure_correct_receiver(operator)?;13901391		set_allowance();13921393		<PalletEvm<T>>::deposit_log(log);1394		Self::deposit_event(Event::ApprovedForAll(1395			collection.id,1396			owner.clone(),1397			operator.clone(),1398			approve,1399		));1400		Ok(())1401	}14021403	/// Set collection property.1404	///1405	/// * `collection` - Collection handler.1406	/// * `sender` - The owner or administrator of the collection.1407	/// * `property` - The property to set.1408	pub fn set_collection_property(1409		collection: &CollectionHandle<T>,1410		sender: &T::CrossAccountId,1411		property: Property,1412	) -> DispatchResult {1413		Self::set_collection_properties(collection, sender, [property].into_iter())1414	}14151416	/// Set a scoped collection property, where the scope is a special prefix1417	/// prohibiting a user access to change the property directly.1418	///1419	/// * `collection_id` - ID of the collection for which the property is being set.1420	/// * `scope` - Property scope.1421	/// * `property` - The property to set.1422	pub fn set_scoped_collection_property(1423		collection_id: CollectionId,1424		scope: PropertyScope,1425		property: Property,1426	) -> DispatchResult {1427		CollectionProperties::<T>::try_mutate(collection_id, |properties| {1428			properties.try_scoped_set(scope, property.key, property.value)1429		})1430		.map_err(<Error<T>>::from)?;14311432		Ok(())1433	}14341435	/// Set scoped collection properties, where the scope is a special prefix1436	/// prohibiting a user access to change the properties directly.1437	///1438	/// * `collection_id` - ID of the collection for which the properties is being set.1439	/// * `scope` - Property scope.1440	/// * `properties` - The properties to set.1441	pub fn set_scoped_collection_properties(1442		collection_id: CollectionId,1443		scope: PropertyScope,1444		properties: impl Iterator<Item = Property>,1445	) -> DispatchResult {1446		CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1447			stored_properties.try_scoped_set_from_iter(scope, properties)1448		})1449		.map_err(<Error<T>>::from)?;14501451		Ok(())1452	}14531454	/// Set collection properties.1455	///1456	/// * `collection` - Collection handler.1457	/// * `sender` - The owner or administrator of the collection.1458	/// * `properties` - The properties to set.1459	pub fn set_collection_properties(1460		collection: &CollectionHandle<T>,1461		sender: &T::CrossAccountId,1462		properties: impl Iterator<Item = Property>,1463	) -> DispatchResult {1464		Self::modify_collection_properties(1465			collection,1466			sender,1467			properties.map(|property| (property.key, Some(property.value))),1468		)1469	}14701471	/// Delete collection property.1472	///1473	/// * `collection` - Collection handler.1474	/// * `sender` - The owner or administrator of the collection.1475	/// * `property` - The property to delete.1476	pub fn delete_collection_property(1477		collection: &CollectionHandle<T>,1478		sender: &T::CrossAccountId,1479		property_key: PropertyKey,1480	) -> DispatchResult {1481		Self::delete_collection_properties(collection, sender, [property_key].into_iter())1482	}14831484	/// Delete collection properties.1485	///1486	/// * `collection` - Collection handler.1487	/// * `sender` - The owner or administrator of the collection.1488	/// * `properties` - The properties to delete.1489	pub fn delete_collection_properties(1490		collection: &CollectionHandle<T>,1491		sender: &T::CrossAccountId,1492		property_keys: impl Iterator<Item = PropertyKey>,1493	) -> DispatchResult {1494		Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1495	}14961497	/// Set collection propetry permission without any checks.1498	///1499	/// Used for migrations.1500	///1501	/// * `collection` - Collection handler.1502	/// * `property_permissions` - Property permissions.1503	pub fn set_property_permission_unchecked(1504		collection: CollectionId,1505		property_permission: PropertyKeyPermission,1506	) -> DispatchResult {1507		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1508			permissions.try_set(property_permission.key, property_permission.permission)1509		})1510		.map_err(<Error<T>>::from)?;1511		Ok(())1512	}15131514	/// Set collection property permission.1515	///1516	/// * `collection` - Collection handler.1517	/// * `sender` - The owner or administrator of the collection.1518	/// * `property_permission` - Property permission.1519	pub fn set_property_permission(1520		collection: &CollectionHandle<T>,1521		sender: &T::CrossAccountId,1522		property_permission: PropertyKeyPermission,1523	) -> DispatchResult {1524		Self::set_scoped_property_permission(1525			collection,1526			sender,1527			PropertyScope::None,1528			property_permission,1529		)1530	}15311532	/// Set collection property permission with scope.1533	///1534	/// * `collection` - Collection handler.1535	/// * `sender` - The owner or administrator of the collection.1536	/// * `scope` - Property scope.1537	/// * `property_permission` - Property permission.1538	pub fn set_scoped_property_permission(1539		collection: &CollectionHandle<T>,1540		sender: &T::CrossAccountId,1541		scope: PropertyScope,1542		property_permission: PropertyKeyPermission,1543	) -> DispatchResult {1544		collection.check_is_owner_or_admin(sender)?;15451546		let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1547		let current_permission = all_permissions.get(&property_permission.key);1548		if matches![1549			current_permission,1550			Some(PropertyPermission { mutable: false, .. })1551		] {1552			return Err(<Error<T>>::NoPermission.into());1553		}15541555		CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1556			let property_permission = property_permission.clone();1557			permissions.try_scoped_set(1558				scope,1559				property_permission.key,1560				property_permission.permission,1561			)1562		})1563		.map_err(<Error<T>>::from)?;15641565		Self::deposit_event(Event::PropertyPermissionSet(1566			collection.id,1567			property_permission.key,1568		));1569		<PalletEvm<T>>::deposit_log(1570			erc::CollectionHelpersEvents::CollectionChanged {1571				collection_id: eth::collection_id_to_address(collection.id),1572			}1573			.to_log(T::ContractAddress::get()),1574		);15751576		Ok(())1577	}15781579	/// Set token property permission.1580	///1581	/// * `collection` - Collection handler.1582	/// * `sender` - The owner or administrator of the collection.1583	/// * `property_permissions` - Property permissions.1584	#[transactional]1585	pub fn set_token_property_permissions(1586		collection: &CollectionHandle<T>,1587		sender: &T::CrossAccountId,1588		property_permissions: Vec<PropertyKeyPermission>,1589	) -> DispatchResult {1590		Self::set_scoped_token_property_permissions(1591			collection,1592			sender,1593			PropertyScope::None,1594			property_permissions,1595		)1596	}15971598	/// Set token property permission with scope.1599	///1600	/// * `collection` - Collection handler.1601	/// * `sender` - The owner or administrator of the collection.1602	/// * `scope` - Property scope.1603	/// * `property_permissions` - Property permissions.1604	#[transactional]1605	pub fn set_scoped_token_property_permissions(1606		collection: &CollectionHandle<T>,1607		sender: &T::CrossAccountId,1608		scope: PropertyScope,1609		property_permissions: Vec<PropertyKeyPermission>,1610	) -> DispatchResult {1611		for prop_pemission in property_permissions {1612			Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1613		}16141615		Ok(())1616	}16171618	/// Get collection property.1619	pub fn get_collection_property(1620		collection_id: CollectionId,1621		key: &PropertyKey,1622	) -> Option<PropertyValue> {1623		Self::collection_properties(collection_id).get(key).cloned()1624	}16251626	/// Convert byte vector to property key vector.1627	pub fn bytes_keys_to_property_keys(1628		keys: Vec<Vec<u8>>,1629	) -> Result<Vec<PropertyKey>, DispatchError> {1630		keys.into_iter()1631			.map(|key| -> Result<PropertyKey, DispatchError> {1632				key.try_into()1633					.map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1634			})1635			.collect::<Result<Vec<PropertyKey>, DispatchError>>()1636	}16371638	/// Get properties according to given keys.1639	pub fn filter_collection_properties(1640		collection_id: CollectionId,1641		keys: Option<Vec<PropertyKey>>,1642	) -> Result<Vec<Property>, DispatchError> {1643		let properties = Self::collection_properties(collection_id);16441645		let properties = keys1646			.map(|keys| {1647				keys.into_iter()1648					.filter_map(|key| {1649						properties.get(&key).map(|value| Property {1650							key,1651							value: value.clone(),1652						})1653					})1654					.collect()1655			})1656			.unwrap_or_else(|| {1657				properties1658					.into_iter()1659					.map(|(key, value)| Property { key, value })1660					.collect()1661			});16621663		Ok(properties)1664	}16651666	/// Get property permissions according to given keys.1667	pub fn filter_property_permissions(1668		collection_id: CollectionId,1669		keys: Option<Vec<PropertyKey>>,1670	) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1671		let permissions = Self::property_permissions(collection_id);16721673		let key_permissions = keys1674			.map(|keys| {1675				keys.into_iter()1676					.filter_map(|key| {1677						permissions1678							.get(&key)1679							.map(|permission| PropertyKeyPermission {1680								key,1681								permission: permission.clone(),1682							})1683					})1684					.collect()1685			})1686			.unwrap_or_else(|| {1687				permissions1688					.into_iter()1689					.map(|(key, permission)| PropertyKeyPermission { key, permission })1690					.collect()1691			});16921693		Ok(key_permissions)1694	}16951696	/// Toggle `user` participation in the `collection`'s allow list.1697	/// #### Store read/writes1698	/// 1 writes1699	pub fn toggle_allowlist(1700		collection: &CollectionHandle<T>,1701		sender: &T::CrossAccountId,1702		user: &T::CrossAccountId,1703		allowed: bool,1704	) -> DispatchResult {1705		collection.check_is_owner_or_admin(sender)?;17061707		// =========17081709		if allowed {1710			<Allowlist<T>>::insert((collection.id, user), true);1711			Self::deposit_event(Event::<T>::AllowListAddressAdded(1712				collection.id,1713				user.clone(),1714			));1715		} else {1716			<Allowlist<T>>::remove((collection.id, user));1717			Self::deposit_event(Event::<T>::AllowListAddressRemoved(1718				collection.id,1719				user.clone(),1720			));1721		}17221723		<PalletEvm<T>>::deposit_log(1724			erc::CollectionHelpersEvents::CollectionChanged {1725				collection_id: eth::collection_id_to_address(collection.id),1726			}1727			.to_log(T::ContractAddress::get()),1728		);17291730		Ok(())1731	}17321733	/// Toggle `user` participation in the `collection`'s admin list.1734	/// #### Store read/writes1735	/// 2 reads, 2 writes1736	pub fn toggle_admin(1737		collection: &CollectionHandle<T>,1738		sender: &T::CrossAccountId,1739		user: &T::CrossAccountId,1740		admin: bool,1741	) -> DispatchResult {1742		collection.check_is_internal()?;1743		collection.check_is_owner(sender)?;17441745		let is_admin = <IsAdmin<T>>::get((collection.id, user));1746		if is_admin == admin {1747			if admin {1748				return Ok(());1749			} else {1750				return Err(Error::<T>::UserIsNotCollectionAdmin.into());1751			}1752		}1753		let amount = <AdminAmount<T>>::get(collection.id);17541755		// =========17561757		if admin {1758			let amount = amount1759				.checked_add(1)1760				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1761			ensure!(1762				amount <= Self::collection_admins_limit(),1763				<Error<T>>::CollectionAdminCountExceeded,1764			);17651766			<AdminAmount<T>>::insert(collection.id, amount);1767			<IsAdmin<T>>::insert((collection.id, user), true);17681769			Self::deposit_event(Event::<T>::CollectionAdminAdded(1770				collection.id,1771				user.clone(),1772			));1773		} else {1774			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1775			<IsAdmin<T>>::remove((collection.id, user));17761777			Self::deposit_event(Event::<T>::CollectionAdminRemoved(1778				collection.id,1779				user.clone(),1780			));1781		}17821783		<PalletEvm<T>>::deposit_log(1784			erc::CollectionHelpersEvents::CollectionChanged {1785				collection_id: eth::collection_id_to_address(collection.id),1786			}1787			.to_log(T::ContractAddress::get()),1788		);17891790		Ok(())1791	}17921793	/// Update collection limits.1794	pub fn update_limits(1795		user: &T::CrossAccountId,1796		collection: &mut CollectionHandle<T>,1797		new_limit: CollectionLimits,1798	) -> DispatchResult {1799		collection.check_is_internal()?;1800		collection.check_is_owner_or_admin(user)?;18011802		collection.limits =1803			Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;18041805		Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1806		<PalletEvm<T>>::deposit_log(1807			erc::CollectionHelpersEvents::CollectionChanged {1808				collection_id: eth::collection_id_to_address(collection.id),1809			}1810			.to_log(T::ContractAddress::get()),1811		);18121813		collection.save()1814	}18151816	/// Merge set fields from `new_limit` to `old_limit`.1817	fn clamp_limits(1818		mode: CollectionMode,1819		old_limit: &CollectionLimits,1820		mut new_limit: CollectionLimits,1821	) -> Result<CollectionLimits, DispatchError> {1822		let limits = old_limit;1823		limit_default!(old_limit, new_limit,1824			account_token_ownership_limit => ensure!(1825				new_limit <= MAX_TOKEN_OWNERSHIP,1826				<Error<T>>::CollectionLimitBoundsExceeded,1827			),1828			sponsored_data_size => ensure!(1829				new_limit <= CUSTOM_DATA_LIMIT,1830				<Error<T>>::CollectionLimitBoundsExceeded,1831			),18321833			sponsored_data_rate_limit => {},1834			token_limit => ensure!(1835				old_limit >= new_limit && new_limit > 0,1836				<Error<T>>::CollectionTokenLimitExceeded1837			),18381839			sponsor_transfer_timeout(match mode {1840				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1841				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1842				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1843			}) => ensure!(1844				new_limit <= MAX_SPONSOR_TIMEOUT,1845				<Error<T>>::CollectionLimitBoundsExceeded,1846			),1847			sponsor_approve_timeout => {},1848			owner_can_transfer => ensure!(1849				!limits.owner_can_transfer_instaled() ||1850				old_limit || !new_limit,1851				<Error<T>>::OwnerPermissionsCantBeReverted,1852			),1853			owner_can_destroy => ensure!(1854				old_limit || !new_limit,1855				<Error<T>>::OwnerPermissionsCantBeReverted,1856			),1857			transfers_enabled => {},1858		);1859		Ok(new_limit)1860	}18611862	/// Update collection permissions.1863	pub fn update_permissions(1864		user: &T::CrossAccountId,1865		collection: &mut CollectionHandle<T>,1866		new_permission: CollectionPermissions,1867	) -> DispatchResult {1868		collection.check_is_internal()?;1869		collection.check_is_owner_or_admin(user)?;1870		collection.permissions = Self::clamp_permissions(1871			collection.mode.clone(),1872			&collection.permissions,1873			new_permission,1874		)?;18751876		Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1877		<PalletEvm<T>>::deposit_log(1878			erc::CollectionHelpersEvents::CollectionChanged {1879				collection_id: eth::collection_id_to_address(collection.id),1880			}1881			.to_log(T::ContractAddress::get()),1882		);18831884		collection.save()1885	}18861887	/// Merge set fields from `new_permission` to `old_permission`.1888	fn clamp_permissions(1889		_mode: CollectionMode,1890		old_permission: &CollectionPermissions,1891		mut new_permission: CollectionPermissions,1892	) -> Result<CollectionPermissions, DispatchError> {1893		limit_default_clone!(old_permission, new_permission,1894			access => {},1895			mint_mode => {},1896			nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },1897		);1898		Ok(new_permission)1899	}19001901	/// Repair possibly broken properties of a collection.1902	pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1903		CollectionProperties::<T>::mutate(collection_id, |properties| {1904			properties.recompute_consumed_space();1905		});19061907		Ok(())1908	}1909}19101911/// Indicates unsupported methods by returning [Error::UnsupportedOperation].1912#[macro_export]1913macro_rules! unsupported {1914	($runtime:path) => {1915		Err($crate::Error::<$runtime>::UnsupportedOperation.into())1916	};1917}19181919/// Return weights for various worst-case operations.1920pub trait CommonWeightInfo<CrossAccountId> {1921	/// Weight of item creation.1922	fn create_item(data: &CreateItemData) -> Weight {1923		Self::create_multiple_items(from_ref(data))1924	}19251926	/// Weight of items creation.1927	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;19281929	/// Weight of items creation.1930	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;19311932	/// The weight of the burning item.1933	fn burn_item() -> Weight;19341935	/// Property setting weight.1936	///1937	/// * `amount`- The number of properties to set.1938	fn set_collection_properties(amount: u32) -> Weight;19391940	/// Collection property deletion weight.1941	///1942	/// * `amount`- The number of properties to set.1943	fn delete_collection_properties(amount: u32) -> Weight;19441945	/// Token property setting weight.1946	///1947	/// * `amount`- The number of properties to set.1948	fn set_token_properties(amount: u32) -> Weight;19491950	/// Token property deletion weight.1951	///1952	/// * `amount`- The number of properties to delete.1953	fn delete_token_properties(amount: u32) -> Weight;19541955	/// Token property permissions set weight.1956	///1957	/// * `amount`- The number of property permissions to set.1958	fn set_token_property_permissions(amount: u32) -> Weight;19591960	/// Transfer price of the token or its parts.1961	fn transfer() -> Weight;19621963	/// The price of setting the permission of the operation from another user.1964	fn approve() -> Weight;19651966	/// The price of setting the permission of the operation from another user for eth mirror.1967	fn approve_from() -> Weight;19681969	/// Transfer price from another user.1970	fn transfer_from() -> Weight;19711972	/// The price of burning a token from another user.1973	fn burn_from() -> Weight;19741975	/// Differs from burn_item in case of Fungible and Refungible, as it should burn1976	/// whole users's balance.1977	///1978	/// This method shouldn't be used directly, as it doesn't count breadth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1979	fn burn_recursively_self_raw() -> Weight;19801981	/// Cost of iterating over `amount` children while burning, without counting child burning itself.1982	///1983	/// This method shouldn't be used directly, as it doesn't count depth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1984	fn burn_recursively_breadth_raw(amount: u32) -> Weight;19851986	/// The price of recursive burning a token.1987	///1988	/// `max_selfs` - The maximum burning weight of the token itself.1989	/// `max_breadth` - The maximum number of nested tokens to burn.1990	fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1991		Self::burn_recursively_self_raw()1992			.saturating_mul(max_selfs.max(1) as u64)1993			.saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1994	}19951996	/// The price of retrieving token owner1997	fn token_owner() -> Weight;19981999	/// The price of setting approval for all2000	fn set_allowance_for_all() -> Weight;20012002	/// The price of repairing an item.2003	fn force_repair_item() -> Weight;2004}20052006/// Weight info extension trait for refungible pallet.2007pub trait RefungibleExtensionsWeightInfo {2008	/// Weight of token repartition.2009	fn repartition() -> Weight;2010}20112012/// Common collection operations.2013///2014/// It wraps methods in Fungible, Nonfungible and Refungible pallets2015/// and adds weight info.2016pub trait CommonCollectionOperations<T: Config> {2017	/// Create token.2018	///2019	/// * `sender` - The user who mint the token and pays for the transaction.2020	/// * `to` - The user who will own the token.2021	/// * `data` - Token data.2022	/// * `nesting_budget` - A budget that can be spent on nesting tokens.2023	fn create_item(2024		&self,2025		sender: T::CrossAccountId,2026		to: T::CrossAccountId,2027		data: CreateItemData,2028		nesting_budget: &dyn Budget,2029	) -> DispatchResultWithPostInfo;20302031	/// Create multiple tokens.2032	///2033	/// * `sender` - The user who mint the token and pays for the transaction.2034	/// * `to` - The user who will own the token.2035	/// * `data` - Token data.2036	/// * `nesting_budget` - A budget that can be spent on nesting tokens.2037	fn create_multiple_items(2038		&self,2039		sender: T::CrossAccountId,2040		to: T::CrossAccountId,2041		data: Vec<CreateItemData>,2042		nesting_budget: &dyn Budget,2043	) -> DispatchResultWithPostInfo;20442045	/// Create multiple tokens.2046	///2047	/// * `sender` - The user who mint the token and pays for the transaction.2048	/// * `to` - The user who will own the token.2049	/// * `data` - Token data.2050	/// * `nesting_budget` - A budget that can be spent on nesting tokens.2051	fn create_multiple_items_ex(2052		&self,2053		sender: T::CrossAccountId,2054		data: CreateItemExData<T::CrossAccountId>,2055		nesting_budget: &dyn Budget,2056	) -> DispatchResultWithPostInfo;20572058	/// Burn token.2059	///2060	/// * `sender` - The user who owns the token.2061	/// * `token` - Token id that will burned.2062	/// * `amount` - The number of parts of the token that will be burned.2063	fn burn_item(2064		&self,2065		sender: T::CrossAccountId,2066		token: TokenId,2067		amount: u128,2068	) -> DispatchResultWithPostInfo;20692070	/// Burn token and all nested tokens recursievly.2071	///2072	/// * `sender` - The user who owns the token.2073	/// * `token` - Token id that will burned.2074	/// * `self_budget` - The budget that can be spent on burning tokens.2075	/// * `breadth_budget` - The budget that can be spent on burning nested tokens.2076	fn burn_item_recursively(2077		&self,2078		sender: T::CrossAccountId,2079		token: TokenId,2080		self_budget: &dyn Budget,2081		breadth_budget: &dyn Budget,2082	) -> DispatchResultWithPostInfo;20832084	/// Set collection properties.2085	///2086	/// * `sender` - Must be either the owner of the collection or its admin.2087	/// * `properties` - Properties to be set.2088	fn set_collection_properties(2089		&self,2090		sender: T::CrossAccountId,2091		properties: Vec<Property>,2092	) -> DispatchResultWithPostInfo;20932094	/// Delete collection properties.2095	///2096	/// * `sender` - Must be either the owner of the collection or its admin.2097	/// * `properties` - The properties to be removed.2098	fn delete_collection_properties(2099		&self,2100		sender: &T::CrossAccountId,2101		property_keys: Vec<PropertyKey>,2102	) -> DispatchResultWithPostInfo;21032104	/// Set token properties.2105	///2106	/// The appropriate [`PropertyPermission`] for the token property2107	/// must be set with [`Self::set_token_property_permissions`].2108	///2109	/// * `sender` - Must be either the owner of the token or its admin.2110	/// * `token_id` - The token for which the properties are being set.2111	/// * `properties` - Properties to be set.2112	/// * `budget` - Budget for setting properties.2113	fn set_token_properties(2114		&self,2115		sender: T::CrossAccountId,2116		token_id: TokenId,2117		properties: Vec<Property>,2118		budget: &dyn Budget,2119	) -> DispatchResultWithPostInfo;21202121	/// Remove token properties.2122	///2123	/// The appropriate [`PropertyPermission`] for the token property2124	/// must be set with [`Self::set_token_property_permissions`].2125	///2126	/// * `sender` - Must be either the owner of the token or its admin.2127	/// * `token_id` - The token for which the properties are being remove.2128	/// * `property_keys` - Keys to remove corresponding properties.2129	/// * `budget` - Budget for removing properties.2130	fn delete_token_properties(2131		&self,2132		sender: T::CrossAccountId,2133		token_id: TokenId,2134		property_keys: Vec<PropertyKey>,2135		budget: &dyn Budget,2136	) -> DispatchResultWithPostInfo;21372138	/// Set token property permissions.2139	///2140	/// * `sender` - Must be either the owner of the token or its admin.2141	/// * `token_id` - The token for which the properties are being set.2142	/// * `property_permissions` - Property permissions to be set.2143	/// * `budget` - Budget for setting properties.2144	fn set_token_property_permissions(2145		&self,2146		sender: &T::CrossAccountId,2147		property_permissions: Vec<PropertyKeyPermission>,2148	) -> DispatchResultWithPostInfo;21492150	/// Transfer amount of token pieces.2151	///2152	/// * `sender` - Donor user.2153	/// * `to` - Recepient user.2154	/// * `token` - The token of which parts are being sent.2155	/// * `amount` - The number of parts of the token that will be transferred.2156	/// * `budget` - The maximum budget that can be spent on the transfer.2157	fn transfer(2158		&self,2159		sender: T::CrossAccountId,2160		to: T::CrossAccountId,2161		token: TokenId,2162		amount: u128,2163		budget: &dyn Budget,2164	) -> DispatchResultWithPostInfo;21652166	/// Grant access to another account to transfer parts of the token owned by the calling user via [Self::transfer_from].2167	///2168	/// * `sender` - The user who grants access to the token.2169	/// * `spender` - The user to whom the rights are granted.2170	/// * `token` - The token to which access is granted.2171	/// * `amount` - The amount of pieces that another user can dispose of.2172	fn approve(2173		&self,2174		sender: T::CrossAccountId,2175		spender: T::CrossAccountId,2176		token: TokenId,2177		amount: u128,2178	) -> DispatchResultWithPostInfo;21792180	/// Grant access to another account to transfer parts of the token owned by the calling user's eth mirror via [Self::transfer_from].2181	///2182	/// * `sender` - The user who grants access to the token.2183	/// * `from` - Spender's eth mirror.2184	/// * `to` - The user to whom the rights are granted.2185	/// * `token` - The token to which access is granted.2186	/// * `amount` - The amount of pieces that another user can dispose of.2187	fn approve_from(2188		&self,2189		sender: T::CrossAccountId,2190		from: T::CrossAccountId,2191		to: T::CrossAccountId,2192		token: TokenId,2193		amount: u128,2194	) -> DispatchResultWithPostInfo;21952196	/// Send parts of a token owned by another user.2197	///2198	/// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2199	///2200	/// * `sender` - The user who must have access to the token (see [`Self::approve`]).2201	/// * `from` - The user who owns the token.2202	/// * `to` - Recepient user.2203	/// * `token` - The token of which parts are being sent.2204	/// * `amount` - The number of parts of the token that will be transferred.2205	/// * `budget` - The maximum budget that can be spent on the transfer.2206	fn transfer_from(2207		&self,2208		sender: T::CrossAccountId,2209		from: T::CrossAccountId,2210		to: T::CrossAccountId,2211		token: TokenId,2212		amount: u128,2213		budget: &dyn Budget,2214	) -> DispatchResultWithPostInfo;22152216	/// Burn parts of a token owned by another user.2217	///2218	/// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2219	///2220	/// * `sender` - The user who must have access to the token (see [`Self::approve`]).2221	/// * `from` - The user who owns the token.2222	/// * `token` - The token of which parts are being sent.2223	/// * `amount` - The number of parts of the token that will be transferred.2224	/// * `budget` - The maximum budget that can be spent on the burn.2225	fn burn_from(2226		&self,2227		sender: T::CrossAccountId,2228		from: T::CrossAccountId,2229		token: TokenId,2230		amount: u128,2231		budget: &dyn Budget,2232	) -> DispatchResultWithPostInfo;22332234	/// Check permission to nest token.2235	///2236	/// * `sender` - The user who initiated the check.2237	/// * `from` - The token that is checked for embedding.2238	/// * `under` - Token under which to check.2239	/// * `budget` - The maximum budget that can be spent on the check.2240	fn check_nesting(2241		&self,2242		sender: T::CrossAccountId,2243		from: (CollectionId, TokenId),2244		under: TokenId,2245		budget: &dyn Budget,2246	) -> DispatchResult;22472248	/// Nest one token into another.2249	///2250	/// * `under` - Token holder.2251	/// * `to_nest` - Nested token.2252	fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22532254	/// Unnest token.2255	///2256	/// * `under` - Token holder.2257	/// * `to_nest` - Token to unnest.2258	fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));22592260	/// Get all user tokens.2261	///2262	/// * `account` - Account for which you need to get tokens.2263	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;22642265	/// Get all the tokens in the collection.2266	fn collection_tokens(&self) -> Vec<TokenId>;22672268	/// Check if the token exists.2269	///2270	/// * `token` - Id token to check.2271	fn token_exists(&self, token: TokenId) -> bool;22722273	/// Get the id of the last minted token.2274	fn last_token_id(&self) -> TokenId;22752276	/// Get the owner of the token.2277	///2278	/// * `token` - The token for which you need to find out the owner.2279	fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22802281	/// Returns 10 tokens owners in no particular order.2282	///2283	/// * `token` - The token for which you need to find out the owners.2284	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;22852286	/// Get the value of the token property by key.2287	///2288	/// * `token` - Token with the property to get.2289	/// * `key` - Property name.2290	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;22912292	/// Get a set of token properties by key vector.2293	///2294	/// * `token` - Token with the property to get.2295	/// * `keys` - Vector of property keys. If this parameter is [None](sp_std::result::Result),2296	/// then all properties are returned.2297	fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;22982299	/// Amount of unique collection tokens2300	fn total_supply(&self) -> u32;23012302	/// Amount of different tokens account has.2303	///2304	/// * `account` - The account for which need to get the balance.2305	fn account_balance(&self, account: T::CrossAccountId) -> u32;23062307	/// Amount of specific token account have.2308	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;23092310	/// Amount of token pieces2311	fn total_pieces(&self, token: TokenId) -> Option<u128>;23122313	/// Get the number of parts of the token that a trusted user can manage.2314	///2315	/// * `sender` - Trusted user.2316	/// * `spender` - Owner of the token.2317	/// * `token` - The token for which to get the value.2318	fn allowance(2319		&self,2320		sender: T::CrossAccountId,2321		spender: T::CrossAccountId,2322		token: TokenId,2323	) -> u128;23242325	/// Get extension for RFT collection.2326	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;23272328	/// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.2329	/// * `owner` - Token owner2330	/// * `operator` - Operator2331	/// * `approve` - Should operator status be granted or revoked?2332	fn set_allowance_for_all(2333		&self,2334		owner: T::CrossAccountId,2335		operator: T::CrossAccountId,2336		approve: bool,2337	) -> DispatchResultWithPostInfo;23382339	/// Tells whether the given `owner` approves the `operator`.2340	fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;23412342	/// Repairs a possibly broken item.2343	fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2344}23452346/// Extension for RFT collection.2347pub trait RefungibleExtensions<T>2348where2349	T: Config,2350{2351	/// Change the number of parts of the token.2352	///2353	/// When the value changes down, this function is equivalent to burning parts of the token.2354	///2355	/// * `sender` - The user calling the repartition operation. Must be the owner of the token.2356	/// * `token` - The token for which you want to change the number of parts.2357	/// * `amount` - The new value of the parts of the token.2358	fn repartition(2359		&self,2360		sender: &T::CrossAccountId,2361		token: TokenId,2362		amount: u128,2363	) -> DispatchResultWithPostInfo;2364}23652366/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2367///2368/// Used for [`CommonCollectionOperations`] implementations and flexible enough to do so.2369pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2370	let post_info = PostDispatchInfo {2371		actual_weight: Some(weight),2372		pays_fee: Pays::Yes,2373	};2374	match res {2375		Ok(()) => Ok(post_info),2376		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2377	}2378}23792380impl<T: Config> From<PropertiesError> for Error<T> {2381	fn from(error: PropertiesError) -> Self {2382		match error {2383			PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2384			PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2385			PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2386			PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2387			PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2388		}2389	}2390}