git.delta.rocks / unique-network / refs/commits / c07e1d724b0d

difftreelog

Merge pull request #368 from UniqueNetwork/feature/CORE-386_1

bugrazoid2022-06-10parents: #3151280 #0aa9474.patch.diff
in: master
Feature/core 386 1

27 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -22,7 +22,9 @@
 pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};
 use pallet_evm_coder_substrate::dispatch_to_evm;
 use sp_std::vec::Vec;
-use up_data_structs::{Property, SponsoringRateLimit, OwnerRestrictedSet, AccessMode};
+use up_data_structs::{
+	Property, SponsoringRateLimit, OwnerRestrictedSet, AccessMode, CollectionPermissions,
+};
 use alloc::format;
 
 use crate::{Pallet, CollectionHandle, Config, CollectionProperties};
@@ -47,10 +49,15 @@
 
 #[solidity_interface(name = "Collection")]
 impl<T: Config> CollectionHandle<T>
-// where
-// 	T::AccountId: From<H256>
+where
+	T::AccountId: From<[u8; 32]>,
 {
-	fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> {
+	fn set_collection_property(
+		&mut self,
+		caller: caller,
+		key: string,
+		value: bytes,
+	) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let key = <Vec<u8>>::from(key)
 			.try_into()
@@ -83,7 +90,7 @@
 	}
 
 	fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {
-		check_is_owner(caller, self)?;
+		check_is_owner_or_admin(caller, self)?;
 
 		let sponsor = T::CrossAccountId::from_eth(sponsor);
 		self.set_sponsor(sponsor.as_sub().clone())
@@ -97,14 +104,14 @@
 			.confirm_sponsorship(caller.as_sub())
 			.map_err(dispatch_to_evm::<T>)?
 		{
-			return Err(Error::Revert("Caller is not set as sponsor".into()));
+			return Err("caller is not set as sponsor".into());
 		}
 		save(self)
 	}
 
 	#[solidity(rename_selector = "setCollectionLimit")]
 	fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {
-		check_is_owner(caller, self)?;
+		check_is_owner_or_admin(caller, self)?;
 		let mut limits = self.limits.clone();
 
 		match limit.as_str() {
@@ -128,7 +135,7 @@
 			}
 			_ => {
 				return Err(Error::Revert(format!(
-					"Unknown integer limit \"{}\"",
+					"unknown integer limit \"{}\"",
 					limit
 				)))
 			}
@@ -140,7 +147,7 @@
 
 	#[solidity(rename_selector = "setCollectionLimit")]
 	fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {
-		check_is_owner(caller, self)?;
+		check_is_owner_or_admin(caller, self)?;
 		let mut limits = self.limits.clone();
 
 		match limit.as_str() {
@@ -155,7 +162,7 @@
 			}
 			_ => {
 				return Err(Error::Revert(format!(
-					"Unknown boolean limit \"{}\"",
+					"unknown boolean limit \"{}\"",
 					limit
 				)))
 			}
@@ -169,52 +176,48 @@
 		Ok(crate::eth::collection_id_to_address(self.id))
 	}
 
-	// fn add_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {
-	// 	let mut new_admin_h256 = H256::default();
-	// 	new_admin.to_little_endian(&mut new_admin_h256.0);
-	// 	let account_id = T::AccountId::from(new_admin_h256);
-	// 	let caller = T::CrossAccountId::from_eth(caller);
-	// 	let new_admin = T::CrossAccountId::from_sub(account_id);
-	// 	<Pallet<T>>::toggle_admin(&self, &caller, &new_admin, true)
-	// 		.map_err(dispatch_to_evm::<T>)?;
-	// 	Ok(())
-	// }
+	fn add_collection_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let mut new_admin_arr: [u8; 32] = Default::default();
+		new_admin.to_big_endian(&mut new_admin_arr);
+		let account_id = T::AccountId::from(new_admin_arr);
+		let new_admin = T::CrossAccountId::from_sub(account_id);
+		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
 
-	// fn remove_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {
-	// 	let mut new_admin_h256 = H256::default();
-	// 	new_admin.to_little_endian(&mut new_admin_h256.0);
-	// 	let account_id = T::AccountId::from(new_admin_h256);
-	// 	let caller = T::CrossAccountId::from_eth(caller);
-	// 	let new_admin = T::CrossAccountId::from_sub(account_id);
-	// 	<Pallet<T>>::toggle_admin(&self, &caller, &new_admin, false)
-	// 		.map_err(dispatch_to_evm::<T>)?;
-	// 	Ok(())
-	// }
+	fn remove_collection_admin_substrate(
+		&self,
+		caller: caller,
+		new_admin: uint256,
+	) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let mut new_admin_arr: [u8; 32] = Default::default();
+		new_admin.to_big_endian(&mut new_admin_arr);
+		let account_id = T::AccountId::from(new_admin_arr);
+		let new_admin = T::CrossAccountId::from_sub(account_id);
+		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, false)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
 
 	fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
 		let new_admin = T::CrossAccountId::from_eth(new_admin);
-		<Pallet<T>>::toggle_admin(&self, &caller, &new_admin, true)
-			.map_err(dispatch_to_evm::<T>)?;
+		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
 
 	fn remove_collection_admin(&self, caller: caller, admin: address) -> Result<void> {
 		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
 		let admin = T::CrossAccountId::from_eth(admin);
-		<Pallet<T>>::toggle_admin(&self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
+		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
 
 	#[solidity(rename_selector = "setCollectionNesting")]
 	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
+		check_is_owner_or_admin(caller, self)?;
 
 		let mut permissions = self.collection.permissions.clone();
 		let mut nesting = permissions.nesting().clone();
@@ -240,11 +243,9 @@
 		collections: Vec<address>,
 	) -> Result<void> {
 		if collections.is_empty() {
-			return Err("No addresses provided".into());
+			return Err("no addresses provided".into());
 		}
-		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
+		check_is_owner_or_admin(caller, self)?;
 
 		let mut permissions = self.collection.permissions.clone();
 		match enable {
@@ -280,27 +281,34 @@
 	}
 
 	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		self.check_is_owner_or_admin(&caller)
-			.map_err(dispatch_to_evm::<T>)?;
-		self.collection.permissions.access = Some(match mode {
-			0 => AccessMode::Normal,
-			1 => AccessMode::AllowList,
-			_ => return Err("Not supported access mode".into()),
-		});
-		save(self)?;
-		Ok(())
+		check_is_owner_or_admin(caller, self)?;
+		let permissions = CollectionPermissions {
+			access: Some(match mode {
+				0 => AccessMode::Normal,
+				1 => AccessMode::AllowList,
+				_ => return Err("not supported access mode".into()),
+			}),
+			..Default::default()
+		};
+		self.collection.permissions = <Pallet<T>>::clamp_permissions(
+			self.collection.mode.clone(),
+			&self.collection.permissions,
+			permissions,
+		)
+		.map_err(dispatch_to_evm::<T>)?;
+
+		save(self)
 	}
 
 	fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
-		let caller = check_is_owner_or_admin(caller, self)?;
+		let caller = T::CrossAccountId::from_eth(caller);
 		let user = T::CrossAccountId::from_eth(user);
 		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
 
 	fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {
-		let caller = check_is_owner_or_admin(caller, self)?;
+		let caller = T::CrossAccountId::from_eth(caller);
 		let user = T::CrossAccountId::from_eth(user);
 		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
 		Ok(())
@@ -308,18 +316,19 @@
 
 	fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
 		check_is_owner_or_admin(caller, self)?;
-		self.collection.permissions.mint_mode = Some(mode);
-		save(self)?;
-		Ok(())
-	}
-}
+		let permissions = CollectionPermissions {
+			mint_mode: Some(mode),
+			..Default::default()
+		};
+		self.collection.permissions = <Pallet<T>>::clamp_permissions(
+			self.collection.mode.clone(),
+			&self.collection.permissions,
+			permissions,
+		)
+		.map_err(dispatch_to_evm::<T>)?;
 
-fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<void> {
-	let caller = T::CrossAccountId::from_eth(caller);
-	collection
-		.check_is_owner(&caller)
-		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
-	Ok(())
+		save(self)
+	}
 }
 
 fn check_is_owner_or_admin<T: Config>(
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -148,7 +148,7 @@
 					.saturating_mul(writes),
 			))
 	}
-	pub fn save(self) -> Result<(), DispatchError> {
+	pub fn save(self) -> DispatchResult {
 		<CollectionById<T>>::insert(self.id, self.collection);
 		Ok(())
 	}
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -152,12 +152,15 @@
 		via("CollectionHandle<T>", common_mut, Collection)
 	)
 )]
-impl<T: Config> FungibleHandle<T> {}
+impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
 
 generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);
 generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);
 
-impl<T: Config> CommonEvmHandler for FungibleHandle<T> {
+impl<T: Config> CommonEvmHandler for FungibleHandle<T>
+where
+	T::AccountId: From<[u8; 32]>,
+{
 	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");
 
 	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -583,13 +583,16 @@
 		TokenProperties,
 	)
 )]
-impl<T: Config> NonfungibleHandle<T> {}
+impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
 
 // Not a tests, but code generators
 generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);
 generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);
 
-impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {
+impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>
+where
+	T::AccountId: From<[u8; 32]>,
+{
 	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");
 
 	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -297,7 +297,40 @@
 	}
 }
 
-// Selector: 6aea9834
+// Selector: 780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) public view returns (uint256) {
+		require(false, stub_error);
+		index;
+		dummy;
+		return 0;
+	}
+
+	// Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		index;
+		dummy;
+		return 0;
+	}
+
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+}
+
+// Selector: 7d9262e6
 contract Collection is Dummy, ERC165 {
 	// Selector: setCollectionProperty(string,bytes) 2f073f66
 	function setCollectionProperty(string memory key, bytes memory value)
@@ -366,6 +399,20 @@
 		return 0x0000000000000000000000000000000000000000;
 	}
 
+	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
+	function addCollectionAdminSubstrate(uint256 newAdmin) public view {
+		require(false, stub_error);
+		newAdmin;
+		dummy;
+	}
+
+	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+	function removeCollectionAdminSubstrate(uint256 newAdmin) public view {
+		require(false, stub_error);
+		newAdmin;
+		dummy;
+	}
+
 	// Selector: addCollectionAdmin(address) 92e462c7
 	function addCollectionAdmin(address newAdmin) public view {
 		require(false, stub_error);
@@ -423,39 +470,6 @@
 		require(false, stub_error);
 		mode;
 		dummy = 0;
-	}
-}
-
-// Selector: 780e9d63
-contract ERC721Enumerable is Dummy, ERC165 {
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
 	}
 }
 
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -491,7 +491,7 @@
 			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.check_is_owner_or_admin(&sender)?;
 			target_collection.check_is_internal()?;
 
 			target_collection.set_sponsor(new_sponsor.clone())?;
@@ -867,7 +867,7 @@
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 			target_collection.check_is_internal()?;
-			target_collection.check_is_owner(&sender)?;
+			target_collection.check_is_owner_or_admin(&sender)?;
 			let old_limit = &target_collection.limits;
 
 			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
@@ -889,7 +889,7 @@
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
 			target_collection.check_is_internal()?;
-			target_collection.check_is_owner(&sender)?;
+			target_collection.check_is_owner_or_admin(&sender)?;
 			let old_limit = &target_collection.permissions;
 
 			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -812,7 +812,7 @@
 		for byte in key.as_slice().iter() {
 			let byte = *byte;
 
-			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' {
+			if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {
 				return Err(PropertiesError::InvalidCharacterInPropertyKey);
 			}
 		}
modifiedruntime/common/src/dispatch.rsdiffbeforeafterboth
--- a/runtime/common/src/dispatch.rs
+++ b/runtime/common/src/dispatch.rs
@@ -112,6 +112,7 @@
 		+ pallet_fungible::Config
 		+ pallet_nonfungible::Config
 		+ pallet_refungible::Config,
+	T::AccountId: From<[u8; 32]>,
 {
 	fn is_reserved(target: &H160) -> bool {
 		map_eth_to_id(target).is_some()
modifiedruntime/tests/src/tests.rsdiffbeforeafterboth
--- a/runtime/tests/src/tests.rs
+++ b/runtime/tests/src/tests.rs
@@ -1264,7 +1264,6 @@
 		let collection1_id =
 			create_test_collection_for_owner(&CollectionMode::NFT, 1, CollectionId(1));
 		let origin1 = Origin::signed(1);
-		let origin2 = Origin::signed(2);
 
 		// Add collection admins 2 and 3
 		assert_ok!(Unique::add_collection_admin(
@@ -1273,7 +1272,7 @@
 			account(2)
 		));
 		assert_ok!(Unique::add_collection_admin(
-			origin1,
+			origin1.clone(),
 			collection1_id,
 			account(3)
 		));
@@ -1289,7 +1288,7 @@
 
 		// remove admin 3
 		assert_ok!(Unique::remove_collection_admin(
-			origin2,
+			origin1,
 			CollectionId(1),
 			account(3)
 		));
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -174,7 +174,24 @@
 	function finishMinting() external returns (bool);
 }
 
-// Selector: 6aea9834
+// Selector: 780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	// Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		external
+		view
+		returns (uint256);
+
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+}
+
+// Selector: 7d9262e6
 interface Collection is Dummy, ERC165 {
 	// Selector: setCollectionProperty(string,bytes) 2f073f66
 	function setCollectionProperty(string memory key, bytes memory value)
@@ -206,6 +223,12 @@
 	// Selector: contractAddress() f6b4dfb4
 	function contractAddress() external view returns (address);
 
+	// Selector: addCollectionAdminSubstrate(uint256) 5730062b
+	function addCollectionAdminSubstrate(uint256 newAdmin) external view;
+
+	// Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+	function removeCollectionAdminSubstrate(uint256 newAdmin) external view;
+
 	// Selector: addCollectionAdmin(address) 92e462c7
 	function addCollectionAdmin(address newAdmin) external view;
 
@@ -230,23 +253,6 @@
 
 	// Selector: setCollectionMintMode(bool) 00018e84
 	function setCollectionMintMode(bool mode) external;
-}
-
-// Selector: 780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	// Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		external
-		view
-		returns (uint256);
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
 }
 
 // Selector: d74d154f
addedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -0,0 +1,296 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {expect} from 'chai';
+import privateKey from '../substrate/privateKey';
+import {
+  createEthAccount,
+  createEthAccountWithBalance, 
+  evmCollection, 
+  evmCollectionHelpers, 
+  getCollectionAddressFromResult, 
+  itWeb3,
+} from './util/helpers';
+
+describe('Add collection admins', () => {
+  itWeb3('Add admin by owner', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const newAdmin = await createEthAccount(web3);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+      .to.be.eq(newAdmin.toLocaleLowerCase());
+  });
+
+  itWeb3('Add substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const newAdmin = privateKey('//Alice');
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
+      .to.be.eq(newAdmin.address.toLocaleLowerCase());
+  });
+
+  itWeb3('(!negative tests!) Add admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdmin(admin).send();
+    
+    const user = await createEthAccount(web3);
+    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(1);
+    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+      .to.be.eq(admin.toLocaleLowerCase());
+  });
+
+  itWeb3('(!negative tests!) Add admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const notAdmin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    
+    const user = await createEthAccount(web3);
+    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(0);
+  });
+
+  itWeb3('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdmin(admin).send();
+
+    const notAdmin = privateKey('//Alice');
+    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin.addressRaw).call({from: admin}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(1);
+    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+      .to.be.eq(admin.toLocaleLowerCase());
+  });
+  
+  itWeb3('(!negative tests!) Add substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const notAdmin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    const notAdmin1 = privateKey('//Alice');
+    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin1.addressRaw).call({from: notAdmin0}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(0);
+  });
+});
+
+describe('Remove collection admins', () => {
+  itWeb3('Remove admin by owner', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const newAdmin = await createEthAccount(web3);
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();
+    {
+      const adminList = await api.rpc.unique.adminlist(collectionId);
+      expect(adminList.length).to.be.eq(1);
+      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+        .to.be.eq(newAdmin.toLocaleLowerCase());
+    }
+
+    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(0);
+  });
+
+  itWeb3('Remove substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const newAdmin = privateKey('//Alice');
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();
+    {
+      const adminList = await api.rpc.unique.adminlist(collectionId);
+      expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
+        .to.be.eq(newAdmin.address.toLocaleLowerCase());
+    }
+    
+    await collectionEvm.methods.removeCollectionAdminSubstrate(newAdmin.addressRaw).send();
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(0);
+  });
+
+  itWeb3('(!negative tests!) Remove admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+
+    const admin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    await collectionEvm.methods.addCollectionAdmin(admin0).send();
+    const admin1 = await createEthAccount(web3);
+    await collectionEvm.methods.addCollectionAdmin(admin1).send();
+
+    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))
+      .to.be.rejectedWith('NoPermission');
+    {
+      const adminList = await api.rpc.unique.adminlist(collectionId);
+      expect(adminList.length).to.be.eq(2);
+      expect(adminList.toString().toLocaleLowerCase())
+        .to.be.deep.contains(admin0.toLocaleLowerCase())
+        .to.be.deep.contains(admin1.toLocaleLowerCase());
+    }
+  });
+
+  itWeb3('(!negative tests!) Remove admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+
+    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    await collectionEvm.methods.addCollectionAdmin(admin).send();
+    const notAdmin = await createEthAccount(web3);
+
+    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))
+      .to.be.rejectedWith('NoPermission');
+    {
+      const adminList = await api.rpc.unique.adminlist(collectionId);
+      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+        .to.be.eq(admin.toLocaleLowerCase());
+      expect(adminList.length).to.be.eq(1);
+    }
+  });
+
+  itWeb3('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const adminSub = privateKey('//Alice');
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();
+    const adminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    await collectionEvm.methods.addCollectionAdmin(adminEth).send();
+
+    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: adminEth}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(2);
+    expect(adminList.toString().toLocaleLowerCase())
+      .to.be.deep.contains(adminSub.address.toLocaleLowerCase())
+      .to.be.deep.contains(adminEth.toLocaleLowerCase());
+  });
+
+  itWeb3('(!negative tests!) Remove substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {
+    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collectionHelper = evmCollectionHelpers(web3, owner);
+        
+    const result = await collectionHelper.methods
+      .createNonfungibleCollection('A', 'B', 'C')
+      .send();
+    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+
+    const adminSub = privateKey('//Alice');
+    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();
+    const notAdminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+
+    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: notAdminEth}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await api.rpc.unique.adminlist(collectionId);
+    expect(adminList.length).to.be.eq(1);
+    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
+      .to.be.eq(adminSub.address.toLocaleLowerCase());
+  });
+});
\ No newline at end of file
modifiedtests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -233,7 +233,7 @@
     const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
     expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
     expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
 
     await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
     collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
@@ -241,14 +241,13 @@
     expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
 
     const user = createEthAccount(web3);
-    let nextTokenId = await collectionEvm.methods.nextTokenId().call();
+    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
     expect(nextTokenId).to.be.equal('1');
 
     const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
     expect(oldPermissions.mintMode).to.be.false;
     expect(oldPermissions.access).to.be.equal('Normal');
 
-    //TODO: change value, when enum generated
     await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
     await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
     await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
@@ -260,34 +259,35 @@
     const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
     const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
 
-    nextTokenId = await collectionEvm.methods.nextTokenId().call({from: user});
-    expect(nextTokenId).to.be.equal('1');
-    result = await collectionEvm.methods.mintWithTokenURI(
-      user,
-      nextTokenId,
-      'Test URI',
-    ).send({from: user});
-    const events = normalizeEvents(result.events);
-    events[0].address = events[0].address.toLocaleLowerCase();
+    {
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await collectionEvm.methods.mintWithTokenURI(
+        user,
+        nextTokenId,
+        'Test URI',
+      ).send({from: user});
+      const events = normalizeEvents(result.events);
 
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionIdAddress.toLocaleLowerCase(),
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: nextTokenId,
+      expect(events).to.be.deep.equal([
+        {
+          address: collectionIdAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user,
+            tokenId: nextTokenId,
+          },
         },
-      },
-    ]);
+      ]);
 
-    expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+      const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
+      const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
 
-    const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+    }
   });
 
   itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => {
@@ -302,7 +302,7 @@
     const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
     expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
     expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
     const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
     collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
@@ -314,6 +314,7 @@
     
     const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
     const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    
   
     const userCollectionEvm = evmCollection(web3, user, collectionIdAddress);
     const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();
modifiedtests/src/eth/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createCollection.test.ts
+++ b/tests/src/eth/createCollection.test.ts
@@ -80,7 +80,7 @@
     expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
     const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
     expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
     const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
     collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
@@ -208,7 +208,7 @@
       const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('Caller is not set as sponsor');
+        .call()).to.be.rejectedWith('caller is not set as sponsor');
     }
     {
       await expect(contractEvmFromNotOwner.methods
@@ -225,6 +225,6 @@
     const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
     await expect(collectionEvm.methods
       .setCollectionLimit('badLimit', 'true')
-      .call()).to.be.rejectedWith('Unknown boolean limit "badLimit"');
+      .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
   });
 });
\ No newline at end of file
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -91,6 +91,15 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
+    ],
+    "name": "addCollectionAdminSubstrate",
+    "outputs": [],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "user", "type": "address" }
     ],
     "name": "addToCollectionAllowList",
@@ -300,6 +309,15 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
+    ],
+    "name": "removeCollectionAdminSubstrate",
+    "outputs": [],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "user", "type": "address" }
     ],
     "name": "removeFromCollectionAllowList",
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -5,7 +5,7 @@
 import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec';
 import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
 import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUnqSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsTokenChild } from '@polkadot/types/lookup';
+import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsTokenChild } from '@polkadot/types/lookup';
 import type { Observable } from '@polkadot/types/types';
 
 declare module '@polkadot/api-base/types/storage' {
@@ -436,7 +436,7 @@
       /**
        * Items to be executed, indexed by the block number that they should be executed on.
        **/
-      agenda: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletUnqSchedulerScheduledV3>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      agenda: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletUniqueSchedulerScheduledV3>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
       /**
        * Lookup from identity to the block number and index of the task.
        **/
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -1,7 +1,7 @@
 // Auto-generated via `yarn polkadot-types-from-defs`, do not edit
 /* eslint-disable */
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUnqSchedulerCall, PalletUnqSchedulerError, PalletUnqSchedulerEvent, PalletUnqSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
 import type { Data, StorageKey } from '@polkadot/types';
 import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
@@ -819,10 +819,10 @@
     PalletUniqueCall: PalletUniqueCall;
     PalletUniqueError: PalletUniqueError;
     PalletUniqueRawEvent: PalletUniqueRawEvent;
-    PalletUnqSchedulerCall: PalletUnqSchedulerCall;
-    PalletUnqSchedulerError: PalletUnqSchedulerError;
-    PalletUnqSchedulerEvent: PalletUnqSchedulerEvent;
-    PalletUnqSchedulerScheduledV3: PalletUnqSchedulerScheduledV3;
+    PalletUniqueSchedulerCall: PalletUniqueSchedulerCall;
+    PalletUniqueSchedulerError: PalletUniqueSchedulerError;
+    PalletUniqueSchedulerEvent: PalletUniqueSchedulerEvent;
+    PalletUniqueSchedulerScheduledV3: PalletUniqueSchedulerScheduledV3;
     PalletVersion: PalletVersion;
     PalletXcmCall: PalletXcmCall;
     PalletXcmError: PalletXcmError;
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -1784,8 +1784,8 @@
   readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'CollectionPermissionSet';
 }
 
-/** @name PalletUnqSchedulerCall */
-export interface PalletUnqSchedulerCall extends Enum {
+/** @name PalletUniqueSchedulerCall */
+export interface PalletUniqueSchedulerCall extends Enum {
   readonly isScheduleNamed: boolean;
   readonly asScheduleNamed: {
     readonly id: U8aFixed;
@@ -1809,8 +1809,8 @@
   readonly type: 'ScheduleNamed' | 'CancelNamed' | 'ScheduleNamedAfter';
 }
 
-/** @name PalletUnqSchedulerError */
-export interface PalletUnqSchedulerError extends Enum {
+/** @name PalletUniqueSchedulerError */
+export interface PalletUniqueSchedulerError extends Enum {
   readonly isFailedToSchedule: boolean;
   readonly isNotFound: boolean;
   readonly isTargetBlockNumberInPast: boolean;
@@ -1818,8 +1818,8 @@
   readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange';
 }
 
-/** @name PalletUnqSchedulerEvent */
-export interface PalletUnqSchedulerEvent extends Enum {
+/** @name PalletUniqueSchedulerEvent */
+export interface PalletUniqueSchedulerEvent extends Enum {
   readonly isScheduled: boolean;
   readonly asScheduled: {
     readonly when: u32;
@@ -1845,8 +1845,8 @@
   readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallLookupFailed';
 }
 
-/** @name PalletUnqSchedulerScheduledV3 */
-export interface PalletUnqSchedulerScheduledV3 extends Struct {
+/** @name PalletUniqueSchedulerScheduledV3 */
+export interface PalletUniqueSchedulerScheduledV3 extends Struct {
   readonly maybeId: Option<U8aFixed>;
   readonly priority: u8;
   readonly call: FrameSupportScheduleMaybeHashed;
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1526,9 +1526,9 @@
     users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>'
   },
   /**
-   * Lookup206: pallet_unq_scheduler::pallet::Call<T>
+   * Lookup206: pallet_unique_scheduler::pallet::Call<T>
    **/
-  PalletUnqSchedulerCall: {
+  PalletUniqueSchedulerCall: {
     _enum: {
       schedule_named: {
         id: '[u8;16]',
@@ -2181,9 +2181,9 @@
     }
   },
   /**
-   * Lookup283: pallet_unq_scheduler::pallet::Event<T>
+   * Lookup283: pallet_unique_scheduler::pallet::Event<T>
    **/
-  PalletUnqSchedulerEvent: {
+  PalletUniqueSchedulerEvent: {
     _enum: {
       Scheduled: {
         when: 'u32',
@@ -2589,9 +2589,9 @@
     _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument']
   },
   /**
-   * Lookup344: pallet_unq_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
+   * Lookup344: pallet_unique_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
    **/
-  PalletUnqSchedulerScheduledV3: {
+  PalletUniqueSchedulerScheduledV3: {
     maybeId: 'Option<[u8;16]>',
     priority: 'u8',
     call: 'FrameSupportScheduleMaybeHashed',
@@ -2748,9 +2748,9 @@
    **/
   SpCoreVoid: 'Null',
   /**
-   * Lookup351: pallet_unq_scheduler::pallet::Error<T>
+   * Lookup351: pallet_unique_scheduler::pallet::Error<T>
    **/
-  PalletUnqSchedulerError: {
+  PalletUniqueSchedulerError: {
     _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange']
   },
   /**
modifiedtests/src/interfaces/registry.tsdiffbeforeafterboth
--- a/tests/src/interfaces/registry.ts
+++ b/tests/src/interfaces/registry.ts
@@ -1,7 +1,7 @@
 // Auto-generated via `yarn polkadot-types-from-defs`, do not edit
 /* eslint-disable */
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUnqSchedulerCall, PalletUnqSchedulerError, PalletUnqSchedulerEvent, PalletUnqSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
 
 declare module '@polkadot/types/types/registry' {
   export interface InterfaceTypes {
@@ -133,10 +133,10 @@
     PalletUniqueCall: PalletUniqueCall;
     PalletUniqueError: PalletUniqueError;
     PalletUniqueRawEvent: PalletUniqueRawEvent;
-    PalletUnqSchedulerCall: PalletUnqSchedulerCall;
-    PalletUnqSchedulerError: PalletUnqSchedulerError;
-    PalletUnqSchedulerEvent: PalletUnqSchedulerEvent;
-    PalletUnqSchedulerScheduledV3: PalletUnqSchedulerScheduledV3;
+    PalletUniqueSchedulerCall: PalletUniqueSchedulerCall;
+    PalletUniqueSchedulerError: PalletUniqueSchedulerError;
+    PalletUniqueSchedulerEvent: PalletUniqueSchedulerEvent;
+    PalletUniqueSchedulerScheduledV3: PalletUniqueSchedulerScheduledV3;
     PalletXcmCall: PalletXcmCall;
     PalletXcmError: PalletXcmError;
     PalletXcmEvent: PalletXcmEvent;
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1650,8 +1650,8 @@
     readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
   }
 
-  /** @name PalletUnqSchedulerCall (206) */
-  export interface PalletUnqSchedulerCall extends Enum {
+  /** @name PalletUniqueSchedulerCall (206) */
+  export interface PalletUniqueSchedulerCall extends Enum {
     readonly isScheduleNamed: boolean;
     readonly asScheduleNamed: {
       readonly id: U8aFixed;
@@ -2350,8 +2350,8 @@
     readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'CollectionPermissionSet';
   }
 
-  /** @name PalletUnqSchedulerEvent (283) */
-  export interface PalletUnqSchedulerEvent extends Enum {
+  /** @name PalletUniqueSchedulerEvent (283) */
+  export interface PalletUniqueSchedulerEvent extends Enum {
     readonly isScheduled: boolean;
     readonly asScheduled: {
       readonly when: u32;
@@ -2805,8 +2805,8 @@
     readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument';
   }
 
-  /** @name PalletUnqSchedulerScheduledV3 (344) */
-  export interface PalletUnqSchedulerScheduledV3 extends Struct {
+  /** @name PalletUniqueSchedulerScheduledV3 (344) */
+  export interface PalletUniqueSchedulerScheduledV3 extends Struct {
     readonly maybeId: Option<U8aFixed>;
     readonly priority: u8;
     readonly call: FrameSupportScheduleMaybeHashed;
@@ -2864,8 +2864,8 @@
   /** @name SpCoreVoid (350) */
   export type SpCoreVoid = Null;
 
-  /** @name PalletUnqSchedulerError (351) */
-  export interface PalletUnqSchedulerError extends Enum {
+  /** @name PalletUniqueSchedulerError (351) */
+  export interface PalletUniqueSchedulerError extends Enum {
     readonly isFailedToSchedule: boolean;
     readonly isNotFound: boolean;
     readonly isTargetBlockNumberInPast: boolean;
modifiedtests/src/nesting/properties.test.tsdiffbeforeafterboth
before · tests/src/nesting/properties.test.ts
1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4  addCollectionAdminExpectSuccess,5  createCollectionExpectSuccess,6  createItemExpectSuccess,7  getCreateCollectionResult,8  transferExpectSuccess,9} from '../util/helpers';10import {IKeyringPair} from '@polkadot/types/types';1112let alice: IKeyringPair;13let bob: IKeyringPair;14let charlie: IKeyringPair;1516describe('Composite Properties Test', () => {17  before(async () => {18    await usingApi(async (api, privateKeyWrapper) => {19      alice = privateKeyWrapper('//Alice');20      bob = privateKeyWrapper('//Bob');21    });22  });2324  it('Makes sure collectionById supplies required fields', async () => {25    await usingApi(async api => {26      const collectionId = await createCollectionExpectSuccess();2728      const collectionOption = await api.rpc.unique.collectionById(collectionId);29      expect(collectionOption.isSome).to.be.true;30      let collection = collectionOption.unwrap();31      expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;32      expect(collection.properties.toHuman()).to.be.empty;3334      const propertyPermissions = [35        {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},36        {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},37      ];38      await expect(executeTransaction(39        api, 40        alice, 41        api.tx.unique.setPropertyPermissions(collectionId, propertyPermissions), 42      )).to.not.be.rejected;4344      const collectionProperties = [45        {key: 'black_hole', value: 'LIGO'},46        {key: 'electron', value: 'come bond'}, 47      ];48      await expect(executeTransaction(49        api, 50        alice, 51        api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 52      )).to.not.be.rejected;5354      collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();55      expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);56      expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);57    });58  });59});6061// ---------- COLLECTION PROPERTIES6263describe('Integration Test: Collection Properties', () => {64  before(async () => {65    await usingApi(async (api, privateKeyWrapper) => {66      alice = privateKeyWrapper('//Alice');67      bob = privateKeyWrapper('//Bob');68    });69  });7071  it('Reads properties from a collection', async () => {72    await usingApi(async api => {73      const collection = await createCollectionExpectSuccess();74      const properties = (await api.query.common.collectionProperties(collection)).toJSON();75      expect(properties.map).to.be.empty;76      expect(properties.consumedSpace).to.equal(0);77    });78  });7980  it('Sets properties for a collection', async () => {81    await usingApi(async api => {82      const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));83      const {collectionId} = getCreateCollectionResult(events);8485      // As owner86      await expect(executeTransaction(87        api, 88        bob, 89        api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 90      )).to.not.be.rejected;9192      await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9394      // As administrator95      await expect(executeTransaction(96        api, 97        alice, 98        api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 99      )).to.not.be.rejected;100101      const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();102      expect(properties).to.be.deep.equal([103        {key: 'electron', value: 'come bond'},104        {key: 'black_hole', value: ''},105      ]);106    });107  });108109  it('Changes properties of a collection', async () => {110    await usingApi(async api => {111      const collection = await createCollectionExpectSuccess();112113      await expect(executeTransaction(114        api, 115        alice, 116        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 117      )).to.not.be.rejected;118119      // Mutate the properties120      await expect(executeTransaction(121        api, 122        alice, 123        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 124      )).to.not.be.rejected;125126      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();127      expect(properties).to.be.deep.equal([128        {key: 'electron', value: 'bonded'},129        {key: 'black_hole', value: 'LIGO'},130      ]);131    });132  });133134  it('Deletes properties of a collection', async () => {135    await usingApi(async api => {136      const collection = await createCollectionExpectSuccess();137138      await expect(executeTransaction(139        api, 140        alice, 141        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 142      )).to.not.be.rejected;143144      await expect(executeTransaction(145        api, 146        alice, 147        api.tx.unique.deleteCollectionProperties(collection, ['electron']), 148      )).to.not.be.rejected;149150      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();151      expect(properties).to.be.deep.equal([152        {key: 'black_hole', value: 'LIGO'},153      ]);154    });155  });156});157158describe('Negative Integration Test: Collection Properties', () => {159  before(async () => {160    await usingApi(async (api, privateKeyWrapper) => {161      alice = privateKeyWrapper('//Alice');162      bob = privateKeyWrapper('//Bob');163    });164  });165  166  it('Fails to set properties in a collection if not its onwer/administrator', async () => {167    await usingApi(async api => {168      const collection = await createCollectionExpectSuccess();169170      await expect(executeTransaction(171        api, 172        bob, 173        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 174      )).to.be.rejectedWith(/common\.NoPermission/);175  176      const properties = (await api.query.common.collectionProperties(collection)).toJSON();177      expect(properties.map).to.be.empty;178      expect(properties.consumedSpace).to.equal(0);179    });180  });181  182  it('Fails to set properties that exceed the limits', async () => {183    await usingApi(async api => {184      const collection = await createCollectionExpectSuccess();185      const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 186187      // Mute the general tx parsing error, too many bytes to process188      {189        console.error = () => {};190        await expect(executeTransaction(191          api, 192          alice, 193          api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 194        )).to.be.rejected;195      }196197      let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();198      expect(properties).to.be.empty;199200      await expect(executeTransaction(201        api, 202        alice, 203        api.tx.unique.setCollectionProperties(collection, [204          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 205          {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 206        ]), 207      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);208209      properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();210      expect(properties).to.be.empty;211    });212  });213  214  it('Fails to set more properties than it is allowed', async () => {215    await usingApi(async api => {216      const collection = await createCollectionExpectSuccess();217218      const propertiesToBeSet = [];219      for (let i = 0; i < 65; i++) {220        propertiesToBeSet.push({221          key: 'electron_' + i,222          value: Math.random() > 0.5 ? 'high' : 'low',223        });224      }225226      await expect(executeTransaction(227        api, 228        alice, 229        api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 230      )).to.be.rejectedWith(/common\.PropertyLimitReached/);231232      const properties = (await api.query.common.collectionProperties(collection)).toJSON();233      expect(properties.map).to.be.empty;234      expect(properties.consumedSpace).to.equal(0);235    });236  });237238  it('Fails to set properties with invalid names', async () => {239    await usingApi(async api => {240      const collection = await createCollectionExpectSuccess();241242      const invalidProperties = [243        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],244        [{key: 'Mr.Sandman', value: 'Bring me a gene'}],245        [{key: 'déjà vu', value: 'hmm...'}],246      ];247248      for (let i = 0; i < invalidProperties.length; i++) {249        await expect(executeTransaction(250          api, 251          alice, 252          api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 253        ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);254      }255256      await expect(executeTransaction(257        api, 258        alice, 259        api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 260      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);261262      await expect(executeTransaction(263        api, 264        alice, 265        api.tx.unique.setCollectionProperties(collection, [266          {key: 'CRISPR-Cas9', value: 'rewriting nature!'},267        ]), 268      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;269270      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');271272      const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();273      expect(properties).to.be.deep.equal([274        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},275      ]);276277      for (let i = 0; i < invalidProperties.length; i++) {278        await expect(executeTransaction(279          api, 280          alice, 281          api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 282        ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);283      }284    });285  });286});287288// ---------- ACCESS RIGHTS289290describe('Integration Test: Access Rights to Token Properties', () => {291  before(async () => {292    await usingApi(async (api, privateKeyWrapper) => {293      alice = privateKeyWrapper('//Alice');294      bob = privateKeyWrapper('//Bob');295    });296  });297  298  it('Reads access rights to properties of a collection', async () => {299    await usingApi(async api => {300      const collection = await createCollectionExpectSuccess();301      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();302      expect(propertyRights).to.be.empty;303    });304  });305  306  it('Sets access rights to properties of a collection', async () => {307    await usingApi(async api => {308      const collection = await createCollectionExpectSuccess();309310      await expect(executeTransaction(311        api, 312        alice, 313        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 314      )).to.not.be.rejected;315316      await addCollectionAdminExpectSuccess(alice, collection, bob.address);317318      await expect(executeTransaction(319        api, 320        alice, 321        api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 322      )).to.not.be.rejected;323324      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();325      expect(propertyRights).to.be.deep.equal([326        {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},327        {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},328      ]);329    });330  });331  332  it('Changes access rights to properties of a collection', async () => {333    await usingApi(async api => {334      const collection = await createCollectionExpectSuccess();335336      await expect(executeTransaction(337        api, 338        alice, 339        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 340      )).to.not.be.rejected;341342      await expect(executeTransaction(343        api, 344        alice, 345        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 346      )).to.not.be.rejected;347348      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();349      expect(propertyRights).to.be.deep.equal([350        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},351      ]);352    });353  });354});355356describe('Negative Integration Test: Access Rights to Token Properties', () => {357  before(async () => {358    await usingApi(async (api, privateKeyWrapper) => {359      alice = privateKeyWrapper('//Alice');360      bob = privateKeyWrapper('//Bob');361    });362  });363364  it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {365    await usingApi(async api => {366      const collection = await createCollectionExpectSuccess();367368      await expect(executeTransaction(369        api, 370        bob, 371        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 372      )).to.be.rejectedWith(/common\.NoPermission/);373374      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();375      expect(propertyRights).to.be.empty;376    });377  });378379  it('Prevents from adding too many possible properties', async () => {380    await usingApi(async api => {381      const collection = await createCollectionExpectSuccess();382383      const constitution = [];384      for (let i = 0; i < 65; i++) {385        constitution.push({386          key: 'property_' + i,387          permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},388        });389      }390391      await expect(executeTransaction(392        api, 393        alice, 394        api.tx.unique.setPropertyPermissions(collection, constitution), 395      )).to.be.rejectedWith(/common\.PropertyLimitReached/);396397      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();398      expect(propertyRights).to.be.empty;399    });400  });401402  it('Prevents access rights to be modified if constant', async () => {403    await usingApi(async api => {404      const collection = await createCollectionExpectSuccess();405406      await expect(executeTransaction(407        api, 408        alice, 409        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 410      )).to.not.be.rejected;411412      await expect(executeTransaction(413        api, 414        alice, 415        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 416      )).to.be.rejectedWith(/common\.NoPermission/);417418      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();419      expect(propertyRights).to.deep.equal([420        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},421      ]);422    });423  });424425  it('Prevents adding properties with invalid names', async () => {426    await usingApi(async api => {427      const collection = await createCollectionExpectSuccess();428429      const invalidProperties = [430        [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],431        [{key: 'G#4', permission: {tokenOwner: true}}],432        [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],433      ];434435      for (let i = 0; i < invalidProperties.length; i++) {436        await expect(executeTransaction(437          api, 438          alice, 439          api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 440        ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);441      }442443      await expect(executeTransaction(444        api, 445        alice, 446        api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 447      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);448449      const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string450      await expect(executeTransaction(451        api, 452        alice, 453        api.tx.unique.setPropertyPermissions(collection, [454          {key: correctKey, permission: {collectionAdmin: true}},455        ]), 456      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;457458      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');459460      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();461      expect(propertyRights).to.be.deep.equal([462        {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},463      ]);464    });465  });466});467468// ---------- TOKEN PROPERTIES469470describe('Integration Test: Token Properties', () => {471  let collection: number;472  let token: number;473  let permissions: {permission: any, signers: IKeyringPair[]}[];474475  before(async () => {476    await usingApi(async (api, privateKeyWrapper) => {477      alice = privateKeyWrapper('//Alice');478      bob = privateKeyWrapper('//Bob');479      charlie = privateKeyWrapper('//Charlie');480481      permissions = [482        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},483        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},484        {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},485        {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},486        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},487        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},488      ];489    });490  });491492  beforeEach(async () => {493    await usingApi(async () => {494      collection = await createCollectionExpectSuccess();495      token = await createItemExpectSuccess(alice, collection, 'NFT');496      await addCollectionAdminExpectSuccess(alice, collection, bob.address);497      await transferExpectSuccess(collection, token, alice, charlie);498    });499  });500  501  it('Reads yet empty properties of a token', async () => {502    await usingApi(async api => {503      const collection = await createCollectionExpectSuccess();504      const token = await createItemExpectSuccess(alice, collection, 'NFT');505  506      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();507      expect(properties.map).to.be.empty;508      expect(properties.consumedSpace).to.be.equal(0);509510      const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;511      expect(tokenData).to.be.empty;512    });513  });514515  it('Assigns properties to a token according to permissions', async () => {516    await usingApi(async api => {517      const propertyKeys: string[] = [];518      let i = 0;519      for (const permission of permissions) {520        for (const signer of permission.signers) {521          const key = i + '_' + signer.address;522          propertyKeys.push(key);523524          await expect(executeTransaction(525            api, 526            alice, 527            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 528          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;529530          await expect(executeTransaction(531            api, 532            signer, 533            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 534          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;535        }536537        i++;538      }539540      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];541      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];542      for (let i = 0; i < properties.length; i++) {543        expect(properties[i].value).to.be.equal('Serotonin increase');544        expect(tokensData[i].value).to.be.equal('Serotonin increase');545      }546    });547  });548549  it('Changes properties of a token according to permissions', async () => {550    await usingApi(async api => {551      const propertyKeys: string[] = [];552      let i = 0;553      for (const permission of permissions) {554        if (!permission.permission.mutable) continue;555        556        for (const signer of permission.signers) {557          const key = i + '_' + signer.address;558          propertyKeys.push(key);559560          await expect(executeTransaction(561            api, 562            alice, 563            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 564          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;565566          await expect(executeTransaction(567            api, 568            signer, 569            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 570          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;571572          await expect(executeTransaction(573            api, 574            signer, 575            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 576          ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;577        }578579        i++;580      }581582      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];583      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];584      for (let i = 0; i < properties.length; i++) {585        expect(properties[i].value).to.be.equal('Serotonin stable');586        expect(tokensData[i].value).to.be.equal('Serotonin stable');587      }588    });589  });590591  it('Deletes properties of a token according to permissions', async () => {592    await usingApi(async api => {593      const propertyKeys: string[] = [];594      let i = 0;595596      for (const permission of permissions) {597        if (!permission.permission.mutable) continue;598        599        for (const signer of permission.signers) {600          const key = i + '_' + signer.address;601          propertyKeys.push(key);602603          await expect(executeTransaction(604            api, 605            alice, 606            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 607          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;608609          await expect(executeTransaction(610            api, 611            signer, 612            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 613          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;614615          await expect(executeTransaction(616            api, 617            signer, 618            api.tx.unique.deleteTokenProperties(collection, token, [key]), 619          ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;620        }621        622        i++;623      }624625      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];626      expect(properties).to.be.empty;627      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];628      expect(tokensData).to.be.empty;629      expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);630    });631  });632});633634describe('Negative Integration Test: Token Properties', () => {635  let collection: number;636  let token: number;637  let originalSpace: number;638  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];639640  before(async () => {641    await usingApi(async (api, privateKeyWrapper) => {642      alice = privateKeyWrapper('//Alice');643      bob = privateKeyWrapper('//Bob');644      charlie = privateKeyWrapper('//Charlie');645      const dave = privateKeyWrapper('//Dave');646647      constitution = [648        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},649        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},650        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},651        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},652        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},653        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},654      ];655    });656  });657658  beforeEach(async () => {659    collection = await createCollectionExpectSuccess();660    token = await createItemExpectSuccess(alice, collection, 'NFT');661    await addCollectionAdminExpectSuccess(alice, collection, bob.address);662    await transferExpectSuccess(collection, token, alice, charlie);663        664    await usingApi(async api => {665      let i = 0;666      for (const passage of constitution) {667        const signer = passage.signers[0];668        669        await expect(executeTransaction(670          api, 671          alice, 672          api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 673        ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;674675        await expect(executeTransaction(676          api, 677          signer, 678          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 679        ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;680681        i++;682      }683684      originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;685    });686  });687688  it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {689    await usingApi(async api => {690      let i = -1;691      for (const forbiddance of constitution) {692        i++;693        if (!forbiddance.permission.mutable) continue;694695        await expect(executeTransaction(696          api, 697          forbiddance.sinner, 698          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 699        ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);700701        await expect(executeTransaction(702          api, 703          forbiddance.sinner, 704          api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 705        ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);706      }707708      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();709      expect(properties.consumedSpace).to.be.equal(originalSpace);710    });711  });712713  it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {714    await usingApi(async api => {715      let i = -1;716      for (const permission of constitution) {717        i++;718        if (permission.permission.mutable) continue;719720        await expect(executeTransaction(721          api, 722          permission.signers[0], 723          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 724        ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);725726        await expect(executeTransaction(727          api, 728          permission.signers[0], 729          api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 730        ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);731      }732733      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();734      expect(properties.consumedSpace).to.be.equal(originalSpace);735    });736  });737738  it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {739    await usingApi(async api => {740      await expect(executeTransaction(741        api, 742        alice, 743        api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 744      ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);745        746      await expect(executeTransaction(747        api, 748        alice, 749        api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 750      ), 'on setting a new non-permitted property').to.not.be.rejected;751752      await expect(executeTransaction(753        api, 754        alice, 755        api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 756      ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);757758      expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;759      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();760      expect(properties.consumedSpace).to.be.equal(originalSpace);761    });762  });763764  it('Forbids adding too many properties to a token', async () => {765    await usingApi(async api => {766      await expect(executeTransaction(767        api, 768        alice, 769        api.tx.unique.setPropertyPermissions(collection, [770          {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 771          {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},772        ]), 773      ), 'on setting a new non-permitted property').to.not.be.rejected;774775      // Mute the general tx parsing error776      {777        console.error = () => {};778        await expect(executeTransaction(779          api, 780          alice, 781          api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 782        )).to.be.rejected;783      }784785      await expect(executeTransaction(786        api, 787        alice, 788        api.tx.unique.setTokenProperties(collection, token, [789          {key: 'a_holy_book', value: 'word '.repeat(3277)}, 790          {key: 'young_years', value: 'neverending'.repeat(1490)},791        ]), 792      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);793794      expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;795      const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();796      expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);797    });798  });799});
modifiedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/removeCollectionAdmin.test.ts
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -118,11 +118,17 @@
       const bob = privateKeyWrapper('//Bob');
       const charlie = privateKeyWrapper('//Charlie');
 
+      const addBobAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
+      await submitTransactionAsync(alice, addBobAdminTx);
+      const addCharlieAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(charlie.address));
+      await submitTransactionAsync(alice, addCharlieAdminTx);
+
       const adminListAfterAddAdmin = await getAdminList(api, collectionId);
       expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
+      expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(charlie.address));
 
       const removeAdminTx = api.tx.unique.removeCollectionAdmin(collectionId, normalizeAccountId(bob.address));
-      await expect(submitTransactionAsync(charlie, removeAdminTx)).to.be.rejected;
+      await expect(submitTransactionExpectFailAsync(charlie, removeAdminTx)).to.be.rejected;
 
       const adminListAfterRemoveAdmin = await getAdminList(api, collectionId);
       expect(adminListAfterRemoveAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
modifiedtests/src/setCollectionLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionLimits.test.ts
+++ b/tests/src/setCollectionLimits.test.ts
@@ -46,6 +46,7 @@
   before(async () => {
     await usingApi(async (api, privateKeyWrapper) => {
       alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
       collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
     });
   });
@@ -115,6 +116,21 @@
     });
   });
 
+  it('execute setCollectionLimits from admin collection', async () => {
+    await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob.address);
+    await usingApi(async (api: ApiPromise) => {
+      tx = api.tx.unique.setCollectionLimits(
+        collectionIdForTesting,
+        {
+          accountTokenOwnershipLimit,
+          sponsoredDataSize,
+          // sponsoredMintSize,
+          tokenLimit,
+        },
+      );
+      await expect(submitTransactionAsync(bob, tx)).to.be.not.rejected;
+    });
+  });
 });
 
 describe('setCollectionLimits negative', () => {
@@ -143,21 +159,6 @@
     });
   });
   it('execute setCollectionLimits from user who is not owner of this collection', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      tx = api.tx.unique.setCollectionLimits(
-        collectionIdForTesting,
-        {
-          accountTokenOwnershipLimit,
-          sponsoredDataSize,
-          // sponsoredMintSize,
-          tokenLimit,
-        },
-      );
-      await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected;
-    });
-  });
-  it('execute setCollectionLimits from admin collection', async () => {
-    await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob.address);
     await usingApi(async (api: ApiPromise) => {
       tx = api.tx.unique.setCollectionLimits(
         collectionIdForTesting,
modifiedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -65,6 +65,11 @@
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await setCollectionSponsorExpectSuccess(collectionId, charlie.address);
   });
+  it('Collection admin add sponsor', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+    await setCollectionSponsorExpectSuccess(collectionId, charlie.address, '//Bob');
+  });
 });
 
 describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => {
@@ -93,10 +98,5 @@
     const collectionId = await createCollectionExpectSuccess();
     await destroyCollectionExpectSuccess(collectionId);
     await setCollectionSponsorExpectFailure(collectionId, bob.address);
-  });
-  it('(!negative test!) Collection admin add sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess();
-    await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
-    await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Bob');
   });
 });
modifiedtests/src/setMintPermission.test.tsdiffbeforeafterboth
--- a/tests/src/setMintPermission.test.ts
+++ b/tests/src/setMintPermission.test.ts
@@ -67,6 +67,14 @@
       await setMintPermissionExpectSuccess(alice, collectionId, false);
     });
   });
+
+  it('Collection admin success on set', async () => {
+    await usingApi(async () => {
+      const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+      await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+      await setMintPermissionExpectSuccess(bob, collectionId, true);
+    });
+  });
 });
 
 describe('Negative Integration Test setMintPermission', () => {
@@ -100,14 +108,6 @@
     const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
     await enableAllowListExpectSuccess(alice, collectionId);
     await setMintPermissionExpectFailure(bob, collectionId, true);
-  });
-
-  it('Collection admin fails on set', async () => {
-    await usingApi(async () => {
-      const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
-      await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
-      await setMintPermissionExpectFailure(bob, collectionId, true);
-    });
   });
 
   it('ensure non-allow-listed non-privileged address can\'t mint tokens', async () => {
modifiedtests/src/setPublicAccessMode.test.tsdiffbeforeafterboth
--- a/tests/src/setPublicAccessMode.test.ts
+++ b/tests/src/setPublicAccessMode.test.ts
@@ -103,22 +103,23 @@
       await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected;
     });
   });
-});
 
-describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => {
-  before(async () => {
-    await usingApi(async (api, privateKeyWrapper) => {
-      alice = privateKeyWrapper('//Alice');
-      bob = privateKeyWrapper('//Bob');
-    });
-  });
   it('setPublicAccessMode by collection admin', async () => {
     await usingApi(async (api: ApiPromise) => {
       // tslint:disable-next-line: no-bitwise
       const collectionId = await createCollectionExpectSuccess();
       await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
       const tx = api.tx.unique.setCollectionPermissions(collectionId, {access: 'AllowList'});
-      await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected;
+      await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.not.rejected;
+    });
+  });
+});
+
+describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => {
+  before(async () => {
+    await usingApi(async (api, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
     });
   });
 });