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

difftreelog

source

primitives/data-structs/src/lib.rs41.4 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)]157#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]158pub struct CollectionId(pub u32);159impl EncodeLike<u32> for CollectionId {}160impl EncodeLike<CollectionId> for u32 {}161162impl From<u32> for CollectionId {163	fn from(value: u32) -> Self {164		Self(value)165	}166}167168impl Deref for CollectionId {169	type Target = u32;170171	fn deref(&self) -> &Self::Target {172		&self.0173	}174}175176/// Token id.177#[derive(178	Encode,179	Decode,180	PartialEq,181	Eq,182	PartialOrd,183	Ord,184	Clone,185	Copy,186	Debug,187	Default,188	TypeInfo,189	MaxEncodedLen,190)]191#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]192pub struct TokenId(pub u32);193impl EncodeLike<u32> for TokenId {}194impl EncodeLike<TokenId> for u32 {}195196impl TokenId {197	/// Try to get next token id.198	///199	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.200	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {201		self.0202			.checked_add(1)203			.ok_or(ArithmeticError::Overflow)204			.map(Self)205	}206}207208impl From<TokenId> for U256 {209	fn from(t: TokenId) -> Self {210		t.0.into()211	}212}213214impl TryFrom<U256> for TokenId {215	type Error = &'static str;216217	fn try_from(value: U256) -> Result<Self, Self::Error> {218		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))219	}220}221222/// Token data.223#[struct_versioning::versioned(version = 2, upper)]224#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]225#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]226pub struct TokenData<CrossAccountId> {227	/// Properties of token.228	pub properties: Vec<Property>,229230	/// Token owner.231	pub owner: Option<CrossAccountId>,232233	/// Token pieces.234	#[version(2.., upper(0))]235	pub pieces: u128,236}237238// TODO: unused type239pub struct OverflowError;240impl From<OverflowError> for &'static str {241	fn from(_: OverflowError) -> Self {242		"overflow occured"243	}244}245246/// Alias for decimal points type.247pub type DecimalPoints = u8;248249/// Collection mode.250///251/// Collection can represent various types of tokens.252/// Each collection can contain only one type of tokens at a time.253/// This type helps to understand which tokens the collection contains.254#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]255#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]256pub enum CollectionMode {257	/// Non fungible tokens.258	NFT,259	/// Fungible tokens.260	Fungible(DecimalPoints),261	/// Refungible tokens.262	ReFungible,263}264265impl CollectionMode {266	/// Get collection mod as number.267	pub fn id(&self) -> u8 {268		match self {269			CollectionMode::NFT => 1,270			CollectionMode::Fungible(_) => 2,271			CollectionMode::ReFungible => 3,272		}273	}274}275276// TODO: unused trait277pub trait SponsoringResolve<AccountId, Call> {278	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;279}280281/// Access mode for some token operations.282#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]283#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]284pub enum AccessMode {285	/// Access grant for owner and admins. Used as default.286	Normal,287	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.288	AllowList,289}290impl Default for AccessMode {291	fn default() -> Self {292		Self::Normal293	}294}295296// TODO: remove in future.297#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]298#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]299pub enum SchemaVersion {300	ImageURL,301	Unique,302}303impl Default for SchemaVersion {304	fn default() -> Self {305		Self::ImageURL306	}307}308309// TODO: unused type310#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]311#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]312pub struct Ownership<AccountId> {313	pub owner: AccountId,314	pub fraction: u128,315}316317/// The state of collection sponsorship.318#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]319#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]320pub enum SponsorshipState<AccountId> {321	/// The fees are applied to the transaction sender.322	Disabled,323	/// The sponsor is under consideration. Until the sponsor gives his consent,324	/// the fee will still be charged to sender.325	Unconfirmed(AccountId),326	/// Transactions are sponsored by specified account.327	Confirmed(AccountId),328}329330impl<AccountId> SponsorshipState<AccountId> {331	/// Get a sponsor of the collection who has confirmed his status.332	pub fn sponsor(&self) -> Option<&AccountId> {333		match self {334			Self::Confirmed(sponsor) => Some(sponsor),335			_ => None,336		}337	}338339	/// Get a sponsor of the collection who has pending or confirmed status.340	pub fn pending_sponsor(&self) -> Option<&AccountId> {341		match self {342			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),343			_ => None,344		}345	}346347	/// Whether the sponsorship is confirmed.348	pub fn confirmed(&self) -> bool {349		matches!(self, Self::Confirmed(_))350	}351}352353impl<T> Default for SponsorshipState<T> {354	fn default() -> Self {355		Self::Disabled356	}357}358359pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;360pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;361pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;362363#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]364#[bondrewd(enforce_bytes = 1)]365pub struct CollectionFlags {366	/// Tokens in foreign collections can be transferred, but not burnt367	#[bondrewd(bits = "0..1")]368	pub foreign: bool,369	/// Supports ERC721Metadata370	#[bondrewd(bits = "1..2")]371	pub erc721metadata: bool,372	/// External collections can't be managed using `unique` api373	#[bondrewd(bits = "7..8")]374	pub external: bool,375	/// Reserved flags376	#[bondrewd(bits = "2..7")]377	pub reserved: u8,378}379bondrewd_codec!(CollectionFlags);380381impl CollectionFlags {382	pub fn is_allowed_for_user(self) -> bool {383		!self.foreign && !self.external && self.reserved == 0384	}385}386387/// Base structure for represent collection.388///389/// Used to provide basic functionality for all types of collections.390///391/// #### Note392/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).393#[struct_versioning::versioned(version = 2, upper)]394#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]395pub struct Collection<AccountId> {396	/// Collection owner account.397	pub owner: AccountId,398399	/// Collection mode.400	pub mode: CollectionMode,401402	/// Access mode.403	#[version(..2)]404	pub access: AccessMode,405406	/// Collection name.407	pub name: CollectionName,408409	/// Collection description.410	pub description: CollectionDescription,411412	/// Token prefix.413	pub token_prefix: CollectionTokenPrefix,414415	#[version(..2)]416	pub mint_mode: bool,417418	#[version(..2)]419	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,420421	#[version(..2)]422	pub schema_version: SchemaVersion,423424	/// The state of sponsorship of the collection.425	pub sponsorship: SponsorshipState<AccountId>,426427	/// Collection limits.428	pub limits: CollectionLimits,429430	/// Collection permissions.431	#[version(2.., upper(Default::default()))]432	pub permissions: CollectionPermissions,433434	#[version(2.., upper(Default::default()))]435	pub flags: CollectionFlags,436437	#[version(..2)]438	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,439440	#[version(..2)]441	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,442443	#[version(..2)]444	pub meta_update_permission: MetaUpdatePermission,445}446447#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]448#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]449pub struct RpcCollectionFlags {450	/// Is collection is foreign.451	pub foreign: bool,452	/// Collection supports ERC721Metadata.453	pub erc721metadata: bool,454}455456/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).457#[struct_versioning::versioned(version = 2, upper)]458#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]459#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]460pub struct RpcCollection<AccountId> {461	/// Collection owner account.462	pub owner: AccountId,463464	/// Collection mode.465	pub mode: CollectionMode,466467	/// Collection name.468	pub name: Vec<u16>,469470	/// Collection description.471	pub description: Vec<u16>,472473	/// Token prefix.474	pub token_prefix: Vec<u8>,475476	/// The state of sponsorship of the collection.477	pub sponsorship: SponsorshipState<AccountId>,478479	/// Collection limits.480	pub limits: CollectionLimits,481482	/// Collection permissions.483	pub permissions: CollectionPermissions,484485	/// Token property permissions.486	pub token_property_permissions: Vec<PropertyKeyPermission>,487488	/// Collection properties.489	pub properties: Vec<Property>,490491	/// Is collection read only.492	pub read_only: bool,493494	/// Extra collection flags495	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]496	pub flags: RpcCollectionFlags,497}498499impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {500	fn from(value: CollectionVersion1<AccountId>) -> Self {501		let CollectionVersion1 {502			name,503			description,504			owner,505			mode,506			access,507			token_prefix,508			mint_mode,509			sponsorship,510			limits,511			..512		} = value;513514		RpcCollection {515			name: name.into_inner(),516			description: description.into_inner(),517			owner,518			mode,519			token_prefix: token_prefix.into_inner(),520			sponsorship,521			limits,522			permissions: CollectionPermissions {523				access: Some(access),524				mint_mode: Some(mint_mode),525				nesting: None,526			},527			token_property_permissions: Vec::default(),528			properties: Vec::default(),529			read_only: true,530531			flags: RpcCollectionFlags {532				foreign: false,533				erc721metadata: false,534			},535		}536	}537}538539pub struct RawEncoded(Vec<u8>);540541impl codec::Decode for RawEncoded {542	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {543		let mut out = Vec::new();544		while let Ok(v) = input.read_byte() {545			out.push(v);546		}547		Ok(Self(out))548	}549}550551impl Deref for RawEncoded {552	type Target = Vec<u8>;553554	fn deref(&self) -> &Self::Target {555		&self.0556	}557}558559/// Data used for create collection.560///561/// All fields are wrapped in [`Option`], where `None` means chain default.562#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]563#[derivative(Debug, Default(bound = ""))]564pub struct CreateCollectionData<CrossAccountId> {565	/// Collection mode.566	#[derivative(Default(value = "CollectionMode::NFT"))]567	pub mode: CollectionMode,568569	/// Access mode.570	pub access: Option<AccessMode>,571572	/// Collection name.573	pub name: CollectionName,574575	/// Collection description.576	pub description: CollectionDescription,577578	/// Token prefix.579	pub token_prefix: CollectionTokenPrefix,580581	/// Collection limits.582	pub limits: Option<CollectionLimits>,583584	/// Collection permissions.585	pub permissions: Option<CollectionPermissions>,586587	/// Token property permissions.588	pub token_property_permissions: CollectionPropertiesPermissionsVec,589590	/// Collection properties.591	pub properties: CollectionPropertiesVec,592593	pub admin_list: Vec<CrossAccountId>,594595	/// Pending collection sponsor.596	pub pending_sponsor: Option<CrossAccountId>,597598	pub flags: CollectionFlags,599}600601/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].602// TODO: maybe rename to PropertiesPermissionsVec603pub type CollectionPropertiesPermissionsVec =604	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;605606/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].607pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;608609/// Limits and restrictions of a collection.610///611/// All fields are wrapped in [`Option`], where `None` means chain default.612///613/// Update with `pallet_common::Pallet::clamp_limits`.614// IMPORTANT: When adding/removing fields from this struct - don't forget to also615#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]616#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]617// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.618// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.619// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.620pub struct CollectionLimits {621	/// How many tokens can a user have on one account.622	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].623	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].624	pub account_token_ownership_limit: Option<u32>,625626	/// How many bytes of data are available for sponsorship.627	/// * Default - [`CUSTOM_DATA_LIMIT`].628	/// * Limit - [`CUSTOM_DATA_LIMIT`].629	pub sponsored_data_size: Option<u32>,630631	// FIXME should we delete this or repurpose it?632	/// Times in how many blocks we sponsor data.633	///634	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.635	///636	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).637	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].638	///639	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]640	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,641	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]642643	/// How many tokens can be mined into this collection.644	///645	/// * Default - [`COLLECTION_TOKEN_LIMIT`].646	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].647	pub token_limit: Option<u32>,648649	/// Timeouts for transfer sponsoring.650	///651	/// * Default652	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]653	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]654	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]655	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].656	pub sponsor_transfer_timeout: Option<u32>,657658	/// Timeout for sponsoring an approval in passed blocks.659	///660	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].661	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].662	pub sponsor_approve_timeout: Option<u32>,663664	/// Whether the collection owner of the collection can send tokens (which belong to other users).665	///666	/// * Default - **false**.667	pub owner_can_transfer: Option<bool>,668669	/// Can the collection owner burn other people's tokens.670	///671	/// * Default - **true**.672	pub owner_can_destroy: Option<bool>,673674	/// Is it possible to send tokens from this collection between users.675	///676	/// * Default - **true**.677	pub transfers_enabled: Option<bool>,678}679680impl CollectionLimits {681	pub fn with_default_limits(collection_type: CollectionMode) -> Self {682		CollectionLimits {683			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),684			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),685			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),686			token_limit: Some(COLLECTION_TOKEN_LIMIT),687			sponsor_transfer_timeout: match collection_type {688				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),689				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),690				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),691			},692			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),693			owner_can_transfer: Some(false),694			owner_can_destroy: Some(true),695			transfers_enabled: Some(true),696		}697	}698699	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).700	pub fn account_token_ownership_limit(&self) -> u32 {701		self.account_token_ownership_limit702			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)703			.min(MAX_TOKEN_OWNERSHIP)704	}705706	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).707	pub fn sponsored_data_size(&self) -> u32 {708		self.sponsored_data_size709			.unwrap_or(CUSTOM_DATA_LIMIT)710			.min(CUSTOM_DATA_LIMIT)711	}712713	/// Get effective value for [`token_limit`](self.token_limit).714	pub fn token_limit(&self) -> u32 {715		self.token_limit716			.unwrap_or(COLLECTION_TOKEN_LIMIT)717			.min(COLLECTION_TOKEN_LIMIT)718	}719720	// TODO: may be replace u32 to mode?721	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).722	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {723		self.sponsor_transfer_timeout724			.unwrap_or(default)725			.min(MAX_SPONSOR_TIMEOUT)726	}727728	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).729	pub fn sponsor_approve_timeout(&self) -> u32 {730		self.sponsor_approve_timeout731			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)732			.min(MAX_SPONSOR_TIMEOUT)733	}734735	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).736	pub fn owner_can_transfer(&self) -> bool {737		self.owner_can_transfer.unwrap_or(false)738	}739740	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).741	pub fn owner_can_transfer_instaled(&self) -> bool {742		self.owner_can_transfer.is_some()743	}744745	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).746	pub fn owner_can_destroy(&self) -> bool {747		self.owner_can_destroy.unwrap_or(true)748	}749750	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).751	pub fn transfers_enabled(&self) -> bool {752		self.transfers_enabled.unwrap_or(true)753	}754755	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).756	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {757		match self758			.sponsored_data_rate_limit759			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)760		{761			SponsoringRateLimit::SponsoringDisabled => None,762			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),763		}764	}765}766767/// Permissions on certain operations within a collection.768///769/// Some fields are wrapped in [`Option`], where `None` means chain default.770///771/// Update with `pallet_common::Pallet::clamp_permissions`.772#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]773#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]774// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.775// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.776pub struct CollectionPermissions {777	/// Access mode.778	///779	/// * Default - [`AccessMode::Normal`].780	pub access: Option<AccessMode>,781782	/// Minting allowance.783	///784	/// * Default - **false**.785	pub mint_mode: Option<bool>,786787	/// Permissions for nesting.788	///789	/// * Default790	///   - `token_owner` - **false**791	///   - `collection_admin` - **false**792	///   - `restricted` - **None**793	pub nesting: Option<NestingPermissions>,794}795796impl CollectionPermissions {797	/// Get effective value for [`access`](self.access).798	pub fn access(&self) -> AccessMode {799		self.access.unwrap_or(AccessMode::Normal)800	}801802	/// Get effective value for [`mint_mode`](self.mint_mode).803	pub fn mint_mode(&self) -> bool {804		self.mint_mode.unwrap_or(false)805	}806807	/// Get effective value for [`nesting`](self.nesting).808	pub fn nesting(&self) -> &NestingPermissions {809		static DEFAULT: NestingPermissions = NestingPermissions {810			token_owner: false,811			collection_admin: false,812			restricted: None,813			#[cfg(feature = "runtime-benchmarks")]814			permissive: false,815		};816		self.nesting.as_ref().unwrap_or(&DEFAULT)817	}818}819820/// Inner set for collections allowed to nest.821type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;822823/// Wraper for collections set allowing nest.824#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]825#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]826#[derivative(Debug)]827pub struct OwnerRestrictedSet(828	#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]829	#[derivative(Debug(format_with = "bounded::set_debug"))]830	pub OwnerRestrictedSetInner,831);832833impl OwnerRestrictedSet {834	/// Create new set.835	pub fn new() -> Self {836		Self(Default::default())837	}838}839impl Default for OwnerRestrictedSet {840	fn default() -> Self {841		Self::new()842	}843}844impl core::ops::Deref for OwnerRestrictedSet {845	type Target = OwnerRestrictedSetInner;846	fn deref(&self) -> &Self::Target {847		&self.0848	}849}850impl core::ops::DerefMut for OwnerRestrictedSet {851	fn deref_mut(&mut self) -> &mut Self::Target {852		&mut self.0853	}854}855856impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {857	type Error = ();858859	fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {860		Ok(Self(value.try_into()?))861	}862}863864/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.865#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]866#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]867#[derivative(Debug)]868pub struct NestingPermissions {869	/// Owner of token can nest tokens under it.870	pub token_owner: bool,871	/// Admin of token collection can nest tokens under token.872	pub collection_admin: bool,873	/// If set - only tokens from specified collections can be nested.874	pub restricted: Option<OwnerRestrictedSet>,875876	#[cfg(feature = "runtime-benchmarks")]877	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.878	pub permissive: bool,879}880881/// Enum denominating how often can sponsoring occur if it is enabled.882///883/// Used for [`collection limits`](CollectionLimits).884#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]885#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]886pub enum SponsoringRateLimit {887	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions888	SponsoringDisabled,889	/// Once per how many blocks can sponsorship of a transaction type occur890	Blocks(u32),891}892893/// Data used to describe an NFT at creation.894#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]895#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]896#[derivative(Debug)]897pub struct CreateNftData {898	/// Key-value pairs used to describe the token as metadata899	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]900	#[derivative(Debug(format_with = "bounded::vec_debug"))]901	/// Properties that wil be assignet to created item.902	pub properties: CollectionPropertiesVec,903}904905/// Data used to describe a Fungible token at creation.906#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]907#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]908pub struct CreateFungibleData {909	/// Number of fungible coins minted910	pub value: u128,911}912913/// Data used to describe a Refungible token at creation.914#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]915#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]916#[derivative(Debug)]917pub struct CreateReFungibleData {918	/// Number of pieces the RFT is split into919	pub pieces: u128,920921	/// Key-value pairs used to describe the token as metadata922	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]923	#[derivative(Debug(format_with = "bounded::vec_debug"))]924	pub properties: CollectionPropertiesVec,925}926927// TODO: remove this.928#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]929#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]930pub enum MetaUpdatePermission {931	ItemOwner,932	Admin,933	None,934}935936/// Enum holding data used for creation of all three item types.937/// Unified data for create item.938#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]939#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]940pub enum CreateItemData {941	/// Data for create NFT.942	NFT(CreateNftData),943	/// Data for create Fungible item.944	Fungible(CreateFungibleData),945	/// Data for create ReFungible item.946	ReFungible(CreateReFungibleData),947}948949/// Extended data for create NFT.950#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]951#[derivative(Debug)]952pub struct CreateNftExData<CrossAccountId> {953	/// Properties that wil be assignet to created item.954	#[derivative(Debug(format_with = "bounded::vec_debug"))]955	pub properties: CollectionPropertiesVec,956957	/// Owner of creating item.958	pub owner: CrossAccountId,959}960961/// Extended data for create ReFungible item.962#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]963#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]964pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {965	#[derivative(Debug(format_with = "bounded::map_debug"))]966	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,967	#[derivative(Debug(format_with = "bounded::vec_debug"))]968	pub properties: CollectionPropertiesVec,969}970971/// Extended data for create ReFungible item.972#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]973#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]974pub struct CreateRefungibleExSingleOwner<CrossAccountId> {975	pub user: CrossAccountId,976	pub pieces: u128,977	#[derivative(Debug(format_with = "bounded::vec_debug"))]978	pub properties: CollectionPropertiesVec,979}980981/// Unified extended data for creating item.982#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]983#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]984pub enum CreateItemExData<CrossAccountId> {985	/// Extended data for create NFT.986	NFT(987		#[derivative(Debug(format_with = "bounded::vec_debug"))]988		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,989	),990991	/// Extended data for create Fungible item.992	Fungible(993		#[derivative(Debug(format_with = "bounded::map_debug"))]994		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,995	),996997	/// Extended data for create ReFungible item in case of998	/// many tokens, each may have only one owner999	RefungibleMultipleItems(1000		#[derivative(Debug(format_with = "bounded::vec_debug"))]1001		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1002	),10031004	/// Extended data for create ReFungible item in case of1005	/// single token, which may have many owners1006	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1007}10081009impl From<CreateNftData> for CreateItemData {1010	fn from(item: CreateNftData) -> Self {1011		CreateItemData::NFT(item)1012	}1013}10141015impl From<CreateReFungibleData> for CreateItemData {1016	fn from(item: CreateReFungibleData) -> Self {1017		CreateItemData::ReFungible(item)1018	}1019}10201021impl From<CreateFungibleData> for CreateItemData {1022	fn from(item: CreateFungibleData) -> Self {1023		CreateItemData::Fungible(item)1024	}1025}10261027/// Token's address, dictated by its collection and token IDs.1028#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1029#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1030// todo possibly rename to be used generally as an address pair1031pub struct TokenChild {1032	/// Token id.1033	pub token: TokenId,10341035	/// Collection id.1036	pub collection: CollectionId,1037}10381039/// Collection statistics.1040#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1041#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1042pub struct CollectionStats {1043	/// Number of created items.1044	pub created: u32,10451046	/// Number of burned items.1047	pub destroyed: u32,10481049	/// Number of current items.1050	pub alive: u32,1051}10521053/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1054#[derive(Encode, Decode, Clone, Debug)]1055#[cfg_attr(feature = "std", derive(PartialEq))]1056pub struct PhantomType<T>(core::marker::PhantomData<T>);10571058impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1059	type Identity = PhantomType<T>;10601061	fn type_info() -> scale_info::Type {1062		use scale_info::{1063			Type, Path,1064			build::{FieldsBuilder, UnnamedFields},1065			form::MetaForm,1066			type_params,1067		};1068		Type::builder()1069			.path(Path::new("up_data_structs", "PhantomType"))1070			.type_params(type_params!(T))1071			.composite(1072				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1073			)1074	}1075}1076impl<T> MaxEncodedLen for PhantomType<T> {1077	fn max_encoded_len() -> usize {1078		01079	}1080}10811082/// Bounded vector of bytes.1083pub type BoundedBytes<S> = BoundedVec<u8, S>;10841085/// Extra properties for external collections.1086pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;10871088/// Property key.1089pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;10901091/// Property value.1092pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;10931094/// Property permission.1095#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1096#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1097pub struct PropertyPermission {1098	/// Permission to change the property and property permission.1099	///1100	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1101	pub mutable: bool,11021103	/// Change permission for the collection administrator.1104	pub collection_admin: bool,11051106	/// Permission to change the property for the owner of the token.1107	pub token_owner: bool,1108}11091110impl PropertyPermission {1111	/// Creates mutable property permission but changes restricted for collection admin and token owner.1112	pub fn none() -> Self {1113		Self {1114			mutable: true,1115			collection_admin: false,1116			token_owner: false,1117		}1118	}1119}11201121/// Property is simpl key-value record.1122#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1123#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1124pub struct Property {1125	/// Property key.1126	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1127	pub key: PropertyKey,11281129	/// Property value.1130	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1131	pub value: PropertyValue,1132}11331134impl From<Property> for (PropertyKey, PropertyValue) {1135	fn from(value: Property) -> Self {1136		(value.key, value.value)1137	}1138}11391140/// Record for proprty key permission.1141#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1142#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1143pub struct PropertyKeyPermission {1144	/// Key.1145	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1146	pub key: PropertyKey,11471148	/// Permission.1149	pub permission: PropertyPermission,1150}11511152impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1153	fn from(value: PropertyKeyPermission) -> Self {1154		(value.key, value.permission)1155	}1156}11571158/// Errors for properties actions.1159#[derive(Debug)]1160pub enum PropertiesError {1161	/// The space allocated for properties has run out.1162	///1163	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1164	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1165	NoSpaceForProperty,11661167	/// The property limit has been reached.1168	///1169	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1170	PropertyLimitReached,11711172	/// Property key contains not allowed character.1173	InvalidCharacterInPropertyKey,11741175	/// Property key length is too long.1176	///1177	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1178	PropertyKeyIsTooLong,11791180	/// Property key is empty.1181	EmptyPropertyKey,1182}11831184/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1185#[derive(Debug)]1186pub enum TokenOwnerError {1187	NotFound,1188	MultipleOwners,1189}11901191/// Marker for scope of property.1192///1193/// Scoped property can't be changed by user. Used for external collections.1194#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1195pub enum PropertyScope {1196	None,1197	Rmrk,1198}11991200impl PropertyScope {1201	pub fn prefix(&self) -> &'static [u8] {1202		match self {1203			Self::None => b"",1204			Self::Rmrk => b"rmrk:",1205		}1206	}1207	/// Apply scope to property key.1208	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1209		let prefix = self.prefix();1210		if prefix == b"" {1211			return Ok(key);1212		}1213		[prefix, key.as_slice()]1214			.concat()1215			.try_into()1216			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1217	}1218}12191220/// Trait for operate with properties.1221pub trait TrySetProperty: Sized {1222	type Value;12231224	/// Try to set property with scope.1225	fn try_scoped_set(1226		&mut self,1227		scope: PropertyScope,1228		key: PropertyKey,1229		value: Self::Value,1230	) -> Result<Option<Self::Value>, PropertiesError>;12311232	/// Try to set property with scope from iterator.1233	fn try_scoped_set_from_iter<I, KV>(1234		&mut self,1235		scope: PropertyScope,1236		iter: I,1237	) -> Result<(), PropertiesError>1238	where1239		I: Iterator<Item = KV>,1240		KV: Into<(PropertyKey, Self::Value)>,1241	{1242		for kv in iter {1243			let (key, value) = kv.into();1244			self.try_scoped_set(scope, key, value)?;1245		}12461247		Ok(())1248	}12491250	/// Try to set property.1251	fn try_set(1252		&mut self,1253		key: PropertyKey,1254		value: Self::Value,1255	) -> Result<Option<Self::Value>, PropertiesError> {1256		self.try_scoped_set(PropertyScope::None, key, value)1257	}12581259	/// Try to set property from iterator.1260	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1261	where1262		I: Iterator<Item = KV>,1263		KV: Into<(PropertyKey, Self::Value)>,1264	{1265		self.try_scoped_set_from_iter(PropertyScope::None, iter)1266	}1267}12681269/// Wrapped map for storing properties.1270#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1271#[derivative(Default(bound = ""))]1272pub struct PropertiesMap<Value>(1273	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1274);12751276impl<Value> PropertiesMap<Value> {1277	/// Create new property map.1278	pub fn new() -> Self {1279		Self(BoundedBTreeMap::new())1280	}12811282	/// Remove property from map.1283	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1284		Self::check_property_key(key)?;12851286		Ok(self.0.remove(key))1287	}12881289	/// Get property with appropriate key from map.1290	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1291		self.0.get(key)1292	}12931294	/// Check if map contains key.1295	pub fn contains_key(&self, key: &PropertyKey) -> bool {1296		self.0.contains_key(key)1297	}12981299	/// Check if map contains key with key validation.1300	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1301		if key.is_empty() {1302			return Err(PropertiesError::EmptyPropertyKey);1303		}13041305		for byte in key.as_slice().iter() {1306			let byte = *byte;13071308			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1309				return Err(PropertiesError::InvalidCharacterInPropertyKey);1310			}1311		}13121313		Ok(())1314	}13151316	pub fn values(&self) -> impl Iterator<Item = &Value> {1317		self.0.values()1318	}13191320	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1321		self.0.iter()1322	}1323}13241325impl<Value> IntoIterator for PropertiesMap<Value> {1326	type Item = (PropertyKey, Value);1327	type IntoIter = <1328		BoundedBTreeMap<1329			PropertyKey,1330			Value,1331			ConstU32<MAX_PROPERTIES_PER_ITEM>1332		> as IntoIterator1333	>::IntoIter;13341335	fn into_iter(self) -> Self::IntoIter {1336		self.0.into_iter()1337	}1338}13391340impl<Value> TrySetProperty for PropertiesMap<Value> {1341	type Value = Value;13421343	fn try_scoped_set(1344		&mut self,1345		scope: PropertyScope,1346		key: PropertyKey,1347		value: Self::Value,1348	) -> Result<Option<Self::Value>, PropertiesError> {1349		Self::check_property_key(&key)?;13501351		let key = scope.apply(key)?;1352		self.01353			.try_insert(key, value)1354			.map_err(|_| PropertiesError::PropertyLimitReached)1355	}1356}13571358/// Alias for property permissions map.1359pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13601361fn slice_size(data: &[u8]) -> u32 {1362	scoped_slice_size(PropertyScope::None, data)1363}1364fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1365	use codec::Compact;1366	let prefix = scope.prefix();1367	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321368		+ data.len() as u321369		+ prefix.len() as u321370}13711372/// Wrapper for properties map with consumed space control.1373#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1374pub struct Properties<const S: u32> {1375	map: PropertiesMap<PropertyValue>,1376	consumed_space: u32,1377	// May be not zero, previously served as a current S generic1378	_reserved: u32,1379}13801381impl<const S: u32> MaxEncodedLen for Properties<S> {1382	fn max_encoded_len() -> usize {1383		// len of map + len of consumed_space + len of space_limit1384		u32::max_encoded_len() * 3 + S as usize1385	}1386}13871388impl<const S: u32> Default for Properties<S> {1389	fn default() -> Self {1390		Self::new()1391	}1392}13931394impl<const S: u32> Properties<S> {1395	/// Create new properies container.1396	pub fn new() -> Self {1397		Self {1398			map: PropertiesMap::new(),1399			consumed_space: 0,1400			_reserved: 0,1401		}1402	}14031404	/// Remove propery with appropiate key.1405	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1406		let value = self.map.remove(key)?;14071408		if let Some(ref value) = value {1409			let kv_len = slice_size(key) + slice_size(value);1410			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1411		}14121413		Ok(value)1414	}14151416	/// Get property with appropriate key.1417	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1418		self.map.get(key)1419	}14201421	/// Recomputes the consumed space for the current properties state.1422	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1423	pub fn recompute_consumed_space(&mut self) {1424		self.consumed_space = self1425			.map1426			.iter()1427			.map(|(key, value)| slice_size(key) + slice_size(value))1428			.sum();1429	}1430}14311432impl<const S: u32> IntoIterator for Properties<S> {1433	type Item = (PropertyKey, PropertyValue);1434	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;14351436	fn into_iter(self) -> Self::IntoIter {1437		self.map.into_iter()1438	}1439}14401441impl<const S: u32> TrySetProperty for Properties<S> {1442	type Value = PropertyValue;14431444	fn try_scoped_set(1445		&mut self,1446		scope: PropertyScope,1447		key: PropertyKey,1448		value: Self::Value,1449	) -> Result<Option<Self::Value>, PropertiesError> {1450		let key_size = scoped_slice_size(scope, &key);1451		let value_size = slice_size(&value);14521453		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1454		{1455			return Err(PropertiesError::NoSpaceForProperty);1456		}14571458		let old_value = self.map.try_scoped_set(scope, key, value)?;14591460		if let Some(old_value) = old_value.as_ref() {1461			let old_value_size = slice_size(old_value);1462			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1463		} else {1464			self.consumed_space += key_size + value_size;1465		}14661467		Ok(old_value)1468	}1469}14701471pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1472pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;