git.delta.rocks / unique-network / refs/commits / 1eed41e9dfea

difftreelog

fix restore foreign flag, minor fixes

Daniel Shiposha2023-10-19parent: #56fde60.patch.diff
in: master

11 files changed

modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -121,7 +121,7 @@
 		owner,
 		CollectionMode::NFT,
 		|owner: T::CrossAccountId, data| {
-			<Pallet<T>>::init_collection(owner.clone(), Some(owner), data)
+			<Pallet<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		|h| h,
 	)
modifiedpallets/common/src/dispatch.rsdiffbeforeafterboth
--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -78,6 +78,21 @@
 		sender: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
 		data: CreateCollectionData<T::CrossAccountId>,
+	) -> Result<CollectionId, DispatchError> {
+		Self::create_internal(sender, payer, false, data)
+	}
+
+	/// Create a collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
+	///
+	/// * `sender` - The user who will become the owner of the collection.
+	/// * `payer` - If set, the user who pays the collection creation deposit.
+	/// * `data` - Description of the created collection.
+	/// * `is_special_collection` -- Whether this collection is a system one, i.e. can have special flags set.
+	fn create_internal(
+		sender: T::CrossAccountId,
+		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
+		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError>;
 
 	/// Delete the collection.
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1088,6 +1088,7 @@
 			read_only: flags.external,
 
 			flags: RpcCollectionFlags {
+				foreign: flags.foreign,
 				erc721metadata: flags.erc721metadata,
 			},
 		})
@@ -1129,12 +1130,16 @@
 	/// * `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.
+	/// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
-		ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
+		if !is_special_collection {
+			ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
+		}
 
 		// Take a (non-refundable) deposit of collection creation
 		if let Some(payer) = payer {
modifiedpallets/foreign-assets/src/lib.rsdiffbeforeafterboth
--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -153,9 +153,11 @@
 				.expect("description length < max description length; qed");
 
 			let payer = None;
-			let collection_id = T::CollectionDispatch::create(
+			let is_special_collection = true;
+			let collection_id = T::CollectionDispatch::create_internal(
 				foreign_collection_owner,
 				payer,
+				is_special_collection,
 				CreateCollectionData {
 					name,
 					description,
modifiedpallets/fungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -31,7 +31,7 @@
 		owner,
 		CollectionMode::Fungible(0),
 		|owner: T::CrossAccountId, data| {
-			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), data)
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		FungibleHandle::cast,
 	)
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -58,7 +58,7 @@
 		owner,
 		CollectionMode::NFT,
 		|owner: T::CrossAccountId, data| {
-			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), data)
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		NonfungibleHandle::cast,
 	)
modifiedpallets/refungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -20,6 +20,7 @@
 use pallet_common::{
 	bench_init,
 	benchmarking::{create_collection_raw, property_key, property_value},
+	Pallet as PalletCommon,
 };
 use sp_std::prelude::*;
 use up_data_structs::{
@@ -61,7 +62,9 @@
 	create_collection_raw(
 		owner,
 		CollectionMode::ReFungible,
-		|owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+		|owner: T::CrossAccountId, data| {
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
+		},
 		RefungibleHandle::cast,
 	)
 }
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -103,7 +103,7 @@
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
 use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};
 use up_data_structs::{
-	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,
+	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId,
 	CreateRefungibleExMultipleOwners, PropertiesPermissionMap, Property, PropertyKey,
 	PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TokenOwnerError,
 	TokenProperties as TokenPropertiesT, MAX_REFUNGIBLE_PIECES,
@@ -296,19 +296,6 @@
 
 // unchecked calls skips any permission checks
 impl<T: Config> Pallet<T> {
-	/// Create RFT collection
-	///
-	/// `init_collection` will take non-refundable deposit for collection creation.
-	///
-	/// - `data`: Contains settings for collection limits and permissions.
-	pub fn init_collection(
-		owner: T::CrossAccountId,
-		payer: T::CrossAccountId,
-		data: CreateCollectionData<T::CrossAccountId>,
-	) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(owner, Some(payer), data)
-	}
-
 	/// Destroy RFT collection
 	///
 	/// `destroy_collection` will throw error if collection contains any tokens.
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	/// 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/config/pallets/foreign_asset.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/foreign_asset.rs
+++ b/runtime/common/config/pallets/foreign_asset.rs
@@ -1,4 +1,6 @@
 use frame_support::{parameter_types, PalletId};
+#[cfg(not(feature = "governance"))]
+use frame_system::EnsureRoot;
 use pallet_evm::account::CrossAccountId;
 use sp_core::H160;
 use staging_xcm::prelude::*;
@@ -6,10 +8,6 @@
 
 #[cfg(feature = "governance")]
 use crate::runtime_common::config::governance;
-
-#[cfg(not(feature = "governance"))]
-use frame_system::EnsureRoot;
-
 use crate::{
 	runtime_common::config::{
 		ethereum::CrossAccountId as ConfigCrossAccountId,
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -66,9 +66,10 @@
 		}
 	}
 
-	fn create(
+	fn create_internal(
 		sender: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
 		match data.mode {
@@ -86,7 +87,7 @@
 			_ => {}
 		};
 
-		<PalletCommon<T>>::init_collection(sender, payer, data)
+		<PalletCommon<T>>::init_collection(sender, payer, is_special_collection, data)
 	}
 
 	fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {