git.delta.rocks / unique-network / refs/commits / 7fa5eef78e38

difftreelog

Merge pull request #5 from usetech-llc/hotfix/collection_count_update

str-mv2020-09-23parents: #f94b085 #6257940.patch.diff
in: master
Hotfix/collection count update

4 files changed

modifiedREADME.mddiffbeforeafterboth
before · README.md
1![Docker build](https://github.com/usetech-llc/nft_parachain/workflows/Docker%20build/badge.svg)23# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the8basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of9a given Collection, and managing their ownership.1011The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a12concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge13about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1415The NFT Chain also provides:1617-   Smart Contracts Pallet and example smart contract that interacts with NFT Runtime18-   ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split19    between multiple users)20-   Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract21    users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection22    Transfer transactions.2324Wider NFT Ecosystem (most of it was developed during Hackusama):2526-   [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)27-   [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)28-   [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2930Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!3132## Hackusama Update3334During the Kusama Hackaphon the following changes were made:3536-   Enabled Smart Contracts Pallet37-   Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)38-   Fixed misc. bugs in NFT Pallet39-   Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.40-   New Features:41    -   Re-Fungible Token Mode42    -   Off-Chain Schema to store token image URLs43    -   Alternative economic model44    -   White Lists and Public Mint Permission45-   Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet46    Blockchain.4748## Application Development4950If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).5152## Building5354Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything55so that we can keep the builds stable.56571. Install Rust:5859```bash60curl https://sh.rustup.rs -sSf | sh61sudo apt-get install libssl-dev pkg-config libclang-dev clang62```63642. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.65663. Install Rust Toolchain 1.44.0:6768```bash69rustup install 1.44.070```71724. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)7374```bash75rustup toolchain list76rustup default 1.44.0-x86_64-unknown-linux-gnu77```78795. Install nightly toolchain and add wasm target for it:8081```bash82rustup toolchain install nightly-2020-05-0183rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu84```85866. Build:8788```bash89cargo build90```9192## Run9394You can start a development chain with:9596```bash97cargo run -- --dev98```99100Detailed logs may be shown by running the node with the following environment variables set:101`RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.102103If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two104validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with105testnet units. Give each node a name and expose them so they are listed on the Polkadot106[telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.107108We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at109`/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from110the `--node-key` value that we specify below:111112```bash113cargo run -- \114  --base-path /tmp/alice \115  --chain=local \116  --alice \117  --node-key 0000000000000000000000000000000000000000000000000000000000000001 \118  --telemetry-url ws://telemetry.polkadot.io:1024 \119  --validator120```121122In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database123stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's124bootnode ID on TCP port 30333:125126```bash127cargo run -- \128  --base-path /tmp/bob \129  --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \130  --chain=local \131  --bob \132  --port 30334 \133  --telemetry-url ws://telemetry.polkadot.io:1024 \134  --validator135```136137Additional CLI usage options are available and may be shown by running `cargo run -- --help`.138139## UI custom types140141```142{143  "Schedule": {144    "version": "u32",145    "put_code_per_byte_cost": "Gas",146    "grow_mem_cost": "Gas",147    "regular_op_cost": "Gas",148    "return_data_per_byte_cost": "Gas",149    "event_data_per_byte_cost": "Gas",150    "event_per_topic_cost": "Gas",151    "event_base_cost": "Gas",152    "call_base_cost": "Gas",153    "instantiate_base_cost": "Gas",154    "dispatch_base_cost": "Gas",155    "sandbox_data_read_cost": "Gas",156    "sandbox_data_write_cost": "Gas",157    "transfer_cost": "Gas",158    "instantiate_cost": "Gas",159    "max_event_topics": "u32",160    "max_stack_height": "u32",161    "max_memory_pages": "u32",162    "max_table_size": "u32",163    "enable_println": "bool",164    "max_subject_len": "u32"165  },166  "CollectionMode": {167    "_enum": {168      "Invalid": null,169      "NFT": "u32",170      "Fungible": "u32",171      "ReFungible": "(u32, u32)"172    }173  },174  "Ownership": {175    "Owner": "AccountId",176    "Fraction": "u128"177  },178  "FungibleItemType": {179    "Collection": "u64",180    "Owner": "AccountId",181    "Value": "u128"182  },183  "ReFungibleItemType": {184    "Collection": "u64",185    "Owner": "Vec<Ownership>",186    "Data": "Vec<u8>"187  },188  "NftItemType": {189    "Collection": "u64",190    "Owner": "AccountId",191    "Data": "Vec<u8>"192  },193  "Ownership": {194    "owner": "AccountId",195    "fraction": "u128"196  },197  "ReFungibleItemType": {198    "Collection": "u64",199    "Owner": "Vec<Ownership<AccountId>>",200    "Data": "Vec<u8>"201  },202  "CollectionType": {203    "Owner": "AccountId",204    "Mode": "CollectionMode",205    "Access": "u8",206    "DecimalPoints": "u32",207    "Name": "Vec<u16>",208    "Description": "Vec<u16>",209    "TokenPrefix": "Vec<u8>",210    "CustomDataSize": "u32",211    "OffchainSchema": "Vec<u8>",212    "Sponsor": "AccountId",213    "UnconfirmedSponsor": "AccountId"214  },215  "RawData": "Vec<u8>",216  "Address": "AccountId",217  "LookupSource": "AccountId",218  "Weight": "u64"219}220```
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -97,6 +97,7 @@
     pub description: Vec<u16>, // 256 include null escape char
     pub token_prefix: Vec<u8>, // 16 include null escape char
     pub custom_data_size: u32,
+    pub mint_mode: bool,
     pub offchain_schema: Vec<u8>,
     pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
     pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
@@ -178,9 +179,6 @@
         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>;
-
-        // Active vesting list
-        // pub VestingList get(fn vesting): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => VestingItem<T::AccountId, T::Moment>;
 
         /// Index list
         pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
@@ -209,7 +207,7 @@
 
         fn on_initialize(now: T::BlockNumber) -> Weight {
 
-            if ChainVersion::get() == 0
+            if ChainVersion::get() < 2
             {
                 let value = NextCollectionID::get();
                 CreatedCollectionCount::put(value);
@@ -224,11 +222,11 @@
         // @param customDataSz size of custom data in each collection item
         // returns collection ID
         #[weight = 0]
-        pub fn create_collection(   origin,
-                                    collection_name: Vec<u16>,
-                                    collection_description: Vec<u16>,
-                                    token_prefix: Vec<u8>,
-                                    mode: CollectionMode) -> DispatchResult {
+        pub fn create_collection(origin,
+                                 collection_name: Vec<u16>,
+                                 collection_description: Vec<u16>,
+                                 token_prefix: Vec<u8>,
+                                 mode: CollectionMode) -> DispatchResult {
 
             // Anyone can create a collection
             let who = ensure_signed(origin)?;
@@ -271,6 +269,7 @@
                 owner: who.clone(),
                 name: name,
                 mode: mode.clone(),
+                mint_mode: false,
                 access: AccessMode::Normal,
                 description: description,
                 decimal_points: decimal_points,
@@ -309,6 +308,73 @@
         }
 
         #[weight = 0]
+        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+
+            let sender = ensure_signed(origin)?;
+            Self::check_owner_or_admin_permissions(collection_id, sender)?;
+
+            let mut white_list_collection: Vec<T::AccountId>;
+            if <WhiteList<T>>::contains_key(collection_id) {
+                white_list_collection = <WhiteList<T>>::get(collection_id);
+                if !white_list_collection.contains(&address.clone())
+                {
+                    white_list_collection.push(address.clone());
+                }
+            }
+            else {
+                white_list_collection = Vec::new();
+                white_list_collection.push(address.clone());
+            }
+
+            <WhiteList<T>>::insert(collection_id, white_list_collection);
+            Ok(())
+        }
+
+        #[weight = 0]
+        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+
+            let sender = ensure_signed(origin)?;
+            Self::check_owner_or_admin_permissions(collection_id, sender)?;
+
+            if <WhiteList<T>>::contains_key(collection_id) {
+                let mut white_list_collection = <WhiteList<T>>::get(collection_id);
+                if white_list_collection.contains(&address.clone())
+                {
+                    white_list_collection.retain(|i| *i != address.clone());
+                    <WhiteList<T>>::insert(collection_id, white_list_collection);
+                }
+            }
+
+            Ok(())
+        }
+
+        #[weight = 0]
+        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult
+        {
+            let sender = ensure_signed(origin)?;
+
+            Self::check_owner_permissions(collection_id, sender)?;
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            target_collection.access = mode;
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        }
+
+        #[weight = 0]
+        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult
+        {
+            let sender = ensure_signed(origin)?;
+
+            Self::check_owner_permissions(collection_id, sender)?;
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            target_collection.mint_mode = mint_permission;
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        }
+
+        #[weight = 0]
         pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
@@ -405,9 +471,17 @@
         pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
+            Self::collection_exists(collection_id)?;
             let target_collection = <Collection<T>>::get(collection_id);
-            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
 
+            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+                if target_collection.mint_mode == false {
+                    panic!("Collection is not in mint mode");
+                }
+
+                Self::check_white_list(collection_id, owner.clone())?;
+            }
+
             match target_collection.mode
             {
                 CollectionMode::NFT(_) => {
@@ -469,10 +543,13 @@
         pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {
 
             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
             {
-                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
+                if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {  
+                    Self::check_white_list(collection_id, sender.clone())?;
+                }
             }
             let target_collection = <Collection<T>>::get(collection_id);
 
@@ -494,11 +571,15 @@
         pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
-            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");
 
+            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
+            if !item_owner {
+                Self::check_white_list(collection_id, sender.clone())?;
+                Self::check_white_list(collection_id, recipient.clone())?;
+            }
+
             let target_collection = <Collection<T>>::get(collection_id);
 
-            // TODO: implement other modes
             match target_collection.mode
             {
                 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,
@@ -518,7 +599,10 @@
             // amount param stub
             let amount = 100000000;
 
-            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");
+            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
+            if !item_owner {
+                Self::check_white_list(collection_id, approved.clone())?;
+            }
 
             let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
             if list_exists {
@@ -547,6 +631,9 @@
             let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone()));
             if approved_list_exists
             {
+                Self::check_white_list(collection_id, from.clone())?;
+                Self::check_white_list(collection_id, recipient.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());
                 ensure!(opt_item.is_some(), "No approve found");
@@ -559,7 +646,7 @@
             }
             else
             {
-                Self::check_owner_or_admin_permissions(collection_id, sender)?;
+                panic!("Only approved addresses can call this method");
             }
 
             let target_collection = <Collection<T>>::get(collection_id);
@@ -675,11 +762,7 @@
         Ok(())
     }
 
-    fn burn_refungible_item(
-        collection_id: u64,
-        item_id: u64,
-        owner: T::AccountId,
-    ) -> DispatchResult {
+    fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {
         ensure!(
             <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
             "Item does not exists"
@@ -770,26 +853,31 @@
         Ok(())
     }
 
-    fn check_owner_or_admin_permissions(
-        collection_id: u64,
-        subject: T::AccountId,
-    ) -> DispatchResult {
-        Self::collection_exists(collection_id)?;
+    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {
 
         let target_collection = <Collection<T>>::get(collection_id);
-        let is_owner = subject == target_collection.owner;
-
-        let no_perm_mes = "You do not have permissions to modify this collection";
+        let mut result: bool = subject == target_collection.owner;
         let exists = <AdminList<T>>::contains_key(collection_id);
 
-        if !is_owner {
-            ensure!(exists, no_perm_mes);
-            ensure!(
-                <AdminList<T>>::get(collection_id).contains(&subject),
-                no_perm_mes
-            );
+        if !result & exists {
+            if <AdminList<T>>::get(collection_id).contains(&subject) {
+                result = true
+            }
         }
-        Ok(())
+
+        result
+    }
+
+    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {
+        
+        Self::collection_exists(collection_id)?;
+        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());
+
+        if result == true {
+            Ok(())
+        } else {
+            panic!("You do not have permissions to modify this collection")
+        }
     }
 
     fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {
@@ -812,6 +900,21 @@
         }
     }
 
+    fn check_white_list(collection_id: u64, address: T::AccountId) -> DispatchResult {
+
+        let mes = "Address is not in white list";
+        if <WhiteList<T>>::contains_key(collection_id){
+            let wl = <WhiteList<T>>::get(collection_id);
+            if !wl.contains(&address.clone()) {
+                panic!(mes);
+            }
+        }
+        else {
+            panic!(mes);
+        }
+        Ok(())
+    }
+
     fn transfer_fungible(
         collection_id: u64,
         item_id: u64,
@@ -819,6 +922,12 @@
         owner: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
+
+        ensure!(
+            <FungibleItemList<T>>::contains_key(collection_id, item_id),
+            "Item not exists"
+        );
+
         let full_item = <FungibleItemList<T>>::get(collection_id, item_id);
         let amount = full_item.value;
 
@@ -903,6 +1012,12 @@
         owner: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
+
+        ensure!(
+            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
+            "Item not exists"
+        );
+
         let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);
         let item = full_item
             .owner
@@ -983,6 +1098,12 @@
         sender: T::AccountId,
         new_owner: T::AccountId,
     ) -> DispatchResult {
+    
+        ensure!(
+            <NftItemList<T>>::contains_key(collection_id, item_id),
+            "Item not exists"
+        );
+
         let mut item = <NftItemList<T>>::get(collection_id, item_id);
 
         ensure!(
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,6 +1,6 @@
 // Tests to be written here
 use crate::mock::*;
-use crate::{ApprovePermissions, CollectionMode, Ownership};
+use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
 use frame_support::{assert_noop, assert_ok};
 
 #[test]
@@ -321,10 +321,11 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_noop!(
-            TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
-            "You do not have permissions to modify this collection"
-        );
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        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::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -390,10 +391,11 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1000);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_noop!(
-            TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
-            "You do not have permissions to modify this collection"
-        );
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        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::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -461,10 +463,11 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1000);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
-        assert_noop!(
-            TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
-            "You do not have permissions to modify this collection"
-        );
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        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::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -573,7 +576,6 @@
         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(),
@@ -583,7 +585,7 @@
         ));
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::create_item(
-            origin2.clone(),
+            origin1.clone(),
             1,
             [1, 2, 3].to_vec(),
             1
@@ -614,7 +616,6 @@
         let mode: CollectionMode = CollectionMode::Fungible(3);
 
         let origin1 = Origin::signed(1);
-        let origin2 = Origin::signed(2);
         assert_ok!(TemplateModule::create_collection(
             origin1.clone(),
             col_name1.clone(),
@@ -624,7 +625,7 @@
         ));
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::create_item(
-            origin2.clone(),
+            origin1.clone(),
             1,
             [].to_vec(),
             1
@@ -661,6 +662,11 @@
             token_prefix1.clone(),
             mode
         ));
+        
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        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_collection_admin(origin1.clone(), 1, 2));
         assert_ok!(TemplateModule::create_item(
             origin2.clone(),
@@ -928,6 +934,13 @@
         // approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
         assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+
+        assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+        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::add_to_white_list(origin1.clone(), 1, 3));
+
         assert_ok!(TemplateModule::transfer_from(
             origin2.clone(),
             1,
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -111,7 +111,7 @@
     spec_name: create_runtime_str!("nft"),
     impl_name: create_runtime_str!("nft"),
     authoring_version: 1,
-    spec_version: 1,
+    spec_version: 2,
     impl_version: 1,
     apis: RUNTIME_API_VERSIONS,
     transaction_version: 1,