difftreelog
fix versioning of the RpcCollection
in: master
3 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -353,7 +353,7 @@
};
let result = $(if _api_version < $ver {
- api.$changed_method_name(at, $($changed_name),*).map(|r| r.map($fixer))
+ api.$changed_method_name(at, $($changed_name),*).map(|r| r.and_then($fixer))
} else)*
{ api.$method_name(at, $($((|$map_arg: $ty| $map))? ($name)),*) };
@@ -440,7 +440,7 @@
#[map(|keys| string_keys_to_bytes_keys(keys))]
keys: Option<Vec<String>>,
) -> TokenData<CrossAccountId>, unique_api;
- changed_in 3, token_data_before_version_3(collection, token_id, string_keys_to_bytes_keys(keys)) => |value| value.into()
+ changed_in 3, token_data_before_version_3(collection, token_id, string_keys_to_bytes_keys(keys)) => |value| Ok(value.into())
);
pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);
@@ -449,7 +449,22 @@
pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);
pass_method!(
collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api;
- changed_in 3, collection_by_id_before_version_3(collection) => |value| value.map(|coll| coll.into())
+ changed_in 3, collection_by_id_before_version_3(collection) => |value| {
+ use codec::IoReader;
+ use up_data_structs::RpcCollectionVersion1;
+ use up_data_structs::CollectionVersion1;
+ use sp_runtime::DispatchError;
+
+ if let Some(bytes) = value {
+ let mut reader = IoReader(bytes.as_slice());
+ Ok(Some(RpcCollection::<AccountId>::decode(&mut reader)
+ .or_else(|_| RpcCollectionVersion1::<AccountId>::decode(&mut reader).map(|col| col.into()))
+ .or_else(|_| CollectionVersion1::<AccountId>::decode(&mut reader).map(|col| col.into()))
+ .map_err(|_| DispatchError::Other("API Error: UniqueApi_collection_by_id"))?))
+ } else {
+ Ok(None)
+ }
+ }
);
pass_method!(collection_stats() -> CollectionStats, unique_api);
pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);
primitives/data-structs/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26};27use frame_support::storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet};2829#[cfg(feature = "serde")]30use serde::{Serialize, Deserialize};3132use sp_core::U256;33use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};34use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};35use bondrewd::Bitfields;36use frame_support::{BoundedVec, traits::ConstU32};37use derivative::Derivative;38use scale_info::TypeInfo;3940mod bondrewd_codec;41mod bounded;42pub mod budget;43pub mod mapping;44mod migration;4546/// Maximum of decimal points.47pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4849/// Maximum pieces for refungible token.50pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5253/// Maximum tokens for user.54pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {55 100_00056} else {57 1058};5960/// Maximum for collections can be created.61pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {62 100_00063} else {64 1065};6667/// Maximum for various custom data of token.68pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {69 204870} else {71 1072};7374/// Maximum admins per collection.75pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7677/// Maximum tokens per collection.78pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;7980/// Maximum tokens per account.81pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {82 1_000_00083} else {84 1085};8687/// Default timeout for transfer sponsoring NFT item.88pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;89/// Default timeout for transfer sponsoring fungible item.90pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;91/// Default timeout for transfer sponsoring refungible item.92pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9394/// Default timeout for sponsored approving.95pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;9697// Schema limits98pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;99pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;100pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;101102// TODO: not used. Delete?103pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;104105/// Maximal length of a collection name.106pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;107108/// Maximal length of a collection description.109pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;110111/// Maximal length of a token prefix.112pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;113114/// Maximal length of a property key.115pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;116117/// Maximal length of a property value.118pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;119120/// A maximum number of token properties.121pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;122123/// Maximal lenght of extended property value.124pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;125126/// Maximum size for all collection properties.127pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;128129/// Maximum size of all token properties.130pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;131132/// How much items can be created per single133/// create_many call.134pub const MAX_ITEMS_PER_BATCH: u32 = 200;135136/// Used for limit bounded types of token custom data.137pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;138139/// Collection id.140#[derive(141 Encode,142 Decode,143 PartialEq,144 Eq,145 PartialOrd,146 Ord,147 Clone,148 Copy,149 Debug,150 Default,151 TypeInfo,152 MaxEncodedLen,153)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub struct CollectionId(pub u32);156impl EncodeLike<u32> for CollectionId {}157impl EncodeLike<CollectionId> for u32 {}158159impl From<u32> for CollectionId {160 fn from(value: u32) -> Self {161 Self(value)162 }163}164165/// Token id.166#[derive(167 Encode,168 Decode,169 PartialEq,170 Eq,171 PartialOrd,172 Ord,173 Clone,174 Copy,175 Debug,176 Default,177 TypeInfo,178 MaxEncodedLen,179)]180#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]181pub struct TokenId(pub u32);182impl EncodeLike<u32> for TokenId {}183impl EncodeLike<TokenId> for u32 {}184185impl TokenId {186 /// Try to get next token id.187 ///188 /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.189 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {190 self.0191 .checked_add(1)192 .ok_or(ArithmeticError::Overflow)193 .map(Self)194 }195}196197impl From<TokenId> for U256 {198 fn from(t: TokenId) -> Self {199 t.0.into()200 }201}202203impl TryFrom<U256> for TokenId {204 type Error = &'static str;205206 fn try_from(value: U256) -> Result<Self, Self::Error> {207 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))208 }209}210211/// Token data.212#[struct_versioning::versioned(version = 2, upper)]213#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]214#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]215pub struct TokenData<CrossAccountId> {216 /// Properties of token.217 pub properties: Vec<Property>,218219 /// Token owner.220 pub owner: Option<CrossAccountId>,221222 /// Token pieces.223 #[version(2.., upper(0))]224 pub pieces: u128,225}226227// TODO: unused type228pub struct OverflowError;229impl From<OverflowError> for &'static str {230 fn from(_: OverflowError) -> Self {231 "overflow occured"232 }233}234235/// Alias for decimal points type.236pub type DecimalPoints = u8;237238/// Collection mode.239///240/// Collection can represent various types of tokens.241/// Each collection can contain only one type of tokens at a time.242/// This type helps to understand which tokens the collection contains.243#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]244#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]245pub enum CollectionMode {246 /// Non fungible tokens.247 NFT,248 /// Fungible tokens.249 Fungible(DecimalPoints),250 /// Refungible tokens.251 ReFungible,252}253254impl CollectionMode {255 /// Get collection mod as number.256 pub fn id(&self) -> u8 {257 match self {258 CollectionMode::NFT => 1,259 CollectionMode::Fungible(_) => 2,260 CollectionMode::ReFungible => 3,261 }262 }263}264265// TODO: unused trait266pub trait SponsoringResolve<AccountId, Call> {267 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;268}269270/// Access mode for some token operations.271#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]272#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]273pub enum AccessMode {274 /// Access grant for owner and admins. Used as default.275 Normal,276 /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.277 AllowList,278}279impl Default for AccessMode {280 fn default() -> Self {281 Self::Normal282 }283}284285// TODO: remove in future.286#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]287#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]288pub enum SchemaVersion {289 ImageURL,290 Unique,291}292impl Default for SchemaVersion {293 fn default() -> Self {294 Self::ImageURL295 }296}297298// TODO: unused type299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]300#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]301pub struct Ownership<AccountId> {302 pub owner: AccountId,303 pub fraction: u128,304}305306/// The state of collection sponsorship.307#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]308#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]309pub enum SponsorshipState<AccountId> {310 /// The fees are applied to the transaction sender.311 Disabled,312 /// The sponsor is under consideration. Until the sponsor gives his consent,313 /// the fee will still be charged to sender.314 Unconfirmed(AccountId),315 /// Transactions are sponsored by specified account.316 Confirmed(AccountId),317}318319impl<AccountId> SponsorshipState<AccountId> {320 /// Get a sponsor of the collection who has confirmed his status.321 pub fn sponsor(&self) -> Option<&AccountId> {322 match self {323 Self::Confirmed(sponsor) => Some(sponsor),324 _ => None,325 }326 }327328 /// Get a sponsor of the collection who has pending or confirmed status.329 pub fn pending_sponsor(&self) -> Option<&AccountId> {330 match self {331 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),332 _ => None,333 }334 }335336 /// Whether the sponsorship is confirmed.337 pub fn confirmed(&self) -> bool {338 matches!(self, Self::Confirmed(_))339 }340}341342impl<T> Default for SponsorshipState<T> {343 fn default() -> Self {344 Self::Disabled345 }346}347348pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;349pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;350pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;351352#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]353#[bondrewd(enforce_bytes = 1)]354pub struct CollectionFlags {355 /// Tokens in foreign collections can be transferred, but not burnt356 #[bondrewd(bits = "0..1")]357 pub foreign: bool,358 /// Supports ERC721Metadata359 #[bondrewd(bits = "1..2")]360 pub erc721metadata: bool,361 /// External collections can't be managed using `unique` api362 #[bondrewd(bits = "7..8")]363 pub external: bool,364365 #[bondrewd(reserve, bits = "2..7")]366 pub reserved: u8,367}368bondrewd_codec!(CollectionFlags);369370/// Base structure for represent collection.371///372/// Used to provide basic functionality for all types of collections.373///374/// #### Note375/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).376#[struct_versioning::versioned(version = 2, upper)]377#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]378pub struct Collection<AccountId> {379 /// Collection owner account.380 pub owner: AccountId,381382 /// Collection mode.383 pub mode: CollectionMode,384385 /// Access mode.386 #[version(..2)]387 pub access: AccessMode,388389 /// Collection name.390 pub name: CollectionName,391392 /// Collection description.393 pub description: CollectionDescription,394395 /// Token prefix.396 pub token_prefix: CollectionTokenPrefix,397398 #[version(..2)]399 pub mint_mode: bool,400401 #[version(..2)]402 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,403404 #[version(..2)]405 pub schema_version: SchemaVersion,406407 /// The state of sponsorship of the collection.408 pub sponsorship: SponsorshipState<AccountId>,409410 /// Collection limits.411 pub limits: CollectionLimits,412413 /// Collection permissions.414 #[version(2.., upper(Default::default()))]415 pub permissions: CollectionPermissions,416417 #[version(2.., upper(Default::default()))]418 pub flags: CollectionFlags,419420 #[version(..2)]421 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,422423 #[version(..2)]424 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,425426 #[version(..2)]427 pub meta_update_permission: MetaUpdatePermission,428}429430#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]431#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]432pub struct RpcCollectionFlags {433 /// Is collection is foreign.434 pub foreign: bool,435 /// Collection supports ERC721Metadata.436 pub erc721metadata: bool,437}438439/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).440#[struct_versioning::versioned(version = 2, upper)]441#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]442#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]443pub struct RpcCollection<AccountId> {444 /// Collection owner account.445 pub owner: AccountId,446447 /// Collection mode.448 pub mode: CollectionMode,449450 /// Collection name.451 pub name: Vec<u16>,452453 /// Collection description.454 pub description: Vec<u16>,455456 /// Token prefix.457 pub token_prefix: Vec<u8>,458459 /// The state of sponsorship of the collection.460 pub sponsorship: SponsorshipState<AccountId>,461462 /// Collection limits.463 pub limits: CollectionLimits,464465 /// Collection permissions.466 pub permissions: CollectionPermissions,467468 /// Token property permissions.469 pub token_property_permissions: Vec<PropertyKeyPermission>,470471 /// Collection properties.472 pub properties: Vec<Property>,473474 /// Is collection read only.475 pub read_only: bool,476477 /// Extra collection flags478 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]479 pub flags: RpcCollectionFlags,480}481482/// Data used for create collection.483///484/// All fields are wrapped in [`Option`], where `None` means chain default.485#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]486#[derivative(Debug, Default(bound = ""))]487pub struct CreateCollectionData<AccountId> {488 /// Collection mode.489 #[derivative(Default(value = "CollectionMode::NFT"))]490 pub mode: CollectionMode,491492 /// Access mode.493 pub access: Option<AccessMode>,494495 /// Collection name.496 pub name: CollectionName,497498 /// Collection description.499 pub description: CollectionDescription,500501 /// Token prefix.502 pub token_prefix: CollectionTokenPrefix,503504 /// Pending collection sponsor.505 pub pending_sponsor: Option<AccountId>,506507 /// Collection limits.508 pub limits: Option<CollectionLimits>,509510 /// Collection permissions.511 pub permissions: Option<CollectionPermissions>,512513 /// Token property permissions.514 pub token_property_permissions: CollectionPropertiesPermissionsVec,515516 /// Collection properties.517 pub properties: CollectionPropertiesVec,518}519520/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].521// TODO: maybe rename to PropertiesPermissionsVec522pub type CollectionPropertiesPermissionsVec =523 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;524525/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].526pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;527528/// Limits and restrictions of a collection.529///530/// All fields are wrapped in [`Option`], where `None` means chain default.531///532/// Update with `pallet_common::Pallet::clamp_limits`.533// IMPORTANT: When adding/removing fields from this struct - don't forget to also534#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]535#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]536// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.537// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.538// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.539pub struct CollectionLimits {540 /// How many tokens can a user have on one account.541 /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].542 /// * Limit - [`MAX_TOKEN_OWNERSHIP`].543 pub account_token_ownership_limit: Option<u32>,544545 /// How many bytes of data are available for sponsorship.546 /// * Default - [`CUSTOM_DATA_LIMIT`].547 /// * Limit - [`CUSTOM_DATA_LIMIT`].548 pub sponsored_data_size: Option<u32>,549550 // FIXME should we delete this or repurpose it?551 /// Times in how many blocks we sponsor data.552 ///553 /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.554 ///555 /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).556 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].557 ///558 /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]559 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,560 /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]561562 /// How many tokens can be mined into this collection.563 ///564 /// * Default - [`COLLECTION_TOKEN_LIMIT`].565 /// * Limit - [`COLLECTION_TOKEN_LIMIT`].566 pub token_limit: Option<u32>,567568 /// Timeouts for transfer sponsoring.569 ///570 /// * Default571 /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]572 /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]573 /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]574 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].575 pub sponsor_transfer_timeout: Option<u32>,576577 /// Timeout for sponsoring an approval in passed blocks.578 ///579 /// * Default - [`SPONSOR_APPROVE_TIMEOUT`].580 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].581 pub sponsor_approve_timeout: Option<u32>,582583 /// Whether the collection owner of the collection can send tokens (which belong to other users).584 ///585 /// * Default - **false**.586 pub owner_can_transfer: Option<bool>,587588 /// Can the collection owner burn other people's tokens.589 ///590 /// * Default - **true**.591 pub owner_can_destroy: Option<bool>,592593 /// Is it possible to send tokens from this collection between users.594 ///595 /// * Default - **true**.596 pub transfers_enabled: Option<bool>,597}598599impl CollectionLimits {600 pub fn with_default_limits(collection_type: CollectionMode) -> Self {601 CollectionLimits {602 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),603 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),604 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),605 token_limit: Some(COLLECTION_TOKEN_LIMIT),606 sponsor_transfer_timeout: match collection_type {607 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),608 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),609 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),610 },611 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),612 owner_can_transfer: Some(false),613 owner_can_destroy: Some(true),614 transfers_enabled: Some(true),615 }616 }617618 /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).619 pub fn account_token_ownership_limit(&self) -> u32 {620 self.account_token_ownership_limit621 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)622 .min(MAX_TOKEN_OWNERSHIP)623 }624625 /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).626 pub fn sponsored_data_size(&self) -> u32 {627 self.sponsored_data_size628 .unwrap_or(CUSTOM_DATA_LIMIT)629 .min(CUSTOM_DATA_LIMIT)630 }631632 /// Get effective value for [`token_limit`](self.token_limit).633 pub fn token_limit(&self) -> u32 {634 self.token_limit635 .unwrap_or(COLLECTION_TOKEN_LIMIT)636 .min(COLLECTION_TOKEN_LIMIT)637 }638639 // TODO: may be replace u32 to mode?640 /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).641 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {642 self.sponsor_transfer_timeout643 .unwrap_or(default)644 .min(MAX_SPONSOR_TIMEOUT)645 }646647 /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).648 pub fn sponsor_approve_timeout(&self) -> u32 {649 self.sponsor_approve_timeout650 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)651 .min(MAX_SPONSOR_TIMEOUT)652 }653654 /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).655 pub fn owner_can_transfer(&self) -> bool {656 self.owner_can_transfer.unwrap_or(false)657 }658659 /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).660 pub fn owner_can_transfer_instaled(&self) -> bool {661 self.owner_can_transfer.is_some()662 }663664 /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).665 pub fn owner_can_destroy(&self) -> bool {666 self.owner_can_destroy.unwrap_or(true)667 }668669 /// Get effective value for [`transfers_enabled`](self.transfers_enabled).670 pub fn transfers_enabled(&self) -> bool {671 self.transfers_enabled.unwrap_or(true)672 }673674 /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).675 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {676 match self677 .sponsored_data_rate_limit678 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)679 {680 SponsoringRateLimit::SponsoringDisabled => None,681 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),682 }683 }684}685686/// Permissions on certain operations within a collection.687///688/// Some fields are wrapped in [`Option`], where `None` means chain default.689///690/// Update with `pallet_common::Pallet::clamp_permissions`.691#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]692#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]693// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.694// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.695pub struct CollectionPermissions {696 /// Access mode.697 ///698 /// * Default - [`AccessMode::Normal`].699 pub access: Option<AccessMode>,700701 /// Minting allowance.702 ///703 /// * Default - **false**.704 pub mint_mode: Option<bool>,705706 /// Permissions for nesting.707 ///708 /// * Default709 /// - `token_owner` - **false**710 /// - `collection_admin` - **false**711 /// - `restricted` - **None**712 pub nesting: Option<NestingPermissions>,713}714715impl CollectionPermissions {716 /// Get effective value for [`access`](self.access).717 pub fn access(&self) -> AccessMode {718 self.access.unwrap_or(AccessMode::Normal)719 }720721 /// Get effective value for [`mint_mode`](self.mint_mode).722 pub fn mint_mode(&self) -> bool {723 self.mint_mode.unwrap_or(false)724 }725726 /// Get effective value for [`nesting`](self.nesting).727 pub fn nesting(&self) -> &NestingPermissions {728 static DEFAULT: NestingPermissions = NestingPermissions {729 token_owner: false,730 collection_admin: false,731 restricted: None,732 #[cfg(feature = "runtime-benchmarks")]733 permissive: false,734 };735 self.nesting.as_ref().unwrap_or(&DEFAULT)736 }737}738739/// Inner set for collections allowed to nest.740type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;741742/// Wraper for collections set allowing nest.743#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]744#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]745#[derivative(Debug)]746pub struct OwnerRestrictedSet(747 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]748 #[derivative(Debug(format_with = "bounded::set_debug"))]749 pub OwnerRestrictedSetInner,750);751752impl OwnerRestrictedSet {753 /// Create new set.754 pub fn new() -> Self {755 Self(Default::default())756 }757}758impl core::ops::Deref for OwnerRestrictedSet {759 type Target = OwnerRestrictedSetInner;760 fn deref(&self) -> &Self::Target {761 &self.0762 }763}764impl core::ops::DerefMut for OwnerRestrictedSet {765 fn deref_mut(&mut self) -> &mut Self::Target {766 &mut self.0767 }768}769770/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.771#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]772#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]773#[derivative(Debug)]774pub struct NestingPermissions {775 /// Owner of token can nest tokens under it.776 pub token_owner: bool,777 /// Admin of token collection can nest tokens under token.778 pub collection_admin: bool,779 /// If set - only tokens from specified collections can be nested.780 pub restricted: Option<OwnerRestrictedSet>,781782 #[cfg(feature = "runtime-benchmarks")]783 /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.784 pub permissive: bool,785}786787/// Enum denominating how often can sponsoring occur if it is enabled.788///789/// Used for [`collection limits`](CollectionLimits).790#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]791#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]792pub enum SponsoringRateLimit {793 /// Sponsoring is disabled, and the collection sponsor will not pay for transactions794 SponsoringDisabled,795 /// Once per how many blocks can sponsorship of a transaction type occur796 Blocks(u32),797}798799/// Data used to describe an NFT at creation.800#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]801#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]802#[derivative(Debug)]803pub struct CreateNftData {804 /// Key-value pairs used to describe the token as metadata805 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]806 #[derivative(Debug(format_with = "bounded::vec_debug"))]807 /// Properties that wil be assignet to created item.808 pub properties: CollectionPropertiesVec,809}810811/// Data used to describe a Fungible token at creation.812#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]813#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]814pub struct CreateFungibleData {815 /// Number of fungible coins minted816 pub value: u128,817}818819/// Data used to describe a Refungible token at creation.820#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]821#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]822#[derivative(Debug)]823pub struct CreateReFungibleData {824 /// Number of pieces the RFT is split into825 pub pieces: u128,826827 /// Key-value pairs used to describe the token as metadata828 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]829 #[derivative(Debug(format_with = "bounded::vec_debug"))]830 pub properties: CollectionPropertiesVec,831}832833// TODO: remove this.834#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]835#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]836pub enum MetaUpdatePermission {837 ItemOwner,838 Admin,839 None,840}841842/// Enum holding data used for creation of all three item types.843/// Unified data for create item.844#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]845#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]846pub enum CreateItemData {847 /// Data for create NFT.848 NFT(CreateNftData),849 /// Data for create Fungible item.850 Fungible(CreateFungibleData),851 /// Data for create ReFungible item.852 ReFungible(CreateReFungibleData),853}854855/// Extended data for create NFT.856#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]857#[derivative(Debug)]858pub struct CreateNftExData<CrossAccountId> {859 /// Properties that wil be assignet to created item.860 #[derivative(Debug(format_with = "bounded::vec_debug"))]861 pub properties: CollectionPropertiesVec,862863 /// Owner of creating item.864 pub owner: CrossAccountId,865}866867/// Extended data for create ReFungible item.868#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]869#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]870pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {871 #[derivative(Debug(format_with = "bounded::map_debug"))]872 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,873 #[derivative(Debug(format_with = "bounded::vec_debug"))]874 pub properties: CollectionPropertiesVec,875}876877/// Extended data for create ReFungible item.878#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]879#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]880pub struct CreateRefungibleExSingleOwner<CrossAccountId> {881 pub user: CrossAccountId,882 pub pieces: u128,883 #[derivative(Debug(format_with = "bounded::vec_debug"))]884 pub properties: CollectionPropertiesVec,885}886887/// Unified extended data for creating item.888#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]889#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]890pub enum CreateItemExData<CrossAccountId> {891 /// Extended data for create NFT.892 NFT(893 #[derivative(Debug(format_with = "bounded::vec_debug"))]894 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,895 ),896897 /// Extended data for create Fungible item.898 Fungible(899 #[derivative(Debug(format_with = "bounded::map_debug"))]900 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,901 ),902903 /// Extended data for create ReFungible item in case of904 /// many tokens, each may have only one owner905 RefungibleMultipleItems(906 #[derivative(Debug(format_with = "bounded::vec_debug"))]907 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,908 ),909910 /// Extended data for create ReFungible item in case of911 /// single token, which may have many owners912 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),913}914915impl From<CreateNftData> for CreateItemData {916 fn from(item: CreateNftData) -> Self {917 CreateItemData::NFT(item)918 }919}920921impl From<CreateReFungibleData> for CreateItemData {922 fn from(item: CreateReFungibleData) -> Self {923 CreateItemData::ReFungible(item)924 }925}926927impl From<CreateFungibleData> for CreateItemData {928 fn from(item: CreateFungibleData) -> Self {929 CreateItemData::Fungible(item)930 }931}932933/// Token's address, dictated by its collection and token IDs.934#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]935#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]936// todo possibly rename to be used generally as an address pair937pub struct TokenChild {938 /// Token id.939 pub token: TokenId,940941 /// Collection id.942 pub collection: CollectionId,943}944945/// Collection statistics.946#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]947#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]948pub struct CollectionStats {949 /// Number of created items.950 pub created: u32,951952 /// Number of burned items.953 pub destroyed: u32,954955 /// Number of current items.956 pub alive: u32,957}958959/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.960#[derive(Encode, Decode, Clone, Debug)]961#[cfg_attr(feature = "std", derive(PartialEq))]962pub struct PhantomType<T>(core::marker::PhantomData<T>);963964impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {965 type Identity = PhantomType<T>;966967 fn type_info() -> scale_info::Type {968 use scale_info::{969 Type, Path,970 build::{FieldsBuilder, UnnamedFields},971 form::MetaForm,972 type_params,973 };974 Type::builder()975 .path(Path::new("up_data_structs", "PhantomType"))976 .type_params(type_params!(T))977 .composite(978 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),979 )980 }981}982impl<T> MaxEncodedLen for PhantomType<T> {983 fn max_encoded_len() -> usize {984 0985 }986}987988/// Bounded vector of bytes.989pub type BoundedBytes<S> = BoundedVec<u8, S>;990991/// Extra properties for external collections.992pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;993994/// Property key.995pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;996997/// Property value.998pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;9991000/// Property permission.1001#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1002#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1003pub struct PropertyPermission {1004 /// Permission to change the property and property permission.1005 ///1006 /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1007 pub mutable: bool,10081009 /// Change permission for the collection administrator.1010 pub collection_admin: bool,10111012 /// Permission to change the property for the owner of the token.1013 pub token_owner: bool,1014}10151016impl PropertyPermission {1017 /// Creates mutable property permission but changes restricted for collection admin and token owner.1018 pub fn none() -> Self {1019 Self {1020 mutable: true,1021 collection_admin: false,1022 token_owner: false,1023 }1024 }1025}10261027/// Property is simpl key-value record.1028#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1029#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1030pub struct Property {1031 /// Property key.1032 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1033 pub key: PropertyKey,10341035 /// Property value.1036 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1037 pub value: PropertyValue,1038}10391040impl Into<(PropertyKey, PropertyValue)> for Property {1041 fn into(self) -> (PropertyKey, PropertyValue) {1042 (self.key, self.value)1043 }1044}10451046/// Record for proprty key permission.1047#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1048#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1049pub struct PropertyKeyPermission {1050 /// Key.1051 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1052 pub key: PropertyKey,10531054 /// Permission.1055 pub permission: PropertyPermission,1056}10571058impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1059 fn into(self) -> (PropertyKey, PropertyPermission) {1060 (self.key, self.permission)1061 }1062}10631064/// Errors for properties actions.1065#[derive(Debug)]1066pub enum PropertiesError {1067 /// The space allocated for properties has run out.1068 ///1069 /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1070 /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1071 NoSpaceForProperty,10721073 /// The property limit has been reached.1074 ///1075 /// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1076 PropertyLimitReached,10771078 /// Property key contains not allowed character.1079 InvalidCharacterInPropertyKey,10801081 /// Property key length is too long.1082 ///1083 /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1084 PropertyKeyIsTooLong,10851086 /// Property key is empty.1087 EmptyPropertyKey,1088}10891090/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1091#[derive(Debug)]1092pub enum TokenOwnerError {1093 NotFound,1094 MultipleOwners,1095}10961097/// Marker for scope of property.1098///1099/// Scoped property can't be changed by user. Used for external collections.1100#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1101pub enum PropertyScope {1102 None,1103 Rmrk,1104}11051106impl PropertyScope {1107 pub fn prefix(&self) -> &'static [u8] {1108 match self {1109 Self::None => b"",1110 Self::Rmrk => b"rmrk:",1111 }1112 }1113 /// Apply scope to property key.1114 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1115 let prefix = self.prefix();1116 if prefix == b"" {1117 return Ok(key);1118 }1119 [prefix, key.as_slice()]1120 .concat()1121 .try_into()1122 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1123 }1124}11251126/// Trait for operate with properties.1127pub trait TrySetProperty: Sized {1128 type Value;11291130 /// Try to set property with scope.1131 fn try_scoped_set(1132 &mut self,1133 scope: PropertyScope,1134 key: PropertyKey,1135 value: Self::Value,1136 ) -> Result<Option<Self::Value>, PropertiesError>;11371138 /// Try to set property with scope from iterator.1139 fn try_scoped_set_from_iter<I, KV>(1140 &mut self,1141 scope: PropertyScope,1142 iter: I,1143 ) -> Result<(), PropertiesError>1144 where1145 I: Iterator<Item = KV>,1146 KV: Into<(PropertyKey, Self::Value)>,1147 {1148 for kv in iter {1149 let (key, value) = kv.into();1150 self.try_scoped_set(scope, key, value)?;1151 }11521153 Ok(())1154 }11551156 /// Try to set property.1157 fn try_set(1158 &mut self,1159 key: PropertyKey,1160 value: Self::Value,1161 ) -> Result<Option<Self::Value>, PropertiesError> {1162 self.try_scoped_set(PropertyScope::None, key, value)1163 }11641165 /// Try to set property from iterator.1166 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1167 where1168 I: Iterator<Item = KV>,1169 KV: Into<(PropertyKey, Self::Value)>,1170 {1171 self.try_scoped_set_from_iter(PropertyScope::None, iter)1172 }1173}11741175/// Wrapped map for storing properties.1176#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1177#[derivative(Default(bound = ""))]1178pub struct PropertiesMap<Value>(1179 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1180);11811182impl<Value> PropertiesMap<Value> {1183 /// Create new property map.1184 pub fn new() -> Self {1185 Self(BoundedBTreeMap::new())1186 }11871188 /// Remove property from map.1189 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1190 Self::check_property_key(key)?;11911192 Ok(self.0.remove(key))1193 }11941195 /// Get property with appropriate key from map.1196 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1197 self.0.get(key)1198 }11991200 /// Check if map contains key.1201 pub fn contains_key(&self, key: &PropertyKey) -> bool {1202 self.0.contains_key(key)1203 }12041205 /// Check if map contains key with key validation.1206 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1207 if key.is_empty() {1208 return Err(PropertiesError::EmptyPropertyKey);1209 }12101211 for byte in key.as_slice().iter() {1212 let byte = *byte;12131214 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1215 return Err(PropertiesError::InvalidCharacterInPropertyKey);1216 }1217 }12181219 Ok(())1220 }12211222 pub fn values(&self) -> impl Iterator<Item = &Value> {1223 self.0.values()1224 }12251226 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1227 self.0.iter()1228 }1229}12301231impl<Value> IntoIterator for PropertiesMap<Value> {1232 type Item = (PropertyKey, Value);1233 type IntoIter = <1234 BoundedBTreeMap<1235 PropertyKey,1236 Value,1237 ConstU32<MAX_PROPERTIES_PER_ITEM>1238 > as IntoIterator1239 >::IntoIter;12401241 fn into_iter(self) -> Self::IntoIter {1242 self.0.into_iter()1243 }1244}12451246impl<Value> TrySetProperty for PropertiesMap<Value> {1247 type Value = Value;12481249 fn try_scoped_set(1250 &mut self,1251 scope: PropertyScope,1252 key: PropertyKey,1253 value: Self::Value,1254 ) -> Result<Option<Self::Value>, PropertiesError> {1255 Self::check_property_key(&key)?;12561257 let key = scope.apply(key)?;1258 self.01259 .try_insert(key, value)1260 .map_err(|_| PropertiesError::PropertyLimitReached)1261 }1262}12631264/// Alias for property permissions map.1265pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;12661267fn slice_size(data: &[u8]) -> u32 {1268 scoped_slice_size(PropertyScope::None, data)1269}1270fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1271 use codec::Compact;1272 let prefix = scope.prefix();1273 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321274 + data.len() as u321275 + prefix.len() as u321276}12771278/// Wrapper for properties map with consumed space control.1279#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1280pub struct Properties<const S: u32> {1281 map: PropertiesMap<PropertyValue>,1282 consumed_space: u32,1283 // May be not zero, previously served as a current S generic1284 _reserved: u32,1285}12861287impl<const S: u32> MaxEncodedLen for Properties<S> {1288 fn max_encoded_len() -> usize {1289 // len of map + len of consumed_space + len of space_limit1290 u32::max_encoded_len() * 3 + S as usize1291 }1292}12931294impl<const S: u32> Default for Properties<S> {1295 fn default() -> Self {1296 Self::new()1297 }1298}12991300impl<const S: u32> Properties<S> {1301 /// Create new properies container.1302 pub fn new() -> Self {1303 Self {1304 map: PropertiesMap::new(),1305 consumed_space: 0,1306 _reserved: 0,1307 }1308 }13091310 /// Remove propery with appropiate key.1311 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1312 let value = self.map.remove(key)?;13131314 if let Some(ref value) = value {1315 let kv_len = slice_size(key) + slice_size(value);1316 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1317 }13181319 Ok(value)1320 }13211322 /// Get property with appropriate key.1323 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1324 self.map.get(key)1325 }13261327 /// Recomputes the consumed space for the current properties state.1328 /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1329 pub fn recompute_consumed_space(&mut self) {1330 self.consumed_space = self1331 .map1332 .iter()1333 .map(|(key, value)| slice_size(key) + slice_size(value))1334 .sum();1335 }1336}13371338impl<const S: u32> IntoIterator for Properties<S> {1339 type Item = (PropertyKey, PropertyValue);1340 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;13411342 fn into_iter(self) -> Self::IntoIter {1343 self.map.into_iter()1344 }1345}13461347impl<const S: u32> TrySetProperty for Properties<S> {1348 type Value = PropertyValue;13491350 fn try_scoped_set(1351 &mut self,1352 scope: PropertyScope,1353 key: PropertyKey,1354 value: Self::Value,1355 ) -> Result<Option<Self::Value>, PropertiesError> {1356 let key_size = scoped_slice_size(scope, &key);1357 let value_size = slice_size(&value) as u32;13581359 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1360 {1361 return Err(PropertiesError::NoSpaceForProperty);1362 }13631364 let old_value = self.map.try_scoped_set(scope, key, value)?;13651366 if let Some(old_value) = old_value.as_ref() {1367 let old_value_size = slice_size(&old_value);1368 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1369 } else {1370 self.consumed_space += key_size + value_size;1371 }13721373 Ok(old_value)1374 }1375}13761377pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1378pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26};27use frame_support::storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet};2829#[cfg(feature = "serde")]30use serde::{Serialize, Deserialize};3132use sp_core::U256;33use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};34use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};35use bondrewd::Bitfields;36use frame_support::{BoundedVec, traits::ConstU32};37use derivative::Derivative;38use scale_info::TypeInfo;3940mod bondrewd_codec;41mod bounded;42pub mod budget;43pub mod mapping;44mod migration;4546/// Maximum of decimal points.47pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4849/// Maximum pieces for refungible token.50pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5253/// Maximum tokens for user.54pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {55 100_00056} else {57 1058};5960/// Maximum for collections can be created.61pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {62 100_00063} else {64 1065};6667/// Maximum for various custom data of token.68pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {69 204870} else {71 1072};7374/// Maximum admins per collection.75pub const COLLECTION_ADMINS_LIMIT: u32 = 5;7677/// Maximum tokens per collection.78pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;7980/// Maximum tokens per account.81pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {82 1_000_00083} else {84 1085};8687/// Default timeout for transfer sponsoring NFT item.88pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;89/// Default timeout for transfer sponsoring fungible item.90pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;91/// Default timeout for transfer sponsoring refungible item.92pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9394/// Default timeout for sponsored approving.95pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;9697// Schema limits98pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;99pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;100pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;101102// TODO: not used. Delete?103pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;104105/// Maximal length of a collection name.106pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;107108/// Maximal length of a collection description.109pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;110111/// Maximal length of a token prefix.112pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;113114/// Maximal length of a property key.115pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;116117/// Maximal length of a property value.118pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;119120/// A maximum number of token properties.121pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;122123/// Maximal lenght of extended property value.124pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;125126/// Maximum size for all collection properties.127pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;128129/// Maximum size of all token properties.130pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;131132/// How much items can be created per single133/// create_many call.134pub const MAX_ITEMS_PER_BATCH: u32 = 200;135136/// Used for limit bounded types of token custom data.137pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;138139/// Collection id.140#[derive(141 Encode,142 Decode,143 PartialEq,144 Eq,145 PartialOrd,146 Ord,147 Clone,148 Copy,149 Debug,150 Default,151 TypeInfo,152 MaxEncodedLen,153)]154#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]155pub struct CollectionId(pub u32);156impl EncodeLike<u32> for CollectionId {}157impl EncodeLike<CollectionId> for u32 {}158159impl From<u32> for CollectionId {160 fn from(value: u32) -> Self {161 Self(value)162 }163}164165/// Token id.166#[derive(167 Encode,168 Decode,169 PartialEq,170 Eq,171 PartialOrd,172 Ord,173 Clone,174 Copy,175 Debug,176 Default,177 TypeInfo,178 MaxEncodedLen,179)]180#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]181pub struct TokenId(pub u32);182impl EncodeLike<u32> for TokenId {}183impl EncodeLike<TokenId> for u32 {}184185impl TokenId {186 /// Try to get next token id.187 ///188 /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.189 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {190 self.0191 .checked_add(1)192 .ok_or(ArithmeticError::Overflow)193 .map(Self)194 }195}196197impl From<TokenId> for U256 {198 fn from(t: TokenId) -> Self {199 t.0.into()200 }201}202203impl TryFrom<U256> for TokenId {204 type Error = &'static str;205206 fn try_from(value: U256) -> Result<Self, Self::Error> {207 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))208 }209}210211/// Token data.212#[struct_versioning::versioned(version = 2, upper)]213#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]214#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]215pub struct TokenData<CrossAccountId> {216 /// Properties of token.217 pub properties: Vec<Property>,218219 /// Token owner.220 pub owner: Option<CrossAccountId>,221222 /// Token pieces.223 #[version(2.., upper(0))]224 pub pieces: u128,225}226227// TODO: unused type228pub struct OverflowError;229impl From<OverflowError> for &'static str {230 fn from(_: OverflowError) -> Self {231 "overflow occured"232 }233}234235/// Alias for decimal points type.236pub type DecimalPoints = u8;237238/// Collection mode.239///240/// Collection can represent various types of tokens.241/// Each collection can contain only one type of tokens at a time.242/// This type helps to understand which tokens the collection contains.243#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]244#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]245pub enum CollectionMode {246 /// Non fungible tokens.247 NFT,248 /// Fungible tokens.249 Fungible(DecimalPoints),250 /// Refungible tokens.251 ReFungible,252}253254impl CollectionMode {255 /// Get collection mod as number.256 pub fn id(&self) -> u8 {257 match self {258 CollectionMode::NFT => 1,259 CollectionMode::Fungible(_) => 2,260 CollectionMode::ReFungible => 3,261 }262 }263}264265// TODO: unused trait266pub trait SponsoringResolve<AccountId, Call> {267 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;268}269270/// Access mode for some token operations.271#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]272#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]273pub enum AccessMode {274 /// Access grant for owner and admins. Used as default.275 Normal,276 /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.277 AllowList,278}279impl Default for AccessMode {280 fn default() -> Self {281 Self::Normal282 }283}284285// TODO: remove in future.286#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]287#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]288pub enum SchemaVersion {289 ImageURL,290 Unique,291}292impl Default for SchemaVersion {293 fn default() -> Self {294 Self::ImageURL295 }296}297298// TODO: unused type299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]300#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]301pub struct Ownership<AccountId> {302 pub owner: AccountId,303 pub fraction: u128,304}305306/// The state of collection sponsorship.307#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]308#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]309pub enum SponsorshipState<AccountId> {310 /// The fees are applied to the transaction sender.311 Disabled,312 /// The sponsor is under consideration. Until the sponsor gives his consent,313 /// the fee will still be charged to sender.314 Unconfirmed(AccountId),315 /// Transactions are sponsored by specified account.316 Confirmed(AccountId),317}318319impl<AccountId> SponsorshipState<AccountId> {320 /// Get a sponsor of the collection who has confirmed his status.321 pub fn sponsor(&self) -> Option<&AccountId> {322 match self {323 Self::Confirmed(sponsor) => Some(sponsor),324 _ => None,325 }326 }327328 /// Get a sponsor of the collection who has pending or confirmed status.329 pub fn pending_sponsor(&self) -> Option<&AccountId> {330 match self {331 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),332 _ => None,333 }334 }335336 /// Whether the sponsorship is confirmed.337 pub fn confirmed(&self) -> bool {338 matches!(self, Self::Confirmed(_))339 }340}341342impl<T> Default for SponsorshipState<T> {343 fn default() -> Self {344 Self::Disabled345 }346}347348pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;349pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;350pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;351352#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]353#[bondrewd(enforce_bytes = 1)]354pub struct CollectionFlags {355 /// Tokens in foreign collections can be transferred, but not burnt356 #[bondrewd(bits = "0..1")]357 pub foreign: bool,358 /// Supports ERC721Metadata359 #[bondrewd(bits = "1..2")]360 pub erc721metadata: bool,361 /// External collections can't be managed using `unique` api362 #[bondrewd(bits = "7..8")]363 pub external: bool,364365 #[bondrewd(reserve, bits = "2..7")]366 pub reserved: u8,367}368bondrewd_codec!(CollectionFlags);369370/// Base structure for represent collection.371///372/// Used to provide basic functionality for all types of collections.373///374/// #### Note375/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).376#[struct_versioning::versioned(version = 2, upper)]377#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]378pub struct Collection<AccountId> {379 /// Collection owner account.380 pub owner: AccountId,381382 /// Collection mode.383 pub mode: CollectionMode,384385 /// Access mode.386 #[version(..2)]387 pub access: AccessMode,388389 /// Collection name.390 pub name: CollectionName,391392 /// Collection description.393 pub description: CollectionDescription,394395 /// Token prefix.396 pub token_prefix: CollectionTokenPrefix,397398 #[version(..2)]399 pub mint_mode: bool,400401 #[version(..2)]402 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,403404 #[version(..2)]405 pub schema_version: SchemaVersion,406407 /// The state of sponsorship of the collection.408 pub sponsorship: SponsorshipState<AccountId>,409410 /// Collection limits.411 pub limits: CollectionLimits,412413 /// Collection permissions.414 #[version(2.., upper(Default::default()))]415 pub permissions: CollectionPermissions,416417 #[version(2.., upper(Default::default()))]418 pub flags: CollectionFlags,419420 #[version(..2)]421 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,422423 #[version(..2)]424 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,425426 #[version(..2)]427 pub meta_update_permission: MetaUpdatePermission,428}429430#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]431#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]432pub struct RpcCollectionFlags {433 /// Is collection is foreign.434 pub foreign: bool,435 /// Collection supports ERC721Metadata.436 pub erc721metadata: bool,437}438439/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).440#[struct_versioning::versioned(version = 2, upper)]441#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]442#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]443pub struct RpcCollection<AccountId> {444 /// Collection owner account.445 pub owner: AccountId,446447 /// Collection mode.448 pub mode: CollectionMode,449450 /// Collection name.451 pub name: Vec<u16>,452453 /// Collection description.454 pub description: Vec<u16>,455456 /// Token prefix.457 pub token_prefix: Vec<u8>,458459 /// The state of sponsorship of the collection.460 pub sponsorship: SponsorshipState<AccountId>,461462 /// Collection limits.463 pub limits: CollectionLimits,464465 /// Collection permissions.466 pub permissions: CollectionPermissions,467468 /// Token property permissions.469 pub token_property_permissions: Vec<PropertyKeyPermission>,470471 /// Collection properties.472 pub properties: Vec<Property>,473474 /// Is collection read only.475 pub read_only: bool,476477 /// Extra collection flags478 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]479 pub flags: RpcCollectionFlags,480}481482impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {483 fn from(value: CollectionVersion1<AccountId>) -> Self {484 let CollectionVersion1 {485 name,486 description,487 owner,488 mode,489 access,490 token_prefix,491 mint_mode,492 sponsorship,493 limits,494 ..495 } = value;496497 RpcCollection {498 name: name.into_inner(),499 description: description.into_inner(),500 owner,501 mode,502 token_prefix: token_prefix.into_inner(),503 sponsorship,504 limits,505 permissions: CollectionPermissions {506 access: Some(access),507 mint_mode: Some(mint_mode),508 nesting: None,509 },510 token_property_permissions: Vec::default(),511 properties: Vec::default(),512 read_only: true,513514 flags: RpcCollectionFlags {515 foreign: false,516 erc721metadata: false,517 },518 }519 }520}521522/// Data used for create collection.523///524/// All fields are wrapped in [`Option`], where `None` means chain default.525#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]526#[derivative(Debug, Default(bound = ""))]527pub struct CreateCollectionData<AccountId> {528 /// Collection mode.529 #[derivative(Default(value = "CollectionMode::NFT"))]530 pub mode: CollectionMode,531532 /// Access mode.533 pub access: Option<AccessMode>,534535 /// Collection name.536 pub name: CollectionName,537538 /// Collection description.539 pub description: CollectionDescription,540541 /// Token prefix.542 pub token_prefix: CollectionTokenPrefix,543544 /// Pending collection sponsor.545 pub pending_sponsor: Option<AccountId>,546547 /// Collection limits.548 pub limits: Option<CollectionLimits>,549550 /// Collection permissions.551 pub permissions: Option<CollectionPermissions>,552553 /// Token property permissions.554 pub token_property_permissions: CollectionPropertiesPermissionsVec,555556 /// Collection properties.557 pub properties: CollectionPropertiesVec,558}559560/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].561// TODO: maybe rename to PropertiesPermissionsVec562pub type CollectionPropertiesPermissionsVec =563 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;564565/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].566pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;567568/// Limits and restrictions of a collection.569///570/// All fields are wrapped in [`Option`], where `None` means chain default.571///572/// Update with `pallet_common::Pallet::clamp_limits`.573// IMPORTANT: When adding/removing fields from this struct - don't forget to also574#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]575#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]576// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.577// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.578// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.579pub struct CollectionLimits {580 /// How many tokens can a user have on one account.581 /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].582 /// * Limit - [`MAX_TOKEN_OWNERSHIP`].583 pub account_token_ownership_limit: Option<u32>,584585 /// How many bytes of data are available for sponsorship.586 /// * Default - [`CUSTOM_DATA_LIMIT`].587 /// * Limit - [`CUSTOM_DATA_LIMIT`].588 pub sponsored_data_size: Option<u32>,589590 // FIXME should we delete this or repurpose it?591 /// Times in how many blocks we sponsor data.592 ///593 /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.594 ///595 /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).596 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].597 ///598 /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]599 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,600 /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]601602 /// How many tokens can be mined into this collection.603 ///604 /// * Default - [`COLLECTION_TOKEN_LIMIT`].605 /// * Limit - [`COLLECTION_TOKEN_LIMIT`].606 pub token_limit: Option<u32>,607608 /// Timeouts for transfer sponsoring.609 ///610 /// * Default611 /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]612 /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]613 /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]614 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].615 pub sponsor_transfer_timeout: Option<u32>,616617 /// Timeout for sponsoring an approval in passed blocks.618 ///619 /// * Default - [`SPONSOR_APPROVE_TIMEOUT`].620 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].621 pub sponsor_approve_timeout: Option<u32>,622623 /// Whether the collection owner of the collection can send tokens (which belong to other users).624 ///625 /// * Default - **false**.626 pub owner_can_transfer: Option<bool>,627628 /// Can the collection owner burn other people's tokens.629 ///630 /// * Default - **true**.631 pub owner_can_destroy: Option<bool>,632633 /// Is it possible to send tokens from this collection between users.634 ///635 /// * Default - **true**.636 pub transfers_enabled: Option<bool>,637}638639impl CollectionLimits {640 pub fn with_default_limits(collection_type: CollectionMode) -> Self {641 CollectionLimits {642 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),643 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),644 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),645 token_limit: Some(COLLECTION_TOKEN_LIMIT),646 sponsor_transfer_timeout: match collection_type {647 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),648 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),649 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),650 },651 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),652 owner_can_transfer: Some(false),653 owner_can_destroy: Some(true),654 transfers_enabled: Some(true),655 }656 }657658 /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).659 pub fn account_token_ownership_limit(&self) -> u32 {660 self.account_token_ownership_limit661 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)662 .min(MAX_TOKEN_OWNERSHIP)663 }664665 /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).666 pub fn sponsored_data_size(&self) -> u32 {667 self.sponsored_data_size668 .unwrap_or(CUSTOM_DATA_LIMIT)669 .min(CUSTOM_DATA_LIMIT)670 }671672 /// Get effective value for [`token_limit`](self.token_limit).673 pub fn token_limit(&self) -> u32 {674 self.token_limit675 .unwrap_or(COLLECTION_TOKEN_LIMIT)676 .min(COLLECTION_TOKEN_LIMIT)677 }678679 // TODO: may be replace u32 to mode?680 /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).681 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {682 self.sponsor_transfer_timeout683 .unwrap_or(default)684 .min(MAX_SPONSOR_TIMEOUT)685 }686687 /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).688 pub fn sponsor_approve_timeout(&self) -> u32 {689 self.sponsor_approve_timeout690 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)691 .min(MAX_SPONSOR_TIMEOUT)692 }693694 /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).695 pub fn owner_can_transfer(&self) -> bool {696 self.owner_can_transfer.unwrap_or(false)697 }698699 /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).700 pub fn owner_can_transfer_instaled(&self) -> bool {701 self.owner_can_transfer.is_some()702 }703704 /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).705 pub fn owner_can_destroy(&self) -> bool {706 self.owner_can_destroy.unwrap_or(true)707 }708709 /// Get effective value for [`transfers_enabled`](self.transfers_enabled).710 pub fn transfers_enabled(&self) -> bool {711 self.transfers_enabled.unwrap_or(true)712 }713714 /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).715 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {716 match self717 .sponsored_data_rate_limit718 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)719 {720 SponsoringRateLimit::SponsoringDisabled => None,721 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),722 }723 }724}725726/// Permissions on certain operations within a collection.727///728/// Some fields are wrapped in [`Option`], where `None` means chain default.729///730/// Update with `pallet_common::Pallet::clamp_permissions`.731#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]732#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]733// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.734// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.735pub struct CollectionPermissions {736 /// Access mode.737 ///738 /// * Default - [`AccessMode::Normal`].739 pub access: Option<AccessMode>,740741 /// Minting allowance.742 ///743 /// * Default - **false**.744 pub mint_mode: Option<bool>,745746 /// Permissions for nesting.747 ///748 /// * Default749 /// - `token_owner` - **false**750 /// - `collection_admin` - **false**751 /// - `restricted` - **None**752 pub nesting: Option<NestingPermissions>,753}754755impl CollectionPermissions {756 /// Get effective value for [`access`](self.access).757 pub fn access(&self) -> AccessMode {758 self.access.unwrap_or(AccessMode::Normal)759 }760761 /// Get effective value for [`mint_mode`](self.mint_mode).762 pub fn mint_mode(&self) -> bool {763 self.mint_mode.unwrap_or(false)764 }765766 /// Get effective value for [`nesting`](self.nesting).767 pub fn nesting(&self) -> &NestingPermissions {768 static DEFAULT: NestingPermissions = NestingPermissions {769 token_owner: false,770 collection_admin: false,771 restricted: None,772 #[cfg(feature = "runtime-benchmarks")]773 permissive: false,774 };775 self.nesting.as_ref().unwrap_or(&DEFAULT)776 }777}778779/// Inner set for collections allowed to nest.780type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;781782/// Wraper for collections set allowing nest.783#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]784#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]785#[derivative(Debug)]786pub struct OwnerRestrictedSet(787 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]788 #[derivative(Debug(format_with = "bounded::set_debug"))]789 pub OwnerRestrictedSetInner,790);791792impl OwnerRestrictedSet {793 /// Create new set.794 pub fn new() -> Self {795 Self(Default::default())796 }797}798impl core::ops::Deref for OwnerRestrictedSet {799 type Target = OwnerRestrictedSetInner;800 fn deref(&self) -> &Self::Target {801 &self.0802 }803}804impl core::ops::DerefMut for OwnerRestrictedSet {805 fn deref_mut(&mut self) -> &mut Self::Target {806 &mut self.0807 }808}809810/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.811#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]812#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]813#[derivative(Debug)]814pub struct NestingPermissions {815 /// Owner of token can nest tokens under it.816 pub token_owner: bool,817 /// Admin of token collection can nest tokens under token.818 pub collection_admin: bool,819 /// If set - only tokens from specified collections can be nested.820 pub restricted: Option<OwnerRestrictedSet>,821822 #[cfg(feature = "runtime-benchmarks")]823 /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.824 pub permissive: bool,825}826827/// Enum denominating how often can sponsoring occur if it is enabled.828///829/// Used for [`collection limits`](CollectionLimits).830#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]831#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]832pub enum SponsoringRateLimit {833 /// Sponsoring is disabled, and the collection sponsor will not pay for transactions834 SponsoringDisabled,835 /// Once per how many blocks can sponsorship of a transaction type occur836 Blocks(u32),837}838839/// Data used to describe an NFT at creation.840#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]841#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]842#[derivative(Debug)]843pub struct CreateNftData {844 /// Key-value pairs used to describe the token as metadata845 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]846 #[derivative(Debug(format_with = "bounded::vec_debug"))]847 /// Properties that wil be assignet to created item.848 pub properties: CollectionPropertiesVec,849}850851/// Data used to describe a Fungible token at creation.852#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]853#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]854pub struct CreateFungibleData {855 /// Number of fungible coins minted856 pub value: u128,857}858859/// Data used to describe a Refungible token at creation.860#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]861#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]862#[derivative(Debug)]863pub struct CreateReFungibleData {864 /// Number of pieces the RFT is split into865 pub pieces: u128,866867 /// Key-value pairs used to describe the token as metadata868 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]869 #[derivative(Debug(format_with = "bounded::vec_debug"))]870 pub properties: CollectionPropertiesVec,871}872873// TODO: remove this.874#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]875#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]876pub enum MetaUpdatePermission {877 ItemOwner,878 Admin,879 None,880}881882/// Enum holding data used for creation of all three item types.883/// Unified data for create item.884#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]885#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]886pub enum CreateItemData {887 /// Data for create NFT.888 NFT(CreateNftData),889 /// Data for create Fungible item.890 Fungible(CreateFungibleData),891 /// Data for create ReFungible item.892 ReFungible(CreateReFungibleData),893}894895/// Extended data for create NFT.896#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]897#[derivative(Debug)]898pub struct CreateNftExData<CrossAccountId> {899 /// Properties that wil be assignet to created item.900 #[derivative(Debug(format_with = "bounded::vec_debug"))]901 pub properties: CollectionPropertiesVec,902903 /// Owner of creating item.904 pub owner: CrossAccountId,905}906907/// Extended data for create ReFungible item.908#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]909#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]910pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {911 #[derivative(Debug(format_with = "bounded::map_debug"))]912 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,913 #[derivative(Debug(format_with = "bounded::vec_debug"))]914 pub properties: CollectionPropertiesVec,915}916917/// Extended data for create ReFungible item.918#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]919#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]920pub struct CreateRefungibleExSingleOwner<CrossAccountId> {921 pub user: CrossAccountId,922 pub pieces: u128,923 #[derivative(Debug(format_with = "bounded::vec_debug"))]924 pub properties: CollectionPropertiesVec,925}926927/// Unified extended data for creating item.928#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]929#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]930pub enum CreateItemExData<CrossAccountId> {931 /// Extended data for create NFT.932 NFT(933 #[derivative(Debug(format_with = "bounded::vec_debug"))]934 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,935 ),936937 /// Extended data for create Fungible item.938 Fungible(939 #[derivative(Debug(format_with = "bounded::map_debug"))]940 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,941 ),942943 /// Extended data for create ReFungible item in case of944 /// many tokens, each may have only one owner945 RefungibleMultipleItems(946 #[derivative(Debug(format_with = "bounded::vec_debug"))]947 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,948 ),949950 /// Extended data for create ReFungible item in case of951 /// single token, which may have many owners952 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),953}954955impl From<CreateNftData> for CreateItemData {956 fn from(item: CreateNftData) -> Self {957 CreateItemData::NFT(item)958 }959}960961impl From<CreateReFungibleData> for CreateItemData {962 fn from(item: CreateReFungibleData) -> Self {963 CreateItemData::ReFungible(item)964 }965}966967impl From<CreateFungibleData> for CreateItemData {968 fn from(item: CreateFungibleData) -> Self {969 CreateItemData::Fungible(item)970 }971}972973/// Token's address, dictated by its collection and token IDs.974#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]975#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]976// todo possibly rename to be used generally as an address pair977pub struct TokenChild {978 /// Token id.979 pub token: TokenId,980981 /// Collection id.982 pub collection: CollectionId,983}984985/// Collection statistics.986#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]987#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]988pub struct CollectionStats {989 /// Number of created items.990 pub created: u32,991992 /// Number of burned items.993 pub destroyed: u32,994995 /// Number of current items.996 pub alive: u32,997}998999/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1000#[derive(Encode, Decode, Clone, Debug)]1001#[cfg_attr(feature = "std", derive(PartialEq))]1002pub struct PhantomType<T>(core::marker::PhantomData<T>);10031004impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1005 type Identity = PhantomType<T>;10061007 fn type_info() -> scale_info::Type {1008 use scale_info::{1009 Type, Path,1010 build::{FieldsBuilder, UnnamedFields},1011 form::MetaForm,1012 type_params,1013 };1014 Type::builder()1015 .path(Path::new("up_data_structs", "PhantomType"))1016 .type_params(type_params!(T))1017 .composite(1018 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1019 )1020 }1021}1022impl<T> MaxEncodedLen for PhantomType<T> {1023 fn max_encoded_len() -> usize {1024 01025 }1026}10271028/// Bounded vector of bytes.1029pub type BoundedBytes<S> = BoundedVec<u8, S>;10301031/// Extra properties for external collections.1032pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;10331034/// Property key.1035pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;10361037/// Property value.1038pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;10391040/// Property permission.1041#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1042#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1043pub struct PropertyPermission {1044 /// Permission to change the property and property permission.1045 ///1046 /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1047 pub mutable: bool,10481049 /// Change permission for the collection administrator.1050 pub collection_admin: bool,10511052 /// Permission to change the property for the owner of the token.1053 pub token_owner: bool,1054}10551056impl PropertyPermission {1057 /// Creates mutable property permission but changes restricted for collection admin and token owner.1058 pub fn none() -> Self {1059 Self {1060 mutable: true,1061 collection_admin: false,1062 token_owner: false,1063 }1064 }1065}10661067/// Property is simpl key-value record.1068#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1069#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1070pub struct Property {1071 /// Property key.1072 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1073 pub key: PropertyKey,10741075 /// Property value.1076 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1077 pub value: PropertyValue,1078}10791080impl Into<(PropertyKey, PropertyValue)> for Property {1081 fn into(self) -> (PropertyKey, PropertyValue) {1082 (self.key, self.value)1083 }1084}10851086/// Record for proprty key permission.1087#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1088#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1089pub struct PropertyKeyPermission {1090 /// Key.1091 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1092 pub key: PropertyKey,10931094 /// Permission.1095 pub permission: PropertyPermission,1096}10971098impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1099 fn into(self) -> (PropertyKey, PropertyPermission) {1100 (self.key, self.permission)1101 }1102}11031104/// Errors for properties actions.1105#[derive(Debug)]1106pub enum PropertiesError {1107 /// The space allocated for properties has run out.1108 ///1109 /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1110 /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1111 NoSpaceForProperty,11121113 /// The property limit has been reached.1114 ///1115 /// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1116 PropertyLimitReached,11171118 /// Property key contains not allowed character.1119 InvalidCharacterInPropertyKey,11201121 /// Property key length is too long.1122 ///1123 /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1124 PropertyKeyIsTooLong,11251126 /// Property key is empty.1127 EmptyPropertyKey,1128}11291130/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1131#[derive(Debug)]1132pub enum TokenOwnerError {1133 NotFound,1134 MultipleOwners,1135}11361137/// Marker for scope of property.1138///1139/// Scoped property can't be changed by user. Used for external collections.1140#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1141pub enum PropertyScope {1142 None,1143 Rmrk,1144}11451146impl PropertyScope {1147 pub fn prefix(&self) -> &'static [u8] {1148 match self {1149 Self::None => b"",1150 Self::Rmrk => b"rmrk:",1151 }1152 }1153 /// Apply scope to property key.1154 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1155 let prefix = self.prefix();1156 if prefix == b"" {1157 return Ok(key);1158 }1159 [prefix, key.as_slice()]1160 .concat()1161 .try_into()1162 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1163 }1164}11651166/// Trait for operate with properties.1167pub trait TrySetProperty: Sized {1168 type Value;11691170 /// Try to set property with scope.1171 fn try_scoped_set(1172 &mut self,1173 scope: PropertyScope,1174 key: PropertyKey,1175 value: Self::Value,1176 ) -> Result<Option<Self::Value>, PropertiesError>;11771178 /// Try to set property with scope from iterator.1179 fn try_scoped_set_from_iter<I, KV>(1180 &mut self,1181 scope: PropertyScope,1182 iter: I,1183 ) -> Result<(), PropertiesError>1184 where1185 I: Iterator<Item = KV>,1186 KV: Into<(PropertyKey, Self::Value)>,1187 {1188 for kv in iter {1189 let (key, value) = kv.into();1190 self.try_scoped_set(scope, key, value)?;1191 }11921193 Ok(())1194 }11951196 /// Try to set property.1197 fn try_set(1198 &mut self,1199 key: PropertyKey,1200 value: Self::Value,1201 ) -> Result<Option<Self::Value>, PropertiesError> {1202 self.try_scoped_set(PropertyScope::None, key, value)1203 }12041205 /// Try to set property from iterator.1206 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1207 where1208 I: Iterator<Item = KV>,1209 KV: Into<(PropertyKey, Self::Value)>,1210 {1211 self.try_scoped_set_from_iter(PropertyScope::None, iter)1212 }1213}12141215/// Wrapped map for storing properties.1216#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1217#[derivative(Default(bound = ""))]1218pub struct PropertiesMap<Value>(1219 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1220);12211222impl<Value> PropertiesMap<Value> {1223 /// Create new property map.1224 pub fn new() -> Self {1225 Self(BoundedBTreeMap::new())1226 }12271228 /// Remove property from map.1229 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1230 Self::check_property_key(key)?;12311232 Ok(self.0.remove(key))1233 }12341235 /// Get property with appropriate key from map.1236 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1237 self.0.get(key)1238 }12391240 /// Check if map contains key.1241 pub fn contains_key(&self, key: &PropertyKey) -> bool {1242 self.0.contains_key(key)1243 }12441245 /// Check if map contains key with key validation.1246 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1247 if key.is_empty() {1248 return Err(PropertiesError::EmptyPropertyKey);1249 }12501251 for byte in key.as_slice().iter() {1252 let byte = *byte;12531254 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1255 return Err(PropertiesError::InvalidCharacterInPropertyKey);1256 }1257 }12581259 Ok(())1260 }12611262 pub fn values(&self) -> impl Iterator<Item = &Value> {1263 self.0.values()1264 }12651266 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1267 self.0.iter()1268 }1269}12701271impl<Value> IntoIterator for PropertiesMap<Value> {1272 type Item = (PropertyKey, Value);1273 type IntoIter = <1274 BoundedBTreeMap<1275 PropertyKey,1276 Value,1277 ConstU32<MAX_PROPERTIES_PER_ITEM>1278 > as IntoIterator1279 >::IntoIter;12801281 fn into_iter(self) -> Self::IntoIter {1282 self.0.into_iter()1283 }1284}12851286impl<Value> TrySetProperty for PropertiesMap<Value> {1287 type Value = Value;12881289 fn try_scoped_set(1290 &mut self,1291 scope: PropertyScope,1292 key: PropertyKey,1293 value: Self::Value,1294 ) -> Result<Option<Self::Value>, PropertiesError> {1295 Self::check_property_key(&key)?;12961297 let key = scope.apply(key)?;1298 self.01299 .try_insert(key, value)1300 .map_err(|_| PropertiesError::PropertyLimitReached)1301 }1302}13031304/// Alias for property permissions map.1305pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13061307fn slice_size(data: &[u8]) -> u32 {1308 scoped_slice_size(PropertyScope::None, data)1309}1310fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1311 use codec::Compact;1312 let prefix = scope.prefix();1313 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321314 + data.len() as u321315 + prefix.len() as u321316}13171318/// Wrapper for properties map with consumed space control.1319#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1320pub struct Properties<const S: u32> {1321 map: PropertiesMap<PropertyValue>,1322 consumed_space: u32,1323 // May be not zero, previously served as a current S generic1324 _reserved: u32,1325}13261327impl<const S: u32> MaxEncodedLen for Properties<S> {1328 fn max_encoded_len() -> usize {1329 // len of map + len of consumed_space + len of space_limit1330 u32::max_encoded_len() * 3 + S as usize1331 }1332}13331334impl<const S: u32> Default for Properties<S> {1335 fn default() -> Self {1336 Self::new()1337 }1338}13391340impl<const S: u32> Properties<S> {1341 /// Create new properies container.1342 pub fn new() -> Self {1343 Self {1344 map: PropertiesMap::new(),1345 consumed_space: 0,1346 _reserved: 0,1347 }1348 }13491350 /// Remove propery with appropiate key.1351 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1352 let value = self.map.remove(key)?;13531354 if let Some(ref value) = value {1355 let kv_len = slice_size(key) + slice_size(value);1356 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1357 }13581359 Ok(value)1360 }13611362 /// Get property with appropriate key.1363 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1364 self.map.get(key)1365 }13661367 /// Recomputes the consumed space for the current properties state.1368 /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1369 pub fn recompute_consumed_space(&mut self) {1370 self.consumed_space = self1371 .map1372 .iter()1373 .map(|(key, value)| slice_size(key) + slice_size(value))1374 .sum();1375 }1376}13771378impl<const S: u32> IntoIterator for Properties<S> {1379 type Item = (PropertyKey, PropertyValue);1380 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;13811382 fn into_iter(self) -> Self::IntoIter {1383 self.map.into_iter()1384 }1385}13861387impl<const S: u32> TrySetProperty for Properties<S> {1388 type Value = PropertyValue;13891390 fn try_scoped_set(1391 &mut self,1392 scope: PropertyScope,1393 key: PropertyKey,1394 value: Self::Value,1395 ) -> Result<Option<Self::Value>, PropertiesError> {1396 let key_size = scoped_slice_size(scope, &key);1397 let value_size = slice_size(&value) as u32;13981399 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1400 {1401 return Err(PropertiesError::NoSpaceForProperty);1402 }14031404 let old_value = self.map.try_scoped_set(scope, key, value)?;14051406 if let Some(old_value) = old_value.as_ref() {1407 let old_value_size = slice_size(&old_value);1408 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1409 } else {1410 self.consumed_space += key_size + value_size;1411 }14121413 Ok(old_value)1414 }1415}14161417pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1418pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;14191420#[cfg(test)]1421mod tests {1422 use super::*;1423 use codec::IoReader;14241425 #[test]1426 fn rpc_collection_supports_decoding_old_versions() {1427 let encoded_rpc_collection: [u8; 1013] = [1428 0, 1, 250, 241, 137, 120, 188, 29, 94, 210, 193, 237, 186, 22, 203, 241, 52, 248, 167,1429 235, 241, 211, 236, 28, 138, 156, 59, 160, 156, 105, 39, 247, 207, 101, 0, 0, 48, 65,1430 0, 73, 0, 32, 0, 67, 0, 114, 0, 101, 0, 97, 0, 116, 0, 105, 0, 111, 0, 110, 0, 115, 0,1431 252, 65, 0, 32, 0, 112, 0, 105, 0, 101, 0, 99, 0, 101, 0, 32, 0, 111, 0, 102, 0, 32, 0,1432 97, 0, 32, 0, 109, 0, 97, 0, 99, 0, 104, 0, 105, 0, 110, 0, 101, 0, 32, 0, 109, 0, 105,1433 0, 110, 0, 100, 0, 46, 0, 32, 0, 69, 0, 118, 0, 101, 0, 114, 0, 121, 0, 32, 0, 78, 0,1434 70, 0, 84, 0, 32, 0, 105, 0, 115, 0, 32, 0, 109, 0, 97, 0, 100, 0, 101, 0, 32, 0, 101,1435 0, 120, 0, 99, 0, 108, 0, 117, 0, 115, 0, 105, 0, 118, 0, 101, 0, 108, 0, 121, 0, 32,1436 0, 98, 0, 121, 0, 32, 0, 65, 0, 73, 0, 46, 0, 12, 65, 73, 67, 0, 0, 1, 0, 0, 0, 0, 0,1437 0, 0, 0, 0, 0, 17, 1, 123, 34, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 67, 111,1438 118, 101, 114, 34, 58, 34, 81, 109, 98, 52, 104, 122, 76, 101, 51, 49, 102, 98, 71, 74,1439 77, 89, 68, 82, 88, 84, 115, 107, 56, 49, 76, 103, 76, 97, 88, 76, 69, 112, 121, 97,1440 122, 102, 66, 85, 103, 111, 110, 118, 49, 118, 84, 85, 34, 125, 125, 11, 123, 34, 110,1441 101, 115, 116, 101, 100, 34, 58, 123, 34, 111, 110, 67, 104, 97, 105, 110, 77, 101,1442 116, 97, 68, 97, 116, 97, 34, 58, 123, 34, 110, 101, 115, 116, 101, 100, 34, 58, 123,1443 34, 78, 70, 84, 77, 101, 116, 97, 34, 58, 123, 34, 102, 105, 101, 108, 100, 115, 34,1444 58, 123, 34, 105, 112, 102, 115, 74, 115, 111, 110, 34, 58, 123, 34, 105, 100, 34, 58,1445 49, 44, 34, 114, 117, 108, 101, 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100, 34,1446 44, 34, 116, 121, 112, 101, 34, 58, 34, 115, 116, 114, 105, 110, 103, 34, 125, 44, 34,1447 67, 111, 108, 111, 114, 34, 58, 123, 34, 105, 100, 34, 58, 50, 44, 34, 114, 117, 108,1448 101, 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100, 34, 44, 34, 116, 121, 112,1449 101, 34, 58, 34, 67, 111, 108, 111, 114, 34, 125, 44, 34, 73, 110, 116, 101, 110, 115,1450 105, 111, 110, 115, 34, 58, 123, 34, 105, 100, 34, 58, 51, 44, 34, 114, 117, 108, 101,1451 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100, 34, 44, 34, 116, 121, 112, 101, 34,1452 58, 34, 73, 110, 116, 101, 110, 115, 105, 111, 110, 115, 34, 125, 44, 34, 77, 111, 111,1453 100, 34, 58, 123, 34, 105, 100, 34, 58, 52, 44, 34, 114, 117, 108, 101, 34, 58, 34,1454 114, 101, 113, 117, 105, 114, 101, 100, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 77,1455 111, 111, 100, 34, 125, 125, 125, 44, 34, 67, 111, 108, 111, 114, 34, 58, 123, 34, 111,1456 112, 116, 105, 111, 110, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34,1457 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 67, 111, 108, 111, 114, 101, 100, 92, 34,1458 125, 34, 44, 34, 102, 105, 101, 108, 100, 50, 34, 58, 34, 123, 92, 34, 101, 110, 92,1459 34, 58, 92, 34, 66, 108, 97, 99, 107, 38, 87, 104, 105, 116, 101, 92, 34, 125, 34, 125,1460 44, 34, 118, 97, 108, 117, 101, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34,1461 58, 48, 44, 34, 102, 105, 101, 108, 100, 50, 34, 58, 49, 125, 125, 44, 34, 73, 110,1462 116, 101, 110, 115, 105, 111, 110, 115, 34, 58, 123, 34, 111, 112, 116, 105, 111, 110,1463 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34, 123, 92, 34, 101, 110,1464 92, 34, 58, 92, 34, 101, 118, 105, 108, 92, 34, 125, 34, 44, 34, 102, 105, 101, 108,1465 100, 50, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 103, 111, 111, 100, 92,1466 34, 125, 34, 44, 34, 102, 105, 101, 108, 100, 51, 34, 58, 34, 123, 92, 34, 101, 110,1467 92, 34, 58, 92, 34, 110, 101, 117, 116, 114, 97, 108, 92, 34, 125, 34, 125, 44, 34,1468 118, 97, 108, 117, 101, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 48,1469 44, 34, 102, 105, 101, 108, 100, 50, 34, 58, 49, 44, 34, 102, 105, 101, 108, 100, 51,1470 34, 58, 50, 125, 125, 44, 34, 77, 111, 111, 100, 34, 58, 123, 34, 111, 112, 116, 105,1471 111, 110, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34, 123, 92, 34,1472 101, 110, 92, 34, 58, 92, 34, 65, 98, 115, 116, 114, 97, 99, 116, 92, 34, 125, 34, 44,1473 34, 102, 105, 101, 108, 100, 50, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34,1474 85, 110, 99, 97, 110, 110, 101, 121, 32, 118, 97, 108, 108, 101, 121, 92, 34, 125, 34,1475 44, 34, 102, 105, 101, 108, 100, 51, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92,1476 34, 83, 117, 114, 114, 101, 97, 108, 105, 115, 116, 92, 34, 125, 34, 125, 44, 34, 118,1477 97, 108, 117, 101, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 48, 44,1478 34, 102, 105, 101, 108, 100, 50, 34, 58, 49, 44, 34, 102, 105, 101, 108, 100, 51, 34,1479 58, 50, 125, 125, 125, 125, 125, 125, 0,1480 ];1481 let mut bytes = IoReader(encoded_rpc_collection.as_slice());1482 RpcCollectionVersion1::<[u8; 34]>::decode(&mut bytes).unwrap();1483 }14841485 #[test]1486 fn rpc_collection_supports_decoding_new_versions() {1487 let encoded_rpc_collection: [u8; 1576] = [1488 0, 1, 238, 236, 179, 149, 150, 47, 71, 194, 69, 174, 250, 116, 251, 148, 90, 15, 56,1489 220, 91, 79, 49, 79, 45, 197, 171, 98, 14, 171, 80, 23, 58, 92, 0, 96, 77, 0, 105, 0,1490 110, 0, 116, 0, 70, 0, 101, 0, 115, 0, 116, 0, 32, 0, 83, 0, 121, 0, 109, 0, 109, 0,1491 101, 0, 116, 0, 114, 0, 121, 0, 32, 0, 66, 0, 114, 0, 101, 0, 97, 0, 99, 0, 104, 0,1492 113, 3, 83, 0, 121, 0, 109, 0, 109, 0, 101, 0, 116, 0, 114, 0, 121, 0, 32, 0, 104, 0,1493 97, 0, 115, 0, 32, 0, 115, 0, 111, 0, 109, 0, 101, 0, 116, 0, 104, 0, 105, 0, 110, 0,1494 103, 0, 32, 0, 105, 0, 110, 0, 116, 0, 111, 0, 120, 0, 105, 0, 99, 0, 97, 0, 116, 0,1495 105, 0, 110, 0, 103, 0, 32, 0, 116, 0, 104, 0, 97, 0, 116, 0, 32, 0, 100, 0, 114, 0,1496 97, 0, 119, 0, 115, 0, 32, 0, 121, 0, 111, 0, 117, 0, 32, 0, 105, 0, 110, 0, 46, 0, 10,1497 0, 73, 0, 110, 0, 115, 0, 112, 0, 105, 0, 114, 0, 101, 0, 100, 0, 32, 0, 98, 0, 121, 0,1498 32, 0, 116, 0, 104, 0, 101, 0, 32, 0, 112, 0, 101, 0, 114, 0, 102, 0, 101, 0, 99, 0,1499 116, 0, 105, 0, 111, 0, 110, 0, 32, 0, 111, 0, 102, 0, 32, 0, 116, 0, 104, 0, 101, 0,1500 32, 0, 115, 0, 121, 0, 109, 0, 109, 0, 101, 0, 116, 0, 114, 0, 121, 0, 44, 0, 32, 0,1501 73, 0, 32, 0, 104, 0, 97, 0, 118, 0, 101, 0, 32, 0, 99, 0, 114, 0, 101, 0, 97, 0, 116,1502 0, 101, 0, 100, 0, 32, 0, 115, 0, 121, 0, 109, 0, 109, 0, 101, 0, 116, 0, 114, 0, 105,1503 0, 99, 0, 97, 0, 108, 0, 32, 0, 105, 0, 109, 0, 97, 0, 103, 0, 101, 0, 115, 0, 32, 0,1504 111, 0, 102, 0, 32, 0, 115, 0, 101, 0, 118, 0, 101, 0, 114, 0, 97, 0, 108, 0, 32, 0,1505 118, 0, 101, 0, 114, 0, 116, 0, 105, 0, 99, 0, 101, 0, 115, 0, 44, 0, 32, 0, 98, 0,1506 117, 0, 116, 0, 32, 0, 115, 0, 111, 0, 109, 0, 101, 0, 32, 0, 115, 0, 121, 0, 109, 0,1507 109, 0, 101, 0, 116, 0, 114, 0, 121, 0, 32, 0, 98, 0, 114, 0, 101, 0, 97, 0, 99, 0,1508 104, 0, 101, 0, 115, 0, 32, 0, 97, 0, 110, 0, 100, 0, 32, 0, 99, 0, 111, 0, 108, 0,1509 111, 0, 114, 0, 115, 0, 32, 0, 116, 0, 104, 0, 97, 0, 116, 0, 32, 0, 109, 0, 97, 0,1510 107, 0, 101, 0, 32, 0, 101, 0, 97, 0, 99, 0, 104, 0, 32, 0, 105, 0, 109, 0, 97, 0, 103,1511 0, 101, 0, 32, 0, 85, 0, 78, 0, 73, 0, 81, 0, 85, 0, 69, 0, 46, 0, 12, 83, 121, 66, 0,1512 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 4, 56, 95, 111, 108, 100, 95, 99,1513 111, 110, 115, 116, 68, 97, 116, 97, 0, 1, 0, 12, 92, 95, 111, 108, 100, 95, 99, 111,1514 110, 115, 116, 79, 110, 67, 104, 97, 105, 110, 83, 99, 104, 101, 109, 97, 97, 13, 123,1515 34, 110, 101, 115, 116, 101, 100, 34, 58, 123, 34, 111, 110, 67, 104, 97, 105, 110, 77,1516 101, 116, 97, 68, 97, 116, 97, 34, 58, 123, 34, 110, 101, 115, 116, 101, 100, 34, 58,1517 123, 34, 78, 70, 84, 77, 101, 116, 97, 34, 58, 123, 34, 102, 105, 101, 108, 100, 115,1518 34, 58, 123, 34, 105, 112, 102, 115, 74, 115, 111, 110, 34, 58, 123, 34, 105, 100, 34,1519 58, 49, 44, 34, 114, 117, 108, 101, 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100,1520 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 115, 116, 114, 105, 110, 103, 34, 125, 44,1521 34, 67, 111, 108, 111, 114, 34, 58, 123, 34, 105, 100, 34, 58, 50, 44, 34, 114, 117,1522 108, 101, 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100, 34, 44, 34, 116, 121,1523 112, 101, 34, 58, 34, 67, 111, 108, 111, 114, 34, 125, 44, 34, 83, 121, 109, 109, 101,1524 116, 114, 121, 32, 66, 114, 101, 97, 99, 104, 34, 58, 123, 34, 105, 100, 34, 58, 51,1525 44, 34, 114, 117, 108, 101, 34, 58, 34, 114, 101, 113, 117, 105, 114, 101, 100, 34, 44,1526 34, 116, 121, 112, 101, 34, 58, 34, 83, 121, 109, 109, 101, 116, 114, 121, 32, 66, 114,1527 101, 97, 99, 104, 34, 125, 44, 34, 86, 101, 114, 116, 105, 99, 101, 34, 58, 123, 34,1528 105, 100, 34, 58, 52, 44, 34, 114, 117, 108, 101, 34, 58, 34, 114, 101, 113, 117, 105,1529 114, 101, 100, 34, 44, 34, 116, 121, 112, 101, 34, 58, 34, 86, 101, 114, 116, 105, 99,1530 101, 34, 125, 125, 125, 44, 34, 67, 111, 108, 111, 114, 34, 58, 123, 34, 111, 112, 116,1531 105, 111, 110, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34, 123, 92,1532 34, 101, 110, 92, 34, 58, 92, 34, 49, 32, 32, 32, 92, 34, 125, 34, 44, 34, 102, 105,1533 101, 108, 100, 50, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 50, 32, 92,1534 34, 125, 34, 44, 34, 102, 105, 101, 108, 100, 51, 34, 58, 34, 123, 92, 34, 101, 110,1535 92, 34, 58, 92, 34, 51, 92, 34, 125, 34, 125, 44, 34, 118, 97, 108, 117, 101, 115, 34,1536 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 48, 44, 34, 102, 105, 101, 108, 100,1537 50, 34, 58, 49, 44, 34, 102, 105, 101, 108, 100, 51, 34, 58, 50, 125, 125, 44, 34, 83,1538 121, 109, 109, 101, 116, 114, 121, 32, 66, 114, 101, 97, 99, 104, 34, 58, 123, 34, 111,1539 112, 116, 105, 111, 110, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34,1540 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 49, 92, 34, 125, 34, 44, 34, 102, 105, 101,1541 108, 100, 50, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 50, 92, 34, 125,1542 34, 125, 44, 34, 118, 97, 108, 117, 101, 115, 34, 58, 123, 34, 102, 105, 101, 108, 100,1543 49, 34, 58, 48, 44, 34, 102, 105, 101, 108, 100, 50, 34, 58, 49, 125, 125, 44, 34, 86,1544 101, 114, 116, 105, 99, 101, 34, 58, 123, 34, 111, 112, 116, 105, 111, 110, 115, 34,1545 58, 123, 34, 102, 105, 101, 108, 100, 49, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34,1546 58, 92, 34, 54, 92, 34, 125, 34, 44, 34, 102, 105, 101, 108, 100, 50, 34, 58, 34, 123,1547 92, 34, 101, 110, 92, 34, 58, 92, 34, 55, 92, 34, 125, 34, 44, 34, 102, 105, 101, 108,1548 100, 51, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 56, 92, 34, 125, 34,1549 44, 34, 102, 105, 101, 108, 100, 52, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92,1550 34, 57, 92, 34, 125, 34, 44, 34, 102, 105, 101, 108, 100, 53, 34, 58, 34, 123, 92, 34,1551 101, 110, 92, 34, 58, 92, 34, 49, 48, 92, 34, 125, 34, 44, 34, 102, 105, 101, 108, 100,1552 54, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34, 49, 49, 92, 34, 125, 34, 44,1553 34, 102, 105, 101, 108, 100, 55, 34, 58, 34, 123, 92, 34, 101, 110, 92, 34, 58, 92, 34,1554 49, 50, 92, 34, 125, 34, 125, 44, 34, 118, 97, 108, 117, 101, 115, 34, 58, 123, 34,1555 102, 105, 101, 108, 100, 49, 34, 58, 48, 44, 34, 102, 105, 101, 108, 100, 50, 34, 58,1556 49, 44, 34, 102, 105, 101, 108, 100, 51, 34, 58, 50, 44, 34, 102, 105, 101, 108, 100,1557 52, 34, 58, 51, 44, 34, 102, 105, 101, 108, 100, 53, 34, 58, 52, 44, 34, 102, 105, 101,1558 108, 100, 54, 34, 58, 53, 44, 34, 102, 105, 101, 108, 100, 55, 34, 58, 54, 125, 125,1559 125, 125, 125, 125, 72, 95, 111, 108, 100, 95, 115, 99, 104, 101, 109, 97, 86, 101,1560 114, 115, 105, 111, 110, 24, 85, 110, 105, 113, 117, 101, 104, 95, 111, 108, 100, 95,1561 118, 97, 114, 105, 97, 98, 108, 101, 79, 110, 67, 104, 97, 105, 110, 83, 99, 104, 101,1562 109, 97, 17, 1, 123, 34, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 67, 111, 118,1563 101, 114, 34, 58, 34, 81, 109, 82, 67, 77, 84, 109, 57, 118, 81, 107, 76, 89, 86, 65,1564 54, 54, 87, 80, 49, 75, 72, 57, 55, 106, 84, 76, 76, 115, 56, 74, 78, 86, 65, 114, 80,1565 66, 52, 56, 98, 106, 87, 84, 75, 74, 110, 34, 125, 0, 0, 0,1566 ];1567 let mut bytes = IoReader(encoded_rpc_collection.as_slice());1568 RpcCollection::<[u8; 34]>::decode(&mut bytes).unwrap();1569 }1570}primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -20,7 +20,7 @@
use up_data_structs::{
CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits, Property,
- PropertyKeyPermission, TokenData, TokenChild, RpcCollectionVersion1, TokenDataVersion1,
+ PropertyKeyPermission, TokenData, TokenChild, TokenDataVersion1,
};
use sp_std::vec::Vec;
@@ -117,7 +117,7 @@
fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollection<AccountId>>>;
#[changed_in(3)]
- fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollectionVersion1<AccountId>>>;
+ fn collection_by_id(collection: CollectionId) -> Result<Option<Vec<u8>>>;
/// Get collection stats.
fn collection_stats() -> Result<CollectionStats>;