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

difftreelog

Merge branch 'develop' into feature/NFTPAR-288

Greg Zaitsev2021-01-26parents: #513c1a0 #cd2216f.patch.diff
in: master

25 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -323,6 +323,8 @@
         CollectionNotFound,
         /// Item not exists.
         TokenNotFound,
+        /// Admin not found
+        AdminNotFound,
         /// Arithmetic calculation overflow.
         NumOverflow,       
         /// Account already has admin role.
@@ -426,7 +428,7 @@
         pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;
 
         /// Tokens transfer baskets
-        pub CreateItemBasket get(fn create_item_basket): map hasher(identity) CollectionId => T::BlockNumber;
+        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;
         pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
         pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;
         pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
@@ -812,13 +814,11 @@
 
             let sender = ensure_signed(origin)?;
             Self::check_owner_or_admin_permissions(collection_id, sender)?;
+            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);
 
-            if <AdminList<T>>::contains_key(collection_id)
-            {
-                let mut admin_arr = <AdminList<T>>::get(collection_id);
-                admin_arr.retain(|i| *i != account_id);
-                <AdminList<T>>::insert(collection_id, admin_arr);
-            }
+            let mut admin_arr = <AdminList<T>>::get(collection_id);
+            admin_arr.retain(|i| *i != account_id);
+            <AdminList<T>>::insert(collection_id, admin_arr);
 
             Ok(())
         }
@@ -1009,7 +1009,7 @@
             {
                 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,
                 CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,
-                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,
+                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, &sender)?,
                 _ => ()
             };
 
@@ -1092,6 +1092,9 @@
 
             let sender = ensure_signed(origin)?;
 
+            Self::collection_exists(collection_id)?;
+            Self::token_exists(collection_id, item_id, &sender)?;
+
             // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
             ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
@@ -1161,7 +1164,7 @@
             }
 
             // Reduce approval by transferred amount or remove if remaining approval drops to 0
-            if approval - value > 0 {
+            if approval.checked_sub(value).unwrap_or(0) > 0 {
                 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);
             }
             else {
@@ -1216,7 +1219,8 @@
             let sender = ensure_signed(origin)?;
             
             Self::collection_exists(collection_id)?;
-            
+            Self::token_exists(collection_id, item_id, &sender)?;
+
             ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);
 
             // Modify permissions check
@@ -1224,8 +1228,6 @@
             ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
                 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
                 Error::<T>::NoPermission);
-
-            Self::item_exists(collection_id, item_id, &target_collection.mode)?;
 
             match target_collection.mode
             {
@@ -1320,7 +1322,7 @@
             Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
 
             // check schema limit
-            ensure!(schema.len() as u32 > ChainLimit::get().const_on_chain_schema_limit, "");
+            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");
 
             let mut target_collection = <Collection<T>>::get(collection_id);
             target_collection.const_on_chain_schema = schema;
@@ -1351,7 +1353,7 @@
             Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
 
             // check schema limit
-            ensure!(schema.len() as u32 > ChainLimit::get().variable_on_chain_schema_limit, "");
+            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");
 
             let mut target_collection = <Collection<T>>::get(collection_id);
             target_collection.variable_on_chain_schema = schema;
@@ -1677,13 +1679,13 @@
         let value = item.owner.first().unwrap().fraction;
         let owner = item.owner.first().unwrap().owner.clone();
 
-        Self::add_token_index(collection_id, current_index, owner.clone())?;
+        Self::add_token_index(collection_id, current_index, &owner)?;
 
         <ItemListIndex>::insert(collection_id, current_index);
         <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);
 
         // Update balance
-        let new_balance = <Balance<T>>::get(collection_id, owner.clone())
+        let new_balance = <Balance<T>>::get(collection_id, &owner)
             .checked_add(value)
             .ok_or(Error::<T>::NumOverflow)?;
         <Balance<T>>::insert(collection_id, owner.clone(), new_balance);
@@ -1697,7 +1699,7 @@
             .ok_or(Error::<T>::NumOverflow)?;
 
         let item_owner = item.owner.clone();
-        Self::add_token_index(collection_id, current_index, item.owner.clone())?;
+        Self::add_token_index(collection_id, current_index, &item.owner)?;
 
         <ItemListIndex>::insert(collection_id, current_index);
         <NftItemList<T>>::insert(collection_id, current_index, item);
@@ -1714,29 +1716,44 @@
     fn burn_refungible_item(
         collection_id: CollectionId,
         item_id: TokenId,
-        owner: T::AccountId,
+        owner: &T::AccountId,
     ) -> DispatchResult {
         ensure!(
             <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
             Error::<T>::TokenNotFound
         );
-        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);
-        let item = collection
+        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);
+        let rft_balance = token
             .owner
             .iter()
-            .filter(|&i| i.owner == owner)
+            .filter(|&i| i.owner == *owner)
             .next()
             .unwrap();
-        Self::remove_token_index(collection_id, item_id, owner.clone())?;
+        Self::remove_token_index(collection_id, item_id, owner)?;
 
         // update balance
-        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
-            .checked_sub(item.fraction)
+        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())
+            .checked_sub(rft_balance.fraction)
             .ok_or(Error::<T>::NumOverflow)?;
-        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
+        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);
 
-        <ReFungibleItemList<T>>::remove(collection_id, item_id);
+        // Re-create owners list with sender removed
+        let index = token
+            .owner
+            .iter()
+            .position(|i| i.owner == *owner)
+            .unwrap();
+        token.owner.remove(index);
+        let owner_count = token.owner.len();
 
+        // Burn the token completely if this was the last (only) owner
+        if owner_count == 0 {
+            <ReFungibleItemList<T>>::remove(collection_id, item_id);
+        }
+        else {
+            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);
+        }
+
         Ok(())
     }
 
@@ -1746,10 +1763,10 @@
             Error::<T>::TokenNotFound
         );
         let item = <NftItemList<T>>::get(collection_id, item_id);
-        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;
+        Self::remove_token_index(collection_id, item_id, &item.owner)?;
 
         // update balance
-        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
+        let new_balance = <Balance<T>>::get(collection_id, &item.owner)
             .checked_sub(1)
             .ok_or(Error::<T>::NumOverflow)?;
         <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
@@ -1858,16 +1875,33 @@
         Ok(())
     }
 
+    /// Check if token exists. In case of Fungible, check if there is an entry for 
+    /// the owner in fungible balances double map
+    fn token_exists(
+        collection_id: CollectionId,
+        item_id: TokenId,
+        owner: &T::AccountId
+    ) -> DispatchResult {
+        let target_collection = <Collection<T>>::get(collection_id);
+        let exists = match target_collection.mode
+        {
+            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),
+            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),
+            CollectionMode::ReFungible(_)  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
+            _ => false
+        };
+
+        ensure!(exists == true, Error::<T>::TokenNotFound);
+        Ok(())
+    }
+
     fn transfer_fungible(
         collection_id: CollectionId,
         value: u128,
         owner: &T::AccountId,
         recipient: &T::AccountId,
     ) -> DispatchResult {
-        ensure!(
-            <FungibleItemList<T>>::contains_key(collection_id, owner),
-            Error::<T>::TokenNotFound
-        );
+        Self::token_exists(collection_id, 0, owner)?;
 
         let mut balance = <FungibleItemList<T>>::get(collection_id, owner);
         ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);
@@ -1897,10 +1931,7 @@
         owner: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
-        ensure!(
-            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
-            Error::<T>::TokenNotFound
-        );
+        Self::token_exists(collection_id, item_id, &owner)?;
 
         let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);
         let item = full_item
@@ -1941,7 +1972,7 @@
             <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
 
             // update index collection
-            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;
+            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;
         } else {
             let mut new_full_item = full_item.clone();
             new_full_item
@@ -1966,7 +1997,7 @@
                     owner: new_owner.clone(),
                     fraction: value,
                 });
-                Self::add_token_index(collection_id, item_id, new_owner.clone())?;
+                Self::add_token_index(collection_id, item_id, &new_owner)?;
             }
 
             <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
@@ -1981,10 +2012,7 @@
         sender: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
-        ensure!(
-            <NftItemList<T>>::contains_key(collection_id, item_id),
-            Error::<T>::TokenNotFound
-        );
+        Self::token_exists(collection_id, item_id, &sender)?;
 
         let mut item = <NftItemList<T>>::get(collection_id, item_id);
 
@@ -2010,25 +2038,11 @@
         <NftItemList<T>>::insert(collection_id, item_id, item);
 
         // update index collection
-        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;
+        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;
 
         Ok(())
     }
     
-    fn item_exists(
-        collection_id: CollectionId,
-        item_id: TokenId,
-        mode: &CollectionMode
-    ) -> DispatchResult {
-        match mode {
-            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
-            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
-            _ => ()
-        };
-        
-        Ok(())
-    }
-
     fn set_re_fungible_variable_data(
         collection_id: CollectionId,
         item_id: TokenId,
@@ -2090,12 +2104,12 @@
             .unwrap();
 
         let item_owner = item.owner.clone();
-        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();
+        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();
 
         <ItemListIndex>::insert(collection_id, current_index);
 
         // Update balance
-        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
+        let new_balance = <Balance<T>>::get(collection_id, &item_owner)
             .checked_add(1)
             .unwrap();
         <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
@@ -2106,7 +2120,7 @@
             .checked_add(1)
             .unwrap();
 
-        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();
+        Self::add_token_index(collection_id, current_index, owner).unwrap();
 
         <ItemListIndex>::insert(collection_id, current_index);
 
@@ -2125,24 +2139,24 @@
         let value = item.owner.first().unwrap().fraction;
         let owner = item.owner.first().unwrap().owner.clone();
 
-        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();
+        Self::add_token_index(collection_id, current_index, &owner).unwrap();
 
         <ItemListIndex>::insert(collection_id, current_index);
 
         // Update balance
-        let new_balance = <Balance<T>>::get(collection_id, owner.clone())
+        let new_balance = <Balance<T>>::get(collection_id, &owner)
             .checked_add(value)
             .unwrap();
         <Balance<T>>::insert(collection_id, owner.clone(), new_balance);
     }
 
-    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {
+    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {
 
         // add to account limit
-        if <AccountItemCount<T>>::contains_key(owner.clone()) {
+        if <AccountItemCount<T>>::contains_key(owner) {
 
             // bound Owned tokens by a single address
-            let count = <AccountItemCount<T>>::get(owner.clone());
+            let count = <AccountItemCount<T>>::get(owner);
             ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);
 
             <AccountItemCount<T>>::insert(owner.clone(), count
@@ -2153,9 +2167,9 @@
             <AccountItemCount<T>>::insert(owner.clone(), 1);
         }
 
-        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
+        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);
         if list_exists {
-            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
+            let mut list = <AddressTokens<T>>::get(collection_id, owner);
             let item_contains = list.contains(&item_index.clone());
 
             if !item_contains {
@@ -2166,8 +2180,7 @@
         } else {
             let mut itm = Vec::new();
             itm.push(item_index.clone());
-            <AddressTokens<T>>::insert(collection_id, owner, itm);
-            
+            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);
         }
 
         Ok(())
@@ -2176,24 +2189,24 @@
     fn remove_token_index(
         collection_id: CollectionId,
         item_index: TokenId,
-        owner: T::AccountId,
+        owner: &T::AccountId,
     ) -> DispatchResult {
 
         // update counter
         <AccountItemCount<T>>::insert(owner.clone(), 
-            <AccountItemCount<T>>::get(owner.clone())
+            <AccountItemCount<T>>::get(owner)
             .checked_sub(1)
             .ok_or(Error::<T>::NumOverflow)?);
 
 
-        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
+        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);
         if list_exists {
-            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
+            let mut list = <AddressTokens<T>>::get(collection_id, owner);
             let item_contains = list.contains(&item_index.clone());
 
             if item_contains {
                 list.retain(|&item| item != item_index);
-                <AddressTokens<T>>::insert(collection_id, owner, list);
+                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);
             }
         }
 
@@ -2203,8 +2216,8 @@
     fn move_token_index(
         collection_id: CollectionId,
         item_index: TokenId,
-        old_owner: T::AccountId,
-        new_owner: T::AccountId,
+        old_owner: &T::AccountId,
+        new_owner: &T::AccountId,
     ) -> DispatchResult {
         Self::remove_token_index(collection_id, item_index, old_owner)?;
         Self::add_token_index(collection_id, item_index, new_owner)?;
@@ -2319,15 +2332,15 @@
 
                 let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;
                 let mut sponsored = true;
-                if <CreateItemBasket<T>>::contains_key(collection_id) {
-                    let last_tx_block = <CreateItemBasket<T>>::get(collection_id);
+                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {
+                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));
                     let limit_time = last_tx_block + limit.into();
                     if block_number <= limit_time {
                         sponsored = false;
                     }
                 }
                 if sponsored {
-                    <CreateItemBasket<T>>::insert(collection_id, block_number);
+                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);
                 }
 
                 // check free create limit
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
1// Tests to be written here1// Tests to be written here
2use super::*;2use super::*;
3use crate::mock::*;3use crate::mock::*;
4use crate::{AccessMode, ApprovePermissions, CollectionMode,4use crate::{AccessMode, CollectionMode,
5 Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,5 Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,
6 CollectionId, TokenId, MAX_DECIMAL_POINTS};6 CollectionId, TokenId, MAX_DECIMAL_POINTS};
7use frame_support::{assert_noop, assert_ok};7use frame_support::{assert_noop, assert_ok};
20 nft_sponsor_transfer_timeout: 15,20 nft_sponsor_transfer_timeout: 15,
21 fungible_sponsor_transfer_timeout: 15,21 fungible_sponsor_transfer_timeout: 15,
22 refungible_sponsor_transfer_timeout: 15, 22 refungible_sponsor_transfer_timeout: 15,
23 const_on_chain_schema_limit: 1024,
24 offchain_schema_limit: 1024,
25 variable_on_chain_schema_limit: 1024,
23 }));26 }));
24}27}
2528
28}31}
2932
30fn default_fungible_data () -> CreateFungibleData {33fn default_fungible_data () -> CreateFungibleData {
31 CreateFungibleData { }34 CreateFungibleData { value: 5 }
32}35}
3336
34fn default_re_fungible_data () -> CreateReFungibleData {37fn default_re_fungible_data () -> CreateReFungibleData {
238 let data = default_fungible_data();241 let data = default_fungible_data();
239 create_test_item(collection_id, &data.into());242 create_test_item(collection_id, &data.into());
240243
241 assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1);244 assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).value, 5);
242 });245 });
243}246}
244247
245#[test]248//#[test]
246fn create_multiple_fungible_items() {249// fn create_multiple_fungible_items() {
247 new_test_ext().execute_with(|| {250// new_test_ext().execute_with(|| {
248 default_limits();251// default_limits();
249252
250 create_test_collection(&CollectionMode::Fungible(3), 1);253// create_test_collection(&CollectionMode::Fungible(3), 1);
251254
252 let origin1 = Origin::signed(1);255// let origin1 = Origin::signed(1);
253256
254 let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];257// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
255258
256 assert_ok!(TemplateModule::create_multiple_items(259// assert_ok!(TemplateModule::create_multiple_items(
257 origin1.clone(),260// origin1.clone(),
258 1,261// 1,
259 1,262// 1,
260 items_data.clone().into_iter().map(|d| { d.into() }).collect()263// items_data.clone().into_iter().map(|d| { d.into() }).collect()
261 ));264// ));
262 265
263 for (index, _) in items_data.iter().enumerate() {266// for (index, _) in items_data.iter().enumerate() {
264 assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).owner, 1);267// assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);
265 }268// }
266 assert_eq!(TemplateModule::balance_count(1, 1), 3000);269// assert_eq!(TemplateModule::balance_count(1, 1), 3000);
267 assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);270// assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
268 });271// });
269}272// }
270273
271#[test]274#[test]
272fn transfer_fungible_item() {275fn transfer_fungible_item() {
281 let data = default_fungible_data();284 let data = default_fungible_data();
282 create_test_item(collection_id, &data.into());285 create_test_item(collection_id, &data.into());
283286
284 assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);287 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 5);
285 assert_eq!(TemplateModule::balance_count(1, 1), 1000);288 assert_eq!(TemplateModule::balance_count(1, 1), 5);
286 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
287289
288 // change owner scenario290 // change owner scenario
289 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));291 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 5));
290 assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
291 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 1000);292 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 0);
292 assert_eq!(TemplateModule::balance_count(1, 1), 0);293 assert_eq!(TemplateModule::balance_count(1, 1), 0);
293 assert_eq!(TemplateModule::balance_count(1, 2), 1000);294 assert_eq!(TemplateModule::balance_count(1, 2), 5);
294 // assert_eq!(TemplateModule::address_tokens(1, 1), []);
295 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
296295
297 // split item scenario296 // split item scenario
298 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 500));297 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 3));
299 assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
300 assert_eq!(TemplateModule::fungible_item_id(1, 2).owner, 3);
301 assert_eq!(TemplateModule::balance_count(1, 2), 500);298 assert_eq!(TemplateModule::balance_count(1, 2), 2);
302 assert_eq!(TemplateModule::balance_count(1, 3), 500);299 assert_eq!(TemplateModule::balance_count(1, 3), 3);
303 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
304 assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
305300
306 // split item and new owner has account scenario301 // split item and new owner has account scenario
307 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 200));302 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 1));
308 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 300);303 assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 1);
309 assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 700);304 assert_eq!(TemplateModule::fungible_item_id(1, 3).value, 4);
310 assert_eq!(TemplateModule::balance_count(1, 2), 300);305 assert_eq!(TemplateModule::balance_count(1, 2), 1);
311 assert_eq!(TemplateModule::balance_count(1, 3), 700);306 assert_eq!(TemplateModule::balance_count(1, 3), 4);
312 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
313 assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
314 });307 });
315}308}
316309
451 1), Error::<Test>::NoPermission);444 1), Error::<Test>::NoPermission);
452445
453 // do approve446 // do approve
454 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));447 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
455 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);448 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
456 assert_eq!(449 assert_eq!(
457 TemplateModule::approved(1, (1, 1))[0],450 TemplateModule::approved(1, (1, 1, 2)),
458 ApprovePermissions {451 5
459 approved: 2,
460 amount: 100000000
461 }
462 );452 );
463453
464 assert_ok!(TemplateModule::transfer_from(454 assert_ok!(TemplateModule::transfer_from(
469 1,459 1,
470 1460 1
471 ));461 ));
472 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);462 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4);
473 });463 });
474}464}
475465
505 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));495 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
506496
507 // do approve497 // do approve
508 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));498 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
509 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);499 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
510 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));500 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
511 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);501 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
512 assert_eq!(
513 TemplateModule::approved(1, (1, 1))[0],
514 ApprovePermissions {
515 approved: 2,
516 amount: 100000000
517 }
518 );
519502
520 assert_ok!(TemplateModule::transfer_from(503 assert_ok!(TemplateModule::transfer_from(
521 origin2.clone(),504 origin2.clone(),
525 1,508 1,
526 1509 1
527 ));510 ));
528 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);511 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4);
529 });512 });
530}513}
531514
560 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));543 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
561544
562 // do approve545 // do approve
563 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));546 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
564 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);547 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
565 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));548 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000));
566 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);549 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000);
567 assert_eq!(
568 TemplateModule::approved(1, (1, 1))[0],
569 ApprovePermissions {
570 approved: 2,
571 amount: 100000000
572 }
573 );
574550
575 assert_ok!(TemplateModule::transfer_from(551 assert_ok!(TemplateModule::transfer_from(
576 origin2.clone(),552 origin2.clone(),
585 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);561 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
586 assert_eq!(TemplateModule::address_tokens(1, 3), [1]);562 assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
587563
588 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);564 assert_eq!(
589 assert_eq!(565 TemplateModule::approved(1, (1, 1, 3)),
590 TemplateModule::approved(1, (1, 1))[0],566 900
591 ApprovePermissions {567 );
592 approved: 3,
593 amount: 100000000
594 }
595 );
596 });568 });
597}569}
598570
609 let origin1 = Origin::signed(1);581 let origin1 = Origin::signed(1);
610 let origin2 = Origin::signed(2);582 let origin2 = Origin::signed(2);
611583
612 assert_eq!(TemplateModule::balance_count(1, 1), 1000);584 assert_eq!(TemplateModule::balance_count(1, 1), 5);
613 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
614585
615 assert_ok!(TemplateModule::set_mint_permission(586 assert_ok!(TemplateModule::set_mint_permission(
616 origin1.clone(),587 origin1.clone(),
627 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));598 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
628599
629 // do approve600 // do approve
630 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));601 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
631 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);602 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
632 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));603 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
633 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);604 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
634 assert_eq!(605 assert_eq!(
635 TemplateModule::approved(1, (1, 1))[0],606 TemplateModule::approved(1, (1, 1, 2)),
636 ApprovePermissions {607 5
637 approved: 2,
638 amount: 100000000
639 }
640 );608 );
641609
642 assert_ok!(TemplateModule::transfer_from(610 assert_ok!(TemplateModule::transfer_from(
645 3,613 3,
646 1,614 1,
647 1,615 1,
648 100616 4
649 ));617 ));
650 assert_eq!(TemplateModule::balance_count(1, 1), 900);618 assert_eq!(TemplateModule::balance_count(1, 1), 1);
651 assert_eq!(TemplateModule::balance_count(1, 3), 100);619 assert_eq!(TemplateModule::balance_count(1, 3), 4);
652 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
653 assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
654620
655 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);621 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
656 assert_eq!(622 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1);
657 TemplateModule::approved(1, (1, 1))[0],
658 ApprovePermissions {
659 approved: 3,
660 amount: 100000000
661 }
662 );
663623
664 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));624 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
665 assert_ok!(TemplateModule::transfer_from(625 assert_noop!(TemplateModule::transfer_from(
666 origin2.clone(),626 origin2.clone(),
667 1,627 1,
668 3,628 3,
669 1,629 1,
670 1,630 1,
671 900631 4
672 ));632 ), Error::<Test>::TokenValueNotEnough);
673 assert_eq!(TemplateModule::balance_count(1, 1), 0);
674 assert_eq!(TemplateModule::balance_count(1, 3), 1000);
675 // assert_eq!(TemplateModule::address_tokens(1, 1), []);
676 assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
677
678 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
679 });633 });
680}634}
681635
725 assert_eq!(TemplateModule::balance_count(1, 1), 1);679 assert_eq!(TemplateModule::balance_count(1, 1), 1);
726680
727 // burn item681 // burn item
728 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));682 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
729 assert_noop!(683 assert_noop!(
730 TemplateModule::burn_item(origin1.clone(), 1, 1),684 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
731 Error::<Test>::TokenNotFound685 Error::<Test>::TokenNotFound
732 );686 );
733687
749 create_test_item(collection_id, &data.into());703 create_test_item(collection_id, &data.into());
750704
751 // check balance (collection with id = 1, user id = 1)705 // check balance (collection with id = 1, user id = 1)
752 assert_eq!(TemplateModule::balance_count(1, 1), 1000);706 assert_eq!(TemplateModule::balance_count(1, 1), 5);
753707
754 // burn item708 // burn item
755 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));709 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
756 assert_noop!(710 assert_noop!(
757 TemplateModule::burn_item(origin1.clone(), 1, 1),711 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
758 Error::<Test>::TokenNotFound712 Error::<Test>::TokenNotFound
759 );713 );
760714
791 assert_eq!(TemplateModule::balance_count(1, 1), 1000);745 assert_eq!(TemplateModule::balance_count(1, 1), 1000);
792746
793 // burn item747 // burn item
794 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));748 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000));
795 assert_noop!(749 assert_noop!(
796 TemplateModule::burn_item(origin1.clone(), 1, 1),750 TemplateModule::burn_item(origin1.clone(), 1, 1, 1000),
797 Error::<Test>::TokenNotFound751 Error::<Test>::TokenNotFound
798 );752 );
799753
875829
876 // check balance (collection with id = 1, user id = 1)830 // check balance (collection with id = 1, user id = 1)
877 assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);831 assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);
878 assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 1000);832 assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5);
879 assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);833 assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);
880 assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);834 assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);
881 assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).owner, 1);835 assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5);
882 assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);836 assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);
883 });837 });
884}838}
896 let origin1 = Origin::signed(1);850 let origin1 = Origin::signed(1);
897 851
898 // approve852 // approve
899 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));853 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
900 assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);854 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
901 });855 });
902}856}
903857
914 create_test_item(collection_id, &data.into());868 create_test_item(collection_id, &data.into());
915869
916 // approve870 // approve
917 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));871 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
918 assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);872 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
919873
920 assert_ok!(TemplateModule::set_mint_permission(874 assert_ok!(TemplateModule::set_mint_permission(
921 origin1.clone(),875 origin1.clone(),
1199 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));1153 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
12001154
1201 // do approve1155 // do approve
1202 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));1156 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
1203 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);1157 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
12041158
1205 assert_ok!(TemplateModule::remove_from_white_list(1159 assert_ok!(TemplateModule::remove_from_white_list(
1206 origin1.clone(),1160 origin1.clone(),
1263 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1217 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
12641218
1265 // do approve1219 // do approve
1266 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));1220 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
1267 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);1221 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
12681222
1269 assert_ok!(TemplateModule::remove_from_white_list(1223 assert_ok!(TemplateModule::remove_from_white_list(
1270 origin1.clone(),1224 origin1.clone(),
1298 AccessMode::WhiteList1252 AccessMode::WhiteList
1299 ));1253 ));
1300 assert_noop!(1254 assert_noop!(
1301 TemplateModule::burn_item(origin1.clone(), 1, 1),1255 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
1302 Error::<Test>::AddresNotInWhiteList1256 Error::<Test>::AddresNotInWhiteList
1303 );1257 );
1304 });1258 });
1305}1259}
13061260
1307// If Public Access mode is set to WhiteList, oken transfers can’t be Approved by a non-whitelisted address (see Approve method).1261// If Public Access mode is set to WhiteList, token transfers can’t be Approved by a non-whitelisted address (see Approve method).
1308#[test]1262#[test]
1309fn white_list_test_6() {1263fn white_list_test_6() {
1310 new_test_ext().execute_with(|| {1264 new_test_ext().execute_with(|| {
13141268
1315 let origin1 = Origin::signed(1);1269 let origin1 = Origin::signed(1);
1270
1271 let data = default_nft_data();
1272 create_test_item(collection_id, &data.into());
1273
1316 assert_ok!(TemplateModule::set_public_access_mode(1274 assert_ok!(TemplateModule::set_public_access_mode(
1317 origin1.clone(),1275 origin1.clone(),
13211279
1322 // do approve1280 // do approve
1323 assert_noop!(1281 assert_noop!(
1324 TemplateModule::approve(origin1.clone(), 1, 1, 1),1282 TemplateModule::approve(origin1.clone(), 1, 1, 1, 5),
1325 Error::<Test>::AddresNotInWhiteList1283 Error::<Test>::AddresNotInWhiteList
1326 );1284 );
1327 });1285 });
1374 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1332 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
13751333
1376 // do approve1334 // do approve
1377 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));1335 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 5));
1378 assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);1336 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 5);
13791337
1380 assert_ok!(TemplateModule::transfer_from(1338 assert_ok!(TemplateModule::transfer_from(
1381 origin1.clone(),1339 origin1.clone(),
1688 nft_sponsor_transfer_timeout: 15,1646 nft_sponsor_transfer_timeout: 15,
1689 fungible_sponsor_transfer_timeout: 15,1647 fungible_sponsor_transfer_timeout: 15,
1690 refungible_sponsor_transfer_timeout: 15, 1648 refungible_sponsor_transfer_timeout: 15,
1649 const_on_chain_schema_limit: 1024,
1650 offchain_schema_limit: 1024,
1651 variable_on_chain_schema_limit: 1024,
1691 }));1652 }));
1692 1653
1693 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1654 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1717 nft_sponsor_transfer_timeout: 15,1678 nft_sponsor_transfer_timeout: 15,
1718 fungible_sponsor_transfer_timeout: 15,1679 fungible_sponsor_transfer_timeout: 15,
1719 refungible_sponsor_transfer_timeout: 15, 1680 refungible_sponsor_transfer_timeout: 15,
1681 const_on_chain_schema_limit: 1024,
1682 offchain_schema_limit: 1024,
1683 variable_on_chain_schema_limit: 1024,
1720 }));1684 }));
1721 1685
1722 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1686 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1740 nft_sponsor_transfer_timeout: 15,1704 nft_sponsor_transfer_timeout: 15,
1741 fungible_sponsor_transfer_timeout: 15,1705 fungible_sponsor_transfer_timeout: 15,
1742 refungible_sponsor_transfer_timeout: 15, 1706 refungible_sponsor_transfer_timeout: 15,
1707 const_on_chain_schema_limit: 1024,
1708 offchain_schema_limit: 1024,
1709 variable_on_chain_schema_limit: 1024,
1743 }));1710 }));
1744 1711
1745 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1712 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1763 nft_sponsor_transfer_timeout: 15,1730 nft_sponsor_transfer_timeout: 15,
1764 fungible_sponsor_transfer_timeout: 15,1731 fungible_sponsor_transfer_timeout: 15,
1765 refungible_sponsor_transfer_timeout: 15, 1732 refungible_sponsor_transfer_timeout: 15,
1733 const_on_chain_schema_limit: 1024,
1734 offchain_schema_limit: 1024,
1735 variable_on_chain_schema_limit: 1024,
1766 }));1736 }));
1767 1737
1768 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1738 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1794 nft_sponsor_transfer_timeout: 15,1764 nft_sponsor_transfer_timeout: 15,
1795 fungible_sponsor_transfer_timeout: 15,1765 fungible_sponsor_transfer_timeout: 15,
1796 refungible_sponsor_transfer_timeout: 15, 1766 refungible_sponsor_transfer_timeout: 15,
1767 const_on_chain_schema_limit: 1024,
1768 offchain_schema_limit: 1024,
1769 variable_on_chain_schema_limit: 1024,
1797 }));1770 }));
17981771
1799 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1772 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1825 nft_sponsor_transfer_timeout: 15,1798 nft_sponsor_transfer_timeout: 15,
1826 fungible_sponsor_transfer_timeout: 15,1799 fungible_sponsor_transfer_timeout: 15,
1827 refungible_sponsor_transfer_timeout: 15, 1800 refungible_sponsor_transfer_timeout: 15,
1801 const_on_chain_schema_limit: 1024,
1802 offchain_schema_limit: 1024,
1803 variable_on_chain_schema_limit: 1024,
1828 }));1804 }));
18291805
1830 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1806 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1856 nft_sponsor_transfer_timeout: 15,1832 nft_sponsor_transfer_timeout: 15,
1857 fungible_sponsor_transfer_timeout: 15,1833 fungible_sponsor_transfer_timeout: 15,
1858 refungible_sponsor_transfer_timeout: 15, 1834 refungible_sponsor_transfer_timeout: 15,
1835 const_on_chain_schema_limit: 1024,
1836 offchain_schema_limit: 1024,
1837 variable_on_chain_schema_limit: 1024,
1859 }));1838 }));
18601839
1861 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1840 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1973 nft_sponsor_transfer_timeout: 15,1952 nft_sponsor_transfer_timeout: 15,
1974 fungible_sponsor_transfer_timeout: 15,1953 fungible_sponsor_transfer_timeout: 15,
1975 refungible_sponsor_transfer_timeout: 15, 1954 refungible_sponsor_transfer_timeout: 15,
1955 const_on_chain_schema_limit: 1024,
1956 offchain_schema_limit: 1024,
1957 variable_on_chain_schema_limit: 1024,
1976 }));1958 }));
19771959
1978 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1960 let collection_id = create_test_collection(&CollectionMode::NFT, 1);
1998 nft_sponsor_transfer_timeout: 15,1980 nft_sponsor_transfer_timeout: 15,
1999 fungible_sponsor_transfer_timeout: 15,1981 fungible_sponsor_transfer_timeout: 15,
2000 refungible_sponsor_transfer_timeout: 15, 1982 refungible_sponsor_transfer_timeout: 15,
1983 const_on_chain_schema_limit: 1024,
1984 offchain_schema_limit: 1024,
1985 variable_on_chain_schema_limit: 1024,
2001 }));1986 }));
20021987
20031988
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -18,10 +18,19 @@
   "scripts": {
     "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts",
     "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts",
+    "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
     "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
     "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
+    "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
     "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
-    "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts"
+    "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+    "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
+    "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
+    "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+    "testToggleContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractWhiteList.test.ts",
+    "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts",
+    "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",
+    "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts"
   },
   "author": "",
   "license": "Apache 2.0",
addedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -0,0 +1,130 @@
+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';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+  it('Add collection admin.', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(alice.address);
+
+      const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, bob.address);
+      await submitTransactionAsync(alice, changeAdminTx);
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddAdmin).to.be.contains(bob.address);
+    });
+  });
+
+  it('Add admin using added collection admin.', async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+
+      const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+      await submitTransactionAsync(Alice, changeAdminTx);
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddAdmin).to.be.contains(Bob.address);
+
+      const changeAdminTxCharlie = api.tx.nft.addCollectionAdmin(collectionId, Charlie.address);
+      await submitTransactionAsync(Bob, changeAdminTxCharlie);
+      const adminListAfterAddNewAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddNewAdmin).to.be.contains(Bob.address);
+      expect(adminListAfterAddNewAdmin).to.be.contains(Charlie.address);
+    });
+  });
+});
+
+describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+  it("Not owner can't add collection admin.", async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const alice = privateKey('//Alice');
+      const nonOwner = privateKey('//Bob_stash');
+
+      const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, alice.address);
+      await expect(submitTransactionExpectFailAsync(nonOwner, changeAdminTx)).to.be.rejected;
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddAdmin).not.to.be.contains(alice.address);
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+  it("Can't add collection admin of not existing collection.", async () => {
+    await usingApi(async (api) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = (1 << 32) - 1;
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+
+      const changeOwnerTx = api.tx.nft.addCollectionAdmin(collectionId, bob.address);
+      await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it("Can't add an admin to a destroyed collection.", async () => {
+    await usingApi(async (api) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      await destroyCollectionExpectSuccess(collectionId);
+      const changeOwnerTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+      await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it('Add an admin to a collection that has reached the maximum number of admins limit', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const accounts = [
+        'GsvVmjr1CBHwQHw84pPHMDxgNY3iBLz6Qn7qS3CH8qPhrHz',
+        'FoQJpPyadYccjavVdTWxpxU7rUEaYhfLCPwXgkfD6Zat9QP',
+        'JKspFU6ohf1Grg3Phdzj2pSgWvsYWzSfKghhfzMbdhNBWs5',
+        'Fr4NzY1udSFFLzb2R3qxVQkwz9cZraWkyfH4h3mVVk7BK7P',
+        'DfnTB4z7eUvYRqcGtTpFsLC69o6tvBSC1pEv8vWPZFtCkaK',
+        'HnMAUz7r2G8G3hB27SYNyit5aJmh2a5P4eMdDtACtMFDbam',
+        'DE14BzQ1bDXWPKeLoAqdLAm1GpyAWaWF1knF74cEZeomTBM',
+      ];
+      const collectionId = await createCollectionExpectSuccess();
+
+      const chainLimit = await api.query.nft.chainLimit() as unknown as { collections_admins_limit: BN };
+      const chainLimitNumber = chainLimit.collections_admins_limit.toNumber();
+      expect(chainLimitNumber).to.be.equal(5);
+
+      for (let i = 0; i < chainLimitNumber; i++) {
+        const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, accounts[i]);
+        await submitTransactionAsync(Alice, changeAdminTx);
+        const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+        expect(adminListAfterAddAdmin).to.be.contains(accounts[i]);
+      }
+
+      const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainLimitNumber]);
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+});
addedtests/src/addToContractWhiteList.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/addToContractWhiteList.test.ts
@@ -0,0 +1,91 @@
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
+import privateKey from "./substrate/privateKey";
+import {
+  deployFlipper,
+  getFlipValue
+} from "./util/contracthelpers";
+import {
+  getGenericResult
+} from "./util/helpers"
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+
+describe('Integration Test addToContractWhiteList', () => {
+
+  it(`Add an address to a contract white list`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
+      const [contract, deployer] = await deployFlipper(api);
+
+      const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+      const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+      const addEvents = await submitTransactionAsync(deployer, addTx);
+      const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+      expect(getGenericResult(addEvents).success).to.be.true;
+      expect(whiteListedBefore).to.be.false;
+      expect(whiteListedAfter).to.be.true;
+    });
+  });
+
+  it(`Adding same address to white list repeatedly should not produce errors`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
+      const [contract, deployer] = await deployFlipper(api);
+
+      const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+      const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+      const addEvents = await submitTransactionAsync(deployer, addTx);
+      const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+      const addAgainEvents = await submitTransactionAsync(deployer, addTx);
+      const whiteListedAgainAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+      expect(getGenericResult(addEvents).success).to.be.true;
+      expect(whiteListedBefore).to.be.false;
+      expect(whiteListedAfter).to.be.true;
+      expect(getGenericResult(addAgainEvents).success).to.be.true;
+      expect(whiteListedAgainAfter).to.be.true;
+    });
+  });
+});
+
+describe('Negative Integration Test addToContractWhiteList', () => {
+
+  it(`Add an address to a white list of a non-contract`, async () => {
+    await usingApi(async api => {
+      const alice = privateKey("//Bob");
+      const bob = privateKey("//Bob");
+      const charlieGuineaPig = privateKey("//Charlie");
+
+      const whiteListedBefore = (await api.query.nft.contractWhiteList(charlieGuineaPig.address, bob.address)).toJSON();
+      const addTx = api.tx.nft.addToContractWhiteList(charlieGuineaPig.address, bob.address);
+      await expect(submitTransactionExpectFailAsync(alice, addTx)).to.be.rejected;
+      const whiteListedAfter = (await api.query.nft.contractWhiteList(charlieGuineaPig.address, bob.address)).toJSON();
+
+      expect(whiteListedBefore).to.be.false;
+      expect(whiteListedAfter).to.be.false;
+    });
+  });
+
+  it(`Add to a contract white list using a non-owner address`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
+      const [contract, deployer] = await deployFlipper(api);
+
+      const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+      const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+      await expect(submitTransactionExpectFailAsync(bob, addTx)).to.be.rejected;
+      const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+      expect(whiteListedBefore).to.be.false;
+      expect(whiteListedAfter).to.be.false;
+    });
+  });
+
+});
addedtests/src/addToWhiteList.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/addToWhiteList.test.ts
@@ -0,0 +1,81 @@
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi, { submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+  addToWhiteListExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  enablePublicMintingExpectSuccess,
+  enableWhiteListExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+
+describe('Integration Test ext. addToWhiteList()', () => {
+
+  before(async () => {
+    await usingApi(async (api) => {
+      Alice = privateKey('//Alice');
+      Bob = privateKey('//Bob');
+    });
+  });
+
+  it('Execute the extrinsic with parameters: Collection ID and address to add to the white list', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+  });
+
+  it('Whitelisted minting: list restrictions', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+    await enableWhiteListExpectSuccess(Alice, collectionId);
+    await enablePublicMintingExpectSuccess(Alice, collectionId);
+    await createItemExpectSuccess(Bob, collectionId, 'NFT', Bob.address);
+  });
+});
+
+describe('Negative Integration Test ext. addToWhiteList()', () => {
+
+  it('White list an address in the collection that does not exist', async () => {
+    await usingApi(async (api) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const Bob = privateKey('//Bob');
+
+      const tx = api.tx.nft.addToWhiteList(collectionId, Bob.address);
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+
+  it('White list an address in the collection that was destroyed', async () => {
+    await usingApi(async (api) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(collectionId);
+      const tx = api.tx.nft.addToWhiteList(collectionId, Bob.address);
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+
+  it('White list an address in the collection that does not have white list access enabled', async () => {
+    await usingApi(async (api) => {
+      const Alice = privateKey('//Alice');
+      const Ferdie = privateKey('//Ferdie');
+      const collectionId = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(Alice, collectionId);
+      await enablePublicMintingExpectSuccess(Alice, collectionId);
+      const tx = api.tx.nft.createItem(collectionId, Ferdie.address, 'NFT');
+      await expect(submitTransactionExpectFailAsync(Ferdie, tx)).to.be.rejected;
+    });
+  });
+
+});
addedtests/src/approve.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/approve.test.ts
@@ -0,0 +1,140 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+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';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+  approveExpectFail,
+  approveExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
+  it('Execute the extrinsic and check approvedList', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+    });
+  });
+
+  it('Remove approval by using 0 amount', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
+    });
+  });
+});
+
+describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
+  it('Approve for a collection that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+      // fungible
+      const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+    });
+  });
+
+  it('Approve for a collection that was destroyed', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(nftCollectionId);
+      await approveExpectFail(nftCollectionId, 1, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      await destroyCollectionExpectSuccess(fungibleCollectionId);
+      await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      await destroyCollectionExpectSuccess(reFungibleCollectionId);
+      await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
+    });
+  });
+
+  it('Approve transfer of a token that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await approveExpectFail(nftCollectionId, 2, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
+    });
+  });
+
+  it('Approve using the address that does not own the approved token', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
+    });
+  });
+});
addedtests/src/burnItem.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/burnItem.test.ts
@@ -0,0 +1,237 @@
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import { 
+  createCollectionExpectSuccess, 
+  createItemExpectSuccess,
+  getGenericResult,
+  destroyCollectionExpectSuccess
+} from './util/helpers';
+import { nullPublicKey } from "./accounts";
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+
+describe('integration test: ext. burnItem():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('Burn item in NFT collection', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+      // Get the item
+      const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();
+      // What to expect
+      // tslint:disable-next-line:no-unused-expression
+      expect(result.success).to.be.true;
+      // tslint:disable-next-line:no-unused-expression
+      expect(item).to.be.not.null;
+      expect(item.Owner).to.be.equal(nullPublicKey);
+    });
+
+  });
+  it('Burn item in Fungible collection', async () => {
+    const createMode = 'Fungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+    await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens
+    const tokenId = 0; // ignored
+
+    await usingApi(async (api) => {
+      // Destroy 1 of 10
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+  
+      // Get alice balance 
+      const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+ 
+      // What to expect
+      expect(result.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Value).to.be.equal(9);
+    });
+
+  });
+  it('Burn item in ReFungible collection', async () => {
+    const createMode = 'ReFungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+  
+      // Get alice balance 
+      const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+      
+      // What to expect
+      expect(result.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Owner.length).to.be.equal(0);
+    });
+
+  });
+
+  it('Burn owned portion of item in ReFungible collection', async () => {
+    const createMode = 'ReFungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      // Transfer 1/100 of the token to Bob
+      const transfertx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 1);
+      const events1 = await submitTransactionAsync(alice, transfertx);
+      const result1 = getGenericResult(events1);
+
+      // Get balances
+      const balanceBefore: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+
+      // Bob burns his portion
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events2 = await submitTransactionAsync(bob, tx);
+      const result2 = getGenericResult(events2);
+  
+      // Get balances 
+      const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+      // console.log(balance);
+      
+      // What to expect before burning
+      expect(result1.success).to.be.true;
+      expect(balanceBefore).to.be.not.null;
+      expect(balanceBefore.Owner.length).to.be.equal(2);
+      expect(balanceBefore.Owner[0].Owner).to.be.equal(alice.address);
+      expect(balanceBefore.Owner[0].Fraction).to.be.equal(99);
+      expect(balanceBefore.Owner[1].Owner).to.be.equal(bob.address);
+      expect(balanceBefore.Owner[1].Fraction).to.be.equal(1);
+
+      // What to expect after burning
+      expect(result2.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Owner.length).to.be.equal(1);
+      expect(balance.Owner[0].Fraction).to.be.equal(99);
+      expect(balance.Owner[0].Owner).to.be.equal(alice.address);
+    });
+
+  });
+
+});
+
+describe('Negative integration test: ext. burnItem():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('Burn a token in a destroyed collection', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+    await destroyCollectionExpectSuccess(collectionId);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn a token that was never created', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = 10;
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn a token using the address that does not own it', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(bob, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Transfer a burned a token', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+
+      const burntx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events1 = await submitTransactionAsync(alice, burntx);
+      const result1 = getGenericResult(events1);
+      expect(result1.success).to.be.true;
+  
+      const tx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn more than owned in Fungible collection', async () => {
+    const createMode = 'Fungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+    // Helper creates 10 fungible tokens
+    await createItemExpectSuccess(alice, collectionId, createMode);
+    const tokenId = 0; // ignored
+
+    await usingApi(async (api) => {
+      // Destroy 11 of 10
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 11);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+      
+      // Get alice balance 
+      const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+ 
+      // What to expect
+      expect(balance).to.be.not.null;
+      expect(balance.Value).to.be.equal(10);
+    });
+
+  });
+
+});
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -89,7 +89,7 @@
   });
 
   it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -115,7 +115,7 @@
   });
 
   it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -170,7 +170,7 @@
     });
   });
 
-  it('NFT: Sponsoring is rate limited', async () => {
+  it('NFT: Sponsoring of transfers is rate limited', async () => {
     const collectionId = await createCollectionExpectSuccess();
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -210,7 +210,7 @@
   });
 
   it('Fungible: Sponsoring is rate limited', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -247,7 +247,7 @@
   });
 
   it('ReFungible: Sponsoring is rate limited', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -285,6 +285,51 @@
     });
   });
 
+  it('NFT: Sponsoring of createItem is rate limited', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // Enable collection white list 
+    await enableWhiteListExpectSuccess(alice, collectionId);
+
+    // Enable public minting
+    await enablePublicMintingExpectSuccess(alice, collectionId);
+
+    await usingApi(async (api) => {
+      // Find unused address
+      const zeroBalance = await findUnusedAddress(api);
+
+      // Add zeroBalance address to white list
+      await addToWhiteListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+      // Mint token using unused address as signer - gets sponsored
+      await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+
+      // Second mint should fail
+      const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+      
+      const consoleError = console.error;
+      const consoleLog = console.log;
+      console.error = () => {};
+      console.log = () => {};
+      const badTransaction = async function () { 
+        await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+      };
+      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+      console.error = consoleError;
+      console.log = consoleLog;
+      const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+      // Try again after Zero gets some balance - now it should succeed
+      const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
+      await submitTransactionAsync(alice, balancetx);
+      await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+
+      expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+    });
+  });
+
 });
 
 describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -2,95 +2,20 @@
 import chaiAsPromised from 'chai-as-promised';
 import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
 import fs from "fs";
-import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
-import { IKeyringPair } from "@polkadot/types/types";
-import { ApiPromise, Keyring } from "@polkadot/api";
-import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import { Abi, ContractPromise as Contract } from "@polkadot/api-contract";
 import privateKey from "./substrate/privateKey";
+import {
+  deployFlipper,
+  getFlipValue
+} from "./util/contracthelpers";
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
-import { BigNumber } from 'bignumber.js';
-import { findUnusedAddress } from './util/helpers';
 
 const value = 0;
 const gasLimit = 3000n * 1000000n;
-const endowment = `1000000000000000`;
 const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';
-
-function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
-  return new Promise<Blueprint>(async (resolve, reject) => {
-    const unsub = await code
-      .createBlueprint()
-      .signAndSend(alice, (result) => {
-        if (result.status.isInBlock || result.status.isFinalized) {
-          // here we have an additional field in the result, containing the blueprint
-          resolve(result.blueprint);
-          unsub();
-        }
-      })
-  });
-}
-
-function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
-  return new Promise<any>(async (resolve, reject) => {
-    const endowment = 1000000000000000n;
-    const initValue = true;
-
-    const unsub = await blueprint.tx
-    .new(endowment, gasLimit, initValue)
-    .signAndSend(alice, (result) => {
-      if (result.status.isInBlock || result.status.isFinalized) {
-        unsub();
-        resolve(result);
-      }
-    });    
-  });
-}
 
-async function prepareDeployer(api: ApiPromise) {
-  // Find unused address
-  const deployer = await findUnusedAddress(api);
-
-  // Transfer balance to it
-  const keyring = new Keyring({ type: 'sr25519' });
-  const alice = keyring.addFromUri(`//Alice`);
-  let amount = new BigNumber(endowment);
-  amount = amount.plus(1e15);
-  const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
-  await submitTransactionAsync(alice, tx);
-
-  return deployer;
-}
-
-export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
-  const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
-  const abi = new Abi(metadata);
-
-  const deployer = await prepareDeployer(api);
-
-  const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
-
-  const code = new CodePromise(api, abi, wasm);
-
-  const blueprint = await deployBlueprint(deployer, code);
-  const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
-
-  const initialGetResponse = await getFlipValue(contract, deployer);
-  expect(initialGetResponse).to.be.true;
-
-  return [contract, deployer];
-}
-
-async function getFlipValue(contract: Contract, deployer: IKeyringPair) {
-  const result = await contract.query.get(deployer.address, value, gasLimit);
-
-  if(!result.result.isSuccess) {
-    throw `Failed to get flipper value`;
-  }
-  return (result.result.asSuccess.data[0] == 0x00) ? false : true;
-}
-
 describe('Contracts', () => {
   it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
     await usingApi(async api => {
@@ -103,68 +28,6 @@
 
       const afterFlipGetResponse = await getFlipValue(contract, deployer);
       expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');
-    });
-  });
-
-  it(`Whitelisted account can call contract.`, async () => {
-    await usingApi(async api => {
-      const bob = privateKey("//Bob");
-
-      const [contract, deployer] = await deployFlipper(api);
-
-      let expectedFlipValue = await getFlipValue(contract, deployer);
-
-      const flip = contract.exec('flip', value, gasLimit);
-      await submitTransactionAsync(bob, flip);
-      expectedFlipValue = !expectedFlipValue;
-      const afterFlip = await getFlipValue(contract,deployer);
-      expect(afterFlip).to.be.eq(expectedFlipValue, `Anyone can call new contract.`);
-
-      const deployerCanFlip = async () => {
-        expectedFlipValue = !expectedFlipValue;
-        const deployerFlip = contract.exec('flip', value, gasLimit);
-        await submitTransactionAsync(deployer, deployerFlip);
-        const aliceFlip1Response = await getFlipValue(contract, deployer);
-        expect(aliceFlip1Response).to.be.eq(expectedFlipValue, `Deployer always can flip.`);
-      };
-      await deployerCanFlip();
-
-      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
-      const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx);
-      const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit);
-      await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected;
-      const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer);
-      expect(flipValueAfterEnableWhiteList).to.be.eq(expectedFlipValue, `Enabling whitelist doesn't make it possible to call contract for everyone.`);
-
-      await deployerCanFlip();
-
-      const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
-      const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx);
-      const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit);
-      await submitTransactionAsync(bob, flipWithWhitelistedBob);
-      expectedFlipValue = !expectedFlipValue;
-      const flipAfterWhiteListed = await getFlipValue(contract,deployer);
-      expect(flipAfterWhiteListed).to.be.eq(expectedFlipValue, `Bob was whitelisted, now he can flip.`);
-
-      await deployerCanFlip();
-
-      const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address);
-      const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx);
-      const bobRemoved = contract.exec('flip', value, gasLimit);
-      await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected;
-      const afterBobRemoved = await getFlipValue(contract, deployer);
-      expect(afterBobRemoved).to.be.eq(expectedFlipValue, `Bob can't call contract, now when he is removeed from white list.`);
-
-      await deployerCanFlip();
-
-      const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false);
-      const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx);
-      const whiteListDisabledFlip = contract.exec('flip', value, gasLimit);
-      await submitTransactionAsync(bob, whiteListDisabledFlip);
-      expectedFlipValue = !expectedFlipValue;
-      const afterWhiteListDisabled = await getFlipValue(contract,deployer);
-      expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`);
-
     });
   });
 
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -5,15 +5,15 @@
 
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
-import { default as usingApi } from "./substrate/substrate-api";
-import { createCollectionExpectSuccess, createCollectionExpectFailure, CollectionMode } from "./util/helpers";
+import { default as usingApi } from './substrate/substrate-api';
+import { createCollectionExpectFailure, createCollectionExpectSuccess } from './util/helpers';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
 
 describe('integration test: ext. createCollection():', () => {
   it('Create new NFT collection', async () => {
-    await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+    await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
   });
   it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {
     await createCollectionExpectSuccess({name: 'A'.repeat(64)});
@@ -25,34 +25,35 @@
     await createCollectionExpectSuccess({tokenPrefix: 'A'.repeat(16)});
   });
   it('Create new Fungible collection', async () => {
-    await createCollectionExpectSuccess({mode: 'Fungible'});
+    await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
   });
   it('Create new ReFungible collection', async () => {
-    await createCollectionExpectSuccess({mode: 'ReFungible'});
+    await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
   });
 });
 
 describe('(!negative test!) integration test: ext. createCollection():', () => {
   it('(!negative test!) create new NFT collection whith incorrect data (mode)', async () => {
     await usingApi(async (api) => {
-      const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+      const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
 
-      const badTransaction = async function () { 
-        await createCollectionExpectSuccess({mode: 'BadMode' as CollectionMode});
+      const badTransaction = async () => {
+        await createCollectionExpectSuccess({mode: {type: 'Invalid'}});
       };
+      // tslint:disable-next-line:no-unused-expression
       expect(badTransaction()).to.be.rejected;
 
-      const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+      const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
       expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Incorrect collection created.');
     });
   });
   it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {
-    await createCollectionExpectFailure({name: 'A'.repeat(65)});
+    await createCollectionExpectFailure({ name: 'A'.repeat(65), mode: {type: 'NFT'}});
   });
   it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {
-    await createCollectionExpectFailure({description: 'A'.repeat(257)});
+    await createCollectionExpectFailure({ description: 'A'.repeat(257), mode: { type: 'NFT' }});
   });
   it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
-    await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17)});
+    await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
   });
 });
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -18,17 +18,17 @@
 
   it('Create new item in NFT collection', async () => {
     const createMode = 'NFT';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
   it('Create new item in Fungible collection', async () => {
     const createMode = 'Fungible';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
   it('Create new item in ReFungible collection', async () => {
     const createMode = 'ReFungible';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
 });
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -14,11 +14,11 @@
     await destroyCollectionExpectSuccess(collectionId);
   });
   it('Fungible collection can be destroyed', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
     await destroyCollectionExpectSuccess(collectionId);
   });
   it('ReFungible collection can be destroyed', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
     await destroyCollectionExpectSuccess(collectionId);
   });
 });
addedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -0,0 +1,82 @@
+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';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  it('Remove collection admin.', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      // first - add collection admin Bob
+      const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+      await submitTransactionAsync(Alice, addAdminTx);
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddAdmin).to.be.contains(Bob.address);
+
+      // then remove bob from admins of collection
+      const removeAdminTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+      await submitTransactionAsync(Alice, removeAdminTx);
+
+      const adminListAfterRemoveAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterRemoveAdmin).not.to.be.contains(Bob.address);
+    });
+  });
+});
+
+describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  it('Can\'t remove collection admin from not existing collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = (1 << 32) - 1;
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+
+      const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, bob.address);
+      await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it('Can\'t remove collection admin from deleted collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+
+      await destroyCollectionExpectSuccess(collectionId);
+
+      const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+      await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it('Remove admin from collection that has no admins', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const collectionId = await createCollectionExpectSuccess();
+
+      const adminListBeforeAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListBeforeAddAdmin).to.have.lengthOf(0);
+
+      const tx = api.tx.nft.removeCollectionAdmin(collectionId, Alice.address);
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+});
modifiedtests/src/setCollectionLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionLimits.test.ts
+++ b/tests/src/setCollectionLimits.test.ts
@@ -32,7 +32,7 @@
   });
   it('choose or create collection for testing', async () => {
     await usingApi(async () => {
-      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
     });
   });
 });
modifiedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -30,11 +30,11 @@
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
   it('Set Fungible collection sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'Fungible', decimalPoints: 0} });
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
   it('Set ReFungible collection sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} });
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
 
addedtests/src/setPublicAccessMode.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/setPublicAccessMode.test.ts
@@ -0,0 +1,89 @@
+// https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion
+import { ApiPromise, Keyring } from '@polkadot/api';
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi, {submitTransactionExpectFailAsync} from './substrate/substrate-api';
+import {
+  addToWhiteListExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  enablePublicMintingExpectSuccess,
+  enableWhiteListExpectSuccess,
+} from './util/helpers';
+import { utf16ToStr } from './util/util';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+
+describe('Integration Test setPublicAccessMode(): ', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      Alice = privateKey('//Alice');
+      Bob = privateKey('//Bob');
+    });
+  });
+
+  it('Run extrinsic with collection id parameters, set the whitelist mode for the collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId: number = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(Alice, collectionId);
+      await enablePublicMintingExpectSuccess(Alice, collectionId);
+      await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+      await createItemExpectSuccess(Bob, collectionId, 'NFT', Bob.address);
+    });
+  });
+
+  it('Whitelisted collection limits', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(Alice, collectionId);
+      await enablePublicMintingExpectSuccess(Alice, collectionId);
+      const tx = api.tx.nft.createItem(collectionId, Bob.address, 'NFT');
+      await expect(submitTransactionExpectFailAsync(Bob, tx)).to.be.rejected;
+    });
+  });
+});
+
+describe('Negative Integration Test ext. setPublicAccessMode(): ', () => {
+  it('Set a non-existent collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: radix
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+
+  it('Set the collection that has been deleted', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(collectionId);
+      const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+
+  it('Re-set the list mode already set in quantity', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId: number = await createCollectionExpectSuccess();
+      await enableWhiteListExpectSuccess(Alice, collectionId);
+      await enableWhiteListExpectSuccess(Alice, collectionId);
+    });
+  });
+
+  it('Execute method not on behalf of the collection owner', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = await createCollectionExpectSuccess();
+      const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+      await expect(submitTransactionExpectFailAsync(Bob, tx)).to.be.rejected;
+    });
+  });
+});
modifiedtests/src/setSchemaVersion.test.tsdiffbeforeafterboth
--- a/tests/src/setSchemaVersion.test.ts
+++ b/tests/src/setSchemaVersion.test.ts
@@ -35,7 +35,7 @@
   });
   it('choose or create collection for testing', async () => {
     await usingApi(async () => {
-      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
     });
   });
 });
@@ -93,12 +93,6 @@
       const nonExistedCollectionId = collectionCount + 1;
       tx = api.tx.nft.setSchemaVersion(nonExistedCollectionId, 'ImageURL');
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
-      /*try {
-        await submitTransactionAsync(alice, tx);
-      } catch (e) {
-        // tslint:disable-next-line:no-unused-expression
-        expect(e).to.be.exist;
-      }*/
     });
   });
 
@@ -119,13 +113,6 @@
       await destroyCollectionExpectSuccess(collectionIdForTesting);
       tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
-      /*try {
-        tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
-        await submitTransactionAsync(alice, tx);
-      } catch (e) {
-        // tslint:disable-next-line:no-unused-expression
-        expect(e).to.be.exist;
-      }*/
     });
   });
 });
modifiedtests/src/substrate/get-balance.tsdiffbeforeafterboth
--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -3,12 +3,12 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
-import { ApiPromise } from "@polkadot/api";
-import promisifySubstrate from "./promisify-substrate";
-import {AccountInfo} from "@polkadot/types/interfaces/system";
+import { ApiPromise } from '@polkadot/api';
+import {AccountInfo} from '@polkadot/types/interfaces/system';
+import promisifySubstrate from './promisify-substrate';
 
-export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<bigint[]> {
-  const balance = promisifySubstrate(api, (accounts: string[]) => api.query.system.account.multi(accounts));
+export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<Array<bigint>> {
+  const balance = promisifySubstrate(api, (acc: string[]) => api.query.system.account.multi(acc));
   const responce = await balance(accounts) as unknown as AccountInfo[];
-  return responce.map(r => r.data.free.toBigInt().valueOf());
+  return responce.map((r) => r.data.free.toBigInt().valueOf());
 }
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -58,21 +58,22 @@
   return TransactionStatus.Fail;
 }
 
-export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
-  return new Promise(async function(resolve, reject) {
+export function
+submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
+  return new Promise(async (resolve, reject) => {
     try {
       await transaction.signAndSend(sender, ({ events = [], status }) => {
         const transactionStatus = getTransactionStatus(events, status);
 
-        if (transactionStatus == TransactionStatus.Success) {
+        if (transactionStatus === TransactionStatus.Success) {
           resolve(events);
-        } else if (transactionStatus == TransactionStatus.Fail) {
+        } else if (transactionStatus === TransactionStatus.Fail) {
           console.log(`Something went wrong with transaction. Status: ${status}`);
           reject(events);
         }
       });
     } catch (e) {
-      console.log("Error: ", e);
+      console.log('Error: ', e);
       reject(e);
     }
   });
@@ -90,7 +91,6 @@
         res(rec);
         console.error = consoleError;
         console.log = consoleLog;
-        
       });
     };
     const reject = (errror: any) => {
@@ -104,6 +104,8 @@
       await transaction.signAndSend(sender, ({ events = [], status }) => {
         const transactionStatus = getTransactionStatus(events, status);
 
+        console.log('transactionStatus', transactionStatus, 'events', events);
+
         if (transactionStatus == TransactionStatus.Success) {
           resolve(events);
         } else if (transactionStatus == TransactionStatus.Fail) {
@@ -114,4 +116,4 @@
       reject(e);
     }
   });
-}
\ No newline at end of file
+}
addedtests/src/toggleContractWhiteList.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/toggleContractWhiteList.test.ts
@@ -0,0 +1,151 @@
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
+import privateKey from "./substrate/privateKey";
+import {
+  deployFlipper,
+  getFlipValue
+} from "./util/contracthelpers";
+import {
+  getGenericResult
+} from "./util/helpers"
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+
+describe('Integration Test toggleContractWhiteList', () => {
+
+  it(`Enable white list contract mode`, async () => {
+    await usingApi(async api => {
+      const [contract, deployer] = await deployFlipper(api);
+
+      const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+      const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+      const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+      expect(getGenericResult(enableEvents).success).to.be.true;
+      expect(enabledBefore).to.be.false;
+      expect(enabled).to.be.true;
+    });
+  });
+
+  it(`Only whitelisted account can call contract`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
+
+      const [contract, deployer] = await deployFlipper(api);
+
+      let flipValueBefore = await getFlipValue(contract, deployer);
+      const flip = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, flip);
+      const flipValueAfter = await getFlipValue(contract,deployer);
+      expect(flipValueAfter).to.be.eq(!flipValueBefore, `Anyone can call new contract.`);
+
+      const deployerCanFlip = async () => {
+        let flipValueBefore = await getFlipValue(contract, deployer);
+        const deployerFlip = contract.exec('flip', value, gasLimit);
+        await submitTransactionAsync(deployer, deployerFlip);
+        const aliceFlip1Response = await getFlipValue(contract, deployer);
+        expect(aliceFlip1Response).to.be.eq(!flipValueBefore, `Deployer always can flip.`);
+      };
+      await deployerCanFlip();
+
+      flipValueBefore = await getFlipValue(contract, deployer);
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+      const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx);
+      const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit);
+      await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected;
+      const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer);
+      expect(flipValueAfterEnableWhiteList).to.be.eq(flipValueBefore, `Enabling whitelist doesn't make it possible to call contract for everyone.`);
+
+      await deployerCanFlip();
+
+      flipValueBefore = await getFlipValue(contract, deployer);
+      const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+      const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx);
+      const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, flipWithWhitelistedBob);
+      const flipAfterWhiteListed = await getFlipValue(contract,deployer);
+      expect(flipAfterWhiteListed).to.be.eq(!flipValueBefore, `Bob was whitelisted, now he can flip.`);
+
+      await deployerCanFlip();
+
+      flipValueBefore = await getFlipValue(contract, deployer);
+      const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address);
+      const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx);
+      const bobRemoved = contract.exec('flip', value, gasLimit);
+      await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected;
+      const afterBobRemoved = await getFlipValue(contract, deployer);
+      expect(afterBobRemoved).to.be.eq(flipValueBefore, `Bob can't call contract, now when he is removeed from white list.`);
+
+      await deployerCanFlip();
+
+      flipValueBefore = await getFlipValue(contract, deployer);
+      const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false);
+      const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx);
+      const whiteListDisabledFlip = contract.exec('flip', value, gasLimit);
+      await submitTransactionAsync(bob, whiteListDisabledFlip);
+      const afterWhiteListDisabled = await getFlipValue(contract,deployer);
+      expect(afterWhiteListDisabled).to.be.eq(!flipValueBefore, `Anyone can call contract with disabled whitelist.`);
+
+    });
+  });
+
+  it(`Enabling white list repeatedly should not produce errors`, async () => {
+    await usingApi(async api => {
+      const [contract, deployer] = await deployFlipper(api);
+
+      const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+      const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+      const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+      const enableAgainEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+      const enabledAgain = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+      expect(getGenericResult(enableEvents).success).to.be.true;
+      expect(enabledBefore).to.be.false;
+      expect(enabled).to.be.true;
+      expect(getGenericResult(enableAgainEvents).success).to.be.true;
+      expect(enabledAgain).to.be.true;
+    });
+  });
+
+});
+
+describe('Negative Integration Test toggleContractWhiteList', () => {
+
+  it(`Enable white list for a non-contract`, async () => {
+    await usingApi(async api => {
+      const alice = privateKey("//Alice");
+      const bobGuineaPig = privateKey("//Bob");
+
+      const enabledBefore = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON();
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(bobGuineaPig.address, true);
+      await expect(submitTransactionExpectFailAsync(alice, enableWhiteListTx)).to.be.rejected;
+      const enabled = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON();
+
+      expect(enabledBefore).to.be.false;
+      expect(enabled).to.be.false;
+    });
+  });
+
+  it(`Enable white list using a non-owner address`, async () => {
+    await usingApi(async api => {
+      const bob = privateKey("//Bob");
+      const [contract, deployer] = await deployFlipper(api);
+
+      const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+      const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+      await expect(submitTransactionExpectFailAsync(bob, enableWhiteListTx)).to.be.rejected;
+      const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+      expect(enabledBefore).to.be.false;
+      expect(enabled).to.be.false;
+    });
+  });
+
+});
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -3,49 +3,174 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
-import { expect, assert } from "chai";
-import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
-import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts";
-import privateKey from "./substrate/privateKey";
-import getBalance from "./substrate/get-balance";
-import { BigNumber } from 'bignumber.js';
-import { findUnusedAddress } from './util/helpers'
+import { ApiPromise } from '@polkadot/api';
+import { IKeyringPair } from '@polkadot/types/types';
+import { expect } from 'chai';
+import { alicesPublicKey, bobsPublicKey } from './accounts';
+import getBalance from './substrate/get-balance';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync } from './substrate/substrate-api';
+import {
+  burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  findUnusedAddress,
+  getCreateCollectionResult,
+  getCreateItemResult,
+  transferExpectFail,
+  transferExpectSuccess,
+} from './util/helpers';
 
-describe('Transfer', () => {
-  it('Balance transfers', async () => {
-    await usingApi(async api => {
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+let Charlie: IKeyringPair;
+
+describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+  it('Balance transfers and check balance', async () => {
+    await usingApi(async (api: ApiPromise) => {
       const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
 
       const alicePrivateKey = privateKey('//Alice');
-      
+
       const transfer = api.tx.balances.transfer(bobsPublicKey, 1n);
-      const result = await submitTransactionAsync(alicePrivateKey, transfer);
+      const events = await submitTransactionAsync(alicePrivateKey, transfer);
+      const result = getCreateItemResult(events);
+      // tslint:disable-next-line:no-unused-expression
+      expect(result.success).to.be.true;
 
       const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
 
+      // tslint:disable-next-line:no-unused-expression
       expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
+      // tslint:disable-next-line:no-unused-expression
       expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;
     });
   });
 
   it('Inability to pay fees error message is correct', async () => {
-    await usingApi(async api => {
+    await usingApi(async (api) => {
       // Find unused address
       const pk = await findUnusedAddress(api);
 
-      const error = console.error;
-      const log = console.log;
-      console.log = function () {};
-      console.error = function () {};
-  
       const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n);
-      const badTransaction = async function () { 
-        const result = await submitTransactionAsync(pk, badTransfer);
+      // const events = await submitTransactionAsync(pk, badTransfer);
+      const badTransaction = async () => {
+        const events = await submitTransactionAsync(pk, badTransfer);
+        const result = getCreateCollectionResult(events);
+        // tslint:disable-next-line:no-unused-expression
+        expect(result.success).to.be.false;
       };
-      await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+      expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');
+    });
+  });
 
-      console.log = log;
-      console.error = error;
+  it('Create collection, balance transfers and check balance', async () => {
+    await usingApi(async (api) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await transferExpectSuccess(reFungibleCollectionId,
+        newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
     });
   });
 });
+
+describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+  before(async () => {
+    await usingApi(async (api: ApiPromise) => {
+      Alice = privateKey('//Alice');
+      Bob = privateKey('//Bob');
+      Charlie = privateKey('//Charlie');
+    });
+  });
+  it('Transfer with not existed collection_id', async () => {
+    await usingApi(async (api) => {
+      // nft
+      const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await transferExpectFail(nftCollectionCount + 1, 1, Alice, Bob, 1);
+      // fungible
+      const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await transferExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob, 1);
+      // reFungible
+      const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await transferExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob, 1);
+    });
+  });
+  it('Transfer with deleted collection_id', async () => {
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await destroyCollectionExpectSuccess(nftCollectionId);
+    await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await destroyCollectionExpectSuccess(fungibleCollectionId);
+    await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+    // reFungible
+    const reFungibleCollectionId = await
+      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await destroyCollectionExpectSuccess(reFungibleCollectionId);
+    await transferExpectFail(reFungibleCollectionId,
+      newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
+  });
+  it('Transfer with not existed item_id', async () => {
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    await transferExpectFail(nftCollectionId, 2, Alice, Bob, 1, 'NFT');
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    await transferExpectFail(fungibleCollectionId, 2, Alice, Bob, 1, 'Fungible');
+    // reFungible
+    const reFungibleCollectionId = await
+      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    await transferExpectFail(reFungibleCollectionId,
+      2, Alice, Bob, 1, 'ReFungible');
+  });
+  it('Transfer with deleted item_id', async () => {
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await burnItemExpectSuccess(Alice, nftCollectionId, newNftTokenId, 1);
+    await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await burnItemExpectSuccess(Alice, fungibleCollectionId, newFungibleTokenId, 10);
+    await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+    // reFungible
+    const reFungibleCollectionId = await
+      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1);
+    await transferExpectFail(reFungibleCollectionId,
+      newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
+  });
+  it('Transfer with recipient that is not owner', async () => {
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await transferExpectFail(nftCollectionId, newNftTokenId, Charlie, Bob, 1, 'NFT');
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Charlie, Bob, 1, 'Fungible');
+    // reFungible
+    const reFungibleCollectionId = await
+      createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await transferExpectFail(reFungibleCollectionId,
+      newReFungibleTokenId, Charlie, Bob, 1, 'ReFungible');
+  });
+});
addedtests/src/transferFrom.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/transferFrom.test.ts
@@ -0,0 +1,188 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+import { ApiPromise } from '@polkadot/api';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+  approveExpectFail,
+  approveExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  transferFromExpectFail,
+  transferFromExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  it('Execute the extrinsic and check nftItemList - owner of token', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+      await transferFromExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1, 'NFT');
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1, 'Fungible');
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+      await transferFromExpectSuccess(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 1, 'ReFungible');
+    });
+  });
+});
+
+describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  it('transferFrom for a collection that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(nftCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(fungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+      // reFungible
+      const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(reFungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+    });
+  });
+
+  /* it('transferFrom for a collection that was destroyed', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  /* it('transferFrom a token that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  /* it('transferFrom a token that was deleted', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  it('transferFrom for not approved address', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+
+      await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1);
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await transferFromExpectFail(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 1);
+    });
+  });
+
+  it('transferFrom incorrect token count', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+      await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 2);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 2);
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+      await transferFromExpectFail(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 2);
+    });
+  });
+
+  it('execute transferFrom from account that is not owner of collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      const Dave = privateKey('//DAVE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      try {
+        await approveExpectFail(nftCollectionId, newNftTokenId, Dave, Bob);
+        await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+
+      // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      try {
+        await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Bob);
+        await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      try {
+        await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Bob);
+        await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+    });
+  });
+});
addedtests/src/util/contracthelpers.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/util/contracthelpers.ts
@@ -0,0 +1,94 @@
+//
+// 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 { submitTransactionAsync } from "../substrate/substrate-api";
+import fs from "fs";
+import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
+import { IKeyringPair } from "@polkadot/types/types";
+import { ApiPromise, Keyring } from "@polkadot/api";
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from '../util/helpers';
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+const endowment = `1000000000000000`;
+
+function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
+  return new Promise<Blueprint>(async (resolve, reject) => {
+    const unsub = await code
+      .createBlueprint()
+      .signAndSend(alice, (result) => {
+        if (result.status.isInBlock || result.status.isFinalized) {
+          // here we have an additional field in the result, containing the blueprint
+          resolve(result.blueprint);
+          unsub();
+        }
+      })
+  });
+}
+
+function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
+  return new Promise<any>(async (resolve, reject) => {
+    const endowment = 1000000000000000n;
+    const initValue = true;
+
+    const unsub = await blueprint.tx
+    .new(endowment, gasLimit, initValue)
+    .signAndSend(alice, (result) => {
+      if (result.status.isInBlock || result.status.isFinalized) {
+        unsub();
+        resolve(result);
+      }
+    });    
+  });
+}
+
+async function prepareDeployer(api: ApiPromise) {
+  // Find unused address
+  const deployer = await findUnusedAddress(api);
+
+  // Transfer balance to it
+  const keyring = new Keyring({ type: 'sr25519' });
+  const alice = keyring.addFromUri(`//Alice`);
+  let amount = new BigNumber(endowment);
+  amount = amount.plus(1e15);
+  const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
+  await submitTransactionAsync(alice, tx);
+
+  return deployer;
+}
+
+export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
+  const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
+  const abi = new Abi(metadata);
+
+  const deployer = await prepareDeployer(api);
+
+  const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
+
+  const code = new CodePromise(api, abi, wasm);
+
+  const blueprint = await deployBlueprint(deployer, code);
+  const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
+
+  const initialGetResponse = await getFlipValue(contract, deployer);
+  expect(initialGetResponse).to.be.true;
+
+  return [contract, deployer];
+}
+
+export async function getFlipValue(contract: Contract, deployer: IKeyringPair) {
+  const result = await contract.query.get(deployer.address, value, gasLimit);
+
+  if(!result.result.isSuccess) {
+    throw `Failed to get flipper value`;
+  }
+  return (result.result.asSuccess.data[0] == 0x00) ? false : true;
+}
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -3,20 +3,20 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
+import { ApiPromise, Keyring } from '@polkadot/api';
+import { Enum, Struct } from '@polkadot/types/codec';
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import { u128 } from '@polkadot/types/primitive';
+import { IKeyringPair } from '@polkadot/types/types';
+import { BigNumber } from 'bignumber.js';
+import BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
-import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
-import { ApiPromise, Keyring } from "@polkadot/api";
-import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api";
+import { alicesPublicKey, nullPublicKey } from '../accounts';
 import privateKey from '../substrate/privateKey';
-import { alicesPublicKey, nullPublicKey } from "../accounts";
-import { strToUTF16, utf16ToStr, hexToStr } from './util';
-import { IKeyringPair } from '@polkadot/types/types';
-import { BigNumber } from 'bignumber.js';
-import { Struct, Enum } from '@polkadot/types/codec';
-import { u128 } from '@polkadot/types/primitive';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';
 import { ICollectionInterface } from '../types';
-import BN from "bn.js";
+import { hexToStr, strToUTF16, utf16ToStr } from './util';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
@@ -25,24 +25,45 @@
   success: boolean,
 };
 
-type CreateCollectionResult = {
-  success: boolean,
-  collectionId: number
-};
+interface CreateCollectionResult {
+  success: boolean;
+  collectionId: number;
+}
+
+interface CreateItemResult {
+  success: boolean;
+  collectionId: number;
+  itemId: number;
+}
+
+interface IReFungibleOwner {
+  Fraction: BN;
+  Owner: number[];
+}
+
+interface ITokenDataType {
+  Owner: number[];
+  ConstData: number[];
+  VariableData: number[];
+}
+
+interface IFungibleTokenDataType {
+  Value: BN;
+}
 
-type CreateItemResult = {
-  success: boolean,
-  collectionId: number,
-  itemId: number
-};
+interface IReFungibleTokenDataType {
+  Owner: IReFungibleOwner[];
+  ConstData: number[];
+  VariableData: number[];
+}
 
 export function getGenericResult(events: EventRecord[]): GenericResult {
-  let result: GenericResult = {
-    success: false
-  }
+  const result: GenericResult = {
+    success: false,
+  };
   events.forEach(({ phase, event: { data, method, section } }) => {
     // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
-    if (method == 'ExtrinsicSuccess') {
+    if (method === 'ExtrinsicSuccess') {
       result.success = true;
     }
   });
@@ -60,10 +81,10 @@
       collectionId = parseInt(data[0].toString());
     }
   });
-  let result: CreateCollectionResult = {
+  const result: CreateCollectionResult = {
     success,
-    collectionId
-  }
+    collectionId,
+  };
   return result;
 }
 
@@ -80,27 +101,60 @@
       itemId = parseInt(data[1].toString());
     }
   });
-  let result: CreateItemResult = {
+  const result: CreateItemResult = {
     success,
     collectionId,
-    itemId
-  }
+    itemId,
+  };
   return result;
 }
 
-export type CollectionMode = 'NFT' | 'Fungible' | 'ReFungible';
+interface Invalid {
+  type: 'Invalid';
+}
+
+interface Nft {
+  type: 'NFT';
+}
+
+interface Fungible {
+  type: 'Fungible';
+  decimalPoints: number;
+}
+
+interface ReFungible {
+  type: 'ReFungible';
+  decimalPoints: number;
+}
+
+interface Nft {
+  type: 'NFT'
+}
+
+interface Fungible {
+  type: 'Fungible',
+  decimalPoints: number
+}
+
+interface ReFungible {
+  type: 'ReFungible',
+  decimalPoints: number
+}
+
+type CollectionMode = Nft | Fungible | ReFungible | Invalid;
+
 export type CreateCollectionParams = {
   mode: CollectionMode,
   name: string,
   description: string,
-  tokenPrefix: string
+  tokenPrefix: string,
 };
 
 const defaultCreateCollectionParams: CreateCollectionParams = {
+  description: 'description',
+  mode: { type: 'NFT' },
   name: 'name',
-  description: 'description',
-  mode: 'NFT',
-  tokenPrefix: 'prefix'
+  tokenPrefix: 'prefix',
 }
 
 export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
@@ -109,25 +163,39 @@
   let collectionId: number = 0;
   await usingApi(async (api) => {
     // Get number of collections before the transaction
-    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
 
     // Run the CreateCollection transaction
     const alicePrivateKey = privateKey('//Alice');
-    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+
+    let modeprm = {};
+    if (mode.type === 'NFT') {
+      modeprm = {nft: null};
+    } else if (mode.type === 'Fungible') {
+      modeprm = {fungible: mode.decimalPoints};
+    } else if (mode.type === 'ReFungible') {
+      modeprm = {refungible: mode.decimalPoints};
+    } else if (mode.type === 'Invalid') {
+      modeprm = {invalid: null};
+    }
+
+    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getCreateCollectionResult(events);
 
     // Get number of collections after the transaction
-    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(result.collectionId)).toJSON();
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
     expect(result.collectionId).to.be.equal(BcollectionCount);
+    // tslint:disable-next-line:no-unused-expression
     expect(collection).to.be.not.null;
-    expect(BcollectionCount).to.be.equal(AcollectionCount+1, 'Error: NFT collection NOT created.');
+    expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');
     expect(collection.Owner).to.be.equal(alicesPublicKey);
     expect(utf16ToStr(collection.Name)).to.be.equal(name);
     expect(utf16ToStr(collection.Description)).to.be.equal(description);
@@ -138,17 +206,28 @@
 
   return collectionId;
 }
-  
+
 export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {
   const {name, description, mode, tokenPrefix } = {...defaultCreateCollectionParams, ...params};
 
+  let modeprm = {};
+  if (mode.type === 'NFT') {
+    modeprm = {nft: null};
+  } else if (mode.type === 'Fungible') {
+    modeprm = {fungible: mode.decimalPoints};
+  } else if (mode.type === 'ReFungible') {
+    modeprm = {refungible: mode.decimalPoints};
+  } else if (mode.type === 'Invalid') {
+    modeprm = {invalid: null};
+  }
+
   await usingApi(async (api) => {
     // Get number of collections before the transaction
     const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
 
     // Run the CreateCollection transaction
     const alicePrivateKey = privateKey('//Alice');
-    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
     const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
     const result = getCreateCollectionResult(events);
 
@@ -156,11 +235,12 @@
     const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.false;
     expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');
   });
 }
-  
+
 export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
   let bal = new BigNumber(0);
   let unused;
@@ -170,7 +250,7 @@
     unused = keyring.addFromUri(`//${randomSeed}`);
     bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());
   } while (bal.toFixed() != '0');
-  return unused; 
+  return unused;
 }
 
 function getDestroyResult(events: EventRecord[]): boolean {
@@ -201,7 +281,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getDestroyResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -220,7 +300,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -239,7 +319,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -278,7 +358,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -300,48 +380,200 @@
 
 export interface CreateFungibleData extends Struct {
   readonly value: u128;
-};
+}
 
-export interface CreateReFungibleData extends Struct {};
-export interface CreateNftData extends Struct {};
+export interface CreateReFungibleData extends Struct {}
+export interface CreateNftData extends Struct {}
 
 export interface CreateItemData extends Enum {
-  NFT: CreateNftData,
-  Fungible: CreateFungibleData,
-  ReFungible: CreateReFungibleData
-};
+  NFT: CreateNftData;
+  Fungible: CreateFungibleData;
+  ReFungible: CreateReFungibleData;
+}
+
+export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {
+  await usingApi(async (api) => {
+    const tx = api.tx.nft.burnItem(collectionId, tokenId, value);
+    const events = await submitTransactionAsync(owner, tx);
+    const result = getGenericResult(events);
+    // Get the item
+    const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    // tslint:disable-next-line:no-unused-expression
+    expect(item).to.be.not.null;
+    expect(item.Owner).to.be.equal(nullPublicKey);
+  });
+}
+
+export async function
+approveExpectSuccess(collectionId: number,
+                     tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const allowanceBefore =
+      await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+    const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+    const events = await submitTransactionAsync(owner, approveNftTx);
+    const result = getCreateItemResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    const allowanceAfter =
+      await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+    expect(allowanceAfter.toNumber() - allowanceBefore.toNumber()).to.be.equal(amount);
+  });
+}
+
+export async function
+transferFromExpectSuccess(collectionId: number,
+                          tokenId: number,
+                          accountApproved: IKeyringPair,
+                          accountFrom: IKeyringPair,
+                          accountTo: IKeyringPair,
+                          value: number = 1,
+                          type: string = 'NFT') {
+  await usingApi(async (api: ApiPromise) => {
+    let balanceBefore = new BN(0);
+    if (type === 'Fungible') {
+      balanceBefore = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+    }
+    const transferFromTx = await api.tx.nft.transferFrom(
+      accountFrom.address, accountTo.address, collectionId, tokenId, value);
+    const events = await submitTransactionAsync(accountFrom, transferFromTx);
+    const result = getCreateItemResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    if (type === 'NFT') {
+      const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType;
+      expect(nftItemData.Owner.toString()).to.be.equal(accountTo.address);
+    }
+    if (type === 'Fungible') {
+      const balanceAfter = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+      expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value);
+    }
+    if (type === 'ReFungible') {
+      const nftItemData =
+        await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType;
+      expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(accountTo.address);
+      expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value);
+    }
+  });
+}
+
+export async function
+transferFromExpectFail(collectionId: number,
+                       tokenId: number,
+                       accountApproved: IKeyringPair,
+                       accountFrom: IKeyringPair,
+                       accountTo: IKeyringPair,
+                       value: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const transferFromTx = await api.tx.nft.transferFrom(
+      accountFrom.address, accountTo.address, collectionId, tokenId, value);
+    const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;
+    const result = getCreateCollectionResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function
+transferExpectSuccess(collectionId: number,
+                      tokenId: number,
+                      sender: IKeyringPair,
+                      recipient: IKeyringPair,
+                      value: number = 1,
+                      type: string = 'NFT') {
+  await usingApi(async (api: ApiPromise) => {
+    let balanceBefore = new BN(0);
+    if (type === 'Fungible') {
+      balanceBefore = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN;
+    }
+    const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value);
+    const events = await submitTransactionAsync(sender, transferTx);
+    const result = getCreateItemResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    if (type === 'NFT') {
+      const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType;
+      expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);
+    }
+    if (type === 'Fungible') {
+      const balanceAfter = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN;
+      expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value);
+    }
+    if (type === 'ReFungible') {
+      const nftItemData =
+        await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType;
+      expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(recipient.address);
+      expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value);
+    }
+  });
+}
+
+export async function
+transferExpectFail(collectionId: number,
+                   tokenId: number,
+                   sender: IKeyringPair,
+                   recipient: IKeyringPair,
+                   value: number = 1,
+                   type: string = 'NFT') {
+  await usingApi(async (api: ApiPromise) => {
+    const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value);
+    const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;
+    if (events && Array.isArray(events)) {
+      const result = getCreateCollectionResult(events);
+      // tslint:disable-next-line:no-unused-expression
+      expect(result.success).to.be.false;
+    }
+  });
+}
+
+export async function
+approveExpectFail(collectionId: number,
+                  tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+    const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;
+    const result = getCreateCollectionResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
 
-export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
+export async function createItemExpectSuccess(
+  sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
   let newItemId: number = 0;
   await usingApi(async (api) => {
-    const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
-    const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+    const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
     const AItemBalance = new BigNumber(Aitem.Value);
 
-    if (owner === '') owner = sender.address;
+    if (owner === '') {
+      owner = sender.address;
+    }
 
     let tx;
-    if (createMode == 'Fungible') {
-      let createData = {fungible: {value: 10}};
+    if (createMode === 'Fungible') {
+      const createData = {fungible: {value: 10}};
       tx = api.tx.nft.createItem(collectionId, owner, createData);
-    }
-    else {
+    } else {
       tx = api.tx.nft.createItem(collectionId, owner, createMode);
     }
     const events = await submitTransactionAsync(sender, tx);
     const result = getCreateItemResult(events);
 
-    const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
-    const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+    const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
     const BItemBalance = new BigNumber(Bitem.Value);
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
-    if (createMode == 'Fungible') {
+    if (createMode === 'Fungible') {
       expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);
-    }
-    else {
-      expect(BItemCount).to.be.equal(AItemCount+1);
+    } else {
+      expect(BItemCount).to.be.equal(AItemCount + 1);
     }
     expect(collectionId).to.be.equal(result.collectionId);
     expect(BItemCount).to.be.equal(result.itemId);
@@ -358,10 +590,11 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
     expect(collection.Access).to.be.equal('WhiteList');
   });
@@ -375,7 +608,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -387,17 +620,22 @@
 export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
   await usingApi(async (api) => {
 
+    const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();
+
     // Run the transaction
     const tx = api.tx.nft.addToWhiteList(collectionId, address);
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
-    const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+    const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
-    expect(collection.MintMode).to.be.equal(true);
+    // tslint:disable-next-line: no-unused-expression
+    expect(whiteListedBefore).to.be.false;
+    // tslint:disable-next-line: no-unused-expression
+    expect(whiteListedAfter).to.be.true;
   });
 }