difftreelog
fix max token ownership = 100_000_000
in: master
3 files changed
js-packages/tests/apiConsts.test.tsdiffbeforeafterboth--- a/js-packages/tests/apiConsts.test.ts
+++ b/js-packages/tests/apiConsts.test.ts
@@ -30,7 +30,7 @@
const NESTING_BUDGET = 5n;
const DEFAULT_COLLETCTION_LIMIT = {
- accountTokenOwnershipLimit: '1,000,000',
+ accountTokenOwnershipLimit: '100,000,000',
sponsoredDataSize: '2,048',
sponsoredDataRateLimit: 'SponsoringDisabled',
tokenLimit: '4,294,967,295',
@@ -113,4 +113,4 @@
function checkConst<T>(constValue: any, expectedValue: T) {
expect(constValue.toBigInt()).equal(expectedValue);
-}
\ No newline at end of file
+}
js-packages/tests/limits.test.tsdiffbeforeafterboth--- a/js-packages/tests/limits.test.ts
+++ b/js-packages/tests/limits.test.ts
@@ -438,7 +438,7 @@
{ // Check that default values defined for collection limits
const limits = await collection.getEffectiveLimits();
- expect(limits.accountTokenOwnershipLimit).to.be.eq(100000);
+ expect(limits.accountTokenOwnershipLimit).to.be.eq(100000000);
expect(limits.sponsoredDataSize).to.be.eq(2048);
expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});
expect(limits.tokenLimit).to.be.eq(4294967295);
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 ops::Deref,27};2829use bondrewd::Bitfields;30use derivative::Derivative;31use evm_coder::AbiCoderFlags;32use frame_support::{33 storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},34 traits::ConstU32,35 BoundedVec,36};37use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use scale_info::TypeInfo;39use serde::{Deserialize, Serialize};40use sp_core::U256;41use sp_runtime::{sp_std::prelude::Vec, ArithmeticError};42use sp_std::collections::btree_set::BTreeSet;4344mod bondrewd_codec;45mod bounded;46pub mod budget;47pub mod mapping;48mod migration;4950/// Maximum of decimal points.51pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5253/// Maximum pieces for refungible token.54pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;55pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5657/// Maximum tokens for user.58pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {59 100_000_00060} else {61 1062};6364/// Maximum for collections can be created.65pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {66 100_00067} else {68 1069};7071/// Maximum for various custom data of token.72pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73 204874} else {75 1076};7778/// Maximum admins per collection.79pub const COLLECTION_ADMINS_LIMIT: u32 = 5;8081/// Maximum tokens per collection.82pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8384/// Maximum tokens per account.85pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {86 100_000_00087} else {88 1089};9091/// Default timeout for transfer sponsoring NFT item.92pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;93/// Default timeout for transfer sponsoring fungible item.94pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;95/// Default timeout for transfer sponsoring refungible item.96pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9798/// Default timeout for sponsored approving.99pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;100101// Schema limits102pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;104pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;105106// TODO: not used. Delete?107pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;108109/// Maximal length of a collection name.110pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;111112/// Maximal length of a collection description.113pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;114115/// Maximal length of a token prefix.116pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;117118/// Maximal length of a property key.119pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;120121/// Maximal length of a property value.122pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;123124/// A maximum number of token properties.125pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;126127/// Maximal lenght of extended property value.128pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;129130/// Maximum size for all collection properties.131pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;132133/// Maximum size of all token properties.134pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;135136/// How much items can be created per single137/// create_many call.138pub const MAX_ITEMS_PER_BATCH: u32 = 120;139140/// Used for limit bounded types of token custom data.141pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;142143/// Collection id.144#[derive(145 Encode,146 Decode,147 PartialEq,148 Eq,149 PartialOrd,150 Ord,151 Clone,152 Copy,153 Debug,154 Default,155 TypeInfo,156 MaxEncodedLen,157 Serialize,158 Deserialize,159)]160pub struct CollectionId(pub u32);161impl EncodeLike<u32> for CollectionId {}162impl EncodeLike<CollectionId> for u32 {}163164impl From<u32> for CollectionId {165 fn from(value: u32) -> Self {166 Self(value)167 }168}169170impl Deref for CollectionId {171 type Target = u32;172173 fn deref(&self) -> &Self::Target {174 &self.0175 }176}177178/// Token id.179#[derive(180 Encode,181 Decode,182 PartialEq,183 Eq,184 PartialOrd,185 Ord,186 Clone,187 Copy,188 Debug,189 Default,190 TypeInfo,191 MaxEncodedLen,192 Serialize,193 Deserialize,194)]195pub struct TokenId(pub u32);196impl EncodeLike<u32> for TokenId {}197impl EncodeLike<TokenId> for u32 {}198199impl TokenId {200 /// Try to get next token id.201 ///202 /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.203 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {204 self.0205 .checked_add(1)206 .ok_or(ArithmeticError::Overflow)207 .map(Self)208 }209}210211impl From<TokenId> for U256 {212 fn from(t: TokenId) -> Self {213 t.0.into()214 }215}216217impl TryFrom<U256> for TokenId {218 type Error = &'static str;219220 fn try_from(value: U256) -> Result<Self, Self::Error> {221 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))222 }223}224225/// Token data.226#[struct_versioning::versioned(version = 2, upper)]227#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]228pub struct TokenData<CrossAccountId> {229 /// Properties of token.230 pub properties: Vec<Property>,231232 /// Token owner.233 pub owner: Option<CrossAccountId>,234235 /// Token pieces.236 #[version(2.., upper(0))]237 pub pieces: u128,238}239240// TODO: unused type241pub struct OverflowError;242impl From<OverflowError> for &'static str {243 fn from(_: OverflowError) -> Self {244 "overflow occured"245 }246}247248/// Alias for decimal points type.249pub type DecimalPoints = u8;250251/// Collection mode.252///253/// Collection can represent various types of tokens.254/// Each collection can contain only one type of tokens at a time.255/// This type helps to understand which tokens the collection contains.256#[derive(257 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,258)]259pub enum CollectionMode {260 /// Non fungible tokens.261 NFT,262 /// Fungible tokens.263 Fungible(DecimalPoints),264 /// Refungible tokens.265 ReFungible,266}267268impl CollectionMode {269 /// Get collection mod as number.270 pub fn id(&self) -> u8 {271 match self {272 CollectionMode::NFT => 1,273 CollectionMode::Fungible(_) => 2,274 CollectionMode::ReFungible => 3,275 }276 }277}278279// TODO: unused trait280pub trait SponsoringResolve<AccountId, Call> {281 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;282}283284/// Access mode for some token operations.285#[derive(286 Encode,287 Decode,288 Eq,289 Debug,290 Clone,291 Copy,292 PartialEq,293 TypeInfo,294 MaxEncodedLen,295 Serialize,296 Deserialize,297)]298pub enum AccessMode {299 /// Access grant for owner and admins. Used as default.300 Normal,301 /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.302 AllowList,303}304impl Default for AccessMode {305 fn default() -> Self {306 Self::Normal307 }308}309310// TODO: remove in future.311#[derive(312 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,313)]314pub enum SchemaVersion {315 ImageURL,316 Unique,317}318impl Default for SchemaVersion {319 fn default() -> Self {320 Self::ImageURL321 }322}323324// TODO: unused type325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]326pub struct Ownership<AccountId> {327 pub owner: AccountId,328 pub fraction: u128,329}330331/// The state of collection sponsorship.332#[derive(333 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,334)]335pub enum SponsorshipState<AccountId> {336 /// The fees are applied to the transaction sender.337 Disabled,338 /// The sponsor is under consideration. Until the sponsor gives his consent,339 /// the fee will still be charged to sender.340 Unconfirmed(AccountId),341 /// Transactions are sponsored by specified account.342 Confirmed(AccountId),343}344345impl<AccountId> SponsorshipState<AccountId> {346 /// Get a sponsor of the collection who has confirmed his status.347 pub fn sponsor(&self) -> Option<&AccountId> {348 match self {349 Self::Confirmed(sponsor) => Some(sponsor),350 _ => None,351 }352 }353354 /// Get a sponsor of the collection who has pending or confirmed status.355 pub fn pending_sponsor(&self) -> Option<&AccountId> {356 match self {357 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),358 _ => None,359 }360 }361362 /// Whether the sponsorship is confirmed.363 pub fn confirmed(&self) -> bool {364 matches!(self, Self::Confirmed(_))365 }366}367368impl<T> Default for SponsorshipState<T> {369 fn default() -> Self {370 Self::Disabled371 }372}373374pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;375pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;376pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;377378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]379#[bondrewd(enforce_bytes = 1)]380pub struct CollectionFlags {381 /// Tokens in foreign collections can be transferred, but not burnt382 #[bondrewd(bits = "0..1")]383 pub foreign: bool,384 /// Supports ERC721Metadata385 #[bondrewd(bits = "1..2")]386 pub erc721metadata: bool,387 /// External collections can't be managed using `unique` api388 #[bondrewd(bits = "7..8")]389 pub external: bool,390 /// Reserved flags391 #[bondrewd(bits = "2..7")]392 pub reserved: u8,393}394bondrewd_codec!(CollectionFlags);395396impl CollectionFlags {397 pub fn is_allowed_for_user(self) -> bool {398 !self.foreign && !self.external && self.reserved == 0399 }400}401402/// Base structure for represent collection.403///404/// Used to provide basic functionality for all types of collections.405///406/// #### Note407/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).408#[struct_versioning::versioned(version = 2, upper)]409#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]410pub struct Collection<AccountId> {411 /// Collection owner account.412 pub owner: AccountId,413414 /// Collection mode.415 pub mode: CollectionMode,416417 /// Access mode.418 #[version(..2)]419 pub access: AccessMode,420421 /// Collection name.422 pub name: CollectionName,423424 /// Collection description.425 pub description: CollectionDescription,426427 /// Token prefix.428 pub token_prefix: CollectionTokenPrefix,429430 #[version(..2)]431 pub mint_mode: bool,432433 #[version(..2)]434 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,435436 #[version(..2)]437 pub schema_version: SchemaVersion,438439 /// The state of sponsorship of the collection.440 pub sponsorship: SponsorshipState<AccountId>,441442 /// Collection limits.443 pub limits: CollectionLimits,444445 /// Collection permissions.446 #[version(2.., upper(Default::default()))]447 pub permissions: CollectionPermissions,448449 #[version(2.., upper(Default::default()))]450 pub flags: CollectionFlags,451452 #[version(..2)]453 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,454455 #[version(..2)]456 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,457458 #[version(..2)]459 pub meta_update_permission: MetaUpdatePermission,460}461462#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]463pub struct RpcCollectionFlags {464 /// Is collection is foreign.465 pub foreign: bool,466 /// Collection supports ERC721Metadata.467 pub erc721metadata: bool,468}469470/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).471#[struct_versioning::versioned(version = 2, upper)]472#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]473pub struct RpcCollection<AccountId> {474 /// Collection owner account.475 pub owner: AccountId,476477 /// Collection mode.478 pub mode: CollectionMode,479480 /// Collection name.481 pub name: Vec<u16>,482483 /// Collection description.484 pub description: Vec<u16>,485486 /// Token prefix.487 pub token_prefix: Vec<u8>,488489 /// The state of sponsorship of the collection.490 pub sponsorship: SponsorshipState<AccountId>,491492 /// Collection limits.493 pub limits: CollectionLimits,494495 /// Collection permissions.496 pub permissions: CollectionPermissions,497498 /// Token property permissions.499 pub token_property_permissions: Vec<PropertyKeyPermission>,500501 /// Collection properties.502 pub properties: Vec<Property>,503504 /// Is collection read only.505 pub read_only: bool,506507 /// Extra collection flags508 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]509 pub flags: RpcCollectionFlags,510}511512impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {513 fn from(value: CollectionVersion1<AccountId>) -> Self {514 let CollectionVersion1 {515 name,516 description,517 owner,518 mode,519 access,520 token_prefix,521 mint_mode,522 sponsorship,523 limits,524 ..525 } = value;526527 RpcCollection {528 name: name.into_inner(),529 description: description.into_inner(),530 owner,531 mode,532 token_prefix: token_prefix.into_inner(),533 sponsorship,534 limits,535 permissions: CollectionPermissions {536 access: Some(access),537 mint_mode: Some(mint_mode),538 nesting: None,539 },540 token_property_permissions: Vec::default(),541 properties: Vec::default(),542 read_only: true,543544 flags: RpcCollectionFlags {545 foreign: false,546 erc721metadata: false,547 },548 }549 }550}551552pub struct RawEncoded(Vec<u8>);553554impl parity_scale_codec::Decode for RawEncoded {555 fn decode<I: parity_scale_codec::Input>(556 input: &mut I,557 ) -> Result<Self, parity_scale_codec::Error> {558 let mut out = Vec::new();559 while let Ok(v) = input.read_byte() {560 out.push(v);561 }562 Ok(Self(out))563 }564}565566impl Deref for RawEncoded {567 type Target = Vec<u8>;568569 fn deref(&self) -> &Self::Target {570 &self.0571 }572}573574/// Data used for create collection.575///576/// All fields are wrapped in [`Option`], where `None` means chain default.577#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]578#[derivative(Debug, Default(bound = ""))]579pub struct CreateCollectionData<CrossAccountId> {580 /// Collection mode.581 #[derivative(Default(value = "CollectionMode::NFT"))]582 pub mode: CollectionMode,583584 /// Access mode.585 pub access: Option<AccessMode>,586587 /// Collection name.588 pub name: CollectionName,589590 /// Collection description.591 pub description: CollectionDescription,592593 /// Token prefix.594 pub token_prefix: CollectionTokenPrefix,595596 /// Collection limits.597 pub limits: Option<CollectionLimits>,598599 /// Collection permissions.600 pub permissions: Option<CollectionPermissions>,601602 /// Token property permissions.603 pub token_property_permissions: CollectionPropertiesPermissionsVec,604605 /// Collection properties.606 pub properties: CollectionPropertiesVec,607608 pub admin_list: Vec<CrossAccountId>,609610 /// Pending collection sponsor.611 pub pending_sponsor: Option<CrossAccountId>,612613 pub flags: CollectionFlags,614}615616/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].617// TODO: maybe rename to PropertiesPermissionsVec618pub type CollectionPropertiesPermissionsVec =619 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;620621/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].622pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;623624/// Limits and restrictions of a collection.625///626/// All fields are wrapped in [`Option`], where `None` means chain default.627///628/// Update with `pallet_common::Pallet::clamp_limits`.629// IMPORTANT: When adding/removing fields from this struct - don't forget to also630#[derive(631 Encode,632 Decode,633 Debug,634 Default,635 Clone,636 PartialEq,637 TypeInfo,638 MaxEncodedLen,639 Serialize,640 Deserialize,641)]642// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.643// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.644// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.645pub struct CollectionLimits {646 /// How many tokens can a user have on one account.647 /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].648 /// * Limit - [`MAX_TOKEN_OWNERSHIP`].649 pub account_token_ownership_limit: Option<u32>,650651 /// How many bytes of data are available for sponsorship.652 /// * Default - [`CUSTOM_DATA_LIMIT`].653 /// * Limit - [`CUSTOM_DATA_LIMIT`].654 pub sponsored_data_size: Option<u32>,655656 // FIXME should we delete this or repurpose it?657 /// Times in how many blocks we sponsor data.658 ///659 /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.660 ///661 /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).662 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].663 ///664 /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]665 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,666 /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]667668 /// How many tokens can be mined into this collection.669 ///670 /// * Default - [`COLLECTION_TOKEN_LIMIT`].671 /// * Limit - [`COLLECTION_TOKEN_LIMIT`].672 pub token_limit: Option<u32>,673674 /// Timeouts for transfer sponsoring.675 ///676 /// * Default677 /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]678 /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]679 /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]680 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].681 pub sponsor_transfer_timeout: Option<u32>,682683 /// Timeout for sponsoring an approval in passed blocks.684 ///685 /// * Default - [`SPONSOR_APPROVE_TIMEOUT`].686 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].687 pub sponsor_approve_timeout: Option<u32>,688689 /// Whether the collection owner of the collection can send tokens (which belong to other users).690 ///691 /// * Default - **false**.692 pub owner_can_transfer: Option<bool>,693694 /// Can the collection owner burn other people's tokens.695 ///696 /// * Default - **true**.697 pub owner_can_destroy: Option<bool>,698699 /// Is it possible to send tokens from this collection between users.700 ///701 /// * Default - **true**.702 pub transfers_enabled: Option<bool>,703}704705impl CollectionLimits {706 pub fn with_default_limits(collection_type: CollectionMode) -> Self {707 CollectionLimits {708 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),709 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),710 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),711 token_limit: Some(COLLECTION_TOKEN_LIMIT),712 sponsor_transfer_timeout: match collection_type {713 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),714 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),715 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),716 },717 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),718 owner_can_transfer: Some(false),719 owner_can_destroy: Some(true),720 transfers_enabled: Some(true),721 }722 }723724 /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).725 pub fn account_token_ownership_limit(&self) -> u32 {726 self.account_token_ownership_limit727 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)728 .min(MAX_TOKEN_OWNERSHIP)729 }730731 /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).732 pub fn sponsored_data_size(&self) -> u32 {733 self.sponsored_data_size734 .unwrap_or(CUSTOM_DATA_LIMIT)735 .min(CUSTOM_DATA_LIMIT)736 }737738 /// Get effective value for [`token_limit`](self.token_limit).739 pub fn token_limit(&self) -> u32 {740 self.token_limit741 .unwrap_or(COLLECTION_TOKEN_LIMIT)742 .min(COLLECTION_TOKEN_LIMIT)743 }744745 // TODO: may be replace u32 to mode?746 /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).747 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {748 self.sponsor_transfer_timeout749 .unwrap_or(default)750 .min(MAX_SPONSOR_TIMEOUT)751 }752753 /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).754 pub fn sponsor_approve_timeout(&self) -> u32 {755 self.sponsor_approve_timeout756 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)757 .min(MAX_SPONSOR_TIMEOUT)758 }759760 /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).761 pub fn owner_can_transfer(&self) -> bool {762 self.owner_can_transfer.unwrap_or(false)763 }764765 /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).766 pub fn owner_can_transfer_instaled(&self) -> bool {767 self.owner_can_transfer.is_some()768 }769770 /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).771 pub fn owner_can_destroy(&self) -> bool {772 self.owner_can_destroy.unwrap_or(true)773 }774775 /// Get effective value for [`transfers_enabled`](self.transfers_enabled).776 pub fn transfers_enabled(&self) -> bool {777 self.transfers_enabled.unwrap_or(true)778 }779780 /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).781 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {782 match self783 .sponsored_data_rate_limit784 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)785 {786 SponsoringRateLimit::SponsoringDisabled => None,787 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),788 }789 }790}791792/// Permissions on certain operations within a collection.793///794/// Some fields are wrapped in [`Option`], where `None` means chain default.795///796/// Update with `pallet_common::Pallet::clamp_permissions`.797#[derive(798 Encode,799 Decode,800 Debug,801 Default,802 Clone,803 PartialEq,804 TypeInfo,805 MaxEncodedLen,806 Serialize,807 Deserialize,808)]809// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.810// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.811pub struct CollectionPermissions {812 /// Access mode.813 ///814 /// * Default - [`AccessMode::Normal`].815 pub access: Option<AccessMode>,816817 /// Minting allowance.818 ///819 /// * Default - **false**.820 pub mint_mode: Option<bool>,821822 /// Permissions for nesting.823 ///824 /// * Default825 /// - `token_owner` - **false**826 /// - `collection_admin` - **false**827 /// - `restricted` - **None**828 pub nesting: Option<NestingPermissions>,829}830831impl CollectionPermissions {832 /// Get effective value for [`access`](self.access).833 pub fn access(&self) -> AccessMode {834 self.access.unwrap_or(AccessMode::Normal)835 }836837 /// Get effective value for [`mint_mode`](self.mint_mode).838 pub fn mint_mode(&self) -> bool {839 self.mint_mode.unwrap_or(false)840 }841842 /// Get effective value for [`nesting`](self.nesting).843 pub fn nesting(&self) -> &NestingPermissions {844 static DEFAULT: NestingPermissions = NestingPermissions {845 token_owner: false,846 collection_admin: false,847 restricted: None,848 #[cfg(feature = "runtime-benchmarks")]849 permissive: false,850 };851 self.nesting.as_ref().unwrap_or(&DEFAULT)852 }853}854855/// Inner set for collections allowed to nest.856type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;857858/// Wraper for collections set allowing nest.859#[derive(860 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,861)]862#[derivative(Debug)]863pub struct OwnerRestrictedSet(864 #[serde(with = "bounded::set_serde")]865 #[derivative(Debug(format_with = "bounded::set_debug"))]866 pub OwnerRestrictedSetInner,867);868869impl OwnerRestrictedSet {870 /// Create new set.871 pub fn new() -> Self {872 Self(Default::default())873 }874}875impl Default for OwnerRestrictedSet {876 fn default() -> Self {877 Self::new()878 }879}880impl core::ops::Deref for OwnerRestrictedSet {881 type Target = OwnerRestrictedSetInner;882 fn deref(&self) -> &Self::Target {883 &self.0884 }885}886impl core::ops::DerefMut for OwnerRestrictedSet {887 fn deref_mut(&mut self) -> &mut Self::Target {888 &mut self.0889 }890}891892impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {893 type Error = ();894895 fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {896 Ok(Self(value.try_into()?))897 }898}899900/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.901#[derive(902 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,903)]904#[derivative(Debug)]905pub struct NestingPermissions {906 /// Owner of token can nest tokens under it.907 pub token_owner: bool,908 /// Admin of token collection can nest tokens under token.909 pub collection_admin: bool,910 /// If set - only tokens from specified collections can be nested.911 pub restricted: Option<OwnerRestrictedSet>,912913 #[cfg(feature = "runtime-benchmarks")]914 /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.915 pub permissive: bool,916}917918/// Enum denominating how often can sponsoring occur if it is enabled.919///920/// Used for [`collection limits`](CollectionLimits).921#[derive(922 Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,923)]924pub enum SponsoringRateLimit {925 /// Sponsoring is disabled, and the collection sponsor will not pay for transactions926 SponsoringDisabled,927 /// Once per how many blocks can sponsorship of a transaction type occur928 Blocks(u32),929}930931/// Data used to describe an NFT at creation.932#[derive(933 Encode,934 Decode,935 MaxEncodedLen,936 Default,937 PartialEq,938 Clone,939 Derivative,940 TypeInfo,941 Serialize,942 Deserialize,943)]944#[derivative(Debug)]945pub struct CreateNftData {946 /// Key-value pairs used to describe the token as metadata947 #[serde(with = "bounded::vec_serde")]948 #[derivative(Debug(format_with = "bounded::vec_debug"))]949 /// Properties that wil be assignet to created item.950 pub properties: CollectionPropertiesVec,951}952953/// Data used to describe a Fungible token at creation.954#[derive(955 Encode,956 Decode,957 MaxEncodedLen,958 Default,959 Debug,960 Clone,961 PartialEq,962 TypeInfo,963 Serialize,964 Deserialize,965)]966pub struct CreateFungibleData {967 /// Number of fungible coins minted968 pub value: u128,969}970971/// Data used to describe a Refungible token at creation.972#[derive(973 Encode,974 Decode,975 MaxEncodedLen,976 Default,977 PartialEq,978 Clone,979 Derivative,980 TypeInfo,981 Serialize,982 Deserialize,983)]984#[derivative(Debug)]985pub struct CreateReFungibleData {986 /// Number of pieces the RFT is split into987 pub pieces: u128,988989 /// Key-value pairs used to describe the token as metadata990 #[serde(with = "bounded::vec_serde")]991 #[derivative(Debug(format_with = "bounded::vec_debug"))]992 pub properties: CollectionPropertiesVec,993}994995// TODO: remove this.996#[derive(997 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,998)]999pub enum MetaUpdatePermission {1000 ItemOwner,1001 Admin,1002 None,1003}10041005/// Enum holding data used for creation of all three item types.1006/// Unified data for create item.1007#[derive(1008 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1009)]1010pub enum CreateItemData {1011 /// Data for create NFT.1012 NFT(CreateNftData),1013 /// Data for create Fungible item.1014 Fungible(CreateFungibleData),1015 /// Data for create ReFungible item.1016 ReFungible(CreateReFungibleData),1017}10181019/// Extended data for create NFT.1020#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1021#[derivative(Debug)]1022pub struct CreateNftExData<CrossAccountId> {1023 /// Properties that wil be assignet to created item.1024 #[derivative(Debug(format_with = "bounded::vec_debug"))]1025 pub properties: CollectionPropertiesVec,10261027 /// Owner of creating item.1028 pub owner: CrossAccountId,1029}10301031/// Extended data for create ReFungible item.1032#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1033#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1034pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1035 #[derivative(Debug(format_with = "bounded::map_debug"))]1036 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1037 #[derivative(Debug(format_with = "bounded::vec_debug"))]1038 pub properties: CollectionPropertiesVec,1039}10401041/// Extended data for create ReFungible item.1042#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1043#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1044pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1045 pub user: CrossAccountId,1046 pub pieces: u128,1047 #[derivative(Debug(format_with = "bounded::vec_debug"))]1048 pub properties: CollectionPropertiesVec,1049}10501051/// Unified extended data for creating item.1052#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1053#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1054pub enum CreateItemExData<CrossAccountId> {1055 /// Extended data for create NFT.1056 NFT(1057 #[derivative(Debug(format_with = "bounded::vec_debug"))]1058 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1059 ),10601061 /// Extended data for create Fungible item.1062 Fungible(1063 #[derivative(Debug(format_with = "bounded::map_debug"))]1064 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1065 ),10661067 /// Extended data for create ReFungible item in case of1068 /// many tokens, each may have only one owner1069 RefungibleMultipleItems(1070 #[derivative(Debug(format_with = "bounded::vec_debug"))]1071 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1072 ),10731074 /// Extended data for create ReFungible item in case of1075 /// single token, which may have many owners1076 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1077}10781079impl From<CreateNftData> for CreateItemData {1080 fn from(item: CreateNftData) -> Self {1081 CreateItemData::NFT(item)1082 }1083}10841085impl From<CreateReFungibleData> for CreateItemData {1086 fn from(item: CreateReFungibleData) -> Self {1087 CreateItemData::ReFungible(item)1088 }1089}10901091impl From<CreateFungibleData> for CreateItemData {1092 fn from(item: CreateFungibleData) -> Self {1093 CreateItemData::Fungible(item)1094 }1095}10961097/// Token's address, dictated by its collection and token IDs.1098#[derive(1099 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1100)]1101// todo possibly rename to be used generally as an address pair1102pub struct TokenChild {1103 /// Token id.1104 pub token: TokenId,11051106 /// Collection id.1107 pub collection: CollectionId,1108}11091110/// Collection statistics.1111#[derive(1112 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1113)]1114pub struct CollectionStats {1115 /// Number of created items.1116 pub created: u32,11171118 /// Number of burned items.1119 pub destroyed: u32,11201121 /// Number of current items.1122 pub alive: u32,1123}11241125/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1126#[derive(Encode, Decode, Clone, Debug)]1127#[cfg_attr(feature = "std", derive(PartialEq))]1128pub struct PhantomType<T>(core::marker::PhantomData<T>);11291130impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1131 type Identity = PhantomType<T>;11321133 fn type_info() -> scale_info::Type {1134 use scale_info::{1135 build::{FieldsBuilder, UnnamedFields},1136 form::MetaForm,1137 type_params, Path, Type,1138 };1139 Type::builder()1140 .path(Path::new("up_data_structs", "PhantomType"))1141 .type_params(type_params!(T))1142 .composite(1143 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1144 )1145 }1146}1147impl<T> MaxEncodedLen for PhantomType<T> {1148 fn max_encoded_len() -> usize {1149 01150 }1151}11521153/// Bounded vector of bytes.1154pub type BoundedBytes<S> = BoundedVec<u8, S>;11551156/// Extra properties for external collections.1157pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;11581159/// Property key.1160pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;11611162/// Property value.1163pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;11641165/// Property permission.1166#[derive(1167 Encode,1168 Decode,1169 TypeInfo,1170 Debug,1171 MaxEncodedLen,1172 PartialEq,1173 Clone,1174 Default,1175 Serialize,1176 Deserialize,1177)]1178pub struct PropertyPermission {1179 /// Permission to change the property and property permission.1180 ///1181 /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1182 pub mutable: bool,11831184 /// Change permission for the collection administrator.1185 pub collection_admin: bool,11861187 /// Permission to change the property for the owner of the token.1188 pub token_owner: bool,1189}11901191impl PropertyPermission {1192 /// Creates mutable property permission but changes restricted for collection admin and token owner.1193 pub fn none() -> Self {1194 Self {1195 mutable: true,1196 collection_admin: false,1197 token_owner: false,1198 }1199 }1200}12011202/// Property is simpl key-value record.1203#[derive(1204 Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1205)]1206pub struct Property {1207 /// Property key.1208 #[serde(with = "bounded::vec_serde")]1209 pub key: PropertyKey,12101211 /// Property value.1212 #[serde(with = "bounded::vec_serde")]1213 pub value: PropertyValue,1214}12151216impl From<Property> for (PropertyKey, PropertyValue) {1217 fn from(value: Property) -> Self {1218 (value.key, value.value)1219 }1220}12211222/// Record for proprty key permission.1223#[derive(1224 Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1225)]1226pub struct PropertyKeyPermission {1227 /// Key.1228 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1229 pub key: PropertyKey,12301231 /// Permission.1232 pub permission: PropertyPermission,1233}12341235impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1236 fn from(value: PropertyKeyPermission) -> Self {1237 (value.key, value.permission)1238 }1239}12401241/// Errors for properties actions.1242#[derive(Debug)]1243pub enum PropertiesError {1244 /// The space allocated for properties has run out.1245 ///1246 /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1247 /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1248 NoSpaceForProperty,12491250 /// The property limit has been reached.1251 ///1252 /// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1253 PropertyLimitReached,12541255 /// Property key contains not allowed character.1256 InvalidCharacterInPropertyKey,12571258 /// Property key length is too long.1259 ///1260 /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1261 PropertyKeyIsTooLong,12621263 /// Property key is empty.1264 EmptyPropertyKey,1265}12661267/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1268#[derive(Debug)]1269pub enum TokenOwnerError {1270 NotFound,1271 MultipleOwners,1272}12731274/// Marker for scope of property.1275///1276/// Scoped property can't be changed by user. Used for external collections.1277#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1278pub enum PropertyScope {1279 None,1280 Rmrk,1281}12821283impl PropertyScope {1284 pub fn prefix(&self) -> &'static [u8] {1285 match self {1286 Self::None => b"",1287 Self::Rmrk => b"rmrk:",1288 }1289 }1290 /// Apply scope to property key.1291 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1292 let prefix = self.prefix();1293 if prefix == b"" {1294 return Ok(key);1295 }1296 [prefix, key.as_slice()]1297 .concat()1298 .try_into()1299 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1300 }1301}13021303/// Trait for operate with properties.1304pub trait TrySetProperty: Sized {1305 type Value;13061307 /// Try to set property with scope.1308 fn try_scoped_set(1309 &mut self,1310 scope: PropertyScope,1311 key: PropertyKey,1312 value: Self::Value,1313 ) -> Result<Option<Self::Value>, PropertiesError>;13141315 /// Try to set property with scope from iterator.1316 fn try_scoped_set_from_iter<I, KV>(1317 &mut self,1318 scope: PropertyScope,1319 iter: I,1320 ) -> Result<(), PropertiesError>1321 where1322 I: Iterator<Item = KV>,1323 KV: Into<(PropertyKey, Self::Value)>,1324 {1325 for kv in iter {1326 let (key, value) = kv.into();1327 self.try_scoped_set(scope, key, value)?;1328 }13291330 Ok(())1331 }13321333 /// Try to set property.1334 fn try_set(1335 &mut self,1336 key: PropertyKey,1337 value: Self::Value,1338 ) -> Result<Option<Self::Value>, PropertiesError> {1339 self.try_scoped_set(PropertyScope::None, key, value)1340 }13411342 /// Try to set property from iterator.1343 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1344 where1345 I: Iterator<Item = KV>,1346 KV: Into<(PropertyKey, Self::Value)>,1347 {1348 self.try_scoped_set_from_iter(PropertyScope::None, iter)1349 }1350}13511352/// Wrapped map for storing properties.1353#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1354#[derivative(Default(bound = ""))]1355pub struct PropertiesMap<Value>(1356 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1357);13581359impl<Value> PropertiesMap<Value> {1360 /// Create new property map.1361 pub fn new() -> Self {1362 Self(BoundedBTreeMap::new())1363 }13641365 /// Remove property from map.1366 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1367 Self::check_property_key(key)?;13681369 Ok(self.0.remove(key))1370 }13711372 /// Get property with appropriate key from map.1373 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1374 self.0.get(key)1375 }13761377 /// Check if map contains key.1378 pub fn contains_key(&self, key: &PropertyKey) -> bool {1379 self.0.contains_key(key)1380 }13811382 /// Check if map contains key with key validation.1383 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1384 if key.is_empty() {1385 return Err(PropertiesError::EmptyPropertyKey);1386 }13871388 for byte in key.as_slice().iter() {1389 let byte = *byte;13901391 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1392 return Err(PropertiesError::InvalidCharacterInPropertyKey);1393 }1394 }13951396 Ok(())1397 }13981399 pub fn values(&self) -> impl Iterator<Item = &Value> {1400 self.0.values()1401 }14021403 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1404 self.0.iter()1405 }1406}14071408impl<Value> IntoIterator for PropertiesMap<Value> {1409 type Item = (PropertyKey, Value);1410 type IntoIter = <1411 BoundedBTreeMap<1412 PropertyKey,1413 Value,1414 ConstU32<MAX_PROPERTIES_PER_ITEM>1415 > as IntoIterator1416 >::IntoIter;14171418 fn into_iter(self) -> Self::IntoIter {1419 self.0.into_iter()1420 }1421}14221423impl<Value> TrySetProperty for PropertiesMap<Value> {1424 type Value = Value;14251426 fn try_scoped_set(1427 &mut self,1428 scope: PropertyScope,1429 key: PropertyKey,1430 value: Self::Value,1431 ) -> Result<Option<Self::Value>, PropertiesError> {1432 Self::check_property_key(&key)?;14331434 let key = scope.apply(key)?;1435 self.01436 .try_insert(key, value)1437 .map_err(|_| PropertiesError::PropertyLimitReached)1438 }1439}14401441/// Alias for property permissions map.1442pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14431444fn slice_size(data: &[u8]) -> u32 {1445 scoped_slice_size(PropertyScope::None, data)1446}1447fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1448 use parity_scale_codec::Compact;1449 let prefix = scope.prefix();1450 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321451 + data.len() as u321452 + prefix.len() as u321453}14541455/// Wrapper for properties map with consumed space control.1456#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1457pub struct Properties<const S: u32> {1458 map: PropertiesMap<PropertyValue>,1459 consumed_space: u32,1460 // May be not zero, previously served as a current S generic1461 _reserved: u32,1462}14631464impl<const S: u32> MaxEncodedLen for Properties<S> {1465 fn max_encoded_len() -> usize {1466 // len of map + len of consumed_space + len of space_limit1467 u32::max_encoded_len() * 3 + S as usize1468 }1469}14701471impl<const S: u32> Default for Properties<S> {1472 fn default() -> Self {1473 Self::new()1474 }1475}14761477impl<const S: u32> Properties<S> {1478 /// Create new properies container.1479 pub fn new() -> Self {1480 Self {1481 map: PropertiesMap::new(),1482 consumed_space: 0,1483 _reserved: 0,1484 }1485 }14861487 /// Remove propery with appropiate key.1488 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1489 let value = self.map.remove(key)?;14901491 if let Some(ref value) = value {1492 let kv_len = slice_size(key) + slice_size(value);1493 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1494 }14951496 Ok(value)1497 }14981499 /// Get property with appropriate key.1500 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1501 self.map.get(key)1502 }15031504 /// Recomputes the consumed space for the current properties state.1505 /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1506 pub fn recompute_consumed_space(&mut self) {1507 self.consumed_space = self1508 .map1509 .iter()1510 .map(|(key, value)| slice_size(key) + slice_size(value))1511 .sum();1512 }1513}15141515impl<const S: u32> IntoIterator for Properties<S> {1516 type Item = (PropertyKey, PropertyValue);1517 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15181519 fn into_iter(self) -> Self::IntoIter {1520 self.map.into_iter()1521 }1522}15231524impl<const S: u32> TrySetProperty for Properties<S> {1525 type Value = PropertyValue;15261527 fn try_scoped_set(1528 &mut self,1529 scope: PropertyScope,1530 key: PropertyKey,1531 value: Self::Value,1532 ) -> Result<Option<Self::Value>, PropertiesError> {1533 let key_size = scoped_slice_size(scope, &key);1534 let value_size = slice_size(&value);15351536 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1537 {1538 return Err(PropertiesError::NoSpaceForProperty);1539 }15401541 let old_value = self.map.try_scoped_set(scope, key, value)?;15421543 if let Some(old_value) = old_value.as_ref() {1544 let old_value_size = slice_size(old_value);1545 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1546 } else {1547 self.consumed_space += key_size + value_size;1548 }15491550 Ok(old_value)1551 }1552}15531554pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1555pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;