123456789101112131415161718192021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26 ops::Deref,27};28use frame_support::storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet};2930#[cfg(feature = "serde")]31use serde::{Serialize, Deserialize};3233use sp_core::U256;34use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};35use sp_std::collections::btree_set::BTreeSet;36use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};37use frame_support::{BoundedVec, traits::ConstU32};38use derivative::Derivative;39use scale_info::TypeInfo;40use evm_coder::AbiCoderFlags;41use bondrewd::Bitfields;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 Serialize,157 Deserialize,158)]159pub struct CollectionId(pub u32);160impl EncodeLike<u32> for CollectionId {}161impl EncodeLike<CollectionId> for u32 {}162163impl From<u32> for CollectionId {164 fn from(value: u32) -> Self {165 Self(value)166 }167}168169impl Deref for CollectionId {170 type Target = u32;171172 fn deref(&self) -> &Self::Target {173 &self.0174 }175}176177178#[derive(179 Encode,180 Decode,181 PartialEq,182 Eq,183 PartialOrd,184 Ord,185 Clone,186 Copy,187 Debug,188 Default,189 TypeInfo,190 MaxEncodedLen,191 Serialize,192 Deserialize,193)]194pub struct TokenId(pub u32);195impl EncodeLike<u32> for TokenId {}196impl EncodeLike<TokenId> for u32 {}197198impl TokenId {199 200 201 202 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {203 self.0204 .checked_add(1)205 .ok_or(ArithmeticError::Overflow)206 .map(Self)207 }208}209210impl From<TokenId> for U256 {211 fn from(t: TokenId) -> Self {212 t.0.into()213 }214}215216impl TryFrom<U256> for TokenId {217 type Error = &'static str;218219 fn try_from(value: U256) -> Result<Self, Self::Error> {220 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))221 }222}223224225#[struct_versioning::versioned(version = 2, upper)]226#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]227pub struct TokenData<CrossAccountId> {228 229 pub properties: Vec<Property>,230231 232 pub owner: Option<CrossAccountId>,233234 235 #[version(2.., upper(0))]236 pub pieces: u128,237}238239240pub struct OverflowError;241impl From<OverflowError> for &'static str {242 fn from(_: OverflowError) -> Self {243 "overflow occured"244 }245}246247248pub type DecimalPoints = u8;249250251252253254255#[derive(256 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,257)]258pub enum CollectionMode {259 260 NFT,261 262 Fungible(DecimalPoints),263 264 ReFungible,265}266267impl CollectionMode {268 269 pub fn id(&self) -> u8 {270 match self {271 CollectionMode::NFT => 1,272 CollectionMode::Fungible(_) => 2,273 CollectionMode::ReFungible => 3,274 }275 }276}277278279pub trait SponsoringResolve<AccountId, Call> {280 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;281}282283284#[derive(285 Encode,286 Decode,287 Eq,288 Debug,289 Clone,290 Copy,291 PartialEq,292 TypeInfo,293 MaxEncodedLen,294 Serialize,295 Deserialize,296)]297pub enum AccessMode {298 299 Normal,300 301 AllowList,302}303impl Default for AccessMode {304 fn default() -> Self {305 Self::Normal306 }307}308309310#[derive(311 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,312)]313pub enum SchemaVersion {314 ImageURL,315 Unique,316}317impl Default for SchemaVersion {318 fn default() -> Self {319 Self::ImageURL320 }321}322323324#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]325pub struct Ownership<AccountId> {326 pub owner: AccountId,327 pub fraction: u128,328}329330331#[derive(332 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,333)]334pub enum SponsorshipState<AccountId> {335 336 Disabled,337 338 339 Unconfirmed(AccountId),340 341 Confirmed(AccountId),342}343344impl<AccountId> SponsorshipState<AccountId> {345 346 pub fn sponsor(&self) -> Option<&AccountId> {347 match self {348 Self::Confirmed(sponsor) => Some(sponsor),349 _ => None,350 }351 }352353 354 pub fn pending_sponsor(&self) -> Option<&AccountId> {355 match self {356 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),357 _ => None,358 }359 }360361 362 pub fn confirmed(&self) -> bool {363 matches!(self, Self::Confirmed(_))364 }365}366367impl<T> Default for SponsorshipState<T> {368 fn default() -> Self {369 Self::Disabled370 }371}372373pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;374pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;375pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;376377#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]378#[bondrewd(enforce_bytes = 1)]379pub struct CollectionFlags {380 381 #[bondrewd(bits = "0..1")]382 pub foreign: bool,383 384 #[bondrewd(bits = "1..2")]385 pub erc721metadata: bool,386 387 #[bondrewd(bits = "7..8")]388 pub external: bool,389 390 #[bondrewd(bits = "2..7")]391 pub reserved: u8,392}393bondrewd_codec!(CollectionFlags);394395impl CollectionFlags {396 pub fn is_allowed_for_user(self) -> bool {397 !self.foreign && !self.external && self.reserved == 0398 }399}400401402403404405406407#[struct_versioning::versioned(version = 2, upper)]408#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]409pub struct Collection<AccountId> {410 411 pub owner: AccountId,412413 414 pub mode: CollectionMode,415416 417 #[version(..2)]418 pub access: AccessMode,419420 421 pub name: CollectionName,422423 424 pub description: CollectionDescription,425426 427 pub token_prefix: CollectionTokenPrefix,428429 #[version(..2)]430 pub mint_mode: bool,431432 #[version(..2)]433 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,434435 #[version(..2)]436 pub schema_version: SchemaVersion,437438 439 pub sponsorship: SponsorshipState<AccountId>,440441 442 pub limits: CollectionLimits,443444 445 #[version(2.., upper(Default::default()))]446 pub permissions: CollectionPermissions,447448 #[version(2.., upper(Default::default()))]449 pub flags: CollectionFlags,450451 #[version(..2)]452 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,453454 #[version(..2)]455 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,456457 #[version(..2)]458 pub meta_update_permission: MetaUpdatePermission,459}460461#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]462pub struct RpcCollectionFlags {463 464 pub foreign: bool,465 466 pub erc721metadata: bool,467}468469470#[struct_versioning::versioned(version = 2, upper)]471#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]472pub struct RpcCollection<AccountId> {473 474 pub owner: AccountId,475476 477 pub mode: CollectionMode,478479 480 pub name: Vec<u16>,481482 483 pub description: Vec<u16>,484485 486 pub token_prefix: Vec<u8>,487488 489 pub sponsorship: SponsorshipState<AccountId>,490491 492 pub limits: CollectionLimits,493494 495 pub permissions: CollectionPermissions,496497 498 pub token_property_permissions: Vec<PropertyKeyPermission>,499500 501 pub properties: Vec<Property>,502503 504 pub read_only: bool,505506 507 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]508 pub flags: RpcCollectionFlags,509}510511impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {512 fn from(value: CollectionVersion1<AccountId>) -> Self {513 let CollectionVersion1 {514 name,515 description,516 owner,517 mode,518 access,519 token_prefix,520 mint_mode,521 sponsorship,522 limits,523 ..524 } = value;525526 RpcCollection {527 name: name.into_inner(),528 description: description.into_inner(),529 owner,530 mode,531 token_prefix: token_prefix.into_inner(),532 sponsorship,533 limits,534 permissions: CollectionPermissions {535 access: Some(access),536 mint_mode: Some(mint_mode),537 nesting: None,538 },539 token_property_permissions: Vec::default(),540 properties: Vec::default(),541 read_only: true,542543 flags: RpcCollectionFlags {544 foreign: false,545 erc721metadata: false,546 },547 }548 }549}550551pub struct RawEncoded(Vec<u8>);552553impl parity_scale_codec::Decode for RawEncoded {554 fn decode<I: parity_scale_codec::Input>(555 input: &mut I,556 ) -> Result<Self, parity_scale_codec::Error> {557 let mut out = Vec::new();558 while let Ok(v) = input.read_byte() {559 out.push(v);560 }561 Ok(Self(out))562 }563}564565impl Deref for RawEncoded {566 type Target = Vec<u8>;567568 fn deref(&self) -> &Self::Target {569 &self.0570 }571}572573574575576#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]577#[derivative(Debug, Default(bound = ""))]578pub struct CreateCollectionData<CrossAccountId> {579 580 #[derivative(Default(value = "CollectionMode::NFT"))]581 pub mode: CollectionMode,582583 584 pub access: Option<AccessMode>,585586 587 pub name: CollectionName,588589 590 pub description: CollectionDescription,591592 593 pub token_prefix: CollectionTokenPrefix,594595 596 pub limits: Option<CollectionLimits>,597598 599 pub permissions: Option<CollectionPermissions>,600601 602 pub token_property_permissions: CollectionPropertiesPermissionsVec,603604 605 pub properties: CollectionPropertiesVec,606607 pub admin_list: Vec<CrossAccountId>,608609 610 pub pending_sponsor: Option<CrossAccountId>,611612 pub flags: CollectionFlags,613}614615616617pub type CollectionPropertiesPermissionsVec =618 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;619620621pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;622623624625626627628629#[derive(630 Encode,631 Decode,632 Debug,633 Default,634 Clone,635 PartialEq,636 TypeInfo,637 MaxEncodedLen,638 Serialize,639 Deserialize,640)]641642643644pub struct CollectionLimits {645 646 647 648 pub account_token_ownership_limit: Option<u32>,649650 651 652 653 pub sponsored_data_size: Option<u32>,654655 656 657 658 659 660 661 662 663 664 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,665 666667 668 669 670 671 pub token_limit: Option<u32>,672673 674 675 676 677 678 679 680 pub sponsor_transfer_timeout: Option<u32>,681682 683 684 685 686 pub sponsor_approve_timeout: Option<u32>,687688 689 690 691 pub owner_can_transfer: Option<bool>,692693 694 695 696 pub owner_can_destroy: Option<bool>,697698 699 700 701 pub transfers_enabled: Option<bool>,702}703704impl CollectionLimits {705 pub fn with_default_limits(collection_type: CollectionMode) -> Self {706 CollectionLimits {707 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),708 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),709 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),710 token_limit: Some(COLLECTION_TOKEN_LIMIT),711 sponsor_transfer_timeout: match collection_type {712 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),713 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),714 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),715 },716 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),717 owner_can_transfer: Some(false),718 owner_can_destroy: Some(true),719 transfers_enabled: Some(true),720 }721 }722723 724 pub fn account_token_ownership_limit(&self) -> u32 {725 self.account_token_ownership_limit726 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)727 .min(MAX_TOKEN_OWNERSHIP)728 }729730 731 pub fn sponsored_data_size(&self) -> u32 {732 self.sponsored_data_size733 .unwrap_or(CUSTOM_DATA_LIMIT)734 .min(CUSTOM_DATA_LIMIT)735 }736737 738 pub fn token_limit(&self) -> u32 {739 self.token_limit740 .unwrap_or(COLLECTION_TOKEN_LIMIT)741 .min(COLLECTION_TOKEN_LIMIT)742 }743744 745 746 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {747 self.sponsor_transfer_timeout748 .unwrap_or(default)749 .min(MAX_SPONSOR_TIMEOUT)750 }751752 753 pub fn sponsor_approve_timeout(&self) -> u32 {754 self.sponsor_approve_timeout755 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)756 .min(MAX_SPONSOR_TIMEOUT)757 }758759 760 pub fn owner_can_transfer(&self) -> bool {761 self.owner_can_transfer.unwrap_or(false)762 }763764 765 pub fn owner_can_transfer_instaled(&self) -> bool {766 self.owner_can_transfer.is_some()767 }768769 770 pub fn owner_can_destroy(&self) -> bool {771 self.owner_can_destroy.unwrap_or(true)772 }773774 775 pub fn transfers_enabled(&self) -> bool {776 self.transfers_enabled.unwrap_or(true)777 }778779 780 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {781 match self782 .sponsored_data_rate_limit783 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)784 {785 SponsoringRateLimit::SponsoringDisabled => None,786 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),787 }788 }789}790791792793794795796#[derive(797 Encode,798 Decode,799 Debug,800 Default,801 Clone,802 PartialEq,803 TypeInfo,804 MaxEncodedLen,805 Serialize,806 Deserialize,807)]808809810pub struct CollectionPermissions {811 812 813 814 pub access: Option<AccessMode>,815816 817 818 819 pub mint_mode: Option<bool>,820821 822 823 824 825 826 827 pub nesting: Option<NestingPermissions>,828}829830impl CollectionPermissions {831 832 pub fn access(&self) -> AccessMode {833 self.access.unwrap_or(AccessMode::Normal)834 }835836 837 pub fn mint_mode(&self) -> bool {838 self.mint_mode.unwrap_or(false)839 }840841 842 pub fn nesting(&self) -> &NestingPermissions {843 static DEFAULT: NestingPermissions = NestingPermissions {844 token_owner: false,845 collection_admin: false,846 restricted: None,847 #[cfg(feature = "runtime-benchmarks")]848 permissive: false,849 };850 self.nesting.as_ref().unwrap_or(&DEFAULT)851 }852}853854855type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;856857858#[derive(859 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,860)]861#[derivative(Debug)]862pub struct OwnerRestrictedSet(863 #[serde(with = "bounded::set_serde")]864 #[derivative(Debug(format_with = "bounded::set_debug"))]865 pub OwnerRestrictedSetInner,866);867868impl OwnerRestrictedSet {869 870 pub fn new() -> Self {871 Self(Default::default())872 }873}874impl Default for OwnerRestrictedSet {875 fn default() -> Self {876 Self::new()877 }878}879impl core::ops::Deref for OwnerRestrictedSet {880 type Target = OwnerRestrictedSetInner;881 fn deref(&self) -> &Self::Target {882 &self.0883 }884}885impl core::ops::DerefMut for OwnerRestrictedSet {886 fn deref_mut(&mut self) -> &mut Self::Target {887 &mut self.0888 }889}890891impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {892 type Error = ();893894 fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {895 Ok(Self(value.try_into()?))896 }897}898899900#[derive(901 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,902)]903#[derivative(Debug)]904pub struct NestingPermissions {905 906 pub token_owner: bool,907 908 pub collection_admin: bool,909 910 pub restricted: Option<OwnerRestrictedSet>,911912 #[cfg(feature = "runtime-benchmarks")]913 914 pub permissive: bool,915}916917918919920#[derive(921 Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,922)]923pub enum SponsoringRateLimit {924 925 SponsoringDisabled,926 927 Blocks(u32),928}929930931#[derive(932 Encode,933 Decode,934 MaxEncodedLen,935 Default,936 PartialEq,937 Clone,938 Derivative,939 TypeInfo,940 Serialize,941 Deserialize,942)]943#[derivative(Debug)]944pub struct CreateNftData {945 946 #[serde(with = "bounded::vec_serde")]947 #[derivative(Debug(format_with = "bounded::vec_debug"))]948 949 pub properties: CollectionPropertiesVec,950}951952953#[derive(954 Encode,955 Decode,956 MaxEncodedLen,957 Default,958 Debug,959 Clone,960 PartialEq,961 TypeInfo,962 Serialize,963 Deserialize,964)]965pub struct CreateFungibleData {966 967 pub value: u128,968}969970971#[derive(972 Encode,973 Decode,974 MaxEncodedLen,975 Default,976 PartialEq,977 Clone,978 Derivative,979 TypeInfo,980 Serialize,981 Deserialize,982)]983#[derivative(Debug)]984pub struct CreateReFungibleData {985 986 pub pieces: u128,987988 989 #[serde(with = "bounded::vec_serde")]990 #[derivative(Debug(format_with = "bounded::vec_debug"))]991 pub properties: CollectionPropertiesVec,992}993994995#[derive(996 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,997)]998pub enum MetaUpdatePermission {999 ItemOwner,1000 Admin,1001 None,1002}1003100410051006#[derive(1007 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1008)]1009pub enum CreateItemData {1010 1011 NFT(CreateNftData),1012 1013 Fungible(CreateFungibleData),1014 1015 ReFungible(CreateReFungibleData),1016}101710181019#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1020#[derivative(Debug)]1021pub struct CreateNftExData<CrossAccountId> {1022 1023 #[derivative(Debug(format_with = "bounded::vec_debug"))]1024 pub properties: CollectionPropertiesVec,10251026 1027 pub owner: CrossAccountId,1028}102910301031#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1032#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1033pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1034 #[derivative(Debug(format_with = "bounded::map_debug"))]1035 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1036 #[derivative(Debug(format_with = "bounded::vec_debug"))]1037 pub properties: CollectionPropertiesVec,1038}103910401041#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1042#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1043pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1044 pub user: CrossAccountId,1045 pub pieces: u128,1046 #[derivative(Debug(format_with = "bounded::vec_debug"))]1047 pub properties: CollectionPropertiesVec,1048}104910501051#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1052#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1053pub enum CreateItemExData<CrossAccountId> {1054 1055 NFT(1056 #[derivative(Debug(format_with = "bounded::vec_debug"))]1057 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1058 ),10591060 1061 Fungible(1062 #[derivative(Debug(format_with = "bounded::map_debug"))]1063 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1064 ),10651066 1067 1068 RefungibleMultipleItems(1069 #[derivative(Debug(format_with = "bounded::vec_debug"))]1070 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1071 ),10721073 1074 1075 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1076}10771078impl From<CreateNftData> for CreateItemData {1079 fn from(item: CreateNftData) -> Self {1080 CreateItemData::NFT(item)1081 }1082}10831084impl From<CreateReFungibleData> for CreateItemData {1085 fn from(item: CreateReFungibleData) -> Self {1086 CreateItemData::ReFungible(item)1087 }1088}10891090impl From<CreateFungibleData> for CreateItemData {1091 fn from(item: CreateFungibleData) -> Self {1092 CreateItemData::Fungible(item)1093 }1094}109510961097#[derive(1098 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1099)]11001101pub struct TokenChild {1102 1103 pub token: TokenId,11041105 1106 pub collection: CollectionId,1107}110811091110#[derive(1111 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1112)]1113pub struct CollectionStats {1114 1115 pub created: u32,11161117 1118 pub destroyed: u32,11191120 1121 pub alive: u32,1122}112311241125#[derive(Encode, Decode, Clone, Debug)]1126#[cfg_attr(feature = "std", derive(PartialEq))]1127pub struct PhantomType<T>(core::marker::PhantomData<T>);11281129impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1130 type Identity = PhantomType<T>;11311132 fn type_info() -> scale_info::Type {1133 use scale_info::{1134 build::{FieldsBuilder, UnnamedFields},1135 form::MetaForm,1136 type_params, Path, Type,1137 };1138 Type::builder()1139 .path(Path::new("up_data_structs", "PhantomType"))1140 .type_params(type_params!(T))1141 .composite(1142 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1143 )1144 }1145}1146impl<T> MaxEncodedLen for PhantomType<T> {1147 fn max_encoded_len() -> usize {1148 01149 }1150}115111521153pub type BoundedBytes<S> = BoundedVec<u8, S>;115411551156pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;115711581159pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;116011611162pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;116311641165#[derive(1166 Encode,1167 Decode,1168 TypeInfo,1169 Debug,1170 MaxEncodedLen,1171 PartialEq,1172 Clone,1173 Default,1174 Serialize,1175 Deserialize,1176)]1177pub struct PropertyPermission {1178 1179 1180 1181 pub mutable: bool,11821183 1184 pub collection_admin: bool,11851186 1187 pub token_owner: bool,1188}11891190impl PropertyPermission {1191 1192 pub fn none() -> Self {1193 Self {1194 mutable: true,1195 collection_admin: false,1196 token_owner: false,1197 }1198 }1199}120012011202#[derive(1203 Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1204)]1205pub struct Property {1206 1207 #[serde(with = "bounded::vec_serde")]1208 pub key: PropertyKey,12091210 1211 #[serde(with = "bounded::vec_serde")]1212 pub value: PropertyValue,1213}12141215impl From<Property> for (PropertyKey, PropertyValue) {1216 fn from(value: Property) -> Self {1217 (value.key, value.value)1218 }1219}122012211222#[derive(1223 Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1224)]1225pub struct PropertyKeyPermission {1226 1227 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1228 pub key: PropertyKey,12291230 1231 pub permission: PropertyPermission,1232}12331234impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1235 fn from(value: PropertyKeyPermission) -> Self {1236 (value.key, value.permission)1237 }1238}123912401241#[derive(Debug)]1242pub enum PropertiesError {1243 1244 1245 1246 1247 NoSpaceForProperty,12481249 1250 1251 1252 PropertyLimitReached,12531254 1255 InvalidCharacterInPropertyKey,12561257 1258 1259 1260 PropertyKeyIsTooLong,12611262 1263 EmptyPropertyKey,1264}126512661267#[derive(Debug)]1268pub enum TokenOwnerError {1269 NotFound,1270 MultipleOwners,1271}12721273127412751276#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1277pub enum PropertyScope {1278 None,1279 Rmrk,1280}12811282impl PropertyScope {1283 pub fn prefix(&self) -> &'static [u8] {1284 match self {1285 Self::None => b"",1286 Self::Rmrk => b"rmrk:",1287 }1288 }1289 1290 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1291 let prefix = self.prefix();1292 if prefix == b"" {1293 return Ok(key);1294 }1295 [prefix, key.as_slice()]1296 .concat()1297 .try_into()1298 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1299 }1300}130113021303pub trait TrySetProperty: Sized {1304 type Value;13051306 1307 fn try_scoped_set(1308 &mut self,1309 scope: PropertyScope,1310 key: PropertyKey,1311 value: Self::Value,1312 ) -> Result<Option<Self::Value>, PropertiesError>;13131314 1315 fn try_scoped_set_from_iter<I, KV>(1316 &mut self,1317 scope: PropertyScope,1318 iter: I,1319 ) -> Result<(), PropertiesError>1320 where1321 I: Iterator<Item = KV>,1322 KV: Into<(PropertyKey, Self::Value)>,1323 {1324 for kv in iter {1325 let (key, value) = kv.into();1326 self.try_scoped_set(scope, key, value)?;1327 }13281329 Ok(())1330 }13311332 1333 fn try_set(1334 &mut self,1335 key: PropertyKey,1336 value: Self::Value,1337 ) -> Result<Option<Self::Value>, PropertiesError> {1338 self.try_scoped_set(PropertyScope::None, key, value)1339 }13401341 1342 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1343 where1344 I: Iterator<Item = KV>,1345 KV: Into<(PropertyKey, Self::Value)>,1346 {1347 self.try_scoped_set_from_iter(PropertyScope::None, iter)1348 }1349}135013511352#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1353#[derivative(Default(bound = ""))]1354pub struct PropertiesMap<Value>(1355 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1356);13571358impl<Value> PropertiesMap<Value> {1359 1360 pub fn new() -> Self {1361 Self(BoundedBTreeMap::new())1362 }13631364 1365 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1366 Self::check_property_key(key)?;13671368 Ok(self.0.remove(key))1369 }13701371 1372 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1373 self.0.get(key)1374 }13751376 1377 pub fn contains_key(&self, key: &PropertyKey) -> bool {1378 self.0.contains_key(key)1379 }13801381 1382 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1383 if key.is_empty() {1384 return Err(PropertiesError::EmptyPropertyKey);1385 }13861387 for byte in key.as_slice().iter() {1388 let byte = *byte;13891390 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1391 return Err(PropertiesError::InvalidCharacterInPropertyKey);1392 }1393 }13941395 Ok(())1396 }13971398 pub fn values(&self) -> impl Iterator<Item = &Value> {1399 self.0.values()1400 }14011402 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1403 self.0.iter()1404 }1405}14061407impl<Value> IntoIterator for PropertiesMap<Value> {1408 type Item = (PropertyKey, Value);1409 type IntoIter = <1410 BoundedBTreeMap<1411 PropertyKey,1412 Value,1413 ConstU32<MAX_PROPERTIES_PER_ITEM>1414 > as IntoIterator1415 >::IntoIter;14161417 fn into_iter(self) -> Self::IntoIter {1418 self.0.into_iter()1419 }1420}14211422impl<Value> TrySetProperty for PropertiesMap<Value> {1423 type Value = Value;14241425 fn try_scoped_set(1426 &mut self,1427 scope: PropertyScope,1428 key: PropertyKey,1429 value: Self::Value,1430 ) -> Result<Option<Self::Value>, PropertiesError> {1431 Self::check_property_key(&key)?;14321433 let key = scope.apply(key)?;1434 self.01435 .try_insert(key, value)1436 .map_err(|_| PropertiesError::PropertyLimitReached)1437 }1438}143914401441pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14421443fn slice_size(data: &[u8]) -> u32 {1444 scoped_slice_size(PropertyScope::None, data)1445}1446fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1447 use parity_scale_codec::Compact;1448 let prefix = scope.prefix();1449 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321450 + data.len() as u321451 + prefix.len() as u321452}145314541455#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1456pub struct Properties<const S: u32> {1457 map: PropertiesMap<PropertyValue>,1458 consumed_space: u32,1459 1460 _reserved: u32,1461}14621463impl<const S: u32> MaxEncodedLen for Properties<S> {1464 fn max_encoded_len() -> usize {1465 1466 u32::max_encoded_len() * 3 + S as usize1467 }1468}14691470impl<const S: u32> Default for Properties<S> {1471 fn default() -> Self {1472 Self::new()1473 }1474}14751476impl<const S: u32> Properties<S> {1477 1478 pub fn new() -> Self {1479 Self {1480 map: PropertiesMap::new(),1481 consumed_space: 0,1482 _reserved: 0,1483 }1484 }14851486 1487 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1488 let value = self.map.remove(key)?;14891490 if let Some(ref value) = value {1491 let kv_len = slice_size(key) + slice_size(value);1492 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1493 }14941495 Ok(value)1496 }14971498 1499 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1500 self.map.get(key)1501 }15021503 1504 1505 pub fn recompute_consumed_space(&mut self) {1506 self.consumed_space = self1507 .map1508 .iter()1509 .map(|(key, value)| slice_size(key) + slice_size(value))1510 .sum();1511 }1512}15131514impl<const S: u32> IntoIterator for Properties<S> {1515 type Item = (PropertyKey, PropertyValue);1516 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15171518 fn into_iter(self) -> Self::IntoIter {1519 self.map.into_iter()1520 }1521}15221523impl<const S: u32> TrySetProperty for Properties<S> {1524 type Value = PropertyValue;15251526 fn try_scoped_set(1527 &mut self,1528 scope: PropertyScope,1529 key: PropertyKey,1530 value: Self::Value,1531 ) -> Result<Option<Self::Value>, PropertiesError> {1532 let key_size = scoped_slice_size(scope, &key);1533 let value_size = slice_size(&value);15341535 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1536 {1537 return Err(PropertiesError::NoSpaceForProperty);1538 }15391540 let old_value = self.map.try_scoped_set(scope, key, value)?;15411542 if let Some(old_value) = old_value.as_ref() {1543 let old_value_size = slice_size(old_value);1544 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1545 } else {1546 self.consumed_space += key_size + value_size;1547 }15481549 Ok(old_value)1550 }1551}15521553pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1554pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;