git.delta.rocks / unique-network / refs/commits / 383b7efb354e

difftreelog

refactor remove redundant foreign flag, use foreign-assets pallet instead

Daniel Shiposha2023-10-18parent: #68fcead.patch.diff
in: master

8 files changed

modifiedpallets/balances-adapter/src/common.rsdiffbeforeafterboth
--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -368,10 +368,6 @@
 }
 
 impl<T: Config> pallet_common::XcmExtensions<T> for NativeFungibleHandle<T> {
-	fn is_foreign(&self) -> bool {
-		false
-	}
-
 	fn create_item_internal(
 		&self,
 		_depositor: &<T>::CrossAccountId,
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1088,7 +1088,6 @@
 			read_only: flags.external,
 
 			flags: RpcCollectionFlags {
-				foreign: flags.foreign,
 				erc721metadata: flags.erc721metadata,
 			},
 		})
@@ -1128,17 +1127,17 @@
 	/// Create new collection.
 	///
 	/// * `owner` - The owner of the collection.
+	/// * `payer` - If set, the user that will pay a deposit for the collection creation.
 	/// * `data` - Description of the created collection.
-	/// * `flags` - Extra flags to store.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
-		payer: T::CrossAccountId,
+		payer: Option<T::CrossAccountId>,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
 		ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
 
 		// Take a (non-refundable) deposit of collection creation
-		{
+		if let Some(payer) = payer {
 			let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
 			imbalance.subsume(<T as Config>::Currency::deposit(
 				&T::TreasuryAccountId::get(),
@@ -1153,16 +1152,6 @@
 		}
 
 		Self::init_collection_internal(owner, data)
-	}
-
-	/// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.
-	pub fn init_foreign_collection(
-		owner: T::CrossAccountId,
-		mut data: CreateCollectionData<T::CrossAccountId>,
-	) -> Result<CollectionId, DispatchError> {
-		data.flags.foreign = true;
-		let id = Self::init_collection_internal(owner, data)?;
-		Ok(id)
 	}
 
 	fn init_collection_internal(
@@ -2348,9 +2337,6 @@
 where
 	T: Config,
 {
-	/// Is the collection a foreign one?
-	fn is_foreign(&self) -> bool;
-
 	/// Does the token have children?
 	fn token_has_children(&self, _token: TokenId) -> bool {
 		false
modifiedpallets/foreign-assets/src/lib.rsdiffbeforeafterboth
--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -304,11 +304,10 @@
 	/// If the `asset_instance` is a part of a local collection,
 	/// the function will return either `Ok(Some(<token ID>))` or an error if the token is not found.
 	fn asset_instance_to_token_id(
-		xcm_ext: &dyn XcmExtensions<T>,
 		collection_id: CollectionId,
 		asset_instance: &AssetInstance,
 	) -> Result<Option<TokenId>, XcmError> {
-		if xcm_ext.is_foreign() {
+		if <CollectionToForeignReserveLocation<T>>::contains_key(collection_id) {
 			Ok(Self::foreign_reserve_asset_instance_to_token_id(
 				collection_id,
 				asset_instance,
@@ -363,7 +362,7 @@
 		to: T::CrossAccountId,
 	) -> XcmResult {
 		let deposit_result = if let Some(token_id) =
-			Self::asset_instance_to_token_id(xcm_ext, collection_id, asset_instance)?
+			Self::asset_instance_to_token_id(collection_id, asset_instance)?
 		{
 			let depositor = &Self::pallet_account();
 			let from = depositor;
@@ -389,7 +388,7 @@
 		asset_instance: &AssetInstance,
 		from: T::CrossAccountId,
 	) -> XcmResult {
-		let token_id = Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?
+		let token_id = Self::asset_instance_to_token_id(collection_id, &asset_instance)?
 			.ok_or(XcmError::AssetNotFound)?;
 
 		if xcm_ext.token_has_children(token_id) {
@@ -517,9 +516,8 @@
 			}
 
 			Fungibility::NonFungible(asset_instance) => {
-				token_id =
-					Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?
-						.ok_or(XcmError::AssetNotFound)?;
+				token_id = Self::asset_instance_to_token_id(collection_id, &asset_instance)?
+					.ok_or(XcmError::AssetNotFound)?;
 
 				amount = 1;
 				map_error = |_| XcmError::FailedToTransactAsset("nonfungible item transfer failed")
@@ -542,17 +540,11 @@
 		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {
 			Some(Here.into())
 		} else {
-			let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;
-			let collection = dispatch.as_dyn();
-			let xcm_ext = collection.xcm_extensions()?;
-
-			if xcm_ext.is_foreign() {
-				<Pallet<T>>::collection_to_foreign_reserve_location(collection_id)
-			} else {
+			<Pallet<T>>::collection_to_foreign_reserve_location(collection_id).or_else(|| {
 				T::SelfLocation::get()
 					.pushed_with_interior(GeneralIndex(collection_id.0.into()))
 					.ok()
-			}
+			})
 		}
 	}
 }
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -459,10 +459,6 @@
 }
 
 impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {
-	fn is_foreign(&self) -> bool {
-		self.flags.foreign
-	}
-
 	fn create_item_internal(
 		&self,
 		depositor: &<T>::CrossAccountId,
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -572,10 +572,6 @@
 }
 
 impl<T: Config> XcmExtensions<T> for NonfungibleHandle<T> {
-	fn is_foreign(&self) -> bool {
-		self.flags.foreign
-	}
-
 	fn token_has_children(&self, token: TokenId) -> bool {
 		<Pallet<T>>::token_has_children(self.id, token)
 	}
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -306,7 +306,7 @@
 		payer: T::CrossAccountId,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(owner, payer, data)
+		<PalletCommon<T>>::init_collection(owner, Some(payer), data)
 	}
 
 	/// Destroy RFT collection
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
before · primitives/data-structs/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24	convert::{TryFrom, TryInto},25	fmt,26	ops::Deref,27};2829use bondrewd::Bitfields;30use derivative::Derivative;31use evm_coder::AbiCoderFlags;32use frame_support::{33	storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},34	traits::ConstU32,35	BoundedVec,36};37use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use scale_info::TypeInfo;39use serde::{Deserialize, Serialize};40use sp_core::U256;41use sp_runtime::{sp_std::prelude::Vec, ArithmeticError};42use sp_std::collections::btree_set::BTreeSet;4344mod bondrewd_codec;45mod bounded;46pub mod budget;47pub mod mapping;48mod migration;4950/// Maximum of decimal points.51pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5253/// Maximum pieces for refungible token.54pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;55pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5657/// Maximum tokens for user.58pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {59	100_000_00060} else {61	1062};6364/// Maximum for collections can be created.65pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {66	100_00067} else {68	1069};7071/// Maximum for various custom data of token.72pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73	204874} else {75	1076};7778/// Maximum admins per collection.79pub const COLLECTION_ADMINS_LIMIT: u32 = 5;8081/// Maximum tokens per collection.82pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8384/// Maximum tokens per account.85pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {86	100_000_00087} else {88	1089};9091/// Default timeout for transfer sponsoring NFT item.92pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;93/// Default timeout for transfer sponsoring fungible item.94pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;95/// Default timeout for transfer sponsoring refungible item.96pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9798/// Default timeout for sponsored approving.99pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;100101// Schema limits102pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;104pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;105106// TODO: not used. Delete?107pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;108109/// Maximal length of a collection name.110pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;111112/// Maximal length of a collection description.113pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;114115/// Maximal length of a token prefix.116pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;117118/// Maximal length of a property key.119pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;120121/// Maximal length of a property value.122pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;123124/// A maximum number of token properties.125pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;126127/// Maximal lenght of extended property value.128pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;129130/// Maximum size for all collection properties.131pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;132133/// Maximum size of all token properties.134pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;135136/// How much items can be created per single137/// create_many call.138pub const MAX_ITEMS_PER_BATCH: u32 = 120;139140/// Used for limit bounded types of token custom data.141pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;142143/// Collection id.144#[derive(145	Encode,146	Decode,147	PartialEq,148	Eq,149	PartialOrd,150	Ord,151	Clone,152	Copy,153	Debug,154	Default,155	TypeInfo,156	MaxEncodedLen,157	Serialize,158	Deserialize,159)]160pub struct CollectionId(pub u32);161impl EncodeLike<u32> for CollectionId {}162impl EncodeLike<CollectionId> for u32 {}163164impl From<u32> for CollectionId {165	fn from(value: u32) -> Self {166		Self(value)167	}168}169170impl Deref for CollectionId {171	type Target = u32;172173	fn deref(&self) -> &Self::Target {174		&self.0175	}176}177178/// Token id.179#[derive(180	Encode,181	Decode,182	PartialEq,183	Eq,184	PartialOrd,185	Ord,186	Clone,187	Copy,188	Debug,189	Default,190	TypeInfo,191	MaxEncodedLen,192	Serialize,193	Deserialize,194)]195pub struct TokenId(pub u32);196impl EncodeLike<u32> for TokenId {}197impl EncodeLike<TokenId> for u32 {}198199impl TokenId {200	/// Try to get next token id.201	///202	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.203	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {204		self.0205			.checked_add(1)206			.ok_or(ArithmeticError::Overflow)207			.map(Self)208	}209}210211impl From<TokenId> for U256 {212	fn from(t: TokenId) -> Self {213		t.0.into()214	}215}216217impl TryFrom<U256> for TokenId {218	type Error = &'static str;219220	fn try_from(value: U256) -> Result<Self, Self::Error> {221		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))222	}223}224225/// Token data.226#[struct_versioning::versioned(version = 2, upper)]227#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]228pub struct TokenData<CrossAccountId> {229	/// Properties of token.230	pub properties: Vec<Property>,231232	/// Token owner.233	pub owner: Option<CrossAccountId>,234235	/// Token pieces.236	#[version(2.., upper(0))]237	pub pieces: u128,238}239240// TODO: unused type241pub struct OverflowError;242impl From<OverflowError> for &'static str {243	fn from(_: OverflowError) -> Self {244		"overflow occured"245	}246}247248/// Alias for decimal points type.249pub type DecimalPoints = u8;250251/// Collection mode.252///253/// Collection can represent various types of tokens.254/// Each collection can contain only one type of tokens at a time.255/// This type helps to understand which tokens the collection contains.256#[derive(257	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,258)]259pub enum CollectionMode {260	/// Non fungible tokens.261	NFT,262	/// Fungible tokens.263	Fungible(DecimalPoints),264	/// Refungible tokens.265	ReFungible,266}267268impl CollectionMode {269	/// Get collection mod as number.270	pub fn id(&self) -> u8 {271		match self {272			CollectionMode::NFT => 1,273			CollectionMode::Fungible(_) => 2,274			CollectionMode::ReFungible => 3,275		}276	}277}278279// TODO: unused trait280pub trait SponsoringResolve<AccountId, Call> {281	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;282}283284/// Access mode for some token operations.285#[derive(286	Encode,287	Decode,288	Eq,289	Debug,290	Clone,291	Copy,292	PartialEq,293	TypeInfo,294	MaxEncodedLen,295	Serialize,296	Deserialize,297)]298pub enum AccessMode {299	/// Access grant for owner and admins. Used as default.300	Normal,301	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.302	AllowList,303}304impl Default for AccessMode {305	fn default() -> Self {306		Self::Normal307	}308}309310// TODO: remove in future.311#[derive(312	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,313)]314pub enum SchemaVersion {315	ImageURL,316	Unique,317}318impl Default for SchemaVersion {319	fn default() -> Self {320		Self::ImageURL321	}322}323324// TODO: unused type325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]326pub struct Ownership<AccountId> {327	pub owner: AccountId,328	pub fraction: u128,329}330331/// The state of collection sponsorship.332#[derive(333	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,334)]335pub enum SponsorshipState<AccountId> {336	/// The fees are applied to the transaction sender.337	Disabled,338	/// The sponsor is under consideration. Until the sponsor gives his consent,339	/// the fee will still be charged to sender.340	Unconfirmed(AccountId),341	/// Transactions are sponsored by specified account.342	Confirmed(AccountId),343}344345impl<AccountId> SponsorshipState<AccountId> {346	/// Get a sponsor of the collection who has confirmed his status.347	pub fn sponsor(&self) -> Option<&AccountId> {348		match self {349			Self::Confirmed(sponsor) => Some(sponsor),350			_ => None,351		}352	}353354	/// Get a sponsor of the collection who has pending or confirmed status.355	pub fn pending_sponsor(&self) -> Option<&AccountId> {356		match self {357			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),358			_ => None,359		}360	}361362	/// Whether the sponsorship is confirmed.363	pub fn confirmed(&self) -> bool {364		matches!(self, Self::Confirmed(_))365	}366}367368impl<T> Default for SponsorshipState<T> {369	fn default() -> Self {370		Self::Disabled371	}372}373374pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;375pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;376pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;377378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]379#[bondrewd(enforce_bytes = 1)]380pub struct CollectionFlags {381	/// Tokens in foreign collections can be transferred, but not burnt382	#[bondrewd(bits = "0..1")]383	pub foreign: bool,384	/// Supports ERC721Metadata385	#[bondrewd(bits = "1..2")]386	pub erc721metadata: bool,387	/// External collections can't be managed using `unique` api388	#[bondrewd(bits = "7..8")]389	pub external: bool,390	/// Reserved flags391	#[bondrewd(bits = "2..7")]392	pub reserved: u8,393}394bondrewd_codec!(CollectionFlags);395396impl CollectionFlags {397	pub fn is_allowed_for_user(self) -> bool {398		!self.foreign && !self.external && self.reserved == 0399	}400}401402/// Base structure for represent collection.403///404/// Used to provide basic functionality for all types of collections.405///406/// #### Note407/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).408#[struct_versioning::versioned(version = 2, upper)]409#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]410pub struct Collection<AccountId> {411	/// Collection owner account.412	pub owner: AccountId,413414	/// Collection mode.415	pub mode: CollectionMode,416417	/// Access mode.418	#[version(..2)]419	pub access: AccessMode,420421	/// Collection name.422	pub name: CollectionName,423424	/// Collection description.425	pub description: CollectionDescription,426427	/// Token prefix.428	pub token_prefix: CollectionTokenPrefix,429430	#[version(..2)]431	pub mint_mode: bool,432433	#[version(..2)]434	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,435436	#[version(..2)]437	pub schema_version: SchemaVersion,438439	/// The state of sponsorship of the collection.440	pub sponsorship: SponsorshipState<AccountId>,441442	/// Collection limits.443	pub limits: CollectionLimits,444445	/// Collection permissions.446	#[version(2.., upper(Default::default()))]447	pub permissions: CollectionPermissions,448449	#[version(2.., upper(Default::default()))]450	pub flags: CollectionFlags,451452	#[version(..2)]453	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,454455	#[version(..2)]456	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,457458	#[version(..2)]459	pub meta_update_permission: MetaUpdatePermission,460}461462#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]463pub struct RpcCollectionFlags {464	/// Is collection is foreign.465	pub foreign: bool,466	/// Collection supports ERC721Metadata.467	pub erc721metadata: bool,468}469470/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).471#[struct_versioning::versioned(version = 2, upper)]472#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]473pub struct RpcCollection<AccountId> {474	/// Collection owner account.475	pub owner: AccountId,476477	/// Collection mode.478	pub mode: CollectionMode,479480	/// Collection name.481	pub name: Vec<u16>,482483	/// Collection description.484	pub description: Vec<u16>,485486	/// Token prefix.487	pub token_prefix: Vec<u8>,488489	/// The state of sponsorship of the collection.490	pub sponsorship: SponsorshipState<AccountId>,491492	/// Collection limits.493	pub limits: CollectionLimits,494495	/// Collection permissions.496	pub permissions: CollectionPermissions,497498	/// Token property permissions.499	pub token_property_permissions: Vec<PropertyKeyPermission>,500501	/// Collection properties.502	pub properties: Vec<Property>,503504	/// Is collection read only.505	pub read_only: bool,506507	/// Extra collection flags508	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]509	pub flags: RpcCollectionFlags,510}511512impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {513	fn from(value: CollectionVersion1<AccountId>) -> Self {514		let CollectionVersion1 {515			name,516			description,517			owner,518			mode,519			access,520			token_prefix,521			mint_mode,522			sponsorship,523			limits,524			..525		} = value;526527		RpcCollection {528			name: name.into_inner(),529			description: description.into_inner(),530			owner,531			mode,532			token_prefix: token_prefix.into_inner(),533			sponsorship,534			limits,535			permissions: CollectionPermissions {536				access: Some(access),537				mint_mode: Some(mint_mode),538				nesting: None,539			},540			token_property_permissions: Vec::default(),541			properties: Vec::default(),542			read_only: true,543544			flags: RpcCollectionFlags {545				foreign: false,546				erc721metadata: false,547			},548		}549	}550}551552pub struct RawEncoded(Vec<u8>);553554impl parity_scale_codec::Decode for RawEncoded {555	fn decode<I: parity_scale_codec::Input>(556		input: &mut I,557	) -> Result<Self, parity_scale_codec::Error> {558		let mut out = Vec::new();559		while let Ok(v) = input.read_byte() {560			out.push(v);561		}562		Ok(Self(out))563	}564}565566impl Deref for RawEncoded {567	type Target = Vec<u8>;568569	fn deref(&self) -> &Self::Target {570		&self.0571	}572}573574/// Data used for create collection.575///576/// All fields are wrapped in [`Option`], where `None` means chain default.577#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]578#[derivative(Debug, Default(bound = ""))]579pub struct CreateCollectionData<CrossAccountId> {580	/// Collection mode.581	#[derivative(Default(value = "CollectionMode::NFT"))]582	pub mode: CollectionMode,583584	/// Access mode.585	pub access: Option<AccessMode>,586587	/// Collection name.588	pub name: CollectionName,589590	/// Collection description.591	pub description: CollectionDescription,592593	/// Token prefix.594	pub token_prefix: CollectionTokenPrefix,595596	/// Collection limits.597	pub limits: Option<CollectionLimits>,598599	/// Collection permissions.600	pub permissions: Option<CollectionPermissions>,601602	/// Token property permissions.603	pub token_property_permissions: CollectionPropertiesPermissionsVec,604605	/// Collection properties.606	pub properties: CollectionPropertiesVec,607608	pub admin_list: Vec<CrossAccountId>,609610	/// Pending collection sponsor.611	pub pending_sponsor: Option<CrossAccountId>,612613	pub flags: CollectionFlags,614}615616/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].617// TODO: maybe rename to PropertiesPermissionsVec618pub type CollectionPropertiesPermissionsVec =619	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;620621/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].622pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;623624/// Limits and restrictions of a collection.625///626/// All fields are wrapped in [`Option`], where `None` means chain default.627///628/// Update with `pallet_common::Pallet::clamp_limits`.629// IMPORTANT: When adding/removing fields from this struct - don't forget to also630#[derive(631	Encode,632	Decode,633	Debug,634	Default,635	Clone,636	PartialEq,637	TypeInfo,638	MaxEncodedLen,639	Serialize,640	Deserialize,641)]642// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.643// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.644// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.645pub struct CollectionLimits {646	/// How many tokens can a user have on one account.647	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].648	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].649	pub account_token_ownership_limit: Option<u32>,650651	/// How many bytes of data are available for sponsorship.652	/// * Default - [`CUSTOM_DATA_LIMIT`].653	/// * Limit - [`CUSTOM_DATA_LIMIT`].654	pub sponsored_data_size: Option<u32>,655656	// FIXME should we delete this or repurpose it?657	/// Times in how many blocks we sponsor data.658	///659	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.660	///661	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).662	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].663	///664	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]665	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,666	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]667668	/// How many tokens can be mined into this collection.669	///670	/// * Default - [`COLLECTION_TOKEN_LIMIT`].671	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].672	pub token_limit: Option<u32>,673674	/// Timeouts for transfer sponsoring.675	///676	/// * Default677	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]678	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]679	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]680	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].681	pub sponsor_transfer_timeout: Option<u32>,682683	/// Timeout for sponsoring an approval in passed blocks.684	///685	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].686	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].687	pub sponsor_approve_timeout: Option<u32>,688689	/// Whether the collection owner of the collection can send tokens (which belong to other users).690	///691	/// * Default - **false**.692	pub owner_can_transfer: Option<bool>,693694	/// Can the collection owner burn other people's tokens.695	///696	/// * Default - **true**.697	pub owner_can_destroy: Option<bool>,698699	/// Is it possible to send tokens from this collection between users.700	///701	/// * Default - **true**.702	pub transfers_enabled: Option<bool>,703}704705impl CollectionLimits {706	pub fn with_default_limits(collection_type: CollectionMode) -> Self {707		CollectionLimits {708			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),709			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),710			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),711			token_limit: Some(COLLECTION_TOKEN_LIMIT),712			sponsor_transfer_timeout: match collection_type {713				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),714				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),715				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),716			},717			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),718			owner_can_transfer: Some(false),719			owner_can_destroy: Some(true),720			transfers_enabled: Some(true),721		}722	}723724	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).725	pub fn account_token_ownership_limit(&self) -> u32 {726		self.account_token_ownership_limit727			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)728			.min(MAX_TOKEN_OWNERSHIP)729	}730731	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).732	pub fn sponsored_data_size(&self) -> u32 {733		self.sponsored_data_size734			.unwrap_or(CUSTOM_DATA_LIMIT)735			.min(CUSTOM_DATA_LIMIT)736	}737738	/// Get effective value for [`token_limit`](self.token_limit).739	pub fn token_limit(&self) -> u32 {740		self.token_limit741			.unwrap_or(COLLECTION_TOKEN_LIMIT)742			.min(COLLECTION_TOKEN_LIMIT)743	}744745	// TODO: may be replace u32 to mode?746	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).747	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {748		self.sponsor_transfer_timeout749			.unwrap_or(default)750			.min(MAX_SPONSOR_TIMEOUT)751	}752753	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).754	pub fn sponsor_approve_timeout(&self) -> u32 {755		self.sponsor_approve_timeout756			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)757			.min(MAX_SPONSOR_TIMEOUT)758	}759760	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).761	pub fn owner_can_transfer(&self) -> bool {762		self.owner_can_transfer.unwrap_or(false)763	}764765	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).766	pub fn owner_can_transfer_instaled(&self) -> bool {767		self.owner_can_transfer.is_some()768	}769770	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).771	pub fn owner_can_destroy(&self) -> bool {772		self.owner_can_destroy.unwrap_or(true)773	}774775	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).776	pub fn transfers_enabled(&self) -> bool {777		self.transfers_enabled.unwrap_or(true)778	}779780	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).781	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {782		match self783			.sponsored_data_rate_limit784			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)785		{786			SponsoringRateLimit::SponsoringDisabled => None,787			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),788		}789	}790}791792/// Permissions on certain operations within a collection.793///794/// Some fields are wrapped in [`Option`], where `None` means chain default.795///796/// Update with `pallet_common::Pallet::clamp_permissions`.797#[derive(798	Encode,799	Decode,800	Debug,801	Default,802	Clone,803	PartialEq,804	TypeInfo,805	MaxEncodedLen,806	Serialize,807	Deserialize,808)]809// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.810// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.811pub struct CollectionPermissions {812	/// Access mode.813	///814	/// * Default - [`AccessMode::Normal`].815	pub access: Option<AccessMode>,816817	/// Minting allowance.818	///819	/// * Default - **false**.820	pub mint_mode: Option<bool>,821822	/// Permissions for nesting.823	///824	/// * Default825	///   - `token_owner` - **false**826	///   - `collection_admin` - **false**827	///   - `restricted` - **None**828	pub nesting: Option<NestingPermissions>,829}830831impl CollectionPermissions {832	/// Get effective value for [`access`](self.access).833	pub fn access(&self) -> AccessMode {834		self.access.unwrap_or(AccessMode::Normal)835	}836837	/// Get effective value for [`mint_mode`](self.mint_mode).838	pub fn mint_mode(&self) -> bool {839		self.mint_mode.unwrap_or(false)840	}841842	/// Get effective value for [`nesting`](self.nesting).843	pub fn nesting(&self) -> &NestingPermissions {844		static DEFAULT: NestingPermissions = NestingPermissions {845			token_owner: false,846			collection_admin: false,847			restricted: None,848			#[cfg(feature = "runtime-benchmarks")]849			permissive: false,850		};851		self.nesting.as_ref().unwrap_or(&DEFAULT)852	}853}854855/// Inner set for collections allowed to nest.856type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;857858/// Wraper for collections set allowing nest.859#[derive(860	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,861)]862#[derivative(Debug)]863pub struct OwnerRestrictedSet(864	#[serde(with = "bounded::set_serde")]865	#[derivative(Debug(format_with = "bounded::set_debug"))]866	pub OwnerRestrictedSetInner,867);868869impl OwnerRestrictedSet {870	/// Create new set.871	pub fn new() -> Self {872		Self(Default::default())873	}874}875impl Default for OwnerRestrictedSet {876	fn default() -> Self {877		Self::new()878	}879}880impl core::ops::Deref for OwnerRestrictedSet {881	type Target = OwnerRestrictedSetInner;882	fn deref(&self) -> &Self::Target {883		&self.0884	}885}886impl core::ops::DerefMut for OwnerRestrictedSet {887	fn deref_mut(&mut self) -> &mut Self::Target {888		&mut self.0889	}890}891892impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {893	type Error = ();894895	fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {896		Ok(Self(value.try_into()?))897	}898}899900/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.901#[derive(902	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,903)]904#[derivative(Debug)]905pub struct NestingPermissions {906	/// Owner of token can nest tokens under it.907	pub token_owner: bool,908	/// Admin of token collection can nest tokens under token.909	pub collection_admin: bool,910	/// If set - only tokens from specified collections can be nested.911	pub restricted: Option<OwnerRestrictedSet>,912913	#[cfg(feature = "runtime-benchmarks")]914	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.915	pub permissive: bool,916}917918/// Enum denominating how often can sponsoring occur if it is enabled.919///920/// Used for [`collection limits`](CollectionLimits).921#[derive(922	Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,923)]924pub enum SponsoringRateLimit {925	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions926	SponsoringDisabled,927	/// Once per how many blocks can sponsorship of a transaction type occur928	Blocks(u32),929}930931/// Data used to describe an NFT at creation.932#[derive(933	Encode,934	Decode,935	MaxEncodedLen,936	Default,937	PartialEq,938	Clone,939	Derivative,940	TypeInfo,941	Serialize,942	Deserialize,943)]944#[derivative(Debug)]945pub struct CreateNftData {946	/// Key-value pairs used to describe the token as metadata947	#[serde(with = "bounded::vec_serde")]948	#[derivative(Debug(format_with = "bounded::vec_debug"))]949	/// Properties that wil be assignet to created item.950	pub properties: CollectionPropertiesVec,951}952953/// Data used to describe a Fungible token at creation.954#[derive(955	Encode,956	Decode,957	MaxEncodedLen,958	Default,959	Debug,960	Clone,961	PartialEq,962	TypeInfo,963	Serialize,964	Deserialize,965)]966pub struct CreateFungibleData {967	/// Number of fungible coins minted968	pub value: u128,969}970971/// Data used to describe a Refungible token at creation.972#[derive(973	Encode,974	Decode,975	MaxEncodedLen,976	Default,977	PartialEq,978	Clone,979	Derivative,980	TypeInfo,981	Serialize,982	Deserialize,983)]984#[derivative(Debug)]985pub struct CreateReFungibleData {986	/// Number of pieces the RFT is split into987	pub pieces: u128,988989	/// Key-value pairs used to describe the token as metadata990	#[serde(with = "bounded::vec_serde")]991	#[derivative(Debug(format_with = "bounded::vec_debug"))]992	pub properties: CollectionPropertiesVec,993}994995// TODO: remove this.996#[derive(997	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,998)]999pub enum MetaUpdatePermission {1000	ItemOwner,1001	Admin,1002	None,1003}10041005/// Enum holding data used for creation of all three item types.1006/// Unified data for create item.1007#[derive(1008	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1009)]1010pub enum CreateItemData {1011	/// Data for create NFT.1012	NFT(CreateNftData),1013	/// Data for create Fungible item.1014	Fungible(CreateFungibleData),1015	/// Data for create ReFungible item.1016	ReFungible(CreateReFungibleData),1017}10181019/// Extended data for create NFT.1020#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1021#[derivative(Debug)]1022pub struct CreateNftExData<CrossAccountId> {1023	/// Properties that wil be assignet to created item.1024	#[derivative(Debug(format_with = "bounded::vec_debug"))]1025	pub properties: CollectionPropertiesVec,10261027	/// Owner of creating item.1028	pub owner: CrossAccountId,1029}10301031/// Extended data for create ReFungible item.1032#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1033#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1034pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1035	#[derivative(Debug(format_with = "bounded::map_debug"))]1036	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1037	#[derivative(Debug(format_with = "bounded::vec_debug"))]1038	pub properties: CollectionPropertiesVec,1039}10401041/// Extended data for create ReFungible item.1042#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1043#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1044pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1045	pub user: CrossAccountId,1046	pub pieces: u128,1047	#[derivative(Debug(format_with = "bounded::vec_debug"))]1048	pub properties: CollectionPropertiesVec,1049}10501051/// Unified extended data for creating item.1052#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1053#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1054pub enum CreateItemExData<CrossAccountId> {1055	/// Extended data for create NFT.1056	NFT(1057		#[derivative(Debug(format_with = "bounded::vec_debug"))]1058		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1059	),10601061	/// Extended data for create Fungible item.1062	Fungible(1063		#[derivative(Debug(format_with = "bounded::map_debug"))]1064		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1065	),10661067	/// Extended data for create ReFungible item in case of1068	/// many tokens, each may have only one owner1069	RefungibleMultipleItems(1070		#[derivative(Debug(format_with = "bounded::vec_debug"))]1071		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1072	),10731074	/// Extended data for create ReFungible item in case of1075	/// single token, which may have many owners1076	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1077}10781079impl From<CreateNftData> for CreateItemData {1080	fn from(item: CreateNftData) -> Self {1081		CreateItemData::NFT(item)1082	}1083}10841085impl From<CreateReFungibleData> for CreateItemData {1086	fn from(item: CreateReFungibleData) -> Self {1087		CreateItemData::ReFungible(item)1088	}1089}10901091impl From<CreateFungibleData> for CreateItemData {1092	fn from(item: CreateFungibleData) -> Self {1093		CreateItemData::Fungible(item)1094	}1095}10961097/// Token's address, dictated by its collection and token IDs.1098#[derive(1099	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1100)]1101// todo possibly rename to be used generally as an address pair1102pub struct TokenChild {1103	/// Token id.1104	pub token: TokenId,11051106	/// Collection id.1107	pub collection: CollectionId,1108}11091110/// Collection statistics.1111#[derive(1112	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1113)]1114pub struct CollectionStats {1115	/// Number of created items.1116	pub created: u32,11171118	/// Number of burned items.1119	pub destroyed: u32,11201121	/// Number of current items.1122	pub alive: u32,1123}11241125/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1126#[derive(Encode, Decode, Clone, Debug)]1127#[cfg_attr(feature = "std", derive(PartialEq))]1128pub struct PhantomType<T>(core::marker::PhantomData<T>);11291130impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1131	type Identity = PhantomType<T>;11321133	fn type_info() -> scale_info::Type {1134		use scale_info::{1135			build::{FieldsBuilder, UnnamedFields},1136			form::MetaForm,1137			type_params, Path, Type,1138		};1139		Type::builder()1140			.path(Path::new("up_data_structs", "PhantomType"))1141			.type_params(type_params!(T))1142			.composite(1143				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1144			)1145	}1146}1147impl<T> MaxEncodedLen for PhantomType<T> {1148	fn max_encoded_len() -> usize {1149		01150	}1151}11521153/// Bounded vector of bytes.1154pub type BoundedBytes<S> = BoundedVec<u8, S>;11551156/// Extra properties for external collections.1157pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;11581159/// Property key.1160pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;11611162/// Property value.1163pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;11641165/// Property permission.1166#[derive(1167	Encode,1168	Decode,1169	TypeInfo,1170	Debug,1171	MaxEncodedLen,1172	PartialEq,1173	Clone,1174	Default,1175	Serialize,1176	Deserialize,1177)]1178pub struct PropertyPermission {1179	/// Permission to change the property and property permission.1180	///1181	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1182	pub mutable: bool,11831184	/// Change permission for the collection administrator.1185	pub collection_admin: bool,11861187	/// Permission to change the property for the owner of the token.1188	pub token_owner: bool,1189}11901191impl PropertyPermission {1192	/// Creates mutable property permission but changes restricted for collection admin and token owner.1193	pub fn none() -> Self {1194		Self {1195			mutable: true,1196			collection_admin: false,1197			token_owner: false,1198		}1199	}1200}12011202/// Property is simpl key-value record.1203#[derive(1204	Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1205)]1206pub struct Property {1207	/// Property key.1208	#[serde(with = "bounded::vec_serde")]1209	pub key: PropertyKey,12101211	/// Property value.1212	#[serde(with = "bounded::vec_serde")]1213	pub value: PropertyValue,1214}12151216impl From<Property> for (PropertyKey, PropertyValue) {1217	fn from(value: Property) -> Self {1218		(value.key, value.value)1219	}1220}12211222/// Record for proprty key permission.1223#[derive(1224	Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1225)]1226pub struct PropertyKeyPermission {1227	/// Key.1228	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1229	pub key: PropertyKey,12301231	/// Permission.1232	pub permission: PropertyPermission,1233}12341235impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1236	fn from(value: PropertyKeyPermission) -> Self {1237		(value.key, value.permission)1238	}1239}12401241/// Errors for properties actions.1242#[derive(Debug)]1243pub enum PropertiesError {1244	/// The space allocated for properties has run out.1245	///1246	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1247	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1248	NoSpaceForProperty,12491250	/// The property limit has been reached.1251	///1252	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1253	PropertyLimitReached,12541255	/// Property key contains not allowed character.1256	InvalidCharacterInPropertyKey,12571258	/// Property key length is too long.1259	///1260	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1261	PropertyKeyIsTooLong,12621263	/// Property key is empty.1264	EmptyPropertyKey,1265}12661267/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1268#[derive(Debug)]1269pub enum TokenOwnerError {1270	NotFound,1271	MultipleOwners,1272}12731274/// Marker for scope of property.1275///1276/// Scoped property can't be changed by user. Used for external collections.1277#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1278pub enum PropertyScope {1279	None,1280	Rmrk,1281}12821283impl PropertyScope {1284	pub fn prefix(&self) -> &'static [u8] {1285		match self {1286			Self::None => b"",1287			Self::Rmrk => b"rmrk:",1288		}1289	}1290	/// Apply scope to property key.1291	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1292		let prefix = self.prefix();1293		if prefix == b"" {1294			return Ok(key);1295		}1296		[prefix, key.as_slice()]1297			.concat()1298			.try_into()1299			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1300	}1301}13021303/// Trait for operate with properties.1304pub trait TrySetProperty: Sized {1305	type Value;13061307	/// Try to set property with scope.1308	fn try_scoped_set(1309		&mut self,1310		scope: PropertyScope,1311		key: PropertyKey,1312		value: Self::Value,1313	) -> Result<Option<Self::Value>, PropertiesError>;13141315	/// Try to set property with scope from iterator.1316	fn try_scoped_set_from_iter<I, KV>(1317		&mut self,1318		scope: PropertyScope,1319		iter: I,1320	) -> Result<(), PropertiesError>1321	where1322		I: Iterator<Item = KV>,1323		KV: Into<(PropertyKey, Self::Value)>,1324	{1325		for kv in iter {1326			let (key, value) = kv.into();1327			self.try_scoped_set(scope, key, value)?;1328		}13291330		Ok(())1331	}13321333	/// Try to set property.1334	fn try_set(1335		&mut self,1336		key: PropertyKey,1337		value: Self::Value,1338	) -> Result<Option<Self::Value>, PropertiesError> {1339		self.try_scoped_set(PropertyScope::None, key, value)1340	}13411342	/// Try to set property from iterator.1343	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1344	where1345		I: Iterator<Item = KV>,1346		KV: Into<(PropertyKey, Self::Value)>,1347	{1348		self.try_scoped_set_from_iter(PropertyScope::None, iter)1349	}1350}13511352/// Wrapped map for storing properties.1353#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1354#[derivative(Default(bound = ""))]1355pub struct PropertiesMap<Value>(1356	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1357);13581359impl<Value> PropertiesMap<Value> {1360	/// Create new property map.1361	pub fn new() -> Self {1362		Self(BoundedBTreeMap::new())1363	}13641365	/// Remove property from map.1366	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1367		Self::check_property_key(key)?;13681369		Ok(self.0.remove(key))1370	}13711372	/// Get property with appropriate key from map.1373	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1374		self.0.get(key)1375	}13761377	/// Check if map contains key.1378	pub fn contains_key(&self, key: &PropertyKey) -> bool {1379		self.0.contains_key(key)1380	}13811382	/// Check if map contains key with key validation.1383	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1384		if key.is_empty() {1385			return Err(PropertiesError::EmptyPropertyKey);1386		}13871388		for byte in key.as_slice().iter() {1389			let byte = *byte;13901391			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1392				return Err(PropertiesError::InvalidCharacterInPropertyKey);1393			}1394		}13951396		Ok(())1397	}13981399	pub fn values(&self) -> impl Iterator<Item = &Value> {1400		self.0.values()1401	}14021403	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1404		self.0.iter()1405	}1406}14071408impl<Value> IntoIterator for PropertiesMap<Value> {1409	type Item = (PropertyKey, Value);1410	type IntoIter = <1411		BoundedBTreeMap<1412			PropertyKey,1413			Value,1414			ConstU32<MAX_PROPERTIES_PER_ITEM>1415		> as IntoIterator1416	>::IntoIter;14171418	fn into_iter(self) -> Self::IntoIter {1419		self.0.into_iter()1420	}1421}14221423impl<Value> TrySetProperty for PropertiesMap<Value> {1424	type Value = Value;14251426	fn try_scoped_set(1427		&mut self,1428		scope: PropertyScope,1429		key: PropertyKey,1430		value: Self::Value,1431	) -> Result<Option<Self::Value>, PropertiesError> {1432		Self::check_property_key(&key)?;14331434		let key = scope.apply(key)?;1435		self.01436			.try_insert(key, value)1437			.map_err(|_| PropertiesError::PropertyLimitReached)1438	}1439}14401441/// Alias for property permissions map.1442pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14431444fn slice_size(data: &[u8]) -> u32 {1445	scoped_slice_size(PropertyScope::None, data)1446}1447fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1448	use parity_scale_codec::Compact;1449	let prefix = scope.prefix();1450	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321451		+ data.len() as u321452		+ prefix.len() as u321453}14541455/// Wrapper for properties map with consumed space control.1456#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1457pub struct Properties<const S: u32> {1458	map: PropertiesMap<PropertyValue>,1459	consumed_space: u32,1460	// May be not zero, previously served as a current S generic1461	_reserved: u32,1462}14631464impl<const S: u32> MaxEncodedLen for Properties<S> {1465	fn max_encoded_len() -> usize {1466		// len of map + len of consumed_space + len of space_limit1467		u32::max_encoded_len() * 3 + S as usize1468	}1469}14701471impl<const S: u32> Default for Properties<S> {1472	fn default() -> Self {1473		Self::new()1474	}1475}14761477impl<const S: u32> Properties<S> {1478	/// Create new properies container.1479	pub fn new() -> Self {1480		Self {1481			map: PropertiesMap::new(),1482			consumed_space: 0,1483			_reserved: 0,1484		}1485	}14861487	/// Remove propery with appropiate key.1488	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1489		let value = self.map.remove(key)?;14901491		if let Some(ref value) = value {1492			let kv_len = slice_size(key) + slice_size(value);1493			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1494		}14951496		Ok(value)1497	}14981499	/// Get property with appropriate key.1500	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1501		self.map.get(key)1502	}15031504	/// Recomputes the consumed space for the current properties state.1505	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1506	pub fn recompute_consumed_space(&mut self) {1507		self.consumed_space = self1508			.map1509			.iter()1510			.map(|(key, value)| slice_size(key) + slice_size(value))1511			.sum();1512	}1513}15141515impl<const S: u32> IntoIterator for Properties<S> {1516	type Item = (PropertyKey, PropertyValue);1517	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15181519	fn into_iter(self) -> Self::IntoIter {1520		self.map.into_iter()1521	}1522}15231524impl<const S: u32> TrySetProperty for Properties<S> {1525	type Value = PropertyValue;15261527	fn try_scoped_set(1528		&mut self,1529		scope: PropertyScope,1530		key: PropertyKey,1531		value: Self::Value,1532	) -> Result<Option<Self::Value>, PropertiesError> {1533		let key_size = scoped_slice_size(scope, &key);1534		let value_size = slice_size(&value);15351536		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1537		{1538			return Err(PropertiesError::NoSpaceForProperty);1539		}15401541		let old_value = self.map.try_scoped_set(scope, key, value)?;15421543		if let Some(old_value) = old_value.as_ref() {1544			let old_value_size = slice_size(old_value);1545			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1546		} else {1547			self.consumed_space += key_size + value_size;1548		}15491550		Ok(old_value)1551	}1552}15531554pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1555pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;
after · primitives/data-structs/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24	convert::{TryFrom, TryInto},25	fmt,26	ops::Deref,27};2829use bondrewd::Bitfields;30use derivative::Derivative;31use evm_coder::AbiCoderFlags;32use frame_support::{33	storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},34	traits::ConstU32,35	BoundedVec,36};37use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use scale_info::TypeInfo;39use serde::{Deserialize, Serialize};40use sp_core::U256;41use sp_runtime::{sp_std::prelude::Vec, ArithmeticError};42use sp_std::collections::btree_set::BTreeSet;4344mod bondrewd_codec;45mod bounded;46pub mod budget;47pub mod mapping;48mod migration;4950/// Maximum of decimal points.51pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5253/// Maximum pieces for refungible token.54pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;55pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5657/// Maximum tokens for user.58pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {59	100_000_00060} else {61	1062};6364/// Maximum for collections can be created.65pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {66	100_00067} else {68	1069};7071/// Maximum for various custom data of token.72pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73	204874} else {75	1076};7778/// Maximum admins per collection.79pub const COLLECTION_ADMINS_LIMIT: u32 = 5;8081/// Maximum tokens per collection.82pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8384/// Maximum tokens per account.85pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {86	100_000_00087} else {88	1089};9091/// Default timeout for transfer sponsoring NFT item.92pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;93/// Default timeout for transfer sponsoring fungible item.94pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;95/// Default timeout for transfer sponsoring refungible item.96pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9798/// Default timeout for sponsored approving.99pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;100101// Schema limits102pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;104pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;105106// TODO: not used. Delete?107pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;108109/// Maximal length of a collection name.110pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;111112/// Maximal length of a collection description.113pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;114115/// Maximal length of a token prefix.116pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;117118/// Maximal length of a property key.119pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;120121/// Maximal length of a property value.122pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;123124/// A maximum number of token properties.125pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;126127/// Maximal lenght of extended property value.128pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;129130/// Maximum size for all collection properties.131pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;132133/// Maximum size of all token properties.134pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;135136/// How much items can be created per single137/// create_many call.138pub const MAX_ITEMS_PER_BATCH: u32 = 120;139140/// Used for limit bounded types of token custom data.141pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;142143/// Collection id.144#[derive(145	Encode,146	Decode,147	PartialEq,148	Eq,149	PartialOrd,150	Ord,151	Clone,152	Copy,153	Debug,154	Default,155	TypeInfo,156	MaxEncodedLen,157	Serialize,158	Deserialize,159)]160pub struct CollectionId(pub u32);161impl EncodeLike<u32> for CollectionId {}162impl EncodeLike<CollectionId> for u32 {}163164impl From<u32> for CollectionId {165	fn from(value: u32) -> Self {166		Self(value)167	}168}169170impl Deref for CollectionId {171	type Target = u32;172173	fn deref(&self) -> &Self::Target {174		&self.0175	}176}177178/// Token id.179#[derive(180	Encode,181	Decode,182	PartialEq,183	Eq,184	PartialOrd,185	Ord,186	Clone,187	Copy,188	Debug,189	Default,190	TypeInfo,191	MaxEncodedLen,192	Serialize,193	Deserialize,194)]195pub struct TokenId(pub u32);196impl EncodeLike<u32> for TokenId {}197impl EncodeLike<TokenId> for u32 {}198199impl TokenId {200	/// Try to get next token id.201	///202	/// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.203	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {204		self.0205			.checked_add(1)206			.ok_or(ArithmeticError::Overflow)207			.map(Self)208	}209}210211impl From<TokenId> for U256 {212	fn from(t: TokenId) -> Self {213		t.0.into()214	}215}216217impl TryFrom<U256> for TokenId {218	type Error = &'static str;219220	fn try_from(value: U256) -> Result<Self, Self::Error> {221		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))222	}223}224225/// Token data.226#[struct_versioning::versioned(version = 2, upper)]227#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]228pub struct TokenData<CrossAccountId> {229	/// Properties of token.230	pub properties: Vec<Property>,231232	/// Token owner.233	pub owner: Option<CrossAccountId>,234235	/// Token pieces.236	#[version(2.., upper(0))]237	pub pieces: u128,238}239240// TODO: unused type241pub struct OverflowError;242impl From<OverflowError> for &'static str {243	fn from(_: OverflowError) -> Self {244		"overflow occured"245	}246}247248/// Alias for decimal points type.249pub type DecimalPoints = u8;250251/// Collection mode.252///253/// Collection can represent various types of tokens.254/// Each collection can contain only one type of tokens at a time.255/// This type helps to understand which tokens the collection contains.256#[derive(257	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,258)]259pub enum CollectionMode {260	/// Non fungible tokens.261	NFT,262	/// Fungible tokens.263	Fungible(DecimalPoints),264	/// Refungible tokens.265	ReFungible,266}267268impl CollectionMode {269	/// Get collection mod as number.270	pub fn id(&self) -> u8 {271		match self {272			CollectionMode::NFT => 1,273			CollectionMode::Fungible(_) => 2,274			CollectionMode::ReFungible => 3,275		}276	}277}278279// TODO: unused trait280pub trait SponsoringResolve<AccountId, Call> {281	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;282}283284/// Access mode for some token operations.285#[derive(286	Encode,287	Decode,288	Eq,289	Debug,290	Clone,291	Copy,292	PartialEq,293	TypeInfo,294	MaxEncodedLen,295	Serialize,296	Deserialize,297)]298pub enum AccessMode {299	/// Access grant for owner and admins. Used as default.300	Normal,301	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.302	AllowList,303}304impl Default for AccessMode {305	fn default() -> Self {306		Self::Normal307	}308}309310// TODO: remove in future.311#[derive(312	Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,313)]314pub enum SchemaVersion {315	ImageURL,316	Unique,317}318impl Default for SchemaVersion {319	fn default() -> Self {320		Self::ImageURL321	}322}323324// TODO: unused type325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]326pub struct Ownership<AccountId> {327	pub owner: AccountId,328	pub fraction: u128,329}330331/// The state of collection sponsorship.332#[derive(333	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,334)]335pub enum SponsorshipState<AccountId> {336	/// The fees are applied to the transaction sender.337	Disabled,338	/// The sponsor is under consideration. Until the sponsor gives his consent,339	/// the fee will still be charged to sender.340	Unconfirmed(AccountId),341	/// Transactions are sponsored by specified account.342	Confirmed(AccountId),343}344345impl<AccountId> SponsorshipState<AccountId> {346	/// Get a sponsor of the collection who has confirmed his status.347	pub fn sponsor(&self) -> Option<&AccountId> {348		match self {349			Self::Confirmed(sponsor) => Some(sponsor),350			_ => None,351		}352	}353354	/// Get a sponsor of the collection who has pending or confirmed status.355	pub fn pending_sponsor(&self) -> Option<&AccountId> {356		match self {357			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),358			_ => None,359		}360	}361362	/// Whether the sponsorship is confirmed.363	pub fn confirmed(&self) -> bool {364		matches!(self, Self::Confirmed(_))365	}366}367368impl<T> Default for SponsorshipState<T> {369	fn default() -> Self {370		Self::Disabled371	}372}373374pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;375pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;376pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;377378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]379#[bondrewd(enforce_bytes = 1)]380pub struct CollectionFlags {381	/// Reserved flag382	#[bondrewd(bits = "0..1")]383	pub reserved_0: bool,384	/// Supports ERC721Metadata385	#[bondrewd(bits = "1..2")]386	pub erc721metadata: bool,387	/// External collections can't be managed using `unique` api388	#[bondrewd(bits = "7..8")]389	pub external: bool,390	/// Reserved flags391	#[bondrewd(bits = "2..7")]392	pub reserved: u8,393}394bondrewd_codec!(CollectionFlags);395396impl CollectionFlags {397	pub fn is_allowed_for_user(self) -> bool {398		!self.reserved_0 && !self.external && self.reserved == 0399	}400}401402/// Base structure for represent collection.403///404/// Used to provide basic functionality for all types of collections.405///406/// #### Note407/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).408#[struct_versioning::versioned(version = 2, upper)]409#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]410pub struct Collection<AccountId> {411	/// Collection owner account.412	pub owner: AccountId,413414	/// Collection mode.415	pub mode: CollectionMode,416417	/// Access mode.418	#[version(..2)]419	pub access: AccessMode,420421	/// Collection name.422	pub name: CollectionName,423424	/// Collection description.425	pub description: CollectionDescription,426427	/// Token prefix.428	pub token_prefix: CollectionTokenPrefix,429430	#[version(..2)]431	pub mint_mode: bool,432433	#[version(..2)]434	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,435436	#[version(..2)]437	pub schema_version: SchemaVersion,438439	/// The state of sponsorship of the collection.440	pub sponsorship: SponsorshipState<AccountId>,441442	/// Collection limits.443	pub limits: CollectionLimits,444445	/// Collection permissions.446	#[version(2.., upper(Default::default()))]447	pub permissions: CollectionPermissions,448449	#[version(2.., upper(Default::default()))]450	pub flags: CollectionFlags,451452	#[version(..2)]453	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,454455	#[version(..2)]456	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,457458	#[version(..2)]459	pub meta_update_permission: MetaUpdatePermission,460}461462#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]463pub struct RpcCollectionFlags {464	/// Collection supports ERC721Metadata.465	pub erc721metadata: bool,466}467468/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).469#[struct_versioning::versioned(version = 2, upper)]470#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]471pub struct RpcCollection<AccountId> {472	/// Collection owner account.473	pub owner: AccountId,474475	/// Collection mode.476	pub mode: CollectionMode,477478	/// Collection name.479	pub name: Vec<u16>,480481	/// Collection description.482	pub description: Vec<u16>,483484	/// Token prefix.485	pub token_prefix: Vec<u8>,486487	/// The state of sponsorship of the collection.488	pub sponsorship: SponsorshipState<AccountId>,489490	/// Collection limits.491	pub limits: CollectionLimits,492493	/// Collection permissions.494	pub permissions: CollectionPermissions,495496	/// Token property permissions.497	pub token_property_permissions: Vec<PropertyKeyPermission>,498499	/// Collection properties.500	pub properties: Vec<Property>,501502	/// Is collection read only.503	pub read_only: bool,504505	/// Extra collection flags506	#[version(2.., upper(RpcCollectionFlags {erc721metadata: false}))]507	pub flags: RpcCollectionFlags,508}509510impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {511	fn from(value: CollectionVersion1<AccountId>) -> Self {512		let CollectionVersion1 {513			name,514			description,515			owner,516			mode,517			access,518			token_prefix,519			mint_mode,520			sponsorship,521			limits,522			..523		} = value;524525		RpcCollection {526			name: name.into_inner(),527			description: description.into_inner(),528			owner,529			mode,530			token_prefix: token_prefix.into_inner(),531			sponsorship,532			limits,533			permissions: CollectionPermissions {534				access: Some(access),535				mint_mode: Some(mint_mode),536				nesting: None,537			},538			token_property_permissions: Vec::default(),539			properties: Vec::default(),540			read_only: true,541542			flags: RpcCollectionFlags {543				erc721metadata: false,544			},545		}546	}547}548549pub struct RawEncoded(Vec<u8>);550551impl parity_scale_codec::Decode for RawEncoded {552	fn decode<I: parity_scale_codec::Input>(553		input: &mut I,554	) -> Result<Self, parity_scale_codec::Error> {555		let mut out = Vec::new();556		while let Ok(v) = input.read_byte() {557			out.push(v);558		}559		Ok(Self(out))560	}561}562563impl Deref for RawEncoded {564	type Target = Vec<u8>;565566	fn deref(&self) -> &Self::Target {567		&self.0568	}569}570571/// Data used for create collection.572///573/// All fields are wrapped in [`Option`], where `None` means chain default.574#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]575#[derivative(Debug, Default(bound = ""))]576pub struct CreateCollectionData<CrossAccountId> {577	/// Collection mode.578	#[derivative(Default(value = "CollectionMode::NFT"))]579	pub mode: CollectionMode,580581	/// Access mode.582	pub access: Option<AccessMode>,583584	/// Collection name.585	pub name: CollectionName,586587	/// Collection description.588	pub description: CollectionDescription,589590	/// Token prefix.591	pub token_prefix: CollectionTokenPrefix,592593	/// Collection limits.594	pub limits: Option<CollectionLimits>,595596	/// Collection permissions.597	pub permissions: Option<CollectionPermissions>,598599	/// Token property permissions.600	pub token_property_permissions: CollectionPropertiesPermissionsVec,601602	/// Collection properties.603	pub properties: CollectionPropertiesVec,604605	pub admin_list: Vec<CrossAccountId>,606607	/// Pending collection sponsor.608	pub pending_sponsor: Option<CrossAccountId>,609610	pub flags: CollectionFlags,611}612613/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].614// TODO: maybe rename to PropertiesPermissionsVec615pub type CollectionPropertiesPermissionsVec =616	BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;617618/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].619pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;620621/// Limits and restrictions of a collection.622///623/// All fields are wrapped in [`Option`], where `None` means chain default.624///625/// Update with `pallet_common::Pallet::clamp_limits`.626// IMPORTANT: When adding/removing fields from this struct - don't forget to also627#[derive(628	Encode,629	Decode,630	Debug,631	Default,632	Clone,633	PartialEq,634	TypeInfo,635	MaxEncodedLen,636	Serialize,637	Deserialize,638)]639// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.640// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.641// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.642pub struct CollectionLimits {643	/// How many tokens can a user have on one account.644	/// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].645	/// * Limit - [`MAX_TOKEN_OWNERSHIP`].646	pub account_token_ownership_limit: Option<u32>,647648	/// How many bytes of data are available for sponsorship.649	/// * Default - [`CUSTOM_DATA_LIMIT`].650	/// * Limit - [`CUSTOM_DATA_LIMIT`].651	pub sponsored_data_size: Option<u32>,652653	// FIXME should we delete this or repurpose it?654	/// Times in how many blocks we sponsor data.655	///656	/// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.657	///658	/// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).659	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].660	///661	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]662	pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,663	/// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]664665	/// How many tokens can be mined into this collection.666	///667	/// * Default - [`COLLECTION_TOKEN_LIMIT`].668	/// * Limit - [`COLLECTION_TOKEN_LIMIT`].669	pub token_limit: Option<u32>,670671	/// Timeouts for transfer sponsoring.672	///673	/// * Default674	///   - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]675	///   - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]676	///   - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]677	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].678	pub sponsor_transfer_timeout: Option<u32>,679680	/// Timeout for sponsoring an approval in passed blocks.681	///682	/// * Default - [`SPONSOR_APPROVE_TIMEOUT`].683	/// * Limit - [`MAX_SPONSOR_TIMEOUT`].684	pub sponsor_approve_timeout: Option<u32>,685686	/// Whether the collection owner of the collection can send tokens (which belong to other users).687	///688	/// * Default - **false**.689	pub owner_can_transfer: Option<bool>,690691	/// Can the collection owner burn other people's tokens.692	///693	/// * Default - **true**.694	pub owner_can_destroy: Option<bool>,695696	/// Is it possible to send tokens from this collection between users.697	///698	/// * Default - **true**.699	pub transfers_enabled: Option<bool>,700}701702impl CollectionLimits {703	pub fn with_default_limits(collection_type: CollectionMode) -> Self {704		CollectionLimits {705			account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),706			sponsored_data_size: Some(CUSTOM_DATA_LIMIT),707			sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),708			token_limit: Some(COLLECTION_TOKEN_LIMIT),709			sponsor_transfer_timeout: match collection_type {710				CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),711				CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),712				CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),713			},714			sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),715			owner_can_transfer: Some(false),716			owner_can_destroy: Some(true),717			transfers_enabled: Some(true),718		}719	}720721	/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).722	pub fn account_token_ownership_limit(&self) -> u32 {723		self.account_token_ownership_limit724			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)725			.min(MAX_TOKEN_OWNERSHIP)726	}727728	/// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).729	pub fn sponsored_data_size(&self) -> u32 {730		self.sponsored_data_size731			.unwrap_or(CUSTOM_DATA_LIMIT)732			.min(CUSTOM_DATA_LIMIT)733	}734735	/// Get effective value for [`token_limit`](self.token_limit).736	pub fn token_limit(&self) -> u32 {737		self.token_limit738			.unwrap_or(COLLECTION_TOKEN_LIMIT)739			.min(COLLECTION_TOKEN_LIMIT)740	}741742	// TODO: may be replace u32 to mode?743	/// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).744	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {745		self.sponsor_transfer_timeout746			.unwrap_or(default)747			.min(MAX_SPONSOR_TIMEOUT)748	}749750	/// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).751	pub fn sponsor_approve_timeout(&self) -> u32 {752		self.sponsor_approve_timeout753			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)754			.min(MAX_SPONSOR_TIMEOUT)755	}756757	/// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).758	pub fn owner_can_transfer(&self) -> bool {759		self.owner_can_transfer.unwrap_or(false)760	}761762	/// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).763	pub fn owner_can_transfer_instaled(&self) -> bool {764		self.owner_can_transfer.is_some()765	}766767	/// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).768	pub fn owner_can_destroy(&self) -> bool {769		self.owner_can_destroy.unwrap_or(true)770	}771772	/// Get effective value for [`transfers_enabled`](self.transfers_enabled).773	pub fn transfers_enabled(&self) -> bool {774		self.transfers_enabled.unwrap_or(true)775	}776777	/// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).778	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {779		match self780			.sponsored_data_rate_limit781			.unwrap_or(SponsoringRateLimit::SponsoringDisabled)782		{783			SponsoringRateLimit::SponsoringDisabled => None,784			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),785		}786	}787}788789/// Permissions on certain operations within a collection.790///791/// Some fields are wrapped in [`Option`], where `None` means chain default.792///793/// Update with `pallet_common::Pallet::clamp_permissions`.794#[derive(795	Encode,796	Decode,797	Debug,798	Default,799	Clone,800	PartialEq,801	TypeInfo,802	MaxEncodedLen,803	Serialize,804	Deserialize,805)]806// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.807// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.808pub struct CollectionPermissions {809	/// Access mode.810	///811	/// * Default - [`AccessMode::Normal`].812	pub access: Option<AccessMode>,813814	/// Minting allowance.815	///816	/// * Default - **false**.817	pub mint_mode: Option<bool>,818819	/// Permissions for nesting.820	///821	/// * Default822	///   - `token_owner` - **false**823	///   - `collection_admin` - **false**824	///   - `restricted` - **None**825	pub nesting: Option<NestingPermissions>,826}827828impl CollectionPermissions {829	/// Get effective value for [`access`](self.access).830	pub fn access(&self) -> AccessMode {831		self.access.unwrap_or(AccessMode::Normal)832	}833834	/// Get effective value for [`mint_mode`](self.mint_mode).835	pub fn mint_mode(&self) -> bool {836		self.mint_mode.unwrap_or(false)837	}838839	/// Get effective value for [`nesting`](self.nesting).840	pub fn nesting(&self) -> &NestingPermissions {841		static DEFAULT: NestingPermissions = NestingPermissions {842			token_owner: false,843			collection_admin: false,844			restricted: None,845			#[cfg(feature = "runtime-benchmarks")]846			permissive: false,847		};848		self.nesting.as_ref().unwrap_or(&DEFAULT)849	}850}851852/// Inner set for collections allowed to nest.853type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;854855/// Wraper for collections set allowing nest.856#[derive(857	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,858)]859#[derivative(Debug)]860pub struct OwnerRestrictedSet(861	#[serde(with = "bounded::set_serde")]862	#[derivative(Debug(format_with = "bounded::set_debug"))]863	pub OwnerRestrictedSetInner,864);865866impl OwnerRestrictedSet {867	/// Create new set.868	pub fn new() -> Self {869		Self(Default::default())870	}871}872impl Default for OwnerRestrictedSet {873	fn default() -> Self {874		Self::new()875	}876}877impl core::ops::Deref for OwnerRestrictedSet {878	type Target = OwnerRestrictedSetInner;879	fn deref(&self) -> &Self::Target {880		&self.0881	}882}883impl core::ops::DerefMut for OwnerRestrictedSet {884	fn deref_mut(&mut self) -> &mut Self::Target {885		&mut self.0886	}887}888889impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {890	type Error = ();891892	fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {893		Ok(Self(value.try_into()?))894	}895}896897/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.898#[derive(899	Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,900)]901#[derivative(Debug)]902pub struct NestingPermissions {903	/// Owner of token can nest tokens under it.904	pub token_owner: bool,905	/// Admin of token collection can nest tokens under token.906	pub collection_admin: bool,907	/// If set - only tokens from specified collections can be nested.908	pub restricted: Option<OwnerRestrictedSet>,909910	#[cfg(feature = "runtime-benchmarks")]911	/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.912	pub permissive: bool,913}914915/// Enum denominating how often can sponsoring occur if it is enabled.916///917/// Used for [`collection limits`](CollectionLimits).918#[derive(919	Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,920)]921pub enum SponsoringRateLimit {922	/// Sponsoring is disabled, and the collection sponsor will not pay for transactions923	SponsoringDisabled,924	/// Once per how many blocks can sponsorship of a transaction type occur925	Blocks(u32),926}927928/// Data used to describe an NFT at creation.929#[derive(930	Encode,931	Decode,932	MaxEncodedLen,933	Default,934	PartialEq,935	Clone,936	Derivative,937	TypeInfo,938	Serialize,939	Deserialize,940)]941#[derivative(Debug)]942pub struct CreateNftData {943	/// Key-value pairs used to describe the token as metadata944	#[serde(with = "bounded::vec_serde")]945	#[derivative(Debug(format_with = "bounded::vec_debug"))]946	/// Properties that wil be assignet to created item.947	pub properties: CollectionPropertiesVec,948}949950/// Data used to describe a Fungible token at creation.951#[derive(952	Encode,953	Decode,954	MaxEncodedLen,955	Default,956	Debug,957	Clone,958	PartialEq,959	TypeInfo,960	Serialize,961	Deserialize,962)]963pub struct CreateFungibleData {964	/// Number of fungible coins minted965	pub value: u128,966}967968/// Data used to describe a Refungible token at creation.969#[derive(970	Encode,971	Decode,972	MaxEncodedLen,973	Default,974	PartialEq,975	Clone,976	Derivative,977	TypeInfo,978	Serialize,979	Deserialize,980)]981#[derivative(Debug)]982pub struct CreateReFungibleData {983	/// Number of pieces the RFT is split into984	pub pieces: u128,985986	/// Key-value pairs used to describe the token as metadata987	#[serde(with = "bounded::vec_serde")]988	#[derivative(Debug(format_with = "bounded::vec_debug"))]989	pub properties: CollectionPropertiesVec,990}991992// TODO: remove this.993#[derive(994	Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,995)]996pub enum MetaUpdatePermission {997	ItemOwner,998	Admin,999	None,1000}10011002/// Enum holding data used for creation of all three item types.1003/// Unified data for create item.1004#[derive(1005	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1006)]1007pub enum CreateItemData {1008	/// Data for create NFT.1009	NFT(CreateNftData),1010	/// Data for create Fungible item.1011	Fungible(CreateFungibleData),1012	/// Data for create ReFungible item.1013	ReFungible(CreateReFungibleData),1014}10151016/// Extended data for create NFT.1017#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1018#[derivative(Debug)]1019pub struct CreateNftExData<CrossAccountId> {1020	/// Properties that wil be assignet to created item.1021	#[derivative(Debug(format_with = "bounded::vec_debug"))]1022	pub properties: CollectionPropertiesVec,10231024	/// Owner of creating item.1025	pub owner: CrossAccountId,1026}10271028/// Extended data for create ReFungible item.1029#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1030#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1031pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1032	#[derivative(Debug(format_with = "bounded::map_debug"))]1033	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1034	#[derivative(Debug(format_with = "bounded::vec_debug"))]1035	pub properties: CollectionPropertiesVec,1036}10371038/// Extended data for create ReFungible item.1039#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1040#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1041pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1042	pub user: CrossAccountId,1043	pub pieces: u128,1044	#[derivative(Debug(format_with = "bounded::vec_debug"))]1045	pub properties: CollectionPropertiesVec,1046}10471048/// Unified extended data for creating item.1049#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1050#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1051pub enum CreateItemExData<CrossAccountId> {1052	/// Extended data for create NFT.1053	NFT(1054		#[derivative(Debug(format_with = "bounded::vec_debug"))]1055		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1056	),10571058	/// Extended data for create Fungible item.1059	Fungible(1060		#[derivative(Debug(format_with = "bounded::map_debug"))]1061		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1062	),10631064	/// Extended data for create ReFungible item in case of1065	/// many tokens, each may have only one owner1066	RefungibleMultipleItems(1067		#[derivative(Debug(format_with = "bounded::vec_debug"))]1068		BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1069	),10701071	/// Extended data for create ReFungible item in case of1072	/// single token, which may have many owners1073	RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1074}10751076impl From<CreateNftData> for CreateItemData {1077	fn from(item: CreateNftData) -> Self {1078		CreateItemData::NFT(item)1079	}1080}10811082impl From<CreateReFungibleData> for CreateItemData {1083	fn from(item: CreateReFungibleData) -> Self {1084		CreateItemData::ReFungible(item)1085	}1086}10871088impl From<CreateFungibleData> for CreateItemData {1089	fn from(item: CreateFungibleData) -> Self {1090		CreateItemData::Fungible(item)1091	}1092}10931094/// Token's address, dictated by its collection and token IDs.1095#[derive(1096	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1097)]1098// todo possibly rename to be used generally as an address pair1099pub struct TokenChild {1100	/// Token id.1101	pub token: TokenId,11021103	/// Collection id.1104	pub collection: CollectionId,1105}11061107/// Collection statistics.1108#[derive(1109	Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1110)]1111pub struct CollectionStats {1112	/// Number of created items.1113	pub created: u32,11141115	/// Number of burned items.1116	pub destroyed: u32,11171118	/// Number of current items.1119	pub alive: u32,1120}11211122/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1123#[derive(Encode, Decode, Clone, Debug)]1124#[cfg_attr(feature = "std", derive(PartialEq))]1125pub struct PhantomType<T>(core::marker::PhantomData<T>);11261127impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1128	type Identity = PhantomType<T>;11291130	fn type_info() -> scale_info::Type {1131		use scale_info::{1132			build::{FieldsBuilder, UnnamedFields},1133			form::MetaForm,1134			type_params, Path, Type,1135		};1136		Type::builder()1137			.path(Path::new("up_data_structs", "PhantomType"))1138			.type_params(type_params!(T))1139			.composite(1140				<FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1141			)1142	}1143}1144impl<T> MaxEncodedLen for PhantomType<T> {1145	fn max_encoded_len() -> usize {1146		01147	}1148}11491150/// Bounded vector of bytes.1151pub type BoundedBytes<S> = BoundedVec<u8, S>;11521153/// Extra properties for external collections.1154pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;11551156/// Property key.1157pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;11581159/// Property value.1160pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;11611162/// Property permission.1163#[derive(1164	Encode,1165	Decode,1166	TypeInfo,1167	Debug,1168	MaxEncodedLen,1169	PartialEq,1170	Clone,1171	Default,1172	Serialize,1173	Deserialize,1174)]1175pub struct PropertyPermission {1176	/// Permission to change the property and property permission.1177	///1178	/// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1179	pub mutable: bool,11801181	/// Change permission for the collection administrator.1182	pub collection_admin: bool,11831184	/// Permission to change the property for the owner of the token.1185	pub token_owner: bool,1186}11871188impl PropertyPermission {1189	/// Creates mutable property permission but changes restricted for collection admin and token owner.1190	pub fn none() -> Self {1191		Self {1192			mutable: true,1193			collection_admin: false,1194			token_owner: false,1195		}1196	}1197}11981199/// Property is simpl key-value record.1200#[derive(1201	Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1202)]1203pub struct Property {1204	/// Property key.1205	#[serde(with = "bounded::vec_serde")]1206	pub key: PropertyKey,12071208	/// Property value.1209	#[serde(with = "bounded::vec_serde")]1210	pub value: PropertyValue,1211}12121213impl From<Property> for (PropertyKey, PropertyValue) {1214	fn from(value: Property) -> Self {1215		(value.key, value.value)1216	}1217}12181219/// Record for proprty key permission.1220#[derive(1221	Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1222)]1223pub struct PropertyKeyPermission {1224	/// Key.1225	#[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1226	pub key: PropertyKey,12271228	/// Permission.1229	pub permission: PropertyPermission,1230}12311232impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1233	fn from(value: PropertyKeyPermission) -> Self {1234		(value.key, value.permission)1235	}1236}12371238/// Errors for properties actions.1239#[derive(Debug)]1240pub enum PropertiesError {1241	/// The space allocated for properties has run out.1242	///1243	/// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1244	/// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1245	NoSpaceForProperty,12461247	/// The property limit has been reached.1248	///1249	/// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1250	PropertyLimitReached,12511252	/// Property key contains not allowed character.1253	InvalidCharacterInPropertyKey,12541255	/// Property key length is too long.1256	///1257	/// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1258	PropertyKeyIsTooLong,12591260	/// Property key is empty.1261	EmptyPropertyKey,1262}12631264/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1265#[derive(Debug)]1266pub enum TokenOwnerError {1267	NotFound,1268	MultipleOwners,1269}12701271/// Marker for scope of property.1272///1273/// Scoped property can't be changed by user. Used for external collections.1274#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1275pub enum PropertyScope {1276	None,1277	Rmrk,1278}12791280impl PropertyScope {1281	pub fn prefix(&self) -> &'static [u8] {1282		match self {1283			Self::None => b"",1284			Self::Rmrk => b"rmrk:",1285		}1286	}1287	/// Apply scope to property key.1288	pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1289		let prefix = self.prefix();1290		if prefix == b"" {1291			return Ok(key);1292		}1293		[prefix, key.as_slice()]1294			.concat()1295			.try_into()1296			.map_err(|_| PropertiesError::PropertyKeyIsTooLong)1297	}1298}12991300/// Trait for operate with properties.1301pub trait TrySetProperty: Sized {1302	type Value;13031304	/// Try to set property with scope.1305	fn try_scoped_set(1306		&mut self,1307		scope: PropertyScope,1308		key: PropertyKey,1309		value: Self::Value,1310	) -> Result<Option<Self::Value>, PropertiesError>;13111312	/// Try to set property with scope from iterator.1313	fn try_scoped_set_from_iter<I, KV>(1314		&mut self,1315		scope: PropertyScope,1316		iter: I,1317	) -> Result<(), PropertiesError>1318	where1319		I: Iterator<Item = KV>,1320		KV: Into<(PropertyKey, Self::Value)>,1321	{1322		for kv in iter {1323			let (key, value) = kv.into();1324			self.try_scoped_set(scope, key, value)?;1325		}13261327		Ok(())1328	}13291330	/// Try to set property.1331	fn try_set(1332		&mut self,1333		key: PropertyKey,1334		value: Self::Value,1335	) -> Result<Option<Self::Value>, PropertiesError> {1336		self.try_scoped_set(PropertyScope::None, key, value)1337	}13381339	/// Try to set property from iterator.1340	fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1341	where1342		I: Iterator<Item = KV>,1343		KV: Into<(PropertyKey, Self::Value)>,1344	{1345		self.try_scoped_set_from_iter(PropertyScope::None, iter)1346	}1347}13481349/// Wrapped map for storing properties.1350#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1351#[derivative(Default(bound = ""))]1352pub struct PropertiesMap<Value>(1353	BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1354);13551356impl<Value> PropertiesMap<Value> {1357	/// Create new property map.1358	pub fn new() -> Self {1359		Self(BoundedBTreeMap::new())1360	}13611362	/// Remove property from map.1363	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1364		Self::check_property_key(key)?;13651366		Ok(self.0.remove(key))1367	}13681369	/// Get property with appropriate key from map.1370	pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1371		self.0.get(key)1372	}13731374	/// Check if map contains key.1375	pub fn contains_key(&self, key: &PropertyKey) -> bool {1376		self.0.contains_key(key)1377	}13781379	/// Check if map contains key with key validation.1380	fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1381		if key.is_empty() {1382			return Err(PropertiesError::EmptyPropertyKey);1383		}13841385		for byte in key.as_slice().iter() {1386			let byte = *byte;13871388			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1389				return Err(PropertiesError::InvalidCharacterInPropertyKey);1390			}1391		}13921393		Ok(())1394	}13951396	pub fn values(&self) -> impl Iterator<Item = &Value> {1397		self.0.values()1398	}13991400	pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1401		self.0.iter()1402	}1403}14041405impl<Value> IntoIterator for PropertiesMap<Value> {1406	type Item = (PropertyKey, Value);1407	type IntoIter = <1408		BoundedBTreeMap<1409			PropertyKey,1410			Value,1411			ConstU32<MAX_PROPERTIES_PER_ITEM>1412		> as IntoIterator1413	>::IntoIter;14141415	fn into_iter(self) -> Self::IntoIter {1416		self.0.into_iter()1417	}1418}14191420impl<Value> TrySetProperty for PropertiesMap<Value> {1421	type Value = Value;14221423	fn try_scoped_set(1424		&mut self,1425		scope: PropertyScope,1426		key: PropertyKey,1427		value: Self::Value,1428	) -> Result<Option<Self::Value>, PropertiesError> {1429		Self::check_property_key(&key)?;14301431		let key = scope.apply(key)?;1432		self.01433			.try_insert(key, value)1434			.map_err(|_| PropertiesError::PropertyLimitReached)1435	}1436}14371438/// Alias for property permissions map.1439pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14401441fn slice_size(data: &[u8]) -> u32 {1442	scoped_slice_size(PropertyScope::None, data)1443}1444fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1445	use parity_scale_codec::Compact;1446	let prefix = scope.prefix();1447	<Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321448		+ data.len() as u321449		+ prefix.len() as u321450}14511452/// Wrapper for properties map with consumed space control.1453#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1454pub struct Properties<const S: u32> {1455	map: PropertiesMap<PropertyValue>,1456	consumed_space: u32,1457	// May be not zero, previously served as a current S generic1458	_reserved: u32,1459}14601461impl<const S: u32> MaxEncodedLen for Properties<S> {1462	fn max_encoded_len() -> usize {1463		// len of map + len of consumed_space + len of space_limit1464		u32::max_encoded_len() * 3 + S as usize1465	}1466}14671468impl<const S: u32> Default for Properties<S> {1469	fn default() -> Self {1470		Self::new()1471	}1472}14731474impl<const S: u32> Properties<S> {1475	/// Create new properies container.1476	pub fn new() -> Self {1477		Self {1478			map: PropertiesMap::new(),1479			consumed_space: 0,1480			_reserved: 0,1481		}1482	}14831484	/// Remove propery with appropiate key.1485	pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1486		let value = self.map.remove(key)?;14871488		if let Some(ref value) = value {1489			let kv_len = slice_size(key) + slice_size(value);1490			self.consumed_space = self.consumed_space.saturating_sub(kv_len);1491		}14921493		Ok(value)1494	}14951496	/// Get property with appropriate key.1497	pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1498		self.map.get(key)1499	}15001501	/// Recomputes the consumed space for the current properties state.1502	/// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1503	pub fn recompute_consumed_space(&mut self) {1504		self.consumed_space = self1505			.map1506			.iter()1507			.map(|(key, value)| slice_size(key) + slice_size(value))1508			.sum();1509	}1510}15111512impl<const S: u32> IntoIterator for Properties<S> {1513	type Item = (PropertyKey, PropertyValue);1514	type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15151516	fn into_iter(self) -> Self::IntoIter {1517		self.map.into_iter()1518	}1519}15201521impl<const S: u32> TrySetProperty for Properties<S> {1522	type Value = PropertyValue;15231524	fn try_scoped_set(1525		&mut self,1526		scope: PropertyScope,1527		key: PropertyKey,1528		value: Self::Value,1529	) -> Result<Option<Self::Value>, PropertiesError> {1530		let key_size = scoped_slice_size(scope, &key);1531		let value_size = slice_size(&value);15321533		if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1534		{1535			return Err(PropertiesError::NoSpaceForProperty);1536		}15371538		let old_value = self.map.try_scoped_set(scope, key, value)?;15391540		if let Some(old_value) = old_value.as_ref() {1541			let old_value_size = slice_size(old_value);1542			self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1543		} else {1544			self.consumed_space += key_size + value_size;1545		}15461547		Ok(old_value)1548	}1549}15501551pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1552pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -86,7 +86,7 @@
 			_ => {}
 		};
 
-		<PalletCommon<T>>::init_collection(sender, payer, data)
+		<PalletCommon<T>>::init_collection(sender, Some(payer), data)
 	}
 
 	fn create_foreign(
@@ -106,7 +106,8 @@
 			_ => {}
 		};
 
-		<PalletCommon<T>>::init_foreign_collection(sender, data)
+		let payer = None;
+		<PalletCommon<T>>::init_collection(sender, payer, data)
 	}
 
 	fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {