--- a/primitives/data-structs/src/bounded.rs +++ b/primitives/data-structs/src/bounded.rs @@ -1,3 +1,21 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! This module contins implementations for support bounded structures ([`BoundedVec`], [`BoundedBTreeMap`], [`BoundedBTreeSet`]) in [`serde`]. + use core::fmt; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use sp_std::vec::Vec; @@ -7,7 +25,7 @@ storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet}, }; -/// BoundedVec doesn't supports serde +/// [`serde`] implementations for [`BoundedVec`]. #[cfg(feature = "serde1")] pub mod vec_serde { use core::convert::TryFrom; @@ -39,6 +57,7 @@ } } +/// Format [`BoundedVec`] for debug output. pub fn vec_debug(v: &BoundedVec, f: &mut fmt::Formatter) -> Result<(), fmt::Error> where V: fmt::Debug, @@ -49,6 +68,7 @@ #[cfg(feature = "serde1")] #[allow(dead_code)] +/// [`serde`] implementations for [`BoundedBTreeMap`]. pub mod map_serde { use core::convert::TryFrom; use sp_std::collections::btree_map::BTreeMap; @@ -84,6 +104,7 @@ } } +/// Format [`BoundedBTreeMap`] for debug output. pub fn map_debug( v: &BoundedBTreeMap, f: &mut fmt::Formatter, @@ -98,6 +119,7 @@ #[cfg(feature = "serde1")] #[allow(dead_code)] +/// [`serde`] implementations for [`BoundedBTreeSet`]. pub mod set_serde { use core::convert::TryFrom; use sp_std::collections::btree_set::BTreeSet; @@ -129,6 +151,7 @@ } } +/// Format [`BoundedBTreeSet`] for debug output. pub fn set_debug(v: &BoundedBTreeSet, f: &mut fmt::Formatter) -> Result<(), fmt::Error> where K: fmt::Debug + Ord, --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +//! # Primitives crate. +//! +//! This crate contains types, traits and constants. + #![cfg_attr(not(feature = "std"), no_std)] use core::{ @@ -55,38 +59,55 @@ pub mod mapping; mod migration; +/// Maximum of decimal points. pub const MAX_DECIMAL_POINTS: DecimalPoints = 30; + +/// Maximum pieces for refungible token. pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000; pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000; +/// Maximum tokens for user. pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) { 100_000 } else { 10 }; + +/// Maximum for collections can be created. pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 100_000 } else { 10 }; + +/// Maximum for various custom data of token. pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 2048 } else { 10 }; + +/// Maximum admins per collection. pub const COLLECTION_ADMINS_LIMIT: u32 = 5; + +/// Maximum tokens per collection. pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX; + +/// Maximum tokens per account. pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 1_000_000 } else { 10 }; -// Timeouts for item types in passed blocks +/// Default timeout for transfer sponsoring NFT item. pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for transfer sponsoring fungible item. pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for transfer sponsoring refungible item. pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for sponsored approving. pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5; // Schema limits @@ -94,27 +115,44 @@ pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192; pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768; +// TODO: not used. Delete? pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT; +/// Maximum length for collection name. pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64; + +/// Maximum length for collection description. pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256; + +/// Maximal token prefix length. pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16; +/// Maximal lenght of property key. pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256; + +/// Maximal lenght of property value. pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768; + +/// Maximum properties that can be assigned to token. pub const MAX_PROPERTIES_PER_ITEM: u32 = 64; +/// Maximal lenght of extended property value. pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048; +/// Maximum size for all collection properties. pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960; + +/// Maximum size for all token properties. pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768; /// How much items can be created per single -/// create_many call +/// create_many call. pub const MAX_ITEMS_PER_BATCH: u32 = 200; +/// Used for limit bounded types of token custom data. pub type CustomDataLimit = ConstU32; +/// Collection id. #[derive( Encode, Decode, @@ -134,6 +172,7 @@ impl EncodeLike for CollectionId {} impl EncodeLike for u32 {} +/// Token id. #[derive( Encode, Decode, @@ -154,6 +193,9 @@ impl EncodeLike for u32 {} impl TokenId { + /// Try to get next token id. + /// + /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned. pub fn try_next(self) -> Result { self.0 .checked_add(1) @@ -176,14 +218,21 @@ } } +/// Token data. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct TokenData { + /// Properties of token. pub properties: Vec, + + /// Token owner. pub owner: Option, + + /// Token pieces. pub pieces: u128, } +// TODO: unused type pub struct OverflowError; impl From for &'static str { fn from(_: OverflowError) -> Self { @@ -191,17 +240,27 @@ } } +/// Alias for decimal points type. pub type DecimalPoints = u8; +/// Collection mode. +/// +/// Collection can represent various types of tokens. +/// Each collection can contain only one type of tokens at a time. +/// This type helps to understand which tokens the collection contains. #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum CollectionMode { + /// Non fungible tokens. NFT, + /// Fungible tokens. Fungible(DecimalPoints), + /// Refungible tokens. ReFungible, } impl CollectionMode { + /// Get collection mod as number. pub fn id(&self) -> u8 { match self { CollectionMode::NFT => 1, @@ -211,14 +270,18 @@ } } +// TODO: unused trait pub trait SponsoringResolve { fn resolve(who: &AccountId, call: &Call) -> Option; } +/// Access mode for some token operations. #[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum AccessMode { + /// Access grant for owner and admins. Used as default. Normal, + /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. AllowList, } impl Default for AccessMode { @@ -227,6 +290,7 @@ } } +// TODO: remove in future. #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SchemaVersion { @@ -239,6 +303,7 @@ } } +// TODO: unused type #[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Ownership { @@ -246,19 +311,21 @@ pub fraction: u128, } +/// The state of collection sponsorship. #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SponsorshipState { - /// The fees are applied to the transaction sender + /// The fees are applied to the transaction sender. Disabled, - /// Pending confirmation from a sponsor-to-be + /// The sponsor is under consideration. Until the sponsor gives his consent, + /// the fee will still be charged to sender. Unconfirmed(AccountId), - /// Transactions are sponsored by specified account + /// Transactions are sponsored by specified account. Confirmed(AccountId), } impl SponsorshipState { - /// Get the acting sponsor account, if present + /// Get a sponsor of the collection who has confirmed his status. pub fn sponsor(&self) -> Option<&AccountId> { match self { Self::Confirmed(sponsor) => Some(sponsor), @@ -266,7 +333,7 @@ } } - /// Get the sponsor account currently pending confirmation, if present + /// Get a sponsor of the collection who has pending or confirmed status. pub fn pending_sponsor(&self) -> Option<&AccountId> { match self { Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor), @@ -274,7 +341,7 @@ } } - /// Is sponsorship set and acting + /// Whether the sponsorship is confirmed. pub fn confirmed(&self) -> bool { matches!(self, Self::Confirmed(_)) } @@ -290,16 +357,32 @@ pub type CollectionDescription = BoundedVec>; pub type CollectionTokenPrefix = BoundedVec>; +/// Base structure for represent collection. +/// +/// Used to provide basic functionality for all types of collections. +/// +/// #### Note /// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version). #[struct_versioning::versioned(version = 2, upper)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)] pub struct Collection { + /// Collection owner account. pub owner: AccountId, + + /// Collection mode. pub mode: CollectionMode, + + /// Access mode. #[version(..2)] pub access: AccessMode, + + /// Collection name. pub name: CollectionName, + + /// Collection description. pub description: CollectionDescription, + + /// Token prefix. pub token_prefix: CollectionTokenPrefix, #[version(..2)] @@ -310,10 +393,14 @@ #[version(..2)] pub schema_version: SchemaVersion, + + /// The state of sponsorship of the collection. pub sponsorship: SponsorshipState, + /// Collection limits. pub limits: CollectionLimits, + /// Collection permissions. #[version(2.., upper(Default::default()))] pub permissions: CollectionPermissions, @@ -335,116 +422,215 @@ #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct RpcCollection { + /// Collection owner account. pub owner: AccountId, + + /// Collection mode. pub mode: CollectionMode, + + /// Collection name. pub name: Vec, + + /// Collection description. pub description: Vec, + + /// Token prefix. pub token_prefix: Vec, + + /// The state of sponsorship of the collection. pub sponsorship: SponsorshipState, + + /// Collection limits. pub limits: CollectionLimits, + + /// Collection permissions. pub permissions: CollectionPermissions, + + /// Token property permissions. pub token_property_permissions: Vec, + + /// Collection properties. pub properties: Vec, + + /// Is collection read only. pub read_only: bool, } +/// Data used for create collection. +/// +/// All fields are wrapped in [`Option`], where `None` means chain default. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)] #[derivative(Debug, Default(bound = ""))] pub struct CreateCollectionData { + /// Collection mode. #[derivative(Default(value = "CollectionMode::NFT"))] pub mode: CollectionMode, + + /// Access mode. pub access: Option, + + /// Collection name. pub name: CollectionName, + + /// Collection description. pub description: CollectionDescription, + + /// Token prefix. pub token_prefix: CollectionTokenPrefix, + + /// Pending collection sponsor. pub pending_sponsor: Option, + + /// Collection limits. pub limits: Option, + + /// Collection permissions. pub permissions: Option, + + /// Token property permissions. pub token_property_permissions: CollectionPropertiesPermissionsVec, + + /// Collection properties. pub properties: CollectionPropertiesVec, } +/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`]. +// TODO: maybe rename to PropertiesPermissionsVec pub type CollectionPropertiesPermissionsVec = BoundedVec>; +/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`]. pub type CollectionPropertiesVec = BoundedVec>; /// Limits and restrictions of a collection. -/// All fields are wrapped in `Option`s, where None means chain default. /// -/// todo:doc links to chain defaults +/// All fields are wrapped in [`Option`], where `None` means chain default. +/// +/// Update with `pallet_common::Pallet::clamp_limits`. // IMPORTANT: When adding/removing fields from this struct - don't forget to also -// update clamp_limits() in pallet-common. #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`. +// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`. +// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values. pub struct CollectionLimits { - /// Maximum number of owned tokens per account. Chain default: [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`] + /// How many tokens can a user have on one account. + /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`]. + /// * Limit - [`MAX_TOKEN_OWNERSHIP`]. pub account_token_ownership_limit: Option, - /// Maximum size of data in bytes of a sponsored transaction. Chain default: [`CUSTOM_DATA_LIMIT`] + + /// How many bytes of data are available for sponsorship. + /// * Default - [`CUSTOM_DATA_LIMIT`]. + /// * Limit - [`CUSTOM_DATA_LIMIT`]. pub sponsored_data_size: Option, - /// FIXME should we delete this or repurpose it? - /// None - setVariableMetadata is not sponsored - /// Some(v) - setVariableMetadata is sponsored - /// if there is v block between txs + // FIXME should we delete this or repurpose it? + /// Times in how many blocks we sponsor data. + /// + /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions. + /// + /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled). + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. /// /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] pub sponsored_data_rate_limit: Option, /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`] + + /// How many tokens can be mined into this collection. + /// + /// * Default - [`COLLECTION_TOKEN_LIMIT`]. + /// * Limit - [`COLLECTION_TOKEN_LIMIT`]. pub token_limit: Option, - /// Timeout for sponsoring a token transfer in passed blocks. Chain default: - /// either [`NFT_SPONSOR_TRANSFER_TIMEOUT`], [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`], or [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`], - /// depending on the collection type. + /// Timeouts for transfer sponsoring. + /// + /// * Default + /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`] + /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`] + /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`] + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. pub sponsor_transfer_timeout: Option, - /// Timeout for sponsoring an approval in passed blocks. Chain default: [`SPONSOR_APPROVE_TIMEOUT`] + + /// Timeout for sponsoring an approval in passed blocks. + /// + /// * Default - [`SPONSOR_APPROVE_TIMEOUT`]. + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. pub sponsor_approve_timeout: Option, - /// Can a token be transferred by the owner. Chain default: `false` + + /// Whether the collection owner of the collection can send tokens (which belong to other users). + /// + /// * Default - **false**. pub owner_can_transfer: Option, - /// Can a token be burned by the owner. Chain default: `true` + + /// Can the collection owner burn other people's tokens. + /// + /// * Default - **true**. pub owner_can_destroy: Option, - /// Can a token be transferred at all. Chain default: `true` + + /// Is it possible to send tokens from this collection between users. + /// + /// * Default - **true**. pub transfers_enabled: Option, } impl CollectionLimits { + /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit). pub fn account_token_ownership_limit(&self) -> u32 { self.account_token_ownership_limit .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT) .min(MAX_TOKEN_OWNERSHIP) } + + /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size). pub fn sponsored_data_size(&self) -> u32 { self.sponsored_data_size .unwrap_or(CUSTOM_DATA_LIMIT) .min(CUSTOM_DATA_LIMIT) } + + /// Get effective value for [`token_limit`](self.token_limit). pub fn token_limit(&self) -> u32 { self.token_limit .unwrap_or(COLLECTION_TOKEN_LIMIT) .min(COLLECTION_TOKEN_LIMIT) } + + // TODO: may be replace u32 to mode? + /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout). pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 { self.sponsor_transfer_timeout .unwrap_or(default) .min(MAX_SPONSOR_TIMEOUT) } + + /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout). pub fn sponsor_approve_timeout(&self) -> u32 { self.sponsor_approve_timeout .unwrap_or(SPONSOR_APPROVE_TIMEOUT) .min(MAX_SPONSOR_TIMEOUT) } + + /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer). pub fn owner_can_transfer(&self) -> bool { self.owner_can_transfer.unwrap_or(false) } + + /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled). pub fn owner_can_transfer_instaled(&self) -> bool { self.owner_can_transfer.is_some() } + + /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy). pub fn owner_can_destroy(&self) -> bool { self.owner_can_destroy.unwrap_or(true) } + + /// Get effective value for [`transfers_enabled`](self.transfers_enabled). pub fn transfers_enabled(&self) -> bool { self.transfers_enabled.unwrap_or(true) } + + /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit). pub fn sponsored_data_rate_limit(&self) -> Option { match self .sponsored_data_rate_limit @@ -457,24 +643,46 @@ } /// Permissions on certain operations within a collection. -/// All fields are wrapped in `Option`s, where None means chain default. -// IMPORTANT: When adding/removing fields from this struct - don't forget to also -// update clamp_limits() in pallet-common. +/// +/// Some fields are wrapped in [`Option`], where `None` means chain default. +/// +/// Update with `pallet_common::Pallet::clamp_permissions`. #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`. +// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`. pub struct CollectionPermissions { + /// Access mode. + /// + /// * Default - [`AccessMode::Normal`]. pub access: Option, + + /// Minting allowance. + /// + /// * Default - **false**. pub mint_mode: Option, + + /// Permissions for nesting. + /// + /// * Default + /// - `token_owner` - **false** + /// - `collection_admin` - **false** + /// - `restricted` - **None** pub nesting: Option, } impl CollectionPermissions { + /// Get effective value for [`access`](self.access). pub fn access(&self) -> AccessMode { self.access.unwrap_or(AccessMode::Normal) } + + /// Get effective value for [`mint_mode`](self.mint_mode). pub fn mint_mode(&self) -> bool { self.mint_mode.unwrap_or(false) } + + /// Get effective value for [`nesting`](self.nesting). pub fn nesting(&self) -> &NestingPermissions { static DEFAULT: NestingPermissions = NestingPermissions { token_owner: false, @@ -487,8 +695,10 @@ } } +/// Inner set for collections allowed to nest. type OwnerRestrictedSetInner = BoundedBTreeSet>; +/// Wraper for collections set allowing nest. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derivative(Debug)] @@ -497,7 +707,9 @@ #[derivative(Debug(format_with = "bounded::set_debug"))] pub OwnerRestrictedSetInner, ); + impl OwnerRestrictedSet { + /// Create new set. pub fn new() -> Self { Self(Default::default()) } @@ -519,19 +731,21 @@ #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derivative(Debug)] pub struct NestingPermissions { - /// Owner of token can nest tokens under it + /// Owner of token can nest tokens under it. pub token_owner: bool, - /// Admin of token collection can nest tokens under token + /// Admin of token collection can nest tokens under token. pub collection_admin: bool, - /// If set - only tokens from specified collections can be nested + /// If set - only tokens from specified collections can be nested. pub restricted: Option, #[cfg(feature = "runtime-benchmarks")] - /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin` + /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`. pub permissive: bool, } /// Enum denominating how often can sponsoring occur if it is enabled. +/// +/// Used for [`collection limits`](CollectionLimits). #[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SponsoringRateLimit { @@ -549,6 +763,7 @@ /// Key-value pairs used to describe the token as metadata #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] #[derivative(Debug(format_with = "bounded::vec_debug"))] + /// Properties that wil be assignet to created item. pub properties: CollectionPropertiesVec, } @@ -570,7 +785,7 @@ #[derivative(Debug(format_with = "bounded::vec_debug"))] pub const_data: BoundedVec, - /// Number of pieces the RFT is split into + /// Pieces of created token. pub pieces: u128, /// Key-value pairs used to describe the token as metadata @@ -579,6 +794,7 @@ pub properties: CollectionPropertiesVec, } +// TODO: remove this. #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum MetaUpdatePermission { @@ -588,57 +804,75 @@ } /// Enum holding data used for creation of all three item types. +/// Unified data for create item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum CreateItemData { + /// Data for create NFT. NFT(CreateNftData), + /// Data for create Fungible item. Fungible(CreateFungibleData), + /// Data for create ReFungible item. ReFungible(CreateReFungibleData), } -/// Explicit NFT creation data with meta parameters. +/// Extended data for create NFT. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug)] pub struct CreateNftExData { + /// Properties that wil be assignet to created item. #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, + + /// Owner of creating item. pub owner: CrossAccountId, } -/// Explicit RFT creation data with meta parameters. +/// Extended data for create ReFungible item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] pub struct CreateRefungibleExData { + /// Custom data stored in token. #[derivative(Debug(format_with = "bounded::vec_debug"))] pub const_data: BoundedVec, + + /// Users who will be assigned the specified number of token parts. #[derivative(Debug(format_with = "bounded::map_debug"))] pub users: BoundedBTreeMap>, #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, } -/// Explicit item creation data with meta parameters, namely the owner. +/// Unified extended data for creating item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] pub enum CreateItemExData { + /// Extended data for create NFT. NFT( #[derivative(Debug(format_with = "bounded::vec_debug"))] BoundedVec, ConstU32>, ), + + /// Extended data for create Fungible item. Fungible( #[derivative(Debug(format_with = "bounded::map_debug"))] BoundedBTreeMap>, ), - /// Many tokens, each may have only one owner + + /// Extended data for create ReFungible item in case of + /// many tokens, each may have only one owner RefungibleMultipleItems( #[derivative(Debug(format_with = "bounded::vec_debug"))] BoundedVec, ConstU32>, ), - /// Single token, which may have many owners + + /// Extended data for create ReFungible item in case of + /// single token, which may have many owners RefungibleMultipleOwners(CreateRefungibleExData), } impl CreateItemData { + /// Get size of custom data. pub fn data_size(&self) -> usize { match self { CreateItemData::ReFungible(data) => data.const_data.len(), @@ -670,18 +904,28 @@ #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] // todo possibly rename to be used generally as an address pair pub struct TokenChild { + /// Token id. pub token: TokenId, + + /// Collection id. pub collection: CollectionId, } +/// Collection statistics. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct CollectionStats { + /// Number of created items. pub created: u32, + + /// Number of burned items. pub destroyed: u32, + + /// Number of current items. pub alive: u32, } +/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata. #[derive(Encode, Decode, Clone, Debug)] #[cfg_attr(feature = "std", derive(PartialEq))] pub struct PhantomType(core::marker::PhantomData); @@ -707,22 +951,36 @@ } } +/// Bounded vector of bytes. pub type BoundedBytes = BoundedVec; +/// Extra properties for external collections. pub type AuxPropertyValue = BoundedBytes>; +/// Property key. pub type PropertyKey = BoundedBytes>; + +/// Property value. pub type PropertyValue = BoundedBytes>; +/// Property permission. #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct PropertyPermission { + /// Permission to change the property and property permission. + /// + /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**. pub mutable: bool, + + /// Change permission for the collection administrator. pub collection_admin: bool, + + /// Permission to change the property for the owner of the token. pub token_owner: bool, } impl PropertyPermission { + /// Creates mutable property permission but changes restricted for collection admin and token owner. pub fn none() -> Self { Self { mutable: true, @@ -732,12 +990,15 @@ } } +/// Property is simpl key-value record. #[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Property { + /// Property key. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + /// Property value. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub value: PropertyValue, } @@ -748,12 +1009,15 @@ } } +/// Record for proprty key permission. #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct PropertyKeyPermission { + /// Key. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + /// Permission. pub permission: PropertyPermission, } @@ -763,15 +1027,35 @@ } } +/// Errors for properties actions. #[derive(Debug)] pub enum PropertiesError { + /// The space allocated for properties has run out. + /// + /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`]. + /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`]. NoSpaceForProperty, + + /// The property limit has been reached. + /// + /// * Limit - [`MAX_PROPERTIES_PER_ITEM`]. PropertyLimitReached, + + /// Property key contains not allowed character. InvalidCharacterInPropertyKey, + + /// Property key length is too long. + /// + /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`]. PropertyKeyIsTooLong, + + /// Property key is empty. EmptyPropertyKey, } +/// Marker for scope of property. +/// +/// Scoped property can't be changed by user. Used for external collections. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)] pub enum PropertyScope { None, @@ -779,6 +1063,7 @@ } impl PropertyScope { + /// Apply scope to property key. pub fn apply(self, key: PropertyKey) -> Result { let scope_str: &[u8] = match self { Self::None => return Ok(key), @@ -792,9 +1077,11 @@ } } +/// Trait for operate with properties. pub trait TrySetProperty: Sized { type Value; + /// Try to set property with scope. fn try_scoped_set( &mut self, scope: PropertyScope, @@ -802,6 +1089,7 @@ value: Self::Value, ) -> Result<(), PropertiesError>; + /// Try to set property with scope from iterator. fn try_scoped_set_from_iter( &mut self, scope: PropertyScope, @@ -819,10 +1107,12 @@ Ok(()) } + /// Try to set property. fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> { self.try_scoped_set(PropertyScope::None, key, value) } + /// Try to set property from iterator. fn try_set_from_iter(&mut self, iter: I) -> Result<(), PropertiesError> where I: Iterator, @@ -832,6 +1122,7 @@ } } +/// Wrapped map for storing properties. #[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)] #[derivative(Default(bound = ""))] pub struct PropertiesMap( @@ -839,24 +1130,29 @@ ); impl PropertiesMap { + /// Create new property map. pub fn new() -> Self { Self(BoundedBTreeMap::new()) } + /// Remove property from map. pub fn remove(&mut self, key: &PropertyKey) -> Result, PropertiesError> { Self::check_property_key(key)?; Ok(self.0.remove(key)) } + /// Get property with appropriate key from map. pub fn get(&self, key: &PropertyKey) -> Option<&Value> { self.0.get(key) } + /// Check if map contains key. pub fn contains_key(&self, key: &PropertyKey) -> bool { self.0.contains_key(key) } + /// Check if map contains key with key validation. fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> { if key.is_empty() { return Err(PropertiesError::EmptyPropertyKey); @@ -909,8 +1205,10 @@ } } +/// Alias for property permissions map. pub type PropertiesPermissionMap = PropertiesMap; +/// Wrapper for properties map with consumed space control. #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)] pub struct Properties { map: PropertiesMap, @@ -919,6 +1217,7 @@ } impl Properties { + /// Create new properies container. pub fn new(space_limit: u32) -> Self { Self { map: PropertiesMap::new(), @@ -927,6 +1226,7 @@ } } + /// Remove propery with appropiate key. pub fn remove(&mut self, key: &PropertyKey) -> Result, PropertiesError> { let value = self.map.remove(key)?; @@ -938,6 +1238,7 @@ Ok(value) } + /// Get property with appropriate key. pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> { self.map.get(key) } @@ -977,6 +1278,7 @@ } } +/// Utility struct for using in `StorageMap`. pub struct CollectionProperties; impl Get for CollectionProperties { @@ -985,6 +1287,7 @@ } } +/// Utility struct for using in `StorageMap`. pub struct TokenProperties; impl Get for TokenProperties { --- a/primitives/data-structs/src/mapping.rs +++ b/primitives/data-structs/src/mapping.rs @@ -1,3 +1,21 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! This module contains mapping between different addresses. + use core::marker::PhantomData; use sp_core::H160; @@ -5,12 +23,19 @@ use crate::{CollectionId, TokenId}; use pallet_evm::account::CrossAccountId; +/// Trait for mapping between token id and some `Address`. pub trait TokenAddressMapping
{ + /// Map token id to `Address`. fn token_to_address(collection: CollectionId, token: TokenId) -> Address; + + /// Map `Address` to token id. fn address_to_token(address: &Address) -> Option<(CollectionId, TokenId)>; + + /// Check is address for token. fn is_token_address(address: &Address) -> bool; } +/// Unit struct for mapping token id to/from *Evm address* represented by [`H160`]. pub struct EvmTokenAddressMapping; /// 0xf8238ccfff8ed887463fd5e00000000100000002 - collection 1, token 2 @@ -46,6 +71,7 @@ } } +/// Unit struct for mapping token id to/from [`CrossAccountId`]. pub struct CrossTokenAddressMapping(PhantomData); impl> TokenAddressMapping for CrossTokenAddressMapping { --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -28,6 +28,7 @@ sp_api::decl_runtime_apis! { #[api_version(2)] + /// Trait for generate rpc. pub trait UniqueApi where AccountId: Decode, CrossAccountId: pallet_evm::account::CrossAccountId, @@ -35,36 +36,57 @@ #[changed_in(2)] fn token_owner(collection: CollectionId, token: TokenId) -> Result; + /// Get number of tokens in collection owned by account. fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result>; + + /// Number of existing tokens in collection. fn collection_tokens(collection: CollectionId) -> Result>; + + /// Check token exist. fn token_exists(collection: CollectionId, token: TokenId) -> Result; + /// Get token owner. fn token_owner(collection: CollectionId, token: TokenId) -> Result>; + + /// Get real owner of nested token. fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result>; + + /// Get nested tokens for the specified item. fn token_children(collection: CollectionId, token: TokenId) -> Result>; + /// Get collection properties. fn collection_properties(collection: CollectionId, properties: Option>>) -> Result>; + /// Get token properties. fn token_properties( collection: CollectionId, token_id: TokenId, properties: Option>> ) -> Result>; + /// Get permissions for token properties. fn property_permissions( collection: CollectionId, properties: Option>> ) -> Result>; + /// Get token data. fn token_data( collection: CollectionId, token_id: TokenId, keys: Option>> ) -> Result>; + /// Total number of tokens in collection. fn total_supply(collection: CollectionId) -> Result; + + /// Get account balance for collection (sum of tokens pieces). fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result; + + /// Get account balance for specified token. fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result; + + /// Amount of token pieces allowed to spend from granded account. fn allowance( collection: CollectionId, sender: CrossAccountId, @@ -72,14 +94,31 @@ token: TokenId, ) -> Result; + /// Get list of collection admins. fn adminlist(collection: CollectionId) -> Result>; + + /// Get list of users that allowet to mint tikens in collection. fn allowlist(collection: CollectionId) -> Result>; + + /// Check that user is in allowed list (see [`allowlist`]). fn allowed(collection: CollectionId, user: CrossAccountId) -> Result; + + /// Last minted token id. fn last_token_id(collection: CollectionId) -> Result; + + /// Get collection by id. fn collection_by_id(collection: CollectionId) -> Result>>; + + /// Get collection stats. fn collection_stats() -> Result; + + /// Get the number of blocks through which sponsorship will be available. fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result>; + + /// Get effective colletion limits. fn effective_collection_limits(collection_id: CollectionId) -> Result>; + + /// Get total pieces of token. fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result>; fn token_owners(collection: CollectionId, token: TokenId) -> Result>; }