From 73335ba4b18180a42823f1108f8256575484bb40 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 26 Nov 2020 12:08:54 +0000 Subject: [PATCH] Merge pull request #10 from usetech-llc/feature/NFTPAR-96 Feature/nftpar-96 --- --- a/pallets/nft/src/benchmarking.rs +++ b/pallets/nft/src/benchmarking.rs @@ -165,7 +165,7 @@ let token_prefix1: Vec = b"token_prefix1".to_vec(); let mode: CollectionMode = CollectionMode::NFT; let caller: T::AccountId = T::AccountId::from(whitelisted_caller()); - let nft_data = CreateNftData { + let mut nft_data = CreateNftData { const_data: vec![], variable_data: vec![] }; @@ -173,7 +173,7 @@ nft_data.const_data.push(10); nft_data.variable_data.push(10); } - let mut data = CreateItemData::NFT(nft_data); + let data = CreateItemData::NFT(nft_data); Nft::::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?; }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data) --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -10,7 +10,7 @@ pub use frame_support::{ construct_runtime, decl_event, decl_module, decl_storage, dispatch::DispatchResult, - ensure, parameter_types, fail, + ensure, fail, parameter_types, traits::{ Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, WithdrawReason, @@ -22,7 +22,6 @@ }, IsSubType, StorageValue, }; -// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; use frame_system::{self as system, ensure_signed, ensure_root}; use sp_runtime::sp_std::prelude::Vec; @@ -489,7 +488,7 @@ } /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money. - /// + /// /// # Permissions /// /// * Collection Owner. @@ -832,79 +831,55 @@ pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult { let sender = ensure_signed(origin)?; + Self::collection_exists(collection_id)?; + let target_collection = >::get(collection_id); - if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) { - ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection."); - Self::check_white_list(collection_id, &owner)?; - Self::check_white_list(collection_id, &sender)?; - } + Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?; + Self::validate_create_item_args(&target_collection, &data)?; + Self::create_item_no_validation(collection_id, &target_collection, owner, data)?; - match target_collection.mode - { - CollectionMode::NFT => { - if let CreateItemData::NFT(data) = data { - // check sizes - ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit."); - ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit."); - - // Create nft item - let item = NftItemType { - collection: collection_id, - owner: owner, - const_data: data.const_data.clone(), - variable_data: data.variable_data.clone() - }; - - Self::add_nft_item(item)?; - - } else { - fail!("Not NFT item data used to mint in NFT collection."); - } - }, - CollectionMode::Fungible(_) => { - if let CreateItemData::Fungible(_) = data { - - let item = FungibleItemType { - collection: collection_id, - owner: owner, - value: (10 as u128).pow(target_collection.decimal_points) - }; - - Self::add_fungible_item(item)?; - } else { - fail!("Not Fungible item data used to mint in Fungible collection."); - } - }, - CollectionMode::ReFungible(_) => { - if let CreateItemData::ReFungible(data) = data { - - // check sizes - ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit."); - ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit."); - - let mut owner_list = Vec::new(); - let value = (10 as u128).pow(target_collection.decimal_points); - owner_list.push(Ownership {owner: owner.clone(), fraction: value}); - - let item = ReFungibleItemType { - collection: collection_id, - owner: owner_list, - const_data: data.const_data.clone(), - variable_data: data.variable_data.clone() - }; - - Self::add_refungible_item(item)?; - } else { - fail!("Not Re Fungible item data used to mint in Re Fungible collection."); - } - }, - _ => { ensure!(1 == 0,"Unexpected collection type."); } - }; + Ok(()) + } + + /// This method creates multiple instances of NFT Collection created with CreateCollection method. + /// + /// # Permissions + /// + /// * Collection Owner. + /// * Collection Admin. + /// * Anyone if + /// * White List is enabled, and + /// * Address is added to white list, and + /// * MintPermission is enabled (see SetMintPermission method) + /// + /// # Arguments + /// + /// * collection_id: ID of the collection. + /// + /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item]. + /// + /// * owner: Address, initial owner of the NFT. + #[weight = T::WeightInfo::create_item(items_data.into_iter() + .map(|data| { data.len() }) + .sum())] + pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec) -> DispatchResult { + + ensure!(items_data.len() > 0, "Length of items properties must be greater than 0."); + let sender = ensure_signed(origin)?; + + Self::collection_exists(collection_id)?; + let target_collection = >::get(collection_id); + + Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?; - // call event - Self::deposit_event(RawEvent::ItemCreated(collection_id, ::get(collection_id))); + for data in &items_data { + Self::validate_create_item_args(&target_collection, data)?; + } + for data in &items_data { + Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?; + } Ok(()) } @@ -1134,7 +1109,7 @@ Ok(()) } - + /// Set off-chain data schema. /// /// # Permissions @@ -1305,6 +1280,97 @@ } impl Module { + + fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult { + + if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) { + ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection"); + Self::check_white_list(collection_id, owner)?; + Self::check_white_list(collection_id, sender)?; + } + + Ok(()) + } + + fn validate_create_item_args(target_collection: &CollectionType, data: &CreateItemData) -> DispatchResult { + match target_collection.mode + { + CollectionMode::NFT => { + if let CreateItemData::NFT(data) = data { + // check sizes + ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit."); + ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit."); + } else { + fail!("Not NFT item data used to mint in NFT collection."); + } + }, + CollectionMode::Fungible(_) => { + if let CreateItemData::Fungible(_) = data { + } else { + fail!("Not Fungible item data used to mint in Fungible collection."); + } + }, + CollectionMode::ReFungible(_) => { + if let CreateItemData::ReFungible(data) = data { + + // check sizes + ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit."); + ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit."); + } else { + fail!("Not Re Fungible item data used to mint in Re Fungible collection."); + } + }, + _ => { fail!("Unexpected collection type."); } + }; + + Ok(()) + } + + fn create_item_no_validation(collection_id: u64, collection: &CollectionType, owner: T::AccountId, data: CreateItemData) -> DispatchResult { + match data + { + CreateItemData::NFT(data) => { + let item = NftItemType { + collection: collection_id, + owner, + const_data: data.const_data, + variable_data: data.variable_data + }; + + Self::add_nft_item(item)?; + }, + CreateItemData::Fungible(_) => { + let item = FungibleItemType { + collection: collection_id, + owner, + value: (10 as u128).pow(collection.decimal_points) + }; + + Self::add_fungible_item(item)?; + }, + CreateItemData::ReFungible(data) => { + let mut owner_list = Vec::new(); + let value = (10 as u128).pow(collection.decimal_points); + owner_list.push(Ownership {owner: owner.clone(), fraction: value}); + + let item = ReFungibleItemType { + collection: collection_id, + owner: owner_list, + const_data: data.const_data, + variable_data: data.variable_data + }; + + Self::add_refungible_item(item)?; + } + }; + + + // call event + Self::deposit_event(RawEvent::ItemCreated(collection_id, ::get(collection_id))); + + Ok(()) + } + fn add_fungible_item(item: FungibleItemType) -> DispatchResult { let current_index = ::get(item.collection) .checked_add(1) @@ -2235,5 +2301,3 @@ } // #endregion - - --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -87,6 +87,32 @@ }); } +// Use cases tests region +// #region +#[test] +fn create_nft_multiple_items() { + new_test_ext().execute_with(|| { + default_limits(); + + create_test_collection(&CollectionMode::NFT, 1); + + let origin1 = Origin::signed(1); + + let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()]; + + assert_ok!(TemplateModule::create_multiple_items( + origin1.clone(), + 1, + 1, + items_data.clone().into_iter().map(|d| { d.into() }).collect() + )); + for (index, data) in items_data.iter().enumerate() { + assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).const_data.to_vec(), data.const_data); + assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).variable_data.to_vec(), data.variable_data); + } + }); +} + #[test] fn create_refungible_item() { new_test_ext().execute_with(|| { @@ -114,6 +140,39 @@ } #[test] +fn create_multiple_refungible_items() { + new_test_ext().execute_with(|| { + default_limits(); + + create_test_collection(&CollectionMode::ReFungible(3), 1); + + let origin1 = Origin::signed(1); + + let items_data = vec![default_re_fungible_data(), default_re_fungible_data(), default_re_fungible_data()]; + + assert_ok!(TemplateModule::create_multiple_items( + origin1.clone(), + 1, + 1, + items_data.clone().into_iter().map(|d| { d.into() }).collect() + )); + for (index, data) in items_data.iter().enumerate() { + + let item = TemplateModule::refungible_item_id(1, (index + 1) as u64); + assert_eq!(item.const_data.to_vec(), data.const_data); + assert_eq!(item.variable_data.to_vec(), data.variable_data); + assert_eq!( + item.owner[0], + Ownership { + owner: 1, + fraction: 1000 + } + ); + } + }); +} + +#[test] fn create_fungible_item() { new_test_ext().execute_with(|| { default_limits(); @@ -124,12 +183,36 @@ create_test_item(collection_id, &data.into()); assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1); - assert_eq!(TemplateModule::balance_count(1, 1), 1000); - assert_eq!(TemplateModule::address_tokens(1, 1), [1]); }); } #[test] +fn create_multiple_fungible_items() { + new_test_ext().execute_with(|| { + default_limits(); + + create_test_collection(&CollectionMode::Fungible(3), 1); + + let origin1 = Origin::signed(1); + + let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()]; + + assert_ok!(TemplateModule::create_multiple_items( + origin1.clone(), + 1, + 1, + items_data.clone().into_iter().map(|d| { d.into() }).collect() + )); + + for (index, _) in items_data.iter().enumerate() { + assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as u64).owner, 1); + } + assert_eq!(TemplateModule::balance_count(1, 1), 3000); + assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]); + }); +} + +#[test] fn transfer_fungible_item() { new_test_ext().execute_with(|| { default_limits(); @@ -1333,7 +1416,7 @@ assert_noop!( TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()), - "Public minting is not allowed for this collection." + "Public minting is not allowed for this collection" ); }); } @@ -1362,7 +1445,7 @@ assert_noop!( TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()), - "Public minting is not allowed for this collection." + "Public minting is not allowed for this collection" ); }); } -- gitstuff