git.delta.rocks / unique-network / refs/commits / 6020bb04e7f4

difftreelog

source

primitives/data-structs/src/lib.rs22.2 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;8384pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;85pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;86pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;8788pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;89pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;90pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;9192// pub const MAX_PROPERTY_KEYS_OVERALL_LENGTH: u32 = MAX_PROPERTY_KEY_LENGTH * MAX_PROPERTIES_PER_ITEM;93pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;94pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;9596pub const MAX_COLLECTION_PROPERTIES_ENCODE_LEN: u32 =97	MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH + MAX_COLLECTION_PROPERTIES_SIZE;9899pub struct MaxPropertiesPermissionsEncodeLen;100101impl Get<u32> for MaxPropertiesPermissionsEncodeLen {102	fn get() -> u32 {103		MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH104			+ <PropertyPermission as MaxEncodedLen>::max_encoded_len() as u32105	}106}107108/// How much items can be created per single109/// create_many call110pub const MAX_ITEMS_PER_BATCH: u32 = 200;111112pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;113114#[derive(115	Encode,116	Decode,117	PartialEq,118	Eq,119	PartialOrd,120	Ord,121	Clone,122	Copy,123	Debug,124	Default,125	TypeInfo,126	MaxEncodedLen,127)]128#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]129pub struct CollectionId(pub u32);130impl EncodeLike<u32> for CollectionId {}131impl EncodeLike<CollectionId> for u32 {}132133#[derive(134	Encode,135	Decode,136	PartialEq,137	Eq,138	PartialOrd,139	Ord,140	Clone,141	Copy,142	Debug,143	Default,144	TypeInfo,145	MaxEncodedLen,146)]147#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]148pub struct TokenId(pub u32);149impl EncodeLike<u32> for TokenId {}150impl EncodeLike<TokenId> for u32 {}151152impl TokenId {153	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {154		self.0155			.checked_add(1)156			.ok_or(ArithmeticError::Overflow)157			.map(Self)158	}159}160161impl From<TokenId> for U256 {162	fn from(t: TokenId) -> Self {163		t.0.into()164	}165}166167impl TryFrom<U256> for TokenId {168	type Error = &'static str;169170	fn try_from(value: U256) -> Result<Self, Self::Error> {171		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))172	}173}174175#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]176#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]177pub struct TokenData<CrossAccountId> {178	pub const_data: Vec<u8>,179	pub properties: Vec<Property>,180	pub owner: Option<CrossAccountId>,181}182183pub struct OverflowError;184impl From<OverflowError> for &'static str {185	fn from(_: OverflowError) -> Self {186		"overflow occured"187	}188}189190pub type DecimalPoints = u8;191192#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]193#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]194pub enum CollectionMode {195	NFT,196	// decimal points197	Fungible(DecimalPoints),198	ReFungible,199}200201impl CollectionMode {202	pub fn id(&self) -> u8 {203		match self {204			CollectionMode::NFT => 1,205			CollectionMode::Fungible(_) => 2,206			CollectionMode::ReFungible => 3,207		}208	}209}210211pub trait SponsoringResolve<AccountId, Call> {212	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;213}214215#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]216#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]217pub enum AccessMode {218	Normal,219	AllowList,220}221impl Default for AccessMode {222	fn default() -> Self {223		Self::Normal224	}225}226227#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]229pub enum SchemaVersion {230	ImageURL,231	Unique,232}233impl Default for SchemaVersion {234	fn default() -> Self {235		Self::ImageURL236	}237}238239#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]240#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]241pub struct Ownership<AccountId> {242	pub owner: AccountId,243	pub fraction: u128,244}245246#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]247#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]248pub enum SponsorshipState<AccountId> {249	/// The fees are applied to the transaction sender250	Disabled,251	Unconfirmed(AccountId),252	/// Transactions are sponsored by specified account253	Confirmed(AccountId),254}255256impl<AccountId> SponsorshipState<AccountId> {257	pub fn sponsor(&self) -> Option<&AccountId> {258		match self {259			Self::Confirmed(sponsor) => Some(sponsor),260			_ => None,261		}262	}263264	pub fn pending_sponsor(&self) -> Option<&AccountId> {265		match self {266			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),267			_ => None,268		}269	}270271	pub fn confirmed(&self) -> bool {272		matches!(self, Self::Confirmed(_))273	}274}275276impl<T> Default for SponsorshipState<T> {277	fn default() -> Self {278		Self::Disabled279	}280}281282/// Used in storage283#[struct_versioning::versioned(version = 2, upper)]284#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]285pub struct Collection<AccountId> {286	pub owner: AccountId,287	pub mode: CollectionMode,288	pub access: AccessMode,289	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,290	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,291	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,292	pub mint_mode: bool,293294	#[version(..2)]295	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,296297	pub schema_version: SchemaVersion,298	pub sponsorship: SponsorshipState<AccountId>,299300	#[version(..2)]301	pub limits: CollectionLimitsVersion1, // Collection private restrictions302	#[version(2.., upper(limits.into()))]303	pub limits: CollectionLimitsVersion2,304305	#[version(..2)]306	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,307308	#[version(..2)]309	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,310311	#[version(..2)]312	pub meta_update_permission: MetaUpdatePermission,313}314315/// Used in RPC calls316#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]317#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]318pub struct RpcCollection<AccountId> {319	pub owner: AccountId,320	pub mode: CollectionMode,321	pub access: AccessMode,322	pub name: Vec<u16>,323	pub description: Vec<u16>,324	pub token_prefix: Vec<u8>,325	pub mint_mode: bool,326	pub offchain_schema: Vec<u8>,327	pub schema_version: SchemaVersion,328	pub sponsorship: SponsorshipState<AccountId>,329	pub limits: CollectionLimits,330	pub const_on_chain_schema: Vec<u8>,331	pub token_property_permissions: Vec<PropertyKeyPermission>,332	pub properties: Vec<Property>,333}334335#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]336#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]337pub enum CollectionField {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 const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,356	pub token_property_permissions: CollectionPropertiesPermissionsVec,357	pub properties: CollectionPropertiesVec,358}359360pub type CollectionPropertiesPermissionsVec =361	BoundedVec<PropertyKeyPermission, MaxPropertiesPermissionsEncodeLen>;362363pub type CollectionPropertiesVec =364	BoundedVec<Property, ConstU32<MAX_COLLECTION_PROPERTIES_ENCODE_LEN>>;365366/// All fields are wrapped in `Option`s, where None means chain default367#[struct_versioning::versioned(version = 2, upper)]368#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]369#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]370pub struct CollectionLimits {371	pub account_token_ownership_limit: Option<u32>,372	pub sponsored_data_size: Option<u32>,373374	/// FIXME should we delete this or repurpose it?375	/// None - setVariableMetadata is not sponsored376	/// Some(v) - setVariableMetadata is sponsored377	///           if there is v block between txs378	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,379	pub token_limit: Option<u32>,380381	// Timeouts for item types in passed blocks382	pub sponsor_transfer_timeout: Option<u32>,383	pub sponsor_approve_timeout: Option<u32>,384	pub owner_can_transfer: Option<bool>,385	pub owner_can_destroy: Option<bool>,386	pub transfers_enabled: Option<bool>,387388	#[version(2.., upper(None))]389	pub nesting_rule: Option<NestingRule>,390}391392impl CollectionLimits {393	pub fn account_token_ownership_limit(&self) -> u32 {394		self.account_token_ownership_limit395			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)396			.min(MAX_TOKEN_OWNERSHIP)397	}398	pub fn sponsored_data_size(&self) -> u32 {399		self.sponsored_data_size400			.unwrap_or(CUSTOM_DATA_LIMIT)401			.min(CUSTOM_DATA_LIMIT)402	}403	pub fn token_limit(&self) -> u32 {404		self.token_limit405			.unwrap_or(COLLECTION_TOKEN_LIMIT)406			.min(COLLECTION_TOKEN_LIMIT)407	}408	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {409		self.sponsor_transfer_timeout410			.unwrap_or(default)411			.min(MAX_SPONSOR_TIMEOUT)412	}413	pub fn sponsor_approve_timeout(&self) -> u32 {414		self.sponsor_approve_timeout415			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)416			.min(MAX_SPONSOR_TIMEOUT)417	}418	pub fn owner_can_transfer(&self) -> bool {419		self.owner_can_transfer.unwrap_or(true)420	}421	pub fn owner_can_destroy(&self) -> bool {422		self.owner_can_destroy.unwrap_or(true)423	}424	pub fn transfers_enabled(&self) -> bool {425		self.transfers_enabled.unwrap_or(true)426	}427	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {428		match self429			.sponsored_data_rate_limit430			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)431		{432			SponsoringRateLimit::SponsoringDisabled => None,433			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),434		}435	}436	pub fn nesting_rule(&self) -> &NestingRule {437		static DEFAULT: NestingRule = NestingRule::Disabled;438		self.nesting_rule.as_ref().unwrap_or(&DEFAULT)439	}440}441442#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]443#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]444#[derivative(Debug)]445pub enum NestingRule {446	/// No one can nest tokens447	Disabled,448	/// Owner can nest any tokens449	Owner,450	/// Owner can nest tokens from specified collections451	OwnerRestricted(452		#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]453		#[derivative(Debug(format_with = "bounded::set_debug"))]454		BoundedBTreeSet<CollectionId, ConstU32<16>>,455	),456}457458#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]459#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]460pub enum SponsoringRateLimit {461	SponsoringDisabled,462	Blocks(u32),463}464465#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]466#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]467#[derivative(Debug)]468pub struct CreateNftData {469	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]470	#[derivative(Debug(format_with = "bounded::vec_debug"))]471	pub const_data: BoundedVec<u8, CustomDataLimit>,472473	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]474	#[derivative(Debug(format_with = "bounded::vec_debug"))]475	pub properties: CollectionPropertiesVec,476}477478#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]479#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]480pub struct CreateFungibleData {481	pub value: u128,482}483484#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]485#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]486#[derivative(Debug)]487pub struct CreateReFungibleData {488	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]489	#[derivative(Debug(format_with = "bounded::vec_debug"))]490	pub const_data: BoundedVec<u8, CustomDataLimit>,491	pub pieces: u128,492}493494#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]495pub enum MetaUpdatePermission {496	ItemOwner,497	Admin,498	None,499}500501#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]502#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]503pub enum CreateItemData {504	NFT(CreateNftData),505	Fungible(CreateFungibleData),506	ReFungible(CreateReFungibleData),507}508509#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]510#[derivative(Debug)]511pub struct CreateNftExData<CrossAccountId> {512	#[derivative(Debug(format_with = "bounded::vec_debug"))]513	pub const_data: BoundedVec<u8, CustomDataLimit>,514	#[derivative(Debug(format_with = "bounded::vec_debug"))]515	pub properties: CollectionPropertiesVec,516	pub owner: CrossAccountId,517}518519#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]520#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]521pub struct CreateRefungibleExData<CrossAccountId> {522	#[derivative(Debug(format_with = "bounded::vec_debug"))]523	pub const_data: BoundedVec<u8, CustomDataLimit>,524	#[derivative(Debug(format_with = "bounded::map_debug"))]525	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,526}527528#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]529#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]530pub enum CreateItemExData<CrossAccountId> {531	NFT(532		#[derivative(Debug(format_with = "bounded::vec_debug"))]533		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,534	),535	Fungible(536		#[derivative(Debug(format_with = "bounded::map_debug"))]537		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,538	),539	/// Many tokens, each may have only one owner540	RefungibleMultipleItems(541		#[derivative(Debug(format_with = "bounded::vec_debug"))]542		BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,543	),544	/// Single token, which may have many owners545	RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),546}547548impl CreateItemData {549	pub fn data_size(&self) -> usize {550		match self {551			CreateItemData::NFT(data) => data.const_data.len(),552			CreateItemData::ReFungible(data) => data.const_data.len(),553			_ => 0,554		}555	}556}557558impl From<CreateNftData> for CreateItemData {559	fn from(item: CreateNftData) -> Self {560		CreateItemData::NFT(item)561	}562}563564impl From<CreateReFungibleData> for CreateItemData {565	fn from(item: CreateReFungibleData) -> Self {566		CreateItemData::ReFungible(item)567	}568}569570impl From<CreateFungibleData> for CreateItemData {571	fn from(item: CreateFungibleData) -> Self {572		CreateItemData::Fungible(item)573	}574}575576#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]577#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]578pub struct CollectionStats {579	pub created: u32,580	pub destroyed: u32,581	pub alive: u32,582}583584#[derive(Encode, Decode, PartialEq, Clone, Debug)]585pub struct PhantomType<T>(core::marker::PhantomData<T>);586587impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {588	type Identity = PhantomType<T>;589590	fn type_info() -> scale_info::Type {591		use scale_info::{592			Type, Path,593			build::{FieldsBuilder, UnnamedFields},594			type_params,595		};596		Type::builder()597			.path(Path::new("up_data_structs", "PhantomType"))598			.type_params(type_params!(T))599			.composite(<FieldsBuilder<UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()))600	}601}602impl<T> MaxEncodedLen for PhantomType<T> {603	fn max_encoded_len() -> usize {604		0605	}606}607608pub type PropertyKey = BoundedVec<u8, ConstU32<MAX_PROPERTY_KEY_LENGTH>>;609pub type PropertyValue = BoundedVec<u8, ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;610611#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]612#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]613pub struct PropertyPermission {614	pub mutable: bool,615	pub collection_admin: bool,616	pub token_owner: bool,617}618619impl PropertyPermission {620	pub fn none() -> Self {621		Self {622			mutable: true,623			collection_admin: false,624			token_owner: false,625		}626	}627}628629#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]630#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]631pub struct Property {632	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]633	pub key: PropertyKey,634635	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]636	pub value: PropertyValue,637}638639#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]640#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]641pub struct PropertyKeyPermission {642	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]643	pub key: PropertyKey,644645	pub permission: PropertyPermission,646}647648pub enum PropertiesError {649	NoSpaceForProperty,650	PropertyLimitReached,651	InvalidCharacterInPropertyKey,652	EmptyPropertyKey,653}654655pub trait TrySet: Sized {656	type Value;657658	fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError>;659660	fn try_set_from_iter<I>(&mut self, iter: I) -> Result<(), PropertiesError>661	where662		I: Iterator<Item = (PropertyKey, Self::Value)>,663	{664		for (key, value) in iter {665			self.try_set(key, value)?;666		}667668		Ok(())669	}670}671672#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]673#[derivative(Default(bound = ""))]674pub struct PropertiesMap<Value>(675	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,676);677678impl<Value> PropertiesMap<Value> {679	pub fn new() -> Self {680		Self(BoundedBTreeMap::new())681	}682683	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {684		Self::check_property_key(key)?;685686		Ok(self.0.remove(key))687	}688689	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {690		self.0.get(key)691	}692693	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {694		self.0.iter()695	}696697	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {698		if key.is_empty() {699			return Err(PropertiesError::EmptyPropertyKey);700		}701702		for byte in key.as_slice().iter() {703			match char::from_u32(*byte as u32) {704				Some(ch)705					if ch.is_ascii_alphanumeric()706					|| ch == '_'707					|| ch == '-' => { /* OK */ },708				_ => return Err(PropertiesError::InvalidCharacterInPropertyKey)709			}710		}711712		Ok(())713	}714}715716impl<Value> TrySet for PropertiesMap<Value> {717	type Value = Value;718719	fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> {720		Self::check_property_key(&key)?;721722		self.0723			.try_insert(key, value)724			.map_err(|_| PropertiesError::PropertyLimitReached)?;725726		Ok(())727	}728}729730pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;731732#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]733pub struct Properties {734	map: PropertiesMap<PropertyValue>,735	consumed_space: u32,736	space_limit: u32,737}738739impl Properties {740	pub fn new(space_limit: u32) -> Self {741		Self {742			map: PropertiesMap::new(),743			consumed_space: 0,744			space_limit,745		}746	}747748	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {749		let value = self.map.remove(key)?;750751		if let Some(ref value) = value {752			let value_len = value.len() as u32;753			self.consumed_space -= value_len;754		}755756		Ok(value)757	}758759	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {760		self.map.get(key)761	}762763	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &PropertyValue)> {764		self.map.iter()765	}766}767768impl TrySet for Properties {769	type Value = PropertyValue;770771	fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> {772		let value_len = value.len();773774		if self.consumed_space as usize + value_len > self.space_limit as usize {775			return Err(PropertiesError::NoSpaceForProperty);776		}777778		self.map.try_set(key, value)?;779780		self.consumed_space += value_len as u32;781782		Ok(())783	}784}785786pub struct CollectionProperties;787788impl Get<Properties> for CollectionProperties {789	fn get() -> Properties {790		Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)791	}792}793794pub struct TokenProperties;795796impl Get<Properties> for TokenProperties {797	fn get() -> Properties {798		Properties::new(MAX_TOKEN_PROPERTIES_SIZE)799	}800}