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

difftreelog

source

primitives/data-structs/src/lib.rs22.5 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use core::{20	convert::{TryFrom, TryInto},21	fmt,22};23use frame_support::{24	storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},25	traits::Get,26};2728#[cfg(feature = "serde")]29use serde::{Serialize, Deserialize};3031use sp_core::U256;32use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};33use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};34use frame_support::{BoundedVec, traits::ConstU32};35use derivative::Derivative;36use scale_info::TypeInfo;3738mod bounded;39pub mod budget;40pub mod mapping;41mod migration;4243pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;44pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;45pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;4647pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {48	100_00049} else {50	1051};52pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {53	100_00054} else {55	1056};57pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {58	204859} else {60	1061};62pub const COLLECTION_ADMINS_LIMIT: u32 = 5;63pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;64pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {65	1_000_00066} else {67	1068};6970// Timeouts for item types in passed blocks71pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;72pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;73pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;7475pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;7677// Schema limits78pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;79pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;80pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;8182pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;83// u32::max is not const: OFFCHAIN_SCHEMA_LIMIT.max(VARIABLE_ON_CHAIN_SCHEMA_LIMIT).max(CONST_ON_CHAIN_SCHEMA_LIMIT);8485pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;86pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;87pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;8889pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;90pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;91pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;9293// pub const MAX_PROPERTY_KEYS_OVERALL_LENGTH: u32 = MAX_PROPERTY_KEY_LENGTH * MAX_PROPERTIES_PER_ITEM;94pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;95pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;9697pub const MAX_COLLECTION_PROPERTIES_ENCODE_LEN: u32 =98	MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH + MAX_COLLECTION_PROPERTIES_SIZE;99100pub struct MaxPropertiesPermissionsEncodeLen;101102impl Get<u32> for MaxPropertiesPermissionsEncodeLen {103	fn get() -> u32 {104		MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH105			+ <PropertyPermission as MaxEncodedLen>::max_encoded_len() as u32106	}107}108109/// How much items can be created per single110/// create_many call111pub const MAX_ITEMS_PER_BATCH: u32 = 200;112113pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;114115#[derive(116	Encode,117	Decode,118	PartialEq,119	Eq,120	PartialOrd,121	Ord,122	Clone,123	Copy,124	Debug,125	Default,126	TypeInfo,127	MaxEncodedLen,128)]129#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]130pub struct CollectionId(pub u32);131impl EncodeLike<u32> for CollectionId {}132impl EncodeLike<CollectionId> for u32 {}133134#[derive(135	Encode,136	Decode,137	PartialEq,138	Eq,139	PartialOrd,140	Ord,141	Clone,142	Copy,143	Debug,144	Default,145	TypeInfo,146	MaxEncodedLen,147)]148#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]149pub struct TokenId(pub u32);150impl EncodeLike<u32> for TokenId {}151impl EncodeLike<TokenId> for u32 {}152153impl TokenId {154	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {155		self.0156			.checked_add(1)157			.ok_or(ArithmeticError::Overflow)158			.map(Self)159	}160}161162impl From<TokenId> for U256 {163	fn from(t: TokenId) -> Self {164		t.0.into()165	}166}167168impl TryFrom<U256> for TokenId {169	type Error = &'static str;170171	fn try_from(value: U256) -> Result<Self, Self::Error> {172		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))173	}174}175176#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]177#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]178pub struct TokenData<CrossAccountId> {179	pub const_data: Vec<u8>,180	pub properties: Vec<Property>,181	pub owner: Option<CrossAccountId>,182}183184pub struct OverflowError;185impl From<OverflowError> for &'static str {186	fn from(_: OverflowError) -> Self {187		"overflow occured"188	}189}190191pub type DecimalPoints = u8;192193#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]194#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]195pub enum CollectionMode {196	NFT,197	// decimal points198	Fungible(DecimalPoints),199	ReFungible,200}201202impl CollectionMode {203	pub fn id(&self) -> u8 {204		match self {205			CollectionMode::NFT => 1,206			CollectionMode::Fungible(_) => 2,207			CollectionMode::ReFungible => 3,208		}209	}210}211212pub trait SponsoringResolve<AccountId, Call> {213	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;214}215216#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]217#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]218pub enum AccessMode {219	Normal,220	AllowList,221}222impl Default for AccessMode {223	fn default() -> Self {224		Self::Normal225	}226}227228#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]229#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]230pub enum SchemaVersion {231	ImageURL,232	Unique,233}234impl Default for SchemaVersion {235	fn default() -> Self {236		Self::ImageURL237	}238}239240#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]241#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]242pub struct Ownership<AccountId> {243	pub owner: AccountId,244	pub fraction: u128,245}246247#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]248#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]249pub enum SponsorshipState<AccountId> {250	/// The fees are applied to the transaction sender251	Disabled,252	Unconfirmed(AccountId),253	/// Transactions are sponsored by specified account254	Confirmed(AccountId),255}256257impl<AccountId> SponsorshipState<AccountId> {258	pub fn sponsor(&self) -> Option<&AccountId> {259		match self {260			Self::Confirmed(sponsor) => Some(sponsor),261			_ => None,262		}263	}264265	pub fn pending_sponsor(&self) -> Option<&AccountId> {266		match self {267			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),268			_ => None,269		}270	}271272	pub fn confirmed(&self) -> bool {273		matches!(self, Self::Confirmed(_))274	}275}276277impl<T> Default for SponsorshipState<T> {278	fn default() -> Self {279		Self::Disabled280	}281}282283/// Used in storage284#[struct_versioning::versioned(version = 2, upper)]285#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]286pub struct Collection<AccountId> {287	pub owner: AccountId,288	pub mode: CollectionMode,289	pub access: AccessMode,290	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,291	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,292	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,293	pub mint_mode: bool,294295	#[version(..2)]296	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,297298	pub schema_version: SchemaVersion,299	pub sponsorship: SponsorshipState<AccountId>,300301	#[version(..2)]302	pub limits: CollectionLimitsVersion1, // Collection private restrictions303	#[version(2.., upper(limits.into()))]304	pub limits: CollectionLimitsVersion2,305306	#[version(..2)]307	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,308	#[version(..2)]309	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,310311	pub meta_update_permission: MetaUpdatePermission,312}313314/// Used in RPC calls315#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]316#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]317pub struct RpcCollection<AccountId> {318	pub owner: AccountId,319	pub mode: CollectionMode,320	pub access: AccessMode,321	pub name: Vec<u16>,322	pub description: Vec<u16>,323	pub token_prefix: Vec<u8>,324	pub mint_mode: bool,325	pub offchain_schema: Vec<u8>,326	pub schema_version: SchemaVersion,327	pub sponsorship: SponsorshipState<AccountId>,328	pub limits: CollectionLimits,329	pub variable_on_chain_schema: Vec<u8>,330	pub const_on_chain_schema: Vec<u8>,331	pub meta_update_permission: MetaUpdatePermission,332}333334#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]335#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]336pub enum CollectionField {337	VariableOnChainSchema,338	ConstOnChainSchema,339	OffchainSchema,340}341342#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]343#[derivative(Debug, Default(bound = ""))]344pub struct CreateCollectionData<AccountId> {345	#[derivative(Default(value = "CollectionMode::NFT"))]346	pub mode: CollectionMode,347	pub access: Option<AccessMode>,348	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,349	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,350	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,351	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,352	pub schema_version: Option<SchemaVersion>,353	pub pending_sponsor: Option<AccountId>,354	pub limits: Option<CollectionLimits>,355	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,356	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,357	pub meta_update_permission: Option<MetaUpdatePermission>,358	pub token_property_permissions: CollectionPropertiesPermissionsVec,359	pub properties: CollectionPropertiesVec,360}361362pub type CollectionPropertiesPermissionsVec =363	BoundedVec<PropertyKeyPermission, MaxPropertiesPermissionsEncodeLen>;364365pub type CollectionPropertiesVec =366	BoundedVec<Property, ConstU32<MAX_COLLECTION_PROPERTIES_ENCODE_LEN>>;367368#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]369#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]370pub struct NftItemType<AccountId> {371	pub owner: AccountId,372	pub const_data: Vec<u8>,373	pub variable_data: Vec<u8>,374}375376#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]377#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]378pub struct FungibleItemType {379	pub value: u128,380}381382#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]383#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]384pub struct ReFungibleItemType<AccountId> {385	pub owner: Vec<Ownership<AccountId>>,386	pub const_data: Vec<u8>,387	pub variable_data: Vec<u8>,388}389390/// All fields are wrapped in `Option`s, where None means chain default391#[struct_versioning::versioned(version = 2, upper)]392#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]393#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]394pub struct CollectionLimits {395	pub account_token_ownership_limit: Option<u32>,396	pub sponsored_data_size: Option<u32>,397	/// None - setVariableMetadata is not sponsored398	/// Some(v) - setVariableMetadata is sponsored399	///           if there is v block between txs400	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,401	pub token_limit: Option<u32>,402403	// Timeouts for item types in passed blocks404	pub sponsor_transfer_timeout: Option<u32>,405	pub sponsor_approve_timeout: Option<u32>,406	pub owner_can_transfer: Option<bool>,407	pub owner_can_destroy: Option<bool>,408	pub transfers_enabled: Option<bool>,409410	#[version(2.., upper(None))]411	pub nesting_rule: Option<NestingRule>,412}413414impl CollectionLimits {415	pub fn account_token_ownership_limit(&self) -> u32 {416		self.account_token_ownership_limit417			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)418			.min(MAX_TOKEN_OWNERSHIP)419	}420	pub fn sponsored_data_size(&self) -> u32 {421		self.sponsored_data_size422			.unwrap_or(CUSTOM_DATA_LIMIT)423			.min(CUSTOM_DATA_LIMIT)424	}425	pub fn token_limit(&self) -> u32 {426		self.token_limit427			.unwrap_or(COLLECTION_TOKEN_LIMIT)428			.min(COLLECTION_TOKEN_LIMIT)429	}430	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {431		self.sponsor_transfer_timeout432			.unwrap_or(default)433			.min(MAX_SPONSOR_TIMEOUT)434	}435	pub fn sponsor_approve_timeout(&self) -> u32 {436		self.sponsor_approve_timeout437			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)438			.min(MAX_SPONSOR_TIMEOUT)439	}440	pub fn owner_can_transfer(&self) -> bool {441		self.owner_can_transfer.unwrap_or(true)442	}443	pub fn owner_can_destroy(&self) -> bool {444		self.owner_can_destroy.unwrap_or(true)445	}446	pub fn transfers_enabled(&self) -> bool {447		self.transfers_enabled.unwrap_or(true)448	}449	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {450		match self451			.sponsored_data_rate_limit452			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)453		{454			SponsoringRateLimit::SponsoringDisabled => None,455			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),456		}457	}458	pub fn nesting_rule(&self) -> &NestingRule {459		static DEFAULT: NestingRule = NestingRule::Owner;460		self.nesting_rule.as_ref().unwrap_or(&DEFAULT)461	}462}463464#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]465#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]466#[derivative(Debug)]467pub enum NestingRule {468	/// No one can nest tokens469	Disabled,470	/// Owner can nest any tokens471	Owner,472	/// Owner can nest tokens from specified collections473	OwnerRestricted(474		#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]475		#[derivative(Debug(format_with = "bounded::set_debug"))]476		BoundedBTreeSet<CollectionId, ConstU32<16>>,477	),478}479480#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]481#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]482pub enum SponsoringRateLimit {483	SponsoringDisabled,484	Blocks(u32),485}486487#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]488#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]489#[derivative(Debug)]490pub struct CreateNftData {491	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]492	#[derivative(Debug(format_with = "bounded::vec_debug"))]493	pub const_data: BoundedVec<u8, CustomDataLimit>,494	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]495	#[derivative(Debug(format_with = "bounded::vec_debug"))]496	pub variable_data: BoundedVec<u8, CustomDataLimit>,497498	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]499	#[derivative(Debug(format_with = "bounded::vec_debug"))]500	pub properties: CollectionPropertiesVec,501}502503#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]504#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]505pub struct CreateFungibleData {506	pub value: u128,507}508509#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]510#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]511#[derivative(Debug)]512pub struct CreateReFungibleData {513	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]514	#[derivative(Debug(format_with = "bounded::vec_debug"))]515	pub const_data: BoundedVec<u8, CustomDataLimit>,516	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]517	#[derivative(Debug(format_with = "bounded::vec_debug"))]518	pub variable_data: BoundedVec<u8, CustomDataLimit>,519	pub pieces: u128,520}521522#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]523#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]524pub enum MetaUpdatePermission {525	ItemOwner,526	Admin,527	None,528}529530impl Default for MetaUpdatePermission {531	fn default() -> Self {532		Self::ItemOwner533	}534}535536#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]537#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]538pub enum CreateItemData {539	NFT(CreateNftData),540	Fungible(CreateFungibleData),541	ReFungible(CreateReFungibleData),542}543544#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]545#[derivative(Debug)]546pub struct CreateNftExData<CrossAccountId> {547	#[derivative(Debug(format_with = "bounded::vec_debug"))]548	pub const_data: BoundedVec<u8, CustomDataLimit>,549	#[derivative(Debug(format_with = "bounded::vec_debug"))]550	pub variable_data: BoundedVec<u8, CustomDataLimit>,551	#[derivative(Debug(format_with = "bounded::vec_debug"))]552	pub properties: CollectionPropertiesVec,553	pub owner: CrossAccountId,554}555556#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]557#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]558pub struct CreateRefungibleExData<CrossAccountId> {559	#[derivative(Debug(format_with = "bounded::vec_debug"))]560	pub const_data: BoundedVec<u8, CustomDataLimit>,561	#[derivative(Debug(format_with = "bounded::vec_debug"))]562	pub variable_data: BoundedVec<u8, CustomDataLimit>,563	#[derivative(Debug(format_with = "bounded::map_debug"))]564	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,565}566567#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]568#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]569pub enum CreateItemExData<CrossAccountId> {570	NFT(571		#[derivative(Debug(format_with = "bounded::vec_debug"))]572		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,573	),574	Fungible(575		#[derivative(Debug(format_with = "bounded::map_debug"))]576		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,577	),578	/// Many tokens, each may have only one owner579	RefungibleMultipleItems(580		#[derivative(Debug(format_with = "bounded::vec_debug"))]581		BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,582	),583	/// Single token, which may have many owners584	RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),585}586587impl CreateItemData {588	pub fn data_size(&self) -> usize {589		match self {590			CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),591			CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),592			_ => 0,593		}594	}595}596597impl From<CreateNftData> for CreateItemData {598	fn from(item: CreateNftData) -> Self {599		CreateItemData::NFT(item)600	}601}602603impl From<CreateReFungibleData> for CreateItemData {604	fn from(item: CreateReFungibleData) -> Self {605		CreateItemData::ReFungible(item)606	}607}608609impl From<CreateFungibleData> for CreateItemData {610	fn from(item: CreateFungibleData) -> Self {611		CreateItemData::Fungible(item)612	}613}614615#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]616#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]617pub struct CollectionStats {618	pub created: u32,619	pub destroyed: u32,620	pub alive: u32,621}622623#[derive(Encode, Decode, PartialEq, Clone, Debug)]624pub struct PhantomType<T>(core::marker::PhantomData<T>);625626impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {627	type Identity = PhantomType<T>;628629	fn type_info() -> scale_info::Type {630		use scale_info::{631			Type, Path,632			build::{FieldsBuilder, UnnamedFields},633			type_params,634		};635		Type::builder()636			.path(Path::new("up_data_structs", "PhantomType"))637			.type_params(type_params!(T))638			.composite(<FieldsBuilder<UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()))639	}640}641impl<T> MaxEncodedLen for PhantomType<T> {642	fn max_encoded_len() -> usize {643		0644	}645}646647pub type PropertyKey = BoundedVec<u8, ConstU32<MAX_PROPERTY_KEY_LENGTH>>;648pub type PropertyValue = BoundedVec<u8, ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;649650#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]651#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]652pub struct PropertyPermission {653	pub mutable: bool,654	pub collection_admin: bool,655	pub token_owner: bool,656}657658impl PropertyPermission {659	pub fn none() -> Self {660		Self {661			mutable: true,662			collection_admin: false,663			token_owner: false,664		}665	}666}667668#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]669#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]670pub struct Property {671	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]672	pub key: PropertyKey,673674	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]675	pub value: PropertyValue,676}677678#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]679#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]680pub struct PropertyKeyPermission {681	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]682	pub key: PropertyKey,683684	pub permission: PropertyPermission,685}686687pub enum PropertiesError {688	NoSpaceForProperty,689	PropertyLimitReached,690}691692pub type PropertiesMap =693	BoundedBTreeMap<PropertyKey, PropertyValue, ConstU32<MAX_PROPERTIES_PER_ITEM>>;694pub type PropertiesPermissionMap =695	BoundedBTreeMap<PropertyKey, PropertyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;696697#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]698pub struct Properties {699	map: PropertiesMap,700	consumed_space: u32,701	space_limit: u32,702}703704impl Properties {705	pub fn new(space_limit: u32) -> Self {706		Self {707			map: BoundedBTreeMap::new(),708			consumed_space: 0,709			space_limit,710		}711	}712713	pub fn from_collection_props_vec(714		data: CollectionPropertiesVec,715	) -> Result<Self, PropertiesError> {716		let mut props = Self::new(MAX_COLLECTION_PROPERTIES_SIZE);717718		for property in data.into_iter() {719			props.try_set_property(property)?;720		}721722		Ok(props)723	}724725	pub fn try_set_property(&mut self, property: Property) -> Result<(), PropertiesError> {726		let value_len = property.value.len();727728		if self.consumed_space as usize + value_len > self.space_limit as usize {729			return Err(PropertiesError::NoSpaceForProperty);730		}731732		self.map733			.try_insert(property.key, property.value)734			.map_err(|_| PropertiesError::PropertyLimitReached)?;735736		self.consumed_space += value_len as u32;737738		Ok(())739	}740741	pub fn remove_property(&mut self, key: &PropertyKey) {742		let property = self.map.get(key);743744		if let Some(value) = property {745			let value_len = value.len() as u32;746747			self.map.remove(key);748			self.consumed_space -= value_len;749		}750	}751752	pub fn get_property(&self, key: &PropertyKey) -> Option<&PropertyValue> {753		self.map.get(key)754	}755756	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &PropertyValue)> {757		self.map.iter()758	}759}760761pub struct CollectionProperties;762763impl Get<Properties> for CollectionProperties {764	fn get() -> Properties {765		Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)766	}767}768769pub struct TokenProperties;770771impl Get<Properties> for TokenProperties {772	fn get() -> Properties {773		Properties::new(MAX_TOKEN_PROPERTIES_SIZE)774	}775}