git.delta.rocks / unique-network / refs/commits / 75d8b9094b27

difftreelog

Merge pull request #277 from UniqueNetwork/feature/create-collection-ex

kozyrevdev2022-02-17parents: #c5b0654 #0660dd1.patch.diff
in: master
Add createCollectionEx call

18 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -14,7 +14,10 @@
 use up_data_structs::{
 	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
 	MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,
-	TokenId, Weight, WithdrawReasons, CollectionStats, CustomDataLimit,
+	TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,
+	NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,
+	CustomDataLimit, CreateCollectionData, SponsorshipState,
 };
 pub use pallet::*;
 use sp_core::H160;
@@ -285,6 +288,10 @@
 		TokenVariableDataLimitExceeded,
 		/// Exceeded max admin count
 		CollectionAdminCountExceeded,
+		/// Collection limit bounds per collection exceeded
+		CollectionLimitBoundsExceeded,
+		/// Tried to enable permissions which are only permitted to be disabled
+		OwnerPermissionsCantBeReverted,
 
 		/// Collection settings not allowing items transferring
 		TransferNotAllowed,
@@ -395,7 +402,10 @@
 }
 
 impl<T: Config> Pallet<T> {
-	pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+	pub fn init_collection(
+		owner: T::AccountId,
+		data: CreateCollectionData<T::AccountId>,
+	) -> Result<CollectionId, DispatchError> {
 		{
 			ensure!(
 				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,
@@ -418,6 +428,29 @@
 
 		// =========
 
+		let collection = Collection {
+			owner: owner.clone(),
+			name: data.name,
+			mode: data.mode.clone(),
+			mint_mode: false,
+			access: data.access.unwrap_or_default(),
+			description: data.description,
+			token_prefix: data.token_prefix,
+			offchain_schema: data.offchain_schema,
+			schema_version: data.schema_version.unwrap_or_default(),
+			sponsorship: data
+				.pending_sponsor
+				.map(SponsorshipState::Unconfirmed)
+				.unwrap_or_default(),
+			variable_on_chain_schema: data.variable_on_chain_schema,
+			const_on_chain_schema: data.const_on_chain_schema,
+			limits: data
+				.limits
+				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))
+				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,
+			meta_update_permission: data.meta_update_permission.unwrap_or_default(),
+		};
+
 		// Take a (non-refundable) deposit of collection creation
 		{
 			let mut imbalance =
@@ -429,7 +462,7 @@
 				),
 			);
 			<T as Config>::Currency::settle(
-				&data.owner,
+				&owner,
 				imbalance,
 				WithdrawReasons::TRANSFER,
 				ExistenceRequirement::KeepAlive,
@@ -438,12 +471,8 @@
 		}
 
 		<CreatedCollectionCount<T>>::put(created_count);
-		<Pallet<T>>::deposit_event(Event::CollectionCreated(
-			id,
-			data.mode.id(),
-			data.owner.clone(),
-		));
-		<CollectionById<T>>::insert(id, data);
+		<Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));
+		<CollectionById<T>>::insert(id, collection);
 		Ok(id)
 	}
 
@@ -527,6 +556,61 @@
 
 		Ok(())
 	}
+
+	pub fn clamp_limits(
+		mode: CollectionMode,
+		old_limit: &CollectionLimits,
+		mut new_limit: CollectionLimits,
+	) -> Result<CollectionLimits, DispatchError> {
+		macro_rules! limit_default {
+				($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
+					$(
+						if let Some($new) = $new.$field {
+							let $old = $old.$field($($arg)?);
+							let _ = $new;
+							let _ = $old;
+							$check
+						} else {
+							$new.$field = $old.$field
+						}
+					)*
+				}};
+			}
+
+		limit_default!(old_limit, new_limit,
+			account_token_ownership_limit => ensure!(
+				new_limit <= MAX_TOKEN_OWNERSHIP,
+				<Error<T>>::CollectionLimitBoundsExceeded,
+			),
+			sponsor_transfer_timeout(match mode {
+				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+			}) => ensure!(
+				new_limit <= MAX_SPONSOR_TIMEOUT,
+				<Error<T>>::CollectionLimitBoundsExceeded,
+			),
+			sponsored_data_size => ensure!(
+				new_limit <= CUSTOM_DATA_LIMIT,
+				<Error<T>>::CollectionLimitBoundsExceeded,
+			),
+			token_limit => ensure!(
+				old_limit >= new_limit && new_limit > 0,
+				<Error<T>>::CollectionTokenLimitExceeded
+			),
+			owner_can_transfer => ensure!(
+				old_limit || !new_limit,
+				<Error<T>>::OwnerPermissionsCantBeReverted,
+			),
+			owner_can_destroy => ensure!(
+				old_limit || !new_limit,
+				<Error<T>>::OwnerPermissionsCantBeReverted,
+			),
+			sponsored_data_rate_limit => {},
+			transfers_enabled => {},
+		);
+		Ok(new_limit)
+	}
 }
 
 #[macro_export]
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -2,7 +2,7 @@
 
 use core::ops::Deref;
 use frame_support::{ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, TokenId};
+use up_data_structs::{AccessMode, Collection, CollectionId, TokenId, CreateCollectionData};
 use pallet_common::{
 	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
 };
@@ -100,8 +100,11 @@
 }
 
 impl<T: Config> Pallet<T> {
-	pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(data)
+	pub fn init_collection(
+		owner: T::AccountId,
+		data: CreateCollectionData<T::AccountId>,
+	) -> Result<CollectionId, DispatchError> {
+		<PalletCommon<T>>::init_collection(owner, data)
 	}
 	pub fn destroy_collection(
 		collection: FungibleHandle<T>,
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -2,7 +2,9 @@
 
 use erc::ERC721Events;
 use frame_support::{BoundedVec, ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, CustomDataLimit, TokenId};
+use up_data_structs::{
+	AccessMode, Collection, CollectionId, CustomDataLimit, TokenId, CreateCollectionData,
+};
 use pallet_common::{
 	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
 };
@@ -140,8 +142,11 @@
 
 // unchecked calls skips any permission checks
 impl<T: Config> Pallet<T> {
-	pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(data)
+	pub fn init_collection(
+		owner: T::AccountId,
+		data: CreateCollectionData<T::AccountId>,
+	) -> Result<CollectionId, DispatchError> {
+		<PalletCommon<T>>::init_collection(owner, data)
 	}
 	pub fn destroy_collection(
 		collection: NonfungibleHandle<T>,
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -3,6 +3,7 @@
 use frame_support::{ensure, BoundedVec};
 use up_data_structs::{
 	AccessMode, Collection, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
+	CreateCollectionData,
 };
 use pallet_common::{
 	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -155,8 +156,11 @@
 
 // unchecked calls skips any permission checks
 impl<T: Config> Pallet<T> {
-	pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(data)
+	pub fn init_collection(
+		owner: T::AccountId,
+		data: CreateCollectionData<T::AccountId>,
+	) -> Result<CollectionId, DispatchError> {
+		<PalletCommon<T>>::init_collection(owner, data)
 	}
 	pub fn destroy_collection(
 		collection: RefungibleHandle<T>,
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -36,13 +36,11 @@
 use frame_system::{self as system, ensure_signed};
 use sp_runtime::{sp_std::prelude::Vec};
 use up_data_structs::{
-	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,
-	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,
-	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-	NFT_SPONSOR_TRANSFER_TIMEOUT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
+	MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,
+	OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
 	MAX_TOKEN_PREFIX_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,
 	CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
-	CustomDataLimit,
+	CreateCollectionData, CustomDataLimit,
 };
 use pallet_common::{
 	account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -84,10 +82,6 @@
 		ConfirmUnsetSponsorFail,
 		/// Length of items properties must be greater than 0.
 		EmptyArgument,
-		/// Collection limit bounds per collection exceeded
-		CollectionLimitBoundsExceeded,
-		/// Tried to enable permissions which are only permitted to be disabled
-		OwnerPermissionsCantBeReverted,
 	}
 }
 
@@ -321,42 +315,39 @@
 		// returns collection ID
 		#[weight = <SelfWeightOf<T>>::create_collection()]
 		#[transactional]
+		#[deprecated]
 		pub fn create_collection(origin,
 								 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
 								 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
 								 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
-								 mode: CollectionMode) -> DispatchResult {
-
-			// Anyone can create a collection
-			let who = ensure_signed(origin)?;
-
-			// Create new collection
-			let new_collection = Collection {
-				owner: who,
+								 mode: CollectionMode) -> DispatchResult  {
+			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {
 				name: collection_name,
-				mode: mode.clone(),
-				mint_mode: false,
-				access: AccessMode::Normal,
 				description: collection_description,
 				token_prefix,
-				offchain_schema: BoundedVec::default(),
-				schema_version: SchemaVersion::ImageURL,
-				sponsorship: SponsorshipState::Disabled,
-				variable_on_chain_schema: BoundedVec::default(),
-				const_on_chain_schema: BoundedVec::default(),
-				limits: Default::default(),
-				meta_update_permission: Default::default(),
+				mode,
+				..Default::default()
 			};
+			Self::create_collection_ex(origin, data)
+		}
+
+		/// This method creates a collection
+		///
+		/// Prefer it to deprecated [`created_collection`] method
+		#[weight = <SelfWeightOf<T>>::create_collection()]
+		#[transactional]
+		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
+			let owner = ensure_signed(origin)?;
 
-			let _id = match mode {
-				CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},
+			let _id = match data.mode {
+				CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},
 				CollectionMode::Fungible(decimal_points) => {
 					// check params
 					ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
-					<PalletFungible<T>>::init_collection(new_collection)?
+					<PalletFungible<T>>::init_collection(owner, data)?
 				}
 				CollectionMode::ReFungible => {
-					<PalletRefungible<T>>::init_collection(new_collection)?
+					<PalletRefungible<T>>::init_collection(owner, data)?
 				}
 			};
 
@@ -1093,61 +1084,12 @@
 			collection_id: CollectionId,
 			new_limit: CollectionLimits,
 		) -> DispatchResult {
-			let mut new_limit = new_limit;
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 			target_collection.check_is_owner(&sender)?;
 			let old_limit = &target_collection.limits;
 
-			macro_rules! limit_default {
-				($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
-					$(
-						if let Some($new) = $new.$field {
-							let $old = $old.$field($($arg)?);
-							let _ = $new;
-							let _ = $old;
-							$check
-						} else {
-							$new.$field = $old.$field
-						}
-					)*
-				}};
-			}
-
-			limit_default!(old_limit, new_limit,
-				account_token_ownership_limit => ensure!(
-					new_limit <= MAX_TOKEN_OWNERSHIP,
-					<Error<T>>::CollectionLimitBoundsExceeded,
-				),
-				sponsor_transfer_timeout(match target_collection.mode {
-					CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
-					CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-					CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
-				}) => ensure!(
-					new_limit <= MAX_SPONSOR_TIMEOUT,
-					<Error<T>>::CollectionLimitBoundsExceeded,
-				),
-				sponsored_data_size => ensure!(
-					new_limit <= CUSTOM_DATA_LIMIT,
-					<Error<T>>::CollectionLimitBoundsExceeded,
-				),
-				token_limit => ensure!(
-					old_limit >= new_limit && new_limit > 0,
-					<CommonError<T>>::CollectionTokenLimitExceeded
-				),
-				owner_can_transfer => ensure!(
-					old_limit || !new_limit,
-					<Error<T>>::OwnerPermissionsCantBeReverted,
-				),
-				owner_can_destroy => ensure!(
-					old_limit || !new_limit,
-					<Error<T>>::OwnerPermissionsCantBeReverted,
-				),
-				sponsored_data_rate_limit => {},
-				transfers_enabled => {},
-			);
-
-			target_collection.limits = new_limit;
+			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
 
 			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
 				collection_id
modifiedpallets/unique/src/tests.rsdiffbeforeafterboth
--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -5,7 +5,7 @@
 use up_data_structs::{
 	COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
 	CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
-	TokenId,
+	TokenId, MAX_TOKEN_OWNERSHIP,
 };
 use frame_support::{assert_noop, assert_ok};
 use sp_std::convert::TryInto;
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
before · primitives/data-structs/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12	BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13	dispatch::DispatchResult,14	ensure, fail, parameter_types,15	traits::{16		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17		Randomness, IsSubType, WithdrawReasons,18	},19	weights::{20		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22		WeightToFeePolynomial, DispatchClass,23	},24	StorageValue, transactional,25	pallet_prelude::ConstU32,26};27use derivative::Derivative;28use scale_info::TypeInfo;2930pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;31pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;32pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;3334pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {35	100_00036} else {37	1038};39pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40	100_00041} else {42	1043};44pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {45	204846} else {47	1048};49pub const COLLECTION_ADMINS_LIMIT: u32 = 5;50pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;51pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {52	1_000_00053} else {54	1055};5657// Timeouts for item types in passed blocks58pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;59pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;60pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;6162pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;6364// Schema limits65pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;66pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;67pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1048576;6869pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;70pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;71pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;7273/// How much items can be created per single74/// create_many call75pub const MAX_ITEMS_PER_BATCH: u32 = 200;7677parameter_types! {78	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;79}8081#[derive(82	Encode,83	Decode,84	PartialEq,85	Eq,86	PartialOrd,87	Ord,88	Clone,89	Copy,90	Debug,91	Default,92	TypeInfo,93	MaxEncodedLen,94)]95#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]96pub struct CollectionId(pub u32);97impl EncodeLike<u32> for CollectionId {}98impl EncodeLike<CollectionId> for u32 {}99100#[derive(101	Encode,102	Decode,103	PartialEq,104	Eq,105	PartialOrd,106	Ord,107	Clone,108	Copy,109	Debug,110	Default,111	TypeInfo,112	MaxEncodedLen,113)]114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]115pub struct TokenId(pub u32);116impl EncodeLike<u32> for TokenId {}117impl EncodeLike<TokenId> for u32 {}118119impl TokenId {120	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {121		self.0122			.checked_add(1)123			.ok_or(ArithmeticError::Overflow)124			.map(Self)125	}126}127128impl From<TokenId> for U256 {129	fn from(t: TokenId) -> Self {130		t.0.into()131	}132}133134impl TryFrom<U256> for TokenId {135	type Error = &'static str;136137	fn try_from(value: U256) -> Result<Self, Self::Error> {138		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))139	}140}141142pub struct OverflowError;143impl From<OverflowError> for &'static str {144	fn from(_: OverflowError) -> Self {145		"overflow occured"146	}147}148149pub type DecimalPoints = u8;150151#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]152#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]153pub enum CollectionMode {154	NFT,155	// decimal points156	Fungible(DecimalPoints),157	ReFungible,158}159160impl CollectionMode {161	pub fn id(&self) -> u8 {162		match self {163			CollectionMode::NFT => 1,164			CollectionMode::Fungible(_) => 2,165			CollectionMode::ReFungible => 3,166		}167	}168}169170pub trait SponsoringResolve<AccountId, Call> {171	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;172}173174#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]175#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]176pub enum AccessMode {177	Normal,178	AllowList,179}180impl Default for AccessMode {181	fn default() -> Self {182		Self::Normal183	}184}185186#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]188pub enum SchemaVersion {189	ImageURL,190	Unique,191}192impl Default for SchemaVersion {193	fn default() -> Self {194		Self::ImageURL195	}196}197198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]199#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]200pub struct Ownership<AccountId> {201	pub owner: AccountId,202	pub fraction: u128,203}204205#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]206#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]207pub enum SponsorshipState<AccountId> {208	/// The fees are applied to the transaction sender209	Disabled,210	Unconfirmed(AccountId),211	/// Transactions are sponsored by specified account212	Confirmed(AccountId),213}214215impl<AccountId> SponsorshipState<AccountId> {216	pub fn sponsor(&self) -> Option<&AccountId> {217		match self {218			Self::Confirmed(sponsor) => Some(sponsor),219			_ => None,220		}221	}222223	pub fn pending_sponsor(&self) -> Option<&AccountId> {224		match self {225			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),226			_ => None,227		}228	}229230	pub fn confirmed(&self) -> bool {231		matches!(self, Self::Confirmed(_))232	}233}234235impl<T> Default for SponsorshipState<T> {236	fn default() -> Self {237		Self::Disabled238	}239}240241#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct Collection<AccountId> {244	pub owner: AccountId,245	pub mode: CollectionMode,246	pub access: AccessMode,247	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]248	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,249	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]250	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,251	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]252	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,253	pub mint_mode: bool,254	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]255	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,256	pub schema_version: SchemaVersion,257	pub sponsorship: SponsorshipState<AccountId>,258	pub limits: CollectionLimits, // Collection private restrictions259	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]260	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,261	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]262	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,263	pub meta_update_permission: MetaUpdatePermission,264}265266#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]267#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]268pub struct NftItemType<AccountId> {269	pub owner: AccountId,270	pub const_data: Vec<u8>,271	pub variable_data: Vec<u8>,272}273274#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]275#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]276pub struct FungibleItemType {277	pub value: u128,278}279280#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]281#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]282pub struct ReFungibleItemType<AccountId> {283	pub owner: Vec<Ownership<AccountId>>,284	pub const_data: Vec<u8>,285	pub variable_data: Vec<u8>,286}287288/// All fields are wrapped in `Option`s, where None means chain default289#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]290#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]291pub struct CollectionLimits {292	pub account_token_ownership_limit: Option<u32>,293	pub sponsored_data_size: Option<u32>,294	/// None - setVariableMetadata is not sponsored295	/// Some(v) - setVariableMetadata is sponsored296	///           if there is v block between txs297	pub sponsored_data_rate_limit: Option<(Option<u32>,)>,298	pub token_limit: Option<u32>,299300	// Timeouts for item types in passed blocks301	pub sponsor_transfer_timeout: Option<u32>,302	pub sponsor_approve_timeout: Option<u32>,303	pub owner_can_transfer: Option<bool>,304	pub owner_can_destroy: Option<bool>,305	pub transfers_enabled: Option<bool>,306}307308impl CollectionLimits {309	pub fn account_token_ownership_limit(&self) -> u32 {310		self.account_token_ownership_limit311			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)312			.min(MAX_TOKEN_OWNERSHIP)313	}314	pub fn sponsored_data_size(&self) -> u32 {315		self.sponsored_data_size316			.unwrap_or(CUSTOM_DATA_LIMIT)317			.min(CUSTOM_DATA_LIMIT)318	}319	pub fn token_limit(&self) -> u32 {320		self.token_limit321			.unwrap_or(COLLECTION_TOKEN_LIMIT)322			.min(COLLECTION_TOKEN_LIMIT)323	}324	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {325		self.sponsor_transfer_timeout326			.unwrap_or(default)327			.min(MAX_SPONSOR_TIMEOUT)328	}329	pub fn sponsor_approve_timeout(&self) -> u32 {330		self.sponsor_approve_timeout331			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)332			.min(MAX_SPONSOR_TIMEOUT)333	}334	pub fn owner_can_transfer(&self) -> bool {335		self.owner_can_transfer.unwrap_or(true)336	}337	pub fn owner_can_destroy(&self) -> bool {338		self.owner_can_destroy.unwrap_or(true)339	}340	pub fn transfers_enabled(&self) -> bool {341		self.transfers_enabled.unwrap_or(true)342	}343	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {344		self.sponsored_data_rate_limit345			.unwrap_or((None,))346			.0347			.map(|v| v.min(MAX_SPONSOR_TIMEOUT))348	}349}350351/// BoundedVec doesn't supports serde352#[cfg(feature = "serde1")]353mod bounded_serde {354	use core::convert::TryFrom;355	use frame_support::{BoundedVec, traits::Get};356	use serde::{357		ser::{self, Serialize},358		de::{self, Deserialize, Error},359	};360	use sp_std::vec::Vec;361362	pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>363	where364		D: ser::Serializer,365		V: Serialize,366	{367		(value as &Vec<_>).serialize(serializer)368	}369370	pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>371	where372		D: de::Deserializer<'de>,373		V: de::Deserialize<'de>,374		S: Get<u32>,375	{376		// TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?377		let vec = <Vec<V>>::deserialize(deserializer)?;378		let len = vec.len();379		TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))380	}381}382383#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]384#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]385#[derivative(Debug)]386pub struct CreateNftData {387	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]388	#[derivative(Debug = "ignore")]389	pub const_data: BoundedVec<u8, CustomDataLimit>,390	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]391	#[derivative(Debug = "ignore")]392	pub variable_data: BoundedVec<u8, CustomDataLimit>,393}394395#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]396#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]397pub struct CreateFungibleData {398	pub value: u128,399}400401#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]402#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]403#[derivative(Debug)]404pub struct CreateReFungibleData {405	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]406	#[derivative(Debug = "ignore")]407	pub const_data: BoundedVec<u8, CustomDataLimit>,408	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]409	#[derivative(Debug = "ignore")]410	pub variable_data: BoundedVec<u8, CustomDataLimit>,411	pub pieces: u128,412}413414#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]415#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]416pub enum MetaUpdatePermission {417	ItemOwner,418	Admin,419	None,420}421422impl Default for MetaUpdatePermission {423	fn default() -> Self {424		Self::ItemOwner425	}426}427428#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]429#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]430pub enum CreateItemData {431	NFT(CreateNftData),432	Fungible(CreateFungibleData),433	ReFungible(CreateReFungibleData),434}435436impl CreateItemData {437	pub fn data_size(&self) -> usize {438		match self {439			CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),440			CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),441			_ => 0,442		}443	}444}445446impl From<CreateNftData> for CreateItemData {447	fn from(item: CreateNftData) -> Self {448		CreateItemData::NFT(item)449	}450}451452impl From<CreateReFungibleData> for CreateItemData {453	fn from(item: CreateReFungibleData) -> Self {454		CreateItemData::ReFungible(item)455	}456}457458impl From<CreateFungibleData> for CreateItemData {459	fn from(item: CreateFungibleData) -> Self {460		CreateItemData::Fungible(item)461	}462}463464#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]465#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]466pub struct CollectionStats {467	pub created: u32,468	pub destroyed: u32,469	pub alive: u32,470}
after · primitives/data-structs/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::convert::{TryFrom, TryInto};45#[cfg(feature = "serde")]6pub use serde::{Serialize, Deserialize};78use sp_core::U256;9use sp_runtime::{ArithmeticError, sp_std::prelude::Vec};10use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};11pub use frame_support::{12	BoundedVec, construct_runtime, decl_event, decl_module, decl_storage, decl_error,13	dispatch::DispatchResult,14	ensure, fail, parameter_types,15	traits::{16		Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,17		Randomness, IsSubType, WithdrawReasons,18	},19	weights::{20		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},21		DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,22		WeightToFeePolynomial, DispatchClass,23	},24	StorageValue, transactional,25	pallet_prelude::ConstU32,26};27use derivative::Derivative;28use scale_info::TypeInfo;2930pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;31pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;32pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;3334pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {35	100_00036} else {37	1038};39pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {40	100_00041} else {42	1043};44pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {45	204846} else {47	1048};49pub const COLLECTION_ADMINS_LIMIT: u32 = 5;50pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;51pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {52	1_000_00053} else {54	1055};5657// Timeouts for item types in passed blocks58pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;59pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;60pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;6162pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;6364// Schema limits65pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;66pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;67pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1048576;6869pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;70pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;71pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;7273/// How much items can be created per single74/// create_many call75pub const MAX_ITEMS_PER_BATCH: u32 = 200;7677parameter_types! {78	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;79}8081#[derive(82	Encode,83	Decode,84	PartialEq,85	Eq,86	PartialOrd,87	Ord,88	Clone,89	Copy,90	Debug,91	Default,92	TypeInfo,93	MaxEncodedLen,94)]95#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]96pub struct CollectionId(pub u32);97impl EncodeLike<u32> for CollectionId {}98impl EncodeLike<CollectionId> for u32 {}99100#[derive(101	Encode,102	Decode,103	PartialEq,104	Eq,105	PartialOrd,106	Ord,107	Clone,108	Copy,109	Debug,110	Default,111	TypeInfo,112	MaxEncodedLen,113)]114#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]115pub struct TokenId(pub u32);116impl EncodeLike<u32> for TokenId {}117impl EncodeLike<TokenId> for u32 {}118119impl TokenId {120	pub fn try_next(self) -> Result<TokenId, ArithmeticError> {121		self.0122			.checked_add(1)123			.ok_or(ArithmeticError::Overflow)124			.map(Self)125	}126}127128impl From<TokenId> for U256 {129	fn from(t: TokenId) -> Self {130		t.0.into()131	}132}133134impl TryFrom<U256> for TokenId {135	type Error = &'static str;136137	fn try_from(value: U256) -> Result<Self, Self::Error> {138		Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))139	}140}141142pub struct OverflowError;143impl From<OverflowError> for &'static str {144	fn from(_: OverflowError) -> Self {145		"overflow occured"146	}147}148149pub type DecimalPoints = u8;150151#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]152#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]153pub enum CollectionMode {154	NFT,155	// decimal points156	Fungible(DecimalPoints),157	ReFungible,158}159160impl CollectionMode {161	pub fn id(&self) -> u8 {162		match self {163			CollectionMode::NFT => 1,164			CollectionMode::Fungible(_) => 2,165			CollectionMode::ReFungible => 3,166		}167	}168}169170pub trait SponsoringResolve<AccountId, Call> {171	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;172}173174#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]175#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]176pub enum AccessMode {177	Normal,178	AllowList,179}180impl Default for AccessMode {181	fn default() -> Self {182		Self::Normal183	}184}185186#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]188pub enum SchemaVersion {189	ImageURL,190	Unique,191}192impl Default for SchemaVersion {193	fn default() -> Self {194		Self::ImageURL195	}196}197198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]199#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]200pub struct Ownership<AccountId> {201	pub owner: AccountId,202	pub fraction: u128,203}204205#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]206#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]207pub enum SponsorshipState<AccountId> {208	/// The fees are applied to the transaction sender209	Disabled,210	Unconfirmed(AccountId),211	/// Transactions are sponsored by specified account212	Confirmed(AccountId),213}214215impl<AccountId> SponsorshipState<AccountId> {216	pub fn sponsor(&self) -> Option<&AccountId> {217		match self {218			Self::Confirmed(sponsor) => Some(sponsor),219			_ => None,220		}221	}222223	pub fn pending_sponsor(&self) -> Option<&AccountId> {224		match self {225			Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),226			_ => None,227		}228	}229230	pub fn confirmed(&self) -> bool {231		matches!(self, Self::Confirmed(_))232	}233}234235impl<T> Default for SponsorshipState<T> {236	fn default() -> Self {237		Self::Disabled238	}239}240241#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]242#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]243pub struct Collection<AccountId> {244	pub owner: AccountId,245	pub mode: CollectionMode,246	pub access: AccessMode,247	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]248	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,249	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]250	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,251	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]252	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,253	pub mint_mode: bool,254	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]255	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,256	pub schema_version: SchemaVersion,257	pub sponsorship: SponsorshipState<AccountId>,258	pub limits: CollectionLimits, // Collection private restrictions259	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]260	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,261	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]262	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,263	pub meta_update_permission: MetaUpdatePermission,264}265266#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Debug, Derivative, MaxEncodedLen)]267#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]268#[derivative(Default(bound = ""))]269pub struct CreateCollectionData<AccountId> {270	#[derivative(Default(value = "CollectionMode::NFT"))]271	pub mode: CollectionMode,272	pub access: Option<AccessMode>,273	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]274	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,275	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]276	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,277	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]278	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,279	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]280	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,281	pub schema_version: Option<SchemaVersion>,282	pub pending_sponsor: Option<AccountId>,283	pub limits: Option<CollectionLimits>,284	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]285	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,286	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]287	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,288	pub meta_update_permission: Option<MetaUpdatePermission>,289}290291#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]292#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]293pub struct NftItemType<AccountId> {294	pub owner: AccountId,295	pub const_data: Vec<u8>,296	pub variable_data: Vec<u8>,297}298299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]300#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]301pub struct FungibleItemType {302	pub value: u128,303}304305#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]306#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]307pub struct ReFungibleItemType<AccountId> {308	pub owner: Vec<Ownership<AccountId>>,309	pub const_data: Vec<u8>,310	pub variable_data: Vec<u8>,311}312313/// All fields are wrapped in `Option`s, where None means chain default314#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]315#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]316pub struct CollectionLimits {317	pub account_token_ownership_limit: Option<u32>,318	pub sponsored_data_size: Option<u32>,319	/// None - setVariableMetadata is not sponsored320	/// Some(v) - setVariableMetadata is sponsored321	///           if there is v block between txs322	pub sponsored_data_rate_limit: Option<(Option<u32>,)>,323	pub token_limit: Option<u32>,324325	// Timeouts for item types in passed blocks326	pub sponsor_transfer_timeout: Option<u32>,327	pub sponsor_approve_timeout: Option<u32>,328	pub owner_can_transfer: Option<bool>,329	pub owner_can_destroy: Option<bool>,330	pub transfers_enabled: Option<bool>,331}332333impl CollectionLimits {334	pub fn account_token_ownership_limit(&self) -> u32 {335		self.account_token_ownership_limit336			.unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)337			.min(MAX_TOKEN_OWNERSHIP)338	}339	pub fn sponsored_data_size(&self) -> u32 {340		self.sponsored_data_size341			.unwrap_or(CUSTOM_DATA_LIMIT)342			.min(CUSTOM_DATA_LIMIT)343	}344	pub fn token_limit(&self) -> u32 {345		self.token_limit346			.unwrap_or(COLLECTION_TOKEN_LIMIT)347			.min(COLLECTION_TOKEN_LIMIT)348	}349	pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {350		self.sponsor_transfer_timeout351			.unwrap_or(default)352			.min(MAX_SPONSOR_TIMEOUT)353	}354	pub fn sponsor_approve_timeout(&self) -> u32 {355		self.sponsor_approve_timeout356			.unwrap_or(SPONSOR_APPROVE_TIMEOUT)357			.min(MAX_SPONSOR_TIMEOUT)358	}359	pub fn owner_can_transfer(&self) -> bool {360		self.owner_can_transfer.unwrap_or(true)361	}362	pub fn owner_can_destroy(&self) -> bool {363		self.owner_can_destroy.unwrap_or(true)364	}365	pub fn transfers_enabled(&self) -> bool {366		self.transfers_enabled.unwrap_or(true)367	}368	pub fn sponsored_data_rate_limit(&self) -> Option<u32> {369		self.sponsored_data_rate_limit370			.unwrap_or((None,))371			.0372			.map(|v| v.min(MAX_SPONSOR_TIMEOUT))373	}374}375376/// BoundedVec doesn't supports serde377#[cfg(feature = "serde1")]378mod bounded_serde {379	use core::convert::TryFrom;380	use frame_support::{BoundedVec, traits::Get};381	use serde::{382		ser::{self, Serialize},383		de::{self, Deserialize, Error},384	};385	use sp_std::vec::Vec;386387	pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>388	where389		D: ser::Serializer,390		V: Serialize,391	{392		(value as &Vec<_>).serialize(serializer)393	}394395	pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>396	where397		D: de::Deserializer<'de>,398		V: de::Deserialize<'de>,399		S: Get<u32>,400	{401		// TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?402		let vec = <Vec<V>>::deserialize(deserializer)?;403		let len = vec.len();404		TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))405	}406}407408#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]409#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]410#[derivative(Debug)]411pub struct CreateNftData {412	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]413	#[derivative(Debug = "ignore")]414	pub const_data: BoundedVec<u8, CustomDataLimit>,415	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]416	#[derivative(Debug = "ignore")]417	pub variable_data: BoundedVec<u8, CustomDataLimit>,418}419420#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]421#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]422pub struct CreateFungibleData {423	pub value: u128,424}425426#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]427#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]428#[derivative(Debug)]429pub struct CreateReFungibleData {430	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]431	#[derivative(Debug = "ignore")]432	pub const_data: BoundedVec<u8, CustomDataLimit>,433	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]434	#[derivative(Debug = "ignore")]435	pub variable_data: BoundedVec<u8, CustomDataLimit>,436	pub pieces: u128,437}438439#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]440#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]441pub enum MetaUpdatePermission {442	ItemOwner,443	Admin,444	None,445}446447impl Default for MetaUpdatePermission {448	fn default() -> Self {449		Self::ItemOwner450	}451}452453#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]454#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]455pub enum CreateItemData {456	NFT(CreateNftData),457	Fungible(CreateFungibleData),458	ReFungible(CreateReFungibleData),459}460461impl CreateItemData {462	pub fn data_size(&self) -> usize {463		match self {464			CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),465			CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),466			_ => 0,467		}468	}469}470471impl From<CreateNftData> for CreateItemData {472	fn from(item: CreateNftData) -> Self {473		CreateItemData::NFT(item)474	}475}476477impl From<CreateReFungibleData> for CreateItemData {478	fn from(item: CreateReFungibleData) -> Self {479		CreateItemData::ReFungible(item)480	}481}482483impl From<CreateFungibleData> for CreateItemData {484	fn from(item: CreateFungibleData) -> Self {485		CreateItemData::Fungible(item)486	}487}488489#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]490#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]491pub struct CollectionStats {492	pub created: u32,493	pub destroyed: u32,494	pub alive: u32,495}
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -68,9 +68,10 @@
     "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
     "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
     "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",
+    "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
     "polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --input src/interfaces/ --package .",
-    "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint ws://localhost:9944 --output src/interfaces/ --package .",
-    "polkadot-types": "yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
+    "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",
+    "polkadot-types": "yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
   },
   "author": "",
   "license": "SEE LICENSE IN ../LICENSE",
modifiedtests/src/check-event/createCollectionEvent.test.tsdiffbeforeafterboth
--- a/tests/src/check-event/createCollectionEvent.test.ts
+++ b/tests/src/check-event/createCollectionEvent.test.ts
@@ -27,7 +27,7 @@
   });
   it('Check event from createCollection(): ', async () => {
     await usingApi(async (api: ApiPromise) => {
-      const tx = api.tx.unique.createCollection([0x31], [0x32], '0x33', 'NFT');
+      const tx = api.tx.unique.createCollectionEx({name: [0x31], description: [0x32], tokenPrefix: '0x33', mode: 'NFT'});
       const events = await submitTransactionAsync(alice, tx);
       const msg = JSON.stringify(uniqueEventMessage(events));
       expect(msg).to.be.contain(checkSection);
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -3,12 +3,11 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import {createCollectionExpectFailure, createCollectionExpectSuccess} from './util/helpers';
+import {expect} from 'chai';
+import privateKey from './substrate/privateKey';
+import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';
+import {createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo} from './util/helpers';
 
-chai.use(chaiAsPromised);
-
 describe('integration test: ext. createCollection():', () => {
   it('Create new NFT collection', async () => {
     await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
@@ -28,6 +27,45 @@
   it('Create new ReFungible collection', async () => {
     await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
   });
+  it('Create new collection with extra fields', async () => {
+    await usingApi(async api => {
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+      const tx = api.tx.unique.createCollectionEx({
+        mode: {Fungible: 8},
+        access: 'AllowList',
+        name: [1],
+        description: [2],
+        tokenPrefix: '0x000000',
+        offchainSchema: '0x111111',
+        schemaVersion: 'Unique',
+        pendingSponsor: bob.address,
+        limits: {
+          accountTokenOwnershipLimit: 3,
+        },
+        variableOnChainSchema: '0x222222',
+        constOnChainSchema: '0x333333',
+        metaUpdatePermission: 'Admin',
+      });
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateCollectionResult(events);
+
+      const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
+      expect(collection.owner.toString()).to.equal(alice.address);
+      expect(collection.mode.asFungible.toNumber()).to.equal(8);
+      expect(collection.access.isAllowList).to.be.true;
+      expect(collection.name.map(v => v.toNumber())).to.deep.equal([1]);
+      expect(collection.description.map(v => v.toNumber())).to.deep.equal([2]);
+      expect(collection.tokenPrefix.toString()).to.equal('0x000000');
+      expect(collection.offchainSchema.toString()).to.equal('0x111111');
+      expect(collection.schemaVersion.isUnique).to.be.true;
+      expect(collection.sponsorship.asUnconfirmed.toString()).to.equal(bob.address);
+      expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.equal(3);
+      expect(collection.variableOnChainSchema.toString()).to.equal('0x222222');
+      expect(collection.constOnChainSchema.toString()).to.equal('0x333333');
+      expect(collection.metaUpdatePermission.isAdmin).to.be.true;
+    });
+  });
 });
 
 describe('(!negative test!) integration test: ext. createCollection():', () => {
@@ -40,4 +78,11 @@
   it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
     await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
   });
+  it('fails when bad limits are set', async () => {
+    await usingApi(async api => {
+      const alice = privateKey('//Alice');
+      const tx = api.tx.unique.createCollectionEx({mode: 'NFT', limits: {tokenLimit: 0}});
+      await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/^common.CollectionTokenLimitExceeded$/);
+    });
+  });
 });
addedtests/src/interfaces/.gitignorediffbeforeafterboth
--- /dev/null
+++ b/tests/src/interfaces/.gitignore
@@ -0,0 +1 @@
+metadata.json
modifiedtests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -69,6 +69,10 @@
        **/
       CollectionDescriptionLimitExceeded: AugmentedError<ApiType>;
       /**
+       * Collection limit bounds per collection exceeded
+       **/
+      CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
+      /**
        * Collection name can not be longer than 63 char.
        **/
       CollectionNameLimitExceeded: AugmentedError<ApiType>;
@@ -97,6 +101,10 @@
        **/
       NoPermission: AugmentedError<ApiType>;
       /**
+       * Tried to enable permissions which are only permitted to be disabled
+       **/
+      OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
+      /**
        * Collection is not in mint mode.
        **/
       PublicMintingNotAllowed: AugmentedError<ApiType>;
@@ -437,10 +445,6 @@
        **/
       CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
       /**
-       * Collection limit bounds per collection exceeded
-       **/
-      CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
-      /**
        * This address is not set as sponsor, use setCollectionSponsor first.
        **/
       ConfirmUnsetSponsorFail: AugmentedError<ApiType>;
@@ -448,10 +452,6 @@
        * Length of items properties must be greater than 0.
        **/
       EmptyArgument: AugmentedError<ApiType>;
-      /**
-       * Tried to enable permissions which are only permitted to be disabled
-       **/
-      OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
       /**
        * Generic error
        **/
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -2,7 +2,7 @@
 /* eslint-disable */
 
 import type { CumulusPrimitivesParachainInherentParachainInherentData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion } from './unique';
 import type { ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types';
 import type { Bytes, Compact, Option, U256, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types';
 import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
@@ -671,6 +671,12 @@
        **/
       createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
       /**
+       * This method creates a collection
+       * 
+       * Prefer it to deprecated [`created_collection`] method
+       **/
+      createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; offchainSchema?: any; schemaVersion?: any; pendingSponsor?: any; limits?: any; variableOnChainSchema?: any; constOnChainSchema?: any; metaUpdatePermission?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
+      /**
        * This method creates a concrete instance of NFT Collection created with CreateCollection method.
        * 
        * # Permissions
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -3,7 +3,7 @@
 
 import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';
 import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';
-import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
+import type { PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionId, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsorshipState, UpDataStructsTokenId } from './unique';
 import type { BitVec, Bool, Bytes, Data, I128, I16, I256, I32, I64, I8, Json, Null, Raw, StorageKey, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
 import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
@@ -1021,6 +1021,7 @@
     UpDataStructsCollectionLimits: UpDataStructsCollectionLimits;
     UpDataStructsCollectionMode: UpDataStructsCollectionMode;
     UpDataStructsCollectionStats: UpDataStructsCollectionStats;
+    UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
     UpDataStructsCreateItemData: UpDataStructsCreateItemData;
     UpDataStructsMetaUpdatePermission: UpDataStructsMetaUpdatePermission;
     UpDataStructsSchemaVersion: UpDataStructsSchemaVersion;
modifiedtests/src/interfaces/unique/definitions.tsdiffbeforeafterboth
--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -67,6 +67,20 @@
       constOnChainSchema: 'Vec<u8>',
       metaUpdatePermission: 'UpDataStructsMetaUpdatePermission',
     },
+    UpDataStructsCreateCollectionData: {
+      mode: 'UpDataStructsCollectionMode',
+      access: 'Option<UpDataStructsAccessMode>',
+      name: 'Vec<u16>',
+      description: 'Vec<u16>',
+      tokenPrefix: 'Vec<u8>',
+      offchainSchema: 'Vec<u8>',
+      schemaVersion: 'Option<UpDataStructsSchemaVersion>',
+      pendingSponsor: 'Option<AccountId>',
+      limits: 'Option<UpDataStructsCollectionLimits>',
+      variableOnChainSchema: 'Vec<u8>',
+      constOnChainSchema: 'Vec<u8>',
+      metaUpdatePermission: 'Option<UpDataStructsMetaUpdatePermission>',
+    },
     UpDataStructsCollectionStats: {
       created: 'u32',
       destroyed: 'u32',
@@ -76,7 +90,13 @@
     UpDataStructsTokenId: 'u32',
     PalletNonfungibleItemData: mkDummy('NftItemData'),
     PalletRefungibleItemData: mkDummy('RftItemData'),
-    UpDataStructsCollectionMode: mkDummy('CollectionMode'),
+    UpDataStructsCollectionMode: {
+      _enum: {
+        NFT: null,
+        Fungible: 'u32',
+        ReFungible: null,
+      },
+    },
     UpDataStructsCreateItemData: mkDummy('CreateItemData'),
     UpDataStructsCollectionLimits: {
       accountTokenOwnershipLimit: 'Option<u32>',
@@ -101,7 +121,9 @@
     UpDataStructsAccessMode: {
       _enum: ['Normal', 'AllowList'],
     },
-    UpDataStructsSchemaVersion: mkDummy('SchemaVersion'),
+    UpDataStructsSchemaVersion: {
+      _enum: ['ImageURL', 'Unique'],
+    },
 
     PalletUnqSchedulerScheduledV2: mkDummy('ScheduledV2'),
     PalletUnqSchedulerCallSpec: mkDummy('CallSpec'),
modifiedtests/src/interfaces/unique/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/unique/types.ts
+++ b/tests/src/interfaces/unique/types.ts
@@ -77,8 +77,11 @@
 }
 
 /** @name UpDataStructsCollectionMode */
-export interface UpDataStructsCollectionMode extends Struct {
-  readonly dummyCollectionMode: u32;
+export interface UpDataStructsCollectionMode extends Enum {
+  readonly isNft: boolean;
+  readonly isFungible: boolean;
+  readonly asFungible: u32;
+  readonly isReFungible: boolean;
 }
 
 /** @name UpDataStructsCollectionStats */
@@ -88,6 +91,22 @@
   readonly alive: u32;
 }
 
+/** @name UpDataStructsCreateCollectionData */
+export interface UpDataStructsCreateCollectionData extends Struct {
+  readonly mode: UpDataStructsCollectionMode;
+  readonly access: Option<UpDataStructsAccessMode>;
+  readonly name: Vec<u16>;
+  readonly description: Vec<u16>;
+  readonly tokenPrefix: Bytes;
+  readonly offchainSchema: Bytes;
+  readonly schemaVersion: Option<UpDataStructsSchemaVersion>;
+  readonly pendingSponsor: Option<AccountId>;
+  readonly limits: Option<UpDataStructsCollectionLimits>;
+  readonly variableOnChainSchema: Bytes;
+  readonly constOnChainSchema: Bytes;
+  readonly metaUpdatePermission: Option<UpDataStructsMetaUpdatePermission>;
+}
+
 /** @name UpDataStructsCreateItemData */
 export interface UpDataStructsCreateItemData extends Struct {
   readonly dummyCreateItemData: u32;
@@ -101,8 +120,9 @@
 }
 
 /** @name UpDataStructsSchemaVersion */
-export interface UpDataStructsSchemaVersion extends Struct {
-  readonly dummySchemaVersion: u32;
+export interface UpDataStructsSchemaVersion extends Enum {
+  readonly isImageUrl: boolean;
+  readonly isUnique: boolean;
 }
 
 /** @name UpDataStructsSponsorshipState */
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -95,6 +95,32 @@
   return TransactionStatus.Fail;
 }
 
+export function executeTransaction(api: ApiPromise, sender: IKeyringPair, transaction: SubmittableExtrinsic<'promise'>): Promise<EventRecord[]> {
+  return new Promise(async (res, rej) => {
+    try {
+      await transaction.signAndSend(sender, ({events, status}) => {
+        if (!status.isInBlock && !status.isFinalized) return;
+        for (const {event} of events) {
+          if (api.events.system.ExtrinsicSuccess.is(event)) {
+            res(events);
+          } else if (api.events.system.ExtrinsicFailed.is(event)) {
+            const {data: [error]} = event;
+            if (error.isModule) {
+              const decoded = api.registry.findMetaError(error.asModule);
+              const {method, section} = decoded;
+              rej(new Error(`${section}.${method}`));
+            } else {
+              rej(new Error(error.toString()));
+            }
+          }
+        }
+      });
+    } catch (e) {
+      rej(e);
+    }
+  });
+}
+
 export function
 submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
   /* eslint no-async-promise-executor: "off" */
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -49,7 +49,7 @@
     };
   } else if ('Substrate' in input) {
     return input;
-  }else if ('substrate' in input) {
+  } else if ('substrate' in input) {
     return {
       Substrate: (input as any).substrate,
     };
@@ -116,15 +116,15 @@
 
 export interface IChainLimits {
   collectionNumbersLimit: number;
-	accountTokenOwnershipLimit: number;
-	collectionsAdminsLimit: number;
-	customDataLimit: number;
-	nftSponsorTransferTimeout: number;
-	fungibleSponsorTransferTimeout: number;
-	refungibleSponsorTransferTimeout: number;
-	offchainSchemaLimit: number;
-	variableOnChainSchemaLimit: number;
-	constOnChainSchemaLimit: number;
+  accountTokenOwnershipLimit: number;
+  collectionsAdminsLimit: number;
+  customDataLimit: number;
+  nftSponsorTransferTimeout: number;
+  fungibleSponsorTransferTimeout: number;
+  refungibleSponsorTransferTimeout: number;
+  offchainSchemaLimit: number;
+  variableOnChainSchemaLimit: number;
+  constOnChainSchemaLimit: number;
 }
 
 export interface IReFungibleTokenDataType {
@@ -283,7 +283,7 @@
       modeprm = {refungible: null};
     }
 
-    const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+    const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getCreateCollectionResult(events);
 
@@ -329,7 +329,7 @@
 
     // Run the CreateCollection transaction
     const alicePrivateKey = privateKey('//Alice');
-    const tx = api.tx.unique.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm as any);
+    const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any});
     const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
     const result = getCreateCollectionResult(events);
 
@@ -557,7 +557,7 @@
 
   await usingApi(async (api) => {
 
-    const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+    const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
@@ -569,7 +569,7 @@
 
   await usingApi(async (api) => {
 
-    const tx = api.tx.unique.setTransfersEnabledFlag (collectionId, enabled);
+    const tx = api.tx.unique.setTransfersEnabledFlag(collectionId, enabled);
     const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
     const result = getGenericResult(events);
 
@@ -811,8 +811,7 @@
 }
 
 export async function
-getFreeBalance(account: IKeyringPair) : Promise<bigint>
-{
+getFreeBalance(account: IKeyringPair): Promise<bigint> {
   let balance = 0n;
   await usingApi(async (api) => {
     balance = BigInt((await api.query.system.account(account.address)).data.free.toString());