git.delta.rocks / unique-network / refs/commits / 991825ed7280

difftreelog

source

primitives/data-structs/src/lib.rs39.0 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::{28	storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},29	traits::Get,30};3132#[cfg(feature = "serde")]33use serde::{Serialize, Deserialize};3435use sp_core::U256;36use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};37use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use bondrewd::Bitfields;39use frame_support::{BoundedVec, traits::ConstU32};40use derivative::Derivative;41use scale_info::TypeInfo;4243mod bondrewd_codec;44mod bounded;45pub mod budget;46pub mod mapping;47mod migration;4849/// Maximum of decimal points.50pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5152/// Maximum pieces for refungible token.53pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;54pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5556/// Maximum tokens for user.57pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {58	100_00059} else {60	1061};6263/// Maximum for collections can be created.64pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {65	100_00066} else {67	1068};6970/// Maximum for various custom data of token.71pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {72	204873} else {74	1075};7677/// Maximum admins per collection.78pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7980/// Maximum tokens per collection.81pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8283/// Maximum tokens per account.84pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {85	1_000_00086} else {87	1088};8990/// Default timeout for transfer sponsoring NFT item.91pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;92/// Default timeout for transfer sponsoring fungible item.93pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;94/// Default timeout for transfer sponsoring refungible item.95pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9697/// Default timeout for sponsored approving.98pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;99100// Schema limits101pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;102pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;104105// TODO: not used. Delete?106pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;107108/// Maximal length of a collection name.109pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;110111/// Maximal length of a collection description.112pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;113114/// Maximal length of a token prefix.115pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;116117/// Maximal length of a property key.118pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;119120/// Maximal length of a property value.121pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;122123/// A maximum number of token properties.124pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;125126/// Maximal lenght of extended property value.127pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;128129/// Maximum size for all collection properties.130pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;131132/// Maximum size of all token properties.133pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;134135/// How much items can be created per single136/// create_many call.137pub const MAX_ITEMS_PER_BATCH: u32 = 200;138139/// Used for limit bounded types of token custom data.140pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;141142/// Collection id.143#[derive(144	Encode,145	Decode,146	PartialEq,147	Eq,148	PartialOrd,149	Ord,150	Clone,151	Copy,152	Debug,153	Default,154	TypeInfo,155	MaxEncodedLen,156)]157#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]158pub struct CollectionId(pub u32);159impl EncodeLike<u32> for CollectionId {}160impl EncodeLike<CollectionId> for u32 {}161162impl From<u32> for CollectionId {163	fn from(value: u32) -> Self {164		Self(value)165	}166}167168/// Token id.169#[derive(170	Encode,171	Decode,172	PartialEq,173	Eq,174	PartialOrd,175	Ord,176	Clone,177	Copy,178	Debug,179	Default,180	TypeInfo,181	MaxEncodedLen,182)]183#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]184pub struct TokenId(pub u32);185impl EncodeLike<u32> for TokenId {}186impl EncodeLike<TokenId> for u32 {}187188impl TokenId {189	/// Try to get next token id.190	///191	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.192	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {193		self.0194			.checked_add(1)195			.ok_or(ArithmeticError::Overflow)196			.map(Self)197	}198}199200impl From<TokenId> for U256 {201	fn from(t: TokenId) -> Self {202		t.0.into()203	}204}205206impl TryFrom<U256> for TokenId {207	type Error = &'static str;208209	fn try_from(value: U256) -> Result<Self, Self::Error> {210		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))211	}212}213214/// Token data.215#[struct_versioning::versioned(version = 2, upper)]216#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]217#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]218pub struct TokenData<CrossAccountId> {219	/// Properties of token.220	pub properties: Vec<Property>,221222	/// Token owner.223	pub owner: Option<CrossAccountId>,224225	/// Token pieces.226	#[version(2.., upper(0))]227	pub pieces: u128,228}229230// TODO: unused type231pub struct OverflowError;232impl From<OverflowError> for &'static str {233	fn from(_: OverflowError) -> Self {234		"overflow occured"235	}236}237238/// Alias for decimal points type.239pub type DecimalPoints = u8;240241/// Collection mode.242///243/// Collection can represent various types of tokens.244/// Each collection can contain only one type of tokens at a time.245/// This type helps to understand which tokens the collection contains.246#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]247#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]248pub enum CollectionMode {249	/// Non fungible tokens.250	NFT,251	/// Fungible tokens.252	Fungible(DecimalPoints),253	/// Refungible tokens.254	ReFungible,255}256257impl CollectionMode {258	/// Get collection mod as number.259	pub fn id(&self) -> u8 {260		match self {261			CollectionMode::NFT => 1,262			CollectionMode::Fungible(_) => 2,263			CollectionMode::ReFungible => 3,264		}265	}266}267268// TODO: unused trait269pub trait SponsoringResolve<AccountId, Call> {270	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;271}272273/// Access mode for some token operations.274#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]275#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]276pub enum AccessMode {277	/// Access grant for owner and admins. Used as default.278	Normal,279	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.280	AllowList,281}282impl Default for AccessMode {283	fn default() -> Self {284		Self::Normal285	}286}287288// TODO: remove in future.289#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]290#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]291pub enum SchemaVersion {292	ImageURL,293	Unique,294}295impl Default for SchemaVersion {296	fn default() -> Self {297		Self::ImageURL298	}299}300301// TODO: unused type302#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]303#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]304pub struct Ownership<AccountId> {305	pub owner: AccountId,306	pub fraction: u128,307}308309/// The state of collection sponsorship.310#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]311#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]312pub enum SponsorshipState<AccountId> {313	/// The fees are applied to the transaction sender.314	Disabled,315	/// The sponsor is under consideration. Until the sponsor gives his consent,316	/// the fee will still be charged to sender.317	Unconfirmed(AccountId),318	/// Transactions are sponsored by specified account.319	Confirmed(AccountId),320}321322impl<AccountId> SponsorshipState<AccountId> {323	/// Get a sponsor of the collection who has confirmed his status.324	pub fn sponsor(&self) -> Option<&AccountId> {325		match self {326			Self::Confirmed(sponsor) => Some(sponsor),327			_ => None,328		}329	}330331	/// Get a sponsor of the collection who has pending or confirmed status.332	pub fn pending_sponsor(&self) -> Option<&AccountId> {333		match self {334			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),335			_ => None,336		}337	}338339	/// Whether the sponsorship is confirmed.340	pub fn confirmed(&self) -> bool {341		matches!(self, Self::Confirmed(_))342	}343}344345impl<T> Default for SponsorshipState<T> {346	fn default() -> Self {347		Self::Disabled348	}349}350351pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;352pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;353pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;354355#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]356#[bondrewd(enforce_bytes = 1)]357pub struct CollectionFlags {358	/// Tokens in foreign collections can be transferred, but not burnt359	#[bondrewd(bits = "0..1")]360	pub foreign: bool,361	/// Supports ERC721Metadata362	#[bondrewd(bits = "1..2")]363	pub erc721metadata: bool,364	/// External collections can't be managed using `unique` api365	#[bondrewd(bits = "7..8")]366	pub external: bool,367368	#[bondrewd(reserve, bits = "2..7")]369	pub reserved: u8,370}371bondrewd_codec!(CollectionFlags);372373/// Base structure for represent collection.374///375/// Used to provide basic functionality for all types of collections.376///377/// #### Note378/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).379#[struct_versioning::versioned(version = 2, upper)]380#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]381pub struct Collection<AccountId> {382	/// Collection owner account.383	pub owner: AccountId,384385	/// Collection mode.386	pub mode: CollectionMode,387388	/// Access mode.389	#[version(..2)]390	pub access: AccessMode,391392	/// Collection name.393	pub name: CollectionName,394395	/// Collection description.396	pub description: CollectionDescription,397398	/// Token prefix.399	pub token_prefix: CollectionTokenPrefix,400401	#[version(..2)]402	pub mint_mode: bool,403404	#[version(..2)]405	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,406407	#[version(..2)]408	pub schema_version: SchemaVersion,409410	/// The state of sponsorship of the collection.411	pub sponsorship: SponsorshipState<AccountId>,412413	/// Collection limits.414	pub limits: CollectionLimits,415416	/// Collection permissions.417	#[version(2.., upper(Default::default()))]418	pub permissions: CollectionPermissions,419420	#[version(2.., upper(Default::default()))]421	pub flags: CollectionFlags,422423	#[version(..2)]424	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,425426	#[version(..2)]427	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,428429	#[version(..2)]430	pub meta_update_permission: MetaUpdatePermission,431}432433#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]434#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]435pub struct RpcCollectionFlags {436	/// Is collection is foreign.437	pub foreign: bool,438	/// Collection supports ERC721Metadata.439	pub erc721metadata: bool,440}441442/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).443#[struct_versioning::versioned(version = 2, upper)]444#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]445#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]446pub struct RpcCollection<AccountId> {447	/// Collection owner account.448	pub owner: AccountId,449450	/// Collection mode.451	pub mode: CollectionMode,452453	/// Collection name.454	pub name: Vec<u16>,455456	/// Collection description.457	pub description: Vec<u16>,458459	/// Token prefix.460	pub token_prefix: Vec<u8>,461462	/// The state of sponsorship of the collection.463	pub sponsorship: SponsorshipState<AccountId>,464465	/// Collection limits.466	pub limits: CollectionLimits,467468	/// Collection permissions.469	pub permissions: CollectionPermissions,470471	/// Token property permissions.472	pub token_property_permissions: Vec<PropertyKeyPermission>,473474	/// Collection properties.475	pub properties: Vec<Property>,476477	/// Is collection read only.478	pub read_only: bool,479480	/// Extra collection flags481	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]482	pub flags: RpcCollectionFlags,483}484485/// Data used for create collection.486///487/// All fields are wrapped in [`Option`], where `None` means chain default.488#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]489#[derivative(Debug, Default(bound = ""))]490pub struct CreateCollectionData<AccountId> {491	/// Collection mode.492	#[derivative(Default(value = "CollectionMode::NFT"))]493	pub mode: CollectionMode,494495	/// Access mode.496	pub access: Option<AccessMode>,497498	/// Collection name.499	pub name: CollectionName,500501	/// Collection description.502	pub description: CollectionDescription,503504	/// Token prefix.505	pub token_prefix: CollectionTokenPrefix,506507	/// Pending collection sponsor.508	pub pending_sponsor: Option<AccountId>,509510	/// Collection limits.511	pub limits: Option<CollectionLimits>,512513	/// Collection permissions.514	pub permissions: Option<CollectionPermissions>,515516	/// Token property permissions.517	pub token_property_permissions: CollectionPropertiesPermissionsVec,518519	/// Collection properties.520	pub properties: CollectionPropertiesVec,521}522523/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].524// TODO: maybe rename to PropertiesPermissionsVec525pub type CollectionPropertiesPermissionsVec =526	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;527528/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].529pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;530531/// Limits and restrictions of a collection.532///533/// All fields are wrapped in [`Option`], where `None` means chain default.534///535/// Update with `pallet_common::Pallet::clamp_limits`.536// IMPORTANT: When adding/removing fields from this struct - don't forget to also537#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]538#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]539// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.540// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.541// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.542pub struct CollectionLimits {543	/// How many tokens can a user have on one account.544	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].545	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].546	pub account_token_ownership_limit: Option<u32>,547548	/// How many bytes of data are available for sponsorship.549	/// * Default - [`CUSTOM_DATA_LIMIT`].550	/// * Limit - [`CUSTOM_DATA_LIMIT`].551	pub sponsored_data_size: Option<u32>,552553	// FIXME should we delete this or repurpose it?554	/// Times in how many blocks we sponsor data.555	///556	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.557	///558	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).559	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].560	///561	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]562	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,563	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]564565	/// How many tokens can be mined into this collection.566	///567	/// * Default - [`COLLECTION_TOKEN_LIMIT`].568	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].569	pub token_limit: Option<u32>,570571	/// Timeouts for transfer sponsoring.572	///573	/// * Default574	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]575	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]576	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]577	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].578	pub sponsor_transfer_timeout: Option<u32>,579580	/// Timeout for sponsoring an approval in passed blocks.581	///582	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].583	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].584	pub sponsor_approve_timeout: Option<u32>,585586	/// Whether the collection owner of the collection can send tokens (which belong to other users).587	///588	/// * Default - **false**.589	pub owner_can_transfer: Option<bool>,590591	/// Can the collection owner burn other people's tokens.592	///593	/// * Default - **true**.594	pub owner_can_destroy: Option<bool>,595596	/// Is it possible to send tokens from this collection between users.597	///598	/// * Default - **true**.599	pub transfers_enabled: Option<bool>,600}601602impl CollectionLimits {603	pub fn with_default_limits(collection_type: CollectionMode) -> Self {604		CollectionLimits {605			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),606			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),607			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),608			token_limit: Some(COLLECTION_TOKEN_LIMIT),609			sponsor_transfer_timeout: match collection_type {610				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),611				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),612				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),613			},614			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),615			owner_can_transfer: Some(false),616			owner_can_destroy: Some(true),617			transfers_enabled: Some(true),618		}619	}620621	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).622	pub fn account_token_ownership_limit(&self) -> u32 {623		self.account_token_ownership_limit624			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)625			.min(MAX_TOKEN_OWNERSHIP)626	}627628	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).629	pub fn sponsored_data_size(&self) -> u32 {630		self.sponsored_data_size631			.unwrap_or(CUSTOM_DATA_LIMIT)632			.min(CUSTOM_DATA_LIMIT)633	}634635	/// Get effective value for [`token_limit`](self.token_limit).636	pub fn token_limit(&self) -> u32 {637		self.token_limit638			.unwrap_or(COLLECTION_TOKEN_LIMIT)639			.min(COLLECTION_TOKEN_LIMIT)640	}641642	// TODO: may be replace u32 to mode?643	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).644	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {645		self.sponsor_transfer_timeout646			.unwrap_or(default)647			.min(MAX_SPONSOR_TIMEOUT)648	}649650	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).651	pub fn sponsor_approve_timeout(&self) -> u32 {652		self.sponsor_approve_timeout653			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)654			.min(MAX_SPONSOR_TIMEOUT)655	}656657	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).658	pub fn owner_can_transfer(&self) -> bool {659		self.owner_can_transfer.unwrap_or(false)660	}661662	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).663	pub fn owner_can_transfer_instaled(&self) -> bool {664		self.owner_can_transfer.is_some()665	}666667	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).668	pub fn owner_can_destroy(&self) -> bool {669		self.owner_can_destroy.unwrap_or(true)670	}671672	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).673	pub fn transfers_enabled(&self) -> bool {674		self.transfers_enabled.unwrap_or(true)675	}676677	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).678	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {679		match self680			.sponsored_data_rate_limit681			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)682		{683			SponsoringRateLimit::SponsoringDisabled => None,684			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),685		}686	}687}688689/// Permissions on certain operations within a collection.690///691/// Some fields are wrapped in [`Option`], where `None` means chain default.692///693/// Update with `pallet_common::Pallet::clamp_permissions`.694#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]695#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]696// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.697// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.698pub struct CollectionPermissions {699	/// Access mode.700	///701	/// * Default - [`AccessMode::Normal`].702	pub access: Option<AccessMode>,703704	/// Minting allowance.705	///706	/// * Default - **false**.707	pub mint_mode: Option<bool>,708709	/// Permissions for nesting.710	///711	/// * Default712	///   - `token_owner` - **false**713	///   - `collection_admin` - **false**714	///   - `restricted` - **None**715	pub nesting: Option<NestingPermissions>,716}717718impl CollectionPermissions {719	/// Get effective value for [`access`](self.access).720	pub fn access(&self) -> AccessMode {721		self.access.unwrap_or(AccessMode::Normal)722	}723724	/// Get effective value for [`mint_mode`](self.mint_mode).725	pub fn mint_mode(&self) -> bool {726		self.mint_mode.unwrap_or(false)727	}728729	/// Get effective value for [`nesting`](self.nesting).730	pub fn nesting(&self) -> &NestingPermissions {731		static DEFAULT: NestingPermissions = NestingPermissions {732			token_owner: false,733			collection_admin: false,734			restricted: None,735			#[cfg(feature = "runtime-benchmarks")]736			permissive: false,737		};738		self.nesting.as_ref().unwrap_or(&DEFAULT)739	}740}741742/// Inner set for collections allowed to nest.743type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;744745/// Wraper for collections set allowing nest.746#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]747#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]748#[derivative(Debug)]749pub struct OwnerRestrictedSet(750	#[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]751	#[derivative(Debug(format_with = "bounded::set_debug"))]752	pub OwnerRestrictedSetInner,753);754755impl OwnerRestrictedSet {756	/// Create new set.757	pub fn new() -> Self {758		Self(Default::default())759	}760}761impl core::ops::Deref for OwnerRestrictedSet {762	type Target = OwnerRestrictedSetInner;763	fn deref(&self) -> &Self::Target {764		&self.0765	}766}767impl core::ops::DerefMut for OwnerRestrictedSet {768	fn deref_mut(&mut self) -> &mut Self::Target {769		&mut self.0770	}771}772773/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.774#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]775#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]776#[derivative(Debug)]777pub struct NestingPermissions {778	/// Owner of token can nest tokens under it.779	pub token_owner: bool,780	/// Admin of token collection can nest tokens under token.781	pub collection_admin: bool,782	/// If set - only tokens from specified collections can be nested.783	pub restricted: Option<OwnerRestrictedSet>,784785	#[cfg(feature = "runtime-benchmarks")]786	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.787	pub permissive: bool,788}789790/// Enum denominating how often can sponsoring occur if it is enabled.791///792/// Used for [`collection limits`](CollectionLimits).793#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]794#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]795pub enum SponsoringRateLimit {796	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions797	SponsoringDisabled,798	/// Once per how many blocks can sponsorship of a transaction type occur799	Blocks(u32),800}801802/// Data used to describe an NFT at creation.803#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]804#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]805#[derivative(Debug)]806pub struct CreateNftData {807	/// Key-value pairs used to describe the token as metadata808	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]809	#[derivative(Debug(format_with = "bounded::vec_debug"))]810	/// Properties that wil be assignet to created item.811	pub properties: CollectionPropertiesVec,812}813814/// Data used to describe a Fungible token at creation.815#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]816#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]817pub struct CreateFungibleData {818	/// Number of fungible coins minted819	pub value: u128,820}821822/// Data used to describe a Refungible token at creation.823#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]824#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]825#[derivative(Debug)]826pub struct CreateReFungibleData {827	/// Number of pieces the RFT is split into828	pub pieces: u128,829830	/// Key-value pairs used to describe the token as metadata831	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]832	#[derivative(Debug(format_with = "bounded::vec_debug"))]833	pub properties: CollectionPropertiesVec,834}835836// TODO: remove this.837#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]838#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]839pub enum MetaUpdatePermission {840	ItemOwner,841	Admin,842	None,843}844845/// Enum holding data used for creation of all three item types.846/// Unified data for create item.847#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]848#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]849pub enum CreateItemData {850	/// Data for create NFT.851	NFT(CreateNftData),852	/// Data for create Fungible item.853	Fungible(CreateFungibleData),854	/// Data for create ReFungible item.855	ReFungible(CreateReFungibleData),856}857858/// Extended data for create NFT.859#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]860#[derivative(Debug)]861pub struct CreateNftExData<CrossAccountId> {862	/// Properties that wil be assignet to created item.863	#[derivative(Debug(format_with = "bounded::vec_debug"))]864	pub properties: CollectionPropertiesVec,865866	/// Owner of creating item.867	pub owner: CrossAccountId,868}869870/// Extended data for create ReFungible item.871#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]872#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]873pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {874	#[derivative(Debug(format_with = "bounded::map_debug"))]875	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,876	#[derivative(Debug(format_with = "bounded::vec_debug"))]877	pub properties: CollectionPropertiesVec,878}879880/// Extended data for create ReFungible item.881#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]882#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]883pub struct CreateRefungibleExSingleOwner<CrossAccountId> {884	pub user: CrossAccountId,885	pub pieces: u128,886	#[derivative(Debug(format_with = "bounded::vec_debug"))]887	pub properties: CollectionPropertiesVec,888}889890/// Unified extended data for creating item.891#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]892#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]893pub enum CreateItemExData<CrossAccountId> {894	/// Extended data for create NFT.895	NFT(896		#[derivative(Debug(format_with = "bounded::vec_debug"))]897		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,898	),899900	/// Extended data for create Fungible item.901	Fungible(902		#[derivative(Debug(format_with = "bounded::map_debug"))]903		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,904	),905906	/// Extended data for create ReFungible item in case of907	/// many tokens, each may have only one owner908	RefungibleMultipleItems(909		#[derivative(Debug(format_with = "bounded::vec_debug"))]910		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,911	),912913	/// Extended data for create ReFungible item in case of914	/// single token, which may have many owners915	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),916}917918impl From<CreateNftData> for CreateItemData {919	fn from(item: CreateNftData) -> Self {920		CreateItemData::NFT(item)921	}922}923924impl From<CreateReFungibleData> for CreateItemData {925	fn from(item: CreateReFungibleData) -> Self {926		CreateItemData::ReFungible(item)927	}928}929930impl From<CreateFungibleData> for CreateItemData {931	fn from(item: CreateFungibleData) -> Self {932		CreateItemData::Fungible(item)933	}934}935936/// Token's address, dictated by its collection and token IDs.937#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]938#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]939// todo possibly rename to be used generally as an address pair940pub struct TokenChild {941	/// Token id.942	pub token: TokenId,943944	/// Collection id.945	pub collection: CollectionId,946}947948/// Collection statistics.949#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]950#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]951pub struct CollectionStats {952	/// Number of created items.953	pub created: u32,954955	/// Number of burned items.956	pub destroyed: u32,957958	/// Number of current items.959	pub alive: u32,960}961962/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.963#[derive(Encode, Decode, Clone, Debug)]964#[cfg_attr(feature = "std", derive(PartialEq))]965pub struct PhantomType<T>(core::marker::PhantomData<T>);966967impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {968	type Identity = PhantomType<T>;969970	fn type_info() -> scale_info::Type {971		use scale_info::{972			Type, Path,973			build::{FieldsBuilder, UnnamedFields},974			form::MetaForm,975			type_params,976		};977		Type::builder()978			.path(Path::new("up_data_structs", "PhantomType"))979			.type_params(type_params!(T))980			.composite(981				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),982			)983	}984}985impl<T> MaxEncodedLen for PhantomType<T> {986	fn max_encoded_len() -> usize {987		0988	}989}990991/// Bounded vector of bytes.992pub type BoundedBytes<S> = BoundedVec<u8, S>;993994/// Extra properties for external collections.995pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;996997/// Property key.998pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;9991000/// Property value.1001pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;10021003/// Property permission.1004#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1005#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1006pub struct PropertyPermission {1007	/// Permission to change the property and property permission.1008	///1009	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1010	pub mutable: bool,10111012	/// Change permission for the collection administrator.1013	pub collection_admin: bool,10141015	/// Permission to change the property for the owner of the token.1016	pub token_owner: bool,1017}10181019impl PropertyPermission {1020	/// Creates mutable property permission but changes restricted for collection admin and token owner.1021	pub fn none() -> Self {1022		Self {1023			mutable: true,1024			collection_admin: false,1025			token_owner: false,1026		}1027	}1028}10291030/// Property is simpl key-value record.1031#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1032#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1033pub struct Property {1034	/// Property key.1035	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1036	pub key: PropertyKey,10371038	/// Property value.1039	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1040	pub value: PropertyValue,1041}10421043impl Into<(PropertyKey, PropertyValue)> for Property {1044	fn into(self) -> (PropertyKey, PropertyValue) {1045		(self.key, self.value)1046	}1047}10481049/// Record for proprty key permission.1050#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1051#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1052pub struct PropertyKeyPermission {1053	/// Key.1054	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1055	pub key: PropertyKey,10561057	/// Permission.1058	pub permission: PropertyPermission,1059}10601061impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1062	fn into(self) -> (PropertyKey, PropertyPermission) {1063		(self.key, self.permission)1064	}1065}10661067/// Errors for properties actions.1068#[derive(Debug)]1069pub enum PropertiesError {1070	/// The space allocated for properties has run out.1071	///1072	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1073	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1074	NoSpaceForProperty,10751076	/// The property limit has been reached.1077	///1078	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1079	PropertyLimitReached,10801081	/// Property key contains not allowed character.1082	InvalidCharacterInPropertyKey,10831084	/// Property key length is too long.1085	///1086	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1087	PropertyKeyIsTooLong,10881089	/// Property key is empty.1090	EmptyPropertyKey,1091}10921093/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1094#[derive(Debug)]1095pub enum TokenOwnerError {1096	NotFound,1097	MultipleOwners,1098}10991100/// Marker for scope of property.1101///1102/// Scoped property can't be changed by user. Used for external collections.1103#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1104pub enum PropertyScope {1105	None,1106	Rmrk,1107}11081109impl PropertyScope {1110	/// Apply scope to property key.1111	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1112		let scope_str: &[u8] = match self {1113			Self::None => return Ok(key),1114			Self::Rmrk => b"rmrk",1115		};11161117		[scope_str, b":", key.as_slice()]1118			.concat()1119			.try_into()1120			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1121	}1122}11231124/// Trait for operate with properties.1125pub trait TrySetProperty: Sized {1126	type Value;11271128	/// Try to set property with scope.1129	fn try_scoped_set(1130		&mut self,1131		scope: PropertyScope,1132		key: PropertyKey,1133		value: Self::Value,1134	) -> Result<Option<Self::Value>, PropertiesError>;11351136	/// Try to set property with scope from iterator.1137	fn try_scoped_set_from_iter<I, KV>(1138		&mut self,1139		scope: PropertyScope,1140		iter: I,1141	) -> Result<(), PropertiesError>1142	where1143		I: Iterator<Item = KV>,1144		KV: Into<(PropertyKey, Self::Value)>,1145	{1146		for kv in iter {1147			let (key, value) = kv.into();1148			self.try_scoped_set(scope, key, value)?;1149		}11501151		Ok(())1152	}11531154	/// Try to set property.1155	fn try_set(1156		&mut self,1157		key: PropertyKey,1158		value: Self::Value,1159	) -> Result<Option<Self::Value>, PropertiesError> {1160		self.try_scoped_set(PropertyScope::None, key, value)1161	}11621163	/// Try to set property from iterator.1164	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1165	where1166		I: Iterator<Item = KV>,1167		KV: Into<(PropertyKey, Self::Value)>,1168	{1169		self.try_scoped_set_from_iter(PropertyScope::None, iter)1170	}1171}11721173/// Wrapped map for storing properties.1174#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1175#[derivative(Default(bound = ""))]1176pub struct PropertiesMap<Value>(1177	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1178);11791180impl<Value> PropertiesMap<Value> {1181	/// Create new property map.1182	pub fn new() -> Self {1183		Self(BoundedBTreeMap::new())1184	}11851186	/// Remove property from map.1187	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1188		Self::check_property_key(key)?;11891190		Ok(self.0.remove(key))1191	}11921193	/// Get property with appropriate key from map.1194	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1195		self.0.get(key)1196	}11971198	/// Check if map contains key.1199	pub fn contains_key(&self, key: &PropertyKey) -> bool {1200		self.0.contains_key(key)1201	}12021203	/// Check if map contains key with key validation.1204	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1205		if key.is_empty() {1206			return Err(PropertiesError::EmptyPropertyKey);1207		}12081209		for byte in key.as_slice().iter() {1210			let byte = *byte;12111212			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1213				return Err(PropertiesError::InvalidCharacterInPropertyKey);1214			}1215		}12161217		Ok(())1218	}12191220	pub fn values(&self) -> impl Iterator<Item = &Value> {1221		self.0.values()1222	}1223}12241225impl<Value> IntoIterator for PropertiesMap<Value> {1226	type Item = (PropertyKey, Value);1227	type IntoIter = <1228		BoundedBTreeMap<1229			PropertyKey,1230			Value,1231			ConstU32<MAX_PROPERTIES_PER_ITEM>1232		> as IntoIterator1233	>::IntoIter;12341235	fn into_iter(self) -> Self::IntoIter {1236		self.0.into_iter()1237	}1238}12391240impl<Value> TrySetProperty for PropertiesMap<Value> {1241	type Value = Value;12421243	fn try_scoped_set(1244		&mut self,1245		scope: PropertyScope,1246		key: PropertyKey,1247		value: Self::Value,1248	) -> Result<Option<Self::Value>, PropertiesError> {1249		Self::check_property_key(&key)?;12501251		let key = scope.apply(key)?;1252		self.01253			.try_insert(key, value)1254			.map_err(|_| PropertiesError::PropertyLimitReached)1255	}1256}12571258/// Alias for property permissions map.1259pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;12601261/// Wrapper for properties map with consumed space control.1262#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1263pub struct Properties {1264	map: PropertiesMap<PropertyValue>,1265	consumed_space: u32,1266	space_limit: u32,1267}12681269impl Properties {1270	/// Create new properies container.1271	pub fn new(space_limit: u32) -> Self {1272		Self {1273			map: PropertiesMap::new(),1274			consumed_space: 0,1275			space_limit,1276		}1277	}12781279	/// Remove propery with appropiate key.1280	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1281		let value = self.map.remove(key)?;12821283		if let Some(ref value) = value {1284			let value_len = value.len() as u32;1285			self.consumed_space -= value_len;1286		}12871288		Ok(value)1289	}12901291	/// Get property with appropriate key.1292	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1293		self.map.get(key)1294	}12951296	/// Recomputes the consumed space for the current properties state.1297	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1298	pub fn recompute_consumed_space(&mut self) {1299		self.consumed_space = self.map.values().map(|value| value.len() as u32).sum();1300	}1301}13021303impl IntoIterator for Properties {1304	type Item = (PropertyKey, PropertyValue);1305	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;13061307	fn into_iter(self) -> Self::IntoIter {1308		self.map.into_iter()1309	}1310}13111312impl TrySetProperty for Properties {1313	type Value = PropertyValue;13141315	fn try_scoped_set(1316		&mut self,1317		scope: PropertyScope,1318		key: PropertyKey,1319		value: Self::Value,1320	) -> Result<Option<Self::Value>, PropertiesError> {1321		let value_len = value.len();13221323		if self.consumed_space as usize + value_len > self.space_limit as usize1324			&& !cfg!(feature = "runtime-benchmarks")1325		{1326			return Err(PropertiesError::NoSpaceForProperty);1327		}13281329		let value_len = value_len as u32;1330		let old_value = self.map.try_scoped_set(scope, key, value)?;13311332		let old_value_len = old_value.as_ref().map(|v| v.len() as u32).unwrap_or(0);13331334		if value_len > old_value_len {1335			self.consumed_space += value_len - old_value_len;1336		} else {1337			self.consumed_space -= old_value_len - value_len;1338		}13391340		Ok(old_value)1341	}1342}13431344/// Utility struct for using in `StorageMap`.1345pub struct CollectionProperties;13461347impl Get<Properties> for CollectionProperties {1348	fn get() -> Properties {1349		Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)1350	}1351}13521353/// Utility struct for using in `StorageMap`.1354pub struct TokenProperties;13551356impl Get<Properties> for TokenProperties {1357	fn get() -> Properties {1358		Properties::new(MAX_TOKEN_PROPERTIES_SIZE)1359	}1360}