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

difftreelog

source

primitives/data-structs/src/lib.rs40.5 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//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24	convert::{TryFrom, TryInto},25	fmt,26	ops::Deref,27};28use frame_support::storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet};2930#[cfg(feature = "serde")]31use serde::{Serialize, Deserialize};3233use sp_core::U256;34use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};35use sp_std::collections::btree_set::BTreeSet;36use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};37use frame_support::{BoundedVec, traits::ConstU32};38use derivative::Derivative;39use scale_info::TypeInfo;40use evm_coder::AbiCoderFlags;41use bondrewd::Bitfields;4243mod bondrewd_codec;44mod bounded;45pub mod budget;46pub mod mapping;47mod migration;4849/// Maximum of decimal points.50pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5152/// Maximum pieces for refungible token.53pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;54pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5556/// Maximum tokens for user.57pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {58	100_00059} else {60	1061};6263/// Maximum for collections can be created.64pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {65	100_00066} else {67	1068};6970/// Maximum for various custom data of token.71pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {72	204873} else {74	1075};7677/// Maximum admins per collection.78pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7980/// Maximum tokens per collection.81pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8283/// Maximum tokens per account.84pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {85	1_000_00086} else {87	1088};8990/// Default timeout for transfer sponsoring NFT item.91pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;92/// Default timeout for transfer sponsoring fungible item.93pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;94/// Default timeout for transfer sponsoring refungible item.95pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9697/// Default timeout for sponsored approving.98pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;99100// Schema limits101pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;102pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;104105// TODO: not used. Delete?106pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;107108/// Maximal length of a collection name.109pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;110111/// Maximal length of a collection description.112pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;113114/// Maximal length of a token prefix.115pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;116117/// Maximal length of a property key.118pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;119120/// Maximal length of a property value.121pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;122123/// A maximum number of token properties.124pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;125126/// Maximal lenght of extended property value.127pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;128129/// Maximum size for all collection properties.130pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;131132/// Maximum size of all token properties.133pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;134135/// How much items can be created per single136/// create_many call.137pub const MAX_ITEMS_PER_BATCH: u32 = 200;138139/// Used for limit bounded types of token custom data.140pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;141142/// Collection id.143#[derive(144	Encode,145	Decode,146	PartialEq,147	Eq,148	PartialOrd,149	Ord,150	Clone,151	Copy,152	Debug,153	Default,154	TypeInfo,155	MaxEncodedLen,156	Serialize,157	Deserialize,158)]159pub struct CollectionId(pub u32);160impl EncodeLike<u32> for CollectionId {}161impl EncodeLike<CollectionId> for u32 {}162163impl From<u32> for CollectionId {164	fn from(value: u32) -> Self {165		Self(value)166	}167}168169impl Deref for CollectionId {170	type Target = u32;171172	fn deref(&self) -> &Self::Target {173		&self.0174	}175}176177/// Token id.178#[derive(179	Encode,180	Decode,181	PartialEq,182	Eq,183	PartialOrd,184	Ord,185	Clone,186	Copy,187	Debug,188	Default,189	TypeInfo,190	MaxEncodedLen,191	Serialize,192	Deserialize,193)]194pub struct TokenId(pub u32);195impl EncodeLike<u32> for TokenId {}196impl EncodeLike<TokenId> for u32 {}197198impl TokenId {199	/// Try to get next token id.200	///201	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.202	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {203		self.0204			.checked_add(1)205			.ok_or(ArithmeticError::Overflow)206			.map(Self)207	}208}209210impl From<TokenId> for U256 {211	fn from(t: TokenId) -> Self {212		t.0.into()213	}214}215216impl TryFrom<U256> for TokenId {217	type Error = &'static str;218219	fn try_from(value: U256) -> Result<Self, Self::Error> {220		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))221	}222}223224/// Token data.225#[struct_versioning::versioned(version = 2, upper)]226#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]227pub struct TokenData<CrossAccountId> {228	/// Properties of token.229	pub properties: Vec<Property>,230231	/// Token owner.232	pub owner: Option<CrossAccountId>,233234	/// Token pieces.235	#[version(2.., upper(0))]236	pub pieces: u128,237}238239// TODO: unused type240pub struct OverflowError;241impl From<OverflowError> for &'static str {242	fn from(_: OverflowError) -> Self {243		"overflow occured"244	}245}246247/// Alias for decimal points type.248pub type DecimalPoints = u8;249250/// Collection mode.251///252/// Collection can represent various types of tokens.253/// Each collection can contain only one type of tokens at a time.254/// This type helps to understand which tokens the collection contains.255#[derive(256	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,257)]258pub enum CollectionMode {259	/// Non fungible tokens.260	NFT,261	/// Fungible tokens.262	Fungible(DecimalPoints),263	/// Refungible tokens.264	ReFungible,265}266267impl CollectionMode {268	/// Get collection mod as number.269	pub fn id(&self) -> u8 {270		match self {271			CollectionMode::NFT => 1,272			CollectionMode::Fungible(_) => 2,273			CollectionMode::ReFungible => 3,274		}275	}276}277278// TODO: unused trait279pub trait SponsoringResolve<AccountId, Call> {280	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;281}282283/// Access mode for some token operations.284#[derive(285	Encode,286	Decode,287	Eq,288	Debug,289	Clone,290	Copy,291	PartialEq,292	TypeInfo,293	MaxEncodedLen,294	Serialize,295	Deserialize,296)]297pub enum AccessMode {298	/// Access grant for owner and admins. Used as default.299	Normal,300	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.301	AllowList,302}303impl Default for AccessMode {304	fn default() -> Self {305		Self::Normal306	}307}308309// TODO: remove in future.310#[derive(311	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,312)]313pub enum SchemaVersion {314	ImageURL,315	Unique,316}317impl Default for SchemaVersion {318	fn default() -> Self {319		Self::ImageURL320	}321}322323// TODO: unused type324#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]325pub struct Ownership<AccountId> {326	pub owner: AccountId,327	pub fraction: u128,328}329330/// The state of collection sponsorship.331#[derive(332	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,333)]334pub enum SponsorshipState<AccountId> {335	/// The fees are applied to the transaction sender.336	Disabled,337	/// The sponsor is under consideration. Until the sponsor gives his consent,338	/// the fee will still be charged to sender.339	Unconfirmed(AccountId),340	/// Transactions are sponsored by specified account.341	Confirmed(AccountId),342}343344impl<AccountId> SponsorshipState<AccountId> {345	/// Get a sponsor of the collection who has confirmed his status.346	pub fn sponsor(&self) -> Option<&AccountId> {347		match self {348			Self::Confirmed(sponsor) => Some(sponsor),349			_ => None,350		}351	}352353	/// Get a sponsor of the collection who has pending or confirmed status.354	pub fn pending_sponsor(&self) -> Option<&AccountId> {355		match self {356			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),357			_ => None,358		}359	}360361	/// Whether the sponsorship is confirmed.362	pub fn confirmed(&self) -> bool {363		matches!(self, Self::Confirmed(_))364	}365}366367impl<T> Default for SponsorshipState<T> {368	fn default() -> Self {369		Self::Disabled370	}371}372373pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;374pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;375pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;376377#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]378#[bondrewd(enforce_bytes = 1)]379pub struct CollectionFlags {380	/// Tokens in foreign collections can be transferred, but not burnt381	#[bondrewd(bits = "0..1")]382	pub foreign: bool,383	/// Supports ERC721Metadata384	#[bondrewd(bits = "1..2")]385	pub erc721metadata: bool,386	/// External collections can't be managed using `unique` api387	#[bondrewd(bits = "7..8")]388	pub external: bool,389	/// Reserved flags390	#[bondrewd(bits = "2..7")]391	pub reserved: u8,392}393bondrewd_codec!(CollectionFlags);394395impl CollectionFlags {396	pub fn is_allowed_for_user(self) -> bool {397		!self.foreign && !self.external && self.reserved == 0398	}399}400401/// Base structure for represent collection.402///403/// Used to provide basic functionality for all types of collections.404///405/// #### Note406/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).407#[struct_versioning::versioned(version = 2, upper)]408#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]409pub struct Collection<AccountId> {410	/// Collection owner account.411	pub owner: AccountId,412413	/// Collection mode.414	pub mode: CollectionMode,415416	/// Access mode.417	#[version(..2)]418	pub access: AccessMode,419420	/// Collection name.421	pub name: CollectionName,422423	/// Collection description.424	pub description: CollectionDescription,425426	/// Token prefix.427	pub token_prefix: CollectionTokenPrefix,428429	#[version(..2)]430	pub mint_mode: bool,431432	#[version(..2)]433	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,434435	#[version(..2)]436	pub schema_version: SchemaVersion,437438	/// The state of sponsorship of the collection.439	pub sponsorship: SponsorshipState<AccountId>,440441	/// Collection limits.442	pub limits: CollectionLimits,443444	/// Collection permissions.445	#[version(2.., upper(Default::default()))]446	pub permissions: CollectionPermissions,447448	#[version(2.., upper(Default::default()))]449	pub flags: CollectionFlags,450451	#[version(..2)]452	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,453454	#[version(..2)]455	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,456457	#[version(..2)]458	pub meta_update_permission: MetaUpdatePermission,459}460461#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]462pub struct RpcCollectionFlags {463	/// Is collection is foreign.464	pub foreign: bool,465	/// Collection supports ERC721Metadata.466	pub erc721metadata: bool,467}468469/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).470#[struct_versioning::versioned(version = 2, upper)]471#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]472pub struct RpcCollection<AccountId> {473	/// Collection owner account.474	pub owner: AccountId,475476	/// Collection mode.477	pub mode: CollectionMode,478479	/// Collection name.480	pub name: Vec<u16>,481482	/// Collection description.483	pub description: Vec<u16>,484485	/// Token prefix.486	pub token_prefix: Vec<u8>,487488	/// The state of sponsorship of the collection.489	pub sponsorship: SponsorshipState<AccountId>,490491	/// Collection limits.492	pub limits: CollectionLimits,493494	/// Collection permissions.495	pub permissions: CollectionPermissions,496497	/// Token property permissions.498	pub token_property_permissions: Vec<PropertyKeyPermission>,499500	/// Collection properties.501	pub properties: Vec<Property>,502503	/// Is collection read only.504	pub read_only: bool,505506	/// Extra collection flags507	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]508	pub flags: RpcCollectionFlags,509}510511impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {512	fn from(value: CollectionVersion1<AccountId>) -> Self {513		let CollectionVersion1 {514			name,515			description,516			owner,517			mode,518			access,519			token_prefix,520			mint_mode,521			sponsorship,522			limits,523			..524		} = value;525526		RpcCollection {527			name: name.into_inner(),528			description: description.into_inner(),529			owner,530			mode,531			token_prefix: token_prefix.into_inner(),532			sponsorship,533			limits,534			permissions: CollectionPermissions {535				access: Some(access),536				mint_mode: Some(mint_mode),537				nesting: None,538			},539			token_property_permissions: Vec::default(),540			properties: Vec::default(),541			read_only: true,542543			flags: RpcCollectionFlags {544				foreign: false,545				erc721metadata: false,546			},547		}548	}549}550551pub struct RawEncoded(Vec<u8>);552553impl parity_scale_codec::Decode for RawEncoded {554	fn decode<I: parity_scale_codec::Input>(555		input: &mut I,556	) -> Result<Self, parity_scale_codec::Error> {557		let mut out = Vec::new();558		while let Ok(v) = input.read_byte() {559			out.push(v);560		}561		Ok(Self(out))562	}563}564565impl Deref for RawEncoded {566	type Target = Vec<u8>;567568	fn deref(&self) -> &Self::Target {569		&self.0570	}571}572573/// Data used for create collection.574///575/// All fields are wrapped in [`Option`], where `None` means chain default.576#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]577#[derivative(Debug, Default(bound = ""))]578pub struct CreateCollectionData<CrossAccountId> {579	/// Collection mode.580	#[derivative(Default(value = "CollectionMode::NFT"))]581	pub mode: CollectionMode,582583	/// Access mode.584	pub access: Option<AccessMode>,585586	/// Collection name.587	pub name: CollectionName,588589	/// Collection description.590	pub description: CollectionDescription,591592	/// Token prefix.593	pub token_prefix: CollectionTokenPrefix,594595	/// Collection limits.596	pub limits: Option<CollectionLimits>,597598	/// Collection permissions.599	pub permissions: Option<CollectionPermissions>,600601	/// Token property permissions.602	pub token_property_permissions: CollectionPropertiesPermissionsVec,603604	/// Collection properties.605	pub properties: CollectionPropertiesVec,606607	pub admin_list: Vec<CrossAccountId>,608609	/// Pending collection sponsor.610	pub pending_sponsor: Option<CrossAccountId>,611612	pub flags: CollectionFlags,613}614615/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].616// TODO: maybe rename to PropertiesPermissionsVec617pub type CollectionPropertiesPermissionsVec =618	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;619620/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].621pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;622623/// Limits and restrictions of a collection.624///625/// All fields are wrapped in [`Option`], where `None` means chain default.626///627/// Update with `pallet_common::Pallet::clamp_limits`.628// IMPORTANT: When adding/removing fields from this struct - don't forget to also629#[derive(630	Encode,631	Decode,632	Debug,633	Default,634	Clone,635	PartialEq,636	TypeInfo,637	MaxEncodedLen,638	Serialize,639	Deserialize,640)]641// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.642// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.643// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.644pub struct CollectionLimits {645	/// How many tokens can a user have on one account.646	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].647	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].648	pub account_token_ownership_limit: Option<u32>,649650	/// How many bytes of data are available for sponsorship.651	/// * Default - [`CUSTOM_DATA_LIMIT`].652	/// * Limit - [`CUSTOM_DATA_LIMIT`].653	pub sponsored_data_size: Option<u32>,654655	// FIXME should we delete this or repurpose it?656	/// Times in how many blocks we sponsor data.657	///658	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.659	///660	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).661	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].662	///663	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]664	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,665	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]666667	/// How many tokens can be mined into this collection.668	///669	/// * Default - [`COLLECTION_TOKEN_LIMIT`].670	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].671	pub token_limit: Option<u32>,672673	/// Timeouts for transfer sponsoring.674	///675	/// * Default676	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]677	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]678	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]679	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].680	pub sponsor_transfer_timeout: Option<u32>,681682	/// Timeout for sponsoring an approval in passed blocks.683	///684	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].685	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].686	pub sponsor_approve_timeout: Option<u32>,687688	/// Whether the collection owner of the collection can send tokens (which belong to other users).689	///690	/// * Default - **false**.691	pub owner_can_transfer: Option<bool>,692693	/// Can the collection owner burn other people's tokens.694	///695	/// * Default - **true**.696	pub owner_can_destroy: Option<bool>,697698	/// Is it possible to send tokens from this collection between users.699	///700	/// * Default - **true**.701	pub transfers_enabled: Option<bool>,702}703704impl CollectionLimits {705	pub fn with_default_limits(collection_type: CollectionMode) -> Self {706		CollectionLimits {707			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),708			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),709			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),710			token_limit: Some(COLLECTION_TOKEN_LIMIT),711			sponsor_transfer_timeout: match collection_type {712				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),713				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),714				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),715			},716			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),717			owner_can_transfer: Some(false),718			owner_can_destroy: Some(true),719			transfers_enabled: Some(true),720		}721	}722723	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).724	pub fn account_token_ownership_limit(&self) -> u32 {725		self.account_token_ownership_limit726			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)727			.min(MAX_TOKEN_OWNERSHIP)728	}729730	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).731	pub fn sponsored_data_size(&self) -> u32 {732		self.sponsored_data_size733			.unwrap_or(CUSTOM_DATA_LIMIT)734			.min(CUSTOM_DATA_LIMIT)735	}736737	/// Get effective value for [`token_limit`](self.token_limit).738	pub fn token_limit(&self) -> u32 {739		self.token_limit740			.unwrap_or(COLLECTION_TOKEN_LIMIT)741			.min(COLLECTION_TOKEN_LIMIT)742	}743744	// TODO: may be replace u32 to mode?745	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).746	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {747		self.sponsor_transfer_timeout748			.unwrap_or(default)749			.min(MAX_SPONSOR_TIMEOUT)750	}751752	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).753	pub fn sponsor_approve_timeout(&self) -> u32 {754		self.sponsor_approve_timeout755			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)756			.min(MAX_SPONSOR_TIMEOUT)757	}758759	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).760	pub fn owner_can_transfer(&self) -> bool {761		self.owner_can_transfer.unwrap_or(false)762	}763764	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).765	pub fn owner_can_transfer_instaled(&self) -> bool {766		self.owner_can_transfer.is_some()767	}768769	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).770	pub fn owner_can_destroy(&self) -> bool {771		self.owner_can_destroy.unwrap_or(true)772	}773774	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).775	pub fn transfers_enabled(&self) -> bool {776		self.transfers_enabled.unwrap_or(true)777	}778779	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).780	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {781		match self782			.sponsored_data_rate_limit783			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)784		{785			SponsoringRateLimit::SponsoringDisabled => None,786			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),787		}788	}789}790791/// Permissions on certain operations within a collection.792///793/// Some fields are wrapped in [`Option`], where `None` means chain default.794///795/// Update with `pallet_common::Pallet::clamp_permissions`.796#[derive(797	Encode,798	Decode,799	Debug,800	Default,801	Clone,802	PartialEq,803	TypeInfo,804	MaxEncodedLen,805	Serialize,806	Deserialize,807)]808// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.809// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.810pub struct CollectionPermissions {811	/// Access mode.812	///813	/// * Default - [`AccessMode::Normal`].814	pub access: Option<AccessMode>,815816	/// Minting allowance.817	///818	/// * Default - **false**.819	pub mint_mode: Option<bool>,820821	/// Permissions for nesting.822	///823	/// * Default824	///   - `token_owner` - **false**825	///   - `collection_admin` - **false**826	///   - `restricted` - **None**827	pub nesting: Option<NestingPermissions>,828}829830impl CollectionPermissions {831	/// Get effective value for [`access`](self.access).832	pub fn access(&self) -> AccessMode {833		self.access.unwrap_or(AccessMode::Normal)834	}835836	/// Get effective value for [`mint_mode`](self.mint_mode).837	pub fn mint_mode(&self) -> bool {838		self.mint_mode.unwrap_or(false)839	}840841	/// Get effective value for [`nesting`](self.nesting).842	pub fn nesting(&self) -> &NestingPermissions {843		static DEFAULT: NestingPermissions = NestingPermissions {844			token_owner: false,845			collection_admin: false,846			restricted: None,847			#[cfg(feature = "runtime-benchmarks")]848			permissive: false,849		};850		self.nesting.as_ref().unwrap_or(&DEFAULT)851	}852}853854/// Inner set for collections allowed to nest.855type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;856857/// Wraper for collections set allowing nest.858#[derive(859	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,860)]861#[derivative(Debug)]862pub struct OwnerRestrictedSet(863	#[serde(with = "bounded::set_serde")]864	#[derivative(Debug(format_with = "bounded::set_debug"))]865	pub OwnerRestrictedSetInner,866);867868impl OwnerRestrictedSet {869	/// Create new set.870	pub fn new() -> Self {871		Self(Default::default())872	}873}874impl Default for OwnerRestrictedSet {875	fn default() -> Self {876		Self::new()877	}878}879impl core::ops::Deref for OwnerRestrictedSet {880	type Target = OwnerRestrictedSetInner;881	fn deref(&self) -> &Self::Target {882		&self.0883	}884}885impl core::ops::DerefMut for OwnerRestrictedSet {886	fn deref_mut(&mut self) -> &mut Self::Target {887		&mut self.0888	}889}890891impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {892	type Error = ();893894	fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {895		Ok(Self(value.try_into()?))896	}897}898899/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.900#[derive(901	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,902)]903#[derivative(Debug)]904pub struct NestingPermissions {905	/// Owner of token can nest tokens under it.906	pub token_owner: bool,907	/// Admin of token collection can nest tokens under token.908	pub collection_admin: bool,909	/// If set - only tokens from specified collections can be nested.910	pub restricted: Option<OwnerRestrictedSet>,911912	#[cfg(feature = "runtime-benchmarks")]913	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.914	pub permissive: bool,915}916917/// Enum denominating how often can sponsoring occur if it is enabled.918///919/// Used for [`collection limits`](CollectionLimits).920#[derive(921	Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,922)]923pub enum SponsoringRateLimit {924	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions925	SponsoringDisabled,926	/// Once per how many blocks can sponsorship of a transaction type occur927	Blocks(u32),928}929930/// Data used to describe an NFT at creation.931#[derive(932	Encode,933	Decode,934	MaxEncodedLen,935	Default,936	PartialEq,937	Clone,938	Derivative,939	TypeInfo,940	Serialize,941	Deserialize,942)]943#[derivative(Debug)]944pub struct CreateNftData {945	/// Key-value pairs used to describe the token as metadata946	#[serde(with = "bounded::vec_serde")]947	#[derivative(Debug(format_with = "bounded::vec_debug"))]948	/// Properties that wil be assignet to created item.949	pub properties: CollectionPropertiesVec,950}951952/// Data used to describe a Fungible token at creation.953#[derive(954	Encode,955	Decode,956	MaxEncodedLen,957	Default,958	Debug,959	Clone,960	PartialEq,961	TypeInfo,962	Serialize,963	Deserialize,964)]965pub struct CreateFungibleData {966	/// Number of fungible coins minted967	pub value: u128,968}969970/// Data used to describe a Refungible token at creation.971#[derive(972	Encode,973	Decode,974	MaxEncodedLen,975	Default,976	PartialEq,977	Clone,978	Derivative,979	TypeInfo,980	Serialize,981	Deserialize,982)]983#[derivative(Debug)]984pub struct CreateReFungibleData {985	/// Number of pieces the RFT is split into986	pub pieces: u128,987988	/// Key-value pairs used to describe the token as metadata989	#[serde(with = "bounded::vec_serde")]990	#[derivative(Debug(format_with = "bounded::vec_debug"))]991	pub properties: CollectionPropertiesVec,992}993994// TODO: remove this.995#[derive(996	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,997)]998pub enum MetaUpdatePermission {999	ItemOwner,1000	Admin,1001	None,1002}10031004/// Enum holding data used for creation of all three item types.1005/// Unified data for create item.1006#[derive(1007	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1008)]1009pub enum CreateItemData {1010	/// Data for create NFT.1011	NFT(CreateNftData),1012	/// Data for create Fungible item.1013	Fungible(CreateFungibleData),1014	/// Data for create ReFungible item.1015	ReFungible(CreateReFungibleData),1016}10171018/// Extended data for create NFT.1019#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1020#[derivative(Debug)]1021pub struct CreateNftExData<CrossAccountId> {1022	/// Properties that wil be assignet to created item.1023	#[derivative(Debug(format_with = "bounded::vec_debug"))]1024	pub properties: CollectionPropertiesVec,10251026	/// Owner of creating item.1027	pub owner: CrossAccountId,1028}10291030/// Extended data for create ReFungible item.1031#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1032#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1033pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1034	#[derivative(Debug(format_with = "bounded::map_debug"))]1035	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1036	#[derivative(Debug(format_with = "bounded::vec_debug"))]1037	pub properties: CollectionPropertiesVec,1038}10391040/// Extended data for create ReFungible item.1041#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1042#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1043pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1044	pub user: CrossAccountId,1045	pub pieces: u128,1046	#[derivative(Debug(format_with = "bounded::vec_debug"))]1047	pub properties: CollectionPropertiesVec,1048}10491050/// Unified extended data for creating item.1051#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1052#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1053pub enum CreateItemExData<CrossAccountId> {1054	/// Extended data for create NFT.1055	NFT(1056		#[derivative(Debug(format_with = "bounded::vec_debug"))]1057		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1058	),10591060	/// Extended data for create Fungible item.1061	Fungible(1062		#[derivative(Debug(format_with = "bounded::map_debug"))]1063		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1064	),10651066	/// Extended data for create ReFungible item in case of1067	/// many tokens, each may have only one owner1068	RefungibleMultipleItems(1069		#[derivative(Debug(format_with = "bounded::vec_debug"))]1070		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1071	),10721073	/// Extended data for create ReFungible item in case of1074	/// single token, which may have many owners1075	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1076}10771078impl From<CreateNftData> for CreateItemData {1079	fn from(item: CreateNftData) -> Self {1080		CreateItemData::NFT(item)1081	}1082}10831084impl From<CreateReFungibleData> for CreateItemData {1085	fn from(item: CreateReFungibleData) -> Self {1086		CreateItemData::ReFungible(item)1087	}1088}10891090impl From<CreateFungibleData> for CreateItemData {1091	fn from(item: CreateFungibleData) -> Self {1092		CreateItemData::Fungible(item)1093	}1094}10951096/// Token's address, dictated by its collection and token IDs.1097#[derive(1098	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1099)]1100// todo possibly rename to be used generally as an address pair1101pub struct TokenChild {1102	/// Token id.1103	pub token: TokenId,11041105	/// Collection id.1106	pub collection: CollectionId,1107}11081109/// Collection statistics.1110#[derive(1111	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1112)]1113pub struct CollectionStats {1114	/// Number of created items.1115	pub created: u32,11161117	/// Number of burned items.1118	pub destroyed: u32,11191120	/// Number of current items.1121	pub alive: u32,1122}11231124/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1125#[derive(Encode, Decode, Clone, Debug)]1126#[cfg_attr(feature = "std", derive(PartialEq))]1127pub struct PhantomType<T>(core::marker::PhantomData<T>);11281129impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1130	type Identity = PhantomType<T>;11311132	fn type_info() -> scale_info::Type {1133		use scale_info::{1134			build::{FieldsBuilder, UnnamedFields},1135			form::MetaForm,1136			type_params, Path, Type,1137		};1138		Type::builder()1139			.path(Path::new("up_data_structs", "PhantomType"))1140			.type_params(type_params!(T))1141			.composite(1142				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1143			)1144	}1145}1146impl<T> MaxEncodedLen for PhantomType<T> {1147	fn max_encoded_len() -> usize {1148		01149	}1150}11511152/// Bounded vector of bytes.1153pub type BoundedBytes<S> = BoundedVec<u8, S>;11541155/// Extra properties for external collections.1156pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;11571158/// Property key.1159pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;11601161/// Property value.1162pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;11631164/// Property permission.1165#[derive(1166	Encode,1167	Decode,1168	TypeInfo,1169	Debug,1170	MaxEncodedLen,1171	PartialEq,1172	Clone,1173	Default,1174	Serialize,1175	Deserialize,1176)]1177pub struct PropertyPermission {1178	/// Permission to change the property and property permission.1179	///1180	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1181	pub mutable: bool,11821183	/// Change permission for the collection administrator.1184	pub collection_admin: bool,11851186	/// Permission to change the property for the owner of the token.1187	pub token_owner: bool,1188}11891190impl PropertyPermission {1191	/// Creates mutable property permission but changes restricted for collection admin and token owner.1192	pub fn none() -> Self {1193		Self {1194			mutable: true,1195			collection_admin: false,1196			token_owner: false,1197		}1198	}1199}12001201/// Property is simpl key-value record.1202#[derive(1203	Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1204)]1205pub struct Property {1206	/// Property key.1207	#[serde(with = "bounded::vec_serde")]1208	pub key: PropertyKey,12091210	/// Property value.1211	#[serde(with = "bounded::vec_serde")]1212	pub value: PropertyValue,1213}12141215impl From<Property> for (PropertyKey, PropertyValue) {1216	fn from(value: Property) -> Self {1217		(value.key, value.value)1218	}1219}12201221/// Record for proprty key permission.1222#[derive(1223	Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1224)]1225pub struct PropertyKeyPermission {1226	/// Key.1227	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1228	pub key: PropertyKey,12291230	/// Permission.1231	pub permission: PropertyPermission,1232}12331234impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1235	fn from(value: PropertyKeyPermission) -> Self {1236		(value.key, value.permission)1237	}1238}12391240/// Errors for properties actions.1241#[derive(Debug)]1242pub enum PropertiesError {1243	/// The space allocated for properties has run out.1244	///1245	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1246	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1247	NoSpaceForProperty,12481249	/// The property limit has been reached.1250	///1251	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1252	PropertyLimitReached,12531254	/// Property key contains not allowed character.1255	InvalidCharacterInPropertyKey,12561257	/// Property key length is too long.1258	///1259	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1260	PropertyKeyIsTooLong,12611262	/// Property key is empty.1263	EmptyPropertyKey,1264}12651266/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1267#[derive(Debug)]1268pub enum TokenOwnerError {1269	NotFound,1270	MultipleOwners,1271}12721273/// Marker for scope of property.1274///1275/// Scoped property can't be changed by user. Used for external collections.1276#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1277pub enum PropertyScope {1278	None,1279	Rmrk,1280}12811282impl PropertyScope {1283	pub fn prefix(&self) -> &'static [u8] {1284		match self {1285			Self::None => b"",1286			Self::Rmrk => b"rmrk:",1287		}1288	}1289	/// Apply scope to property key.1290	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1291		let prefix = self.prefix();1292		if prefix == b"" {1293			return Ok(key);1294		}1295		[prefix, key.as_slice()]1296			.concat()1297			.try_into()1298			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1299	}1300}13011302/// Trait for operate with properties.1303pub trait TrySetProperty: Sized {1304	type Value;13051306	/// Try to set property with scope.1307	fn try_scoped_set(1308		&mut self,1309		scope: PropertyScope,1310		key: PropertyKey,1311		value: Self::Value,1312	) -> Result<Option<Self::Value>, PropertiesError>;13131314	/// Try to set property with scope from iterator.1315	fn try_scoped_set_from_iter<I, KV>(1316		&mut self,1317		scope: PropertyScope,1318		iter: I,1319	) -> Result<(), PropertiesError>1320	where1321		I: Iterator<Item = KV>,1322		KV: Into<(PropertyKey, Self::Value)>,1323	{1324		for kv in iter {1325			let (key, value) = kv.into();1326			self.try_scoped_set(scope, key, value)?;1327		}13281329		Ok(())1330	}13311332	/// Try to set property.1333	fn try_set(1334		&mut self,1335		key: PropertyKey,1336		value: Self::Value,1337	) -> Result<Option<Self::Value>, PropertiesError> {1338		self.try_scoped_set(PropertyScope::None, key, value)1339	}13401341	/// Try to set property from iterator.1342	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1343	where1344		I: Iterator<Item = KV>,1345		KV: Into<(PropertyKey, Self::Value)>,1346	{1347		self.try_scoped_set_from_iter(PropertyScope::None, iter)1348	}1349}13501351/// Wrapped map for storing properties.1352#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1353#[derivative(Default(bound = ""))]1354pub struct PropertiesMap<Value>(1355	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1356);13571358impl<Value> PropertiesMap<Value> {1359	/// Create new property map.1360	pub fn new() -> Self {1361		Self(BoundedBTreeMap::new())1362	}13631364	/// Remove property from map.1365	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1366		Self::check_property_key(key)?;13671368		Ok(self.0.remove(key))1369	}13701371	/// Get property with appropriate key from map.1372	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1373		self.0.get(key)1374	}13751376	/// Check if map contains key.1377	pub fn contains_key(&self, key: &PropertyKey) -> bool {1378		self.0.contains_key(key)1379	}13801381	/// Check if map contains key with key validation.1382	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1383		if key.is_empty() {1384			return Err(PropertiesError::EmptyPropertyKey);1385		}13861387		for byte in key.as_slice().iter() {1388			let byte = *byte;13891390			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1391				return Err(PropertiesError::InvalidCharacterInPropertyKey);1392			}1393		}13941395		Ok(())1396	}13971398	pub fn values(&self) -> impl Iterator<Item = &Value> {1399		self.0.values()1400	}14011402	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1403		self.0.iter()1404	}1405}14061407impl<Value> IntoIterator for PropertiesMap<Value> {1408	type Item = (PropertyKey, Value);1409	type IntoIter = <1410		BoundedBTreeMap<1411			PropertyKey,1412			Value,1413			ConstU32<MAX_PROPERTIES_PER_ITEM>1414		> as IntoIterator1415	>::IntoIter;14161417	fn into_iter(self) -> Self::IntoIter {1418		self.0.into_iter()1419	}1420}14211422impl<Value> TrySetProperty for PropertiesMap<Value> {1423	type Value = Value;14241425	fn try_scoped_set(1426		&mut self,1427		scope: PropertyScope,1428		key: PropertyKey,1429		value: Self::Value,1430	) -> Result<Option<Self::Value>, PropertiesError> {1431		Self::check_property_key(&key)?;14321433		let key = scope.apply(key)?;1434		self.01435			.try_insert(key, value)1436			.map_err(|_| PropertiesError::PropertyLimitReached)1437	}1438}14391440/// Alias for property permissions map.1441pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14421443fn slice_size(data: &[u8]) -> u32 {1444	scoped_slice_size(PropertyScope::None, data)1445}1446fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1447	use parity_scale_codec::Compact;1448	let prefix = scope.prefix();1449	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321450		+ data.len() as u321451		+ prefix.len() as u321452}14531454/// Wrapper for properties map with consumed space control.1455#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1456pub struct Properties<const S: u32> {1457	map: PropertiesMap<PropertyValue>,1458	consumed_space: u32,1459	// May be not zero, previously served as a current S generic1460	_reserved: u32,1461}14621463impl<const S: u32> MaxEncodedLen for Properties<S> {1464	fn max_encoded_len() -> usize {1465		// len of map + len of consumed_space + len of space_limit1466		u32::max_encoded_len() * 3 + S as usize1467	}1468}14691470impl<const S: u32> Default for Properties<S> {1471	fn default() -> Self {1472		Self::new()1473	}1474}14751476impl<const S: u32> Properties<S> {1477	/// Create new properies container.1478	pub fn new() -> Self {1479		Self {1480			map: PropertiesMap::new(),1481			consumed_space: 0,1482			_reserved: 0,1483		}1484	}14851486	/// Remove propery with appropiate key.1487	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1488		let value = self.map.remove(key)?;14891490		if let Some(ref value) = value {1491			let kv_len = slice_size(key) + slice_size(value);1492			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1493		}14941495		Ok(value)1496	}14971498	/// Get property with appropriate key.1499	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1500		self.map.get(key)1501	}15021503	/// Recomputes the consumed space for the current properties state.1504	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1505	pub fn recompute_consumed_space(&mut self) {1506		self.consumed_space = self1507			.map1508			.iter()1509			.map(|(key, value)| slice_size(key) + slice_size(value))1510			.sum();1511	}1512}15131514impl<const S: u32> IntoIterator for Properties<S> {1515	type Item = (PropertyKey, PropertyValue);1516	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15171518	fn into_iter(self) -> Self::IntoIter {1519		self.map.into_iter()1520	}1521}15221523impl<const S: u32> TrySetProperty for Properties<S> {1524	type Value = PropertyValue;15251526	fn try_scoped_set(1527		&mut self,1528		scope: PropertyScope,1529		key: PropertyKey,1530		value: Self::Value,1531	) -> Result<Option<Self::Value>, PropertiesError> {1532		let key_size = scoped_slice_size(scope, &key);1533		let value_size = slice_size(&value);15341535		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1536		{1537			return Err(PropertiesError::NoSpaceForProperty);1538		}15391540		let old_value = self.map.try_scoped_set(scope, key, value)?;15411542		if let Some(old_value) = old_value.as_ref() {1543			let old_value_size = slice_size(old_value);1544			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1545		} else {1546			self.consumed_space += key_size + value_size;1547		}15481549		Ok(old_value)1550	}1551}15521553pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1554pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;