git.delta.rocks / unique-network / refs/commits / 3ee9e950e03f

difftreelog

refactor move properties around

Yaroslav Bolyukin2022-05-27parent: #a034a2f.patch.diff
in: master

18 files changed

modifiedclient/rpc/src/lib.rsdiffbeforeafterboth
--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -73,13 +73,6 @@
 		token: TokenId,
 		at: Option<BlockHash>,
 	) -> Result<Option<CrossAccountId>>;
-	#[rpc(name = "unique_constMetadata")]
-	fn const_metadata(
-		&self,
-		collection: CollectionId,
-		token: TokenId,
-		at: Option<BlockHash>,
-	) -> Result<Vec<u8>>;
 
 	#[rpc(name = "unique_collectionProperties")]
 	fn collection_properties(
@@ -418,9 +411,6 @@
 	);
 	pass_method!(
 		topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api
-	);
-	pass_method!(
-		const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>, unique_api
 	);
 	pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);
 	pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -86,8 +86,6 @@
 	let name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();
 	let description = create_u16_data::<MAX_COLLECTION_DESCRIPTION_LENGTH>();
 	let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();
-	let offchain_schema = create_data::<OFFCHAIN_SCHEMA_LIMIT>();
-	let const_on_chain_schema = create_data::<CONST_ON_CHAIN_SCHEMA_LIMIT>();
 	handler(
 		owner,
 		CreateCollectionData {
@@ -95,8 +93,6 @@
 			name,
 			description,
 			token_prefix,
-			offchain_schema,
-			const_on_chain_schema,
 			..Default::default()
 		},
 	)
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -55,7 +55,6 @@
 	SponsoringRateLimit,
 	budget::Budget,
 	COLLECTION_FIELD_LIMIT,
-	CollectionField,
 	PhantomType,
 	Property,
 	Properties,
@@ -77,6 +76,8 @@
 	RmrkPartType,
 	RmrkTheme,
 	RmrkNftChild,
+	CollectionPermissions,
+	SchemaVersion,
 };
 
 pub use pallet::*;
@@ -433,17 +434,6 @@
 		Hasher = Blake2_128Concat,
 		Key = CollectionId,
 		Value = PropertiesPermissionMap,
-		QueryKind = ValueQuery,
-	>;
-
-	/// Large variable-size collection fields are extracted here
-	#[pallet::storage]
-	pub type CollectionData<T> = StorageNMap<
-		Key = (
-			Key<Twox64Concat, CollectionId>,
-			Key<Twox64Concat, CollectionField>,
-		),
-		Value = BoundedVec<u8, ConstU32<COLLECTION_FIELD_LIMIT>>,
 		QueryKind = ValueQuery,
 	>;
 
@@ -505,19 +495,37 @@
 			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {
 				use up_data_structs::{CollectionVersion1, CollectionVersion2};
 				<CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {
-					Self::set_field_raw(
-						id,
-						CollectionField::OffchainSchema,
-						v.offchain_schema.clone().into_inner(),
-					)
-					.expect("data has lower bounds than field");
-					Self::set_field_raw(
+					let mut props = Vec::new();
+					if !v.offchain_schema.is_empty() {
+						props.push(Property {
+							key: b"_old_offchainSchema".to_vec().try_into().unwrap(),
+							value: v.offchain_schema.clone().into_inner().try_into().expect("offchain schema too big"),
+						});
+					}
+					if !v.variable_on_chain_schema.is_empty() {
+						props.push(Property {
+							key: b"_old_variableOnChainSchema".to_vec().try_into().unwrap(),
+							value: v.variable_on_chain_schema.clone().into_inner().try_into().expect("offchain schema too big"),
+						});
+					}
+					if !v.const_on_chain_schema.is_empty() {
+						props.push(Property {
+							key: b"_old_constOnChainSchema".to_vec().try_into().unwrap(),
+							value: v.const_on_chain_schema.clone().into_inner().try_into().expect("offchain schema too big"),
+						});
+					}
+					props.push(Property {
+						key: b"_old_schemaVersion".to_vec().try_into().unwrap(),
+						value: match v.schema_version {
+							SchemaVersion::ImageURL => b"ImageUrl".as_slice(),
+							SchemaVersion::Unique => b"Unique".as_slice(),
+						}.to_vec().try_into().unwrap(),
+					});
+					Self::set_scoped_collection_properties(
 						id,
-						CollectionField::ConstOnChainSchema,
-						v.const_on_chain_schema.clone().into_inner(),
-					)
-					.expect("data has lower bounds than field");
-
+						PropertyScope::None,
+						props.into_iter(),
+					).expect("existing data larger than properties");
 					Some(CollectionVersion2::from(v))
 				});
 			}
@@ -587,7 +595,6 @@
 			owner_can_transfer: Some(limits.owner_can_transfer()),
 			owner_can_destroy: Some(limits.owner_can_destroy()),
 			transfers_enabled: Some(limits.transfers_enabled()),
-			nesting_rule: Some(limits.nesting_rule().clone()),
 		};
 
 		Some(effective_limits)
@@ -599,12 +606,10 @@
 			description,
 			owner,
 			mode,
-			access,
 			token_prefix,
-			mint_mode,
-			schema_version,
 			sponsorship,
 			limits,
+			permissions,
 		} = <CollectionById<T>>::get(collection)?;
 
 		let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)
@@ -628,28 +633,45 @@
 			description: description.into_inner(),
 			owner,
 			mode,
-			access,
 			token_prefix: token_prefix.into_inner(),
-			mint_mode,
-			schema_version,
 			sponsorship,
 			limits,
-			offchain_schema: <CollectionData<T>>::get((
-				collection,
-				CollectionField::OffchainSchema,
-			))
-			.into_inner(),
-			const_on_chain_schema: <CollectionData<T>>::get((
-				collection,
-				CollectionField::ConstOnChainSchema,
-			))
-			.into_inner(),
+			permissions,
 			token_property_permissions,
 			properties,
 		})
 	}
 }
 
+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
+			}
+		)*
+	}};
+}
+macro_rules! limit_default_clone {
+	($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
+		$(
+			if let Some($new) = $new.$field.clone() {
+				let $old = $old.$field($($arg)?);
+				let _ = $new;
+				let _ = $old;
+				$check
+			} else {
+				$new.$field = $old.$field.clone()
+			}
+		)*
+	}};
+}
+
 impl<T: Config> Pallet<T> {
 	pub fn init_collection(
 		owner: T::AccountId,
@@ -681,11 +703,8 @@
 			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,
-			schema_version: data.schema_version.unwrap_or_default(),
 			sponsorship: data
 				.pending_sponsor
 				.map(SponsorshipState::Unconfirmed)
@@ -694,6 +713,10 @@
 				.limits
 				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))
 				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,
+			permissions: data
+				.permissions
+				.map(|permissions| Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions))
+				.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,
 		};
 
 		let mut collection_properties = up_data_structs::CollectionProperties::get();
@@ -732,18 +755,6 @@
 		<CreatedCollectionCount<T>>::put(created_count);
 		<Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));
 		<CollectionById<T>>::insert(id, collection);
-		Self::set_field_raw(
-			id,
-			CollectionField::OffchainSchema,
-			data.offchain_schema.into_inner(),
-		)
-		.expect("data has lower bounds than field");
-		Self::set_field_raw(
-			id,
-			CollectionField::ConstOnChainSchema,
-			data.const_on_chain_schema.into_inner(),
-		)
-		.expect("data has lower bounds than field");
 		Ok(id)
 	}
 
@@ -766,7 +777,6 @@
 
 		<DestroyedCollectionCount<T>>::put(destroyed_collections);
 		<CollectionById<T>>::remove(collection.id);
-		<CollectionData<T>>::remove_prefix((collection.id,), None);
 		<AdminAmount<T>>::remove(collection.id);
 		<IsAdmin<T>>::remove_prefix((collection.id,), None);
 		<Allowlist<T>>::remove_prefix((collection.id,), None);
@@ -866,6 +876,18 @@
 		Ok(())
 	}
 
+	// For migrations
+	pub fn set_property_permission_unchecked(
+		collection: CollectionId,
+		property_permission: PropertyKeyPermission,
+	) -> DispatchResult {
+		<CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {
+			permissions.try_set(property_permission.key, property_permission.permission)
+		})
+		.map_err(<Error<T>>::from)?;
+		Ok(())
+	}
+
 	pub fn set_property_permission(
 		collection: &CollectionHandle<T>,
 		sender: &T::CrossAccountId,
@@ -989,35 +1011,6 @@
 		Ok(key_permissions)
 	}
 
-	fn set_field_raw(
-		collection_id: CollectionId,
-		field: CollectionField,
-		value: Vec<u8>,
-	) -> DispatchResult {
-		if !value.is_empty() {
-			<CollectionData<T>>::insert(
-				(collection_id, field),
-				BoundedVec::try_from(value).map_err(|_| <Error<T>>::CollectionFieldSizeExceeded)?,
-			)
-		} else {
-			<CollectionData<T>>::remove((collection_id, field));
-		}
-		Ok(())
-	}
-
-	pub fn set_field(
-		collection: &CollectionHandle<T>,
-		sender: &T::CrossAccountId,
-		field: CollectionField,
-		value: Vec<u8>,
-	) -> DispatchResult {
-		collection.check_is_owner_or_admin(sender)?;
-
-		// =========
-
-		Self::set_field_raw(collection.id, field, value)
-	}
-
 	pub fn toggle_allowlist(
 		collection: &CollectionHandle<T>,
 		sender: &T::CrossAccountId,
@@ -1077,21 +1070,6 @@
 		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,
@@ -1126,6 +1104,15 @@
 		);
 		Ok(new_limit)
 	}
+	pub fn clamp_permissions(
+		mode: CollectionMode,
+		old_limit: &CollectionPermissions,
+		mut new_limit: CollectionPermissions,
+	) -> Result<CollectionPermissions, DispatchError> {
+		limit_default_clone!(old_limit, new_limit,
+		);
+		Ok(new_limit)
+	}
 }
 
 #[macro_export]
@@ -1253,7 +1240,6 @@
 	fn last_token_id(&self) -> TokenId;
 
 	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;
-	fn const_metadata(&self, token: TokenId) -> Vec<u8>;
 	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;
 	fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;
 	/// Amount of unique collection tokens
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -51,27 +51,27 @@
 		<SelfWeightOf<T>>::burn_item()
 	}
 
-	fn set_collection_properties(amount: u32) -> Weight {
+	fn set_collection_properties(_amount: u32) -> Weight {
 		// Error
 		0
 	}
 
-	fn delete_collection_properties(amount: u32) -> Weight {
+	fn delete_collection_properties(_amount: u32) -> Weight {
 		// Error
 		0
 	}
 
-	fn set_token_properties(amount: u32) -> Weight {
+	fn set_token_properties(_amount: u32) -> Weight {
 		// Error
 		0
 	}
 
-	fn delete_token_properties(amount: u32) -> Weight {
+	fn delete_token_properties(_amount: u32) -> Weight {
 		// Error
 		0
 	}
 
-	fn set_property_permissions(amount: u32) -> Weight {
+	fn set_property_permissions(_amount: u32) -> Weight {
 		// Error
 		0
 	}
@@ -320,9 +320,6 @@
 
 	fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {
 		None
-	}
-	fn const_metadata(&self, _token: TokenId) -> Vec<u8> {
-		Vec::new()
 	}
 
 	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -168,7 +168,7 @@
 			.checked_sub(amount)
 			.ok_or(<CommonError<T>>::TokenValueTooLow)?;
 
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(owner)?;
 		}
 
@@ -210,7 +210,7 @@
 			<CommonError<T>>::TransferNotAllowed,
 		);
 
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(from)?;
 			collection.check_allowlist(to)?;
 		}
@@ -280,7 +280,7 @@
 	) -> DispatchResult {
 		if !collection.is_owner_or_admin(sender) {
 			ensure!(
-				collection.mint_mode,
+				collection.permissions.mint_mode(),
 				<CommonError<T>>::PublicMintingNotAllowed
 			);
 			collection.check_allowlist(sender)?;
@@ -380,7 +380,7 @@
 		spender: &T::CrossAccountId,
 		amount: u128,
 	) -> DispatchResult {
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(owner)?;
 			collection.check_allowlist(spender)?;
 		}
@@ -408,7 +408,7 @@
 		if spender.conv_eq(from) {
 			return Ok(None);
 		}
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			// `from`, `to` checked in [`transfer`]
 			collection.check_allowlist(spender)?;
 		}
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -29,9 +29,7 @@
 const SEED: u32 = 1;
 
 fn create_max_item_data<T: Config>(owner: T::CrossAccountId) -> CreateItemData<T> {
-	let const_data = create_data::<CUSTOM_DATA_LIMIT>();
 	CreateItemData::<T> {
-		const_data,
 		owner,
 		properties: Default::default(),
 	}
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -116,7 +116,6 @@
 ) -> Result<CreateItemData<T>, DispatchError> {
 	match data {
 		up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData::<T> {
-			const_data: data.const_data,
 			properties: data.properties,
 			owner: to.clone(),
 		}),
@@ -376,12 +375,6 @@
 
 	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId> {
 		<TokenData<T>>::get((self.id, token)).map(|t| t.owner)
-	}
-	fn const_metadata(&self, token: TokenId) -> Vec<u8> {
-		<TokenData<T>>::get((self.id, token))
-			.map(|t| t.const_data)
-			.unwrap_or_default()
-			.into_inner()
 	}
 
 	fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -109,15 +109,6 @@
 	}
 }
 
-fn error_unsupported_schema_version() -> Error {
-	alloc::format!(
-		"Unsupported schema version! Support only {:?}",
-		SchemaVersion::ImageURL
-	)
-	.as_str()
-	.into()
-}
-
 #[derive(ToLog)]
 pub enum ERC721Events {
 	Transfer {
@@ -167,16 +158,10 @@
 	/// Returns token's const_metadata
 	#[solidity(rename_selector = "tokenURI")]
 	fn token_uri(&self, token_id: uint256) -> Result<string> {
-		if !matches!(self.schema_version, SchemaVersion::ImageURL) {
-			return Err(error_unsupported_schema_version());
-		}
-
 		self.consume_store_reads(1)?;
-		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
+		let _token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
 		Ok(string::from_utf8_lossy(
-			&<TokenData<T>>::get((self.id, token_id))
-				.ok_or("token not found")?
-				.const_data,
+			todo!()
 		)
 		.into())
 	}
@@ -344,7 +329,6 @@
 			self,
 			&caller,
 			CreateItemData::<T> {
-				const_data: BoundedVec::default(),
 				properties: BoundedVec::default(),
 				owner: to,
 			},
@@ -366,10 +350,6 @@
 		token_id: uint256,
 		token_uri: string,
 	) -> Result<bool> {
-		if !matches!(self.schema_version, SchemaVersion::ImageURL) {
-			return Err(error_unsupported_schema_version());
-		}
-
 		let caller = T::CrossAccountId::from_eth(caller);
 		let to = T::CrossAccountId::from_eth(to);
 		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
@@ -385,13 +365,12 @@
 			return Err("item id should be next".into());
 		}
 
+		todo!("token uri");
+
 		<Pallet<T>>::create_item(
 			self,
 			&caller,
 			CreateItemData::<T> {
-				const_data: Vec::<u8>::from(token_uri)
-					.try_into()
-					.map_err(|_| "token uri is too long")?,
 				properties: BoundedVec::default(),
 				owner: to,
 			},
@@ -477,7 +456,6 @@
 		}
 		let data = (0..total_tokens)
 			.map(|_| CreateItemData::<T> {
-				const_data: BoundedVec::default(),
 				properties: BoundedVec::default(),
 				owner: to.clone(),
 			})
@@ -496,10 +474,6 @@
 		to: address,
 		tokens: Vec<(uint256, string)>,
 	) -> Result<bool> {
-		if !matches!(self.schema_version, SchemaVersion::ImageURL) {
-			return Err(error_unsupported_schema_version());
-		}
-
 		let caller = T::CrossAccountId::from_eth(caller);
 		let to = T::CrossAccountId::from_eth(to);
 		let mut expected_index = <TokensMinted<T>>::get(self.id)
@@ -517,10 +491,8 @@
 			}
 			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
 
+			todo!("token uri");
 			data.push(CreateItemData::<T> {
-				const_data: Vec::<u8>::from(token_uri)
-					.try_into()
-					.map_err(|_| "token uri is too long")?,
 				properties: BoundedVec::default(),
 				owner: to.clone(),
 			});
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -33,7 +33,7 @@
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
-use sp_std::{vec::Vec, vec};
+use sp_std::{vec::Vec, vec, collections::btree_set::BTreeSet};
 use core::ops::Deref;
 use sp_std::collections::btree_map::BTreeMap;
 use codec::{Encode, Decode, MaxEncodedLen};
@@ -52,6 +52,7 @@
 #[struct_versioning::versioned(version = 2, upper)]
 #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
 pub struct ItemData<CrossAccountId> {
+	#[version(..2)]
 	pub const_data: BoundedVec<u8, CustomDataLimit>,
 
 	#[version(..2)]
@@ -148,9 +149,45 @@
 	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
 		fn on_runtime_upgrade() -> Weight {
 			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {
-				<TokenData<T>>::translate_values::<ItemDataVersion1<T::CrossAccountId>, _>(|v| {
+				let mut had_consts = BTreeSet::new();
+				<TokenData<T>>::translate::<ItemDataVersion1<T::CrossAccountId>, _>(|(collection, token), v| {
+					let mut props = vec![];
+					if !v.const_data.is_empty() {
+						props.push(Property {
+							key: b"_old_constData".to_vec().try_into().unwrap(),
+							value: v.const_data.clone().into_inner().try_into().expect("const too long"),
+						});
+						had_consts.insert(collection);
+					}
+					if !v.variable_data.is_empty() {
+						props.push(Property {
+							key: b"_old_variableData".to_vec().try_into().unwrap(),
+							value: v.variable_data.clone().into_inner().try_into().expect("variable too long"),
+						})
+					}
+					if !props.is_empty() {
+						Self::set_scoped_token_properties(
+							collection,
+							token,
+							PropertyScope::None,
+							props.into_iter(),
+						).expect("existing token data exceeds property storage");
+					}
 					Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))
-				})
+				});
+				for collection in had_consts {
+					<PalletCommon<T>>::set_property_permission_unchecked(
+						collection,
+						PropertyKeyPermission {
+							key: b"_old_constData".to_vec().try_into().unwrap(),
+							permission: PropertyPermission {
+								mutable: false,
+								collection_admin: true,
+								token_owner: false,
+							},
+						}
+					).expect("failed to configure permission");
+				}
 			}
 
 			0
@@ -267,7 +304,7 @@
 			<CommonError<T>>::NoPermission
 		);
 
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(sender)?;
 		}
 
@@ -493,7 +530,7 @@
 			<CommonError<T>>::NoPermission
 		);
 
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(from)?;
 			collection.check_allowlist(to)?;
 		}
@@ -579,7 +616,7 @@
 	) -> DispatchResult {
 		if !collection.is_owner_or_admin(sender) {
 			ensure!(
-				collection.mint_mode,
+				collection.permissions.mint_mode(),
 				<CommonError<T>>::PublicMintingNotAllowed
 			);
 			collection.check_allowlist(sender)?;
@@ -639,7 +676,7 @@
 				<TokenData<T>>::insert(
 					(collection.id, token),
 					ItemData {
-						const_data: data.const_data.clone(),
+						// const_data: data.const_data.clone(),
 						owner: data.owner.clone(),
 					},
 				);
@@ -756,7 +793,7 @@
 		token: TokenId,
 		spender: Option<&T::CrossAccountId>,
 	) -> DispatchResult {
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(sender)?;
 			if let Some(spender) = spender {
 				collection.check_allowlist(spender)?;
@@ -791,7 +828,7 @@
 		if spender.conv_eq(from) {
 			return Ok(());
 		}
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			// `from`, `to` checked in [`transfer`]
 			collection.check_allowlist(spender)?;
 		}
@@ -875,7 +912,7 @@
 			);
 			Ok(())
 		}
-		match handle.limits.nesting_rule() {
+		match handle.permissions.nesting() {
 			NestingRule::Disabled => fail!(<CommonError<T>>::NestingIsDisabled),
 			NestingRule::Owner => {
 				ensure_sender_allowed::<T>(handle.id, under, from, sender, nesting_budget)?
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -403,10 +403,8 @@
         nft_type: NftType,
         properties: impl Iterator<Item=Property>
     ) -> Result<TokenId, DispatchError> {
+        todo!("store nft type");
         let data = CreateNftExData {
-            const_data: nft_type.encode()
-                .try_into()
-                .map_err(|_| <Error<T>>::NftTypeEncodeError)?,
             properties: BoundedVec::default(),
             owner: owner.clone(),
         };
@@ -528,13 +526,8 @@
         Ok(nft_property)
     }
 
-    pub fn get_nft_type(collection_id: CollectionId, token_id: TokenId) -> Result<NftType, DispatchError> {
-        let token_data = <TokenData<T>>::get((collection_id, token_id))
-            .ok_or(<Error<T>>::NoAvailableNftId)?;
-
-        let mut const_data = token_data.const_data.as_slice();
-
-        NftType::decode(&mut const_data).map_err(|_| <Error<T>>::NoAvailableNftId.into())
+    pub fn get_nft_type(_collection_id: CollectionId, _token_id: TokenId) -> Result<NftType, DispatchError> {
+        todo!("should get it from properties?")
     }
 
     pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult {
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -336,11 +336,6 @@
 	fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {
 		None
 	}
-	fn const_metadata(&self, token: TokenId) -> Vec<u8> {
-		<TokenData<T>>::get((self.id, token))
-			.const_data
-			.into_inner()
-	}
 
 	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {
 		None
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -321,7 +321,7 @@
 			<CommonError<T>>::TransferNotAllowed
 		);
 
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(from)?;
 			collection.check_allowlist(to)?;
 		}
@@ -424,7 +424,7 @@
 	) -> DispatchResult {
 		if !collection.is_owner_or_admin(sender) {
 			ensure!(
-				collection.mint_mode,
+				collection.permissions.mint_mode(),
 				<CommonError<T>>::PublicMintingNotAllowed
 			);
 			collection.check_allowlist(sender)?;
@@ -566,7 +566,7 @@
 		token: TokenId,
 		amount: u128,
 	) -> DispatchResult {
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(sender)?;
 			collection.check_allowlist(spender)?;
 		}
@@ -598,7 +598,7 @@
 		if spender.conv_eq(from) {
 			return Ok(None);
 		}
-		if collection.access == AccessMode::AllowList {
+		if collection.permissions.access() == AccessMode::AllowList {
 			// `from`, `to` checked in [`transfer`]
 			collection.check_allowlist(spender)?;
 		}
modifiedpallets/unique/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/unique/src/benchmarking.rs
+++ b/pallets/unique/src/benchmarking.rs
@@ -130,26 +130,6 @@
 		let collection = create_nft_collection::<T>(caller.clone())?;
 	}: _(RawOrigin::Signed(caller.clone()), collection, false)
 
-	set_offchain_schema {
-		let b in 0..OFFCHAIN_SCHEMA_LIMIT;
-
-		let caller: T::AccountId = account("caller", 0, SEED);
-		let collection = create_nft_collection::<T>(caller.clone())?;
-		let data = create_var_data(b);
-	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data)
-
-	set_const_on_chain_schema {
-		let b in 0..CONST_ON_CHAIN_SCHEMA_LIMIT;
-
-		let caller: T::AccountId = account("caller", 0, SEED);
-		let collection = create_nft_collection::<T>(caller.clone())?;
-		let data = create_var_data(b);
-	}: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
-
-	set_schema_version {
-		let caller: T::AccountId = account("caller", 0, SEED);
-		let collection = create_nft_collection::<T>(caller.clone())?;
-	}: set_schema_version(RawOrigin::Signed(caller.clone()), collection, SchemaVersion::Unique)
 
 	set_collection_limits{
 		let caller: T::AccountId = account("caller", 0, SEED);
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -35,10 +35,10 @@
 use frame_system::{self as system, ensure_signed};
 use sp_runtime::{sp_std::prelude::Vec};
 use up_data_structs::{
-	CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH,
+	MAX_COLLECTION_NAME_LENGTH,
 	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,
-	CollectionLimits, CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState,
-	CreateCollectionData, CreateItemExData, budget, CollectionField, Property, PropertyKey,
+	CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,
+	CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,
 	PropertyKeyPermission,
 };
 use pallet_evm::account::CrossAccountId;
@@ -162,6 +162,8 @@
 		/// * collection_id: Globally unique collection identifier.
 		CollectionLimitSet(CollectionId),
 
+		CollectionPermissionSet(CollectionId),
+
 		/// Mint permission	was set
 		///
 		/// # Arguments
@@ -417,67 +419,6 @@
 			));
 
 			Ok(())
-		}
-
-		/// Toggle between normal and allow list access for the methods with access for `Anyone`.
-		///
-		/// # Permissions
-		///
-		/// * Collection Owner.
-		///
-		/// # Arguments
-		///
-		/// * collection_id.
-		///
-		/// * mode: [AccessMode]
-		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]
-		#[transactional]
-		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult
-		{
-			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)?;
-
-			target_collection.access = mode.clone();
-
-			<Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(
-				collection_id,
-				mode
-			));
-
-			target_collection.save()
-		}
-
-		/// Allows Anyone to create tokens if:
-		/// * Allow List is enabled, and
-		/// * Address is added to allow list, and
-		/// * This method was called with True parameter
-		///
-		/// # Permissions
-		/// * Collection Owner
-		///
-		/// # Arguments
-		///
-		/// * collection_id.
-		///
-		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.
-		#[weight = <SelfWeightOf<T>>::set_mint_permission()]
-		#[transactional]
-		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult
-		{
-			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)?;
-
-			target_collection.mint_mode = mint_permission;
-
-			<Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(
-				collection_id
-			));
-
-			target_collection.save()
 		}
 
 		/// Change the owner of the collection.
@@ -941,118 +882,42 @@
 			dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))
 		}
 
-		/// Set schema standard
-		/// ImageURL
-		/// Unique
-		///
-		/// # Permissions
-		///
-		/// * Collection Owner
-		/// * Collection Admin
-		///
-		/// # Arguments
-		///
-		/// * collection_id.
-		///
-		/// * schema: SchemaVersion: enum
-		#[weight = <SelfWeightOf<T>>::set_schema_version()]
+		#[weight = <SelfWeightOf<T>>::set_collection_limits()]
 		#[transactional]
-		pub fn set_schema_version(
+		pub fn set_collection_limits(
 			origin,
 			collection_id: CollectionId,
-			version: SchemaVersion
+			new_limit: CollectionLimits,
 		) -> DispatchResult {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
-			target_collection.check_is_owner_or_admin(&sender)?;
-			target_collection.schema_version = version;
-
-			<Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(
-				collection_id
-			));
-
-			target_collection.save()
-		}
-
-		/// Set off-chain data schema.
-		///
-		/// # Permissions
-		///
-		/// * Collection Owner
-		/// * Collection Admin
-		///
-		/// # Arguments
-		///
-		/// * collection_id.
-		///
-		/// * schema: String representing the offchain data schema.
-		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]
-		#[transactional]
-		pub fn set_offchain_schema(
-			origin,
-			collection_id: CollectionId,
-			schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
-		) -> DispatchResult {
-			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
-			let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+			target_collection.check_is_owner(&sender)?;
+			let old_limit = &target_collection.limits;
 
-			// =========
+			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
 
-			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::OffchainSchema, schema.into_inner())?;
-
-			<Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(
+			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
 				collection_id
 			));
-			Ok(())
-		}
 
-		/// Set const on-chain data schema.
-		///
-		/// # Permissions
-		///
-		/// * Collection Owner
-		/// * Collection Admin
-		///
-		/// # Arguments
-		///
-		/// * collection_id.
-		///
-		/// * schema: String representing the const on-chain data schema.
-		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]
-		#[transactional]
-		pub fn set_const_on_chain_schema (
-			origin,
-			collection_id: CollectionId,
-			schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>
-		) -> DispatchResult {
-			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
-			let collection = <CollectionHandle<T>>::try_get(collection_id)?;
-
-			// =========
-
-			<PalletCommon<T>>::set_field(&collection, &sender, CollectionField::ConstOnChainSchema, schema.into_inner())?;
-
-			<Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(
-				collection_id
-			));
-			Ok(())
+			target_collection.save()
 		}
 
 		#[weight = <SelfWeightOf<T>>::set_collection_limits()]
 		#[transactional]
-		pub fn set_collection_limits(
+		pub fn set_collection_permissions(
 			origin,
 			collection_id: CollectionId,
-			new_limit: CollectionLimits,
+			new_limit: CollectionPermissions,
 		) -> DispatchResult {
 			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;
+			let old_limit = &target_collection.permissions;
 
-			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
+			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;
 
-			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
+			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(
 				collection_id
 			));
 
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -186,7 +186,6 @@
 #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct TokenData<CrossAccountId> {
-	pub const_data: Vec<u8>,
 	pub properties: Vec<Property>,
 	pub owner: Option<CrossAccountId>,
 }
@@ -223,7 +222,7 @@
 	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;
 }
 
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
+#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum AccessMode {
 	Normal,
@@ -296,22 +295,26 @@
 pub struct Collection<AccountId> {
 	pub owner: AccountId,
 	pub mode: CollectionMode,
+	#[version(..2)]
 	pub access: AccessMode,
 	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
 	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
 	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
+
+	#[version(..2)]
 	pub mint_mode: bool,
 
 	#[version(..2)]
 	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
 
+	#[version(..2)]
 	pub schema_version: SchemaVersion,
 	pub sponsorship: SponsorshipState<AccountId>,
 
-	#[version(..2)]
-	pub limits: CollectionLimitsVersion1, // Collection private restrictions
-	#[version(2.., upper(limits.into()))]
-	pub limits: CollectionLimitsVersion2,
+	pub limits: CollectionLimits,
+
+	#[version(2.., upper(Default::default()))]
+	pub permissions: CollectionPermissions,
 
 	#[version(..2)]
 	pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,
@@ -329,27 +332,16 @@
 pub struct RpcCollection<AccountId> {
 	pub owner: AccountId,
 	pub mode: CollectionMode,
-	pub access: AccessMode,
 	pub name: Vec<u16>,
 	pub description: Vec<u16>,
 	pub token_prefix: Vec<u8>,
-	pub mint_mode: bool,
-	pub offchain_schema: Vec<u8>,
-	pub schema_version: SchemaVersion,
 	pub sponsorship: SponsorshipState<AccountId>,
 	pub limits: CollectionLimits,
-	pub const_on_chain_schema: Vec<u8>,
+	pub permissions: CollectionPermissions,
 	pub token_property_permissions: Vec<PropertyKeyPermission>,
 	pub properties: Vec<Property>,
 }
 
-#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
-#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-pub enum CollectionField {
-	ConstOnChainSchema,
-	OffchainSchema,
-}
-
 #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]
 #[derivative(Debug, Default(bound = ""))]
 pub struct CreateCollectionData<AccountId> {
@@ -359,11 +351,9 @@
 	pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
 	pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
 	pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
-	pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
-	pub schema_version: Option<SchemaVersion>,
 	pub pending_sponsor: Option<AccountId>,
 	pub limits: Option<CollectionLimits>,
-	pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,
+	pub permissions: Option<CollectionPermissions>,
 	pub token_property_permissions: CollectionPropertiesPermissionsVec,
 	pub properties: CollectionPropertiesVec,
 }
@@ -375,7 +365,6 @@
 	BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;
 
 /// All fields are wrapped in `Option`s, where None means chain default
-#[struct_versioning::versioned(version = 2, upper)]
 #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct CollectionLimits {
@@ -395,9 +384,6 @@
 	pub owner_can_transfer: Option<bool>,
 	pub owner_can_destroy: Option<bool>,
 	pub transfers_enabled: Option<bool>,
-
-	#[version(2.., upper(None))]
-	pub nesting_rule: Option<NestingRule>,
 }
 
 impl CollectionLimits {
@@ -444,9 +430,26 @@
 			SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),
 		}
 	}
-	pub fn nesting_rule(&self) -> &NestingRule {
+}
+
+#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+pub struct CollectionPermissions {
+	pub access: Option<AccessMode>,
+	pub mint_mode: Option<bool>,
+	pub nesting: Option<NestingRule>,
+}
+
+impl CollectionPermissions {
+	pub fn access(&self) -> AccessMode {
+		self.access.unwrap_or(AccessMode::Normal)
+	}
+	pub fn mint_mode(&self) -> bool {
+		self.mint_mode.unwrap_or(false)
+	}
+	pub fn nesting(&self) -> &NestingRule {
 		static DEFAULT: NestingRule = NestingRule::Disabled;
-		self.nesting_rule.as_ref().unwrap_or(&DEFAULT)
+		self.nesting.as_ref().unwrap_or(&DEFAULT)
 	}
 }
 
@@ -520,8 +523,6 @@
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
 #[derivative(Debug)]
 pub struct CreateNftExData<CrossAccountId> {
-	#[derivative(Debug(format_with = "bounded::vec_debug"))]
-	pub const_data: BoundedVec<u8, CustomDataLimit>,
 	#[derivative(Debug(format_with = "bounded::vec_debug"))]
 	pub properties: CollectionPropertiesVec,
 	pub owner: CrossAccountId,
modifiedprimitives/rpc/src/lib.rsdiffbeforeafterboth
--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -41,7 +41,6 @@
 
 		fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>>;
 		fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>>;
-		fn const_metadata(collection: CollectionId, token: TokenId) -> Result<Vec<u8>>;
 
 		fn collection_properties(collection: CollectionId, properties: Option<Vec<Vec<u8>>>) -> Result<Vec<Property>>;
 
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -29,9 +29,6 @@
 
                     Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))
                 }
-                fn const_metadata(collection: CollectionId, token: TokenId) -> Result<Vec<u8>, DispatchError> {
-                    dispatch_unique_runtime!(collection.const_metadata(token))
-                }
 
                 fn collection_properties(
                     collection: CollectionId,
@@ -73,7 +70,6 @@
                     keys: Option<Vec<Vec<u8>>>
                 ) -> Result<TokenData<CrossAccountId>, DispatchError> {
                     let token_data = TokenData {
-                        const_data: Self::const_metadata(collection, token_id)?,
                         properties: Self::token_properties(collection, token_id, keys)?,
                         owner: Self::token_owner(collection, token_id)?
                     };
modifiedruntime/tests/src/tests.rsdiffbeforeafterboth
before · runtime/tests/src/tests.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// Tests to be written here18use crate::{Test, TestCrossAccountId, CollectionCreationPrice, Origin, Unique, new_test_ext};19use up_data_structs::{20	COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,21	CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, TokenId,22	MAX_TOKEN_OWNERSHIP, CreateCollectionData, CollectionField, SchemaVersion, CollectionMode,23	AccessMode,24};25use frame_support::{assert_noop, assert_ok, assert_err};26use sp_std::convert::TryInto;27use pallet_evm::account::CrossAccountId;28use pallet_common::Error as CommonError;29use pallet_unique::Error as UniqueError;3031fn add_balance(user: u64, value: u64) {32	const DONOR_USER: u64 = 999;33	assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(34		Origin::root(),35		DONOR_USER,36		value,37		038	));39	assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(40		Origin::root(),41		DONOR_USER,42		user,43		value44	));45}4647fn default_nft_data() -> CreateNftData {48	CreateNftData {49		const_data: vec![1, 2, 3].try_into().unwrap(),50		properties: vec![].try_into().unwrap(),51	}52}5354fn default_fungible_data() -> CreateFungibleData {55	CreateFungibleData { value: 5 }56}5758fn default_re_fungible_data() -> CreateReFungibleData {59	CreateReFungibleData {60		const_data: vec![1, 2, 3].try_into().unwrap(),61		pieces: 1023,62	}63}6465fn create_test_collection_for_owner(66	mode: &CollectionMode,67	owner: u64,68	id: CollectionId,69) -> CollectionId {70	add_balance(owner, CollectionCreationPrice::get() as u64 + 1);7172	let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();73	let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();74	let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();7576	let data: CreateCollectionData<u64> = CreateCollectionData {77		name: col_name1.try_into().unwrap(),78		description: col_desc1.try_into().unwrap(),79		token_prefix: token_prefix1.try_into().unwrap(),80		mode: mode.clone(),81		..Default::default()82	};8384	let origin1 = Origin::signed(owner);85	assert_ok!(Unique::create_collection_ex(origin1, data));8687	let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();88	let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();89	let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();90	assert_eq!(91		<pallet_common::CollectionById<Test>>::get(id)92			.unwrap()93			.owner,94		owner95	);96	assert_eq!(97		<pallet_common::CollectionById<Test>>::get(id).unwrap().name,98		saved_col_name99	);100	assert_eq!(101		<pallet_common::CollectionById<Test>>::get(id).unwrap().mode,102		*mode103	);104	assert_eq!(105		<pallet_common::CollectionById<Test>>::get(id)106			.unwrap()107			.description,108		saved_description109	);110	assert_eq!(111		<pallet_common::CollectionById<Test>>::get(id)112			.unwrap()113			.token_prefix,114		saved_prefix115	);116	id117}118119fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {120	create_test_collection_for_owner(&mode, 1, id)121}122123fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {124	let origin1 = Origin::signed(1);125	assert_ok!(Unique::create_item(126		origin1,127		collection_id,128		account(1),129		data.clone()130	));131}132133fn account(sub: u64) -> TestCrossAccountId {134	TestCrossAccountId::from_sub(sub)135}136137// Use cases tests region138// #region139140#[test]141fn set_version_schema() {142	new_test_ext().execute_with(|| {143		let origin1 = Origin::signed(1);144		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));145146		assert_ok!(Unique::set_schema_version(147			origin1,148			collection_id,149			SchemaVersion::Unique150		));151		assert_eq!(152			<pallet_common::CollectionById<Test>>::get(collection_id)153				.unwrap()154				.schema_version,155			SchemaVersion::Unique156		);157	});158}159160#[test]161fn check_not_sufficient_founds() {162	new_test_ext().execute_with(|| {163		let acc: u64 = 1;164		<pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();165166		let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();167		let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();168		let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();169170		let data: CreateCollectionData<<Test as frame_system::Config>::AccountId> =171			CreateCollectionData {172				name: name.try_into().unwrap(),173				description: description.try_into().unwrap(),174				token_prefix: token_prefix.try_into().unwrap(),175				mode: CollectionMode::NFT,176				..Default::default()177			};178179		let result = Unique::create_collection_ex(Origin::signed(acc), data);180		assert_err!(result, <CommonError<Test>>::NotSufficientFounds);181	});182}183184#[test]185fn create_fungible_collection_fails_with_large_decimal_numbers() {186	new_test_ext().execute_with(|| {187		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();188		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();189		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();190191		let data: CreateCollectionData<u64> = CreateCollectionData {192			name: col_name1.try_into().unwrap(),193			description: col_desc1.try_into().unwrap(),194			token_prefix: token_prefix1.try_into().unwrap(),195			mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),196			..Default::default()197		};198199		let origin1 = Origin::signed(1);200		assert_noop!(201			Unique::create_collection_ex(origin1, data),202			UniqueError::<Test>::CollectionDecimalPointLimitExceeded203		);204	});205}206207#[test]208fn create_nft_item() {209	new_test_ext().execute_with(|| {210		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));211212		let data = default_nft_data();213		create_test_item(collection_id, &data.clone().into());214215		let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();216		assert_eq!(item.const_data, data.const_data.into_inner());217	});218}219220// Use cases tests region221// #region222#[test]223fn create_nft_multiple_items() {224	new_test_ext().execute_with(|| {225		create_test_collection(&CollectionMode::NFT, CollectionId(1));226227		let origin1 = Origin::signed(1);228229		let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];230231		assert_ok!(Unique::create_multiple_items(232			origin1,233			CollectionId(1),234			account(1),235			items_data236				.clone()237				.into_iter()238				.map(|d| { d.into() })239				.collect()240		));241		for (index, data) in items_data.into_iter().enumerate() {242			let item = <pallet_nonfungible::TokenData<Test>>::get((243				CollectionId(1),244				TokenId((index + 1) as u32),245			))246			.unwrap();247			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());248		}249	});250}251252#[test]253fn create_refungible_item() {254	new_test_ext().execute_with(|| {255		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));256257		let data = default_re_fungible_data();258		create_test_item(collection_id, &data.clone().into());259		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));260		let balance =261			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));262		assert_eq!(item.const_data, data.const_data.into_inner());263		assert_eq!(balance, 1023);264	});265}266267#[test]268fn create_multiple_refungible_items() {269	new_test_ext().execute_with(|| {270		create_test_collection(&CollectionMode::ReFungible, CollectionId(1));271272		let origin1 = Origin::signed(1);273274		let items_data = vec![275			default_re_fungible_data(),276			default_re_fungible_data(),277			default_re_fungible_data(),278		];279280		assert_ok!(Unique::create_multiple_items(281			origin1,282			CollectionId(1),283			account(1),284			items_data285				.clone()286				.into_iter()287				.map(|d| { d.into() })288				.collect()289		));290		for (index, data) in items_data.into_iter().enumerate() {291			let item = <pallet_refungible::TokenData<Test>>::get((292				CollectionId(1),293				TokenId((index + 1) as u32),294			));295			let balance =296				<pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));297			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());298			assert_eq!(balance, 1023);299		}300	});301}302303#[test]304fn create_fungible_item() {305	new_test_ext().execute_with(|| {306		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));307308		let data = default_fungible_data();309		create_test_item(collection_id, &data.into());310311		assert_eq!(312			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),313			5314		);315	});316}317318//#[test]319// fn create_multiple_fungible_items() {320//     new_test_ext().execute_with(|| {321//         default_limits();322323//         create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));324325//         let origin1 = Origin::signed(1);326327//         let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];328329//         assert_ok!(Unique::create_multiple_items(330//             origin1.clone(),331//             1,332//             1,333//             items_data.clone().into_iter().map(|d| { d.into() }).collect()334//         ));335336//         for (index, _) in items_data.iter().enumerate() {337//             assert_eq!(Unique::fungible_item_id(1, (index + 1) as TokenId).value, 5);338//         }339//         assert_eq!(Unique::balance_count(1, 1), 3000);340//         assert_eq!(Unique::address_tokens(1, 1), [1, 2, 3]);341//     });342// }343344#[test]345fn transfer_fungible_item() {346	new_test_ext().execute_with(|| {347		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));348349		let origin1 = Origin::signed(1);350		let origin2 = Origin::signed(2);351352		let data = default_fungible_data();353		create_test_item(collection_id, &data.into());354355		assert_eq!(356			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),357			5358		);359360		// change owner scenario361		assert_ok!(Unique::transfer(362			origin1,363			account(2),364			CollectionId(1),365			TokenId(0),366			5367		));368		assert_eq!(369			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),370			0371		);372373		// split item scenario374		assert_ok!(Unique::transfer(375			origin2.clone(),376			account(3),377			CollectionId(1),378			TokenId(0),379			3380		));381382		// split item and new owner has account scenario383		assert_ok!(Unique::transfer(384			origin2,385			account(3),386			CollectionId(1),387			TokenId(0),388			1389		));390		assert_eq!(391			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),392			1393		);394		assert_eq!(395			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),396			4397		);398	});399}400401#[test]402fn transfer_refungible_item() {403	new_test_ext().execute_with(|| {404		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));405406		// Create RFT 1 in 1023 pieces for account 1407		let data = default_re_fungible_data();408		create_test_item(collection_id, &data.clone().into());409		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));410		assert_eq!(item.const_data, data.const_data.into_inner());411		assert_eq!(412			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),413			1414		);415		assert_eq!(416			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),417			1023418		);419		assert_eq!(420			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),421			true422		);423424		// Account 1 transfers all 1023 pieces of RFT 1 to account 2425		let origin1 = Origin::signed(1);426		let origin2 = Origin::signed(2);427		assert_ok!(Unique::transfer(428			origin1,429			account(2),430			CollectionId(1),431			TokenId(1),432			1023433		));434		assert_eq!(435			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),436			1023437		);438		assert_eq!(439			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),440			0441		);442		assert_eq!(443			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),444			1445		);446		assert_eq!(447			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),448			false449		);450		assert_eq!(451			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),452			true453		);454455		// Account 2 transfers 500 pieces of RFT 1 to account 3456		assert_ok!(Unique::transfer(457			origin2.clone(),458			account(3),459			CollectionId(1),460			TokenId(1),461			500462		));463		assert_eq!(464			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),465			523466		);467		assert_eq!(468			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),469			500470		);471		assert_eq!(472			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),473			1474		);475		assert_eq!(476			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),477			1478		);479		assert_eq!(480			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),481			true482		);483		assert_eq!(484			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),485			true486		);487488		// Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance489		assert_ok!(Unique::transfer(490			origin2,491			account(3),492			CollectionId(1),493			TokenId(1),494			200495		));496		assert_eq!(497			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),498			323499		);500		assert_eq!(501			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),502			700503		);504		assert_eq!(505			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),506			1507		);508		assert_eq!(509			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),510			1511		);512		assert_eq!(513			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),514			true515		);516		assert_eq!(517			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),518			true519		);520	});521}522523#[test]524fn transfer_nft_item() {525	new_test_ext().execute_with(|| {526		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));527528		let data = default_nft_data();529		create_test_item(collection_id, &data.into());530		assert_eq!(531			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),532			1533		);534		assert_eq!(535			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),536			true537		);538539		let origin1 = Origin::signed(1);540		// default scenario541		assert_ok!(Unique::transfer(542			origin1,543			account(2),544			CollectionId(1),545			TokenId(1),546			1547		));548		assert_eq!(549			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),550			0551		);552		assert_eq!(553			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),554			1555		);556		assert_eq!(557			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),558			false559		);560		assert_eq!(561			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),562			true563		);564	});565}566567#[test]568fn transfer_nft_item_wrong_value() {569	new_test_ext().execute_with(|| {570		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));571572		let data = default_nft_data();573		create_test_item(collection_id, &data.into());574		assert_eq!(575			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),576			1577		);578		assert_eq!(579			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),580			true581		);582583		let origin1 = Origin::signed(1);584585		assert_noop!(586			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)587				.map_err(|e| e.error),588			<pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount589		);590	});591}592593#[test]594fn transfer_nft_item_zero_value() {595	new_test_ext().execute_with(|| {596		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));597598		let data = default_nft_data();599		create_test_item(collection_id, &data.into());600		assert_eq!(601			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),602			1603		);604		assert_eq!(605			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),606			true607		);608609		let origin1 = Origin::signed(1);610611		// Transferring 0 amount works on NFT...612		assert_ok!(Unique::transfer(613			origin1,614			account(2),615			CollectionId(1),616			TokenId(1),617			0618		));619		// ... and results in no transfer620		assert_eq!(621			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),622			1623		);624		assert_eq!(625			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),626			true627		);628	});629}630631#[test]632fn nft_approve_and_transfer_from() {633	new_test_ext().execute_with(|| {634		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));635636		let data = default_nft_data();637		create_test_item(collection_id, &data.into());638639		let origin1 = Origin::signed(1);640		let origin2 = Origin::signed(2);641642		assert_eq!(643			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),644			1645		);646		assert_eq!(647			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),648			true649		);650651		// neg transfer_from652		assert_noop!(653			Unique::transfer_from(654				origin2.clone(),655				account(1),656				account(2),657				CollectionId(1),658				TokenId(1),659				1660			)661			.map_err(|e| e.error),662			CommonError::<Test>::ApprovedValueTooLow663		);664665		// do approve666		assert_ok!(Unique::approve(667			origin1,668			account(2),669			CollectionId(1),670			TokenId(1),671			1672		));673		assert_eq!(674			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),675			account(2)676		);677678		assert_ok!(Unique::transfer_from(679			origin2,680			account(1),681			account(3),682			CollectionId(1),683			TokenId(1),684			1685		));686		assert!(687			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()688		);689	});690}691692#[test]693fn nft_approve_and_transfer_from_allow_list() {694	new_test_ext().execute_with(|| {695		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));696697		let origin1 = Origin::signed(1);698		let origin2 = Origin::signed(2);699700		// Create NFT 1 for account 1701		let data = default_nft_data();702		create_test_item(collection_id, &data.clone().into());703		assert_eq!(704			&<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))705				.unwrap()706				.const_data,707			&data.const_data.into_inner()708		);709		assert_eq!(710			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),711			1712		);713		assert_eq!(714			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),715			true716		);717718		// Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list719		assert_ok!(Unique::set_mint_permission(720			origin1.clone(),721			CollectionId(1),722			true723		));724		assert_ok!(Unique::set_public_access_mode(725			origin1.clone(),726			CollectionId(1),727			AccessMode::AllowList728		));729		assert_ok!(Unique::add_to_allow_list(730			origin1.clone(),731			CollectionId(1),732			account(1)733		));734		assert_ok!(Unique::add_to_allow_list(735			origin1.clone(),736			CollectionId(1),737			account(2)738		));739		assert_ok!(Unique::add_to_allow_list(740			origin1.clone(),741			CollectionId(1),742			account(3)743		));744745		// Account 1 approves account 2 for NFT 1746		assert_ok!(Unique::approve(747			origin1.clone(),748			account(2),749			CollectionId(1),750			TokenId(1),751			1752		));753		assert_eq!(754			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),755			account(2)756		);757758		// Account 2 transfers NFT 1 from account 1 to account 3759		assert_ok!(Unique::transfer_from(760			origin2,761			account(1),762			account(3),763			CollectionId(1),764			TokenId(1),765			1766		));767		assert!(768			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()769		);770	});771}772773#[test]774fn refungible_approve_and_transfer_from() {775	new_test_ext().execute_with(|| {776		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));777778		let origin1 = Origin::signed(1);779		let origin2 = Origin::signed(2);780781		// Create RFT 1 in 1023 pieces for account 1782		let data = default_re_fungible_data();783		create_test_item(collection_id, &data.into());784785		assert_eq!(786			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),787			1788		);789		assert_eq!(790			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),791			1023792		);793		assert_eq!(794			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),795			true796		);797798		// Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list799		assert_ok!(Unique::set_mint_permission(800			origin1.clone(),801			CollectionId(1),802			true803		));804		assert_ok!(Unique::set_public_access_mode(805			origin1.clone(),806			CollectionId(1),807			AccessMode::AllowList808		));809		assert_ok!(Unique::add_to_allow_list(810			origin1.clone(),811			CollectionId(1),812			account(1)813		));814		assert_ok!(Unique::add_to_allow_list(815			origin1.clone(),816			CollectionId(1),817			account(2)818		));819		assert_ok!(Unique::add_to_allow_list(820			origin1.clone(),821			CollectionId(1),822			account(3)823		));824825		// Account 1 approves account 2 for 1023 pieces of RFT 1826		assert_ok!(Unique::approve(827			origin1,828			account(2),829			CollectionId(1),830			TokenId(1),831			1023832		));833		assert_eq!(834			<pallet_refungible::Allowance<Test>>::get((835				CollectionId(1),836				TokenId(1),837				account(1),838				account(2)839			)),840			1023841		);842843		// Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3844		assert_ok!(Unique::transfer_from(845			origin2,846			account(1),847			account(3),848			CollectionId(1),849			TokenId(1),850			100851		));852		assert_eq!(853			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),854			1855		);856		assert_eq!(857			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),858			1859		);860		assert_eq!(861			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),862			923863		);864		assert_eq!(865			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),866			100867		);868		assert_eq!(869			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),870			true871		);872		assert_eq!(873			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),874			true875		);876		assert_eq!(877			<pallet_refungible::Allowance<Test>>::get((878				CollectionId(1),879				TokenId(1),880				account(1),881				account(2)882			)),883			923884		);885	});886}887888#[test]889fn fungible_approve_and_transfer_from() {890	new_test_ext().execute_with(|| {891		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));892893		let data = default_fungible_data();894		create_test_item(collection_id, &data.into());895896		let origin1 = Origin::signed(1);897		let origin2 = Origin::signed(2);898899		assert_ok!(Unique::set_mint_permission(900			origin1.clone(),901			CollectionId(1),902			true903		));904		assert_ok!(Unique::set_public_access_mode(905			origin1.clone(),906			CollectionId(1),907			AccessMode::AllowList908		));909		assert_ok!(Unique::add_to_allow_list(910			origin1.clone(),911			CollectionId(1),912			account(1)913		));914		assert_ok!(Unique::add_to_allow_list(915			origin1.clone(),916			CollectionId(1),917			account(2)918		));919		assert_ok!(Unique::add_to_allow_list(920			origin1.clone(),921			CollectionId(1),922			account(3)923		));924925		// do approve926		assert_ok!(Unique::approve(927			origin1.clone(),928			account(2),929			CollectionId(1),930			TokenId(0),931			5932		));933		assert_eq!(934			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),935			5936		);937		assert_ok!(Unique::approve(938			origin1,939			account(3),940			CollectionId(1),941			TokenId(0),942			5943		));944		assert_eq!(945			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),946			5947		);948		assert_eq!(949			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),950			5951		);952953		assert_ok!(Unique::transfer_from(954			origin2.clone(),955			account(1),956			account(3),957			CollectionId(1),958			TokenId(0),959			4960		));961962		assert_eq!(963			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),964			1965		);966967		assert_noop!(968			Unique::transfer_from(969				origin2,970				account(1),971				account(3),972				CollectionId(1),973				TokenId(0),974				4975			)976			.map_err(|e| e.error),977			CommonError::<Test>::ApprovedValueTooLow978		);979	});980}981982#[test]983fn change_collection_owner() {984	new_test_ext().execute_with(|| {985		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));986987		let origin1 = Origin::signed(1);988		assert_ok!(Unique::change_collection_owner(origin1, collection_id, 2));989		assert_eq!(990			<pallet_common::CollectionById<Test>>::get(collection_id)991				.unwrap()992				.owner,993			2994		);995	});996}997998#[test]999fn destroy_collection() {1000	new_test_ext().execute_with(|| {1001		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10021003		let origin1 = Origin::signed(1);1004		assert_ok!(Unique::destroy_collection(origin1, collection_id));1005	});1006}10071008#[test]1009fn burn_nft_item() {1010	new_test_ext().execute_with(|| {1011		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10121013		let origin1 = Origin::signed(1);10141015		let data = default_nft_data();1016		create_test_item(collection_id, &data.into());10171018		// check balance (collection with id = 1, user id = 1)1019		assert_eq!(1020			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1021			11022		);10231024		// burn item1025		assert_ok!(Unique::burn_item(1026			origin1.clone(),1027			collection_id,1028			TokenId(1),1029			11030		));1031		assert_eq!(1032			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1033			01034		);1035	});1036}10371038#[test]1039fn burn_same_nft_item_twice() {1040	new_test_ext().execute_with(|| {1041		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10421043		let origin1 = Origin::signed(1);10441045		let data = default_nft_data();1046		create_test_item(collection_id, &data.into());10471048		// check balance (collection with id = 1, user id = 1)1049		assert_eq!(1050			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1051			11052		);10531054		// burn item1055		assert_ok!(Unique::burn_item(1056			origin1.clone(),1057			collection_id,1058			TokenId(1),1059			11060		));10611062		// burn item again1063		assert_noop!(1064			Unique::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),1065			CommonError::<Test>::TokenNotFound1066		);10671068		assert_eq!(1069			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1070			01071		);1072	});1073}10741075#[test]1076fn burn_fungible_item() {1077	new_test_ext().execute_with(|| {1078		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));10791080		let origin1 = Origin::signed(1);1081		assert_ok!(Unique::add_collection_admin(1082			origin1.clone(),1083			collection_id,1084			account(2)1085		));10861087		let data = default_fungible_data();1088		create_test_item(collection_id, &data.into());10891090		// check balance (collection with id = 1, user id = 1)1091		assert_eq!(1092			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1093			51094		);10951096		// burn item1097		assert_ok!(Unique::burn_item(1098			origin1.clone(),1099			CollectionId(1),1100			TokenId(0),1101			51102		));1103		assert_noop!(1104			Unique::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),1105			CommonError::<Test>::TokenValueTooLow1106		);11071108		assert_eq!(1109			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1110			01111		);1112	});1113}11141115#[test]1116fn burn_fungible_item_with_token_id() {1117	new_test_ext().execute_with(|| {1118		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));11191120		let origin1 = Origin::signed(1);1121		assert_ok!(Unique::add_collection_admin(1122			origin1.clone(),1123			collection_id,1124			account(2)1125		));11261127		let data = default_fungible_data();1128		create_test_item(collection_id, &data.into());11291130		// check balance (collection with id = 1, user id = 1)1131		assert_eq!(1132			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1133			51134		);11351136		// Try to burn item using Token ID1137		assert_noop!(1138			Unique::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),1139			<pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId1140		);1141	});1142}1143#[test]1144fn burn_refungible_item() {1145	new_test_ext().execute_with(|| {1146		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));1147		let origin1 = Origin::signed(1);11481149		assert_ok!(Unique::set_mint_permission(1150			origin1.clone(),1151			collection_id,1152			true1153		));1154		assert_ok!(Unique::set_public_access_mode(1155			origin1.clone(),1156			collection_id,1157			AccessMode::AllowList1158		));1159		assert_ok!(Unique::add_to_allow_list(1160			origin1.clone(),1161			collection_id,1162			account(1)1163		));11641165		assert_ok!(Unique::add_collection_admin(1166			origin1.clone(),1167			collection_id,1168			account(2)1169		));11701171		let data = default_re_fungible_data();1172		create_test_item(collection_id, &data.into());11731174		// check balance (collection with id = 1, user id = 2)1175		assert_eq!(1176			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),1177			11178		);1179		assert_eq!(1180			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1181			10231182		);11831184		// burn item1185		assert_ok!(Unique::burn_item(1186			origin1.clone(),1187			collection_id,1188			TokenId(1),1189			10231190		));1191		assert_noop!(1192			Unique::burn_item(origin1, collection_id, TokenId(1), 1023).map_err(|e| e.error),1193			CommonError::<Test>::TokenValueTooLow1194		);11951196		assert_eq!(1197			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1198			01199		);1200	});1201}12021203#[test]1204fn add_collection_admin() {1205	new_test_ext().execute_with(|| {1206		let collection1_id =1207			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1208		let origin1 = Origin::signed(1);12091210		// Add collection admins1211		assert_ok!(Unique::add_collection_admin(1212			origin1.clone(),1213			collection1_id,1214			account(2)1215		));1216		assert_ok!(Unique::add_collection_admin(1217			origin1,1218			collection1_id,1219			account(3)1220		));12211222		// Owner is not an admin by default1223		assert_eq!(1224			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),1225			false1226		);1227		assert!(<pallet_common::IsAdmin<Test>>::get((1228			CollectionId(1),1229			account(2)1230		)));1231		assert!(<pallet_common::IsAdmin<Test>>::get((1232			CollectionId(1),1233			account(3)1234		)));1235	});1236}12371238#[test]1239fn remove_collection_admin() {1240	new_test_ext().execute_with(|| {1241		let collection1_id =1242			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1243		let origin1 = Origin::signed(1);1244		let origin2 = Origin::signed(2);12451246		// Add collection admins 2 and 31247		assert_ok!(Unique::add_collection_admin(1248			origin1.clone(),1249			collection1_id,1250			account(2)1251		));1252		assert_ok!(Unique::add_collection_admin(1253			origin1,1254			collection1_id,1255			account(3)1256		));12571258		assert!(<pallet_common::IsAdmin<Test>>::get((1259			CollectionId(1),1260			account(2)1261		)));1262		assert!(<pallet_common::IsAdmin<Test>>::get((1263			CollectionId(1),1264			account(3)1265		)));12661267		// remove admin 31268		assert_ok!(Unique::remove_collection_admin(1269			origin2,1270			CollectionId(1),1271			account(3)1272		));12731274		// 2 is still admin, 3 is not an admin anymore1275		assert!(<pallet_common::IsAdmin<Test>>::get((1276			CollectionId(1),1277			account(2)1278		)));1279		assert_eq!(1280			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),1281			false1282		);1283	});1284}12851286#[test]1287fn balance_of() {1288	new_test_ext().execute_with(|| {1289		let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1290		let fungible_collection_id =1291			create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));1292		let re_fungible_collection_id =1293			create_test_collection(&CollectionMode::ReFungible, CollectionId(3));12941295		// check balance before1296		assert_eq!(1297			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1298			01299		);1300		assert_eq!(1301			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1302			01303		);1304		assert_eq!(1305			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1306			01307		);13081309		let nft_data = default_nft_data();1310		create_test_item(nft_collection_id, &nft_data.into());13111312		let fungible_data = default_fungible_data();1313		create_test_item(fungible_collection_id, &fungible_data.into());13141315		let re_fungible_data = default_re_fungible_data();1316		create_test_item(re_fungible_collection_id, &re_fungible_data.into());13171318		// check balance (collection with id = 1, user id = 1)1319		assert_eq!(1320			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1321			11322		);1323		assert_eq!(1324			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1325			51326		);1327		assert_eq!(1328			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1329			11330		);13311332		assert_eq!(1333			<pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),1334			true1335		);1336		assert_eq!(1337			<pallet_refungible::Owned<Test>>::get((1338				re_fungible_collection_id,1339				account(1),1340				TokenId(1)1341			)),1342			true1343		);1344	});1345}13461347#[test]1348fn approve() {1349	new_test_ext().execute_with(|| {1350		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));13511352		let data = default_nft_data();1353		create_test_item(collection_id, &data.into());13541355		let origin1 = Origin::signed(1);13561357		// approve1358		assert_ok!(Unique::approve(1359			origin1,1360			account(2),1361			CollectionId(1),1362			TokenId(1),1363			11364		));1365		assert_eq!(1366			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1367			account(2)1368		);1369	});1370}13711372#[test]1373fn transfer_from() {1374	new_test_ext().execute_with(|| {1375		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1376		let origin1 = Origin::signed(1);1377		let origin2 = Origin::signed(2);13781379		let data = default_nft_data();1380		create_test_item(collection_id, &data.into());13811382		// approve1383		assert_ok!(Unique::approve(1384			origin1.clone(),1385			account(2),1386			CollectionId(1),1387			TokenId(1),1388			11389		));1390		assert_eq!(1391			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1392			account(2)1393		);13941395		assert_ok!(Unique::set_mint_permission(1396			origin1.clone(),1397			CollectionId(1),1398			true1399		));1400		assert_ok!(Unique::set_public_access_mode(1401			origin1.clone(),1402			CollectionId(1),1403			AccessMode::AllowList1404		));1405		assert_ok!(Unique::add_to_allow_list(1406			origin1.clone(),1407			CollectionId(1),1408			account(1)1409		));1410		assert_ok!(Unique::add_to_allow_list(1411			origin1.clone(),1412			CollectionId(1),1413			account(2)1414		));1415		assert_ok!(Unique::add_to_allow_list(1416			origin1,1417			CollectionId(1),1418			account(3)1419		));14201421		assert_ok!(Unique::transfer_from(1422			origin2,1423			account(1),1424			account(2),1425			CollectionId(1),1426			TokenId(1),1427			11428		));14291430		// after transfer1431		assert_eq!(1432			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),1433			01434		);1435		assert_eq!(1436			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),1437			11438		);1439	});1440}14411442// #endregion14431444// Coverage tests region1445// #region14461447#[test]1448fn owner_can_add_address_to_allow_list() {1449	new_test_ext().execute_with(|| {1450		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));14511452		let origin1 = Origin::signed(1);1453		assert_ok!(Unique::add_to_allow_list(1454			origin1,1455			collection_id,1456			account(2)1457		));1458		assert!(<pallet_common::Allowlist<Test>>::get((1459			collection_id,1460			account(2)1461		)));1462	});1463}14641465#[test]1466fn admin_can_add_address_to_allow_list() {1467	new_test_ext().execute_with(|| {1468		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1469		let origin1 = Origin::signed(1);1470		let origin2 = Origin::signed(2);14711472		assert_ok!(Unique::add_collection_admin(1473			origin1,1474			collection_id,1475			account(2)1476		));1477		assert_ok!(Unique::add_to_allow_list(1478			origin2,1479			collection_id,1480			account(3)1481		));1482		assert!(<pallet_common::Allowlist<Test>>::get((1483			collection_id,1484			account(3)1485		)));1486	});1487}14881489#[test]1490fn nonprivileged_user_cannot_add_address_to_allow_list() {1491	new_test_ext().execute_with(|| {1492		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));14931494		let origin2 = Origin::signed(2);1495		assert_noop!(1496			Unique::add_to_allow_list(origin2, collection_id, account(3)),1497			CommonError::<Test>::NoPermission1498		);1499	});1500}15011502#[test]1503fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {1504	new_test_ext().execute_with(|| {1505		let origin1 = Origin::signed(1);15061507		assert_noop!(1508			Unique::add_to_allow_list(origin1, CollectionId(1), account(2)),1509			CommonError::<Test>::CollectionNotFound1510		);1511	});1512}15131514#[test]1515fn nobody_can_add_address_to_allow_list_of_deleted_collection() {1516	new_test_ext().execute_with(|| {1517		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15181519		let origin1 = Origin::signed(1);1520		assert_ok!(Unique::destroy_collection(origin1.clone(), collection_id));1521		assert_noop!(1522			Unique::add_to_allow_list(origin1, collection_id, account(2)),1523			CommonError::<Test>::CollectionNotFound1524		);1525	});1526}15271528// If address is already added to allow list, nothing happens1529#[test]1530fn address_is_already_added_to_allow_list() {1531	new_test_ext().execute_with(|| {1532		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1533		let origin1 = Origin::signed(1);15341535		assert_ok!(Unique::add_to_allow_list(1536			origin1.clone(),1537			collection_id,1538			account(2)1539		));1540		assert_ok!(Unique::add_to_allow_list(1541			origin1,1542			collection_id,1543			account(2)1544		));1545		assert!(<pallet_common::Allowlist<Test>>::get((1546			collection_id,1547			account(2)1548		)));1549	});1550}15511552#[test]1553fn owner_can_remove_address_from_allow_list() {1554	new_test_ext().execute_with(|| {1555		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15561557		let origin1 = Origin::signed(1);1558		assert_ok!(Unique::add_to_allow_list(1559			origin1.clone(),1560			collection_id,1561			account(2)1562		));1563		assert_ok!(Unique::remove_from_allow_list(1564			origin1,1565			collection_id,1566			account(2)1567		));1568		assert_eq!(1569			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1570			false1571		);1572	});1573}15741575#[test]1576fn admin_can_remove_address_from_allow_list() {1577	new_test_ext().execute_with(|| {1578		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1579		let origin1 = Origin::signed(1);1580		let origin2 = Origin::signed(2);15811582		// Owner adds admin1583		assert_ok!(Unique::add_collection_admin(1584			origin1.clone(),1585			collection_id,1586			account(2)1587		));15881589		// Owner adds address 3 to allow list1590		assert_ok!(Unique::add_to_allow_list(1591			origin1,1592			collection_id,1593			account(3)1594		));15951596		// Admin removes address 3 from allow list1597		assert_ok!(Unique::remove_from_allow_list(1598			origin2,1599			collection_id,1600			account(3)1601		));1602		assert_eq!(1603			<pallet_common::Allowlist<Test>>::get((collection_id, account(3))),1604			false1605		);1606	});1607}16081609#[test]1610fn nonprivileged_user_cannot_remove_address_from_allow_list() {1611	new_test_ext().execute_with(|| {1612		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1613		let origin1 = Origin::signed(1);1614		let origin2 = Origin::signed(2);16151616		assert_ok!(Unique::add_to_allow_list(1617			origin1,1618			collection_id,1619			account(2)1620		));1621		assert_noop!(1622			Unique::remove_from_allow_list(origin2, collection_id, account(2)),1623			CommonError::<Test>::NoPermission1624		);1625		assert!(<pallet_common::Allowlist<Test>>::get((1626			collection_id,1627			account(2)1628		)));1629	});1630}16311632#[test]1633fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {1634	new_test_ext().execute_with(|| {1635		let origin1 = Origin::signed(1);16361637		assert_noop!(1638			Unique::remove_from_allow_list(origin1, CollectionId(1), account(2)),1639			CommonError::<Test>::CollectionNotFound1640		);1641	});1642}16431644#[test]1645fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {1646	new_test_ext().execute_with(|| {1647		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1648		let origin1 = Origin::signed(1);1649		let origin2 = Origin::signed(2);16501651		// Add account 2 to allow list1652		assert_ok!(Unique::add_to_allow_list(1653			origin1.clone(),1654			collection_id,1655			account(2)1656		));16571658		// Account 2 is in collection allow-list1659		assert!(<pallet_common::Allowlist<Test>>::get((1660			collection_id,1661			account(2)1662		)));16631664		// Destroy collection1665		assert_ok!(Unique::destroy_collection(origin1, collection_id));16661667		// Attempt to remove account 2 from collection allow-list => error1668		assert_noop!(1669			Unique::remove_from_allow_list(origin2, collection_id, account(2)),1670			CommonError::<Test>::CollectionNotFound1671		);16721673		// Account 2 is not found in collection allow-list anyway1674		assert_eq!(1675			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1676			false1677		);1678	});1679}16801681// If address is already removed from allow list, nothing happens1682#[test]1683fn address_is_already_removed_from_allow_list() {1684	new_test_ext().execute_with(|| {1685		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1686		let origin1 = Origin::signed(1);16871688		assert_ok!(Unique::add_to_allow_list(1689			origin1.clone(),1690			collection_id,1691			account(2)1692		));1693		assert_ok!(Unique::remove_from_allow_list(1694			origin1.clone(),1695			collection_id,1696			account(2)1697		));1698		assert_eq!(1699			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1700			false1701		);1702		assert_ok!(Unique::remove_from_allow_list(1703			origin1,1704			collection_id,1705			account(2)1706		));1707		assert_eq!(1708			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1709			false1710		);1711	});1712}17131714// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)1715#[test]1716fn allow_list_test_1() {1717	new_test_ext().execute_with(|| {1718		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));17191720		let origin1 = Origin::signed(1);17211722		let data = default_nft_data();1723		create_test_item(collection_id, &data.into());17241725		assert_ok!(Unique::set_public_access_mode(1726			origin1.clone(),1727			collection_id,1728			AccessMode::AllowList1729		));1730		assert_ok!(Unique::add_to_allow_list(1731			origin1.clone(),1732			collection_id,1733			account(2)1734		));17351736		assert_noop!(1737			Unique::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)1738				.map_err(|e| e.error),1739			CommonError::<Test>::AddressNotInAllowlist1740		);1741	});1742}17431744#[test]1745fn allow_list_test_2() {1746	new_test_ext().execute_with(|| {1747		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1748		let origin1 = Origin::signed(1);17491750		let data = default_nft_data();1751		create_test_item(collection_id, &data.into());17521753		assert_ok!(Unique::set_public_access_mode(1754			origin1.clone(),1755			collection_id,1756			AccessMode::AllowList1757		));1758		assert_ok!(Unique::add_to_allow_list(1759			origin1.clone(),1760			collection_id,1761			account(1)1762		));1763		assert_ok!(Unique::add_to_allow_list(1764			origin1.clone(),1765			collection_id,1766			account(2)1767		));17681769		// do approve1770		assert_ok!(Unique::approve(1771			origin1.clone(),1772			account(1),1773			collection_id,1774			TokenId(1),1775			11776		));1777		assert_eq!(1778			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1779			account(1)1780		);17811782		assert_ok!(Unique::remove_from_allow_list(1783			origin1.clone(),1784			collection_id,1785			account(1)1786		));17871788		assert_noop!(1789			Unique::transfer_from(1790				origin1,1791				account(1),1792				account(3),1793				CollectionId(1),1794				TokenId(1),1795				11796			)1797			.map_err(|e| e.error),1798			CommonError::<Test>::AddressNotInAllowlist1799		);1800	});1801}18021803// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)1804#[test]1805fn allow_list_test_3() {1806	new_test_ext().execute_with(|| {1807		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18081809		let origin1 = Origin::signed(1);18101811		let data = default_nft_data();1812		create_test_item(collection_id, &data.into());18131814		assert_ok!(Unique::set_public_access_mode(1815			origin1.clone(),1816			collection_id,1817			AccessMode::AllowList1818		));1819		assert_ok!(Unique::add_to_allow_list(1820			origin1.clone(),1821			collection_id,1822			account(1)1823		));18241825		assert_noop!(1826			Unique::transfer(origin1, account(3), collection_id, TokenId(1), 1)1827				.map_err(|e| e.error),1828			CommonError::<Test>::AddressNotInAllowlist1829		);1830	});1831}18321833#[test]1834fn allow_list_test_4() {1835	new_test_ext().execute_with(|| {1836		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18371838		let origin1 = Origin::signed(1);18391840		let data = default_nft_data();1841		create_test_item(collection_id, &data.into());18421843		assert_ok!(Unique::set_public_access_mode(1844			origin1.clone(),1845			collection_id,1846			AccessMode::AllowList1847		));1848		assert_ok!(Unique::add_to_allow_list(1849			origin1.clone(),1850			collection_id,1851			account(1)1852		));1853		assert_ok!(Unique::add_to_allow_list(1854			origin1.clone(),1855			collection_id,1856			account(2)1857		));18581859		// do approve1860		assert_ok!(Unique::approve(1861			origin1.clone(),1862			account(1),1863			collection_id,1864			TokenId(1),1865			11866		));1867		assert_eq!(1868			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1869			account(1)1870		);18711872		assert_ok!(Unique::remove_from_allow_list(1873			origin1.clone(),1874			collection_id,1875			account(2)1876		));18771878		assert_noop!(1879			Unique::transfer_from(1880				origin1,1881				account(1),1882				account(3),1883				collection_id,1884				TokenId(1),1885				11886			)1887			.map_err(|e| e.error),1888			CommonError::<Test>::AddressNotInAllowlist1889		);1890	});1891}18921893// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)1894#[test]1895fn allow_list_test_5() {1896	new_test_ext().execute_with(|| {1897		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18981899		let origin1 = Origin::signed(1);19001901		let data = default_nft_data();1902		create_test_item(collection_id, &data.into());19031904		assert_ok!(Unique::set_public_access_mode(1905			origin1.clone(),1906			collection_id,1907			AccessMode::AllowList1908		));1909		assert_noop!(1910			Unique::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1).map_err(|e| e.error),1911			CommonError::<Test>::AddressNotInAllowlist1912		);1913	});1914}19151916// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).1917#[test]1918fn allow_list_test_6() {1919	new_test_ext().execute_with(|| {1920		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19211922		let origin1 = Origin::signed(1);19231924		let data = default_nft_data();1925		create_test_item(collection_id, &data.into());19261927		assert_ok!(Unique::set_public_access_mode(1928			origin1.clone(),1929			collection_id,1930			AccessMode::AllowList1931		));19321933		// do approve1934		assert_noop!(1935			Unique::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)1936				.map_err(|e| e.error),1937			CommonError::<Test>::AddressNotInAllowlist1938		);1939	});1940}19411942// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and1943//          tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)1944#[test]1945fn allow_list_test_7() {1946	new_test_ext().execute_with(|| {1947		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19481949		let data = default_nft_data();1950		create_test_item(collection_id, &data.into());19511952		let origin1 = Origin::signed(1);19531954		assert_ok!(Unique::set_public_access_mode(1955			origin1.clone(),1956			collection_id,1957			AccessMode::AllowList1958		));1959		assert_ok!(Unique::add_to_allow_list(1960			origin1.clone(),1961			collection_id,1962			account(1)1963		));1964		assert_ok!(Unique::add_to_allow_list(1965			origin1.clone(),1966			collection_id,1967			account(2)1968		));19691970		assert_ok!(Unique::transfer(1971			origin1,1972			account(2),1973			CollectionId(1),1974			TokenId(1),1975			11976		));1977	});1978}19791980#[test]1981fn allow_list_test_8() {1982	new_test_ext().execute_with(|| {1983		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19841985		// Create NFT for account 11986		let data = default_nft_data();1987		create_test_item(collection_id, &data.into());19881989		let origin1 = Origin::signed(1);19901991		// Toggle Allow List mode and add accounts 1 and 21992		assert_ok!(Unique::set_public_access_mode(1993			origin1.clone(),1994			collection_id,1995			AccessMode::AllowList1996		));1997		assert_ok!(Unique::add_to_allow_list(1998			origin1.clone(),1999			collection_id,2000			account(1)2001		));2002		assert_ok!(Unique::add_to_allow_list(2003			origin1.clone(),2004			collection_id,2005			account(2)2006		));20072008		// Sself-approve account 1 for NFT 12009		assert_ok!(Unique::approve(2010			origin1.clone(),2011			account(1),2012			CollectionId(1),2013			TokenId(1),2014			12015		));2016		assert_eq!(2017			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),2018			account(1)2019		);20202021		// Transfer from 1 to 22022		assert_ok!(Unique::transfer_from(2023			origin1,2024			account(1),2025			account(2),2026			CollectionId(1),2027			TokenId(1),2028			12029		));2030	});2031}20322033// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.2034#[test]2035fn allow_list_test_9() {2036	new_test_ext().execute_with(|| {2037		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2038		let origin1 = Origin::signed(1);20392040		assert_ok!(Unique::set_public_access_mode(2041			origin1.clone(),2042			collection_id,2043			AccessMode::AllowList2044		));2045		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));20462047		let data = default_nft_data();2048		create_test_item(collection_id, &data.into());2049	});2050}20512052// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.2053#[test]2054fn allow_list_test_10() {2055	new_test_ext().execute_with(|| {2056		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));20572058		let origin1 = Origin::signed(1);2059		let origin2 = Origin::signed(2);20602061		assert_ok!(Unique::set_public_access_mode(2062			origin1.clone(),2063			collection_id,2064			AccessMode::AllowList2065		));2066		assert_ok!(Unique::set_mint_permission(2067			origin1.clone(),2068			collection_id,2069			false2070		));20712072		assert_ok!(Unique::add_collection_admin(2073			origin1,2074			collection_id,2075			account(2)2076		));20772078		assert_ok!(Unique::create_item(2079			origin2,2080			collection_id,2081			account(2),2082			default_nft_data().into()2083		));2084	});2085}20862087// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.2088#[test]2089fn allow_list_test_11() {2090	new_test_ext().execute_with(|| {2091		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));20922093		let origin1 = Origin::signed(1);2094		let origin2 = Origin::signed(2);20952096		assert_ok!(Unique::set_public_access_mode(2097			origin1.clone(),2098			collection_id,2099			AccessMode::AllowList2100		));2101		assert_ok!(Unique::set_mint_permission(2102			origin1.clone(),2103			collection_id,2104			false2105		));2106		assert_ok!(Unique::add_to_allow_list(2107			origin1,2108			collection_id,2109			account(2)2110		));21112112		assert_noop!(2113			Unique::create_item(2114				origin2,2115				CollectionId(1),2116				account(2),2117				default_nft_data().into()2118			)2119			.map_err(|e| e.error),2120			CommonError::<Test>::PublicMintingNotAllowed2121		);2122	});2123}21242125// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.2126#[test]2127fn allow_list_test_12() {2128	new_test_ext().execute_with(|| {2129		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21302131		let origin1 = Origin::signed(1);2132		let origin2 = Origin::signed(2);21332134		assert_ok!(Unique::set_public_access_mode(2135			origin1.clone(),2136			collection_id,2137			AccessMode::AllowList2138		));2139		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));21402141		assert_noop!(2142			Unique::create_item(2143				origin2,2144				CollectionId(1),2145				account(2),2146				default_nft_data().into()2147			)2148			.map_err(|e| e.error),2149			CommonError::<Test>::PublicMintingNotAllowed2150		);2151	});2152}21532154// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.2155#[test]2156fn allow_list_test_13() {2157	new_test_ext().execute_with(|| {2158		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21592160		let origin1 = Origin::signed(1);21612162		assert_ok!(Unique::set_public_access_mode(2163			origin1.clone(),2164			collection_id,2165			AccessMode::AllowList2166		));2167		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));21682169		let data = default_nft_data();2170		create_test_item(collection_id, &data.into());2171	});2172}21732174// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.2175#[test]2176fn allow_list_test_14() {2177	new_test_ext().execute_with(|| {2178		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21792180		let origin1 = Origin::signed(1);2181		let origin2 = Origin::signed(2);21822183		assert_ok!(Unique::set_public_access_mode(2184			origin1.clone(),2185			collection_id,2186			AccessMode::AllowList2187		));2188		assert_ok!(Unique::set_mint_permission(2189			origin1.clone(),2190			collection_id,2191			true2192		));21932194		assert_ok!(Unique::add_collection_admin(2195			origin1,2196			collection_id,2197			account(2)2198		));21992200		assert_ok!(Unique::create_item(2201			origin2,2202			collection_id,2203			account(2),2204			default_nft_data().into()2205		));2206	});2207}22082209// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.2210#[test]2211fn allow_list_test_15() {2212	new_test_ext().execute_with(|| {2213		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22142215		let origin1 = Origin::signed(1);2216		let origin2 = Origin::signed(2);22172218		assert_ok!(Unique::set_public_access_mode(2219			origin1.clone(),2220			collection_id,2221			AccessMode::AllowList2222		));2223		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));22242225		assert_noop!(2226			Unique::create_item(2227				origin2,2228				collection_id,2229				account(2),2230				default_nft_data().into()2231			)2232			.map_err(|e| e.error),2233			CommonError::<Test>::AddressNotInAllowlist2234		);2235	});2236}22372238// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.2239#[test]2240fn allow_list_test_16() {2241	new_test_ext().execute_with(|| {2242		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22432244		let origin1 = Origin::signed(1);2245		let origin2 = Origin::signed(2);22462247		assert_ok!(Unique::set_public_access_mode(2248			origin1.clone(),2249			collection_id,2250			AccessMode::AllowList2251		));2252		assert_ok!(Unique::set_mint_permission(2253			origin1.clone(),2254			collection_id,2255			true2256		));2257		assert_ok!(Unique::add_to_allow_list(2258			origin1,2259			collection_id,2260			account(2)2261		));22622263		assert_ok!(Unique::create_item(2264			origin2,2265			collection_id,2266			account(2),2267			default_nft_data().into()2268		));2269	});2270}22712272// Total number of collections. Positive test2273#[test]2274fn total_number_collections_bound() {2275	new_test_ext().execute_with(|| {2276		create_test_collection(&CollectionMode::NFT, CollectionId(1));2277	});2278}22792280#[test]2281fn create_max_collections() {2282	new_test_ext().execute_with(|| {2283		for i in 1..COLLECTION_NUMBER_LIMIT {2284			create_test_collection(&CollectionMode::NFT, CollectionId(i));2285		}2286	});2287}22882289// Total number of collections. Negative test2290#[test]2291fn total_number_collections_bound_neg() {2292	new_test_ext().execute_with(|| {2293		let origin1 = Origin::signed(1);22942295		for i in 1..=COLLECTION_NUMBER_LIMIT {2296			create_test_collection(&CollectionMode::NFT, CollectionId(i));2297		}22982299		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();2300		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();2301		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();23022303		let data: CreateCollectionData<u64> = CreateCollectionData {2304			name: col_name1.try_into().unwrap(),2305			description: col_desc1.try_into().unwrap(),2306			token_prefix: token_prefix1.try_into().unwrap(),2307			mode: CollectionMode::NFT,2308			..Default::default()2309		};23102311		// 11-th collection in chain. Expects error2312		assert_noop!(2313			Unique::create_collection_ex(origin1, data),2314			CommonError::<Test>::TotalCollectionsLimitExceeded2315		);2316	});2317}23182319// Owned tokens by a single address. Positive test2320#[test]2321fn owned_tokens_bound() {2322	new_test_ext().execute_with(|| {2323		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23242325		let data = default_nft_data();2326		create_test_item(collection_id, &data.clone().into());2327		create_test_item(collection_id, &data.into());2328	});2329}23302331// Owned tokens by a single address. Negotive test2332#[test]2333fn owned_tokens_bound_neg() {2334	new_test_ext().execute_with(|| {2335		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23362337		let origin1 = Origin::signed(1);23382339		for _ in 1..=MAX_TOKEN_OWNERSHIP {2340			let data = default_nft_data();2341			create_test_item(collection_id, &data.clone().into());2342		}23432344		let data = default_nft_data();2345		assert_noop!(2346			Unique::create_item(origin1, CollectionId(1), account(1), data.into())2347				.map_err(|e| e.error),2348			CommonError::<Test>::AccountTokenLimitExceeded2349		);2350	});2351}23522353// Number of collection admins. Positive test2354#[test]2355fn collection_admins_bound() {2356	new_test_ext().execute_with(|| {2357		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23582359		let origin1 = Origin::signed(1);23602361		assert_ok!(Unique::add_collection_admin(2362			origin1.clone(),2363			collection_id,2364			account(2)2365		));2366		assert_ok!(Unique::add_collection_admin(2367			origin1,2368			collection_id,2369			account(3)2370		));2371	});2372}23732374// Number of collection admins. Negotive test2375#[test]2376fn collection_admins_bound_neg() {2377	new_test_ext().execute_with(|| {2378		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23792380		let origin1 = Origin::signed(1);23812382		for i in 0..COLLECTION_ADMINS_LIMIT {2383			assert_ok!(Unique::add_collection_admin(2384				origin1.clone(),2385				collection_id,2386				account((2 + i).into())2387			));2388		}2389		assert_noop!(2390			Unique::add_collection_admin(2391				origin1,2392				collection_id,2393				account((3 + COLLECTION_ADMINS_LIMIT).into())2394			),2395			CommonError::<Test>::CollectionAdminCountExceeded2396		);2397	});2398}2399// #endregion24002401#[test]2402fn set_const_on_chain_schema() {2403	new_test_ext().execute_with(|| {2404		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));24052406		let origin1 = Origin::signed(1);2407		assert_ok!(Unique::set_const_on_chain_schema(2408			origin1,2409			collection_id,2410			b"test const on chain schema".to_vec().try_into().unwrap()2411		));24122413		assert_eq!(2414			<pallet_common::CollectionData<Test>>::get((2415				collection_id,2416				CollectionField::ConstOnChainSchema2417			)),2418			b"test const on chain schema".to_vec()2419		);2420	});2421}24222423#[test]2424fn collection_transfer_flag_works() {2425	new_test_ext().execute_with(|| {2426		let origin1 = Origin::signed(1);24272428		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2429		assert_ok!(Unique::set_transfers_enabled_flag(2430			origin1,2431			collection_id,2432			true2433		));24342435		let data = default_nft_data();2436		create_test_item(collection_id, &data.into());2437		assert_eq!(2438			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2439			12440		);2441		assert_eq!(2442			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2443			true2444		);24452446		let origin1 = Origin::signed(1);24472448		// default scenario2449		assert_ok!(Unique::transfer(2450			origin1,2451			account(2),2452			collection_id,2453			TokenId(1),2454			12455		));2456		assert_eq!(2457			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2458			false2459		);2460		assert_eq!(2461			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2462			true2463		);2464		assert_eq!(2465			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2466			02467		);2468		assert_eq!(2469			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2470			12471		);2472	});2473}24742475#[test]2476fn collection_transfer_flag_works_neg() {2477	new_test_ext().execute_with(|| {2478		let origin1 = Origin::signed(1);24792480		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2481		assert_ok!(Unique::set_transfers_enabled_flag(2482			origin1,2483			collection_id,2484			false2485		));24862487		let data = default_nft_data();2488		create_test_item(collection_id, &data.into());2489		assert_eq!(2490			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2491			12492		);2493		assert_eq!(2494			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2495			true2496		);24972498		let origin1 = Origin::signed(1);24992500		// default scenario2501		assert_noop!(2502			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)2503				.map_err(|e| e.error),2504			CommonError::<Test>::TransferNotAllowed2505		);2506		assert_eq!(2507			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2508			12509		);2510		assert_eq!(2511			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2512			02513		);2514		assert_eq!(2515			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2516			true2517		);2518		assert_eq!(2519			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2520			false2521		);2522	});2523}25242525#[test]2526fn collection_sponsoring() {2527	new_test_ext().execute_with(|| {2528		// default_limits();2529		let user1 = 1_u64;2530		let user2 = 777_u64;2531		let origin1 = Origin::signed(user1);2532		let origin2 = Origin::signed(user2);2533		let account2 = account(user2);25342535		let collection_id =2536			create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));2537		assert_ok!(Unique::set_collection_sponsor(2538			origin1.clone(),2539			collection_id,2540			user12541		));2542		assert_ok!(Unique::confirm_sponsorship(origin1.clone(), collection_id));25432544		// Expect error while have no permissions2545		assert!(Unique::create_item(2546			origin2.clone(),2547			collection_id,2548			account2.clone(),2549			default_nft_data().into()2550		)2551		.is_err());25522553		assert_ok!(Unique::set_public_access_mode(2554			origin1.clone(),2555			collection_id,2556			AccessMode::AllowList2557		));2558		assert_ok!(Unique::add_to_allow_list(2559			origin1.clone(),2560			collection_id,2561			account2.clone()2562		));2563		assert_ok!(Unique::set_mint_permission(2564			origin1.clone(),2565			collection_id,2566			true2567		));25682569		assert_ok!(Unique::create_item(2570			origin2,2571			collection_id,2572			account2,2573			default_nft_data().into()2574		));2575	});2576}
after · runtime/tests/src/tests.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// Tests to be written here18use crate::{Test, TestCrossAccountId, CollectionCreationPrice, Origin, Unique, new_test_ext};19use up_data_structs::{20	COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,21	CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, TokenId,22	MAX_TOKEN_OWNERSHIP, CreateCollectionData, CollectionField, SchemaVersion, CollectionMode,23	AccessMode,24};25use frame_support::{assert_noop, assert_ok, assert_err};26use sp_std::convert::TryInto;27use pallet_evm::account::CrossAccountId;28use pallet_common::Error as CommonError;29use pallet_unique::Error as UniqueError;3031fn add_balance(user: u64, value: u64) {32	const DONOR_USER: u64 = 999;33	assert_ok!(<pallet_balances::Pallet<Test>>::set_balance(34		Origin::root(),35		DONOR_USER,36		value,37		038	));39	assert_ok!(<pallet_balances::Pallet<Test>>::force_transfer(40		Origin::root(),41		DONOR_USER,42		user,43		value44	));45}4647fn default_nft_data() -> CreateNftData {48	CreateNftData {49		const_data: vec![1, 2, 3].try_into().unwrap(),50		properties: vec![].try_into().unwrap(),51	}52}5354fn default_fungible_data() -> CreateFungibleData {55	CreateFungibleData { value: 5 }56}5758fn default_re_fungible_data() -> CreateReFungibleData {59	CreateReFungibleData {60		const_data: vec![1, 2, 3].try_into().unwrap(),61		pieces: 1023,62	}63}6465fn create_test_collection_for_owner(66	mode: &CollectionMode,67	owner: u64,68	id: CollectionId,69) -> CollectionId {70	add_balance(owner, CollectionCreationPrice::get() as u64 + 1);7172	let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();73	let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();74	let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();7576	let data: CreateCollectionData<u64> = CreateCollectionData {77		name: col_name1.try_into().unwrap(),78		description: col_desc1.try_into().unwrap(),79		token_prefix: token_prefix1.try_into().unwrap(),80		mode: mode.clone(),81		..Default::default()82	};8384	let origin1 = Origin::signed(owner);85	assert_ok!(Unique::create_collection_ex(origin1, data));8687	let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();88	let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();89	let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();90	assert_eq!(91		<pallet_common::CollectionById<Test>>::get(id)92			.unwrap()93			.owner,94		owner95	);96	assert_eq!(97		<pallet_common::CollectionById<Test>>::get(id).unwrap().name,98		saved_col_name99	);100	assert_eq!(101		<pallet_common::CollectionById<Test>>::get(id).unwrap().mode,102		*mode103	);104	assert_eq!(105		<pallet_common::CollectionById<Test>>::get(id)106			.unwrap()107			.description,108		saved_description109	);110	assert_eq!(111		<pallet_common::CollectionById<Test>>::get(id)112			.unwrap()113			.token_prefix,114		saved_prefix115	);116	id117}118119fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {120	create_test_collection_for_owner(&mode, 1, id)121}122123fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {124	let origin1 = Origin::signed(1);125	assert_ok!(Unique::create_item(126		origin1,127		collection_id,128		account(1),129		data.clone()130	));131}132133fn account(sub: u64) -> TestCrossAccountId {134	TestCrossAccountId::from_sub(sub)135}136137// Use cases tests region138// #region139140#[test]141fn set_version_schema() {142	new_test_ext().execute_with(|| {143		let origin1 = Origin::signed(1);144		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));145146		assert_ok!(Unique::set_schema_version(147			origin1,148			collection_id,149			SchemaVersion::Unique150		));151		assert_eq!(152			<pallet_common::CollectionById<Test>>::get(collection_id)153				.unwrap()154				.schema_version,155			SchemaVersion::Unique156		);157	});158}159160#[test]161fn check_not_sufficient_founds() {162	new_test_ext().execute_with(|| {163		let acc: u64 = 1;164		<pallet_balances::Pallet<Test>>::set_balance(Origin::root(), acc, 0, 0).unwrap();165166		let name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();167		let description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();168		let token_prefix: Vec<u8> = b"token_prefix1\0".to_vec();169170		let data: CreateCollectionData<<Test as frame_system::Config>::AccountId> =171			CreateCollectionData {172				name: name.try_into().unwrap(),173				description: description.try_into().unwrap(),174				token_prefix: token_prefix.try_into().unwrap(),175				mode: CollectionMode::NFT,176				..Default::default()177			};178179		let result = Unique::create_collection_ex(Origin::signed(acc), data);180		assert_err!(result, <CommonError<Test>>::NotSufficientFounds);181	});182}183184#[test]185fn create_fungible_collection_fails_with_large_decimal_numbers() {186	new_test_ext().execute_with(|| {187		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();188		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();189		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();190191		let data: CreateCollectionData<u64> = CreateCollectionData {192			name: col_name1.try_into().unwrap(),193			description: col_desc1.try_into().unwrap(),194			token_prefix: token_prefix1.try_into().unwrap(),195			mode: CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1),196			..Default::default()197		};198199		let origin1 = Origin::signed(1);200		assert_noop!(201			Unique::create_collection_ex(origin1, data),202			UniqueError::<Test>::CollectionDecimalPointLimitExceeded203		);204	});205}206207#[test]208fn create_nft_item() {209	new_test_ext().execute_with(|| {210		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));211212		let data = default_nft_data();213		create_test_item(collection_id, &data.clone().into());214215		let item = <pallet_nonfungible::TokenData<Test>>::get((collection_id, 1)).unwrap();216		assert_eq!(item.const_data, data.const_data.into_inner());217	});218}219220// Use cases tests region221// #region222#[test]223fn create_nft_multiple_items() {224	new_test_ext().execute_with(|| {225		create_test_collection(&CollectionMode::NFT, CollectionId(1));226227		let origin1 = Origin::signed(1);228229		let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];230231		assert_ok!(Unique::create_multiple_items(232			origin1,233			CollectionId(1),234			account(1),235			items_data236				.clone()237				.into_iter()238				.map(|d| { d.into() })239				.collect()240		));241		for (index, data) in items_data.into_iter().enumerate() {242			let item = <pallet_nonfungible::TokenData<Test>>::get((243				CollectionId(1),244				TokenId((index + 1) as u32),245			))246			.unwrap();247			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());248		}249	});250}251252#[test]253fn create_refungible_item() {254	new_test_ext().execute_with(|| {255		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));256257		let data = default_re_fungible_data();258		create_test_item(collection_id, &data.clone().into());259		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));260		let balance =261			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1)));262		assert_eq!(item.const_data, data.const_data.into_inner());263		assert_eq!(balance, 1023);264	});265}266267#[test]268fn create_multiple_refungible_items() {269	new_test_ext().execute_with(|| {270		create_test_collection(&CollectionMode::ReFungible, CollectionId(1));271272		let origin1 = Origin::signed(1);273274		let items_data = vec![275			default_re_fungible_data(),276			default_re_fungible_data(),277			default_re_fungible_data(),278		];279280		assert_ok!(Unique::create_multiple_items(281			origin1,282			CollectionId(1),283			account(1),284			items_data285				.clone()286				.into_iter()287				.map(|d| { d.into() })288				.collect()289		));290		for (index, data) in items_data.into_iter().enumerate() {291			let item = <pallet_refungible::TokenData<Test>>::get((292				CollectionId(1),293				TokenId((index + 1) as u32),294			));295			let balance =296				<pallet_refungible::Balance<Test>>::get((CollectionId(1), TokenId(1), account(1)));297			assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());298			assert_eq!(balance, 1023);299		}300	});301}302303#[test]304fn create_fungible_item() {305	new_test_ext().execute_with(|| {306		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));307308		let data = default_fungible_data();309		create_test_item(collection_id, &data.into());310311		assert_eq!(312			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),313			5314		);315	});316}317318//#[test]319// fn create_multiple_fungible_items() {320//     new_test_ext().execute_with(|| {321//         default_limits();322323//         create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));324325//         let origin1 = Origin::signed(1);326327//         let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];328329//         assert_ok!(Unique::create_multiple_items(330//             origin1.clone(),331//             1,332//             1,333//             items_data.clone().into_iter().map(|d| { d.into() }).collect()334//         ));335336//         for (index, _) in items_data.iter().enumerate() {337//             assert_eq!(Unique::fungible_item_id(1, (index + 1) as TokenId).value, 5);338//         }339//         assert_eq!(Unique::balance_count(1, 1), 3000);340//         assert_eq!(Unique::address_tokens(1, 1), [1, 2, 3]);341//     });342// }343344#[test]345fn transfer_fungible_item() {346	new_test_ext().execute_with(|| {347		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));348349		let origin1 = Origin::signed(1);350		let origin2 = Origin::signed(2);351352		let data = default_fungible_data();353		create_test_item(collection_id, &data.into());354355		assert_eq!(356			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),357			5358		);359360		// change owner scenario361		assert_ok!(Unique::transfer(362			origin1,363			account(2),364			CollectionId(1),365			TokenId(0),366			5367		));368		assert_eq!(369			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(1))),370			0371		);372373		// split item scenario374		assert_ok!(Unique::transfer(375			origin2.clone(),376			account(3),377			CollectionId(1),378			TokenId(0),379			3380		));381382		// split item and new owner has account scenario383		assert_ok!(Unique::transfer(384			origin2,385			account(3),386			CollectionId(1),387			TokenId(0),388			1389		));390		assert_eq!(391			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(2))),392			1393		);394		assert_eq!(395			<pallet_fungible::Balance<Test>>::get((CollectionId(1), account(3))),396			4397		);398	});399}400401#[test]402fn transfer_refungible_item() {403	new_test_ext().execute_with(|| {404		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));405406		// Create RFT 1 in 1023 pieces for account 1407		let data = default_re_fungible_data();408		create_test_item(collection_id, &data.clone().into());409		let item = <pallet_refungible::TokenData<Test>>::get((collection_id, TokenId(1)));410		assert_eq!(item.const_data, data.const_data.into_inner());411		assert_eq!(412			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),413			1414		);415		assert_eq!(416			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),417			1023418		);419		assert_eq!(420			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),421			true422		);423424		// Account 1 transfers all 1023 pieces of RFT 1 to account 2425		let origin1 = Origin::signed(1);426		let origin2 = Origin::signed(2);427		assert_ok!(Unique::transfer(428			origin1,429			account(2),430			CollectionId(1),431			TokenId(1),432			1023433		));434		assert_eq!(435			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),436			1023437		);438		assert_eq!(439			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),440			0441		);442		assert_eq!(443			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),444			1445		);446		assert_eq!(447			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),448			false449		);450		assert_eq!(451			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),452			true453		);454455		// Account 2 transfers 500 pieces of RFT 1 to account 3456		assert_ok!(Unique::transfer(457			origin2.clone(),458			account(3),459			CollectionId(1),460			TokenId(1),461			500462		));463		assert_eq!(464			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),465			523466		);467		assert_eq!(468			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),469			500470		);471		assert_eq!(472			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),473			1474		);475		assert_eq!(476			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),477			1478		);479		assert_eq!(480			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),481			true482		);483		assert_eq!(484			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),485			true486		);487488		// Account 2 transfers 200 more pieces of RFT 1 to account 3 with pre-existing balance489		assert_ok!(Unique::transfer(490			origin2,491			account(3),492			CollectionId(1),493			TokenId(1),494			200495		));496		assert_eq!(497			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(2))),498			323499		);500		assert_eq!(501			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),502			700503		);504		assert_eq!(505			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(2))),506			1507		);508		assert_eq!(509			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),510			1511		);512		assert_eq!(513			<pallet_refungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),514			true515		);516		assert_eq!(517			<pallet_refungible::Owned<Test>>::get((collection_id, account(3), TokenId(1))),518			true519		);520	});521}522523#[test]524fn transfer_nft_item() {525	new_test_ext().execute_with(|| {526		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));527528		let data = default_nft_data();529		create_test_item(collection_id, &data.into());530		assert_eq!(531			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),532			1533		);534		assert_eq!(535			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),536			true537		);538539		let origin1 = Origin::signed(1);540		// default scenario541		assert_ok!(Unique::transfer(542			origin1,543			account(2),544			CollectionId(1),545			TokenId(1),546			1547		));548		assert_eq!(549			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),550			0551		);552		assert_eq!(553			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),554			1555		);556		assert_eq!(557			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),558			false559		);560		assert_eq!(561			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),562			true563		);564	});565}566567#[test]568fn transfer_nft_item_wrong_value() {569	new_test_ext().execute_with(|| {570		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));571572		let data = default_nft_data();573		create_test_item(collection_id, &data.into());574		assert_eq!(575			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),576			1577		);578		assert_eq!(579			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),580			true581		);582583		let origin1 = Origin::signed(1);584585		assert_noop!(586			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 2)587				.map_err(|e| e.error),588			<pallet_nonfungible::Error::<Test>>::NonfungibleItemsHaveNoAmount589		);590	});591}592593#[test]594fn transfer_nft_item_zero_value() {595	new_test_ext().execute_with(|| {596		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));597598		let data = default_nft_data();599		create_test_item(collection_id, &data.into());600		assert_eq!(601			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),602			1603		);604		assert_eq!(605			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),606			true607		);608609		let origin1 = Origin::signed(1);610611		// Transferring 0 amount works on NFT...612		assert_ok!(Unique::transfer(613			origin1,614			account(2),615			CollectionId(1),616			TokenId(1),617			0618		));619		// ... and results in no transfer620		assert_eq!(621			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),622			1623		);624		assert_eq!(625			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),626			true627		);628	});629}630631#[test]632fn nft_approve_and_transfer_from() {633	new_test_ext().execute_with(|| {634		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));635636		let data = default_nft_data();637		create_test_item(collection_id, &data.into());638639		let origin1 = Origin::signed(1);640		let origin2 = Origin::signed(2);641642		assert_eq!(643			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),644			1645		);646		assert_eq!(647			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),648			true649		);650651		// neg transfer_from652		assert_noop!(653			Unique::transfer_from(654				origin2.clone(),655				account(1),656				account(2),657				CollectionId(1),658				TokenId(1),659				1660			)661			.map_err(|e| e.error),662			CommonError::<Test>::ApprovedValueTooLow663		);664665		// do approve666		assert_ok!(Unique::approve(667			origin1,668			account(2),669			CollectionId(1),670			TokenId(1),671			1672		));673		assert_eq!(674			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),675			account(2)676		);677678		assert_ok!(Unique::transfer_from(679			origin2,680			account(1),681			account(3),682			CollectionId(1),683			TokenId(1),684			1685		));686		assert!(687			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()688		);689	});690}691692#[test]693fn nft_approve_and_transfer_from_allow_list() {694	new_test_ext().execute_with(|| {695		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));696697		let origin1 = Origin::signed(1);698		let origin2 = Origin::signed(2);699700		// Create NFT 1 for account 1701		let data = default_nft_data();702		create_test_item(collection_id, &data.clone().into());703		assert_eq!(704			&<pallet_nonfungible::TokenData<Test>>::get((collection_id, TokenId(1)))705				.unwrap()706				.const_data,707			&data.const_data.into_inner()708		);709		assert_eq!(710			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),711			1712		);713		assert_eq!(714			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),715			true716		);717718		// Allow allow-list users to mint and add accounts 1, 2, and 3 to allow-list719		assert_ok!(Unique::set_mint_permission(720			origin1.clone(),721			CollectionId(1),722			true723		));724		assert_ok!(Unique::set_public_access_mode(725			origin1.clone(),726			CollectionId(1),727			AccessMode::AllowList728		));729		assert_ok!(Unique::add_to_allow_list(730			origin1.clone(),731			CollectionId(1),732			account(1)733		));734		assert_ok!(Unique::add_to_allow_list(735			origin1.clone(),736			CollectionId(1),737			account(2)738		));739		assert_ok!(Unique::add_to_allow_list(740			origin1.clone(),741			CollectionId(1),742			account(3)743		));744745		// Account 1 approves account 2 for NFT 1746		assert_ok!(Unique::approve(747			origin1.clone(),748			account(2),749			CollectionId(1),750			TokenId(1),751			1752		));753		assert_eq!(754			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),755			account(2)756		);757758		// Account 2 transfers NFT 1 from account 1 to account 3759		assert_ok!(Unique::transfer_from(760			origin2,761			account(1),762			account(3),763			CollectionId(1),764			TokenId(1),765			1766		));767		assert!(768			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).is_none()769		);770	});771}772773#[test]774fn refungible_approve_and_transfer_from() {775	new_test_ext().execute_with(|| {776		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));777778		let origin1 = Origin::signed(1);779		let origin2 = Origin::signed(2);780781		// Create RFT 1 in 1023 pieces for account 1782		let data = default_re_fungible_data();783		create_test_item(collection_id, &data.into());784785		assert_eq!(786			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),787			1788		);789		assert_eq!(790			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),791			1023792		);793		assert_eq!(794			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),795			true796		);797798		// Allow public minting, enable allow-list and add accounts 1, 2, 3 to allow-list799		assert_ok!(Unique::set_mint_permission(800			origin1.clone(),801			CollectionId(1),802			true803		));804		assert_ok!(Unique::set_public_access_mode(805			origin1.clone(),806			CollectionId(1),807			AccessMode::AllowList808		));809		assert_ok!(Unique::add_to_allow_list(810			origin1.clone(),811			CollectionId(1),812			account(1)813		));814		assert_ok!(Unique::add_to_allow_list(815			origin1.clone(),816			CollectionId(1),817			account(2)818		));819		assert_ok!(Unique::add_to_allow_list(820			origin1.clone(),821			CollectionId(1),822			account(3)823		));824825		// Account 1 approves account 2 for 1023 pieces of RFT 1826		assert_ok!(Unique::approve(827			origin1,828			account(2),829			CollectionId(1),830			TokenId(1),831			1023832		));833		assert_eq!(834			<pallet_refungible::Allowance<Test>>::get((835				CollectionId(1),836				TokenId(1),837				account(1),838				account(2)839			)),840			1023841		);842843		// Account 2 transfers 100 pieces of RFT 1 from account 1 to account 3844		assert_ok!(Unique::transfer_from(845			origin2,846			account(1),847			account(3),848			CollectionId(1),849			TokenId(1),850			100851		));852		assert_eq!(853			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),854			1855		);856		assert_eq!(857			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(3))),858			1859		);860		assert_eq!(861			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),862			923863		);864		assert_eq!(865			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(3))),866			100867		);868		assert_eq!(869			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),870			true871		);872		assert_eq!(873			<pallet_refungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),874			true875		);876		assert_eq!(877			<pallet_refungible::Allowance<Test>>::get((878				CollectionId(1),879				TokenId(1),880				account(1),881				account(2)882			)),883			923884		);885	});886}887888#[test]889fn fungible_approve_and_transfer_from() {890	new_test_ext().execute_with(|| {891		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));892893		let data = default_fungible_data();894		create_test_item(collection_id, &data.into());895896		let origin1 = Origin::signed(1);897		let origin2 = Origin::signed(2);898899		assert_ok!(Unique::set_mint_permission(900			origin1.clone(),901			CollectionId(1),902			true903		));904		assert_ok!(Unique::set_public_access_mode(905			origin1.clone(),906			CollectionId(1),907			AccessMode::AllowList908		));909		assert_ok!(Unique::add_to_allow_list(910			origin1.clone(),911			CollectionId(1),912			account(1)913		));914		assert_ok!(Unique::add_to_allow_list(915			origin1.clone(),916			CollectionId(1),917			account(2)918		));919		assert_ok!(Unique::add_to_allow_list(920			origin1.clone(),921			CollectionId(1),922			account(3)923		));924925		// do approve926		assert_ok!(Unique::approve(927			origin1.clone(),928			account(2),929			CollectionId(1),930			TokenId(0),931			5932		));933		assert_eq!(934			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),935			5936		);937		assert_ok!(Unique::approve(938			origin1,939			account(3),940			CollectionId(1),941			TokenId(0),942			5943		));944		assert_eq!(945			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),946			5947		);948		assert_eq!(949			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(3))),950			5951		);952953		assert_ok!(Unique::transfer_from(954			origin2.clone(),955			account(1),956			account(3),957			CollectionId(1),958			TokenId(0),959			4960		));961962		assert_eq!(963			<pallet_fungible::Allowance<Test>>::get((CollectionId(1), account(1), account(2))),964			1965		);966967		assert_noop!(968			Unique::transfer_from(969				origin2,970				account(1),971				account(3),972				CollectionId(1),973				TokenId(0),974				4975			)976			.map_err(|e| e.error),977			CommonError::<Test>::ApprovedValueTooLow978		);979	});980}981982#[test]983fn change_collection_owner() {984	new_test_ext().execute_with(|| {985		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));986987		let origin1 = Origin::signed(1);988		assert_ok!(Unique::change_collection_owner(origin1, collection_id, 2));989		assert_eq!(990			<pallet_common::CollectionById<Test>>::get(collection_id)991				.unwrap()992				.owner,993			2994		);995	});996}997998#[test]999fn destroy_collection() {1000	new_test_ext().execute_with(|| {1001		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10021003		let origin1 = Origin::signed(1);1004		assert_ok!(Unique::destroy_collection(origin1, collection_id));1005	});1006}10071008#[test]1009fn burn_nft_item() {1010	new_test_ext().execute_with(|| {1011		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10121013		let origin1 = Origin::signed(1);10141015		let data = default_nft_data();1016		create_test_item(collection_id, &data.into());10171018		// check balance (collection with id = 1, user id = 1)1019		assert_eq!(1020			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1021			11022		);10231024		// burn item1025		assert_ok!(Unique::burn_item(1026			origin1.clone(),1027			collection_id,1028			TokenId(1),1029			11030		));1031		assert_eq!(1032			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1033			01034		);1035	});1036}10371038#[test]1039fn burn_same_nft_item_twice() {1040	new_test_ext().execute_with(|| {1041		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));10421043		let origin1 = Origin::signed(1);10441045		let data = default_nft_data();1046		create_test_item(collection_id, &data.into());10471048		// check balance (collection with id = 1, user id = 1)1049		assert_eq!(1050			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1051			11052		);10531054		// burn item1055		assert_ok!(Unique::burn_item(1056			origin1.clone(),1057			collection_id,1058			TokenId(1),1059			11060		));10611062		// burn item again1063		assert_noop!(1064			Unique::burn_item(origin1, collection_id, TokenId(1), 1).map_err(|e| e.error),1065			CommonError::<Test>::TokenNotFound1066		);10671068		assert_eq!(1069			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),1070			01071		);1072	});1073}10741075#[test]1076fn burn_fungible_item() {1077	new_test_ext().execute_with(|| {1078		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));10791080		let origin1 = Origin::signed(1);1081		assert_ok!(Unique::add_collection_admin(1082			origin1.clone(),1083			collection_id,1084			account(2)1085		));10861087		let data = default_fungible_data();1088		create_test_item(collection_id, &data.into());10891090		// check balance (collection with id = 1, user id = 1)1091		assert_eq!(1092			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1093			51094		);10951096		// burn item1097		assert_ok!(Unique::burn_item(1098			origin1.clone(),1099			CollectionId(1),1100			TokenId(0),1101			51102		));1103		assert_noop!(1104			Unique::burn_item(origin1, CollectionId(1), TokenId(0), 5).map_err(|e| e.error),1105			CommonError::<Test>::TokenValueTooLow1106		);11071108		assert_eq!(1109			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1110			01111		);1112	});1113}11141115#[test]1116fn burn_fungible_item_with_token_id() {1117	new_test_ext().execute_with(|| {1118		let collection_id = create_test_collection(&CollectionMode::Fungible(3), CollectionId(1));11191120		let origin1 = Origin::signed(1);1121		assert_ok!(Unique::add_collection_admin(1122			origin1.clone(),1123			collection_id,1124			account(2)1125		));11261127		let data = default_fungible_data();1128		create_test_item(collection_id, &data.into());11291130		// check balance (collection with id = 1, user id = 1)1131		assert_eq!(1132			<pallet_fungible::Balance<Test>>::get((collection_id, account(1))),1133			51134		);11351136		// Try to burn item using Token ID1137		assert_noop!(1138			Unique::burn_item(origin1, CollectionId(1), TokenId(1), 5).map_err(|e| e.error),1139			<pallet_fungible::Error::<Test>>::FungibleItemsHaveNoId1140		);1141	});1142}1143#[test]1144fn burn_refungible_item() {1145	new_test_ext().execute_with(|| {1146		let collection_id = create_test_collection(&CollectionMode::ReFungible, CollectionId(1));1147		let origin1 = Origin::signed(1);11481149		assert_ok!(Unique::set_mint_permission(1150			origin1.clone(),1151			collection_id,1152			true1153		));1154		assert_ok!(Unique::set_public_access_mode(1155			origin1.clone(),1156			collection_id,1157			AccessMode::AllowList1158		));1159		assert_ok!(Unique::add_to_allow_list(1160			origin1.clone(),1161			collection_id,1162			account(1)1163		));11641165		assert_ok!(Unique::add_collection_admin(1166			origin1.clone(),1167			collection_id,1168			account(2)1169		));11701171		let data = default_re_fungible_data();1172		create_test_item(collection_id, &data.into());11731174		// check balance (collection with id = 1, user id = 2)1175		assert_eq!(1176			<pallet_refungible::AccountBalance<Test>>::get((collection_id, account(1))),1177			11178		);1179		assert_eq!(1180			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1181			10231182		);11831184		// burn item1185		assert_ok!(Unique::burn_item(1186			origin1.clone(),1187			collection_id,1188			TokenId(1),1189			10231190		));1191		assert_noop!(1192			Unique::burn_item(origin1, collection_id, TokenId(1), 1023).map_err(|e| e.error),1193			CommonError::<Test>::TokenValueTooLow1194		);11951196		assert_eq!(1197			<pallet_refungible::Balance<Test>>::get((collection_id, TokenId(1), account(1))),1198			01199		);1200	});1201}12021203#[test]1204fn add_collection_admin() {1205	new_test_ext().execute_with(|| {1206		let collection1_id =1207			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1208		let origin1 = Origin::signed(1);12091210		// Add collection admins1211		assert_ok!(Unique::add_collection_admin(1212			origin1.clone(),1213			collection1_id,1214			account(2)1215		));1216		assert_ok!(Unique::add_collection_admin(1217			origin1,1218			collection1_id,1219			account(3)1220		));12211222		// Owner is not an admin by default1223		assert_eq!(1224			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(1))),1225			false1226		);1227		assert!(<pallet_common::IsAdmin<Test>>::get((1228			CollectionId(1),1229			account(2)1230		)));1231		assert!(<pallet_common::IsAdmin<Test>>::get((1232			CollectionId(1),1233			account(3)1234		)));1235	});1236}12371238#[test]1239fn remove_collection_admin() {1240	new_test_ext().execute_with(|| {1241		let collection1_id =1242			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));1243		let origin1 = Origin::signed(1);1244		let origin2 = Origin::signed(2);12451246		// Add collection admins 2 and 31247		assert_ok!(Unique::add_collection_admin(1248			origin1.clone(),1249			collection1_id,1250			account(2)1251		));1252		assert_ok!(Unique::add_collection_admin(1253			origin1,1254			collection1_id,1255			account(3)1256		));12571258		assert!(<pallet_common::IsAdmin<Test>>::get((1259			CollectionId(1),1260			account(2)1261		)));1262		assert!(<pallet_common::IsAdmin<Test>>::get((1263			CollectionId(1),1264			account(3)1265		)));12661267		// remove admin 31268		assert_ok!(Unique::remove_collection_admin(1269			origin2,1270			CollectionId(1),1271			account(3)1272		));12731274		// 2 is still admin, 3 is not an admin anymore1275		assert!(<pallet_common::IsAdmin<Test>>::get((1276			CollectionId(1),1277			account(2)1278		)));1279		assert_eq!(1280			<pallet_common::IsAdmin<Test>>::get((CollectionId(1), account(3))),1281			false1282		);1283	});1284}12851286#[test]1287fn balance_of() {1288	new_test_ext().execute_with(|| {1289		let nft_collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1290		let fungible_collection_id =1291			create_test_collection(&CollectionMode::Fungible(3), CollectionId(2));1292		let re_fungible_collection_id =1293			create_test_collection(&CollectionMode::ReFungible, CollectionId(3));12941295		// check balance before1296		assert_eq!(1297			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1298			01299		);1300		assert_eq!(1301			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1302			01303		);1304		assert_eq!(1305			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1306			01307		);13081309		let nft_data = default_nft_data();1310		create_test_item(nft_collection_id, &nft_data.into());13111312		let fungible_data = default_fungible_data();1313		create_test_item(fungible_collection_id, &fungible_data.into());13141315		let re_fungible_data = default_re_fungible_data();1316		create_test_item(re_fungible_collection_id, &re_fungible_data.into());13171318		// check balance (collection with id = 1, user id = 1)1319		assert_eq!(1320			<pallet_nonfungible::AccountBalance<Test>>::get((nft_collection_id, account(1))),1321			11322		);1323		assert_eq!(1324			<pallet_fungible::Balance<Test>>::get((fungible_collection_id, account(1))),1325			51326		);1327		assert_eq!(1328			<pallet_refungible::AccountBalance<Test>>::get((re_fungible_collection_id, account(1))),1329			11330		);13311332		assert_eq!(1333			<pallet_nonfungible::Owned<Test>>::get((nft_collection_id, account(1), TokenId(1))),1334			true1335		);1336		assert_eq!(1337			<pallet_refungible::Owned<Test>>::get((1338				re_fungible_collection_id,1339				account(1),1340				TokenId(1)1341			)),1342			true1343		);1344	});1345}13461347#[test]1348fn approve() {1349	new_test_ext().execute_with(|| {1350		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));13511352		let data = default_nft_data();1353		create_test_item(collection_id, &data.into());13541355		let origin1 = Origin::signed(1);13561357		// approve1358		assert_ok!(Unique::approve(1359			origin1,1360			account(2),1361			CollectionId(1),1362			TokenId(1),1363			11364		));1365		assert_eq!(1366			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1367			account(2)1368		);1369	});1370}13711372#[test]1373fn transfer_from() {1374	new_test_ext().execute_with(|| {1375		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1376		let origin1 = Origin::signed(1);1377		let origin2 = Origin::signed(2);13781379		let data = default_nft_data();1380		create_test_item(collection_id, &data.into());13811382		// approve1383		assert_ok!(Unique::approve(1384			origin1.clone(),1385			account(2),1386			CollectionId(1),1387			TokenId(1),1388			11389		));1390		assert_eq!(1391			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1392			account(2)1393		);13941395		assert_ok!(Unique::set_mint_permission(1396			origin1.clone(),1397			CollectionId(1),1398			true1399		));1400		assert_ok!(Unique::set_public_access_mode(1401			origin1.clone(),1402			CollectionId(1),1403			AccessMode::AllowList1404		));1405		assert_ok!(Unique::add_to_allow_list(1406			origin1.clone(),1407			CollectionId(1),1408			account(1)1409		));1410		assert_ok!(Unique::add_to_allow_list(1411			origin1.clone(),1412			CollectionId(1),1413			account(2)1414		));1415		assert_ok!(Unique::add_to_allow_list(1416			origin1,1417			CollectionId(1),1418			account(3)1419		));14201421		assert_ok!(Unique::transfer_from(1422			origin2,1423			account(1),1424			account(2),1425			CollectionId(1),1426			TokenId(1),1427			11428		));14291430		// after transfer1431		assert_eq!(1432			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(1))),1433			01434		);1435		assert_eq!(1436			<pallet_nonfungible::AccountBalance<Test>>::get((CollectionId(1), account(2))),1437			11438		);1439	});1440}14411442// #endregion14431444// Coverage tests region1445// #region14461447#[test]1448fn owner_can_add_address_to_allow_list() {1449	new_test_ext().execute_with(|| {1450		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));14511452		let origin1 = Origin::signed(1);1453		assert_ok!(Unique::add_to_allow_list(1454			origin1,1455			collection_id,1456			account(2)1457		));1458		assert!(<pallet_common::Allowlist<Test>>::get((1459			collection_id,1460			account(2)1461		)));1462	});1463}14641465#[test]1466fn admin_can_add_address_to_allow_list() {1467	new_test_ext().execute_with(|| {1468		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1469		let origin1 = Origin::signed(1);1470		let origin2 = Origin::signed(2);14711472		assert_ok!(Unique::add_collection_admin(1473			origin1,1474			collection_id,1475			account(2)1476		));1477		assert_ok!(Unique::add_to_allow_list(1478			origin2,1479			collection_id,1480			account(3)1481		));1482		assert!(<pallet_common::Allowlist<Test>>::get((1483			collection_id,1484			account(3)1485		)));1486	});1487}14881489#[test]1490fn nonprivileged_user_cannot_add_address_to_allow_list() {1491	new_test_ext().execute_with(|| {1492		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));14931494		let origin2 = Origin::signed(2);1495		assert_noop!(1496			Unique::add_to_allow_list(origin2, collection_id, account(3)),1497			CommonError::<Test>::NoPermission1498		);1499	});1500}15011502#[test]1503fn nobody_can_add_address_to_allow_list_of_nonexisting_collection() {1504	new_test_ext().execute_with(|| {1505		let origin1 = Origin::signed(1);15061507		assert_noop!(1508			Unique::add_to_allow_list(origin1, CollectionId(1), account(2)),1509			CommonError::<Test>::CollectionNotFound1510		);1511	});1512}15131514#[test]1515fn nobody_can_add_address_to_allow_list_of_deleted_collection() {1516	new_test_ext().execute_with(|| {1517		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15181519		let origin1 = Origin::signed(1);1520		assert_ok!(Unique::destroy_collection(origin1.clone(), collection_id));1521		assert_noop!(1522			Unique::add_to_allow_list(origin1, collection_id, account(2)),1523			CommonError::<Test>::CollectionNotFound1524		);1525	});1526}15271528// If address is already added to allow list, nothing happens1529#[test]1530fn address_is_already_added_to_allow_list() {1531	new_test_ext().execute_with(|| {1532		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1533		let origin1 = Origin::signed(1);15341535		assert_ok!(Unique::add_to_allow_list(1536			origin1.clone(),1537			collection_id,1538			account(2)1539		));1540		assert_ok!(Unique::add_to_allow_list(1541			origin1,1542			collection_id,1543			account(2)1544		));1545		assert!(<pallet_common::Allowlist<Test>>::get((1546			collection_id,1547			account(2)1548		)));1549	});1550}15511552#[test]1553fn owner_can_remove_address_from_allow_list() {1554	new_test_ext().execute_with(|| {1555		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));15561557		let origin1 = Origin::signed(1);1558		assert_ok!(Unique::add_to_allow_list(1559			origin1.clone(),1560			collection_id,1561			account(2)1562		));1563		assert_ok!(Unique::remove_from_allow_list(1564			origin1,1565			collection_id,1566			account(2)1567		));1568		assert_eq!(1569			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1570			false1571		);1572	});1573}15741575#[test]1576fn admin_can_remove_address_from_allow_list() {1577	new_test_ext().execute_with(|| {1578		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1579		let origin1 = Origin::signed(1);1580		let origin2 = Origin::signed(2);15811582		// Owner adds admin1583		assert_ok!(Unique::add_collection_admin(1584			origin1.clone(),1585			collection_id,1586			account(2)1587		));15881589		// Owner adds address 3 to allow list1590		assert_ok!(Unique::add_to_allow_list(1591			origin1,1592			collection_id,1593			account(3)1594		));15951596		// Admin removes address 3 from allow list1597		assert_ok!(Unique::remove_from_allow_list(1598			origin2,1599			collection_id,1600			account(3)1601		));1602		assert_eq!(1603			<pallet_common::Allowlist<Test>>::get((collection_id, account(3))),1604			false1605		);1606	});1607}16081609#[test]1610fn nonprivileged_user_cannot_remove_address_from_allow_list() {1611	new_test_ext().execute_with(|| {1612		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1613		let origin1 = Origin::signed(1);1614		let origin2 = Origin::signed(2);16151616		assert_ok!(Unique::add_to_allow_list(1617			origin1,1618			collection_id,1619			account(2)1620		));1621		assert_noop!(1622			Unique::remove_from_allow_list(origin2, collection_id, account(2)),1623			CommonError::<Test>::NoPermission1624		);1625		assert!(<pallet_common::Allowlist<Test>>::get((1626			collection_id,1627			account(2)1628		)));1629	});1630}16311632#[test]1633fn nobody_can_remove_address_from_allow_list_of_nonexisting_collection() {1634	new_test_ext().execute_with(|| {1635		let origin1 = Origin::signed(1);16361637		assert_noop!(1638			Unique::remove_from_allow_list(origin1, CollectionId(1), account(2)),1639			CommonError::<Test>::CollectionNotFound1640		);1641	});1642}16431644#[test]1645fn nobody_can_remove_address_from_allow_list_of_deleted_collection() {1646	new_test_ext().execute_with(|| {1647		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1648		let origin1 = Origin::signed(1);1649		let origin2 = Origin::signed(2);16501651		// Add account 2 to allow list1652		assert_ok!(Unique::add_to_allow_list(1653			origin1.clone(),1654			collection_id,1655			account(2)1656		));16571658		// Account 2 is in collection allow-list1659		assert!(<pallet_common::Allowlist<Test>>::get((1660			collection_id,1661			account(2)1662		)));16631664		// Destroy collection1665		assert_ok!(Unique::destroy_collection(origin1, collection_id));16661667		// Attempt to remove account 2 from collection allow-list => error1668		assert_noop!(1669			Unique::remove_from_allow_list(origin2, collection_id, account(2)),1670			CommonError::<Test>::CollectionNotFound1671		);16721673		// Account 2 is not found in collection allow-list anyway1674		assert_eq!(1675			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1676			false1677		);1678	});1679}16801681// If address is already removed from allow list, nothing happens1682#[test]1683fn address_is_already_removed_from_allow_list() {1684	new_test_ext().execute_with(|| {1685		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1686		let origin1 = Origin::signed(1);16871688		assert_ok!(Unique::add_to_allow_list(1689			origin1.clone(),1690			collection_id,1691			account(2)1692		));1693		assert_ok!(Unique::remove_from_allow_list(1694			origin1.clone(),1695			collection_id,1696			account(2)1697		));1698		assert_eq!(1699			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1700			false1701		);1702		assert_ok!(Unique::remove_from_allow_list(1703			origin1,1704			collection_id,1705			account(2)1706		));1707		assert_eq!(1708			<pallet_common::Allowlist<Test>>::get((collection_id, account(2))),1709			false1710		);1711	});1712}17131714// If Public Access mode is set to AllowList, tokens can’t be transferred from a non-allowlisted address with transfer or transferFrom (2 tests)1715#[test]1716fn allow_list_test_1() {1717	new_test_ext().execute_with(|| {1718		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));17191720		let origin1 = Origin::signed(1);17211722		let data = default_nft_data();1723		create_test_item(collection_id, &data.into());17241725		assert_ok!(Unique::set_public_access_mode(1726			origin1.clone(),1727			collection_id,1728			AccessMode::AllowList1729		));1730		assert_ok!(Unique::add_to_allow_list(1731			origin1.clone(),1732			collection_id,1733			account(2)1734		));17351736		assert_noop!(1737			Unique::transfer(origin1, account(3), CollectionId(1), TokenId(1), 1)1738				.map_err(|e| e.error),1739			CommonError::<Test>::AddressNotInAllowlist1740		);1741	});1742}17431744#[test]1745fn allow_list_test_2() {1746	new_test_ext().execute_with(|| {1747		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));1748		let origin1 = Origin::signed(1);17491750		let data = default_nft_data();1751		create_test_item(collection_id, &data.into());17521753		assert_ok!(Unique::set_public_access_mode(1754			origin1.clone(),1755			collection_id,1756			AccessMode::AllowList1757		));1758		assert_ok!(Unique::add_to_allow_list(1759			origin1.clone(),1760			collection_id,1761			account(1)1762		));1763		assert_ok!(Unique::add_to_allow_list(1764			origin1.clone(),1765			collection_id,1766			account(2)1767		));17681769		// do approve1770		assert_ok!(Unique::approve(1771			origin1.clone(),1772			account(1),1773			collection_id,1774			TokenId(1),1775			11776		));1777		assert_eq!(1778			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1779			account(1)1780		);17811782		assert_ok!(Unique::remove_from_allow_list(1783			origin1.clone(),1784			collection_id,1785			account(1)1786		));17871788		assert_noop!(1789			Unique::transfer_from(1790				origin1,1791				account(1),1792				account(3),1793				CollectionId(1),1794				TokenId(1),1795				11796			)1797			.map_err(|e| e.error),1798			CommonError::<Test>::AddressNotInAllowlist1799		);1800	});1801}18021803// If Public Access mode is set to AllowList, tokens can’t be transferred to a non-allowlisted address with transfer or transferFrom (2 tests)1804#[test]1805fn allow_list_test_3() {1806	new_test_ext().execute_with(|| {1807		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18081809		let origin1 = Origin::signed(1);18101811		let data = default_nft_data();1812		create_test_item(collection_id, &data.into());18131814		assert_ok!(Unique::set_public_access_mode(1815			origin1.clone(),1816			collection_id,1817			AccessMode::AllowList1818		));1819		assert_ok!(Unique::add_to_allow_list(1820			origin1.clone(),1821			collection_id,1822			account(1)1823		));18241825		assert_noop!(1826			Unique::transfer(origin1, account(3), collection_id, TokenId(1), 1)1827				.map_err(|e| e.error),1828			CommonError::<Test>::AddressNotInAllowlist1829		);1830	});1831}18321833#[test]1834fn allow_list_test_4() {1835	new_test_ext().execute_with(|| {1836		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18371838		let origin1 = Origin::signed(1);18391840		let data = default_nft_data();1841		create_test_item(collection_id, &data.into());18421843		assert_ok!(Unique::set_public_access_mode(1844			origin1.clone(),1845			collection_id,1846			AccessMode::AllowList1847		));1848		assert_ok!(Unique::add_to_allow_list(1849			origin1.clone(),1850			collection_id,1851			account(1)1852		));1853		assert_ok!(Unique::add_to_allow_list(1854			origin1.clone(),1855			collection_id,1856			account(2)1857		));18581859		// do approve1860		assert_ok!(Unique::approve(1861			origin1.clone(),1862			account(1),1863			collection_id,1864			TokenId(1),1865			11866		));1867		assert_eq!(1868			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),1869			account(1)1870		);18711872		assert_ok!(Unique::remove_from_allow_list(1873			origin1.clone(),1874			collection_id,1875			account(2)1876		));18771878		assert_noop!(1879			Unique::transfer_from(1880				origin1,1881				account(1),1882				account(3),1883				collection_id,1884				TokenId(1),1885				11886			)1887			.map_err(|e| e.error),1888			CommonError::<Test>::AddressNotInAllowlist1889		);1890	});1891}18921893// If Public Access mode is set to AllowList, tokens can’t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)1894#[test]1895fn allow_list_test_5() {1896	new_test_ext().execute_with(|| {1897		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));18981899		let origin1 = Origin::signed(1);19001901		let data = default_nft_data();1902		create_test_item(collection_id, &data.into());19031904		assert_ok!(Unique::set_public_access_mode(1905			origin1.clone(),1906			collection_id,1907			AccessMode::AllowList1908		));1909		assert_noop!(1910			Unique::burn_item(origin1.clone(), CollectionId(1), TokenId(1), 1).map_err(|e| e.error),1911			CommonError::<Test>::AddressNotInAllowlist1912		);1913	});1914}19151916// If Public Access mode is set to AllowList, token transfers can’t be Approved by a non-allowlisted address (see Approve method).1917#[test]1918fn allow_list_test_6() {1919	new_test_ext().execute_with(|| {1920		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19211922		let origin1 = Origin::signed(1);19231924		let data = default_nft_data();1925		create_test_item(collection_id, &data.into());19261927		assert_ok!(Unique::set_public_access_mode(1928			origin1.clone(),1929			collection_id,1930			AccessMode::AllowList1931		));19321933		// do approve1934		assert_noop!(1935			Unique::approve(origin1, account(1), CollectionId(1), TokenId(1), 1)1936				.map_err(|e| e.error),1937			CommonError::<Test>::AddressNotInAllowlist1938		);1939	});1940}19411942// If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests) and1943//          tokens can be transferred from a allowlisted address with transfer or transferFrom (2 tests)1944#[test]1945fn allow_list_test_7() {1946	new_test_ext().execute_with(|| {1947		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19481949		let data = default_nft_data();1950		create_test_item(collection_id, &data.into());19511952		let origin1 = Origin::signed(1);19531954		assert_ok!(Unique::set_public_access_mode(1955			origin1.clone(),1956			collection_id,1957			AccessMode::AllowList1958		));1959		assert_ok!(Unique::add_to_allow_list(1960			origin1.clone(),1961			collection_id,1962			account(1)1963		));1964		assert_ok!(Unique::add_to_allow_list(1965			origin1.clone(),1966			collection_id,1967			account(2)1968		));19691970		assert_ok!(Unique::transfer(1971			origin1,1972			account(2),1973			CollectionId(1),1974			TokenId(1),1975			11976		));1977	});1978}19791980#[test]1981fn allow_list_test_8() {1982	new_test_ext().execute_with(|| {1983		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));19841985		// Create NFT for account 11986		let data = default_nft_data();1987		create_test_item(collection_id, &data.into());19881989		let origin1 = Origin::signed(1);19901991		// Toggle Allow List mode and add accounts 1 and 21992		assert_ok!(Unique::set_public_access_mode(1993			origin1.clone(),1994			collection_id,1995			AccessMode::AllowList1996		));1997		assert_ok!(Unique::add_to_allow_list(1998			origin1.clone(),1999			collection_id,2000			account(1)2001		));2002		assert_ok!(Unique::add_to_allow_list(2003			origin1.clone(),2004			collection_id,2005			account(2)2006		));20072008		// Sself-approve account 1 for NFT 12009		assert_ok!(Unique::approve(2010			origin1.clone(),2011			account(1),2012			CollectionId(1),2013			TokenId(1),2014			12015		));2016		assert_eq!(2017			<pallet_nonfungible::Allowance<Test>>::get((CollectionId(1), TokenId(1))).unwrap(),2018			account(1)2019		);20202021		// Transfer from 1 to 22022		assert_ok!(Unique::transfer_from(2023			origin1,2024			account(1),2025			account(2),2026			CollectionId(1),2027			TokenId(1),2028			12029		));2030	});2031}20322033// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by owner.2034#[test]2035fn allow_list_test_9() {2036	new_test_ext().execute_with(|| {2037		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2038		let origin1 = Origin::signed(1);20392040		assert_ok!(Unique::set_public_access_mode(2041			origin1.clone(),2042			collection_id,2043			AccessMode::AllowList2044		));2045		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));20462047		let data = default_nft_data();2048		create_test_item(collection_id, &data.into());2049	});2050}20512052// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens can be created by admin.2053#[test]2054fn allow_list_test_10() {2055	new_test_ext().execute_with(|| {2056		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));20572058		let origin1 = Origin::signed(1);2059		let origin2 = Origin::signed(2);20602061		assert_ok!(Unique::set_public_access_mode(2062			origin1.clone(),2063			collection_id,2064			AccessMode::AllowList2065		));2066		assert_ok!(Unique::set_mint_permission(2067			origin1.clone(),2068			collection_id,2069			false2070		));20712072		assert_ok!(Unique::add_collection_admin(2073			origin1,2074			collection_id,2075			account(2)2076		));20772078		assert_ok!(Unique::create_item(2079			origin2,2080			collection_id,2081			account(2),2082			default_nft_data().into()2083		));2084	});2085}20862087// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and allow listed address.2088#[test]2089fn allow_list_test_11() {2090	new_test_ext().execute_with(|| {2091		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));20922093		let origin1 = Origin::signed(1);2094		let origin2 = Origin::signed(2);20952096		assert_ok!(Unique::set_public_access_mode(2097			origin1.clone(),2098			collection_id,2099			AccessMode::AllowList2100		));2101		assert_ok!(Unique::set_mint_permission(2102			origin1.clone(),2103			collection_id,2104			false2105		));2106		assert_ok!(Unique::add_to_allow_list(2107			origin1,2108			collection_id,2109			account(2)2110		));21112112		assert_noop!(2113			Unique::create_item(2114				origin2,2115				CollectionId(1),2116				account(2),2117				default_nft_data().into()2118			)2119			.map_err(|e| e.error),2120			CommonError::<Test>::PublicMintingNotAllowed2121		);2122	});2123}21242125// If Public Access mode is set to AllowList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-allow listed address.2126#[test]2127fn allow_list_test_12() {2128	new_test_ext().execute_with(|| {2129		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21302131		let origin1 = Origin::signed(1);2132		let origin2 = Origin::signed(2);21332134		assert_ok!(Unique::set_public_access_mode(2135			origin1.clone(),2136			collection_id,2137			AccessMode::AllowList2138		));2139		assert_ok!(Unique::set_mint_permission(origin1, collection_id, false));21402141		assert_noop!(2142			Unique::create_item(2143				origin2,2144				CollectionId(1),2145				account(2),2146				default_nft_data().into()2147			)2148			.map_err(|e| e.error),2149			CommonError::<Test>::PublicMintingNotAllowed2150		);2151	});2152}21532154// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by owner.2155#[test]2156fn allow_list_test_13() {2157	new_test_ext().execute_with(|| {2158		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21592160		let origin1 = Origin::signed(1);21612162		assert_ok!(Unique::set_public_access_mode(2163			origin1.clone(),2164			collection_id,2165			AccessMode::AllowList2166		));2167		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));21682169		let data = default_nft_data();2170		create_test_item(collection_id, &data.into());2171	});2172}21732174// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by admin.2175#[test]2176fn allow_list_test_14() {2177	new_test_ext().execute_with(|| {2178		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));21792180		let origin1 = Origin::signed(1);2181		let origin2 = Origin::signed(2);21822183		assert_ok!(Unique::set_public_access_mode(2184			origin1.clone(),2185			collection_id,2186			AccessMode::AllowList2187		));2188		assert_ok!(Unique::set_mint_permission(2189			origin1.clone(),2190			collection_id,2191			true2192		));21932194		assert_ok!(Unique::add_collection_admin(2195			origin1,2196			collection_id,2197			account(2)2198		));21992200		assert_ok!(Unique::create_item(2201			origin2,2202			collection_id,2203			account(2),2204			default_nft_data().into()2205		));2206	});2207}22082209// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-allow listed address.2210#[test]2211fn allow_list_test_15() {2212	new_test_ext().execute_with(|| {2213		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22142215		let origin1 = Origin::signed(1);2216		let origin2 = Origin::signed(2);22172218		assert_ok!(Unique::set_public_access_mode(2219			origin1.clone(),2220			collection_id,2221			AccessMode::AllowList2222		));2223		assert_ok!(Unique::set_mint_permission(origin1, collection_id, true));22242225		assert_noop!(2226			Unique::create_item(2227				origin2,2228				collection_id,2229				account(2),2230				default_nft_data().into()2231			)2232			.map_err(|e| e.error),2233			CommonError::<Test>::AddressNotInAllowlist2234		);2235	});2236}22372238// If Public Access mode is set to AllowList, and Mint Permission is set to true, tokens can be created by non-privileged and allow listed address.2239#[test]2240fn allow_list_test_16() {2241	new_test_ext().execute_with(|| {2242		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));22432244		let origin1 = Origin::signed(1);2245		let origin2 = Origin::signed(2);22462247		assert_ok!(Unique::set_public_access_mode(2248			origin1.clone(),2249			collection_id,2250			AccessMode::AllowList2251		));2252		assert_ok!(Unique::set_mint_permission(2253			origin1.clone(),2254			collection_id,2255			true2256		));2257		assert_ok!(Unique::add_to_allow_list(2258			origin1,2259			collection_id,2260			account(2)2261		));22622263		assert_ok!(Unique::create_item(2264			origin2,2265			collection_id,2266			account(2),2267			default_nft_data().into()2268		));2269	});2270}22712272// Total number of collections. Positive test2273#[test]2274fn total_number_collections_bound() {2275	new_test_ext().execute_with(|| {2276		create_test_collection(&CollectionMode::NFT, CollectionId(1));2277	});2278}22792280#[test]2281fn create_max_collections() {2282	new_test_ext().execute_with(|| {2283		for i in 1..COLLECTION_NUMBER_LIMIT {2284			create_test_collection(&CollectionMode::NFT, CollectionId(i));2285		}2286	});2287}22882289// Total number of collections. Negative test2290#[test]2291fn total_number_collections_bound_neg() {2292	new_test_ext().execute_with(|| {2293		let origin1 = Origin::signed(1);22942295		for i in 1..=COLLECTION_NUMBER_LIMIT {2296			create_test_collection(&CollectionMode::NFT, CollectionId(i));2297		}22982299		let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();2300		let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();2301		let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();23022303		let data: CreateCollectionData<u64> = CreateCollectionData {2304			name: col_name1.try_into().unwrap(),2305			description: col_desc1.try_into().unwrap(),2306			token_prefix: token_prefix1.try_into().unwrap(),2307			mode: CollectionMode::NFT,2308			..Default::default()2309		};23102311		// 11-th collection in chain. Expects error2312		assert_noop!(2313			Unique::create_collection_ex(origin1, data),2314			CommonError::<Test>::TotalCollectionsLimitExceeded2315		);2316	});2317}23182319// Owned tokens by a single address. Positive test2320#[test]2321fn owned_tokens_bound() {2322	new_test_ext().execute_with(|| {2323		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23242325		let data = default_nft_data();2326		create_test_item(collection_id, &data.clone().into());2327		create_test_item(collection_id, &data.into());2328	});2329}23302331// Owned tokens by a single address. Negotive test2332#[test]2333fn owned_tokens_bound_neg() {2334	new_test_ext().execute_with(|| {2335		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23362337		let origin1 = Origin::signed(1);23382339		for _ in 1..=MAX_TOKEN_OWNERSHIP {2340			let data = default_nft_data();2341			create_test_item(collection_id, &data.clone().into());2342		}23432344		let data = default_nft_data();2345		assert_noop!(2346			Unique::create_item(origin1, CollectionId(1), account(1), data.into())2347				.map_err(|e| e.error),2348			CommonError::<Test>::AccountTokenLimitExceeded2349		);2350	});2351}23522353// Number of collection admins. Positive test2354#[test]2355fn collection_admins_bound() {2356	new_test_ext().execute_with(|| {2357		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23582359		let origin1 = Origin::signed(1);23602361		assert_ok!(Unique::add_collection_admin(2362			origin1.clone(),2363			collection_id,2364			account(2)2365		));2366		assert_ok!(Unique::add_collection_admin(2367			origin1,2368			collection_id,2369			account(3)2370		));2371	});2372}23732374// Number of collection admins. Negotive test2375#[test]2376fn collection_admins_bound_neg() {2377	new_test_ext().execute_with(|| {2378		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));23792380		let origin1 = Origin::signed(1);23812382		for i in 0..COLLECTION_ADMINS_LIMIT {2383			assert_ok!(Unique::add_collection_admin(2384				origin1.clone(),2385				collection_id,2386				account((2 + i).into())2387			));2388		}2389		assert_noop!(2390			Unique::add_collection_admin(2391				origin1,2392				collection_id,2393				account((3 + COLLECTION_ADMINS_LIMIT).into())2394			),2395			CommonError::<Test>::CollectionAdminCountExceeded2396		);2397	});2398}2399// #endregion24002401#[test]2402fn collection_transfer_flag_works() {2403	new_test_ext().execute_with(|| {2404		let origin1 = Origin::signed(1);24052406		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2407		assert_ok!(Unique::set_transfers_enabled_flag(2408			origin1,2409			collection_id,2410			true2411		));24122413		let data = default_nft_data();2414		create_test_item(collection_id, &data.into());2415		assert_eq!(2416			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2417			12418		);2419		assert_eq!(2420			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2421			true2422		);24232424		let origin1 = Origin::signed(1);24252426		// default scenario2427		assert_ok!(Unique::transfer(2428			origin1,2429			account(2),2430			collection_id,2431			TokenId(1),2432			12433		));2434		assert_eq!(2435			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2436			false2437		);2438		assert_eq!(2439			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2440			true2441		);2442		assert_eq!(2443			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2444			02445		);2446		assert_eq!(2447			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2448			12449		);2450	});2451}24522453#[test]2454fn collection_transfer_flag_works_neg() {2455	new_test_ext().execute_with(|| {2456		let origin1 = Origin::signed(1);24572458		let collection_id = create_test_collection(&CollectionMode::NFT, CollectionId(1));2459		assert_ok!(Unique::set_transfers_enabled_flag(2460			origin1,2461			collection_id,2462			false2463		));24642465		let data = default_nft_data();2466		create_test_item(collection_id, &data.into());2467		assert_eq!(2468			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2469			12470		);2471		assert_eq!(2472			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2473			true2474		);24752476		let origin1 = Origin::signed(1);24772478		// default scenario2479		assert_noop!(2480			Unique::transfer(origin1, account(2), CollectionId(1), TokenId(1), 1)2481				.map_err(|e| e.error),2482			CommonError::<Test>::TransferNotAllowed2483		);2484		assert_eq!(2485			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(1))),2486			12487		);2488		assert_eq!(2489			<pallet_nonfungible::AccountBalance<Test>>::get((collection_id, account(2))),2490			02491		);2492		assert_eq!(2493			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(1), TokenId(1))),2494			true2495		);2496		assert_eq!(2497			<pallet_nonfungible::Owned<Test>>::get((collection_id, account(2), TokenId(1))),2498			false2499		);2500	});2501}25022503#[test]2504fn collection_sponsoring() {2505	new_test_ext().execute_with(|| {2506		// default_limits();2507		let user1 = 1_u64;2508		let user2 = 777_u64;2509		let origin1 = Origin::signed(user1);2510		let origin2 = Origin::signed(user2);2511		let account2 = account(user2);25122513		let collection_id =2514			create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));2515		assert_ok!(Unique::set_collection_sponsor(2516			origin1.clone(),2517			collection_id,2518			user12519		));2520		assert_ok!(Unique::confirm_sponsorship(origin1.clone(), collection_id));25212522		// Expect error while have no permissions2523		assert!(Unique::create_item(2524			origin2.clone(),2525			collection_id,2526			account2.clone(),2527			default_nft_data().into()2528		)2529		.is_err());25302531		assert_ok!(Unique::set_public_access_mode(2532			origin1.clone(),2533			collection_id,2534			AccessMode::AllowList2535		));2536		assert_ok!(Unique::add_to_allow_list(2537			origin1.clone(),2538			collection_id,2539			account2.clone()2540		));2541		assert_ok!(Unique::set_mint_permission(2542			origin1.clone(),2543			collection_id,2544			true2545		));25462547		assert_ok!(Unique::create_item(2548			origin2,2549			collection_id,2550			account2,2551			default_nft_data().into()2552		));2553	});2554}