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

difftreelog

source

primitives/data-structs/src/lib.rs40.8 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 codec::{Decode, Encode, EncodeLike, MaxEncodedLen};36use bondrewd::Bitfields;37use frame_support::{BoundedVec, traits::ConstU32};38use derivative::Derivative;39use scale_info::TypeInfo;4041mod bondrewd_codec;42mod bounded;43pub mod budget;44pub mod mapping;45mod migration;4647/// Maximum of decimal points.48pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4950/// Maximum pieces for refungible token.51pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;52pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5354/// Maximum tokens for user.55pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {56	100_00057} else {58	1059};6061/// Maximum for collections can be created.62pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {63	100_00064} else {65	1066};6768/// Maximum for various custom data of token.69pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {70	204871} else {72	1073};7475/// Maximum admins per collection.76pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7778/// Maximum tokens per collection.79pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8081/// Maximum tokens per account.82pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {83	1_000_00084} else {85	1086};8788/// Default timeout for transfer sponsoring NFT item.89pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;90/// Default timeout for transfer sponsoring fungible item.91pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;92/// Default timeout for transfer sponsoring refungible item.93pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9495/// Default timeout for sponsored approving.96pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;9798// Schema limits99pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;100pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;101pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;102103// TODO: not used. Delete?104pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;105106/// Maximal length of a collection name.107pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;108109/// Maximal length of a collection description.110pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;111112/// Maximal length of a token prefix.113pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;114115/// Maximal length of a property key.116pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;117118/// Maximal length of a property value.119pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;120121/// A maximum number of token properties.122pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;123124/// Maximal lenght of extended property value.125pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;126127/// Maximum size for all collection properties.128pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;129130/// Maximum size of all token properties.131pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;132133/// How much items can be created per single134/// create_many call.135pub const MAX_ITEMS_PER_BATCH: u32 = 200;136137/// Used for limit bounded types of token custom data.138pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;139140/// Collection id.141#[derive(142	Encode,143	Decode,144	PartialEq,145	Eq,146	PartialOrd,147	Ord,148	Clone,149	Copy,150	Debug,151	Default,152	TypeInfo,153	MaxEncodedLen,154)]155#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]156pub struct CollectionId(pub u32);157impl EncodeLike<u32> for CollectionId {}158impl EncodeLike<CollectionId> for u32 {}159160impl From<u32> for CollectionId {161	fn from(value: u32) -> Self {162		Self(value)163	}164}165166/// Token id.167#[derive(168	Encode,169	Decode,170	PartialEq,171	Eq,172	PartialOrd,173	Ord,174	Clone,175	Copy,176	Debug,177	Default,178	TypeInfo,179	MaxEncodedLen,180)]181#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]182pub struct TokenId(pub u32);183impl EncodeLike<u32> for TokenId {}184impl EncodeLike<TokenId> for u32 {}185186impl TokenId {187	/// Try to get next token id.188	///189	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.190	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {191		self.0192			.checked_add(1)193			.ok_or(ArithmeticError::Overflow)194			.map(Self)195	}196}197198impl From<TokenId> for U256 {199	fn from(t: TokenId) -> Self {200		t.0.into()201	}202}203204impl TryFrom<U256> for TokenId {205	type Error = &'static str;206207	fn try_from(value: U256) -> Result<Self, Self::Error> {208		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))209	}210}211212/// Token data.213#[struct_versioning::versioned(version = 2, upper)]214#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]215#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]216pub struct TokenData<CrossAccountId> {217	/// Properties of token.218	pub properties: Vec<Property>,219220	/// Token owner.221	pub owner: Option<CrossAccountId>,222223	/// Token pieces.224	#[version(2.., upper(0))]225	pub pieces: u128,226}227228// TODO: unused type229pub struct OverflowError;230impl From<OverflowError> for &'static str {231	fn from(_: OverflowError) -> Self {232		"overflow occured"233	}234}235236/// Alias for decimal points type.237pub type DecimalPoints = u8;238239/// Collection mode.240///241/// Collection can represent various types of tokens.242/// Each collection can contain only one type of tokens at a time.243/// This type helps to understand which tokens the collection contains.244#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]245#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]246pub enum CollectionMode {247	/// Non fungible tokens.248	NFT,249	/// Fungible tokens.250	Fungible(DecimalPoints),251	/// Refungible tokens.252	ReFungible,253}254255impl CollectionMode {256	/// Get collection mod as number.257	pub fn id(&self) -> u8 {258		match self {259			CollectionMode::NFT => 1,260			CollectionMode::Fungible(_) => 2,261			CollectionMode::ReFungible => 3,262		}263	}264}265266// TODO: unused trait267pub trait SponsoringResolve<AccountId, Call> {268	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;269}270271/// Access mode for some token operations.272#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]273#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]274pub enum AccessMode {275	/// Access grant for owner and admins. Used as default.276	Normal,277	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.278	AllowList,279}280impl Default for AccessMode {281	fn default() -> Self {282		Self::Normal283	}284}285286// TODO: remove in future.287#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]288#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]289pub enum SchemaVersion {290	ImageURL,291	Unique,292}293impl Default for SchemaVersion {294	fn default() -> Self {295		Self::ImageURL296	}297}298299// TODO: unused type300#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]301#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]302pub struct Ownership<AccountId> {303	pub owner: AccountId,304	pub fraction: u128,305}306307/// The state of collection sponsorship.308#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]309#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]310pub enum SponsorshipState<AccountId> {311	/// The fees are applied to the transaction sender.312	Disabled,313	/// The sponsor is under consideration. Until the sponsor gives his consent,314	/// the fee will still be charged to sender.315	Unconfirmed(AccountId),316	/// Transactions are sponsored by specified account.317	Confirmed(AccountId),318}319320impl<AccountId> SponsorshipState<AccountId> {321	/// Get a sponsor of the collection who has confirmed his status.322	pub fn sponsor(&self) -> Option<&AccountId> {323		match self {324			Self::Confirmed(sponsor) => Some(sponsor),325			_ => None,326		}327	}328329	/// Get a sponsor of the collection who has pending or confirmed status.330	pub fn pending_sponsor(&self) -> Option<&AccountId> {331		match self {332			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),333			_ => None,334		}335	}336337	/// Whether the sponsorship is confirmed.338	pub fn confirmed(&self) -> bool {339		matches!(self, Self::Confirmed(_))340	}341}342343impl<T> Default for SponsorshipState<T> {344	fn default() -> Self {345		Self::Disabled346	}347}348349pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;350pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;351pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;352353#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]354#[bondrewd(enforce_bytes = 1)]355pub struct CollectionFlags {356	/// Tokens in foreign collections can be transferred, but not burnt357	#[bondrewd(bits = "0..1")]358	pub foreign: bool,359	/// Supports ERC721Metadata360	#[bondrewd(bits = "1..2")]361	pub erc721metadata: bool,362	/// External collections can't be managed using `unique` api363	#[bondrewd(bits = "7..8")]364	pub external: bool,365366	#[bondrewd(reserve, bits = "2..7")]367	pub reserved: u8,368}369bondrewd_codec!(CollectionFlags);370371/// Base structure for represent collection.372///373/// Used to provide basic functionality for all types of collections.374///375/// #### Note376/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).377#[struct_versioning::versioned(version = 2, upper)]378#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]379pub struct Collection<AccountId> {380	/// Collection owner account.381	pub owner: AccountId,382383	/// Collection mode.384	pub mode: CollectionMode,385386	/// Access mode.387	#[version(..2)]388	pub access: AccessMode,389390	/// Collection name.391	pub name: CollectionName,392393	/// Collection description.394	pub description: CollectionDescription,395396	/// Token prefix.397	pub token_prefix: CollectionTokenPrefix,398399	#[version(..2)]400	pub mint_mode: bool,401402	#[version(..2)]403	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,404405	#[version(..2)]406	pub schema_version: SchemaVersion,407408	/// The state of sponsorship of the collection.409	pub sponsorship: SponsorshipState<AccountId>,410411	/// Collection limits.412	pub limits: CollectionLimits,413414	/// Collection permissions.415	#[version(2.., upper(Default::default()))]416	pub permissions: CollectionPermissions,417418	#[version(2.., upper(Default::default()))]419	pub flags: CollectionFlags,420421	#[version(..2)]422	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,423424	#[version(..2)]425	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,426427	#[version(..2)]428	pub meta_update_permission: MetaUpdatePermission,429}430431#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]432#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]433pub struct RpcCollectionFlags {434	/// Is collection is foreign.435	pub foreign: bool,436	/// Collection supports ERC721Metadata.437	pub erc721metadata: bool,438}439440/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).441#[struct_versioning::versioned(version = 2, upper)]442#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]443#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]444pub struct RpcCollection<AccountId> {445	/// Collection owner account.446	pub owner: AccountId,447448	/// Collection mode.449	pub mode: CollectionMode,450451	/// Collection name.452	pub name: Vec<u16>,453454	/// Collection description.455	pub description: Vec<u16>,456457	/// Token prefix.458	pub token_prefix: Vec<u8>,459460	/// The state of sponsorship of the collection.461	pub sponsorship: SponsorshipState<AccountId>,462463	/// Collection limits.464	pub limits: CollectionLimits,465466	/// Collection permissions.467	pub permissions: CollectionPermissions,468469	/// Token property permissions.470	pub token_property_permissions: Vec<PropertyKeyPermission>,471472	/// Collection properties.473	pub properties: Vec<Property>,474475	/// Is collection read only.476	pub read_only: bool,477478	/// Extra collection flags479	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]480	pub flags: RpcCollectionFlags,481}482483impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {484	fn from(value: CollectionVersion1<AccountId>) -> Self {485		let CollectionVersion1 {486			name,487			description,488			owner,489			mode,490			access,491			token_prefix,492			mint_mode,493			sponsorship,494			limits,495			..496		} = value;497498		RpcCollection {499			name: name.into_inner(),500			description: description.into_inner(),501			owner,502			mode,503			token_prefix: token_prefix.into_inner(),504			sponsorship,505			limits,506			permissions: CollectionPermissions {507				access: Some(access),508				mint_mode: Some(mint_mode),509				nesting: None,510			},511			token_property_permissions: Vec::default(),512			properties: Vec::default(),513			read_only: true,514515			flags: RpcCollectionFlags {516				foreign: false,517				erc721metadata: false,518			},519		}520	}521}522523pub struct RawEncoded(Vec<u8>);524525impl codec::Decode for RawEncoded {526	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {527		let mut out = Vec::new();528		while let Ok(v) = input.read_byte() {529			out.push(v);530		}531		Ok(Self(out))532	}533}534535impl Deref for RawEncoded {536	type Target = Vec<u8>;537538	fn deref(&self) -> &Self::Target {539		return &self.0;540	}541}542543/// Data used for create collection.544///545/// All fields are wrapped in [`Option`], where `None` means chain default.546#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]547#[derivative(Debug, Default(bound = ""))]548pub struct CreateCollectionData<AccountId> {549	/// Collection mode.550	#[derivative(Default(value = "CollectionMode::NFT"))]551	pub mode: CollectionMode,552553	/// Access mode.554	pub access: Option<AccessMode>,555556	/// Collection name.557	pub name: CollectionName,558559	/// Collection description.560	pub description: CollectionDescription,561562	/// Token prefix.563	pub token_prefix: CollectionTokenPrefix,564565	/// Pending collection sponsor.566	pub pending_sponsor: Option<AccountId>,567568	/// Collection limits.569	pub limits: Option<CollectionLimits>,570571	/// Collection permissions.572	pub permissions: Option<CollectionPermissions>,573574	/// Token property permissions.575	pub token_property_permissions: CollectionPropertiesPermissionsVec,576577	/// Collection properties.578	pub properties: CollectionPropertiesVec,579}580581/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].582// TODO: maybe rename to PropertiesPermissionsVec583pub type CollectionPropertiesPermissionsVec =584	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;585586/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].587pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;588589/// Limits and restrictions of a collection.590///591/// All fields are wrapped in [`Option`], where `None` means chain default.592///593/// Update with `pallet_common::Pallet::clamp_limits`.594// IMPORTANT: When adding/removing fields from this struct - don't forget to also595#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]596#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]597// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.598// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.599// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.600pub struct CollectionLimits {601	/// How many tokens can a user have on one account.602	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].603	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].604	pub account_token_ownership_limit: Option<u32>,605606	/// How many bytes of data are available for sponsorship.607	/// * Default - [`CUSTOM_DATA_LIMIT`].608	/// * Limit - [`CUSTOM_DATA_LIMIT`].609	pub sponsored_data_size: Option<u32>,610611	// FIXME should we delete this or repurpose it?612	/// Times in how many blocks we sponsor data.613	///614	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.615	///616	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).617	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].618	///619	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]620	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,621	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]622623	/// How many tokens can be mined into this collection.624	///625	/// * Default - [`COLLECTION_TOKEN_LIMIT`].626	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].627	pub token_limit: Option<u32>,628629	/// Timeouts for transfer sponsoring.630	///631	/// * Default632	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]633	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]634	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]635	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].636	pub sponsor_transfer_timeout: Option<u32>,637638	/// Timeout for sponsoring an approval in passed blocks.639	///640	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].641	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].642	pub sponsor_approve_timeout: Option<u32>,643644	/// Whether the collection owner of the collection can send tokens (which belong to other users).645	///646	/// * Default - **false**.647	pub owner_can_transfer: Option<bool>,648649	/// Can the collection owner burn other people's tokens.650	///651	/// * Default - **true**.652	pub owner_can_destroy: Option<bool>,653654	/// Is it possible to send tokens from this collection between users.655	///656	/// * Default - **true**.657	pub transfers_enabled: Option<bool>,658}659660impl CollectionLimits {661	pub fn with_default_limits(collection_type: CollectionMode) -> Self {662		CollectionLimits {663			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),664			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),665			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),666			token_limit: Some(COLLECTION_TOKEN_LIMIT),667			sponsor_transfer_timeout: match collection_type {668				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),669				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),670				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),671			},672			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),673			owner_can_transfer: Some(false),674			owner_can_destroy: Some(true),675			transfers_enabled: Some(true),676		}677	}678679	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).680	pub fn account_token_ownership_limit(&self) -> u32 {681		self.account_token_ownership_limit682			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)683			.min(MAX_TOKEN_OWNERSHIP)684	}685686	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).687	pub fn sponsored_data_size(&self) -> u32 {688		self.sponsored_data_size689			.unwrap_or(CUSTOM_DATA_LIMIT)690			.min(CUSTOM_DATA_LIMIT)691	}692693	/// Get effective value for [`token_limit`](self.token_limit).694	pub fn token_limit(&self) -> u32 {695		self.token_limit696			.unwrap_or(COLLECTION_TOKEN_LIMIT)697			.min(COLLECTION_TOKEN_LIMIT)698	}699700	// TODO: may be replace u32 to mode?701	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).702	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {703		self.sponsor_transfer_timeout704			.unwrap_or(default)705			.min(MAX_SPONSOR_TIMEOUT)706	}707708	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).709	pub fn sponsor_approve_timeout(&self) -> u32 {710		self.sponsor_approve_timeout711			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)712			.min(MAX_SPONSOR_TIMEOUT)713	}714715	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).716	pub fn owner_can_transfer(&self) -> bool {717		self.owner_can_transfer.unwrap_or(false)718	}719720	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).721	pub fn owner_can_transfer_instaled(&self) -> bool {722		self.owner_can_transfer.is_some()723	}724725	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).726	pub fn owner_can_destroy(&self) -> bool {727		self.owner_can_destroy.unwrap_or(true)728	}729730	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).731	pub fn transfers_enabled(&self) -> bool {732		self.transfers_enabled.unwrap_or(true)733	}734735	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).736	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {737		match self738			.sponsored_data_rate_limit739			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)740		{741			SponsoringRateLimit::SponsoringDisabled => None,742			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),743		}744	}745}746747/// Permissions on certain operations within a collection.748///749/// Some fields are wrapped in [`Option`], where `None` means chain default.750///751/// Update with `pallet_common::Pallet::clamp_permissions`.752#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]753#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]754// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.755// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.756pub struct CollectionPermissions {757	/// Access mode.758	///759	/// * Default - [`AccessMode::Normal`].760	pub access: Option<AccessMode>,761762	/// Minting allowance.763	///764	/// * Default - **false**.765	pub mint_mode: Option<bool>,766767	/// Permissions for nesting.768	///769	/// * Default770	///   - `token_owner` - **false**771	///   - `collection_admin` - **false**772	///   - `restricted` - **None**773	pub nesting: Option<NestingPermissions>,774}775776impl CollectionPermissions {777	/// Get effective value for [`access`](self.access).778	pub fn access(&self) -> AccessMode {779		self.access.unwrap_or(AccessMode::Normal)780	}781782	/// Get effective value for [`mint_mode`](self.mint_mode).783	pub fn mint_mode(&self) -> bool {784		self.mint_mode.unwrap_or(false)785	}786787	/// Get effective value for [`nesting`](self.nesting).788	pub fn nesting(&self) -> &NestingPermissions {789		static DEFAULT: NestingPermissions = NestingPermissions {790			token_owner: false,791			collection_admin: false,792			restricted: None,793			#[cfg(feature = "runtime-benchmarks")]794			permissive: false,795		};796		self.nesting.as_ref().unwrap_or(&DEFAULT)797	}798}799800/// Inner set for collections allowed to nest.801type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;802803/// Wraper for collections set allowing nest.804#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]805#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]806#[derivative(Debug)]807pub struct OwnerRestrictedSet(808	#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]809	#[derivative(Debug(format_with = "bounded::set_debug"))]810	pub OwnerRestrictedSetInner,811);812813impl OwnerRestrictedSet {814	/// Create new set.815	pub fn new() -> Self {816		Self(Default::default())817	}818}819impl core::ops::Deref for OwnerRestrictedSet {820	type Target = OwnerRestrictedSetInner;821	fn deref(&self) -> &Self::Target {822		&self.0823	}824}825impl core::ops::DerefMut for OwnerRestrictedSet {826	fn deref_mut(&mut self) -> &mut Self::Target {827		&mut self.0828	}829}830831/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.832#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]833#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]834#[derivative(Debug)]835pub struct NestingPermissions {836	/// Owner of token can nest tokens under it.837	pub token_owner: bool,838	/// Admin of token collection can nest tokens under token.839	pub collection_admin: bool,840	/// If set - only tokens from specified collections can be nested.841	pub restricted: Option<OwnerRestrictedSet>,842843	#[cfg(feature = "runtime-benchmarks")]844	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.845	pub permissive: bool,846}847848/// Enum denominating how often can sponsoring occur if it is enabled.849///850/// Used for [`collection limits`](CollectionLimits).851#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]852#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]853pub enum SponsoringRateLimit {854	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions855	SponsoringDisabled,856	/// Once per how many blocks can sponsorship of a transaction type occur857	Blocks(u32),858}859860/// Data used to describe an NFT at creation.861#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]862#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]863#[derivative(Debug)]864pub struct CreateNftData {865	/// Key-value pairs used to describe the token as metadata866	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]867	#[derivative(Debug(format_with = "bounded::vec_debug"))]868	/// Properties that wil be assignet to created item.869	pub properties: CollectionPropertiesVec,870}871872/// Data used to describe a Fungible token at creation.873#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]874#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]875pub struct CreateFungibleData {876	/// Number of fungible coins minted877	pub value: u128,878}879880/// Data used to describe a Refungible token at creation.881#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]882#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]883#[derivative(Debug)]884pub struct CreateReFungibleData {885	/// Number of pieces the RFT is split into886	pub pieces: u128,887888	/// Key-value pairs used to describe the token as metadata889	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]890	#[derivative(Debug(format_with = "bounded::vec_debug"))]891	pub properties: CollectionPropertiesVec,892}893894// TODO: remove this.895#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]896#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]897pub enum MetaUpdatePermission {898	ItemOwner,899	Admin,900	None,901}902903/// Enum holding data used for creation of all three item types.904/// Unified data for create item.905#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]906#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]907pub enum CreateItemData {908	/// Data for create NFT.909	NFT(CreateNftData),910	/// Data for create Fungible item.911	Fungible(CreateFungibleData),912	/// Data for create ReFungible item.913	ReFungible(CreateReFungibleData),914}915916/// Extended data for create NFT.917#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]918#[derivative(Debug)]919pub struct CreateNftExData<CrossAccountId> {920	/// Properties that wil be assignet to created item.921	#[derivative(Debug(format_with = "bounded::vec_debug"))]922	pub properties: CollectionPropertiesVec,923924	/// Owner of creating item.925	pub owner: CrossAccountId,926}927928/// Extended data for create ReFungible item.929#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]930#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]931pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {932	#[derivative(Debug(format_with = "bounded::map_debug"))]933	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,934	#[derivative(Debug(format_with = "bounded::vec_debug"))]935	pub properties: CollectionPropertiesVec,936}937938/// Extended data for create ReFungible item.939#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]940#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]941pub struct CreateRefungibleExSingleOwner<CrossAccountId> {942	pub user: CrossAccountId,943	pub pieces: u128,944	#[derivative(Debug(format_with = "bounded::vec_debug"))]945	pub properties: CollectionPropertiesVec,946}947948/// Unified extended data for creating item.949#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]950#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]951pub enum CreateItemExData<CrossAccountId> {952	/// Extended data for create NFT.953	NFT(954		#[derivative(Debug(format_with = "bounded::vec_debug"))]955		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,956	),957958	/// Extended data for create Fungible item.959	Fungible(960		#[derivative(Debug(format_with = "bounded::map_debug"))]961		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,962	),963964	/// Extended data for create ReFungible item in case of965	/// many tokens, each may have only one owner966	RefungibleMultipleItems(967		#[derivative(Debug(format_with = "bounded::vec_debug"))]968		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,969	),970971	/// Extended data for create ReFungible item in case of972	/// single token, which may have many owners973	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),974}975976impl From<CreateNftData> for CreateItemData {977	fn from(item: CreateNftData) -> Self {978		CreateItemData::NFT(item)979	}980}981982impl From<CreateReFungibleData> for CreateItemData {983	fn from(item: CreateReFungibleData) -> Self {984		CreateItemData::ReFungible(item)985	}986}987988impl From<CreateFungibleData> for CreateItemData {989	fn from(item: CreateFungibleData) -> Self {990		CreateItemData::Fungible(item)991	}992}993994/// Token's address, dictated by its collection and token IDs.995#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]996#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]997// todo possibly rename to be used generally as an address pair998pub struct TokenChild {999	/// Token id.1000	pub token: TokenId,10011002	/// Collection id.1003	pub collection: CollectionId,1004}10051006/// Collection statistics.1007#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1008#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1009pub struct CollectionStats {1010	/// Number of created items.1011	pub created: u32,10121013	/// Number of burned items.1014	pub destroyed: u32,10151016	/// Number of current items.1017	pub alive: u32,1018}10191020/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1021#[derive(Encode, Decode, Clone, Debug)]1022#[cfg_attr(feature = "std", derive(PartialEq))]1023pub struct PhantomType<T>(core::marker::PhantomData<T>);10241025impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1026	type Identity = PhantomType<T>;10271028	fn type_info() -> scale_info::Type {1029		use scale_info::{1030			Type, Path,1031			build::{FieldsBuilder, UnnamedFields},1032			form::MetaForm,1033			type_params,1034		};1035		Type::builder()1036			.path(Path::new("up_data_structs", "PhantomType"))1037			.type_params(type_params!(T))1038			.composite(1039				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1040			)1041	}1042}1043impl<T> MaxEncodedLen for PhantomType<T> {1044	fn max_encoded_len() -> usize {1045		01046	}1047}10481049/// Bounded vector of bytes.1050pub type BoundedBytes<S> = BoundedVec<u8, S>;10511052/// Extra properties for external collections.1053pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;10541055/// Property key.1056pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;10571058/// Property value.1059pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;10601061/// Property permission.1062#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1063#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1064pub struct PropertyPermission {1065	/// Permission to change the property and property permission.1066	///1067	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1068	pub mutable: bool,10691070	/// Change permission for the collection administrator.1071	pub collection_admin: bool,10721073	/// Permission to change the property for the owner of the token.1074	pub token_owner: bool,1075}10761077impl PropertyPermission {1078	/// Creates mutable property permission but changes restricted for collection admin and token owner.1079	pub fn none() -> Self {1080		Self {1081			mutable: true,1082			collection_admin: false,1083			token_owner: false,1084		}1085	}1086}10871088/// Property is simpl key-value record.1089#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1090#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1091pub struct Property {1092	/// Property key.1093	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1094	pub key: PropertyKey,10951096	/// Property value.1097	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1098	pub value: PropertyValue,1099}11001101impl Into<(PropertyKey, PropertyValue)> for Property {1102	fn into(self) -> (PropertyKey, PropertyValue) {1103		(self.key, self.value)1104	}1105}11061107/// Record for proprty key permission.1108#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1109#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1110pub struct PropertyKeyPermission {1111	/// Key.1112	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1113	pub key: PropertyKey,11141115	/// Permission.1116	pub permission: PropertyPermission,1117}11181119impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1120	fn into(self) -> (PropertyKey, PropertyPermission) {1121		(self.key, self.permission)1122	}1123}11241125/// Errors for properties actions.1126#[derive(Debug)]1127pub enum PropertiesError {1128	/// The space allocated for properties has run out.1129	///1130	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1131	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1132	NoSpaceForProperty,11331134	/// The property limit has been reached.1135	///1136	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1137	PropertyLimitReached,11381139	/// Property key contains not allowed character.1140	InvalidCharacterInPropertyKey,11411142	/// Property key length is too long.1143	///1144	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1145	PropertyKeyIsTooLong,11461147	/// Property key is empty.1148	EmptyPropertyKey,1149}11501151/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1152#[derive(Debug)]1153pub enum TokenOwnerError {1154	NotFound,1155	MultipleOwners,1156}11571158/// Marker for scope of property.1159///1160/// Scoped property can't be changed by user. Used for external collections.1161#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1162pub enum PropertyScope {1163	None,1164	Rmrk,1165}11661167impl PropertyScope {1168	pub fn prefix(&self) -> &'static [u8] {1169		match self {1170			Self::None => b"",1171			Self::Rmrk => b"rmrk:",1172		}1173	}1174	/// Apply scope to property key.1175	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1176		let prefix = self.prefix();1177		if prefix == b"" {1178			return Ok(key);1179		}1180		[prefix, key.as_slice()]1181			.concat()1182			.try_into()1183			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1184	}1185}11861187/// Trait for operate with properties.1188pub trait TrySetProperty: Sized {1189	type Value;11901191	/// Try to set property with scope.1192	fn try_scoped_set(1193		&mut self,1194		scope: PropertyScope,1195		key: PropertyKey,1196		value: Self::Value,1197	) -> Result<Option<Self::Value>, PropertiesError>;11981199	/// Try to set property with scope from iterator.1200	fn try_scoped_set_from_iter<I, KV>(1201		&mut self,1202		scope: PropertyScope,1203		iter: I,1204	) -> Result<(), PropertiesError>1205	where1206		I: Iterator<Item = KV>,1207		KV: Into<(PropertyKey, Self::Value)>,1208	{1209		for kv in iter {1210			let (key, value) = kv.into();1211			self.try_scoped_set(scope, key, value)?;1212		}12131214		Ok(())1215	}12161217	/// Try to set property.1218	fn try_set(1219		&mut self,1220		key: PropertyKey,1221		value: Self::Value,1222	) -> Result<Option<Self::Value>, PropertiesError> {1223		self.try_scoped_set(PropertyScope::None, key, value)1224	}12251226	/// Try to set property from iterator.1227	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1228	where1229		I: Iterator<Item = KV>,1230		KV: Into<(PropertyKey, Self::Value)>,1231	{1232		self.try_scoped_set_from_iter(PropertyScope::None, iter)1233	}1234}12351236/// Wrapped map for storing properties.1237#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1238#[derivative(Default(bound = ""))]1239pub struct PropertiesMap<Value>(1240	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1241);12421243impl<Value> PropertiesMap<Value> {1244	/// Create new property map.1245	pub fn new() -> Self {1246		Self(BoundedBTreeMap::new())1247	}12481249	/// Remove property from map.1250	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1251		Self::check_property_key(key)?;12521253		Ok(self.0.remove(key))1254	}12551256	/// Get property with appropriate key from map.1257	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1258		self.0.get(key)1259	}12601261	/// Check if map contains key.1262	pub fn contains_key(&self, key: &PropertyKey) -> bool {1263		self.0.contains_key(key)1264	}12651266	/// Check if map contains key with key validation.1267	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1268		if key.is_empty() {1269			return Err(PropertiesError::EmptyPropertyKey);1270		}12711272		for byte in key.as_slice().iter() {1273			let byte = *byte;12741275			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1276				return Err(PropertiesError::InvalidCharacterInPropertyKey);1277			}1278		}12791280		Ok(())1281	}12821283	pub fn values(&self) -> impl Iterator<Item = &Value> {1284		self.0.values()1285	}12861287	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1288		self.0.iter()1289	}1290}12911292impl<Value> IntoIterator for PropertiesMap<Value> {1293	type Item = (PropertyKey, Value);1294	type IntoIter = <1295		BoundedBTreeMap<1296			PropertyKey,1297			Value,1298			ConstU32<MAX_PROPERTIES_PER_ITEM>1299		> as IntoIterator1300	>::IntoIter;13011302	fn into_iter(self) -> Self::IntoIter {1303		self.0.into_iter()1304	}1305}13061307impl<Value> TrySetProperty for PropertiesMap<Value> {1308	type Value = Value;13091310	fn try_scoped_set(1311		&mut self,1312		scope: PropertyScope,1313		key: PropertyKey,1314		value: Self::Value,1315	) -> Result<Option<Self::Value>, PropertiesError> {1316		Self::check_property_key(&key)?;13171318		let key = scope.apply(key)?;1319		self.01320			.try_insert(key, value)1321			.map_err(|_| PropertiesError::PropertyLimitReached)1322	}1323}13241325/// Alias for property permissions map.1326pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13271328fn slice_size(data: &[u8]) -> u32 {1329	scoped_slice_size(PropertyScope::None, data)1330}1331fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1332	use codec::Compact;1333	let prefix = scope.prefix();1334	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321335		+ data.len() as u321336		+ prefix.len() as u321337}13381339/// Wrapper for properties map with consumed space control.1340#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1341pub struct Properties<const S: u32> {1342	map: PropertiesMap<PropertyValue>,1343	consumed_space: u32,1344	// May be not zero, previously served as a current S generic1345	_reserved: u32,1346}13471348impl<const S: u32> MaxEncodedLen for Properties<S> {1349	fn max_encoded_len() -> usize {1350		// len of map + len of consumed_space + len of space_limit1351		u32::max_encoded_len() * 3 + S as usize1352	}1353}13541355impl<const S: u32> Default for Properties<S> {1356	fn default() -> Self {1357		Self::new()1358	}1359}13601361impl<const S: u32> Properties<S> {1362	/// Create new properies container.1363	pub fn new() -> Self {1364		Self {1365			map: PropertiesMap::new(),1366			consumed_space: 0,1367			_reserved: 0,1368		}1369	}13701371	/// Remove propery with appropiate key.1372	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1373		let value = self.map.remove(key)?;13741375		if let Some(ref value) = value {1376			let kv_len = slice_size(key) + slice_size(value);1377			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1378		}13791380		Ok(value)1381	}13821383	/// Get property with appropriate key.1384	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1385		self.map.get(key)1386	}13871388	/// Recomputes the consumed space for the current properties state.1389	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1390	pub fn recompute_consumed_space(&mut self) {1391		self.consumed_space = self1392			.map1393			.iter()1394			.map(|(key, value)| slice_size(key) + slice_size(value))1395			.sum();1396	}1397}13981399impl<const S: u32> IntoIterator for Properties<S> {1400	type Item = (PropertyKey, PropertyValue);1401	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;14021403	fn into_iter(self) -> Self::IntoIter {1404		self.map.into_iter()1405	}1406}14071408impl<const S: u32> TrySetProperty for Properties<S> {1409	type Value = PropertyValue;14101411	fn try_scoped_set(1412		&mut self,1413		scope: PropertyScope,1414		key: PropertyKey,1415		value: Self::Value,1416	) -> Result<Option<Self::Value>, PropertiesError> {1417		let key_size = scoped_slice_size(scope, &key);1418		let value_size = slice_size(&value) as u32;14191420		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1421		{1422			return Err(PropertiesError::NoSpaceForProperty);1423		}14241425		let old_value = self.map.try_scoped_set(scope, key, value)?;14261427		if let Some(old_value) = old_value.as_ref() {1428			let old_value_size = slice_size(&old_value);1429			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1430		} else {1431			self.consumed_space += key_size + value_size;1432		}14331434		Ok(old_value)1435	}1436}14371438pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1439pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;