git.delta.rocks / unique-network / refs/commits / 51e66acd38a2

difftreelog

Merge branch 'develop' into feature/CORE-189

Igor Kozyrev2021-09-09parents: #2e92a20 #e873ca1.patch.diff
in: master

9 files changed

modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -196,6 +196,7 @@
 					const_on_chain_schema: vec![],
 					variable_on_chain_schema: vec![],
 					limits: CollectionLimits::default(),
+					meta_update_permission: MetaUpdatePermission::ItemOwner,
 					transfers_enabled: true,
 				},
 			)],
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -43,7 +43,7 @@
 	OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,
 	MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,
 	CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,
-	FungibleItemType, ReFungibleItemType,
+	MetaUpdatePermission, FungibleItemType, ReFungibleItemType,
 };
 
 #[cfg(test)]
@@ -145,6 +145,10 @@
 		BadCreateRefungibleCall,
 		/// Gas limit exceeded
 		OutOfGas,
+		/// Metadata update denied by collection settings
+		MetadataUpdateDenied,
+		/// Metadata update flag become unmutable with None option
+		MetadataFlagFrozen,
 		/// Collection settings not allowing items transferring
 		TransferNotAllowed,
 		/// Can't transfer tokens to ethereum zero address
@@ -541,6 +545,7 @@
 				variable_on_chain_schema: Vec::new(),
 				const_on_chain_schema: Vec::new(),
 				limits,
+				meta_update_permission: MetaUpdatePermission::default(),
 				transfers_enabled: true,
 			};
 
@@ -940,6 +945,36 @@
 			target_collection.save()
 		}
 
+		// TODO! transaction weight
+		/// Set meta_update_permission value for particular collection
+		///
+		/// # Permissions
+		///
+		/// * Collection Owner.
+		///
+		/// # Arguments
+		///
+		/// * collection_id: ID of the collection.
+		///
+		/// * value: New flag value.
+		#[weight = <T as Config>::WeightInfo::burn_item()]
+		#[transactional]
+		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {
+
+			let sender = ensure_signed(origin)?;
+			let mut target_collection = Self::get_collection(collection_id)?;
+
+			ensure!(
+				target_collection.meta_update_permission != MetaUpdatePermission::None,
+				Error::<T>::MetadataFlagFrozen
+			);
+			Self::check_owner_permissions(&target_collection, &sender)?;
+
+			target_collection.meta_update_permission = value;
+
+			target_collection.save()
+		}
+
 		/// Destroys a concrete instance of NFT.
 		///
 		/// # Permissions
@@ -1485,10 +1520,11 @@
 			Error::<T>::TokenVariableDataLimitExceeded
 		);
 
-		// Modify permissions check
 		ensure!(
-			Self::is_item_owner(sender, collection, item_id)?
-				|| Self::is_owner_or_admin_permissions(collection, sender)?,
+			(Self::is_item_owner(sender, collection, item_id)?
+				&& collection.meta_update_permission == MetaUpdatePermission::ItemOwner)
+				|| (Self::is_owner_or_admin_permissions(collection, sender)?
+					&& collection.meta_update_permission == MetaUpdatePermission::Admin),
 			Error::<T>::NoPermission
 		);
 
@@ -1504,6 +1540,26 @@
 		Ok(())
 	}
 
+	pub fn meta_update_check(
+		sender: &T::CrossAccountId,
+		collection: &CollectionHandle<T>,
+		item_id: TokenId,
+	) -> DispatchResult {
+		match collection.meta_update_permission {
+			MetaUpdatePermission::ItemOwner => ensure!(
+				Self::is_item_owner(sender, collection, item_id)?,
+				Error::<T>::NoPermission
+			),
+			MetaUpdatePermission::Admin => ensure!(
+				Self::is_owner_or_admin_permissions(collection, sender)?,
+				Error::<T>::NoPermission
+			),
+			MetaUpdatePermission::None => fail!(Error::<T>::MetadataUpdateDenied),
+		}
+
+		Ok(())
+	}
+
 	pub fn get_variable_metadata(
 		collection: &CollectionHandle<T>,
 		item_id: TokenId,
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
2004 });2004 });
2005}2005}
2006
2007#[test]
2008fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {
2009 new_test_ext().execute_with(|| {
2010 //default_limits();
2011
2012 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
2013
2014 let origin1 = Origin::signed(1);
2015
2016 let data = default_nft_data();
2017 create_test_item(1, &data.into());
2018
2019 TemplateModule::set_meta_update_permission_flag(
2020 origin1.clone(),
2021 collection_id,
2022 MetaUpdatePermission::ItemOwner,
2023 );
2024
2025 let variable_data = b"ten chars.".to_vec();
2026 assert_ok!(TemplateModule::set_variable_meta_data(
2027 origin1,
2028 collection_id,
2029 1,
2030 variable_data.clone()
2031 ));
2032
2033 assert_eq!(
2034 TemplateModule::nft_item_id(collection_id, 1)
2035 .unwrap()
2036 .variable_data,
2037 variable_data
2038 );
2039 });
2040}
2041
2042#[test]
2043fn set_variable_meta_data_on_nft_with_item_owner_permission_flag_neg() {
2044 new_test_ext().execute_with(|| {
2045 // default_limits();
2046
2047 let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
2048
2049 let origin1 = Origin::signed(1);
2050 let origin2 = Origin::signed(2);
2051
2052 assert_ok!(TemplateModule::set_mint_permission(
2053 origin2.clone(),
2054 collection_id,
2055 true
2056 ));
2057 assert_ok!(TemplateModule::add_to_white_list(
2058 origin2.clone(),
2059 collection_id,
2060 account(1)
2061 ));
2062
2063 let data = default_nft_data();
2064 create_test_item(1, &data.into());
2065
2066 assert_ok!(TemplateModule::set_meta_update_permission_flag(
2067 origin2.clone(),
2068 collection_id,
2069 MetaUpdatePermission::ItemOwner,
2070 ));
2071
2072 let variable_data = b"ten chars.++".to_vec();
2073 assert_noop!(
2074 TemplateModule::set_variable_meta_data(
2075 origin2,
2076 collection_id,
2077 1,
2078 variable_data.clone()
2079 ),
2080 Error::<Test>::TokenVariableDataLimitExceeded
2081 );
20062082
2007#[test]2083 #[test]
2008fn collection_transfer_flag_works() {2084 fn collection_transfer_flag_works() {
2029 });2105 });
2030}2106 }
2107
2108 #[test]
2109 fn set_variable_meta_data_on_nft_with_admin_flag() {
2110 new_test_ext().execute_with(|| {
2111 // default_limits();
2112
2113 let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
2114
2115 let origin1 = Origin::signed(1);
2116 let origin2 = Origin::signed(2);
2117
2118 assert_ok!(TemplateModule::set_mint_permission(
2119 origin2.clone(),
2120 collection_id,
2121 true
2122 ));
2123 assert_ok!(TemplateModule::add_to_white_list(
2124 origin2.clone(),
2125 collection_id,
2126 account(1)
2127 ));
2128
2129 assert_ok!(TemplateModule::add_collection_admin(
2130 origin2.clone(),
2131 collection_id,
2132 account(1)
2133 ));
2134
2135 let data = default_nft_data();
2136 create_test_item(1, &data.into());
2137
2138 assert_ok!(TemplateModule::set_meta_update_permission_flag(
2139 origin2.clone(),
2140 collection_id,
2141 MetaUpdatePermission::Admin,
2142 ));
2143
2144 let variable_data = b"test set_variable_meta_data method.".to_vec();
2145 assert_ok!(TemplateModule::set_variable_meta_data(
2146 origin1,
2147 collection_id,
2148 1,
2149 variable_data.clone()
2150 ));
2151
2152 assert_eq!(
2153 TemplateModule::nft_item_id(collection_id, 1)
2154 .unwrap()
2155 .variable_data,
2156 variable_data
2157 );
2158 });
2159 }
2160
2161 #[test]
2162 fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
2163 new_test_ext().execute_with(|| {
2164 // default_limits();
2165
2166 let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
2167
2168 let origin1 = Origin::signed(1);
2169 let origin2 = Origin::signed(2);
2170
2171 assert_ok!(TemplateModule::set_mint_permission(
2172 origin2.clone(),
2173 collection_id,
2174 true
2175 ));
2176 assert_ok!(TemplateModule::add_to_white_list(
2177 origin2.clone(),
2178 collection_id,
2179 account(1)
2180 ));
2181
2182 let data = default_nft_data();
2183 create_test_item(1, &data.into());
2184
2185 assert_ok!(TemplateModule::set_meta_update_permission_flag(
2186 origin2.clone(),
2187 collection_id,
2188 MetaUpdatePermission::Admin,
2189 ));
2190
2191 let variable_data = b"test set_variable_meta_data method.".to_vec();
2192 assert_noop!(
2193 TemplateModule::set_variable_meta_data(
2194 origin1,
2195 collection_id,
2196 1,
2197 variable_data.clone()
2198 ),
2199 Error::<Test>::NoPermission
2200 );
2201 });
2202 }
2203
2204 #[test]
2205 fn set_variable_meta_flag_after_freeze() {
2206 new_test_ext().execute_with(|| {
2207 // default_limits();
2208
2209 let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
2210
2211 let origin2 = Origin::signed(2);
2212
2213 assert_ok!(TemplateModule::set_meta_update_permission_flag(
2214 origin2.clone(),
2215 collection_id,
2216 MetaUpdatePermission::None,
2217 ));
2218 assert_noop!(
2219 TemplateModule::set_meta_update_permission_flag(
2220 origin2.clone(),
2221 collection_id,
2222 MetaUpdatePermission::Admin
2223 ),
2224 Error::<Test>::MetadataFlagFrozen
2225 );
2226 });
2227 }
2228
2229 #[test]
2230 fn set_variable_meta_data_on_nft_with_none_flag_neg() {
2231 new_test_ext().execute_with(|| {
2232 // default_limits();
2233
2234 let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
2235 let origin1 = Origin::signed(1);
2236
2237 let data = default_nft_data();
2238 create_test_item(1, &data.into());
2239
2240 assert_ok!(TemplateModule::set_meta_update_permission_flag(
2241 origin1.clone(),
2242 collection_id,
2243 MetaUpdatePermission::None,
2244 ));
2245
2246 let variable_data = b"test set_variable_meta_data method.".to_vec();
2247 assert_noop!(
2248 TemplateModule::set_variable_meta_data(
2249 origin1.clone(),
2250 collection_id,
2251 1,
2252 variable_data.clone()
2253 ),
2254 Error::<Test>::MetadataUpdateDenied
2255 );
2256 });
2257 }
20312258
2032#[test]2259 #[test]
2033fn collection_transfer_flag_works_neg() {2260 fn collection_transfer_flag_works_neg() {
2058 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);2285 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
2059 });2286 });
2060}2287 }
2288 });
2289}
20612290
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -185,6 +185,7 @@
 	pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
 	pub variable_on_chain_schema: Vec<u8>,        //
 	pub const_on_chain_schema: Vec<u8>,           //
+	pub meta_update_permission: MetaUpdatePermission,
 	pub transfers_enabled: bool,
 }
 
@@ -305,6 +306,20 @@
 	pub pieces: u128,
 }
 
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum MetaUpdatePermission {
+	ItemOwner,
+	Admin,
+	None,
+}
+
+impl Default for MetaUpdatePermission {
+	fn default() -> Self {
+		Self::ItemOwner
+	}
+}
+
 #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum CreateItemData {
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -63,6 +63,7 @@
       "Limits": "CollectionLimits",
       "VariableOnChainSchema": "Vec<u8>",
       "ConstOnChainSchema": "Vec<u8>",
+      "MetaUpdatePermission": "MetaUpdatePermission",
       "TransfersEnabled": "bool"
     },
     "RawData": "Vec<u8>",
@@ -94,6 +95,13 @@
         "Unique"
       ]
     },
+    "MetaUpdatePermission": {
+      "_enum": [
+        "ItemOwner",
+        "Admin",
+        "None"      
+      ]
+    },
     "CollectionId": "u32",
     "TokenId": "u32",
     "ChainLimits": {
modifiedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -4,7 +4,6 @@
 //
 
 import { ApiPromise } from '@polkadot/api';
-import BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import privateKey from './substrate/privateKey';
modifiedtests/src/collision-tests/adminLimitsOff.test.tsdiffbeforeafterboth
--- a/tests/src/collision-tests/adminLimitsOff.test.ts
+++ b/tests/src/collision-tests/adminLimitsOff.test.ts
@@ -1,5 +1,4 @@
 import { IKeyringPair } from '@polkadot/types/types';
-import BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import privateKey from '../substrate/privateKey';
addedtests/src/metadataUpdate.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/metadataUpdate.test.ts
@@ -0,0 +1,204 @@
+
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import {
+  createItemExpectSuccess,
+  createCollectionExpectSuccess,
+  enablePublicMintingExpectSuccess,
+  enableWhiteListExpectSuccess,
+  setMetadataUpdatePermissionFlagExpectSuccess,
+  setVariableMetaDataExpectSuccess,
+  setMintPermissionExpectSuccess,
+  addToWhiteListExpectSuccess,
+  addCollectionAdminExpectSuccess,
+  setVariableMetaDataExpectFailure,
+  setMetadataUpdatePermissionFlagExpectFailure,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+
+describe('Metadata update permissions with ItemOwner flag', () => {
+  it('ItemOwner can set variable metadata with ItemOwner permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+
+      const data = [1, 2, 254, 255];
+
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'ItemOwner');
+
+      await setVariableMetaDataExpectSuccess(Alice, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('Admin can\'n set variable metadata with ItemOwner permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'ItemOwner');
+
+      await setMintPermissionExpectSuccess(Alice, nftCollectionId, true);
+      await addToWhiteListExpectSuccess(Alice, nftCollectionId, Bob.address);
+      await addCollectionAdminExpectSuccess(Alice, nftCollectionId, Bob);
+  
+      await setVariableMetaDataExpectFailure(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('User can\'n set variable metadata with ItemOwner permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'ItemOwner');
+
+      await setMintPermissionExpectSuccess(Alice, nftCollectionId, true);  
+      await setVariableMetaDataExpectFailure(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+});
+
+describe('Metadata update permissions with Admin flag', () => {
+  it('Admin can set variable metadata with Admin permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'Admin');
+
+      await setMintPermissionExpectSuccess(Alice, nftCollectionId, true);
+      await addToWhiteListExpectSuccess(Alice, nftCollectionId, Bob.address);
+      await addCollectionAdminExpectSuccess(Alice, nftCollectionId, Bob);
+  
+      await setVariableMetaDataExpectSuccess(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('User can\'n can set variable metadata with Admin permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'Admin');
+
+      await setMintPermissionExpectSuccess(Alice, nftCollectionId, true);
+      await addToWhiteListExpectSuccess(Alice, nftCollectionId, Bob.address);
+      await addCollectionAdminExpectSuccess(Alice, nftCollectionId, Bob);
+  
+      await setVariableMetaDataExpectSuccess(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('ItemOwner can\'n can set variable metadata with Admin permission flag', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await enablePublicMintingExpectSuccess(Alice, nftCollectionId);
+      await addToWhiteListExpectSuccess(Alice, nftCollectionId, Bob.address);
+      await enableWhiteListExpectSuccess(Alice, nftCollectionId);
+      const newNftTokenId = await createItemExpectSuccess(Bob, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'Admin');
+  
+      await setVariableMetaDataExpectFailure(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+});
+
+describe('Metadata update permissions with None flag', () => {
+  it('Nobody can set variable metadata with None flag (Regular)', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'None');
+  
+      await setVariableMetaDataExpectFailure(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('Nobody can set variable metadata with None flag (Admin)', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'None');
+  
+      await setMintPermissionExpectSuccess(Alice, nftCollectionId, true);
+      await addToWhiteListExpectSuccess(Alice, nftCollectionId, Bob.address);
+      await addCollectionAdminExpectSuccess(Alice, nftCollectionId, Bob);
+  
+      await setVariableMetaDataExpectFailure(Bob, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('Nobody can set variable metadata with None flag (ItemOwner)', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+  
+      const data = [1, 2, 254, 255];
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'None');
+  
+      await setVariableMetaDataExpectFailure(Alice, nftCollectionId, newNftTokenId, data);
+    });
+  });
+
+  it('Nobody can set variable metadata flag after freeze', async () => {
+    await usingApi(async () => {
+      const Alice = privateKey('//Alice');
+  
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await setMetadataUpdatePermissionFlagExpectSuccess(Alice, nftCollectionId, 'None');
+      await setMetadataUpdatePermissionFlagExpectFailure(Alice, nftCollectionId, 'Admin');       
+    });
+  });
+});
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -525,6 +525,28 @@
   });
 }
 
+export async function setMetadataUpdatePermissionFlagExpectSuccess(sender: IKeyringPair, collectionId: number, flag: string) {
+
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setMetaUpdatePermissionFlag(collectionId, flag); 
+    const events = await submitTransactionAsync(sender, tx);
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.true;
+  }); 
+}
+
+export async function setMetadataUpdatePermissionFlagExpectFailure(sender: IKeyringPair, collectionId: number, flag: string) {
+
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.setMetaUpdatePermissionFlag(collectionId, flag); 
+    const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
+    const result = getGenericResult(events);
+
+    expect(result.success).to.be.false;
+  }); 
+}
+
 export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {
   await usingApi(async (api) => {
     const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);