123456789101112131415161718192021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26};27use frame_support::{28 storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},29 traits::Get,30};3132#[cfg(feature = "serde")]33use serde::{Serialize, Deserialize};3435use sp_core::U256;36use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};37use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use bondrewd::Bitfields;39use frame_support::{BoundedVec, traits::ConstU32};40use derivative::Derivative;41use scale_info::TypeInfo;4243mod bondrewd_codec;44mod bounded;45pub mod budget;46pub mod mapping;47mod migration;484950pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;515253pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;54pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;555657pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {58 100_00059} else {60 1061};626364pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {65 100_00066} else {67 1068};697071pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {72 204873} else {74 1075};767778pub const COLLECTION_ADMINS_LIMIT: u32 = 5;798081pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;828384pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {85 1_000_00086} else {87 1088};899091pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9293pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9495pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;969798pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;99100101pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;102pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;104105106pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;107108109pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;110111112pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;113114115pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;116117118pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;119120121pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;122123124pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;125126127pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;128129130pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;131132133pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;134135136137pub const MAX_ITEMS_PER_BATCH: u32 = 200;138139140pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;141142143#[derive(144 Encode,145 Decode,146 PartialEq,147 Eq,148 PartialOrd,149 Ord,150 Clone,151 Copy,152 Debug,153 Default,154 TypeInfo,155 MaxEncodedLen,156)]157#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]158pub struct CollectionId(pub u32);159impl EncodeLike<u32> for CollectionId {}160impl EncodeLike<CollectionId> for u32 {}161162impl From<u32> for CollectionId {163 fn from(value: u32) -> Self {164 Self(value)165 }166}167168169#[derive(170 Encode,171 Decode,172 PartialEq,173 Eq,174 PartialOrd,175 Ord,176 Clone,177 Copy,178 Debug,179 Default,180 TypeInfo,181 MaxEncodedLen,182)]183#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]184pub struct TokenId(pub u32);185impl EncodeLike<u32> for TokenId {}186impl EncodeLike<TokenId> for u32 {}187188impl TokenId {189 190 191 192 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {193 self.0194 .checked_add(1)195 .ok_or(ArithmeticError::Overflow)196 .map(Self)197 }198}199200impl From<TokenId> for U256 {201 fn from(t: TokenId) -> Self {202 t.0.into()203 }204}205206impl TryFrom<U256> for TokenId {207 type Error = &'static str;208209 fn try_from(value: U256) -> Result<Self, Self::Error> {210 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))211 }212}213214215#[struct_versioning::versioned(version = 2, upper)]216#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]217#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]218pub struct TokenData<CrossAccountId> {219 220 pub properties: Vec<Property>,221222 223 pub owner: Option<CrossAccountId>,224225 226 #[version(2.., upper(0))]227 pub pieces: u128,228}229230231pub struct OverflowError;232impl From<OverflowError> for &'static str {233 fn from(_: OverflowError) -> Self {234 "overflow occured"235 }236}237238239pub type DecimalPoints = u8;240241242243244245246#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]247#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]248pub enum CollectionMode {249 250 NFT,251 252 Fungible(DecimalPoints),253 254 ReFungible,255}256257impl CollectionMode {258 259 pub fn id(&self) -> u8 {260 match self {261 CollectionMode::NFT => 1,262 CollectionMode::Fungible(_) => 2,263 CollectionMode::ReFungible => 3,264 }265 }266}267268269pub trait SponsoringResolve<AccountId, Call> {270 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;271}272273274#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]275#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]276pub enum AccessMode {277 278 Normal,279 280 AllowList,281}282impl Default for AccessMode {283 fn default() -> Self {284 Self::Normal285 }286}287288289#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]290#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]291pub enum SchemaVersion {292 ImageURL,293 Unique,294}295impl Default for SchemaVersion {296 fn default() -> Self {297 Self::ImageURL298 }299}300301302#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]303#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]304pub struct Ownership<AccountId> {305 pub owner: AccountId,306 pub fraction: u128,307}308309310#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]311#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]312pub enum SponsorshipState<AccountId> {313 314 Disabled,315 316 317 Unconfirmed(AccountId),318 319 Confirmed(AccountId),320}321322impl<AccountId> SponsorshipState<AccountId> {323 324 pub fn sponsor(&self) -> Option<&AccountId> {325 match self {326 Self::Confirmed(sponsor) => Some(sponsor),327 _ => None,328 }329 }330331 332 pub fn pending_sponsor(&self) -> Option<&AccountId> {333 match self {334 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),335 _ => None,336 }337 }338339 340 pub fn confirmed(&self) -> bool {341 matches!(self, Self::Confirmed(_))342 }343}344345impl<T> Default for SponsorshipState<T> {346 fn default() -> Self {347 Self::Disabled348 }349}350351pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;352pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;353pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;354355#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]356#[bondrewd(enforce_bytes = 1)]357pub struct CollectionFlags {358 359 #[bondrewd(bits = "0..1")]360 pub foreign: bool,361 362 #[bondrewd(bits = "1..2")]363 pub erc721metadata: bool,364 365 #[bondrewd(bits = "7..8")]366 pub external: bool,367368 #[bondrewd(reserve, bits = "2..7")]369 pub reserved: u8,370}371bondrewd_codec!(CollectionFlags);372373374375376377378379#[struct_versioning::versioned(version = 2, upper)]380#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]381pub struct Collection<AccountId> {382 383 pub owner: AccountId,384385 386 pub mode: CollectionMode,387388 389 #[version(..2)]390 pub access: AccessMode,391392 393 pub name: CollectionName,394395 396 pub description: CollectionDescription,397398 399 pub token_prefix: CollectionTokenPrefix,400401 #[version(..2)]402 pub mint_mode: bool,403404 #[version(..2)]405 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,406407 #[version(..2)]408 pub schema_version: SchemaVersion,409410 411 pub sponsorship: SponsorshipState<AccountId>,412413 414 pub limits: CollectionLimits,415416 417 #[version(2.., upper(Default::default()))]418 pub permissions: CollectionPermissions,419420 #[version(2.., upper(Default::default()))]421 pub flags: CollectionFlags,422423 #[version(..2)]424 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,425426 #[version(..2)]427 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,428429 #[version(..2)]430 pub meta_update_permission: MetaUpdatePermission,431}432433#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]434#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]435pub struct RpcCollectionFlags {436 437 pub foreign: bool,438 439 pub erc721metadata: bool,440}441442443#[struct_versioning::versioned(version = 2, upper)]444#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]445#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]446pub struct RpcCollection<AccountId> {447 448 pub owner: AccountId,449450 451 pub mode: CollectionMode,452453 454 pub name: Vec<u16>,455456 457 pub description: Vec<u16>,458459 460 pub token_prefix: Vec<u8>,461462 463 pub sponsorship: SponsorshipState<AccountId>,464465 466 pub limits: CollectionLimits,467468 469 pub permissions: CollectionPermissions,470471 472 pub token_property_permissions: Vec<PropertyKeyPermission>,473474 475 pub properties: Vec<Property>,476477 478 pub read_only: bool,479480 481 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]482 pub flags: RpcCollectionFlags,483}484485486487488#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]489#[derivative(Debug, Default(bound = ""))]490pub struct CreateCollectionData<AccountId> {491 492 #[derivative(Default(value = "CollectionMode::NFT"))]493 pub mode: CollectionMode,494495 496 pub access: Option<AccessMode>,497498 499 pub name: CollectionName,500501 502 pub description: CollectionDescription,503504 505 pub token_prefix: CollectionTokenPrefix,506507 508 pub pending_sponsor: Option<AccountId>,509510 511 pub limits: Option<CollectionLimits>,512513 514 pub permissions: Option<CollectionPermissions>,515516 517 pub token_property_permissions: CollectionPropertiesPermissionsVec,518519 520 pub properties: CollectionPropertiesVec,521}522523524525pub type CollectionPropertiesPermissionsVec =526 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;527528529pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;530531532533534535536537#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]538#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]539540541542pub struct CollectionLimits {543 544 545 546 pub account_token_ownership_limit: Option<u32>,547548 549 550 551 pub sponsored_data_size: Option<u32>,552553 554 555 556 557 558 559 560 561 562 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,563 564565 566 567 568 569 pub token_limit: Option<u32>,570571 572 573 574 575 576 577 578 pub sponsor_transfer_timeout: Option<u32>,579580 581 582 583 584 pub sponsor_approve_timeout: Option<u32>,585586 587 588 589 pub owner_can_transfer: Option<bool>,590591 592 593 594 pub owner_can_destroy: Option<bool>,595596 597 598 599 pub transfers_enabled: Option<bool>,600}601602impl CollectionLimits {603 pub fn with_default_limits(collection_type: CollectionMode) -> Self {604 CollectionLimits {605 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),606 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),607 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),608 token_limit: Some(COLLECTION_TOKEN_LIMIT),609 sponsor_transfer_timeout: match collection_type {610 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),611 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),612 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),613 },614 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),615 owner_can_transfer: Some(false),616 owner_can_destroy: Some(true),617 transfers_enabled: Some(true),618 }619 }620621 622 pub fn account_token_ownership_limit(&self) -> u32 {623 self.account_token_ownership_limit624 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)625 .min(MAX_TOKEN_OWNERSHIP)626 }627628 629 pub fn sponsored_data_size(&self) -> u32 {630 self.sponsored_data_size631 .unwrap_or(CUSTOM_DATA_LIMIT)632 .min(CUSTOM_DATA_LIMIT)633 }634635 636 pub fn token_limit(&self) -> u32 {637 self.token_limit638 .unwrap_or(COLLECTION_TOKEN_LIMIT)639 .min(COLLECTION_TOKEN_LIMIT)640 }641642 643 644 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {645 self.sponsor_transfer_timeout646 .unwrap_or(default)647 .min(MAX_SPONSOR_TIMEOUT)648 }649650 651 pub fn sponsor_approve_timeout(&self) -> u32 {652 self.sponsor_approve_timeout653 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)654 .min(MAX_SPONSOR_TIMEOUT)655 }656657 658 pub fn owner_can_transfer(&self) -> bool {659 self.owner_can_transfer.unwrap_or(false)660 }661662 663 pub fn owner_can_transfer_instaled(&self) -> bool {664 self.owner_can_transfer.is_some()665 }666667 668 pub fn owner_can_destroy(&self) -> bool {669 self.owner_can_destroy.unwrap_or(true)670 }671672 673 pub fn transfers_enabled(&self) -> bool {674 self.transfers_enabled.unwrap_or(true)675 }676677 678 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {679 match self680 .sponsored_data_rate_limit681 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)682 {683 SponsoringRateLimit::SponsoringDisabled => None,684 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),685 }686 }687}688689690691692693694#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]695#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]696697698pub struct CollectionPermissions {699 700 701 702 pub access: Option<AccessMode>,703704 705 706 707 pub mint_mode: Option<bool>,708709 710 711 712 713 714 715 pub nesting: Option<NestingPermissions>,716}717718impl CollectionPermissions {719 720 pub fn access(&self) -> AccessMode {721 self.access.unwrap_or(AccessMode::Normal)722 }723724 725 pub fn mint_mode(&self) -> bool {726 self.mint_mode.unwrap_or(false)727 }728729 730 pub fn nesting(&self) -> &NestingPermissions {731 static DEFAULT: NestingPermissions = NestingPermissions {732 token_owner: false,733 collection_admin: false,734 restricted: None,735 #[cfg(feature = "runtime-benchmarks")]736 permissive: false,737 };738 self.nesting.as_ref().unwrap_or(&DEFAULT)739 }740}741742743type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;744745746#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]747#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]748#[derivative(Debug)]749pub struct OwnerRestrictedSet(750 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]751 #[derivative(Debug(format_with = "bounded::set_debug"))]752 pub OwnerRestrictedSetInner,753);754755impl OwnerRestrictedSet {756 757 pub fn new() -> Self {758 Self(Default::default())759 }760}761impl core::ops::Deref for OwnerRestrictedSet {762 type Target = OwnerRestrictedSetInner;763 fn deref(&self) -> &Self::Target {764 &self.0765 }766}767impl core::ops::DerefMut for OwnerRestrictedSet {768 fn deref_mut(&mut self) -> &mut Self::Target {769 &mut self.0770 }771}772773774#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]775#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]776#[derivative(Debug)]777pub struct NestingPermissions {778 779 pub token_owner: bool,780 781 pub collection_admin: bool,782 783 pub restricted: Option<OwnerRestrictedSet>,784785 #[cfg(feature = "runtime-benchmarks")]786 787 pub permissive: bool,788}789790791792793#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]794#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]795pub enum SponsoringRateLimit {796 797 SponsoringDisabled,798 799 Blocks(u32),800}801802803#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]804#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]805#[derivative(Debug)]806pub struct CreateNftData {807 808 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]809 #[derivative(Debug(format_with = "bounded::vec_debug"))]810 811 pub properties: CollectionPropertiesVec,812}813814815#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]816#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]817pub struct CreateFungibleData {818 819 pub value: u128,820}821822823#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]824#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]825#[derivative(Debug)]826pub struct CreateReFungibleData {827 828 pub pieces: u128,829830 831 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]832 #[derivative(Debug(format_with = "bounded::vec_debug"))]833 pub properties: CollectionPropertiesVec,834}835836837#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]838#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]839pub enum MetaUpdatePermission {840 ItemOwner,841 Admin,842 None,843}844845846847#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]848#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]849pub enum CreateItemData {850 851 NFT(CreateNftData),852 853 Fungible(CreateFungibleData),854 855 ReFungible(CreateReFungibleData),856}857858859#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]860#[derivative(Debug)]861pub struct CreateNftExData<CrossAccountId> {862 863 #[derivative(Debug(format_with = "bounded::vec_debug"))]864 pub properties: CollectionPropertiesVec,865866 867 pub owner: CrossAccountId,868}869870871#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]872#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]873pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {874 #[derivative(Debug(format_with = "bounded::map_debug"))]875 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,876 #[derivative(Debug(format_with = "bounded::vec_debug"))]877 pub properties: CollectionPropertiesVec,878}879880881#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]882#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]883pub struct CreateRefungibleExSingleOwner<CrossAccountId> {884 pub user: CrossAccountId,885 pub pieces: u128,886 #[derivative(Debug(format_with = "bounded::vec_debug"))]887 pub properties: CollectionPropertiesVec,888}889890891#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]892#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]893pub enum CreateItemExData<CrossAccountId> {894 895 NFT(896 #[derivative(Debug(format_with = "bounded::vec_debug"))]897 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,898 ),899900 901 Fungible(902 #[derivative(Debug(format_with = "bounded::map_debug"))]903 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,904 ),905906 907 908 RefungibleMultipleItems(909 #[derivative(Debug(format_with = "bounded::vec_debug"))]910 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,911 ),912913 914 915 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),916}917918impl From<CreateNftData> for CreateItemData {919 fn from(item: CreateNftData) -> Self {920 CreateItemData::NFT(item)921 }922}923924impl From<CreateReFungibleData> for CreateItemData {925 fn from(item: CreateReFungibleData) -> Self {926 CreateItemData::ReFungible(item)927 }928}929930impl From<CreateFungibleData> for CreateItemData {931 fn from(item: CreateFungibleData) -> Self {932 CreateItemData::Fungible(item)933 }934}935936937#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]938#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]939940pub struct TokenChild {941 942 pub token: TokenId,943944 945 pub collection: CollectionId,946}947948949#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]950#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]951pub struct CollectionStats {952 953 pub created: u32,954955 956 pub destroyed: u32,957958 959 pub alive: u32,960}961962963#[derive(Encode, Decode, Clone, Debug)]964#[cfg_attr(feature = "std", derive(PartialEq))]965pub struct PhantomType<T>(core::marker::PhantomData<T>);966967impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {968 type Identity = PhantomType<T>;969970 fn type_info() -> scale_info::Type {971 use scale_info::{972 Type, Path,973 build::{FieldsBuilder, UnnamedFields},974 form::MetaForm,975 type_params,976 };977 Type::builder()978 .path(Path::new("up_data_structs", "PhantomType"))979 .type_params(type_params!(T))980 .composite(981 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),982 )983 }984}985impl<T> MaxEncodedLen for PhantomType<T> {986 fn max_encoded_len() -> usize {987 0988 }989}990991992pub type BoundedBytes<S> = BoundedVec<u8, S>;993994995pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;996997998pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;99910001001pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;100210031004#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1005#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1006pub struct PropertyPermission {1007 1008 1009 1010 pub mutable: bool,10111012 1013 pub collection_admin: bool,10141015 1016 pub token_owner: bool,1017}10181019impl PropertyPermission {1020 1021 pub fn none() -> Self {1022 Self {1023 mutable: true,1024 collection_admin: false,1025 token_owner: false,1026 }1027 }1028}102910301031#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1032#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1033pub struct Property {1034 1035 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1036 pub key: PropertyKey,10371038 1039 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1040 pub value: PropertyValue,1041}10421043impl Into<(PropertyKey, PropertyValue)> for Property {1044 fn into(self) -> (PropertyKey, PropertyValue) {1045 (self.key, self.value)1046 }1047}104810491050#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1051#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1052pub struct PropertyKeyPermission {1053 1054 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1055 pub key: PropertyKey,10561057 1058 pub permission: PropertyPermission,1059}10601061impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1062 fn into(self) -> (PropertyKey, PropertyPermission) {1063 (self.key, self.permission)1064 }1065}106610671068#[derive(Debug)]1069pub enum PropertiesError {1070 1071 1072 1073 1074 NoSpaceForProperty,10751076 1077 1078 1079 PropertyLimitReached,10801081 1082 InvalidCharacterInPropertyKey,10831084 1085 1086 1087 PropertyKeyIsTooLong,10881089 1090 EmptyPropertyKey,1091}109210931094#[derive(Debug)]1095pub enum TokenOwnerError {1096 NotFound,1097 MultipleOwners,1098}10991100110111021103#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1104pub enum PropertyScope {1105 None,1106 Rmrk,1107}11081109impl PropertyScope {1110 1111 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1112 let scope_str: &[u8] = match self {1113 Self::None => return Ok(key),1114 Self::Rmrk => b"rmrk",1115 };11161117 [scope_str, b":", key.as_slice()]1118 .concat()1119 .try_into()1120 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1121 }1122}112311241125pub trait TrySetProperty: Sized {1126 type Value;11271128 1129 fn try_scoped_set(1130 &mut self,1131 scope: PropertyScope,1132 key: PropertyKey,1133 value: Self::Value,1134 ) -> Result<Option<Self::Value>, PropertiesError>;11351136 1137 fn try_scoped_set_from_iter<I, KV>(1138 &mut self,1139 scope: PropertyScope,1140 iter: I,1141 ) -> Result<(), PropertiesError>1142 where1143 I: Iterator<Item = KV>,1144 KV: Into<(PropertyKey, Self::Value)>,1145 {1146 for kv in iter {1147 let (key, value) = kv.into();1148 self.try_scoped_set(scope, key, value)?;1149 }11501151 Ok(())1152 }11531154 1155 fn try_set(1156 &mut self,1157 key: PropertyKey,1158 value: Self::Value,1159 ) -> Result<Option<Self::Value>, PropertiesError> {1160 self.try_scoped_set(PropertyScope::None, key, value)1161 }11621163 1164 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1165 where1166 I: Iterator<Item = KV>,1167 KV: Into<(PropertyKey, Self::Value)>,1168 {1169 self.try_scoped_set_from_iter(PropertyScope::None, iter)1170 }1171}117211731174#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1175#[derivative(Default(bound = ""))]1176pub struct PropertiesMap<Value>(1177 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1178);11791180impl<Value> PropertiesMap<Value> {1181 1182 pub fn new() -> Self {1183 Self(BoundedBTreeMap::new())1184 }11851186 1187 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1188 Self::check_property_key(key)?;11891190 Ok(self.0.remove(key))1191 }11921193 1194 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1195 self.0.get(key)1196 }11971198 1199 pub fn contains_key(&self, key: &PropertyKey) -> bool {1200 self.0.contains_key(key)1201 }12021203 1204 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1205 if key.is_empty() {1206 return Err(PropertiesError::EmptyPropertyKey);1207 }12081209 for byte in key.as_slice().iter() {1210 let byte = *byte;12111212 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1213 return Err(PropertiesError::InvalidCharacterInPropertyKey);1214 }1215 }12161217 Ok(())1218 }12191220 pub fn values(&self) -> impl Iterator<Item = &Value> {1221 self.0.values()1222 }1223}12241225impl<Value> IntoIterator for PropertiesMap<Value> {1226 type Item = (PropertyKey, Value);1227 type IntoIter = <1228 BoundedBTreeMap<1229 PropertyKey,1230 Value,1231 ConstU32<MAX_PROPERTIES_PER_ITEM>1232 > as IntoIterator1233 >::IntoIter;12341235 fn into_iter(self) -> Self::IntoIter {1236 self.0.into_iter()1237 }1238}12391240impl<Value> TrySetProperty for PropertiesMap<Value> {1241 type Value = Value;12421243 fn try_scoped_set(1244 &mut self,1245 scope: PropertyScope,1246 key: PropertyKey,1247 value: Self::Value,1248 ) -> Result<Option<Self::Value>, PropertiesError> {1249 Self::check_property_key(&key)?;12501251 let key = scope.apply(key)?;1252 self.01253 .try_insert(key, value)1254 .map_err(|_| PropertiesError::PropertyLimitReached)1255 }1256}125712581259pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;126012611262#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1263pub struct Properties {1264 map: PropertiesMap<PropertyValue>,1265 consumed_space: u32,1266 space_limit: u32,1267}12681269impl Properties {1270 1271 pub fn new(space_limit: u32) -> Self {1272 Self {1273 map: PropertiesMap::new(),1274 consumed_space: 0,1275 space_limit,1276 }1277 }12781279 1280 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1281 let value = self.map.remove(key)?;12821283 if let Some(ref value) = value {1284 let value_len = value.len() as u32;1285 self.consumed_space -= value_len;1286 }12871288 Ok(value)1289 }12901291 1292 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1293 self.map.get(key)1294 }12951296 1297 1298 pub fn recompute_consumed_space(&mut self) {1299 self.consumed_space = self.map.values().map(|value| value.len() as u32).sum();1300 }1301}13021303impl IntoIterator for Properties {1304 type Item = (PropertyKey, PropertyValue);1305 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;13061307 fn into_iter(self) -> Self::IntoIter {1308 self.map.into_iter()1309 }1310}13111312impl TrySetProperty for Properties {1313 type Value = PropertyValue;13141315 fn try_scoped_set(1316 &mut self,1317 scope: PropertyScope,1318 key: PropertyKey,1319 value: Self::Value,1320 ) -> Result<Option<Self::Value>, PropertiesError> {1321 let value_len = value.len();13221323 if self.consumed_space as usize + value_len > self.space_limit as usize1324 && !cfg!(feature = "runtime-benchmarks")1325 {1326 return Err(PropertiesError::NoSpaceForProperty);1327 }13281329 let value_len = value_len as u32;1330 let old_value = self.map.try_scoped_set(scope, key, value)?;13311332 let old_value_len = old_value.as_ref().map(|v| v.len() as u32).unwrap_or(0);13331334 if value_len > old_value_len {1335 self.consumed_space += value_len - old_value_len;1336 } else {1337 self.consumed_space -= old_value_len - value_len;1338 }13391340 Ok(old_value)1341 }1342}134313441345pub struct CollectionProperties;13461347impl Get<Properties> for CollectionProperties {1348 fn get() -> Properties {1349 Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)1350 }1351}135213531354pub struct TokenProperties;13551356impl Get<Properties> for TokenProperties {1357 fn get() -> Properties {1358 Properties::new(MAX_TOKEN_PROPERTIES_SIZE)1359 }1360}