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 codec::{Decode, Encode, EncodeLike, MaxEncodedLen};36use bondrewd::Bitfields;37use frame_support::{BoundedVec, traits::ConstU32};38use derivative::Derivative;39use scale_info::TypeInfo;4041mod bondrewd_codec;42mod bounded;43pub mod budget;44pub mod mapping;45mod migration;464748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;495051pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;52pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;535455pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {56 100_00057} else {58 1059};606162pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {63 100_00064} else {65 1066};676869pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {70 204871} else {72 1073};747576pub const COLLECTION_ADMINS_LIMIT: u32 = 5;777879pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;808182pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {83 1_000_00084} else {85 1086};878889pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9091pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9293pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;949596pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;979899pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;100pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;101pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;102103104pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;105106107pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;108109110pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;111112113pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;114115116pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;117118119pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;120121122pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;123124125pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;126127128pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;129130131pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;132133134135pub const MAX_ITEMS_PER_BATCH: u32 = 200;136137138pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;139140141#[derive(142 Encode,143 Decode,144 PartialEq,145 Eq,146 PartialOrd,147 Ord,148 Clone,149 Copy,150 Debug,151 Default,152 TypeInfo,153 MaxEncodedLen,154)]155#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]156pub struct CollectionId(pub u32);157impl EncodeLike<u32> for CollectionId {}158impl EncodeLike<CollectionId> for u32 {}159160impl From<u32> for CollectionId {161 fn from(value: u32) -> Self {162 Self(value)163 }164}165166167#[derive(168 Encode,169 Decode,170 PartialEq,171 Eq,172 PartialOrd,173 Ord,174 Clone,175 Copy,176 Debug,177 Default,178 TypeInfo,179 MaxEncodedLen,180)]181#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]182pub struct TokenId(pub u32);183impl EncodeLike<u32> for TokenId {}184impl EncodeLike<TokenId> for u32 {}185186impl TokenId {187 188 189 190 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {191 self.0192 .checked_add(1)193 .ok_or(ArithmeticError::Overflow)194 .map(Self)195 }196}197198impl From<TokenId> for U256 {199 fn from(t: TokenId) -> Self {200 t.0.into()201 }202}203204impl TryFrom<U256> for TokenId {205 type Error = &'static str;206207 fn try_from(value: U256) -> Result<Self, Self::Error> {208 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))209 }210}211212213#[struct_versioning::versioned(version = 2, upper)]214#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]215#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]216pub struct TokenData<CrossAccountId> {217 218 pub properties: Vec<Property>,219220 221 pub owner: Option<CrossAccountId>,222223 224 #[version(2.., upper(0))]225 pub pieces: u128,226}227228229pub struct OverflowError;230impl From<OverflowError> for &'static str {231 fn from(_: OverflowError) -> Self {232 "overflow occured"233 }234}235236237pub type DecimalPoints = u8;238239240241242243244#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]245#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]246pub enum CollectionMode {247 248 NFT,249 250 Fungible(DecimalPoints),251 252 ReFungible,253}254255impl CollectionMode {256 257 pub fn id(&self) -> u8 {258 match self {259 CollectionMode::NFT => 1,260 CollectionMode::Fungible(_) => 2,261 CollectionMode::ReFungible => 3,262 }263 }264}265266267pub trait SponsoringResolve<AccountId, Call> {268 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;269}270271272#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]273#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]274pub enum AccessMode {275 276 Normal,277 278 AllowList,279}280impl Default for AccessMode {281 fn default() -> Self {282 Self::Normal283 }284}285286287#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]288#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]289pub enum SchemaVersion {290 ImageURL,291 Unique,292}293impl Default for SchemaVersion {294 fn default() -> Self {295 Self::ImageURL296 }297}298299300#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]301#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]302pub struct Ownership<AccountId> {303 pub owner: AccountId,304 pub fraction: u128,305}306307308#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]309#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]310pub enum SponsorshipState<AccountId> {311 312 Disabled,313 314 315 Unconfirmed(AccountId),316 317 Confirmed(AccountId),318}319320impl<AccountId> SponsorshipState<AccountId> {321 322 pub fn sponsor(&self) -> Option<&AccountId> {323 match self {324 Self::Confirmed(sponsor) => Some(sponsor),325 _ => None,326 }327 }328329 330 pub fn pending_sponsor(&self) -> Option<&AccountId> {331 match self {332 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),333 _ => None,334 }335 }336337 338 pub fn confirmed(&self) -> bool {339 matches!(self, Self::Confirmed(_))340 }341}342343impl<T> Default for SponsorshipState<T> {344 fn default() -> Self {345 Self::Disabled346 }347}348349pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;350pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;351pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;352353#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]354#[bondrewd(enforce_bytes = 1)]355pub struct CollectionFlags {356 357 #[bondrewd(bits = "0..1")]358 pub foreign: bool,359 360 #[bondrewd(bits = "1..2")]361 pub erc721metadata: bool,362 363 #[bondrewd(bits = "7..8")]364 pub external: bool,365366 #[bondrewd(reserve, bits = "2..7")]367 pub reserved: u8,368}369bondrewd_codec!(CollectionFlags);370371372373374375376377#[struct_versioning::versioned(version = 2, upper)]378#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]379pub struct Collection<AccountId> {380 381 pub owner: AccountId,382383 384 pub mode: CollectionMode,385386 387 #[version(..2)]388 pub access: AccessMode,389390 391 pub name: CollectionName,392393 394 pub description: CollectionDescription,395396 397 pub token_prefix: CollectionTokenPrefix,398399 #[version(..2)]400 pub mint_mode: bool,401402 #[version(..2)]403 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,404405 #[version(..2)]406 pub schema_version: SchemaVersion,407408 409 pub sponsorship: SponsorshipState<AccountId>,410411 412 pub limits: CollectionLimits,413414 415 #[version(2.., upper(Default::default()))]416 pub permissions: CollectionPermissions,417418 #[version(2.., upper(Default::default()))]419 pub flags: CollectionFlags,420421 #[version(..2)]422 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,423424 #[version(..2)]425 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,426427 #[version(..2)]428 pub meta_update_permission: MetaUpdatePermission,429}430431#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]432#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]433pub struct RpcCollectionFlags {434 435 pub foreign: bool,436 437 pub erc721metadata: bool,438}439440441#[struct_versioning::versioned(version = 2, upper)]442#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo)]443#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]444pub struct RpcCollection<AccountId> {445 446 pub owner: AccountId,447448 449 pub mode: CollectionMode,450451 452 pub name: Vec<u16>,453454 455 pub description: Vec<u16>,456457 458 pub token_prefix: Vec<u8>,459460 461 pub sponsorship: SponsorshipState<AccountId>,462463 464 pub limits: CollectionLimits,465466 467 pub permissions: CollectionPermissions,468469 470 pub token_property_permissions: Vec<PropertyKeyPermission>,471472 473 pub properties: Vec<Property>,474475 476 pub read_only: bool,477478 479 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]480 pub flags: RpcCollectionFlags,481}482483impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {484 fn from(value: CollectionVersion1<AccountId>) -> Self {485 let CollectionVersion1 {486 name,487 description,488 owner,489 mode,490 access,491 token_prefix,492 mint_mode,493 sponsorship,494 limits,495 ..496 } = value;497498 RpcCollection {499 name: name.into_inner(),500 description: description.into_inner(),501 owner,502 mode,503 token_prefix: token_prefix.into_inner(),504 sponsorship,505 limits,506 permissions: CollectionPermissions {507 access: Some(access),508 mint_mode: Some(mint_mode),509 nesting: None,510 },511 token_property_permissions: Vec::default(),512 properties: Vec::default(),513 read_only: true,514515 flags: RpcCollectionFlags {516 foreign: false,517 erc721metadata: false,518 },519 }520 }521}522523pub struct RawEncoded(Vec<u8>);524525impl codec::Decode for RawEncoded {526 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {527 let mut out = Vec::new();528 while let Ok(v) = input.read_byte() {529 out.push(v);530 }531 Ok(Self(out))532 }533}534535impl Deref for RawEncoded {536 type Target = Vec<u8>;537538 fn deref(&self) -> &Self::Target {539 &self.0540 }541}542543544545546#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]547#[derivative(Debug, Default(bound = ""))]548pub struct CreateCollectionData<AccountId> {549 550 #[derivative(Default(value = "CollectionMode::NFT"))]551 pub mode: CollectionMode,552553 554 pub access: Option<AccessMode>,555556 557 pub name: CollectionName,558559 560 pub description: CollectionDescription,561562 563 pub token_prefix: CollectionTokenPrefix,564565 566 pub pending_sponsor: Option<AccountId>,567568 569 pub limits: Option<CollectionLimits>,570571 572 pub permissions: Option<CollectionPermissions>,573574 575 pub token_property_permissions: CollectionPropertiesPermissionsVec,576577 578 pub properties: CollectionPropertiesVec,579}580581582583pub type CollectionPropertiesPermissionsVec =584 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;585586587pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;588589590591592593594595#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]596#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]597598599600pub struct CollectionLimits {601 602 603 604 pub account_token_ownership_limit: Option<u32>,605606 607 608 609 pub sponsored_data_size: Option<u32>,610611 612 613 614 615 616 617 618 619 620 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,621 622623 624 625 626 627 pub token_limit: Option<u32>,628629 630 631 632 633 634 635 636 pub sponsor_transfer_timeout: Option<u32>,637638 639 640 641 642 pub sponsor_approve_timeout: Option<u32>,643644 645 646 647 pub owner_can_transfer: Option<bool>,648649 650 651 652 pub owner_can_destroy: Option<bool>,653654 655 656 657 pub transfers_enabled: Option<bool>,658}659660impl CollectionLimits {661 pub fn with_default_limits(collection_type: CollectionMode) -> Self {662 CollectionLimits {663 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),664 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),665 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),666 token_limit: Some(COLLECTION_TOKEN_LIMIT),667 sponsor_transfer_timeout: match collection_type {668 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),669 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),670 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),671 },672 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),673 owner_can_transfer: Some(false),674 owner_can_destroy: Some(true),675 transfers_enabled: Some(true),676 }677 }678679 680 pub fn account_token_ownership_limit(&self) -> u32 {681 self.account_token_ownership_limit682 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)683 .min(MAX_TOKEN_OWNERSHIP)684 }685686 687 pub fn sponsored_data_size(&self) -> u32 {688 self.sponsored_data_size689 .unwrap_or(CUSTOM_DATA_LIMIT)690 .min(CUSTOM_DATA_LIMIT)691 }692693 694 pub fn token_limit(&self) -> u32 {695 self.token_limit696 .unwrap_or(COLLECTION_TOKEN_LIMIT)697 .min(COLLECTION_TOKEN_LIMIT)698 }699700 701 702 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {703 self.sponsor_transfer_timeout704 .unwrap_or(default)705 .min(MAX_SPONSOR_TIMEOUT)706 }707708 709 pub fn sponsor_approve_timeout(&self) -> u32 {710 self.sponsor_approve_timeout711 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)712 .min(MAX_SPONSOR_TIMEOUT)713 }714715 716 pub fn owner_can_transfer(&self) -> bool {717 self.owner_can_transfer.unwrap_or(false)718 }719720 721 pub fn owner_can_transfer_instaled(&self) -> bool {722 self.owner_can_transfer.is_some()723 }724725 726 pub fn owner_can_destroy(&self) -> bool {727 self.owner_can_destroy.unwrap_or(true)728 }729730 731 pub fn transfers_enabled(&self) -> bool {732 self.transfers_enabled.unwrap_or(true)733 }734735 736 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {737 match self738 .sponsored_data_rate_limit739 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)740 {741 SponsoringRateLimit::SponsoringDisabled => None,742 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),743 }744 }745}746747748749750751752#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]753#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]754755756pub struct CollectionPermissions {757 758 759 760 pub access: Option<AccessMode>,761762 763 764 765 pub mint_mode: Option<bool>,766767 768 769 770 771 772 773 pub nesting: Option<NestingPermissions>,774}775776impl CollectionPermissions {777 778 pub fn access(&self) -> AccessMode {779 self.access.unwrap_or(AccessMode::Normal)780 }781782 783 pub fn mint_mode(&self) -> bool {784 self.mint_mode.unwrap_or(false)785 }786787 788 pub fn nesting(&self) -> &NestingPermissions {789 static DEFAULT: NestingPermissions = NestingPermissions {790 token_owner: false,791 collection_admin: false,792 restricted: None,793 #[cfg(feature = "runtime-benchmarks")]794 permissive: false,795 };796 self.nesting.as_ref().unwrap_or(&DEFAULT)797 }798}799800801type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;802803804#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]805#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]806#[derivative(Debug)]807pub struct OwnerRestrictedSet(808 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]809 #[derivative(Debug(format_with = "bounded::set_debug"))]810 pub OwnerRestrictedSetInner,811);812813impl OwnerRestrictedSet {814 815 pub fn new() -> Self {816 Self(Default::default())817 }818}819impl Default for OwnerRestrictedSet {820 fn default() -> Self {821 Self::new()822 }823}824impl core::ops::Deref for OwnerRestrictedSet {825 type Target = OwnerRestrictedSetInner;826 fn deref(&self) -> &Self::Target {827 &self.0828 }829}830impl core::ops::DerefMut for OwnerRestrictedSet {831 fn deref_mut(&mut self) -> &mut Self::Target {832 &mut self.0833 }834}835836837#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]838#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]839#[derivative(Debug)]840pub struct NestingPermissions {841 842 pub token_owner: bool,843 844 pub collection_admin: bool,845 846 pub restricted: Option<OwnerRestrictedSet>,847848 #[cfg(feature = "runtime-benchmarks")]849 850 pub permissive: bool,851}852853854855856#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]857#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]858pub enum SponsoringRateLimit {859 860 SponsoringDisabled,861 862 Blocks(u32),863}864865866#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]867#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]868#[derivative(Debug)]869pub struct CreateNftData {870 871 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]872 #[derivative(Debug(format_with = "bounded::vec_debug"))]873 874 pub properties: CollectionPropertiesVec,875}876877878#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]879#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]880pub struct CreateFungibleData {881 882 pub value: u128,883}884885886#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]887#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]888#[derivative(Debug)]889pub struct CreateReFungibleData {890 891 pub pieces: u128,892893 894 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]895 #[derivative(Debug(format_with = "bounded::vec_debug"))]896 pub properties: CollectionPropertiesVec,897}898899900#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]901#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]902pub enum MetaUpdatePermission {903 ItemOwner,904 Admin,905 None,906}907908909910#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]911#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]912pub enum CreateItemData {913 914 NFT(CreateNftData),915 916 Fungible(CreateFungibleData),917 918 ReFungible(CreateReFungibleData),919}920921922#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]923#[derivative(Debug)]924pub struct CreateNftExData<CrossAccountId> {925 926 #[derivative(Debug(format_with = "bounded::vec_debug"))]927 pub properties: CollectionPropertiesVec,928929 930 pub owner: CrossAccountId,931}932933934#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]935#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]936pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {937 #[derivative(Debug(format_with = "bounded::map_debug"))]938 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,939 #[derivative(Debug(format_with = "bounded::vec_debug"))]940 pub properties: CollectionPropertiesVec,941}942943944#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]945#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]946pub struct CreateRefungibleExSingleOwner<CrossAccountId> {947 pub user: CrossAccountId,948 pub pieces: u128,949 #[derivative(Debug(format_with = "bounded::vec_debug"))]950 pub properties: CollectionPropertiesVec,951}952953954#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]955#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]956pub enum CreateItemExData<CrossAccountId> {957 958 NFT(959 #[derivative(Debug(format_with = "bounded::vec_debug"))]960 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,961 ),962963 964 Fungible(965 #[derivative(Debug(format_with = "bounded::map_debug"))]966 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,967 ),968969 970 971 RefungibleMultipleItems(972 #[derivative(Debug(format_with = "bounded::vec_debug"))]973 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,974 ),975976 977 978 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),979}980981impl From<CreateNftData> for CreateItemData {982 fn from(item: CreateNftData) -> Self {983 CreateItemData::NFT(item)984 }985}986987impl From<CreateReFungibleData> for CreateItemData {988 fn from(item: CreateReFungibleData) -> Self {989 CreateItemData::ReFungible(item)990 }991}992993impl From<CreateFungibleData> for CreateItemData {994 fn from(item: CreateFungibleData) -> Self {995 CreateItemData::Fungible(item)996 }997}9989991000#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1001#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]10021003pub struct TokenChild {1004 1005 pub token: TokenId,10061007 1008 pub collection: CollectionId,1009}101010111012#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1013#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1014pub struct CollectionStats {1015 1016 pub created: u32,10171018 1019 pub destroyed: u32,10201021 1022 pub alive: u32,1023}102410251026#[derive(Encode, Decode, Clone, Debug)]1027#[cfg_attr(feature = "std", derive(PartialEq))]1028pub struct PhantomType<T>(core::marker::PhantomData<T>);10291030impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1031 type Identity = PhantomType<T>;10321033 fn type_info() -> scale_info::Type {1034 use scale_info::{1035 Type, Path,1036 build::{FieldsBuilder, UnnamedFields},1037 form::MetaForm,1038 type_params,1039 };1040 Type::builder()1041 .path(Path::new("up_data_structs", "PhantomType"))1042 .type_params(type_params!(T))1043 .composite(1044 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1045 )1046 }1047}1048impl<T> MaxEncodedLen for PhantomType<T> {1049 fn max_encoded_len() -> usize {1050 01051 }1052}105310541055pub type BoundedBytes<S> = BoundedVec<u8, S>;105610571058pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;105910601061pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;106210631064pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;106510661067#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1068#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1069pub struct PropertyPermission {1070 1071 1072 1073 pub mutable: bool,10741075 1076 pub collection_admin: bool,10771078 1079 pub token_owner: bool,1080}10811082impl PropertyPermission {1083 1084 pub fn none() -> Self {1085 Self {1086 mutable: true,1087 collection_admin: false,1088 token_owner: false,1089 }1090 }1091}109210931094#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1095#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1096pub struct Property {1097 1098 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1099 pub key: PropertyKey,11001101 1102 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1103 pub value: PropertyValue,1104}11051106impl From<Property> for (PropertyKey, PropertyValue) {1107 fn from(value: Property) -> Self {1108 (value.key, value.value)1109 }1110}111111121113#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1115pub struct PropertyKeyPermission {1116 1117 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1118 pub key: PropertyKey,11191120 1121 pub permission: PropertyPermission,1122}11231124impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1125 fn from(value: PropertyKeyPermission) -> Self {1126 (value.key, value.permission)1127 }1128}112911301131#[derive(Debug)]1132pub enum PropertiesError {1133 1134 1135 1136 1137 NoSpaceForProperty,11381139 1140 1141 1142 PropertyLimitReached,11431144 1145 InvalidCharacterInPropertyKey,11461147 1148 1149 1150 PropertyKeyIsTooLong,11511152 1153 EmptyPropertyKey,1154}115511561157#[derive(Debug)]1158pub enum TokenOwnerError {1159 NotFound,1160 MultipleOwners,1161}11621163116411651166#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1167pub enum PropertyScope {1168 None,1169 Rmrk,1170}11711172impl PropertyScope {1173 pub fn prefix(&self) -> &'static [u8] {1174 match self {1175 Self::None => b"",1176 Self::Rmrk => b"rmrk:",1177 }1178 }1179 1180 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1181 let prefix = self.prefix();1182 if prefix == b"" {1183 return Ok(key);1184 }1185 [prefix, key.as_slice()]1186 .concat()1187 .try_into()1188 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1189 }1190}119111921193pub trait TrySetProperty: Sized {1194 type Value;11951196 1197 fn try_scoped_set(1198 &mut self,1199 scope: PropertyScope,1200 key: PropertyKey,1201 value: Self::Value,1202 ) -> Result<Option<Self::Value>, PropertiesError>;12031204 1205 fn try_scoped_set_from_iter<I, KV>(1206 &mut self,1207 scope: PropertyScope,1208 iter: I,1209 ) -> Result<(), PropertiesError>1210 where1211 I: Iterator<Item = KV>,1212 KV: Into<(PropertyKey, Self::Value)>,1213 {1214 for kv in iter {1215 let (key, value) = kv.into();1216 self.try_scoped_set(scope, key, value)?;1217 }12181219 Ok(())1220 }12211222 1223 fn try_set(1224 &mut self,1225 key: PropertyKey,1226 value: Self::Value,1227 ) -> Result<Option<Self::Value>, PropertiesError> {1228 self.try_scoped_set(PropertyScope::None, key, value)1229 }12301231 1232 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1233 where1234 I: Iterator<Item = KV>,1235 KV: Into<(PropertyKey, Self::Value)>,1236 {1237 self.try_scoped_set_from_iter(PropertyScope::None, iter)1238 }1239}124012411242#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1243#[derivative(Default(bound = ""))]1244pub struct PropertiesMap<Value>(1245 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1246);12471248impl<Value> PropertiesMap<Value> {1249 1250 pub fn new() -> Self {1251 Self(BoundedBTreeMap::new())1252 }12531254 1255 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1256 Self::check_property_key(key)?;12571258 Ok(self.0.remove(key))1259 }12601261 1262 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1263 self.0.get(key)1264 }12651266 1267 pub fn contains_key(&self, key: &PropertyKey) -> bool {1268 self.0.contains_key(key)1269 }12701271 1272 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1273 if key.is_empty() {1274 return Err(PropertiesError::EmptyPropertyKey);1275 }12761277 for byte in key.as_slice().iter() {1278 let byte = *byte;12791280 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1281 return Err(PropertiesError::InvalidCharacterInPropertyKey);1282 }1283 }12841285 Ok(())1286 }12871288 pub fn values(&self) -> impl Iterator<Item = &Value> {1289 self.0.values()1290 }12911292 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1293 self.0.iter()1294 }1295}12961297impl<Value> IntoIterator for PropertiesMap<Value> {1298 type Item = (PropertyKey, Value);1299 type IntoIter = <1300 BoundedBTreeMap<1301 PropertyKey,1302 Value,1303 ConstU32<MAX_PROPERTIES_PER_ITEM>1304 > as IntoIterator1305 >::IntoIter;13061307 fn into_iter(self) -> Self::IntoIter {1308 self.0.into_iter()1309 }1310}13111312impl<Value> TrySetProperty for PropertiesMap<Value> {1313 type Value = Value;13141315 fn try_scoped_set(1316 &mut self,1317 scope: PropertyScope,1318 key: PropertyKey,1319 value: Self::Value,1320 ) -> Result<Option<Self::Value>, PropertiesError> {1321 Self::check_property_key(&key)?;13221323 let key = scope.apply(key)?;1324 self.01325 .try_insert(key, value)1326 .map_err(|_| PropertiesError::PropertyLimitReached)1327 }1328}132913301331pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13321333fn slice_size(data: &[u8]) -> u32 {1334 scoped_slice_size(PropertyScope::None, data)1335}1336fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1337 use codec::Compact;1338 let prefix = scope.prefix();1339 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321340 + data.len() as u321341 + prefix.len() as u321342}134313441345#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1346pub struct Properties<const S: u32> {1347 map: PropertiesMap<PropertyValue>,1348 consumed_space: u32,1349 1350 _reserved: u32,1351}13521353impl<const S: u32> MaxEncodedLen for Properties<S> {1354 fn max_encoded_len() -> usize {1355 1356 u32::max_encoded_len() * 3 + S as usize1357 }1358}13591360impl<const S: u32> Default for Properties<S> {1361 fn default() -> Self {1362 Self::new()1363 }1364}13651366impl<const S: u32> Properties<S> {1367 1368 pub fn new() -> Self {1369 Self {1370 map: PropertiesMap::new(),1371 consumed_space: 0,1372 _reserved: 0,1373 }1374 }13751376 1377 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1378 let value = self.map.remove(key)?;13791380 if let Some(ref value) = value {1381 let kv_len = slice_size(key) + slice_size(value);1382 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1383 }13841385 Ok(value)1386 }13871388 1389 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1390 self.map.get(key)1391 }13921393 1394 1395 pub fn recompute_consumed_space(&mut self) {1396 self.consumed_space = self1397 .map1398 .iter()1399 .map(|(key, value)| slice_size(key) + slice_size(value))1400 .sum();1401 }1402}14031404impl<const S: u32> IntoIterator for Properties<S> {1405 type Item = (PropertyKey, PropertyValue);1406 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;14071408 fn into_iter(self) -> Self::IntoIter {1409 self.map.into_iter()1410 }1411}14121413impl<const S: u32> TrySetProperty for Properties<S> {1414 type Value = PropertyValue;14151416 fn try_scoped_set(1417 &mut self,1418 scope: PropertyScope,1419 key: PropertyKey,1420 value: Self::Value,1421 ) -> Result<Option<Self::Value>, PropertiesError> {1422 let key_size = scoped_slice_size(scope, &key);1423 let value_size = slice_size(&value);14241425 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1426 {1427 return Err(PropertiesError::NoSpaceForProperty);1428 }14291430 let old_value = self.map.try_scoped_set(scope, key, value)?;14311432 if let Some(old_value) = old_value.as_ref() {1433 let old_value_size = slice_size(old_value);1434 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1435 } else {1436 self.consumed_space += key_size + value_size;1437 }14381439 Ok(old_value)1440 }1441}14421443pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1444pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;