1234567891011121314151617#![cfg_attr(not(feature = "std"), no_std)]1819use core::{20 convert::{TryFrom, TryInto},21 fmt,22};23use frame_support::{24 storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},25 traits::Get,26 parameter_types,27};2829#[cfg(feature = "serde")]30use serde::{Serialize, Deserialize};3132use sp_core::U256;33use sp_runtime::{ArithmeticError, sp_std::prelude::Vec, Permill};34use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};35use frame_support::{BoundedVec, traits::ConstU32};36use derivative::Derivative;37use scale_info::TypeInfo;3839pub mod rmrk;404142use rmrk::{43 CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme, ThemeProperty,44};45pub use rmrk::{46 primitives::{47 CollectionId as RmrkCollectionId, NftId as RmrkNftId, BaseId as RmrkBaseId,48 PartId as RmrkPartId, ResourceId as RmrkResourceId,49 },50 NftChild as RmrkNftChild, AccountIdOrCollectionNftTuple as RmrkAccountIdOrCollectionNftTuple,51};5253mod bounded;54pub mod budget;55pub mod mapping;56mod migration;5758pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;59pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;60pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;6162pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {63 100_00064} else {65 1066};67pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {68 100_00069} else {70 1071};72pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73 204874} else {75 1076};77pub const COLLECTION_ADMINS_LIMIT: u32 = 5;78pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;79pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {80 1_000_00081} else {82 1083};848586pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;87pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;88pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;8990pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;919293pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;94pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;95pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;9697pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;9899pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;100pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;101pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;102103pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;104pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;105pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;106107108pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;109pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;110111pub const MAX_COLLECTION_PROPERTIES_ENCODE_LEN: u32 =112 MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH + MAX_COLLECTION_PROPERTIES_SIZE;113114115pub const RMRK_STRING_LIMIT: u32 = 128;116pub const RMRK_COLLECTION_SYMBOL_LIMIT: u32 = 100;117pub const RMRK_RESOURCE_SYMBOL_LIMIT: u32 = 10;118pub const RMRK_KEY_LIMIT: u32 = 32;119pub const RMRK_VALUE_LIMIT: u32 = 256;120121pub struct MaxPropertiesPermissionsEncodeLen;122123impl Get<u32> for MaxPropertiesPermissionsEncodeLen {124 fn get() -> u32 {125 MAX_PROPERTIES_PER_ITEM * MAX_PROPERTY_KEY_LENGTH126 + <PropertyPermission as MaxEncodedLen>::max_encoded_len() as u32127 }128}129130131132pub const MAX_ITEMS_PER_BATCH: u32 = 200;133134pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;135136#[derive(137 Encode,138 Decode,139 PartialEq,140 Eq,141 PartialOrd,142 Ord,143 Clone,144 Copy,145 Debug,146 Default,147 TypeInfo,148 MaxEncodedLen,149)]150#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]151pub struct CollectionId(pub u32);152impl EncodeLike<u32> for CollectionId {}153impl EncodeLike<CollectionId> for u32 {}154155#[derive(156 Encode,157 Decode,158 PartialEq,159 Eq,160 PartialOrd,161 Ord,162 Clone,163 Copy,164 Debug,165 Default,166 TypeInfo,167 MaxEncodedLen,168)]169#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]170pub struct TokenId(pub u32);171impl EncodeLike<u32> for TokenId {}172impl EncodeLike<TokenId> for u32 {}173174impl TokenId {175 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {176 self.0177 .checked_add(1)178 .ok_or(ArithmeticError::Overflow)179 .map(Self)180 }181}182183impl From<TokenId> for U256 {184 fn from(t: TokenId) -> Self {185 t.0.into()186 }187}188189impl TryFrom<U256> for TokenId {190 type Error = &'static str;191192 fn try_from(value: U256) -> Result<Self, Self::Error> {193 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))194 }195}196197#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]198#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]199pub struct TokenData<CrossAccountId> {200 pub const_data: Vec<u8>,201 pub properties: Vec<Property>,202 pub owner: Option<CrossAccountId>,203}204205pub struct OverflowError;206impl From<OverflowError> for &'static str {207 fn from(_: OverflowError) -> Self {208 "overflow occured"209 }210}211212pub type DecimalPoints = u8;213214#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]215#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]216pub enum CollectionMode {217 NFT,218 219 Fungible(DecimalPoints),220 ReFungible,221}222223impl CollectionMode {224 pub fn id(&self) -> u8 {225 match self {226 CollectionMode::NFT => 1,227 CollectionMode::Fungible(_) => 2,228 CollectionMode::ReFungible => 3,229 }230 }231}232233pub trait SponsoringResolve<AccountId, Call> {234 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;235}236237#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]238#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]239pub enum AccessMode {240 Normal,241 AllowList,242}243impl Default for AccessMode {244 fn default() -> Self {245 Self::Normal246 }247}248249#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]251pub enum SchemaVersion {252 ImageURL,253 Unique,254}255impl Default for SchemaVersion {256 fn default() -> Self {257 Self::ImageURL258 }259}260261#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]262#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]263pub struct Ownership<AccountId> {264 pub owner: AccountId,265 pub fraction: u128,266}267268#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]269#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]270pub enum SponsorshipState<AccountId> {271 272 Disabled,273 Unconfirmed(AccountId),274 275 Confirmed(AccountId),276}277278impl<AccountId> SponsorshipState<AccountId> {279 pub fn sponsor(&self) -> Option<&AccountId> {280 match self {281 Self::Confirmed(sponsor) => Some(sponsor),282 _ => None,283 }284 }285286 pub fn pending_sponsor(&self) -> Option<&AccountId> {287 match self {288 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),289 _ => None,290 }291 }292293 pub fn confirmed(&self) -> bool {294 matches!(self, Self::Confirmed(_))295 }296}297298impl<T> Default for SponsorshipState<T> {299 fn default() -> Self {300 Self::Disabled301 }302}303304305#[struct_versioning::versioned(version = 2, upper)]306#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]307pub struct Collection<AccountId> {308 pub owner: AccountId,309 pub mode: CollectionMode,310 pub access: AccessMode,311 pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,312 pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,313 pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,314 pub mint_mode: bool,315316 #[version(..2)]317 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,318319 pub schema_version: SchemaVersion,320 pub sponsorship: SponsorshipState<AccountId>,321322 #[version(..2)]323 pub limits: CollectionLimitsVersion1, 324 #[version(2.., upper(limits.into()))]325 pub limits: CollectionLimitsVersion2,326327 #[version(..2)]328 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,329330 #[version(..2)]331 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,332333 #[version(..2)]334 pub meta_update_permission: MetaUpdatePermission,335}336337338#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]339#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]340pub struct RpcCollection<AccountId> {341 pub owner: AccountId,342 pub mode: CollectionMode,343 pub access: AccessMode,344 pub name: Vec<u16>,345 pub description: Vec<u16>,346 pub token_prefix: Vec<u8>,347 pub mint_mode: bool,348 pub offchain_schema: Vec<u8>,349 pub schema_version: SchemaVersion,350 pub sponsorship: SponsorshipState<AccountId>,351 pub limits: CollectionLimits,352 pub const_on_chain_schema: Vec<u8>,353 pub token_property_permissions: Vec<PropertyKeyPermission>,354 pub properties: Vec<Property>,355}356357#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]358#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]359pub enum CollectionField {360 ConstOnChainSchema,361 OffchainSchema,362}363364#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]365#[derivative(Debug, Default(bound = ""))]366pub struct CreateCollectionData<AccountId> {367 #[derivative(Default(value = "CollectionMode::NFT"))]368 pub mode: CollectionMode,369 pub access: Option<AccessMode>,370 pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,371 pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,372 pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,373 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,374 pub schema_version: Option<SchemaVersion>,375 pub pending_sponsor: Option<AccountId>,376 pub limits: Option<CollectionLimits>,377 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,378 pub token_property_permissions: CollectionPropertiesPermissionsVec,379 pub properties: CollectionPropertiesVec,380}381382pub type CollectionPropertiesPermissionsVec =383 BoundedVec<PropertyKeyPermission, MaxPropertiesPermissionsEncodeLen>;384385pub type CollectionPropertiesVec =386 BoundedVec<Property, ConstU32<MAX_COLLECTION_PROPERTIES_ENCODE_LEN>>;387388389#[struct_versioning::versioned(version = 2, upper)]390#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]391#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]392pub struct CollectionLimits {393 pub account_token_ownership_limit: Option<u32>,394 pub sponsored_data_size: Option<u32>,395396 397 398 399 400 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,401 pub token_limit: Option<u32>,402403 404 pub sponsor_transfer_timeout: Option<u32>,405 pub sponsor_approve_timeout: Option<u32>,406 pub owner_can_transfer: Option<bool>,407 pub owner_can_destroy: Option<bool>,408 pub transfers_enabled: Option<bool>,409410 #[version(2.., upper(None))]411 pub nesting_rule: Option<NestingRule>,412}413414impl CollectionLimits {415 pub fn account_token_ownership_limit(&self) -> u32 {416 self.account_token_ownership_limit417 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)418 .min(MAX_TOKEN_OWNERSHIP)419 }420 pub fn sponsored_data_size(&self) -> u32 {421 self.sponsored_data_size422 .unwrap_or(CUSTOM_DATA_LIMIT)423 .min(CUSTOM_DATA_LIMIT)424 }425 pub fn token_limit(&self) -> u32 {426 self.token_limit427 .unwrap_or(COLLECTION_TOKEN_LIMIT)428 .min(COLLECTION_TOKEN_LIMIT)429 }430 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {431 self.sponsor_transfer_timeout432 .unwrap_or(default)433 .min(MAX_SPONSOR_TIMEOUT)434 }435 pub fn sponsor_approve_timeout(&self) -> u32 {436 self.sponsor_approve_timeout437 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)438 .min(MAX_SPONSOR_TIMEOUT)439 }440 pub fn owner_can_transfer(&self) -> bool {441 self.owner_can_transfer.unwrap_or(true)442 }443 pub fn owner_can_destroy(&self) -> bool {444 self.owner_can_destroy.unwrap_or(true)445 }446 pub fn transfers_enabled(&self) -> bool {447 self.transfers_enabled.unwrap_or(true)448 }449 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {450 match self451 .sponsored_data_rate_limit452 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)453 {454 SponsoringRateLimit::SponsoringDisabled => None,455 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),456 }457 }458 pub fn nesting_rule(&self) -> &NestingRule {459 static DEFAULT: NestingRule = NestingRule::Disabled;460 self.nesting_rule.as_ref().unwrap_or(&DEFAULT)461 }462}463464#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]465#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]466#[derivative(Debug)]467pub enum NestingRule {468 469 Disabled,470 471 Owner,472 473 OwnerRestricted(474 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]475 #[derivative(Debug(format_with = "bounded::set_debug"))]476 BoundedBTreeSet<CollectionId, ConstU32<16>>,477 ),478}479480#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]481#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]482pub enum SponsoringRateLimit {483 SponsoringDisabled,484 Blocks(u32),485}486487#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]488#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]489#[derivative(Debug)]490pub struct CreateNftData {491 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]492 #[derivative(Debug(format_with = "bounded::vec_debug"))]493 pub const_data: BoundedVec<u8, CustomDataLimit>,494495 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]496 #[derivative(Debug(format_with = "bounded::vec_debug"))]497 pub properties: CollectionPropertiesVec,498}499500#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]501#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]502pub struct CreateFungibleData {503 pub value: u128,504}505506#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]507#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]508#[derivative(Debug)]509pub struct CreateReFungibleData {510 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]511 #[derivative(Debug(format_with = "bounded::vec_debug"))]512 pub const_data: BoundedVec<u8, CustomDataLimit>,513 pub pieces: u128,514}515516#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]517pub enum MetaUpdatePermission {518 ItemOwner,519 Admin,520 None,521}522523#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]524#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]525pub enum CreateItemData {526 NFT(CreateNftData),527 Fungible(CreateFungibleData),528 ReFungible(CreateReFungibleData),529}530531#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]532#[derivative(Debug)]533pub struct CreateNftExData<CrossAccountId> {534 #[derivative(Debug(format_with = "bounded::vec_debug"))]535 pub const_data: BoundedVec<u8, CustomDataLimit>,536 #[derivative(Debug(format_with = "bounded::vec_debug"))]537 pub properties: CollectionPropertiesVec,538 pub owner: CrossAccountId,539}540541#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]542#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]543pub struct CreateRefungibleExData<CrossAccountId> {544 #[derivative(Debug(format_with = "bounded::vec_debug"))]545 pub const_data: BoundedVec<u8, CustomDataLimit>,546 #[derivative(Debug(format_with = "bounded::map_debug"))]547 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,548}549550#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]551#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]552pub enum CreateItemExData<CrossAccountId> {553 NFT(554 #[derivative(Debug(format_with = "bounded::vec_debug"))]555 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,556 ),557 Fungible(558 #[derivative(Debug(format_with = "bounded::map_debug"))]559 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,560 ),561 562 RefungibleMultipleItems(563 #[derivative(Debug(format_with = "bounded::vec_debug"))]564 BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,565 ),566 567 RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),568}569570impl CreateItemData {571 pub fn data_size(&self) -> usize {572 match self {573 CreateItemData::NFT(data) => data.const_data.len(),574 CreateItemData::ReFungible(data) => data.const_data.len(),575 _ => 0,576 }577 }578}579580impl From<CreateNftData> for CreateItemData {581 fn from(item: CreateNftData) -> Self {582 CreateItemData::NFT(item)583 }584}585586impl From<CreateReFungibleData> for CreateItemData {587 fn from(item: CreateReFungibleData) -> Self {588 CreateItemData::ReFungible(item)589 }590}591592impl From<CreateFungibleData> for CreateItemData {593 fn from(item: CreateFungibleData) -> Self {594 CreateItemData::Fungible(item)595 }596}597598#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]599#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]600pub struct CollectionStats {601 pub created: u32,602 pub destroyed: u32,603 pub alive: u32,604}605606#[derive(Encode, Decode, Clone, Debug)]607#[cfg_attr(feature = "std", derive(PartialEq))]608pub struct PhantomType<T>(core::marker::PhantomData<T>);609610impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {611 type Identity = PhantomType<T>;612613 fn type_info() -> scale_info::Type {614 use scale_info::{615 Type, Path,616 build::{FieldsBuilder, UnnamedFields},617 type_params,618 };619 Type::builder()620 .path(Path::new("up_data_structs", "PhantomType"))621 .type_params(type_params!(T))622 .composite(<FieldsBuilder<UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()))623 }624}625impl<T> MaxEncodedLen for PhantomType<T> {626 fn max_encoded_len() -> usize {627 0628 }629}630631pub type PropertyKey = BoundedVec<u8, ConstU32<MAX_PROPERTY_KEY_LENGTH>>;632pub type PropertyValue = BoundedVec<u8, ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;633634#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]635#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]636pub struct PropertyPermission {637 pub mutable: bool,638 pub collection_admin: bool,639 pub token_owner: bool,640}641642impl PropertyPermission {643 pub fn none() -> Self {644 Self {645 mutable: true,646 collection_admin: false,647 token_owner: false,648 }649 }650}651652#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]653#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]654pub struct Property {655 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]656 pub key: PropertyKey,657658 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]659 pub value: PropertyValue,660}661662impl Into<(PropertyKey, PropertyValue)> for Property {663 fn into(self) -> (PropertyKey, PropertyValue) {664 (self.key, self.value)665 }666}667668#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]669#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]670pub struct PropertyKeyPermission {671 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]672 pub key: PropertyKey,673674 pub permission: PropertyPermission,675}676677impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {678 fn into(self) -> (PropertyKey, PropertyPermission) {679 (self.key, self.permission)680 }681}682683#[derive(Debug)]684pub enum PropertiesError {685 NoSpaceForProperty,686 PropertyLimitReached,687 InvalidCharacterInPropertyKey,688 PropertyKeyIsTooLong,689 EmptyPropertyKey,690}691692#[derive(Clone, Copy)]693pub enum PropertyScope {694 None,695 Rmrk,696}697698impl PropertyScope {699 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {700 let scope_str: &[u8] = match self {701 Self::None => return Ok(key),702 Self::Rmrk => b"rmrk",703 };704705 [scope_str, b":", key.as_slice()]706 .concat()707 .try_into()708 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)709 }710}711712pub trait TrySetProperty: Sized {713 type Value;714715 fn try_scoped_set(716 &mut self,717 scope: PropertyScope,718 key: PropertyKey,719 value: Self::Value,720 ) -> Result<(), PropertiesError>;721722 fn try_scoped_set_from_iter<I, KV>(723 &mut self,724 scope: PropertyScope,725 iter: I,726 ) -> Result<(), PropertiesError>727 where728 I: Iterator<Item=KV>,729 KV: Into<(PropertyKey, Self::Value)>730 {731 for kv in iter {732 let (key, value) = kv.into();733 self.try_scoped_set(scope, key, value)?;734 }735736 Ok(())737 }738739 fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> {740 self.try_scoped_set(PropertyScope::None, key, value)741 }742743 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>744 where745 I: Iterator<Item=KV>,746 KV: Into<(PropertyKey, Self::Value)>747 {748 self.try_scoped_set_from_iter(PropertyScope::None, iter)749 }750}751752#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]753#[derivative(Default(bound = ""))]754pub struct PropertiesMap<Value>(755 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,756);757758impl<Value> PropertiesMap<Value> {759 pub fn new() -> Self {760 Self(BoundedBTreeMap::new())761 }762763 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {764 Self::check_property_key(key)?;765766 Ok(self.0.remove(key))767 }768769 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {770 self.0.get(key)771 }772773 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {774 self.0.iter()775 }776777 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {778 if key.is_empty() {779 return Err(PropertiesError::EmptyPropertyKey);780 }781782 for byte in key.as_slice().iter() {783 let byte = *byte;784785 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' {786 return Err(PropertiesError::InvalidCharacterInPropertyKey);787 }788 }789790 Ok(())791 }792}793794impl<Value> TrySetProperty for PropertiesMap<Value> {795 type Value = Value;796797 fn try_scoped_set(798 &mut self,799 scope: PropertyScope,800 key: PropertyKey,801 value: Self::Value,802 ) -> Result<(), PropertiesError> {803 Self::check_property_key(&key)?;804805 let key = scope.apply(key)?;806 self.0807 .try_insert(key, value)808 .map_err(|_| PropertiesError::PropertyLimitReached)?;809810 Ok(())811 }812}813814pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;815816#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]817pub struct Properties {818 map: PropertiesMap<PropertyValue>,819 consumed_space: u32,820 space_limit: u32,821}822823impl Properties {824 pub fn new(space_limit: u32) -> Self {825 Self {826 map: PropertiesMap::new(),827 consumed_space: 0,828 space_limit,829 }830 }831832 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {833 let value = self.map.remove(key)?;834835 if let Some(ref value) = value {836 let value_len = value.len() as u32;837 self.consumed_space -= value_len;838 }839840 Ok(value)841 }842843 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {844 self.map.get(key)845 }846847 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &PropertyValue)> {848 self.map.iter()849 }850}851852impl TrySetProperty for Properties {853 type Value = PropertyValue;854855 fn try_scoped_set(856 &mut self,857 scope: PropertyScope,858 key: PropertyKey,859 value: Self::Value,860 ) -> Result<(), PropertiesError> {861 let value_len = value.len();862863 if self.consumed_space as usize + value_len > self.space_limit as usize {864 return Err(PropertiesError::NoSpaceForProperty);865 }866867 self.map.try_scoped_set(scope, key, value)?;868869 self.consumed_space += value_len as u32;870871 Ok(())872 }873}874875pub struct CollectionProperties;876877impl Get<Properties> for CollectionProperties {878 fn get() -> Properties {879 Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)880 }881}882883pub struct TokenProperties;884885impl Get<Properties> for TokenProperties {886 fn get() -> Properties {887 Properties::new(MAX_TOKEN_PROPERTIES_SIZE)888 }889}890891892893parameter_types! {894 #[derive(PartialEq, TypeInfo)]895 pub const RmrkStringLimit: u32 = 128;896 #[derive(PartialEq)]897 pub const RmrkCollectionSymbolLimit: u32 = 100;898 #[derive(PartialEq)]899 pub const RmrkResourceSymbolLimit: u32 = 10;900 #[derive(PartialEq)]901 pub const RmrkKeyLimit: u32 = 32;902 #[derive(PartialEq)]903 pub const RmrkValueLimit: u32 = 256;904 #[derive(PartialEq)]905 pub const RmrkMaxCollectionsEquippablePerPart: u32 = 100;906 #[derive(PartialEq)]907 pub const RmrkPartsLimit: u32 = 3;908}909910impl From<RmrkCollectionId> for CollectionId {911 fn from(id: RmrkCollectionId) -> Self {912 Self(id)913 }914}915916impl From<RmrkNftId> for TokenId {917 fn from(id: RmrkNftId) -> Self {918 Self(id)919 }920}921922pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;923pub type RmrkCollectionInfo<AccountId> = CollectionInfo<RmrkString, RmrkCollectionSymbol, AccountId>;924pub type RmrkInstanceInfo<AccountId> = NftInfo<AccountId, Permill, RmrkString>;925pub type RmrkResourceInfo = ResourceInfo<926 BoundedVec<u8, RmrkResourceSymbolLimit>,927 RmrkString,928 BoundedVec<RmrkPartId, RmrkPartsLimit>,929>;930pub type RmrkPropertyInfo =931 PropertyInfo<BoundedVec<u8, RmrkKeyLimit>, BoundedVec<u8, RmrkValueLimit>>;932pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;933pub type RmrkPartType =934 PartType<RmrkString, BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>>;935pub type RmrkTheme = Theme<RmrkString, Vec<ThemeProperty<RmrkString>>>;936937pub type RmrkRpcString = Vec<u8>;938pub type RmrkThemeName = RmrkRpcString;939pub type RmrkPropertyKey = RmrkRpcString;940941pub type RmrkString = BoundedVec<u8, RmrkStringLimit>;