123456789101112131415161718192021#![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;495051pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;525354pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;55pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;565758pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {59 100_000_00060} else {61 1062};636465pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {66 100_00067} else {68 1069};707172pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73 204874} else {75 1076};777879pub const COLLECTION_ADMINS_LIMIT: u32 = 5;808182pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;838485pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {86 100_000_00087} else {88 1089};909192pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9394pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9596pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;979899pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;100101102pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;104pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;105106107pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;108109110pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;111112113pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;114115116pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;117118119pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;120121122pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;123124125pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;126127128pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;129130131pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;132133134pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;135136137138pub const MAX_ITEMS_PER_BATCH: u32 = 120;139140141pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;142143144#[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}177178179#[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 201 202 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}224225226#[struct_versioning::versioned(version = 2, upper)]227#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]228pub struct TokenData<CrossAccountId> {229 230 pub properties: Vec<Property>,231232 233 pub owner: Option<CrossAccountId>,234235 236 #[version(2.., upper(0))]237 pub pieces: u128,238}239240241pub struct OverflowError;242impl From<OverflowError> for &'static str {243 fn from(_: OverflowError) -> Self {244 "overflow occured"245 }246}247248249pub type DecimalPoints = u8;250251252253254255256#[derive(257 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,258)]259pub enum CollectionMode {260 261 NFT,262 263 Fungible(DecimalPoints),264 265 ReFungible,266}267268impl CollectionMode {269 270 pub fn id(&self) -> u8 {271 match self {272 CollectionMode::NFT => 1,273 CollectionMode::Fungible(_) => 2,274 CollectionMode::ReFungible => 3,275 }276 }277}278279280pub trait SponsoringResolve<AccountId, Call> {281 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;282}283284285#[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 300 Normal,301 302 AllowList,303}304impl Default for AccessMode {305 fn default() -> Self {306 Self::Normal307 }308}309310311#[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}323324325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]326pub struct Ownership<AccountId> {327 pub owner: AccountId,328 pub fraction: u128,329}330331332#[derive(333 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,334)]335pub enum SponsorshipState<AccountId> {336 337 Disabled,338 339 340 Unconfirmed(AccountId),341 342 Confirmed(AccountId),343}344345impl<AccountId> SponsorshipState<AccountId> {346 347 pub fn sponsor(&self) -> Option<&AccountId> {348 match self {349 Self::Confirmed(sponsor) => Some(sponsor),350 _ => None,351 }352 }353354 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 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 382 #[bondrewd(bits = "0..1")]383 pub reserved_0: bool,384 385 #[bondrewd(bits = "1..2")]386 pub erc721metadata: bool,387 388 #[bondrewd(bits = "7..8")]389 pub external: bool,390 391 #[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.reserved_0 && !self.external && self.reserved == 0399 }400}401402403404405406407408#[struct_versioning::versioned(version = 2, upper)]409#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]410pub struct Collection<AccountId> {411 412 pub owner: AccountId,413414 415 pub mode: CollectionMode,416417 418 #[version(..2)]419 pub access: AccessMode,420421 422 pub name: CollectionName,423424 425 pub description: CollectionDescription,426427 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 440 pub sponsorship: SponsorshipState<AccountId>,441442 443 pub limits: CollectionLimits,444445 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 465 pub erc721metadata: bool,466}467468469#[struct_versioning::versioned(version = 2, upper)]470#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]471pub struct RpcCollection<AccountId> {472 473 pub owner: AccountId,474475 476 pub mode: CollectionMode,477478 479 pub name: Vec<u16>,480481 482 pub description: Vec<u16>,483484 485 pub token_prefix: Vec<u8>,486487 488 pub sponsorship: SponsorshipState<AccountId>,489490 491 pub limits: CollectionLimits,492493 494 pub permissions: CollectionPermissions,495496 497 pub token_property_permissions: Vec<PropertyKeyPermission>,498499 500 pub properties: Vec<Property>,501502 503 pub read_only: bool,504505 506 #[version(2.., upper(RpcCollectionFlags {erc721metadata: false}))]507 pub flags: RpcCollectionFlags,508}509510impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {511 fn from(value: CollectionVersion1<AccountId>) -> Self {512 let CollectionVersion1 {513 name,514 description,515 owner,516 mode,517 access,518 token_prefix,519 mint_mode,520 sponsorship,521 limits,522 ..523 } = value;524525 RpcCollection {526 name: name.into_inner(),527 description: description.into_inner(),528 owner,529 mode,530 token_prefix: token_prefix.into_inner(),531 sponsorship,532 limits,533 permissions: CollectionPermissions {534 access: Some(access),535 mint_mode: Some(mint_mode),536 nesting: None,537 },538 token_property_permissions: Vec::default(),539 properties: Vec::default(),540 read_only: true,541542 flags: RpcCollectionFlags {543 erc721metadata: false,544 },545 }546 }547}548549pub struct RawEncoded(Vec<u8>);550551impl parity_scale_codec::Decode for RawEncoded {552 fn decode<I: parity_scale_codec::Input>(553 input: &mut I,554 ) -> Result<Self, parity_scale_codec::Error> {555 let mut out = Vec::new();556 while let Ok(v) = input.read_byte() {557 out.push(v);558 }559 Ok(Self(out))560 }561}562563impl Deref for RawEncoded {564 type Target = Vec<u8>;565566 fn deref(&self) -> &Self::Target {567 &self.0568 }569}570571572573574#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]575#[derivative(Debug, Default(bound = ""))]576pub struct CreateCollectionData<CrossAccountId> {577 578 #[derivative(Default(value = "CollectionMode::NFT"))]579 pub mode: CollectionMode,580581 582 pub access: Option<AccessMode>,583584 585 pub name: CollectionName,586587 588 pub description: CollectionDescription,589590 591 pub token_prefix: CollectionTokenPrefix,592593 594 pub limits: Option<CollectionLimits>,595596 597 pub permissions: Option<CollectionPermissions>,598599 600 pub token_property_permissions: CollectionPropertiesPermissionsVec,601602 603 pub properties: CollectionPropertiesVec,604605 pub admin_list: Vec<CrossAccountId>,606607 608 pub pending_sponsor: Option<CrossAccountId>,609610 pub flags: CollectionFlags,611}612613614615pub type CollectionPropertiesPermissionsVec =616 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;617618619pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;620621622623624625626627#[derive(628 Encode,629 Decode,630 Debug,631 Default,632 Clone,633 PartialEq,634 TypeInfo,635 MaxEncodedLen,636 Serialize,637 Deserialize,638)]639640641642pub struct CollectionLimits {643 644 645 646 pub account_token_ownership_limit: Option<u32>,647648 649 650 651 pub sponsored_data_size: Option<u32>,652653 654 655 656 657 658 659 660 661 662 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,663 664665 666 667 668 669 pub token_limit: Option<u32>,670671 672 673 674 675 676 677 678 pub sponsor_transfer_timeout: Option<u32>,679680 681 682 683 684 pub sponsor_approve_timeout: Option<u32>,685686 687 688 689 pub owner_can_transfer: Option<bool>,690691 692 693 694 pub owner_can_destroy: Option<bool>,695696 697 698 699 pub transfers_enabled: Option<bool>,700}701702impl CollectionLimits {703 pub fn with_default_limits(collection_type: CollectionMode) -> Self {704 CollectionLimits {705 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),706 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),707 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),708 token_limit: Some(COLLECTION_TOKEN_LIMIT),709 sponsor_transfer_timeout: match collection_type {710 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),711 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),712 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),713 },714 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),715 owner_can_transfer: Some(false),716 owner_can_destroy: Some(true),717 transfers_enabled: Some(true),718 }719 }720721 722 pub fn account_token_ownership_limit(&self) -> u32 {723 self.account_token_ownership_limit724 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)725 .min(MAX_TOKEN_OWNERSHIP)726 }727728 729 pub fn sponsored_data_size(&self) -> u32 {730 self.sponsored_data_size731 .unwrap_or(CUSTOM_DATA_LIMIT)732 .min(CUSTOM_DATA_LIMIT)733 }734735 736 pub fn token_limit(&self) -> u32 {737 self.token_limit738 .unwrap_or(COLLECTION_TOKEN_LIMIT)739 .min(COLLECTION_TOKEN_LIMIT)740 }741742 743 744 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {745 self.sponsor_transfer_timeout746 .unwrap_or(default)747 .min(MAX_SPONSOR_TIMEOUT)748 }749750 751 pub fn sponsor_approve_timeout(&self) -> u32 {752 self.sponsor_approve_timeout753 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)754 .min(MAX_SPONSOR_TIMEOUT)755 }756757 758 pub fn owner_can_transfer(&self) -> bool {759 self.owner_can_transfer.unwrap_or(false)760 }761762 763 pub fn owner_can_transfer_instaled(&self) -> bool {764 self.owner_can_transfer.is_some()765 }766767 768 pub fn owner_can_destroy(&self) -> bool {769 self.owner_can_destroy.unwrap_or(true)770 }771772 773 pub fn transfers_enabled(&self) -> bool {774 self.transfers_enabled.unwrap_or(true)775 }776777 778 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {779 match self780 .sponsored_data_rate_limit781 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)782 {783 SponsoringRateLimit::SponsoringDisabled => None,784 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),785 }786 }787}788789790791792793794#[derive(795 Encode,796 Decode,797 Debug,798 Default,799 Clone,800 PartialEq,801 TypeInfo,802 MaxEncodedLen,803 Serialize,804 Deserialize,805)]806807808pub struct CollectionPermissions {809 810 811 812 pub access: Option<AccessMode>,813814 815 816 817 pub mint_mode: Option<bool>,818819 820 821 822 823 824 825 pub nesting: Option<NestingPermissions>,826}827828impl CollectionPermissions {829 830 pub fn access(&self) -> AccessMode {831 self.access.unwrap_or(AccessMode::Normal)832 }833834 835 pub fn mint_mode(&self) -> bool {836 self.mint_mode.unwrap_or(false)837 }838839 840 pub fn nesting(&self) -> &NestingPermissions {841 static DEFAULT: NestingPermissions = NestingPermissions {842 token_owner: false,843 collection_admin: false,844 restricted: None,845 #[cfg(feature = "runtime-benchmarks")]846 permissive: false,847 };848 self.nesting.as_ref().unwrap_or(&DEFAULT)849 }850}851852853type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;854855856#[derive(857 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,858)]859#[derivative(Debug)]860pub struct OwnerRestrictedSet(861 #[serde(with = "bounded::set_serde")]862 #[derivative(Debug(format_with = "bounded::set_debug"))]863 pub OwnerRestrictedSetInner,864);865866impl OwnerRestrictedSet {867 868 pub fn new() -> Self {869 Self(Default::default())870 }871}872impl Default for OwnerRestrictedSet {873 fn default() -> Self {874 Self::new()875 }876}877impl core::ops::Deref for OwnerRestrictedSet {878 type Target = OwnerRestrictedSetInner;879 fn deref(&self) -> &Self::Target {880 &self.0881 }882}883impl core::ops::DerefMut for OwnerRestrictedSet {884 fn deref_mut(&mut self) -> &mut Self::Target {885 &mut self.0886 }887}888889impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {890 type Error = ();891892 fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {893 Ok(Self(value.try_into()?))894 }895}896897898#[derive(899 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,900)]901#[derivative(Debug)]902pub struct NestingPermissions {903 904 pub token_owner: bool,905 906 pub collection_admin: bool,907 908 pub restricted: Option<OwnerRestrictedSet>,909910 #[cfg(feature = "runtime-benchmarks")]911 912 pub permissive: bool,913}914915916917918#[derive(919 Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,920)]921pub enum SponsoringRateLimit {922 923 SponsoringDisabled,924 925 Blocks(u32),926}927928929#[derive(930 Encode,931 Decode,932 MaxEncodedLen,933 Default,934 PartialEq,935 Clone,936 Derivative,937 TypeInfo,938 Serialize,939 Deserialize,940)]941#[derivative(Debug)]942pub struct CreateNftData {943 944 #[serde(with = "bounded::vec_serde")]945 #[derivative(Debug(format_with = "bounded::vec_debug"))]946 947 pub properties: CollectionPropertiesVec,948}949950951#[derive(952 Encode,953 Decode,954 MaxEncodedLen,955 Default,956 Debug,957 Clone,958 PartialEq,959 TypeInfo,960 Serialize,961 Deserialize,962)]963pub struct CreateFungibleData {964 965 pub value: u128,966}967968969#[derive(970 Encode,971 Decode,972 MaxEncodedLen,973 Default,974 PartialEq,975 Clone,976 Derivative,977 TypeInfo,978 Serialize,979 Deserialize,980)]981#[derivative(Debug)]982pub struct CreateReFungibleData {983 984 pub pieces: u128,985986 987 #[serde(with = "bounded::vec_serde")]988 #[derivative(Debug(format_with = "bounded::vec_debug"))]989 pub properties: CollectionPropertiesVec,990}991992993#[derive(994 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,995)]996pub enum MetaUpdatePermission {997 ItemOwner,998 Admin,999 None,1000}1001100210031004#[derive(1005 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1006)]1007pub enum CreateItemData {1008 1009 NFT(CreateNftData),1010 1011 Fungible(CreateFungibleData),1012 1013 ReFungible(CreateReFungibleData),1014}101510161017#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1018#[derivative(Debug)]1019pub struct CreateNftExData<CrossAccountId> {1020 1021 #[derivative(Debug(format_with = "bounded::vec_debug"))]1022 pub properties: CollectionPropertiesVec,10231024 1025 pub owner: CrossAccountId,1026}102710281029#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1030#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1031pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1032 #[derivative(Debug(format_with = "bounded::map_debug"))]1033 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1034 #[derivative(Debug(format_with = "bounded::vec_debug"))]1035 pub properties: CollectionPropertiesVec,1036}103710381039#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1040#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1041pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1042 pub user: CrossAccountId,1043 pub pieces: u128,1044 #[derivative(Debug(format_with = "bounded::vec_debug"))]1045 pub properties: CollectionPropertiesVec,1046}104710481049#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1050#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1051pub enum CreateItemExData<CrossAccountId> {1052 1053 NFT(1054 #[derivative(Debug(format_with = "bounded::vec_debug"))]1055 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1056 ),10571058 1059 Fungible(1060 #[derivative(Debug(format_with = "bounded::map_debug"))]1061 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1062 ),10631064 1065 1066 RefungibleMultipleItems(1067 #[derivative(Debug(format_with = "bounded::vec_debug"))]1068 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1069 ),10701071 1072 1073 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1074}10751076impl From<CreateNftData> for CreateItemData {1077 fn from(item: CreateNftData) -> Self {1078 CreateItemData::NFT(item)1079 }1080}10811082impl From<CreateReFungibleData> for CreateItemData {1083 fn from(item: CreateReFungibleData) -> Self {1084 CreateItemData::ReFungible(item)1085 }1086}10871088impl From<CreateFungibleData> for CreateItemData {1089 fn from(item: CreateFungibleData) -> Self {1090 CreateItemData::Fungible(item)1091 }1092}109310941095#[derive(1096 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1097)]10981099pub struct TokenChild {1100 1101 pub token: TokenId,11021103 1104 pub collection: CollectionId,1105}110611071108#[derive(1109 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1110)]1111pub struct CollectionStats {1112 1113 pub created: u32,11141115 1116 pub destroyed: u32,11171118 1119 pub alive: u32,1120}112111221123#[derive(Encode, Decode, Clone, Debug)]1124#[cfg_attr(feature = "std", derive(PartialEq))]1125pub struct PhantomType<T>(core::marker::PhantomData<T>);11261127impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1128 type Identity = PhantomType<T>;11291130 fn type_info() -> scale_info::Type {1131 use scale_info::{1132 build::{FieldsBuilder, UnnamedFields},1133 form::MetaForm,1134 type_params, Path, Type,1135 };1136 Type::builder()1137 .path(Path::new("up_data_structs", "PhantomType"))1138 .type_params(type_params!(T))1139 .composite(1140 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1141 )1142 }1143}1144impl<T> MaxEncodedLen for PhantomType<T> {1145 fn max_encoded_len() -> usize {1146 01147 }1148}114911501151pub type BoundedBytes<S> = BoundedVec<u8, S>;115211531154pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;115511561157pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;115811591160pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;116111621163#[derive(1164 Encode,1165 Decode,1166 TypeInfo,1167 Debug,1168 MaxEncodedLen,1169 PartialEq,1170 Clone,1171 Default,1172 Serialize,1173 Deserialize,1174)]1175pub struct PropertyPermission {1176 1177 1178 1179 pub mutable: bool,11801181 1182 pub collection_admin: bool,11831184 1185 pub token_owner: bool,1186}11871188impl PropertyPermission {1189 1190 pub fn none() -> Self {1191 Self {1192 mutable: true,1193 collection_admin: false,1194 token_owner: false,1195 }1196 }1197}119811991200#[derive(1201 Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1202)]1203pub struct Property {1204 1205 #[serde(with = "bounded::vec_serde")]1206 pub key: PropertyKey,12071208 1209 #[serde(with = "bounded::vec_serde")]1210 pub value: PropertyValue,1211}12121213impl From<Property> for (PropertyKey, PropertyValue) {1214 fn from(value: Property) -> Self {1215 (value.key, value.value)1216 }1217}121812191220#[derive(1221 Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1222)]1223pub struct PropertyKeyPermission {1224 1225 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1226 pub key: PropertyKey,12271228 1229 pub permission: PropertyPermission,1230}12311232impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1233 fn from(value: PropertyKeyPermission) -> Self {1234 (value.key, value.permission)1235 }1236}123712381239#[derive(Debug)]1240pub enum PropertiesError {1241 1242 1243 1244 1245 NoSpaceForProperty,12461247 1248 1249 1250 PropertyLimitReached,12511252 1253 InvalidCharacterInPropertyKey,12541255 1256 1257 1258 PropertyKeyIsTooLong,12591260 1261 EmptyPropertyKey,1262}126312641265#[derive(Debug)]1266pub enum TokenOwnerError {1267 NotFound,1268 MultipleOwners,1269}12701271127212731274#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1275pub enum PropertyScope {1276 None,1277 Rmrk,1278}12791280impl PropertyScope {1281 pub fn prefix(&self) -> &'static [u8] {1282 match self {1283 Self::None => b"",1284 Self::Rmrk => b"rmrk:",1285 }1286 }1287 1288 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1289 let prefix = self.prefix();1290 if prefix == b"" {1291 return Ok(key);1292 }1293 [prefix, key.as_slice()]1294 .concat()1295 .try_into()1296 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1297 }1298}129913001301pub trait TrySetProperty: Sized {1302 type Value;13031304 1305 fn try_scoped_set(1306 &mut self,1307 scope: PropertyScope,1308 key: PropertyKey,1309 value: Self::Value,1310 ) -> Result<Option<Self::Value>, PropertiesError>;13111312 1313 fn try_scoped_set_from_iter<I, KV>(1314 &mut self,1315 scope: PropertyScope,1316 iter: I,1317 ) -> Result<(), PropertiesError>1318 where1319 I: Iterator<Item = KV>,1320 KV: Into<(PropertyKey, Self::Value)>,1321 {1322 for kv in iter {1323 let (key, value) = kv.into();1324 self.try_scoped_set(scope, key, value)?;1325 }13261327 Ok(())1328 }13291330 1331 fn try_set(1332 &mut self,1333 key: PropertyKey,1334 value: Self::Value,1335 ) -> Result<Option<Self::Value>, PropertiesError> {1336 self.try_scoped_set(PropertyScope::None, key, value)1337 }13381339 1340 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1341 where1342 I: Iterator<Item = KV>,1343 KV: Into<(PropertyKey, Self::Value)>,1344 {1345 self.try_scoped_set_from_iter(PropertyScope::None, iter)1346 }1347}134813491350#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1351#[derivative(Default(bound = ""))]1352pub struct PropertiesMap<Value>(1353 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1354);13551356impl<Value> PropertiesMap<Value> {1357 1358 pub fn new() -> Self {1359 Self(BoundedBTreeMap::new())1360 }13611362 1363 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1364 Self::check_property_key(key)?;13651366 Ok(self.0.remove(key))1367 }13681369 1370 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1371 self.0.get(key)1372 }13731374 1375 pub fn contains_key(&self, key: &PropertyKey) -> bool {1376 self.0.contains_key(key)1377 }13781379 1380 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1381 if key.is_empty() {1382 return Err(PropertiesError::EmptyPropertyKey);1383 }13841385 for byte in key.as_slice().iter() {1386 let byte = *byte;13871388 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1389 return Err(PropertiesError::InvalidCharacterInPropertyKey);1390 }1391 }13921393 Ok(())1394 }13951396 pub fn values(&self) -> impl Iterator<Item = &Value> {1397 self.0.values()1398 }13991400 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1401 self.0.iter()1402 }1403}14041405impl<Value> IntoIterator for PropertiesMap<Value> {1406 type Item = (PropertyKey, Value);1407 type IntoIter = <1408 BoundedBTreeMap<1409 PropertyKey,1410 Value,1411 ConstU32<MAX_PROPERTIES_PER_ITEM>1412 > as IntoIterator1413 >::IntoIter;14141415 fn into_iter(self) -> Self::IntoIter {1416 self.0.into_iter()1417 }1418}14191420impl<Value> TrySetProperty for PropertiesMap<Value> {1421 type Value = Value;14221423 fn try_scoped_set(1424 &mut self,1425 scope: PropertyScope,1426 key: PropertyKey,1427 value: Self::Value,1428 ) -> Result<Option<Self::Value>, PropertiesError> {1429 Self::check_property_key(&key)?;14301431 let key = scope.apply(key)?;1432 self.01433 .try_insert(key, value)1434 .map_err(|_| PropertiesError::PropertyLimitReached)1435 }1436}143714381439pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14401441fn slice_size(data: &[u8]) -> u32 {1442 scoped_slice_size(PropertyScope::None, data)1443}1444fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1445 use parity_scale_codec::Compact;1446 let prefix = scope.prefix();1447 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321448 + data.len() as u321449 + prefix.len() as u321450}145114521453#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1454pub struct Properties<const S: u32> {1455 map: PropertiesMap<PropertyValue>,1456 consumed_space: u32,1457 1458 _reserved: u32,1459}14601461impl<const S: u32> MaxEncodedLen for Properties<S> {1462 fn max_encoded_len() -> usize {1463 1464 u32::max_encoded_len() * 3 + S as usize1465 }1466}14671468impl<const S: u32> Default for Properties<S> {1469 fn default() -> Self {1470 Self::new()1471 }1472}14731474impl<const S: u32> Properties<S> {1475 1476 pub fn new() -> Self {1477 Self {1478 map: PropertiesMap::new(),1479 consumed_space: 0,1480 _reserved: 0,1481 }1482 }14831484 1485 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1486 let value = self.map.remove(key)?;14871488 if let Some(ref value) = value {1489 let kv_len = slice_size(key) + slice_size(value);1490 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1491 }14921493 Ok(value)1494 }14951496 1497 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1498 self.map.get(key)1499 }15001501 1502 1503 pub fn recompute_consumed_space(&mut self) {1504 self.consumed_space = self1505 .map1506 .iter()1507 .map(|(key, value)| slice_size(key) + slice_size(value))1508 .sum();1509 }1510}15111512impl<const S: u32> IntoIterator for Properties<S> {1513 type Item = (PropertyKey, PropertyValue);1514 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15151516 fn into_iter(self) -> Self::IntoIter {1517 self.map.into_iter()1518 }1519}15201521impl<const S: u32> TrySetProperty for Properties<S> {1522 type Value = PropertyValue;15231524 fn try_scoped_set(1525 &mut self,1526 scope: PropertyScope,1527 key: PropertyKey,1528 value: Self::Value,1529 ) -> Result<Option<Self::Value>, PropertiesError> {1530 let key_size = scoped_slice_size(scope, &key);1531 let value_size = slice_size(&value);15321533 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1534 {1535 return Err(PropertiesError::NoSpaceForProperty);1536 }15371538 let old_value = self.map.try_scoped_set(scope, key, value)?;15391540 if let Some(old_value) = old_value.as_ref() {1541 let old_value_size = slice_size(old_value);1542 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1543 } else {1544 self.consumed_space += key_size + value_size;1545 }15461547 Ok(old_value)1548 }1549}15501551pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1552pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;