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

difftreelog

Merge pull request #3 from usetech-llc/feature/white_list_tests

usetech-llc2020-10-02parents: #cf4d1ac #29e7096.patch.diff
in: master
Feature/white list tests

4 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
before · node/src/chain_spec.rs
1use nft_runtime::{2    AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,3    SystemConfig, WASM_BINARY,4};5use nft_runtime::{ContractsConfig, ContractsSchedule};6use sc_service::ChainType;7use sp_consensus_aura::sr25519::AuthorityId as AuraId;8use sp_core::{sr25519, Pair, Public};9use sp_finality_grandpa::AuthorityId as GrandpaId;10use sp_runtime::traits::{IdentifyAccount, Verify};1112// Note this is the URL for the telemetry server13//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";1415/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.16pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;1718/// Helper function to generate a crypto pair from seed19pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {20    TPublic::Pair::from_string(&format!("//{}", seed), None)21        .expect("static values are valid; qed")22        .public()23}2425type AccountPublic = <Signature as Verify>::Signer;2627/// Helper function to generate an account ID from seed28pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId29where30    AccountPublic: From<<TPublic::Pair as Pair>::Public>,31{32    AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()33}3435/// Helper function to generate an authority key for Aura36pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {37    (get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))38}3940pub fn development_config() -> ChainSpec {41    ChainSpec::from_genesis(42        "Development",43        "dev",44        ChainType::Development,45        || {46            testnet_genesis(47                vec![authority_keys_from_seed("Alice")],48                get_account_id_from_seed::<sr25519::Public>("Alice"),49                vec![50                    get_account_id_from_seed::<sr25519::Public>("Alice"),51                    get_account_id_from_seed::<sr25519::Public>("Bob"),52                    get_account_id_from_seed::<sr25519::Public>("Alice//stash"),53                    get_account_id_from_seed::<sr25519::Public>("Bob//stash"),54                ],55                true,56            )57        },58        vec![],59        None,60        None,61        None,62        None,63    )64}6566pub fn local_testnet_config() -> ChainSpec {67    ChainSpec::from_genesis(68        "Local Testnet",69        "local_testnet",70        ChainType::Local,71        || {72            testnet_genesis(73                vec![74                    authority_keys_from_seed("Alice"),75                    authority_keys_from_seed("Bob"),76                ],77                get_account_id_from_seed::<sr25519::Public>("Alice"),78                vec![79                    get_account_id_from_seed::<sr25519::Public>("Alice"),80                    get_account_id_from_seed::<sr25519::Public>("Bob"),81                    get_account_id_from_seed::<sr25519::Public>("Charlie"),82                    get_account_id_from_seed::<sr25519::Public>("Dave"),83                    get_account_id_from_seed::<sr25519::Public>("Eve"),84                    get_account_id_from_seed::<sr25519::Public>("Ferdie"),85                    get_account_id_from_seed::<sr25519::Public>("Alice//stash"),86                    get_account_id_from_seed::<sr25519::Public>("Bob//stash"),87                    get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),88                    get_account_id_from_seed::<sr25519::Public>("Dave//stash"),89                    get_account_id_from_seed::<sr25519::Public>("Eve//stash"),90                    get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),91                ],92                true,93            )94        },95        vec![],96        None,97        None,98        None,99        None,100    )101}102103fn testnet_genesis(104    initial_authorities: Vec<(AuraId, GrandpaId)>,105    root_key: AccountId,106    endowed_accounts: Vec<AccountId>,107    enable_println: bool,108) -> GenesisConfig {109    GenesisConfig {110        system: Some(SystemConfig {111            code: WASM_BINARY.to_vec(),112            changes_trie_config: Default::default(),113        }),114        balances: Some(BalancesConfig {115            balances: endowed_accounts116                .iter()117                .cloned()118                .map(|k| (k, 1 << 100))119                .collect(),120        }),121        aura: Some(AuraConfig {122            authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),123        }),124        grandpa: Some(GrandpaConfig {125            authorities: initial_authorities126                .iter()127                .map(|x| (x.1.clone(), 1))128                .collect(),129        }),130        sudo: Some(SudoConfig { key: root_key }),131        contracts: Some(ContractsConfig {132            current_schedule: ContractsSchedule {133                enable_println,134                ..Default::default()135            },136        }),137    }138}
after · node/src/chain_spec.rs
1// use nft_runtime::{2//     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,3//     SystemConfig, WASM_BINARY,4// };5// use nft_runtime::{ContractsConfig, ContractsSchedule, NftConfig, CollectionType};6use nft_runtime::*;7use sc_service::ChainType;8use sp_consensus_aura::sr25519::AuthorityId as AuraId;9use sp_core::{sr25519, Pair, Public};10use sp_finality_grandpa::AuthorityId as GrandpaId;11use sp_runtime::traits::{IdentifyAccount, Verify};1213// Note this is the URL for the telemetry server14//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";1516/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.17pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;1819/// Helper function to generate a crypto pair from seed20pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {21    TPublic::Pair::from_string(&format!("//{}", seed), None)22        .expect("static values are valid; qed")23        .public()24}2526type AccountPublic = <Signature as Verify>::Signer;2728/// Helper function to generate an account ID from seed29pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId30where31    AccountPublic: From<<TPublic::Pair as Pair>::Public>,32{33    AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()34}3536/// Helper function to generate an authority key for Aura37pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {38    (get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))39}4041pub fn development_config() -> ChainSpec {42    ChainSpec::from_genesis(43        "Development",44        "dev",45        ChainType::Development,46        || {47            testnet_genesis(48                vec![authority_keys_from_seed("Alice")],49                get_account_id_from_seed::<sr25519::Public>("Alice"),50                vec![51                    get_account_id_from_seed::<sr25519::Public>("Alice"),52                    get_account_id_from_seed::<sr25519::Public>("Bob"),53                    get_account_id_from_seed::<sr25519::Public>("Alice//stash"),54                    get_account_id_from_seed::<sr25519::Public>("Bob//stash"),55                ],56                true,57            )58        },59        vec![],60        None,61        None,62        None,63        None,64    )65}6667pub fn local_testnet_config() -> ChainSpec {68    ChainSpec::from_genesis(69        "Local Testnet",70        "local_testnet",71        ChainType::Local,72        || {73            testnet_genesis(74                vec![75                    authority_keys_from_seed("Alice"),76                    authority_keys_from_seed("Bob"),77                ],78                get_account_id_from_seed::<sr25519::Public>("Alice"),79                vec![80                    get_account_id_from_seed::<sr25519::Public>("Alice"),81                    get_account_id_from_seed::<sr25519::Public>("Bob"),82                    get_account_id_from_seed::<sr25519::Public>("Charlie"),83                    get_account_id_from_seed::<sr25519::Public>("Dave"),84                    get_account_id_from_seed::<sr25519::Public>("Eve"),85                    get_account_id_from_seed::<sr25519::Public>("Ferdie"),86                    get_account_id_from_seed::<sr25519::Public>("Alice//stash"),87                    get_account_id_from_seed::<sr25519::Public>("Bob//stash"),88                    get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),89                    get_account_id_from_seed::<sr25519::Public>("Dave//stash"),90                    get_account_id_from_seed::<sr25519::Public>("Eve//stash"),91                    get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),92                ],93                true,94            )95        },96        vec![],97        None,98        None,99        None,100        None,101    )102}103104fn testnet_genesis(105    initial_authorities: Vec<(AuraId, GrandpaId)>,106    root_key: AccountId,107    endowed_accounts: Vec<AccountId>,108    enable_println: bool,109) -> GenesisConfig {110    GenesisConfig {111        system: Some(SystemConfig {112            code: WASM_BINARY.to_vec(),113            changes_trie_config: Default::default(),114        }),115        balances: Some(BalancesConfig {116            balances: endowed_accounts117                .iter()118                .cloned()119                .map(|k| (k, 1 << 100))120                .collect(),121        }),122        aura: Some(AuraConfig {123            authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),124        }),125        grandpa: Some(GrandpaConfig {126            authorities: initial_authorities127                .iter()128                .map(|x| (x.1.clone(), 1))129                .collect(),130        }),131        sudo: Some(SudoConfig { key: root_key }),132        nft: Some(NftConfig {133            collection: vec![(1, CollectionType { 134                owner: get_account_id_from_seed::<sr25519::Public>("Alice"),135                mode: CollectionMode::NFT(50),136                access: AccessMode::Normal,137                decimal_points: 0,138                name: vec!(),139                description: vec!(),140                token_prefix: vec!(),141                custom_data_size: 50,142                mint_mode: false,143                offchain_schema: vec!(),144                sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),145                unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),146            })],147            nft_item_id: vec!(),148            fungible_item_id: vec!(),149            refungible_item_id: vec!(),150        }),151        contracts: Some(ContractsConfig {152            current_schedule: ContractsSchedule {153                enable_println,154                ..Default::default()155            },156        }),157    }158}
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1,7 +1,8 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-/// For more guidance on Substrate FRAME, see the example pallet
-/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs
+#[cfg(feature = "std")]
+pub use serde::*;
+
 use codec::{Decode, Encode};
 pub use frame_support::{
     construct_runtime, decl_event, decl_module, decl_storage,
@@ -32,7 +33,6 @@
     },
     FixedPointOperand, FixedU128,
 };
-use sp_std::prelude::*;
 
 #[cfg(test)]
 mod mock;
@@ -40,7 +40,11 @@
 #[cfg(test)]
 mod tests;
 
-#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]
+// Structs
+// #region
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub enum CollectionMode {
     Invalid,
     // custom data size
@@ -62,7 +66,8 @@
     }
 }
 
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub enum AccessMode {
     Normal,
     WhiteList,
@@ -79,15 +84,15 @@
     }
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct Ownership<AccountId> {
     pub owner: AccountId,
     pub fraction: u128,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct CollectionType<AccountId> {
     pub owner: AccountId,
     pub mode: CollectionMode,
@@ -103,46 +108,46 @@
     pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct CollectionAdminsType<AccountId> {
     pub admin: AccountId,
     pub collection_id: u64,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct NftItemType<AccountId> {
     pub collection: u64,
     pub owner: AccountId,
     pub data: Vec<u8>,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct FungibleItemType<AccountId> {
     pub collection: u64,
     pub owner: AccountId,
     pub value: u128,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct ReFungibleItemType<AccountId> {
     pub collection: u64,
     pub owner: Vec<Ownership<AccountId>>,
     pub data: Vec<u8>,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct ApprovePermissions<AccountId> {
     pub approved: AccountId,
     pub amount: u64,
 }
 
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub struct VestingItem<AccountId, Moment> {
     pub sender: AccountId,
     pub recipient: AccountId,
@@ -156,6 +161,8 @@
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 }
 
+// #endregion
+
 decl_storage! {
     trait Store for Module<T: Trait> as Nft {
 
@@ -165,7 +172,7 @@
         ChainVersion: u64;
         ItemListIndex: map hasher(blake2_128_concat) u64 => u64;
 
-        pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;
+        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;
         pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;
         pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;
 
@@ -176,9 +183,9 @@
         pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;
 
         /// Item collections
-        pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
-        pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
-        pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
+        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
+        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
+        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
 
         /// Index list
         pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
@@ -187,6 +194,26 @@
         pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
         pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
     }
+    add_extra_genesis {
+        build(|config: &GenesisConfig<T>| {
+			// Modification of storage
+            for (_num, _c) in &config.collection {
+                <Module<T>>::init_collection(_c);
+            }
+
+            for (_num, _q, _i) in &config.nft_item_id {
+                <Module<T>>::init_nft_token(_i);
+            }
+
+            for (_num, _q, _i) in &config.fungible_item_id {
+                <Module<T>>::init_fungible_token(_i);
+            }
+
+            for (_num, _q, _i) in &config.refungible_item_id {
+                <Module<T>>::init_refungible_token(_i);
+            }
+		})
+    }
 }
 
 decl_event!(
@@ -541,14 +568,16 @@
 
             let sender = ensure_signed(origin)?;
             Self::collection_exists(collection_id)?;
-            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
-            if !item_owner
-            {
-                if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {  
-                    Self::check_white_list(collection_id, sender.clone())?;
-                }
+
+            // Transfer permissions check
+            let target_collection = <Collection<T>>::get(collection_id);
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+                "Only item owner, collection owner and admins can modify item");
+
+            if target_collection.access == AccessMode::WhiteList {
+                Self::check_white_list(collection_id, sender.clone())?;
             }
-            let target_collection = <Collection<T>>::get(collection_id);
 
             match target_collection.mode
             {
@@ -568,10 +597,18 @@
         pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
-            Self::check_white_list(collection_id, sender.clone())?;
-            Self::check_white_list(collection_id, recipient.clone())?;
+
+            // Transfer permissions check
             let target_collection = <Collection<T>>::get(collection_id);
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+                "Only item owner, collection owner and admins can modify item");
 
+            if target_collection.access == AccessMode::WhiteList {
+                Self::check_white_list(collection_id, sender.clone())?;
+                Self::check_white_list(collection_id, recipient.clone())?;
+            }
+
             match target_collection.mode
             {
                 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,
@@ -588,14 +625,20 @@
 
             let sender = ensure_signed(origin)?;
 
-            // amount param stub
-            let amount = 100000000;
+            // Transfer permissions check
+            let target_collection = <Collection<T>>::get(collection_id);
+            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 
+                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+                "Only item owner, collection owner and admins can approve");
 
-            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
-            if !item_owner {
+            if target_collection.access == AccessMode::WhiteList {
+                Self::check_white_list(collection_id, sender.clone())?;
                 Self::check_white_list(collection_id, approved.clone())?;
             }
 
+            // amount param stub
+            let amount = 100000000;
+
             let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
             if list_exists {
 
@@ -620,24 +663,31 @@
         pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
-            let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone()));
+            let mut appoved_transfer = false;
 
-            ensure!(approved_list_exists, "Only approved addresses can call this method");
+            // Check approve
+            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {
+                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));
+                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
+                appoved_transfer = opt_item.is_some();
+                ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");
+            }
 
-            Self::check_white_list(collection_id, from.clone())?;
-            Self::check_white_list(collection_id, recipient.clone())?;
+            // Transfer permissions check
+            let target_collection = <Collection<T>>::get(collection_id);
+            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 
+                "Only item owner, collection owner and admins can modify items");
 
-            let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));
-            let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
-            ensure!(opt_item.is_some(), "No approve found");
-            ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");
+            if target_collection.access == AccessMode::WhiteList {
+                Self::check_white_list(collection_id, sender.clone())?;
+                Self::check_white_list(collection_id, recipient.clone())?;
+            }
 
             // remove approve
             let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))
                 .into_iter().filter(|i| i.approved != sender.clone()).collect();
             <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);
 
-            let target_collection = <Collection<T>>::get(collection_id);
 
             match target_collection.mode
             {
@@ -1115,6 +1165,80 @@
         Ok(())
     }
 
+    fn init_collection(item: &CollectionType<T::AccountId>){
+
+                // check params
+                assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");
+                assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");
+                assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");
+                assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");
+    
+                // Generate next collection ID
+                let next_id = CreatedCollectionCount::get()
+                    .checked_add(1)
+                    .expect("collection id error");
+    
+                CreatedCollectionCount::put(next_id);  
+    }
+
+    fn init_nft_token(item: &NftItemType<T::AccountId>){
+
+        let current_index = <ItemListIndex>::get(item.collection)
+            .checked_add(1)
+            .expect("Item list index id error");
+
+        let item_owner = item.owner.clone();
+        let collection_id = item.collection.clone();
+        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();
+
+        <ItemListIndex>::insert(collection_id, current_index);
+
+        // Update balance
+        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
+            .checked_add(1)
+            .unwrap();
+        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
+    }
+
+    fn init_fungible_token(item: &FungibleItemType<T::AccountId>){
+
+        let current_index = <ItemListIndex>::get(item.collection)
+            .checked_add(1)
+            .expect("Item list index id error");
+        let owner = item.owner.clone();
+        let value = item.value as u64;
+
+        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
+
+        <ItemListIndex>::insert(item.collection, current_index);
+
+        // Update balance
+        let new_balance = <Balance<T>>::get(item.collection, owner.clone())
+            .checked_add(value)
+            .unwrap();
+        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);
+    }
+
+    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){
+
+        let current_index = <ItemListIndex>::get(item.collection)
+            .checked_add(1)
+            .expect("Item list index id error");
+
+        let value = item.owner.first().unwrap().fraction as u64;
+        let owner = item.owner.first().unwrap().owner.clone();
+
+        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
+
+        <ItemListIndex>::insert(item.collection, current_index);
+
+        // Update balance
+        let new_balance = <Balance<T>>::get(item.collection, owner.clone())
+            .checked_add(value)
+            .unwrap();
+        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);
+    }
+
     fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {
         let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
         if list_exists {
@@ -1169,6 +1293,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Economic models
+// #region
 
 /// Fee multiplier.
 pub type Multiplier = FixedU128;
@@ -1363,3 +1488,4 @@
         Ok(())
     }
 }
+// #endregion
\ No newline at end of file
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -3,6 +3,8 @@
 use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
 use frame_support::{assert_noop, assert_ok};
 
+// Use cases tests region
+// #region
 #[test]
 fn create_nft_item() {
     new_test_ext().execute_with(|| {
@@ -330,7 +332,7 @@
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
         assert_eq!(
             TemplateModule::approved(1, (1, 1))[0],
@@ -400,7 +402,7 @@
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
         assert_eq!(
             TemplateModule::approved(1, (1, 1))[0],
@@ -427,7 +429,7 @@
         assert_eq!(
             TemplateModule::approved(1, (1, 1))[0],
             ApprovePermissions {
-                approved: 10,
+                approved: 3,
                 amount: 100000000
             }
         );
@@ -472,7 +474,7 @@
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
         assert_eq!(
             TemplateModule::approved(1, (1, 1))[0],
@@ -499,7 +501,7 @@
         assert_eq!(
             TemplateModule::approved(1, (1, 1))[0],
             ApprovePermissions {
-                approved: 10,
+                approved: 3,
                 amount: 100000000
             }
         );
@@ -955,3 +957,833 @@
         assert_eq!(TemplateModule::balance_count(1, 2), 1);
     });
 }
+
+// #endregion
+
+// Coverage tests region
+// #region
+
+#[test]
+fn owner_can_add_address_to_white_list() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_eq!(TemplateModule::white_list(1)[0], 2);
+    });
+}
+
+#[test]
+fn admin_can_add_address_to_white_list() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3));
+        assert_eq!(TemplateModule::white_list(1)[0], 3);
+    });
+}
+
+#[test]
+fn nonprivileged_user_cannot_add_address_to_white_list() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_noop!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3), "You do not have permissions to modify this collection");
+    });
+}
+
+#[test]
+fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
+    new_test_ext().execute_with(|| {
+
+        let origin1 = Origin::signed(1);
+        assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+    });
+}
+
+#[test]
+fn nobody_can_add_address_to_white_list_of_deleted_collection() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+        assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+    });
+}
+
+// If address is already added to white list, nothing happens
+#[test]
+fn address_is_already_added_to_white_list() {
+    new_test_ext().execute_with(|| {
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_eq!(TemplateModule::white_list(1)[0], 2);
+        assert_eq!(TemplateModule::white_list(1).len(), 1);
+    });
+}
+
+#[test]
+fn owner_can_remove_address_from_white_list() {
+    new_test_ext().execute_with(|| {
+        
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+        assert_eq!(TemplateModule::white_list(1).len(), 0);
+    });
+}
+
+#[test]
+fn admin_can_remove_address_from_white_list() {
+    new_test_ext().execute_with(|| {
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
+        assert_ok!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 3));
+        assert_eq!(TemplateModule::white_list(1).len(), 0);
+    });
+}
+
+#[test]
+fn nonprivileged_user_cannot_remove_address_from_white_list() {
+    new_test_ext().execute_with(|| {
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "You do not have permissions to modify this collection");
+        assert_eq!(TemplateModule::white_list(1)[0], 2);
+    });
+}
+
+#[test]
+fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
+    new_test_ext().execute_with(|| {
+
+        let origin1 = Origin::signed(1);
+        assert_noop!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+    });
+}
+
+#[test]
+fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
+    new_test_ext().execute_with(|| {
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+        assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "This collection does not exist");
+        assert_eq!(TemplateModule::white_list(1).len(), 0);
+    });
+}
+
+// If address is already removed from white list, nothing happens
+#[test]
+fn address_is_already_removed_from_white_list() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+        let origin1 = Origin::signed(1);
+
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+        assert_eq!(TemplateModule::white_list(1).len(), 0);
+    });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be transferred from a non-whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_1() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        assert_noop!(TemplateModule::transfer(
+            origin1.clone(),
+            3,
+            1,
+            1,
+            1
+        ), "Address is not in white list");
+    });
+}
+
+#[test]
+fn white_list_test_2() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        // do approve
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 1));
+
+        assert_noop!(TemplateModule::transfer_from(
+            origin1.clone(),
+            1,
+            3,
+            1,
+            1,
+            1
+        ), "Address is not in white list");
+    });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be transferred to a non-whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_3() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+
+        assert_noop!(TemplateModule::transfer(
+            origin1.clone(),
+            3,
+            1,
+            1,
+            1
+        ), "Address is not in white list");
+    });
+}
+
+#[test]
+fn white_list_test_4() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        // do approve
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+        assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+
+        assert_noop!(TemplateModule::transfer_from(
+            origin1.clone(),
+            1,
+            3,
+            1,
+            1,
+            1
+        ), "Address is not in white list");
+    });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be destroyed by a non-whitelisted address (even if it owned them before enabling WhiteList mode)
+#[test]
+fn white_list_test_5() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_noop!(TemplateModule::burn_item(origin1.clone(), 1, 1), "Address is not in white list");
+    });
+}
+
+// If Public Access mode is set to WhiteList, oken transfers can’t be Approved by a non-whitelisted address (see Approve method).
+#[test]
+fn white_list_test_6() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+
+        // do approve
+        assert_noop!(TemplateModule::approve(origin1.clone(), 1, 1, 1), "Address is not in white list");
+    });
+}
+
+// If Public Access mode is set to WhiteList, tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests) and
+//          tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_7() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        assert_ok!(TemplateModule::transfer(
+            origin1.clone(),
+            2,
+            1,
+            1,
+            1
+        ));
+    });
+}
+
+#[test]
+fn white_list_test_8() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        // do approve
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+        assert_ok!(TemplateModule::transfer_from(
+            origin1.clone(),
+            1,
+            2,
+            1,
+            1,
+            1
+        ));
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by owner.
+#[test]
+fn white_list_test_9() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by admin.
+#[test]
+fn white_list_test_10() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+        assert_ok!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ));
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens cannot be created by non-privileged and white listed address.
+#[test]
+fn white_list_test_11() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        assert_noop!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ), "Collection is not in mint mode");
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-white listed address.
+#[test]
+fn white_list_test_12() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+        assert_noop!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ), "Collection is not in mint mode");
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by owner.
+#[test]
+fn white_list_test_13() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by admin.
+#[test]
+fn white_list_test_14() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+        assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+        assert_ok!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ));
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-white listed address.
+#[test]
+fn white_list_test_15() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+        assert_noop!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ), "Address is not in white list");
+    });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by non-privileged and white listed address.
+#[test]
+fn white_list_test_16() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+
+        assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+        assert_ok!(TemplateModule::create_item(
+            origin2.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            2
+        ));
+    });
+}
+
+// #endregion
\ No newline at end of file
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -53,8 +53,10 @@
 
 pub use timestamp::Call as TimestampCall;
 
-/// Importing a nft pallet
-pub use nft;
+/// Re-export a nft pallet
+/// TODO: Check this re-export. Is this safe and good style?
+extern crate nft;
+pub use nft::*;
 
 /// An index to a block.
 pub type BlockNumber = u32;
@@ -318,7 +320,7 @@
         Balances: balances::{Module, Call, Storage, Config<T>, Event<T>},
         TransactionPayment: transaction_payment::{Module, Storage},
         Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>},
-        Nft: nft::{Module, Call, Storage, Event<T>},
+        Nft: nft::{Module, Call, Config<T>, Storage, Event<T>},
     }
 );