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)]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}167168impl Deref for CollectionId {169 type Target = u32;170171 fn deref(&self) -> &Self::Target {172 &self.0173 }174}175176177#[derive(178 Encode,179 Decode,180 PartialEq,181 Eq,182 PartialOrd,183 Ord,184 Clone,185 Copy,186 Debug,187 Default,188 TypeInfo,189 MaxEncodedLen,190)]191#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]192pub struct TokenId(pub u32);193impl EncodeLike<u32> for TokenId {}194impl EncodeLike<TokenId> for u32 {}195196impl TokenId {197 198 199 200 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {201 self.0202 .checked_add(1)203 .ok_or(ArithmeticError::Overflow)204 .map(Self)205 }206}207208impl From<TokenId> for U256 {209 fn from(t: TokenId) -> Self {210 t.0.into()211 }212}213214impl TryFrom<U256> for TokenId {215 type Error = &'static str;216217 fn try_from(value: U256) -> Result<Self, Self::Error> {218 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))219 }220}221222223#[struct_versioning::versioned(version = 2, upper)]224#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]225#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]226pub struct TokenData<CrossAccountId> {227 228 pub properties: Vec<Property>,229230 231 pub owner: Option<CrossAccountId>,232233 234 #[version(2.., upper(0))]235 pub pieces: u128,236}237238239pub struct OverflowError;240impl From<OverflowError> for &'static str {241 fn from(_: OverflowError) -> Self {242 "overflow occured"243 }244}245246247pub type DecimalPoints = u8;248249250251252253254#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]255#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]256pub enum CollectionMode {257 258 NFT,259 260 Fungible(DecimalPoints),261 262 ReFungible,263}264265impl CollectionMode {266 267 pub fn id(&self) -> u8 {268 match self {269 CollectionMode::NFT => 1,270 CollectionMode::Fungible(_) => 2,271 CollectionMode::ReFungible => 3,272 }273 }274}275276277pub trait SponsoringResolve<AccountId, Call> {278 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;279}280281282#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]283#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]284pub enum AccessMode {285 286 Normal,287 288 AllowList,289}290impl Default for AccessMode {291 fn default() -> Self {292 Self::Normal293 }294}295296297#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]298#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]299pub enum SchemaVersion {300 ImageURL,301 Unique,302}303impl Default for SchemaVersion {304 fn default() -> Self {305 Self::ImageURL306 }307}308309310#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]311#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]312pub struct Ownership<AccountId> {313 pub owner: AccountId,314 pub fraction: u128,315}316317318#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]319#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]320pub enum SponsorshipState<AccountId> {321 322 Disabled,323 324 325 Unconfirmed(AccountId),326 327 Confirmed(AccountId),328}329330impl<AccountId> SponsorshipState<AccountId> {331 332 pub fn sponsor(&self) -> Option<&AccountId> {333 match self {334 Self::Confirmed(sponsor) => Some(sponsor),335 _ => None,336 }337 }338339 340 pub fn pending_sponsor(&self) -> Option<&AccountId> {341 match self {342 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),343 _ => None,344 }345 }346347 348 pub fn confirmed(&self) -> bool {349 matches!(self, Self::Confirmed(_))350 }351}352353impl<T> Default for SponsorshipState<T> {354 fn default() -> Self {355 Self::Disabled356 }357}358359pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;360pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;361pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;362363#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]364#[bondrewd(enforce_bytes = 1)]365pub struct CollectionFlags {366 367 #[bondrewd(bits = "0..1")]368 pub foreign: bool,369 370 #[bondrewd(bits = "1..2")]371 pub erc721metadata: bool,372 373 #[bondrewd(bits = "7..8")]374 pub external: bool,375 376 #[bondrewd(bits = "2..7")]377 pub reserved: u8,378}379bondrewd_codec!(CollectionFlags);380381impl CollectionFlags {382 pub fn is_allowed_for_user(self) -> bool {383 !self.foreign && !self.external && self.reserved == 0384 }385}386387388389390391392393#[struct_versioning::versioned(version = 2, upper)]394#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]395pub struct Collection<AccountId> {396 397 pub owner: AccountId,398399 400 pub mode: CollectionMode,401402 403 #[version(..2)]404 pub access: AccessMode,405406 407 pub name: CollectionName,408409 410 pub description: CollectionDescription,411412 413 pub token_prefix: CollectionTokenPrefix,414415 #[version(..2)]416 pub mint_mode: bool,417418 #[version(..2)]419 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,420421 #[version(..2)]422 pub schema_version: SchemaVersion,423424 425 pub sponsorship: SponsorshipState<AccountId>,426427 428 pub limits: CollectionLimits,429430 431 #[version(2.., upper(Default::default()))]432 pub permissions: CollectionPermissions,433434 #[version(2.., upper(Default::default()))]435 pub flags: CollectionFlags,436437 #[version(..2)]438 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,439440 #[version(..2)]441 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,442443 #[version(..2)]444 pub meta_update_permission: MetaUpdatePermission,445}446447#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]448#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]449pub struct RpcCollectionFlags {450 451 pub foreign: bool,452 453 pub erc721metadata: bool,454}455456457#[struct_versioning::versioned(version = 2, upper)]458#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]459#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]460pub struct RpcCollection<AccountId> {461 462 pub owner: AccountId,463464 465 pub mode: CollectionMode,466467 468 pub name: Vec<u16>,469470 471 pub description: Vec<u16>,472473 474 pub token_prefix: Vec<u8>,475476 477 pub sponsorship: SponsorshipState<AccountId>,478479 480 pub limits: CollectionLimits,481482 483 pub permissions: CollectionPermissions,484485 486 pub token_property_permissions: Vec<PropertyKeyPermission>,487488 489 pub properties: Vec<Property>,490491 492 pub read_only: bool,493494 495 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]496 pub flags: RpcCollectionFlags,497}498499impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {500 fn from(value: CollectionVersion1<AccountId>) -> Self {501 let CollectionVersion1 {502 name,503 description,504 owner,505 mode,506 access,507 token_prefix,508 mint_mode,509 sponsorship,510 limits,511 ..512 } = value;513514 RpcCollection {515 name: name.into_inner(),516 description: description.into_inner(),517 owner,518 mode,519 token_prefix: token_prefix.into_inner(),520 sponsorship,521 limits,522 permissions: CollectionPermissions {523 access: Some(access),524 mint_mode: Some(mint_mode),525 nesting: None,526 },527 token_property_permissions: Vec::default(),528 properties: Vec::default(),529 read_only: true,530531 flags: RpcCollectionFlags {532 foreign: false,533 erc721metadata: false,534 },535 }536 }537}538539pub struct RawEncoded(Vec<u8>);540541impl codec::Decode for RawEncoded {542 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {543 let mut out = Vec::new();544 while let Ok(v) = input.read_byte() {545 out.push(v);546 }547 Ok(Self(out))548 }549}550551impl Deref for RawEncoded {552 type Target = Vec<u8>;553554 fn deref(&self) -> &Self::Target {555 &self.0556 }557}558559560561562#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]563#[derivative(Debug, Default(bound = ""))]564pub struct CreateCollectionData<CrossAccountId> {565 566 #[derivative(Default(value = "CollectionMode::NFT"))]567 pub mode: CollectionMode,568569 570 pub access: Option<AccessMode>,571572 573 pub name: CollectionName,574575 576 pub description: CollectionDescription,577578 579 pub token_prefix: CollectionTokenPrefix,580581 582 pub limits: Option<CollectionLimits>,583584 585 pub permissions: Option<CollectionPermissions>,586587 588 pub token_property_permissions: CollectionPropertiesPermissionsVec,589590 591 pub properties: CollectionPropertiesVec,592593 pub admin_list: Vec<CrossAccountId>,594595 596 pub pending_sponsor: Option<CrossAccountId>,597598 pub flags: CollectionFlags,599}600601602603pub type CollectionPropertiesPermissionsVec =604 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;605606607pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;608609610611612613614615#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]616#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]617618619620pub struct CollectionLimits {621 622 623 624 pub account_token_ownership_limit: Option<u32>,625626 627 628 629 pub sponsored_data_size: Option<u32>,630631 632 633 634 635 636 637 638 639 640 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,641 642643 644 645 646 647 pub token_limit: Option<u32>,648649 650 651 652 653 654 655 656 pub sponsor_transfer_timeout: Option<u32>,657658 659 660 661 662 pub sponsor_approve_timeout: Option<u32>,663664 665 666 667 pub owner_can_transfer: Option<bool>,668669 670 671 672 pub owner_can_destroy: Option<bool>,673674 675 676 677 pub transfers_enabled: Option<bool>,678}679680impl CollectionLimits {681 pub fn with_default_limits(collection_type: CollectionMode) -> Self {682 CollectionLimits {683 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),684 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),685 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),686 token_limit: Some(COLLECTION_TOKEN_LIMIT),687 sponsor_transfer_timeout: match collection_type {688 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),689 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),690 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),691 },692 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),693 owner_can_transfer: Some(false),694 owner_can_destroy: Some(true),695 transfers_enabled: Some(true),696 }697 }698699 700 pub fn account_token_ownership_limit(&self) -> u32 {701 self.account_token_ownership_limit702 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)703 .min(MAX_TOKEN_OWNERSHIP)704 }705706 707 pub fn sponsored_data_size(&self) -> u32 {708 self.sponsored_data_size709 .unwrap_or(CUSTOM_DATA_LIMIT)710 .min(CUSTOM_DATA_LIMIT)711 }712713 714 pub fn token_limit(&self) -> u32 {715 self.token_limit716 .unwrap_or(COLLECTION_TOKEN_LIMIT)717 .min(COLLECTION_TOKEN_LIMIT)718 }719720 721 722 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {723 self.sponsor_transfer_timeout724 .unwrap_or(default)725 .min(MAX_SPONSOR_TIMEOUT)726 }727728 729 pub fn sponsor_approve_timeout(&self) -> u32 {730 self.sponsor_approve_timeout731 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)732 .min(MAX_SPONSOR_TIMEOUT)733 }734735 736 pub fn owner_can_transfer(&self) -> bool {737 self.owner_can_transfer.unwrap_or(false)738 }739740 741 pub fn owner_can_transfer_instaled(&self) -> bool {742 self.owner_can_transfer.is_some()743 }744745 746 pub fn owner_can_destroy(&self) -> bool {747 self.owner_can_destroy.unwrap_or(true)748 }749750 751 pub fn transfers_enabled(&self) -> bool {752 self.transfers_enabled.unwrap_or(true)753 }754755 756 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {757 match self758 .sponsored_data_rate_limit759 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)760 {761 SponsoringRateLimit::SponsoringDisabled => None,762 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),763 }764 }765}766767768769770771772#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]773#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]774775776pub struct CollectionPermissions {777 778 779 780 pub access: Option<AccessMode>,781782 783 784 785 pub mint_mode: Option<bool>,786787 788 789 790 791 792 793 pub nesting: Option<NestingPermissions>,794}795796impl CollectionPermissions {797 798 pub fn access(&self) -> AccessMode {799 self.access.unwrap_or(AccessMode::Normal)800 }801802 803 pub fn mint_mode(&self) -> bool {804 self.mint_mode.unwrap_or(false)805 }806807 808 pub fn nesting(&self) -> &NestingPermissions {809 static DEFAULT: NestingPermissions = NestingPermissions {810 token_owner: false,811 collection_admin: false,812 restricted: None,813 #[cfg(feature = "runtime-benchmarks")]814 permissive: false,815 };816 self.nesting.as_ref().unwrap_or(&DEFAULT)817 }818}819820821type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;822823824#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]825#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]826#[derivative(Debug)]827pub struct OwnerRestrictedSet(828 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]829 #[derivative(Debug(format_with = "bounded::set_debug"))]830 pub OwnerRestrictedSetInner,831);832833impl OwnerRestrictedSet {834 835 pub fn new() -> Self {836 Self(Default::default())837 }838}839impl Default for OwnerRestrictedSet {840 fn default() -> Self {841 Self::new()842 }843}844impl core::ops::Deref for OwnerRestrictedSet {845 type Target = OwnerRestrictedSetInner;846 fn deref(&self) -> &Self::Target {847 &self.0848 }849}850impl core::ops::DerefMut for OwnerRestrictedSet {851 fn deref_mut(&mut self) -> &mut Self::Target {852 &mut self.0853 }854}855856impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {857 type Error = ();858859 fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {860 Ok(Self(value.try_into()?))861 }862}863864865#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]866#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]867#[derivative(Debug)]868pub struct NestingPermissions {869 870 pub token_owner: bool,871 872 pub collection_admin: bool,873 874 pub restricted: Option<OwnerRestrictedSet>,875876 #[cfg(feature = "runtime-benchmarks")]877 878 pub permissive: bool,879}880881882883884#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]885#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]886pub enum SponsoringRateLimit {887 888 SponsoringDisabled,889 890 Blocks(u32),891}892893894#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]895#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]896#[derivative(Debug)]897pub struct CreateNftData {898 899 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]900 #[derivative(Debug(format_with = "bounded::vec_debug"))]901 902 pub properties: CollectionPropertiesVec,903}904905906#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]907#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]908pub struct CreateFungibleData {909 910 pub value: u128,911}912913914#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]915#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]916#[derivative(Debug)]917pub struct CreateReFungibleData {918 919 pub pieces: u128,920921 922 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]923 #[derivative(Debug(format_with = "bounded::vec_debug"))]924 pub properties: CollectionPropertiesVec,925}926927928#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]929#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]930pub enum MetaUpdatePermission {931 ItemOwner,932 Admin,933 None,934}935936937938#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]939#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]940pub enum CreateItemData {941 942 NFT(CreateNftData),943 944 Fungible(CreateFungibleData),945 946 ReFungible(CreateReFungibleData),947}948949950#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]951#[derivative(Debug)]952pub struct CreateNftExData<CrossAccountId> {953 954 #[derivative(Debug(format_with = "bounded::vec_debug"))]955 pub properties: CollectionPropertiesVec,956957 958 pub owner: CrossAccountId,959}960961962#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]963#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]964pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {965 #[derivative(Debug(format_with = "bounded::map_debug"))]966 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,967 #[derivative(Debug(format_with = "bounded::vec_debug"))]968 pub properties: CollectionPropertiesVec,969}970971972#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]973#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]974pub struct CreateRefungibleExSingleOwner<CrossAccountId> {975 pub user: CrossAccountId,976 pub pieces: u128,977 #[derivative(Debug(format_with = "bounded::vec_debug"))]978 pub properties: CollectionPropertiesVec,979}980981982#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]983#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]984pub enum CreateItemExData<CrossAccountId> {985 986 NFT(987 #[derivative(Debug(format_with = "bounded::vec_debug"))]988 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,989 ),990991 992 Fungible(993 #[derivative(Debug(format_with = "bounded::map_debug"))]994 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,995 ),996997 998 999 RefungibleMultipleItems(1000 #[derivative(Debug(format_with = "bounded::vec_debug"))]1001 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1002 ),10031004 1005 1006 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1007}10081009impl From<CreateNftData> for CreateItemData {1010 fn from(item: CreateNftData) -> Self {1011 CreateItemData::NFT(item)1012 }1013}10141015impl From<CreateReFungibleData> for CreateItemData {1016 fn from(item: CreateReFungibleData) -> Self {1017 CreateItemData::ReFungible(item)1018 }1019}10201021impl From<CreateFungibleData> for CreateItemData {1022 fn from(item: CreateFungibleData) -> Self {1023 CreateItemData::Fungible(item)1024 }1025}102610271028#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1029#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]10301031pub struct TokenChild {1032 1033 pub token: TokenId,10341035 1036 pub collection: CollectionId,1037}103810391040#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1041#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1042pub struct CollectionStats {1043 1044 pub created: u32,10451046 1047 pub destroyed: u32,10481049 1050 pub alive: u32,1051}105210531054#[derive(Encode, Decode, Clone, Debug)]1055#[cfg_attr(feature = "std", derive(PartialEq))]1056pub struct PhantomType<T>(core::marker::PhantomData<T>);10571058impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1059 type Identity = PhantomType<T>;10601061 fn type_info() -> scale_info::Type {1062 use scale_info::{1063 Type, Path,1064 build::{FieldsBuilder, UnnamedFields},1065 form::MetaForm,1066 type_params,1067 };1068 Type::builder()1069 .path(Path::new("up_data_structs", "PhantomType"))1070 .type_params(type_params!(T))1071 .composite(1072 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1073 )1074 }1075}1076impl<T> MaxEncodedLen for PhantomType<T> {1077 fn max_encoded_len() -> usize {1078 01079 }1080}108110821083pub type BoundedBytes<S> = BoundedVec<u8, S>;108410851086pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;108710881089pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;109010911092pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;109310941095#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1096#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1097pub struct PropertyPermission {1098 1099 1100 1101 pub mutable: bool,11021103 1104 pub collection_admin: bool,11051106 1107 pub token_owner: bool,1108}11091110impl PropertyPermission {1111 1112 pub fn none() -> Self {1113 Self {1114 mutable: true,1115 collection_admin: false,1116 token_owner: false,1117 }1118 }1119}112011211122#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1123#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1124pub struct Property {1125 1126 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1127 pub key: PropertyKey,11281129 1130 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1131 pub value: PropertyValue,1132}11331134impl From<Property> for (PropertyKey, PropertyValue) {1135 fn from(value: Property) -> Self {1136 (value.key, value.value)1137 }1138}113911401141#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1142#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1143pub struct PropertyKeyPermission {1144 1145 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1146 pub key: PropertyKey,11471148 1149 pub permission: PropertyPermission,1150}11511152impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1153 fn from(value: PropertyKeyPermission) -> Self {1154 (value.key, value.permission)1155 }1156}115711581159#[derive(Debug)]1160pub enum PropertiesError {1161 1162 1163 1164 1165 NoSpaceForProperty,11661167 1168 1169 1170 PropertyLimitReached,11711172 1173 InvalidCharacterInPropertyKey,11741175 1176 1177 1178 PropertyKeyIsTooLong,11791180 1181 EmptyPropertyKey,1182}118311841185#[derive(Debug)]1186pub enum TokenOwnerError {1187 NotFound,1188 MultipleOwners,1189}11901191119211931194#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1195pub enum PropertyScope {1196 None,1197 Rmrk,1198}11991200impl PropertyScope {1201 pub fn prefix(&self) -> &'static [u8] {1202 match self {1203 Self::None => b"",1204 Self::Rmrk => b"rmrk:",1205 }1206 }1207 1208 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1209 let prefix = self.prefix();1210 if prefix == b"" {1211 return Ok(key);1212 }1213 [prefix, key.as_slice()]1214 .concat()1215 .try_into()1216 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1217 }1218}121912201221pub trait TrySetProperty: Sized {1222 type Value;12231224 1225 fn try_scoped_set(1226 &mut self,1227 scope: PropertyScope,1228 key: PropertyKey,1229 value: Self::Value,1230 ) -> Result<Option<Self::Value>, PropertiesError>;12311232 1233 fn try_scoped_set_from_iter<I, KV>(1234 &mut self,1235 scope: PropertyScope,1236 iter: I,1237 ) -> Result<(), PropertiesError>1238 where1239 I: Iterator<Item = KV>,1240 KV: Into<(PropertyKey, Self::Value)>,1241 {1242 for kv in iter {1243 let (key, value) = kv.into();1244 self.try_scoped_set(scope, key, value)?;1245 }12461247 Ok(())1248 }12491250 1251 fn try_set(1252 &mut self,1253 key: PropertyKey,1254 value: Self::Value,1255 ) -> Result<Option<Self::Value>, PropertiesError> {1256 self.try_scoped_set(PropertyScope::None, key, value)1257 }12581259 1260 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1261 where1262 I: Iterator<Item = KV>,1263 KV: Into<(PropertyKey, Self::Value)>,1264 {1265 self.try_scoped_set_from_iter(PropertyScope::None, iter)1266 }1267}126812691270#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1271#[derivative(Default(bound = ""))]1272pub struct PropertiesMap<Value>(1273 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1274);12751276impl<Value> PropertiesMap<Value> {1277 1278 pub fn new() -> Self {1279 Self(BoundedBTreeMap::new())1280 }12811282 1283 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1284 Self::check_property_key(key)?;12851286 Ok(self.0.remove(key))1287 }12881289 1290 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1291 self.0.get(key)1292 }12931294 1295 pub fn contains_key(&self, key: &PropertyKey) -> bool {1296 self.0.contains_key(key)1297 }12981299 1300 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1301 if key.is_empty() {1302 return Err(PropertiesError::EmptyPropertyKey);1303 }13041305 for byte in key.as_slice().iter() {1306 let byte = *byte;13071308 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1309 return Err(PropertiesError::InvalidCharacterInPropertyKey);1310 }1311 }13121313 Ok(())1314 }13151316 pub fn values(&self) -> impl Iterator<Item = &Value> {1317 self.0.values()1318 }13191320 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1321 self.0.iter()1322 }1323}13241325impl<Value> IntoIterator for PropertiesMap<Value> {1326 type Item = (PropertyKey, Value);1327 type IntoIter = <1328 BoundedBTreeMap<1329 PropertyKey,1330 Value,1331 ConstU32<MAX_PROPERTIES_PER_ITEM>1332 > as IntoIterator1333 >::IntoIter;13341335 fn into_iter(self) -> Self::IntoIter {1336 self.0.into_iter()1337 }1338}13391340impl<Value> TrySetProperty for PropertiesMap<Value> {1341 type Value = Value;13421343 fn try_scoped_set(1344 &mut self,1345 scope: PropertyScope,1346 key: PropertyKey,1347 value: Self::Value,1348 ) -> Result<Option<Self::Value>, PropertiesError> {1349 Self::check_property_key(&key)?;13501351 let key = scope.apply(key)?;1352 self.01353 .try_insert(key, value)1354 .map_err(|_| PropertiesError::PropertyLimitReached)1355 }1356}135713581359pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13601361fn slice_size(data: &[u8]) -> u32 {1362 scoped_slice_size(PropertyScope::None, data)1363}1364fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1365 use codec::Compact;1366 let prefix = scope.prefix();1367 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321368 + data.len() as u321369 + prefix.len() as u321370}137113721373#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1374pub struct Properties<const S: u32> {1375 map: PropertiesMap<PropertyValue>,1376 consumed_space: u32,1377 1378 _reserved: u32,1379}13801381impl<const S: u32> MaxEncodedLen for Properties<S> {1382 fn max_encoded_len() -> usize {1383 1384 u32::max_encoded_len() * 3 + S as usize1385 }1386}13871388impl<const S: u32> Default for Properties<S> {1389 fn default() -> Self {1390 Self::new()1391 }1392}13931394impl<const S: u32> Properties<S> {1395 1396 pub fn new() -> Self {1397 Self {1398 map: PropertiesMap::new(),1399 consumed_space: 0,1400 _reserved: 0,1401 }1402 }14031404 1405 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1406 let value = self.map.remove(key)?;14071408 if let Some(ref value) = value {1409 let kv_len = slice_size(key) + slice_size(value);1410 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1411 }14121413 Ok(value)1414 }14151416 1417 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1418 self.map.get(key)1419 }14201421 1422 1423 pub fn recompute_consumed_space(&mut self) {1424 self.consumed_space = self1425 .map1426 .iter()1427 .map(|(key, value)| slice_size(key) + slice_size(value))1428 .sum();1429 }1430}14311432impl<const S: u32> IntoIterator for Properties<S> {1433 type Item = (PropertyKey, PropertyValue);1434 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;14351436 fn into_iter(self) -> Self::IntoIter {1437 self.map.into_iter()1438 }1439}14401441impl<const S: u32> TrySetProperty for Properties<S> {1442 type Value = PropertyValue;14431444 fn try_scoped_set(1445 &mut self,1446 scope: PropertyScope,1447 key: PropertyKey,1448 value: Self::Value,1449 ) -> Result<Option<Self::Value>, PropertiesError> {1450 let key_size = scoped_slice_size(scope, &key);1451 let value_size = slice_size(&value);14521453 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1454 {1455 return Err(PropertiesError::NoSpaceForProperty);1456 }14571458 let old_value = self.map.try_scoped_set(scope, key, value)?;14591460 if let Some(old_value) = old_value.as_ref() {1461 let old_value_size = slice_size(old_value);1462 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1463 } else {1464 self.consumed_space += key_size + value_size;1465 }14661467 Ok(old_value)1468 }1469}14701471pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1472pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;