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 return &self.0;540 }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 core::ops::Deref for OwnerRestrictedSet {820 type Target = OwnerRestrictedSetInner;821 fn deref(&self) -> &Self::Target {822 &self.0823 }824}825impl core::ops::DerefMut for OwnerRestrictedSet {826 fn deref_mut(&mut self) -> &mut Self::Target {827 &mut self.0828 }829}830831832#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]833#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]834#[derivative(Debug)]835pub struct NestingPermissions {836 837 pub token_owner: bool,838 839 pub collection_admin: bool,840 841 pub restricted: Option<OwnerRestrictedSet>,842843 #[cfg(feature = "runtime-benchmarks")]844 845 pub permissive: bool,846}847848849850851#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]852#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]853pub enum SponsoringRateLimit {854 855 SponsoringDisabled,856 857 Blocks(u32),858}859860861#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]862#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]863#[derivative(Debug)]864pub struct CreateNftData {865 866 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]867 #[derivative(Debug(format_with = "bounded::vec_debug"))]868 869 pub properties: CollectionPropertiesVec,870}871872873#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]874#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]875pub struct CreateFungibleData {876 877 pub value: u128,878}879880881#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]882#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]883#[derivative(Debug)]884pub struct CreateReFungibleData {885 886 pub pieces: u128,887888 889 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]890 #[derivative(Debug(format_with = "bounded::vec_debug"))]891 pub properties: CollectionPropertiesVec,892}893894895#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]896#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]897pub enum MetaUpdatePermission {898 ItemOwner,899 Admin,900 None,901}902903904905#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]906#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]907pub enum CreateItemData {908 909 NFT(CreateNftData),910 911 Fungible(CreateFungibleData),912 913 ReFungible(CreateReFungibleData),914}915916917#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]918#[derivative(Debug)]919pub struct CreateNftExData<CrossAccountId> {920 921 #[derivative(Debug(format_with = "bounded::vec_debug"))]922 pub properties: CollectionPropertiesVec,923924 925 pub owner: CrossAccountId,926}927928929#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]930#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]931pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {932 #[derivative(Debug(format_with = "bounded::map_debug"))]933 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,934 #[derivative(Debug(format_with = "bounded::vec_debug"))]935 pub properties: CollectionPropertiesVec,936}937938939#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]940#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]941pub struct CreateRefungibleExSingleOwner<CrossAccountId> {942 pub user: CrossAccountId,943 pub pieces: u128,944 #[derivative(Debug(format_with = "bounded::vec_debug"))]945 pub properties: CollectionPropertiesVec,946}947948949#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]950#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]951pub enum CreateItemExData<CrossAccountId> {952 953 NFT(954 #[derivative(Debug(format_with = "bounded::vec_debug"))]955 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,956 ),957958 959 Fungible(960 #[derivative(Debug(format_with = "bounded::map_debug"))]961 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,962 ),963964 965 966 RefungibleMultipleItems(967 #[derivative(Debug(format_with = "bounded::vec_debug"))]968 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,969 ),970971 972 973 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),974}975976impl From<CreateNftData> for CreateItemData {977 fn from(item: CreateNftData) -> Self {978 CreateItemData::NFT(item)979 }980}981982impl From<CreateReFungibleData> for CreateItemData {983 fn from(item: CreateReFungibleData) -> Self {984 CreateItemData::ReFungible(item)985 }986}987988impl From<CreateFungibleData> for CreateItemData {989 fn from(item: CreateFungibleData) -> Self {990 CreateItemData::Fungible(item)991 }992}993994995#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]996#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]997998pub struct TokenChild {999 1000 pub token: TokenId,10011002 1003 pub collection: CollectionId,1004}100510061007#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]1008#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1009pub struct CollectionStats {1010 1011 pub created: u32,10121013 1014 pub destroyed: u32,10151016 1017 pub alive: u32,1018}101910201021#[derive(Encode, Decode, Clone, Debug)]1022#[cfg_attr(feature = "std", derive(PartialEq))]1023pub struct PhantomType<T>(core::marker::PhantomData<T>);10241025impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1026 type Identity = PhantomType<T>;10271028 fn type_info() -> scale_info::Type {1029 use scale_info::{1030 Type, Path,1031 build::{FieldsBuilder, UnnamedFields},1032 form::MetaForm,1033 type_params,1034 };1035 Type::builder()1036 .path(Path::new("up_data_structs", "PhantomType"))1037 .type_params(type_params!(T))1038 .composite(1039 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1040 )1041 }1042}1043impl<T> MaxEncodedLen for PhantomType<T> {1044 fn max_encoded_len() -> usize {1045 01046 }1047}104810491050pub type BoundedBytes<S> = BoundedVec<u8, S>;105110521053pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;105410551056pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;105710581059pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;106010611062#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]1063#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1064pub struct PropertyPermission {1065 1066 1067 1068 pub mutable: bool,10691070 1071 pub collection_admin: bool,10721073 1074 pub token_owner: bool,1075}10761077impl PropertyPermission {1078 1079 pub fn none() -> Self {1080 Self {1081 mutable: true,1082 collection_admin: false,1083 token_owner: false,1084 }1085 }1086}108710881089#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1090#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1091pub struct Property {1092 1093 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1094 pub key: PropertyKey,10951096 1097 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1098 pub value: PropertyValue,1099}11001101impl Into<(PropertyKey, PropertyValue)> for Property {1102 fn into(self) -> (PropertyKey, PropertyValue) {1103 (self.key, self.value)1104 }1105}110611071108#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1109#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1110pub struct PropertyKeyPermission {1111 1112 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1113 pub key: PropertyKey,11141115 1116 pub permission: PropertyPermission,1117}11181119impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1120 fn into(self) -> (PropertyKey, PropertyPermission) {1121 (self.key, self.permission)1122 }1123}112411251126#[derive(Debug)]1127pub enum PropertiesError {1128 1129 1130 1131 1132 NoSpaceForProperty,11331134 1135 1136 1137 PropertyLimitReached,11381139 1140 InvalidCharacterInPropertyKey,11411142 1143 1144 1145 PropertyKeyIsTooLong,11461147 1148 EmptyPropertyKey,1149}115011511152#[derive(Debug)]1153pub enum TokenOwnerError {1154 NotFound,1155 MultipleOwners,1156}11571158115911601161#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1162pub enum PropertyScope {1163 None,1164 Rmrk,1165}11661167impl PropertyScope {1168 pub fn prefix(&self) -> &'static [u8] {1169 match self {1170 Self::None => b"",1171 Self::Rmrk => b"rmrk:",1172 }1173 }1174 1175 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1176 let prefix = self.prefix();1177 if prefix == b"" {1178 return Ok(key);1179 }1180 [prefix, key.as_slice()]1181 .concat()1182 .try_into()1183 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1184 }1185}118611871188pub trait TrySetProperty: Sized {1189 type Value;11901191 1192 fn try_scoped_set(1193 &mut self,1194 scope: PropertyScope,1195 key: PropertyKey,1196 value: Self::Value,1197 ) -> Result<Option<Self::Value>, PropertiesError>;11981199 1200 fn try_scoped_set_from_iter<I, KV>(1201 &mut self,1202 scope: PropertyScope,1203 iter: I,1204 ) -> Result<(), PropertiesError>1205 where1206 I: Iterator<Item = KV>,1207 KV: Into<(PropertyKey, Self::Value)>,1208 {1209 for kv in iter {1210 let (key, value) = kv.into();1211 self.try_scoped_set(scope, key, value)?;1212 }12131214 Ok(())1215 }12161217 1218 fn try_set(1219 &mut self,1220 key: PropertyKey,1221 value: Self::Value,1222 ) -> Result<Option<Self::Value>, PropertiesError> {1223 self.try_scoped_set(PropertyScope::None, key, value)1224 }12251226 1227 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1228 where1229 I: Iterator<Item = KV>,1230 KV: Into<(PropertyKey, Self::Value)>,1231 {1232 self.try_scoped_set_from_iter(PropertyScope::None, iter)1233 }1234}123512361237#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1238#[derivative(Default(bound = ""))]1239pub struct PropertiesMap<Value>(1240 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1241);12421243impl<Value> PropertiesMap<Value> {1244 1245 pub fn new() -> Self {1246 Self(BoundedBTreeMap::new())1247 }12481249 1250 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1251 Self::check_property_key(key)?;12521253 Ok(self.0.remove(key))1254 }12551256 1257 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1258 self.0.get(key)1259 }12601261 1262 pub fn contains_key(&self, key: &PropertyKey) -> bool {1263 self.0.contains_key(key)1264 }12651266 1267 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1268 if key.is_empty() {1269 return Err(PropertiesError::EmptyPropertyKey);1270 }12711272 for byte in key.as_slice().iter() {1273 let byte = *byte;12741275 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1276 return Err(PropertiesError::InvalidCharacterInPropertyKey);1277 }1278 }12791280 Ok(())1281 }12821283 pub fn values(&self) -> impl Iterator<Item = &Value> {1284 self.0.values()1285 }12861287 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1288 self.0.iter()1289 }1290}12911292impl<Value> IntoIterator for PropertiesMap<Value> {1293 type Item = (PropertyKey, Value);1294 type IntoIter = <1295 BoundedBTreeMap<1296 PropertyKey,1297 Value,1298 ConstU32<MAX_PROPERTIES_PER_ITEM>1299 > as IntoIterator1300 >::IntoIter;13011302 fn into_iter(self) -> Self::IntoIter {1303 self.0.into_iter()1304 }1305}13061307impl<Value> TrySetProperty for PropertiesMap<Value> {1308 type Value = Value;13091310 fn try_scoped_set(1311 &mut self,1312 scope: PropertyScope,1313 key: PropertyKey,1314 value: Self::Value,1315 ) -> Result<Option<Self::Value>, PropertiesError> {1316 Self::check_property_key(&key)?;13171318 let key = scope.apply(key)?;1319 self.01320 .try_insert(key, value)1321 .map_err(|_| PropertiesError::PropertyLimitReached)1322 }1323}132413251326pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;13271328fn slice_size(data: &[u8]) -> u32 {1329 scoped_slice_size(PropertyScope::None, data)1330}1331fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1332 use codec::Compact;1333 let prefix = scope.prefix();1334 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321335 + data.len() as u321336 + prefix.len() as u321337}133813391340#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1341pub struct Properties<const S: u32> {1342 map: PropertiesMap<PropertyValue>,1343 consumed_space: u32,1344 1345 _reserved: u32,1346}13471348impl<const S: u32> MaxEncodedLen for Properties<S> {1349 fn max_encoded_len() -> usize {1350 1351 u32::max_encoded_len() * 3 + S as usize1352 }1353}13541355impl<const S: u32> Default for Properties<S> {1356 fn default() -> Self {1357 Self::new()1358 }1359}13601361impl<const S: u32> Properties<S> {1362 1363 pub fn new() -> Self {1364 Self {1365 map: PropertiesMap::new(),1366 consumed_space: 0,1367 _reserved: 0,1368 }1369 }13701371 1372 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1373 let value = self.map.remove(key)?;13741375 if let Some(ref value) = value {1376 let kv_len = slice_size(key) + slice_size(value);1377 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1378 }13791380 Ok(value)1381 }13821383 1384 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1385 self.map.get(key)1386 }13871388 1389 1390 pub fn recompute_consumed_space(&mut self) {1391 self.consumed_space = self1392 .map1393 .iter()1394 .map(|(key, value)| slice_size(key) + slice_size(value))1395 .sum();1396 }1397}13981399impl<const S: u32> IntoIterator for Properties<S> {1400 type Item = (PropertyKey, PropertyValue);1401 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;14021403 fn into_iter(self) -> Self::IntoIter {1404 self.map.into_iter()1405 }1406}14071408impl<const S: u32> TrySetProperty for Properties<S> {1409 type Value = PropertyValue;14101411 fn try_scoped_set(1412 &mut self,1413 scope: PropertyScope,1414 key: PropertyKey,1415 value: Self::Value,1416 ) -> Result<Option<Self::Value>, PropertiesError> {1417 let key_size = scoped_slice_size(scope, &key);1418 let value_size = slice_size(&value) as u32;14191420 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1421 {1422 return Err(PropertiesError::NoSpaceForProperty);1423 }14241425 let old_value = self.map.try_scoped_set(scope, key, value)?;14261427 if let Some(old_value) = old_value.as_ref() {1428 let old_value_size = slice_size(&old_value);1429 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1430 } else {1431 self.consumed_space += key_size + value_size;1432 }14331434 Ok(old_value)1435 }1436}14371438pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1439pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;