From d16b1c1c96a6d71c423acd4b4dc42d74bc132834 Mon Sep 17 00:00:00 2001 From: str-mv Date: Wed, 06 May 2020 08:02:50 +0000 Subject: [PATCH] Substrate 2 --- --- a/Cargo.lock +++ b/Cargo.lock @@ -2859,6 +2859,7 @@ "sp-core", "sp-io", "sp-runtime", + "sp-std", ] [[package]] --- a/pallets/template/Cargo.toml +++ b/pallets/template/Cargo.toml @@ -17,6 +17,14 @@ package = 'parity-scale-codec' version = '1.3.0' +[dependencies.sp-std] +default-features = false +version = '2.0.0-alpha.6' + +[dependencies.sp-runtime] +default-features = false +version = '2.0.0-alpha.6' + [dependencies.frame-support] default-features = false version = '2.0.0-alpha.6' @@ -32,14 +40,15 @@ default-features = false version = '2.0.0-alpha.6' -[dev-dependencies.sp-runtime] -default-features = false -version = '2.0.0-alpha.6' + [features] default = ['std'] std = [ 'codec/std', 'frame-support/std', 'frame-system/std', + "sp-io/std", + "sp-runtime/std", + 'sp-std/std', ] --- a/pallets/template/src/lib.rs +++ b/pallets/template/src/lib.rs @@ -9,8 +9,14 @@ /// For more guidance on Substrate FRAME, see the example pallet /// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs -use frame_support::{decl_module, decl_storage, decl_event, decl_error, dispatch}; -use frame_system::{self as system, ensure_signed}; +use frame_support::{ + dispatch::DispatchResult, decl_module, decl_storage, decl_event, + weights::{DispatchClass, ClassifyDispatch, WeighData, Weight}, + ensure, +}; +use frame_system::{self as system, ensure_signed }; +use codec::{Encode, Decode}; +use sp_runtime::sp_std::prelude::Vec; #[cfg(test)] mod mock; @@ -18,6 +24,29 @@ #[cfg(test)] mod tests; +#[derive(Encode, Decode, Default, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct CollectionType { + pub owner: AccountId, + pub next_item_id: u64, + pub custom_data_size: u32, +} + +#[derive(Encode, Decode, Default, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct CollectionAdminsType { + pub admin: AccountId, + pub collection_id: u64, +} + +#[derive(Encode, Decode, Default, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct NftItemType { + pub collection: u64, + pub owner: AccountId, + pub data: Vec, +} + /// The pallet's configuration trait. pub trait Trait: system::Trait { // Add other types and constants required to configure this pallet. @@ -30,80 +59,258 @@ decl_storage! { // It is important to update your storage name so that your pallet's // storage items are isolated from other pallets. - // ---------------------------------vvvvvvvvvvvvvv trait Store for Module as TemplateModule { - // Just a dummy storage item. - // Here we are declaring a StorageValue, `Something` as a Option - // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored - Something get(fn something): Option; + + /// Next available collection ID + pub NextCollectionID get(next_collection_id): u64; + + /// Collection map + pub Collection get(collection): map hasher(blake2_128_concat) u64 => CollectionType; + + /// Admins map (collection) + pub AdminList get(admin_list_collection): map hasher(blake2_128_concat) u64 => Vec; + + /// Balance owner per collection map + pub Balance get(balance_count): map hasher(blake2_128_concat) (u64, T::AccountId) => u64; + + /// Item double map (collection) + pub ItemList get(item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; + pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64; } } // The pallet's events decl_event!( pub enum Event where AccountId = ::AccountId { - /// Just a dummy event. - /// Event `Something` is declared with a parameter of the type `u32` and `AccountId` - /// To emit this event, we call the deposit function, from our runtime functions - SomethingStored(u32, AccountId), + Created(u32, AccountId), } ); -// The pallet's errors -decl_error! { - pub enum Error for Module { - /// Value was None - NoneValue, - /// Value reached maximum and cannot be incremented further - StorageOverflow, - } -} - // The pallet's dispatchable functions. decl_module! { /// The module declaration. pub struct Module for enum Call where origin: T::Origin { - // Initializing errors - // this includes information about your errors in the node's metadata. - // it is needed only if you are using errors in your pallet - type Error = Error; // Initializing events // this is needed only if you are using events in your pallet fn deposit_event() = default; - /// Just a dummy entry point. - /// function that can be called by the external world as an extrinsics call - /// takes a parameter of the type `AccountId`, stores it, and emits an event + // Initializing events + // this is needed only if you are using events in your module + // fn deposit_event() = default; + + // Create collection of NFT with given parameters + // + // @param customDataSz size of custom data in each collection item + // returns collection ID + + // Create collection of NFT with given parameters + // + // @param customDataSz size of custom data in each collection item + // returns collection ID #[weight = frame_support::weights::SimpleDispatchInfo::default()] - pub fn do_something(origin, something: u32) -> dispatch::DispatchResult { - // Check it was signed and get the signer. See also: ensure_root and ensure_none + pub fn create_collection(origin, custom_data_sz: u32) -> DispatchResult { + // Anyone can create a collection let who = ensure_signed(origin)?; - // Code to execute when something calls this. - // For example: the following line stores the passed in u32 in the storage - Something::put(something); + // Generate next collection ID + let next_id = Self::next_collection_id(); + ::put(next_id+1); + + // Create new collection + let new_collection = CollectionType { + owner: who, + next_item_id: next_id, + custom_data_size: custom_data_sz, + }; + + // Add new collection to map + >::insert(next_id, new_collection); + + Ok(()) + } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let owner = >::get(collection_id).owner; + ensure!(sender == owner, "You do not own this collection"); + >::remove(collection_id); + + Ok(()) + } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let mut target_collection = >::get(collection_id); + ensure!(sender == target_collection.owner, "You do not own this collection"); + + target_collection.owner = new_owner; + >::insert(collection_id, target_collection); - // Here we are raising the Something event - Self::deposit_event(RawEvent::SomethingStored(something, who)); Ok(()) } - /// Another dummy entry point. - /// takes no parameters, attempts to increment storage value, and possibly throws an error #[weight = frame_support::weights::SimpleDispatchInfo::default()] - pub fn cause_error(origin) -> dispatch::DispatchResult { - // Check it was signed and get the signer. See also: ensure_root and ensure_none - let _who = ensure_signed(origin)?; + pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult { - match Something::get() { - None => Err(Error::::NoneValue)?, - Some(old) => { - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - Something::put(new); - Ok(()) - }, + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let target_collection = >::get(collection_id); + let is_owner = sender == target_collection.owner; + + let no_perm_mes = "You do not have permissions to modify this collection"; + let exists = >::contains_key(collection_id); + + if !is_owner + { + ensure!(exists, no_perm_mes); + ensure!(>::get(collection_id).contains(&sender), no_perm_mes); + } + + let mut admin_arr: Vec = Vec::new(); + if exists + { + admin_arr = >::get(collection_id); + ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role"); + } + + admin_arr.push(new_admin_id); + >::insert(collection_id, admin_arr); + + Ok(()) + } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let target_collection = >::get(collection_id); + let is_owner = sender == target_collection.owner; + + let no_perm_mes = "You do not have permissions to modify this collection"; + let exists = >::contains_key(collection_id); + + if !is_owner + { + ensure!(exists, no_perm_mes); + ensure!(>::get(collection_id).contains(&sender), no_perm_mes); } + + if exists + { + let mut admin_arr = >::get(collection_id); + admin_arr.retain(|i| *i != account_id); + >::insert(collection_id, admin_arr); + } + + Ok(()) } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn create_item(origin, collection_id: u64, properties: Vec) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let target_collection = >::get(collection_id); + ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large"); + let is_owner = sender == target_collection.owner; + + let no_perm_mes = "You do not have permissions to modify this collection"; + let exists = >::contains_key(collection_id); + + if !is_owner + { + ensure!(exists, no_perm_mes); + ensure!(>::get(collection_id).contains(&sender), no_perm_mes); + } + + let new_balance = >::get((collection_id, sender.clone())) + 1; + >::insert((collection_id, sender.clone()), new_balance); + + // Create new item + let new_item = NftItemType { + collection: collection_id, + owner: sender, + data: properties, + }; + + let current_index = ::get(collection_id); + ::insert(collection_id, current_index+1); + >::insert((collection_id, current_index), new_item); + + Ok(()) + } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let target_collection = >::get(collection_id); + let is_owner = sender == target_collection.owner; + + ensure!(>::contains_key((collection_id, item_id)), "Item does not exists"); + let item = >::get((collection_id, item_id)); + + if !is_owner + { + // check if item owner + if item.owner != sender + { + let no_perm_mes = "You do not have permissions to modify this collection"; + + ensure!(>::contains_key(collection_id), no_perm_mes); + ensure!(>::get(collection_id).contains(&sender), no_perm_mes); + } + } + >::remove((collection_id, item_id)); + + Ok(()) + } + + #[weight = frame_support::weights::SimpleDispatchInfo::default()] + pub fn transfer(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult { + + let sender = ensure_signed(origin)?; + ensure!(>::contains_key(collection_id), "This collection does not exist"); + + let target_collection = >::get(collection_id); + let is_owner = sender == target_collection.owner; + + ensure!(>::contains_key((collection_id, item_id)), "Item does not exists"); + let mut item = >::get((collection_id, item_id)); + + if !is_owner + { + // check if item owner + if item.owner != sender + { + let no_perm_mes = "You do not have permissions to modify this collection"; + + ensure!(>::contains_key(collection_id), no_perm_mes); + ensure!(>::get(collection_id).contains(&sender), no_perm_mes); + } + } + >::remove((collection_id, item_id)); + + // change owner + item.owner = new_owner; + >::insert((collection_id, item_id), item); + + Ok(()) + } } -} +} \ No newline at end of file --- a/pallets/template/src/tests.rs +++ b/pallets/template/src/tests.rs @@ -1,26 +1,24 @@ // Tests to be written here - -use crate::{Error, mock::*}; use frame_support::{assert_ok, assert_noop}; -#[test] -fn it_works_for_default_value() { - new_test_ext().execute_with(|| { - // Just a dummy test for the dummy function `do_something` - // calling the `do_something` function with a value 42 - assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); - // asserting that the stored value is equal to what we stored - assert_eq!(TemplateModule::something(), Some(42)); - }); -} +// #[test] +// fn it_works_for_default_value() { +// new_test_ext().execute_with(|| { +// // Just a dummy test for the dummy function `do_something` +// // calling the `do_something` function with a value 42 +// assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); +// // asserting that the stored value is equal to what we stored +// assert_eq!(TemplateModule::something(), Some(42)); +// }); +// } -#[test] -fn correct_error_for_none_value() { - new_test_ext().execute_with(|| { - // Ensure the correct error is thrown on None value - assert_noop!( - TemplateModule::cause_error(Origin::signed(1)), - Error::::NoneValue - ); - }); -} +// #[test] +// fn correct_error_for_none_value() { +// new_test_ext().execute_with(|| { +// // Ensure the correct error is thrown on None value +// assert_noop!( +// TemplateModule::cause_error(Origin::signed(1)), +// Error::::NoneValue +// ); +// }); +// } -- gitstuff