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

difftreelog

source

primitives/data-structs/src/lib.rs39.6 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};27use frame_support::storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet};2829#[cfg(feature = "serde")]30use serde::{Serialize, Deserialize};3132use sp_core::U256;33use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};34use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};35use bondrewd::Bitfields;36use frame_support::{BoundedVec, traits::ConstU32};37use derivative::Derivative;38use scale_info::TypeInfo;3940mod bondrewd_codec;41mod bounded;42pub mod budget;43pub mod mapping;44mod migration;4546/// Maximum of decimal points.47pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4849/// Maximum pieces for refungible token.50pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5253/// Maximum tokens for user.54pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {55	100_00056} else {57	1058};5960/// Maximum for collections can be created.61pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {62	100_00063} else {64	1065};6667/// Maximum for various custom data of token.68pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {69	204870} else {71	1072};7374/// Maximum admins per collection.75pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7677/// Maximum tokens per collection.78pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;7980/// Maximum tokens per account.81pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {82	1_000_00083} else {84	1085};8687/// Default timeout for transfer sponsoring NFT item.88pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;89/// Default timeout for transfer sponsoring fungible item.90pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;91/// Default timeout for transfer sponsoring refungible item.92pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9394/// Default timeout for sponsored approving.95pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;9697// Schema limits98pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;99pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;100pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;101102// TODO: not used. Delete?103pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;104105/// Maximal length of a collection name.106pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;107108/// Maximal length of a collection description.109pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;110111/// Maximal length of a token prefix.112pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;113114/// Maximal length of a property key.115pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;116117/// Maximal length of a property value.118pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;119120/// A maximum number of token properties.121pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;122123/// Maximal lenght of extended property value.124pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;125126/// Maximum size for all collection properties.127pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;128129/// Maximum size of all token properties.130pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;131132/// How much items can be created per single133/// create_many call.134pub const MAX_ITEMS_PER_BATCH: u32 = 200;135136/// Used for limit bounded types of token custom data.137pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;138139/// Collection id.140#[derive(141	Encode,142	Decode,143	PartialEq,144	Eq,145	PartialOrd,146	Ord,147	Clone,148	Copy,149	Debug,150	Default,151	TypeInfo,152	MaxEncodedLen,153)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub struct CollectionId(pub u32);156impl EncodeLike<u32> for CollectionId {}157impl EncodeLike<CollectionId> for u32 {}158159impl From<u32> for CollectionId {160	fn from(value: u32) -> Self {161		Self(value)162	}163}164165/// Token id.166#[derive(167	Encode,168	Decode,169	PartialEq,170	Eq,171	PartialOrd,172	Ord,173	Clone,174	Copy,175	Debug,176	Default,177	TypeInfo,178	MaxEncodedLen,179)]180#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]181pub struct TokenId(pub u32);182impl EncodeLike<u32> for TokenId {}183impl EncodeLike<TokenId> for u32 {}184185impl TokenId {186	/// Try to get next token id.187	///188	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.189	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {190		self.0191			.checked_add(1)192			.ok_or(ArithmeticError::Overflow)193			.map(Self)194	}195}196197impl From<TokenId> for U256 {198	fn from(t: TokenId) -> Self {199		t.0.into()200	}201}202203impl TryFrom<U256> for TokenId {204	type Error = &'static str;205206	fn try_from(value: U256) -> Result<Self, Self::Error> {207		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))208	}209}210211/// Token data.212#[struct_versioning::versioned(version = 2, upper)]213#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]214#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]215pub struct TokenData<CrossAccountId> {216	/// Properties of token.217	pub properties: Vec<Property>,218219	/// Token owner.220	pub owner: Option<CrossAccountId>,221222	/// Token pieces.223	#[version(2.., upper(0))]224	pub pieces: u128,225}226227// TODO: unused type228pub struct OverflowError;229impl From<OverflowError> for &'static str {230	fn from(_: OverflowError) -> Self {231		"overflow occured"232	}233}234235/// Alias for decimal points type.236pub type DecimalPoints = u8;237238/// Collection mode.239///240/// Collection can represent various types of tokens.241/// Each collection can contain only one type of tokens at a time.242/// This type helps to understand which tokens the collection contains.243#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]244#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]245pub enum CollectionMode {246	/// Non fungible tokens.247	NFT,248	/// Fungible tokens.249	Fungible(DecimalPoints),250	/// Refungible tokens.251	ReFungible,252}253254impl CollectionMode {255	/// Get collection mod as number.256	pub fn id(&self) -> u8 {257		match self {258			CollectionMode::NFT => 1,259			CollectionMode::Fungible(_) => 2,260			CollectionMode::ReFungible => 3,261		}262	}263}264265// TODO: unused trait266pub trait SponsoringResolve<AccountId, Call> {267	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;268}269270/// Access mode for some token operations.271#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]272#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]273pub enum AccessMode {274	/// Access grant for owner and admins. Used as default.275	Normal,276	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.277	AllowList,278}279impl Default for AccessMode {280	fn default() -> Self {281		Self::Normal282	}283}284285// TODO: remove in future.286#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]287#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]288pub enum SchemaVersion {289	ImageURL,290	Unique,291}292impl Default for SchemaVersion {293	fn default() -> Self {294		Self::ImageURL295	}296}297298// TODO: unused type299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]300#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]301pub struct Ownership<AccountId> {302	pub owner: AccountId,303	pub fraction: u128,304}305306/// The state of collection sponsorship.307#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]308#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]309pub enum SponsorshipState<AccountId> {310	/// The fees are applied to the transaction sender.311	Disabled,312	/// The sponsor is under consideration. Until the sponsor gives his consent,313	/// the fee will still be charged to sender.314	Unconfirmed(AccountId),315	/// Transactions are sponsored by specified account.316	Confirmed(AccountId),317}318319impl<AccountId> SponsorshipState<AccountId> {320	/// Get a sponsor of the collection who has confirmed his status.321	pub fn sponsor(&self) -> Option<&AccountId> {322		match self {323			Self::Confirmed(sponsor) => Some(sponsor),324			_ => None,325		}326	}327328	/// Get a sponsor of the collection who has pending or confirmed status.329	pub fn pending_sponsor(&self) -> Option<&AccountId> {330		match self {331			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),332			_ => None,333		}334	}335336	/// Whether the sponsorship is confirmed.337	pub fn confirmed(&self) -> bool {338		matches!(self, Self::Confirmed(_))339	}340}341342impl<T> Default for SponsorshipState<T> {343	fn default() -> Self {344		Self::Disabled345	}346}347348pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;349pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;350pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;351352#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]353#[bondrewd(enforce_bytes = 1)]354pub struct CollectionFlags {355	/// Tokens in foreign collections can be transferred, but not burnt356	#[bondrewd(bits = "0..1")]357	pub foreign: bool,358	/// Supports ERC721Metadata359	#[bondrewd(bits = "1..2")]360	pub erc721metadata: bool,361	/// External collections can't be managed using `unique` api362	#[bondrewd(bits = "7..8")]363	pub external: bool,364365	#[bondrewd(reserve, bits = "2..7")]366	pub reserved: u8,367}368bondrewd_codec!(CollectionFlags);369370/// Base structure for represent collection.371///372/// Used to provide basic functionality for all types of collections.373///374/// #### Note375/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).376#[struct_versioning::versioned(version = 2, upper)]377#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]378pub struct Collection<AccountId> {379	/// Collection owner account.380	pub owner: AccountId,381382	/// Collection mode.383	pub mode: CollectionMode,384385	/// Access mode.386	#[version(..2)]387	pub access: AccessMode,388389	/// Collection name.390	pub name: CollectionName,391392	/// Collection description.393	pub description: CollectionDescription,394395	/// Token prefix.396	pub token_prefix: CollectionTokenPrefix,397398	#[version(..2)]399	pub mint_mode: bool,400401	#[version(..2)]402	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,403404	#[version(..2)]405	pub schema_version: SchemaVersion,406407	/// The state of sponsorship of the collection.408	pub sponsorship: SponsorshipState<AccountId>,409410	/// Collection limits.411	pub limits: CollectionLimits,412413	/// Collection permissions.414	#[version(2.., upper(Default::default()))]415	pub permissions: CollectionPermissions,416417	#[version(2.., upper(Default::default()))]418	pub flags: CollectionFlags,419420	#[version(..2)]421	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,422423	#[version(..2)]424	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,425426	#[version(..2)]427	pub meta_update_permission: MetaUpdatePermission,428}429430#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]431#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]432pub struct RpcCollectionFlags {433	/// Is collection is foreign.434	pub foreign: bool,435	/// Collection supports ERC721Metadata.436	pub erc721metadata: bool,437}438439/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).440#[struct_versioning::versioned(version = 2, upper)]441#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]442#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]443pub struct RpcCollection<AccountId> {444	/// Collection owner account.445	pub owner: AccountId,446447	/// Collection mode.448	pub mode: CollectionMode,449450	/// Collection name.451	pub name: Vec<u16>,452453	/// Collection description.454	pub description: Vec<u16>,455456	/// Token prefix.457	pub token_prefix: Vec<u8>,458459	/// The state of sponsorship of the collection.460	pub sponsorship: SponsorshipState<AccountId>,461462	/// Collection limits.463	pub limits: CollectionLimits,464465	/// Collection permissions.466	pub permissions: CollectionPermissions,467468	/// Token property permissions.469	pub token_property_permissions: Vec<PropertyKeyPermission>,470471	/// Collection properties.472	pub properties: Vec<Property>,473474	/// Is collection read only.475	pub read_only: bool,476477	/// Extra collection flags478	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]479	pub flags: RpcCollectionFlags,480}481482/// Data used for create collection.483///484/// All fields are wrapped in [`Option`], where `None` means chain default.485#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]486#[derivative(Debug, Default(bound = ""))]487pub struct CreateCollectionData<AccountId> {488	/// Collection mode.489	#[derivative(Default(value = "CollectionMode::NFT"))]490	pub mode: CollectionMode,491492	/// Access mode.493	pub access: Option<AccessMode>,494495	/// Collection name.496	pub name: CollectionName,497498	/// Collection description.499	pub description: CollectionDescription,500501	/// Token prefix.502	pub token_prefix: CollectionTokenPrefix,503504	/// Pending collection sponsor.505	pub pending_sponsor: Option<AccountId>,506507	/// Collection limits.508	pub limits: Option<CollectionLimits>,509510	/// Collection permissions.511	pub permissions: Option<CollectionPermissions>,512513	/// Token property permissions.514	pub token_property_permissions: CollectionPropertiesPermissionsVec,515516	/// Collection properties.517	pub properties: CollectionPropertiesVec,518}519520/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].521// TODO: maybe rename to PropertiesPermissionsVec522pub type CollectionPropertiesPermissionsVec =523	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;524525/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].526pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;527528/// Limits and restrictions of a collection.529///530/// All fields are wrapped in [`Option`], where `None` means chain default.531///532/// Update with `pallet_common::Pallet::clamp_limits`.533// IMPORTANT: When adding/removing fields from this struct - don't forget to also534#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]535#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]536// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.537// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.538// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.539pub struct CollectionLimits {540	/// How many tokens can a user have on one account.541	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].542	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].543	pub account_token_ownership_limit: Option<u32>,544545	/// How many bytes of data are available for sponsorship.546	/// * Default - [`CUSTOM_DATA_LIMIT`].547	/// * Limit - [`CUSTOM_DATA_LIMIT`].548	pub sponsored_data_size: Option<u32>,549550	// FIXME should we delete this or repurpose it?551	/// Times in how many blocks we sponsor data.552	///553	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.554	///555	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).556	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].557	///558	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]559	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,560	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]561562	/// How many tokens can be mined into this collection.563	///564	/// * Default - [`COLLECTION_TOKEN_LIMIT`].565	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].566	pub token_limit: Option<u32>,567568	/// Timeouts for transfer sponsoring.569	///570	/// * Default571	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]572	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]573	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]574	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].575	pub sponsor_transfer_timeout: Option<u32>,576577	/// Timeout for sponsoring an approval in passed blocks.578	///579	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].580	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].581	pub sponsor_approve_timeout: Option<u32>,582583	/// Whether the collection owner of the collection can send tokens (which belong to other users).584	///585	/// * Default - **false**.586	pub owner_can_transfer: Option<bool>,587588	/// Can the collection owner burn other people's tokens.589	///590	/// * Default - **true**.591	pub owner_can_destroy: Option<bool>,592593	/// Is it possible to send tokens from this collection between users.594	///595	/// * Default - **true**.596	pub transfers_enabled: Option<bool>,597}598599impl CollectionLimits {600	pub fn with_default_limits(collection_type: CollectionMode) -> Self {601		CollectionLimits {602			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),603			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),604			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),605			token_limit: Some(COLLECTION_TOKEN_LIMIT),606			sponsor_transfer_timeout: match collection_type {607				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),608				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),609				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),610			},611			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),612			owner_can_transfer: Some(false),613			owner_can_destroy: Some(true),614			transfers_enabled: Some(true),615		}616	}617618	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).619	pub fn account_token_ownership_limit(&self) -> u32 {620		self.account_token_ownership_limit621			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)622			.min(MAX_TOKEN_OWNERSHIP)623	}624625	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).626	pub fn sponsored_data_size(&self) -> u32 {627		self.sponsored_data_size628			.unwrap_or(CUSTOM_DATA_LIMIT)629			.min(CUSTOM_DATA_LIMIT)630	}631632	/// Get effective value for [`token_limit`](self.token_limit).633	pub fn token_limit(&self) -> u32 {634		self.token_limit635			.unwrap_or(COLLECTION_TOKEN_LIMIT)636			.min(COLLECTION_TOKEN_LIMIT)637	}638639	// TODO: may be replace u32 to mode?640	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).641	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {642		self.sponsor_transfer_timeout643			.unwrap_or(default)644			.min(MAX_SPONSOR_TIMEOUT)645	}646647	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).648	pub fn sponsor_approve_timeout(&self) -> u32 {649		self.sponsor_approve_timeout650			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)651			.min(MAX_SPONSOR_TIMEOUT)652	}653654	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).655	pub fn owner_can_transfer(&self) -> bool {656		self.owner_can_transfer.unwrap_or(false)657	}658659	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).660	pub fn owner_can_transfer_instaled(&self) -> bool {661		self.owner_can_transfer.is_some()662	}663664	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).665	pub fn owner_can_destroy(&self) -> bool {666		self.owner_can_destroy.unwrap_or(true)667	}668669	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).670	pub fn transfers_enabled(&self) -> bool {671		self.transfers_enabled.unwrap_or(true)672	}673674	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).675	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {676		match self677			.sponsored_data_rate_limit678			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)679		{680			SponsoringRateLimit::SponsoringDisabled => None,681			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),682		}683	}684}685686/// Permissions on certain operations within a collection.687///688/// Some fields are wrapped in [`Option`], where `None` means chain default.689///690/// Update with `pallet_common::Pallet::clamp_permissions`.691#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]692#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]693// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.694// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.695pub struct CollectionPermissions {696	/// Access mode.697	///698	/// * Default - [`AccessMode::Normal`].699	pub access: Option<AccessMode>,700701	/// Minting allowance.702	///703	/// * Default - **false**.704	pub mint_mode: Option<bool>,705706	/// Permissions for nesting.707	///708	/// * Default709	///   - `token_owner` - **false**710	///   - `collection_admin` - **false**711	///   - `restricted` - **None**712	pub nesting: Option<NestingPermissions>,713}714715impl CollectionPermissions {716	/// Get effective value for [`access`](self.access).717	pub fn access(&self) -> AccessMode {718		self.access.unwrap_or(AccessMode::Normal)719	}720721	/// Get effective value for [`mint_mode`](self.mint_mode).722	pub fn mint_mode(&self) -> bool {723		self.mint_mode.unwrap_or(false)724	}725726	/// Get effective value for [`nesting`](self.nesting).727	pub fn nesting(&self) -> &NestingPermissions {728		static DEFAULT: NestingPermissions = NestingPermissions {729			token_owner: false,730			collection_admin: false,731			restricted: None,732			#[cfg(feature = "runtime-benchmarks")]733			permissive: false,734		};735		self.nesting.as_ref().unwrap_or(&DEFAULT)736	}737}738739/// Inner set for collections allowed to nest.740type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;741742/// Wraper for collections set allowing nest.743#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]744#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]745#[derivative(Debug)]746pub struct OwnerRestrictedSet(747	#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]748	#[derivative(Debug(format_with = "bounded::set_debug"))]749	pub OwnerRestrictedSetInner,750);751752impl OwnerRestrictedSet {753	/// Create new set.754	pub fn new() -> Self {755		Self(Default::default())756	}757}758impl core::ops::Deref for OwnerRestrictedSet {759	type Target = OwnerRestrictedSetInner;760	fn deref(&self) -> &Self::Target {761		&self.0762	}763}764impl core::ops::DerefMut for OwnerRestrictedSet {765	fn deref_mut(&mut self) -> &mut Self::Target {766		&mut self.0767	}768}769770/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.771#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]772#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]773#[derivative(Debug)]774pub struct NestingPermissions {775	/// Owner of token can nest tokens under it.776	pub token_owner: bool,777	/// Admin of token collection can nest tokens under token.778	pub collection_admin: bool,779	/// If set - only tokens from specified collections can be nested.780	pub restricted: Option<OwnerRestrictedSet>,781782	#[cfg(feature = "runtime-benchmarks")]783	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.784	pub permissive: bool,785}786787/// Enum denominating how often can sponsoring occur if it is enabled.788///789/// Used for [`collection limits`](CollectionLimits).790#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]791#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]792pub enum SponsoringRateLimit {793	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions794	SponsoringDisabled,795	/// Once per how many blocks can sponsorship of a transaction type occur796	Blocks(u32),797}798799/// Data used to describe an NFT at creation.800#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]801#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]802#[derivative(Debug)]803pub struct CreateNftData {804	/// Key-value pairs used to describe the token as metadata805	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]806	#[derivative(Debug(format_with = "bounded::vec_debug"))]807	/// Properties that wil be assignet to created item.808	pub properties: CollectionPropertiesVec,809}810811/// Data used to describe a Fungible token at creation.812#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]813#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]814pub struct CreateFungibleData {815	/// Number of fungible coins minted816	pub value: u128,817}818819/// Data used to describe a Refungible token at creation.820#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]821#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]822#[derivative(Debug)]823pub struct CreateReFungibleData {824	/// Number of pieces the RFT is split into825	pub pieces: u128,826827	/// Key-value pairs used to describe the token as metadata828	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]829	#[derivative(Debug(format_with = "bounded::vec_debug"))]830	pub properties: CollectionPropertiesVec,831}832833// TODO: remove this.834#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]835#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]836pub enum MetaUpdatePermission {837	ItemOwner,838	Admin,839	None,840}841842/// Enum holding data used for creation of all three item types.843/// Unified data for create item.844#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]845#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]846pub enum CreateItemData {847	/// Data for create NFT.848	NFT(CreateNftData),849	/// Data for create Fungible item.850	Fungible(CreateFungibleData),851	/// Data for create ReFungible item.852	ReFungible(CreateReFungibleData),853}854855/// Extended data for create NFT.856#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]857#[derivative(Debug)]858pub struct CreateNftExData<CrossAccountId> {859	/// Properties that wil be assignet to created item.860	#[derivative(Debug(format_with = "bounded::vec_debug"))]861	pub properties: CollectionPropertiesVec,862863	/// Owner of creating item.864	pub owner: CrossAccountId,865}866867/// Extended data for create ReFungible item.868#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]869#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]870pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {871	#[derivative(Debug(format_with = "bounded::map_debug"))]872	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,873	#[derivative(Debug(format_with = "bounded::vec_debug"))]874	pub properties: CollectionPropertiesVec,875}876877/// Extended data for create ReFungible item.878#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]879#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]880pub struct CreateRefungibleExSingleOwner<CrossAccountId> {881	pub user: CrossAccountId,882	pub pieces: u128,883	#[derivative(Debug(format_with = "bounded::vec_debug"))]884	pub properties: CollectionPropertiesVec,885}886887/// Unified extended data for creating item.888#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]889#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]890pub enum CreateItemExData<CrossAccountId> {891	/// Extended data for create NFT.892	NFT(893		#[derivative(Debug(format_with = "bounded::vec_debug"))]894		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,895	),896897	/// Extended data for create Fungible item.898	Fungible(899		#[derivative(Debug(format_with = "bounded::map_debug"))]900		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,901	),902903	/// Extended data for create ReFungible item in case of904	/// many tokens, each may have only one owner905	RefungibleMultipleItems(906		#[derivative(Debug(format_with = "bounded::vec_debug"))]907		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,908	),909910	/// Extended data for create ReFungible item in case of911	/// single token, which may have many owners912	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),913}914915impl From<CreateNftData> for CreateItemData {916	fn from(item: CreateNftData) -> Self {917		CreateItemData::NFT(item)918	}919}920921impl From<CreateReFungibleData> for CreateItemData {922	fn from(item: CreateReFungibleData) -> Self {923		CreateItemData::ReFungible(item)924	}925}926927impl From<CreateFungibleData> for CreateItemData {928	fn from(item: CreateFungibleData) -> Self {929		CreateItemData::Fungible(item)930	}931}932933/// Token's address, dictated by its collection and token IDs.934#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]935#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]936// todo possibly rename to be used generally as an address pair937pub struct TokenChild {938	/// Token id.939	pub token: TokenId,940941	/// Collection id.942	pub collection: CollectionId,943}944945/// Collection statistics.946#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]947#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]948pub struct CollectionStats {949	/// Number of created items.950	pub created: u32,951952	/// Number of burned items.953	pub destroyed: u32,954955	/// Number of current items.956	pub alive: u32,957}958959/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.960#[derive(Encode, Decode, Clone, Debug)]961#[cfg_attr(feature = "std", derive(PartialEq))]962pub struct PhantomType<T>(core::marker::PhantomData<T>);963964impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {965	type Identity = PhantomType<T>;966967	fn type_info() -> scale_info::Type {968		use scale_info::{969			Type, Path,970			build::{FieldsBuilder, UnnamedFields},971			form::MetaForm,972			type_params,973		};974		Type::builder()975			.path(Path::new("up_data_structs", "PhantomType"))976			.type_params(type_params!(T))977			.composite(978				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),979			)980	}981}982impl<T> MaxEncodedLen for PhantomType<T> {983	fn max_encoded_len() -> usize {984		0985	}986}987988/// Bounded vector of bytes.989pub type BoundedBytes<S> = BoundedVec<u8, S>;990991/// Extra properties for external collections.992pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;993994/// Property key.995pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;996997/// Property value.998pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;9991000/// Property permission.1001#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1002#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1003pub struct PropertyPermission {1004	/// Permission to change the property and property permission.1005	///1006	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1007	pub mutable: bool,10081009	/// Change permission for the collection administrator.1010	pub collection_admin: bool,10111012	/// Permission to change the property for the owner of the token.1013	pub token_owner: bool,1014}10151016impl PropertyPermission {1017	/// Creates mutable property permission but changes restricted for collection admin and token owner.1018	pub fn none() -> Self {1019		Self {1020			mutable: true,1021			collection_admin: false,1022			token_owner: false,1023		}1024	}1025}10261027/// Property is simpl key-value record.1028#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1029#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1030pub struct Property {1031	/// Property key.1032	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1033	pub key: PropertyKey,10341035	/// Property value.1036	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1037	pub value: PropertyValue,1038}10391040impl Into<(PropertyKey, PropertyValue)> for Property {1041	fn into(self) -> (PropertyKey, PropertyValue) {1042		(self.key, self.value)1043	}1044}10451046/// Record for proprty key permission.1047#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1048#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1049pub struct PropertyKeyPermission {1050	/// Key.1051	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1052	pub key: PropertyKey,10531054	/// Permission.1055	pub permission: PropertyPermission,1056}10571058impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1059	fn into(self) -> (PropertyKey, PropertyPermission) {1060		(self.key, self.permission)1061	}1062}10631064/// Errors for properties actions.1065#[derive(Debug)]1066pub enum PropertiesError {1067	/// The space allocated for properties has run out.1068	///1069	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1070	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1071	NoSpaceForProperty,10721073	/// The property limit has been reached.1074	///1075	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1076	PropertyLimitReached,10771078	/// Property key contains not allowed character.1079	InvalidCharacterInPropertyKey,10801081	/// Property key length is too long.1082	///1083	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1084	PropertyKeyIsTooLong,10851086	/// Property key is empty.1087	EmptyPropertyKey,1088}10891090/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1091#[derive(Debug)]1092pub enum TokenOwnerError {1093	NotFound,1094	MultipleOwners,1095}10961097/// Marker for scope of property.1098///1099/// Scoped property can't be changed by user. Used for external collections.1100#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1101pub enum PropertyScope {1102	None,1103	Rmrk,1104}11051106impl PropertyScope {1107	pub fn prefix(&self) -> &'static [u8] {1108		match self {1109			Self::None => b"",1110			Self::Rmrk => b"rmrk:",1111		}1112	}1113	/// Apply scope to property key.1114	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1115		let prefix = self.prefix();1116		if prefix == b"" {1117			return Ok(key);1118		}1119		[prefix, key.as_slice()]1120			.concat()1121			.try_into()1122			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1123	}1124}11251126/// Trait for operate with properties.1127pub trait TrySetProperty: Sized {1128	type Value;11291130	/// Try to set property with scope.1131	fn try_scoped_set(1132		&mut self,1133		scope: PropertyScope,1134		key: PropertyKey,1135		value: Self::Value,1136	) -> Result<Option<Self::Value>, PropertiesError>;11371138	/// Try to set property with scope from iterator.1139	fn try_scoped_set_from_iter<I, KV>(1140		&mut self,1141		scope: PropertyScope,1142		iter: I,1143	) -> Result<(), PropertiesError>1144	where1145		I: Iterator<Item = KV>,1146		KV: Into<(PropertyKey, Self::Value)>,1147	{1148		for kv in iter {1149			let (key, value) = kv.into();1150			self.try_scoped_set(scope, key, value)?;1151		}11521153		Ok(())1154	}11551156	/// Try to set property.1157	fn try_set(1158		&mut self,1159		key: PropertyKey,1160		value: Self::Value,1161	) -> Result<Option<Self::Value>, PropertiesError> {1162		self.try_scoped_set(PropertyScope::None, key, value)1163	}11641165	/// Try to set property from iterator.1166	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1167	where1168		I: Iterator<Item = KV>,1169		KV: Into<(PropertyKey, Self::Value)>,1170	{1171		self.try_scoped_set_from_iter(PropertyScope::None, iter)1172	}1173}11741175/// Wrapped map for storing properties.1176#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1177#[derivative(Default(bound = ""))]1178pub struct PropertiesMap<Value>(1179	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1180);11811182impl<Value> PropertiesMap<Value> {1183	/// Create new property map.1184	pub fn new() -> Self {1185		Self(BoundedBTreeMap::new())1186	}11871188	/// Remove property from map.1189	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1190		Self::check_property_key(key)?;11911192		Ok(self.0.remove(key))1193	}11941195	/// Get property with appropriate key from map.1196	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1197		self.0.get(key)1198	}11991200	/// Check if map contains key.1201	pub fn contains_key(&self, key: &PropertyKey) -> bool {1202		self.0.contains_key(key)1203	}12041205	/// Check if map contains key with key validation.1206	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1207		if key.is_empty() {1208			return Err(PropertiesError::EmptyPropertyKey);1209		}12101211		for byte in key.as_slice().iter() {1212			let byte = *byte;12131214			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1215				return Err(PropertiesError::InvalidCharacterInPropertyKey);1216			}1217		}12181219		Ok(())1220	}12211222	pub fn values(&self) -> impl Iterator<Item = &Value> {1223		self.0.values()1224	}12251226	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1227		self.0.iter()1228	}1229}12301231impl<Value> IntoIterator for PropertiesMap<Value> {1232	type Item = (PropertyKey, Value);1233	type IntoIter = <1234		BoundedBTreeMap<1235			PropertyKey,1236			Value,1237			ConstU32<MAX_PROPERTIES_PER_ITEM>1238		> as IntoIterator1239	>::IntoIter;12401241	fn into_iter(self) -> Self::IntoIter {1242		self.0.into_iter()1243	}1244}12451246impl<Value> TrySetProperty for PropertiesMap<Value> {1247	type Value = Value;12481249	fn try_scoped_set(1250		&mut self,1251		scope: PropertyScope,1252		key: PropertyKey,1253		value: Self::Value,1254	) -> Result<Option<Self::Value>, PropertiesError> {1255		Self::check_property_key(&key)?;12561257		let key = scope.apply(key)?;1258		self.01259			.try_insert(key, value)1260			.map_err(|_| PropertiesError::PropertyLimitReached)1261	}1262}12631264/// Alias for property permissions map.1265pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;12661267fn slice_size(data: &[u8]) -> u32 {1268	scoped_slice_size(PropertyScope::None, data)1269}1270fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1271	use codec::Compact;1272	let prefix = scope.prefix();1273	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321274		+ data.len() as u321275		+ prefix.len() as u321276}12771278/// Wrapper for properties map with consumed space control.1279#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1280pub struct Properties<const S: u32> {1281	map: PropertiesMap<PropertyValue>,1282	consumed_space: u32,1283	// May be not zero, previously served as a current S generic1284	_reserved: u32,1285}12861287impl<const S: u32> MaxEncodedLen for Properties<S> {1288	fn max_encoded_len() -> usize {1289		// len of map + len of consumed_space + len of space_limit1290		u32::max_encoded_len() * 3 + S as usize1291	}1292}12931294impl<const S: u32> Default for Properties<S> {1295	fn default() -> Self {1296		Self::new()1297	}1298}12991300impl<const S: u32> Properties<S> {1301	/// Create new properies container.1302	pub fn new() -> Self {1303		Self {1304			map: PropertiesMap::new(),1305			consumed_space: 0,1306			_reserved: 0,1307		}1308	}13091310	/// Remove propery with appropiate key.1311	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1312		let value = self.map.remove(key)?;13131314		if let Some(ref value) = value {1315			let kv_len = slice_size(key) + slice_size(value);1316			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1317		}13181319		Ok(value)1320	}13211322	/// Get property with appropriate key.1323	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1324		self.map.get(key)1325	}13261327	/// Recomputes the consumed space for the current properties state.1328	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1329	pub fn recompute_consumed_space(&mut self) {1330		self.consumed_space = self1331			.map1332			.iter()1333			.map(|(key, value)| slice_size(key) + slice_size(value))1334			.sum();1335	}1336}13371338impl<const S: u32> IntoIterator for Properties<S> {1339	type Item = (PropertyKey, PropertyValue);1340	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;13411342	fn into_iter(self) -> Self::IntoIter {1343		self.map.into_iter()1344	}1345}13461347impl<const S: u32> TrySetProperty for Properties<S> {1348	type Value = PropertyValue;13491350	fn try_scoped_set(1351		&mut self,1352		scope: PropertyScope,1353		key: PropertyKey,1354		value: Self::Value,1355	) -> Result<Option<Self::Value>, PropertiesError> {1356		let key_size = scoped_slice_size(scope, &key);1357		let value_size = slice_size(&value) as u32;13581359		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1360		{1361			return Err(PropertiesError::NoSpaceForProperty);1362		}13631364		let old_value = self.map.try_scoped_set(scope, key, value)?;13651366		if let Some(old_value) = old_value.as_ref() {1367			let old_value_size = slice_size(&old_value);1368			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1369		} else {1370			self.consumed_space += key_size + value_size;1371		}13721373		Ok(old_value)1374	}1375}13761377pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1378pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;