difftreelog
Substrate nft v2. 4 tests added
in: master
16 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2512,25 +2512,12 @@
]
[[package]]
-name = "nix"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
-dependencies = [
- "bitflags",
- "cc",
- "cfg-if",
- "libc",
- "void",
-]
-
-[[package]]
-name = "node-template"
+name = "nft"
version = "2.0.0-alpha.6"
dependencies = [
"futures 0.3.4",
"log",
- "node-template-runtime",
+ "nft-runtime",
"sc-basic-authorship",
"sc-cli",
"sc-client",
@@ -2553,7 +2540,7 @@
]
[[package]]
-name = "node-template-runtime"
+name = "nft-runtime"
version = "2.0.0-alpha.6"
dependencies = [
"frame-executive",
@@ -2562,9 +2549,9 @@
"pallet-aura",
"pallet-balances",
"pallet-grandpa",
+ "pallet-nft",
"pallet-randomness-collective-flip",
"pallet-sudo",
- "pallet-template",
"pallet-timestamp",
"pallet-transaction-payment",
"parity-scale-codec",
@@ -2585,6 +2572,19 @@
]
[[package]]
+name = "nix"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "void",
+]
+
+[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2802,6 +2802,19 @@
]
[[package]]
+name = "pallet-nft"
+version = "2.0.0-alpha.6"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
name = "pallet-randomness-collective-flip"
version = "2.0.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2844,19 +2857,6 @@
"frame-system",
"parity-scale-codec",
"serde",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
-[[package]]
-name = "pallet-template"
-version = "2.0.0-alpha.6"
-dependencies = [
- "frame-support",
- "frame-system",
- "parity-scale-codec",
- "sp-core",
"sp-io",
"sp-runtime",
"sp-std",
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[workspace]
members = [
'node',
- 'pallets/template',
+ 'pallets/nft',
'runtime',
]
[profile.release]
node/Cargo.tomldiffbeforeafterboth--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -1,11 +1,11 @@
[package]
authors = ['Anonymous']
build = 'build.rs'
-description = 'Substrate Node template'
+description = 'Substrate Node nft'
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicense'
-name = 'node-template'
+name = 'nft'
repository = 'https://github.com/paritytech/substrate/'
version = '2.0.0-alpha.6'
@@ -17,7 +17,7 @@
log = '0.4.8'
structopt = '0.3.8'
-[dependencies.node-template-runtime]
+[dependencies.nft-runtime]
path = '../runtime'
version = '2.0.0-alpha.6'
@@ -75,4 +75,4 @@
version = '2.0.0-alpha.6'
[[bin]]
-name = 'node-template'
+name = 'nft'
node/src/chain_spec.rsdiffbeforeafterboth--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,5 +1,5 @@
use sp_core::{Pair, Public, sr25519};
-use node_template_runtime::{
+use nft_runtime::{
AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig,
SudoConfig, SystemConfig, WASM_BINARY, Signature
};
node/src/command.rsdiffbeforeafterboth--- a/node/src/command.rs
+++ b/node/src/command.rs
@@ -74,7 +74,7 @@
runner.run_node(
service::new_light,
service::new_full,
- node_template_runtime::VERSION
+ nft_runtime::VERSION
)
}
}
node/src/service.rsdiffbeforeafterboth1//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.23use std::sync::Arc;4use std::time::Duration;5use sc_client::LongestChain;6use sc_client_api::ExecutorProvider;7use node_template_runtime::{self, opaque::Block, RuntimeApi};8use sc_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder};9use sp_inherents::InherentDataProviders;10use sc_executor::native_executor_instance;11pub use sc_executor::NativeExecutor;12use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};13use sc_finality_grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider, StorageAndProofProvider};1415// Our native executor instance.16native_executor_instance!(17 pub Executor,18 node_template_runtime::api::dispatch,19 node_template_runtime::native_version,20);2122/// Starts a `ServiceBuilder` for a full service.23///24/// Use this macro if you don't actually need the full service, but just the builder in order to25/// be able to perform chain operations.26macro_rules! new_full_start {27 ($config:expr) => {{28 use std::sync::Arc;29 let mut import_setup = None;30 let inherent_data_providers = sp_inherents::InherentDataProviders::new();3132 let builder = sc_service::ServiceBuilder::new_full::<33 node_template_runtime::opaque::Block, node_template_runtime::RuntimeApi, crate::service::Executor34 >($config)?35 .with_select_chain(|_config, backend| {36 Ok(sc_client::LongestChain::new(backend.clone()))37 })?38 .with_transaction_pool(|config, client, _fetcher| {39 let pool_api = sc_transaction_pool::FullChainApi::new(client.clone());40 Ok(sc_transaction_pool::BasicPool::new(config, std::sync::Arc::new(pool_api)))41 })?42 .with_import_queue(|_config, client, mut select_chain, _transaction_pool| {43 let select_chain = select_chain.take()44 .ok_or_else(|| sc_service::Error::SelectChainRequired)?;4546 let (grandpa_block_import, grandpa_link) =47 sc_finality_grandpa::block_import(client.clone(), &(client.clone() as Arc<_>), select_chain)?;4849 let aura_block_import = sc_consensus_aura::AuraBlockImport::<_, _, _, AuraPair>::new(50 grandpa_block_import.clone(), client.clone(),51 );5253 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair>(54 sc_consensus_aura::slot_duration(&*client)?,55 aura_block_import,56 Some(Box::new(grandpa_block_import.clone())),57 None,58 client,59 inherent_data_providers.clone(),60 )?;6162 import_setup = Some((grandpa_block_import, grandpa_link));6364 Ok(import_queue)65 })?;6667 (builder, import_setup, inherent_data_providers)68 }}69}7071/// Builds a new service for a full client.72pub fn new_full(config: Configuration)73 -> Result<impl AbstractService, ServiceError>74{75 let role = config.role.clone();76 let force_authoring = config.force_authoring;77 let name = config.network.node_name.clone();78 let disable_grandpa = config.disable_grandpa;7980 let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);8182 let (block_import, grandpa_link) =83 import_setup.take()84 .expect("Link Half and Block Import are present for Full Services or setup failed before. qed");8586 let service = builder87 .with_finality_proof_provider(|client, backend| {88 // GenesisAuthoritySetProvider is implemented for StorageAndProofProvider89 let provider = client as Arc<dyn StorageAndProofProvider<_, _>>;90 Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _)91 })?92 .build()?;9394 if role.is_authority() {95 let proposer =96 sc_basic_authorship::ProposerFactory::new(service.client(), service.transaction_pool());9798 let client = service.client();99 let select_chain = service.select_chain()100 .ok_or(ServiceError::SelectChainRequired)?;101102 let can_author_with =103 sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone());104105 let aura = sc_consensus_aura::start_aura::<_, _, _, _, _, AuraPair, _, _, _>(106 sc_consensus_aura::slot_duration(&*client)?,107 client,108 select_chain,109 block_import,110 proposer,111 service.network(),112 inherent_data_providers.clone(),113 force_authoring,114 service.keystore(),115 can_author_with,116 )?;117118 // the AURA authoring task is considered essential, i.e. if it119 // fails we take down the service with it.120 service.spawn_essential_task("aura", aura);121 }122123 // if the node isn't actively participating in consensus then it doesn't124 // need a keystore, regardless of which protocol we use below.125 let keystore = if role.is_authority() {126 Some(service.keystore())127 } else {128 None129 };130131 let grandpa_config = sc_finality_grandpa::Config {132 // FIXME #1578 make this available through chainspec133 gossip_duration: Duration::from_millis(333),134 justification_period: 512,135 name: Some(name),136 observer_enabled: false,137 keystore,138 is_authority: role.is_network_authority(),139 };140141 let enable_grandpa = !disable_grandpa;142 if enable_grandpa {143 // start the full GRANDPA voter144 // NOTE: non-authorities could run the GRANDPA observer protocol, but at145 // this point the full voter should provide better guarantees of block146 // and vote data availability than the observer. The observer has not147 // been tested extensively yet and having most nodes in a network run it148 // could lead to finality stalls.149 let grandpa_config = sc_finality_grandpa::GrandpaParams {150 config: grandpa_config,151 link: grandpa_link,152 network: service.network(),153 inherent_data_providers: inherent_data_providers.clone(),154 telemetry_on_connect: Some(service.telemetry_on_connect_stream()),155 voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(),156 prometheus_registry: service.prometheus_registry()157 };158159 // the GRANDPA voter task is considered infallible, i.e.160 // if it fails we take down the service with it.161 service.spawn_essential_task(162 "grandpa-voter",163 sc_finality_grandpa::run_grandpa_voter(grandpa_config)?164 );165 } else {166 sc_finality_grandpa::setup_disabled_grandpa(167 service.client(),168 &inherent_data_providers,169 service.network(),170 )?;171 }172173 Ok(service)174}175176/// Builds a new service for a light client.177pub fn new_light(config: Configuration)178 -> Result<impl AbstractService, ServiceError>179{180 let inherent_data_providers = InherentDataProviders::new();181182 ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?183 .with_select_chain(|_config, backend| {184 Ok(LongestChain::new(backend.clone()))185 })?186 .with_transaction_pool(|config, client, fetcher| {187 let fetcher = fetcher188 .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?;189190 let pool_api = sc_transaction_pool::LightChainApi::new(client.clone(), fetcher.clone());191 let pool = sc_transaction_pool::BasicPool::with_revalidation_type(192 config, Arc::new(pool_api), sc_transaction_pool::RevalidationType::Light,193 );194 Ok(pool)195 })?196 .with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {197 let fetch_checker = fetcher198 .map(|fetcher| fetcher.checker().clone())199 .ok_or_else(|| "Trying to start light import queue without active fetch checker")?;200 let grandpa_block_import = sc_finality_grandpa::light_block_import(201 client.clone(),202 backend,203 &(client.clone() as Arc<_>),204 Arc::new(fetch_checker),205 )?;206 let finality_proof_import = grandpa_block_import.clone();207 let finality_proof_request_builder =208 finality_proof_import.create_finality_proof_request_builder();209210 let import_queue = sc_consensus_aura::import_queue::<_, _, _, AuraPair>(211 sc_consensus_aura::slot_duration(&*client)?,212 grandpa_block_import,213 None,214 Some(Box::new(finality_proof_import)),215 client,216 inherent_data_providers.clone(),217 )?;218219 Ok((import_queue, finality_proof_request_builder))220 })?221 .with_finality_proof_provider(|client, backend| {222 // GenesisAuthoritySetProvider is implemented for StorageAndProofProvider223 let provider = client as Arc<dyn StorageAndProofProvider<_, _>>;224 Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _)225 })?226 .build()227}pallets/nft/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+authors = ['Anonymous']
+description = 'FRAME pallet nft'
+edition = '2018'
+homepage = 'https://substrate.dev'
+license = 'Unlicense'
+name = 'pallet-nft'
+repository = 'https://github.com/paritytech/substrate/'
+version = '2.0.0-alpha.6'
+
+[package.metadata.docs.rs]
+targets = ['x86_64-unknown-linux-gnu']
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+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'
+
+[dependencies.frame-system]
+default-features = false
+version = '2.0.0-alpha.6'
+[dev-dependencies.sp-core]
+default-features = false
+version = '2.0.0-alpha.6'
+
+[dev-dependencies.sp-io]
+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',
+]
pallets/nft/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/lib.rs
@@ -0,0 +1,315 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+/// A FRAME pallet template with necessary imports
+
+/// Feel free to remove or edit this file as needed.
+/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs
+/// If you remove this file, you can remove those references
+
+/// 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::{
+ dispatch::DispatchResult, decl_module, decl_storage, decl_event,
+ ensure,
+};
+use frame_system::{self as system, ensure_signed };
+use codec::{Encode, Decode};
+use sp_runtime::sp_std::prelude::Vec;
+
+#[cfg(test)]
+mod mock;
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Encode, Decode, Default, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct CollectionType<AccountId> {
+ 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<AccountId> {
+ pub admin: AccountId,
+ pub collection_id: u64,
+}
+
+#[derive(Encode, Decode, Default, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Debug))]
+pub struct NftItemType<AccountId> {
+ pub collection: u64,
+ pub owner: AccountId,
+ pub data: Vec<u8>,
+}
+
+/// The pallet's configuration trait.
+pub trait Trait: system::Trait {
+ // Add other types and constants required to configure this pallet.
+
+ /// The overarching event type.
+ type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+}
+
+// This pallet's storage items.
+decl_storage! {
+ // It is important to update your storage name so that your pallet's
+ // storage items are isolated from other pallets.
+ trait Store for Module<T: Trait> as TemplateModule {
+
+ /// Next available collection ID
+ pub NextCollectionID get(next_collection_id): u64 = 1;
+
+ /// Collection map
+ pub Collection get(collection): map hasher(blake2_128_concat) u64 => CollectionType<T::AccountId>;
+
+ /// Admins map (collection)
+ pub AdminList get(admin_list_collection): map hasher(blake2_128_concat) u64 => Vec<T::AccountId>;
+
+ /// 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<T::AccountId>;
+ pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64;
+ }
+}
+
+// The pallet's events
+decl_event!(
+ pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
+ Created(u32, AccountId),
+ }
+);
+
+// The pallet's dispatchable functions.
+decl_module! {
+ /// The module declaration.
+ pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+
+ // Initializing events
+ // this is needed only if you are using events in your pallet
+ fn deposit_event() = default;
+
+ // Initializing events
+ // this is needed only if you are using events in your module
+ // fn deposit_event<T>() = 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 create_collection(origin, custom_data_sz: u32) -> DispatchResult {
+ // Anyone can create a collection
+ let who = ensure_signed(origin)?;
+
+ // Generate next collection ID
+ let next_id = Self::next_collection_id();
+ <NextCollectionID>::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
+ <Collection<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let owner = <Collection<T>>::get(collection_id).owner;
+ ensure!(sender == owner, "You do not own this collection");
+ <Collection<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let mut target_collection = <Collection<T>>::get(collection_id);
+ ensure!(sender == target_collection.owner, "You do not own this collection");
+
+ target_collection.owner = new_owner;
+ <Collection<T>>::insert(collection_id, target_collection);
+
+ Ok(())
+ }
+
+ #[weight = frame_support::weights::SimpleDispatchInfo::default()]
+ pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {
+
+ let sender = ensure_signed(origin)?;
+ ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
+
+ if !is_owner
+ {
+ ensure!(exists, no_perm_mes);
+ ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
+ }
+
+ let mut admin_arr: Vec<T::AccountId> = Vec::new();
+ if exists
+ {
+ admin_arr = <AdminList<T>>::get(collection_id);
+ ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");
+ }
+
+ admin_arr.push(new_admin_id);
+ <AdminList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
+
+ if !is_owner
+ {
+ ensure!(exists, no_perm_mes);
+ ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
+ }
+
+ if exists
+ {
+ let mut admin_arr = <AdminList<T>>::get(collection_id);
+ admin_arr.retain(|i| *i != account_id);
+ <AdminList<T>>::insert(collection_id, admin_arr);
+ }
+
+ Ok(())
+ }
+
+ #[weight = frame_support::weights::SimpleDispatchInfo::default()]
+ pub fn create_item(origin, collection_id: u64, properties: Vec<u8>) -> DispatchResult {
+
+ let sender = ensure_signed(origin)?;
+ ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
+
+ if !is_owner
+ {
+ ensure!(exists, no_perm_mes);
+ ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
+ }
+
+ let new_balance = <Balance<T>>::get((collection_id, sender.clone())) + 1;
+ <Balance<T>>::insert((collection_id, sender.clone()), new_balance);
+
+ // Create new item
+ let new_item = NftItemType {
+ collection: collection_id,
+ owner: sender,
+ data: properties,
+ };
+
+ let current_index = <ItemListIndex>::get(collection_id);
+ <ItemListIndex>::insert(collection_id, current_index+1);
+ <ItemList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let target_collection = <Collection<T>>::get(collection_id);
+ let is_owner = sender == target_collection.owner;
+
+ ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");
+ let item = <ItemList<T>>::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!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);
+ ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
+ }
+ }
+ <ItemList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+ let target_collection = <Collection<T>>::get(collection_id);
+ let is_owner = sender == target_collection.owner;
+
+ ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");
+ let mut item = <ItemList<T>>::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!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);
+ ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
+ }
+ }
+ <ItemList<T>>::remove((collection_id, item_id));
+
+ // change owner
+ item.owner = new_owner;
+ <ItemList<T>>::insert((collection_id, item_id), item);
+
+ Ok(())
+ }
+ }
+}
\ No newline at end of file
pallets/nft/src/mock.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/mock.rs
@@ -0,0 +1,56 @@
+// Creating mock runtime here
+
+use crate::{Module, Trait};
+use sp_core::H256;
+use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
+use sp_runtime::{
+ traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill,
+};
+use frame_system as system;
+
+impl_outer_origin! {
+ pub enum Origin for Test {}
+}
+
+// For testing the pallet, we construct most of a mock runtime. This means
+// first constructing a configuration type (`Test`) which `impl`s each of the
+// configuration traits of pallets we want to use.
+#[derive(Clone, Eq, PartialEq)]
+pub struct Test;
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+ pub const MaximumBlockWeight: Weight = 1024;
+ pub const MaximumBlockLength: u32 = 2 * 1024;
+ pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
+}
+impl system::Trait for Test {
+ type Origin = Origin;
+ type Call = ();
+ type Index = u64;
+ type BlockNumber = u64;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = u64;
+ type Lookup = IdentityLookup<Self::AccountId>;
+ type Header = Header;
+ type Event = ();
+ type BlockHashCount = BlockHashCount;
+ type MaximumBlockWeight = MaximumBlockWeight;
+ type MaximumBlockLength = MaximumBlockLength;
+ type AvailableBlockRatio = AvailableBlockRatio;
+ type Version = ();
+ type ModuleToIndex = ();
+ type AccountData = ();
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+}
+impl Trait for Test {
+ type Event = ();
+}
+pub type TemplateModule = Module<Test>;
+
+// This function basically just builds a genesis storage key/value store according to
+// our desired mockup.
+pub fn new_test_ext() -> sp_io::TestExternalities {
+ system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
+}
pallets/nft/src/tests.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/tests.rs
@@ -0,0 +1,84 @@
+// Tests to be written here
+use crate::{ mock::*};
+use frame_support::{assert_ok, assert_noop};
+
+#[test]
+fn create_collection_test() {
+ new_test_ext().execute_with(|| {
+ let size = 1024;
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(origin1.clone(), size));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+ });
+}
+
+#[test]
+fn change_collection_owner() {
+ new_test_ext().execute_with(|| {
+ let size = 1024;
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(origin1.clone(), size));
+ assert_ok!(TemplateModule::change_collection_owner(origin1.clone(), 1, 2));
+ assert_eq!(TemplateModule::collection(1).owner, 2);
+ });
+}
+
+#[test]
+fn destroy_collection() {
+ new_test_ext().execute_with(|| {
+ let size = 1024;
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(origin1.clone(), size));
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ });
+}
+
+#[test]
+fn create_item() {
+ new_test_ext().execute_with(|| {
+ let size = 1024;
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(origin1.clone(), size));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::create_item(origin2.clone(), 1, [1,1,1].to_vec()));
+
+ // check balance (collection with id = 1, user id = 2)
+ assert_eq!(TemplateModule::balance_count((1, 2)), 1);
+ });
+}
+
+// #[test]
+// fn burn_item() {
+// new_test_ext().execute_with(|| {
+// let size = 1024;
+// let origin1 = Origin::signed(1);
+// let origin2 = Origin::signed(2);
+
+// assert_ok!(TemplateModule::create_collection(origin1.clone(), size));
+// assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+// assert_ok!(TemplateModule::create_item(origin2.clone(), 1, [1,1,1].to_vec()));
+
+// // check balance (collection with id = 1, user id = 2)
+// assert_eq!(TemplateModule::balance_count((1, 2)), 1);
+
+// // burn item
+// assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+// assert_noop!(TemplateModule::burn_item(origin1.clone(), 1, 1), "Item does not exists");
+// });
+// }
+
+// #[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::<Test>::NoneValue
+// );
+// });
+// }
pallets/template/Cargo.tomldiffbeforeafterboth--- a/pallets/template/Cargo.toml
+++ /dev/null
@@ -1,54 +0,0 @@
-[package]
-authors = ['Anonymous']
-description = 'FRAME pallet template'
-edition = '2018'
-homepage = 'https://substrate.dev'
-license = 'Unlicense'
-name = 'pallet-template'
-repository = 'https://github.com/paritytech/substrate/'
-version = '2.0.0-alpha.6'
-
-[package.metadata.docs.rs]
-targets = ['x86_64-unknown-linux-gnu']
-
-[dependencies.codec]
-default-features = false
-features = ['derive']
-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'
-
-[dependencies.frame-system]
-default-features = false
-version = '2.0.0-alpha.6'
-[dev-dependencies.sp-core]
-default-features = false
-version = '2.0.0-alpha.6'
-
-[dev-dependencies.sp-io]
-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',
-]
pallets/template/src/lib.rsdiffbeforeafterboth--- a/pallets/template/src/lib.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-#![cfg_attr(not(feature = "std"), no_std)]
-
-/// A FRAME pallet template with necessary imports
-
-/// Feel free to remove or edit this file as needed.
-/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs
-/// If you remove this file, you can remove those references
-
-/// 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::{
- 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;
-
-#[cfg(test)]
-mod tests;
-
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
-pub struct CollectionType<AccountId> {
- 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<AccountId> {
- pub admin: AccountId,
- pub collection_id: u64,
-}
-
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
-pub struct NftItemType<AccountId> {
- pub collection: u64,
- pub owner: AccountId,
- pub data: Vec<u8>,
-}
-
-/// The pallet's configuration trait.
-pub trait Trait: system::Trait {
- // Add other types and constants required to configure this pallet.
-
- /// The overarching event type.
- type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
-}
-
-// This pallet's storage items.
-decl_storage! {
- // It is important to update your storage name so that your pallet's
- // storage items are isolated from other pallets.
- trait Store for Module<T: Trait> as TemplateModule {
-
- /// Next available collection ID
- pub NextCollectionID get(next_collection_id): u64;
-
- /// Collection map
- pub Collection get(collection): map hasher(blake2_128_concat) u64 => CollectionType<T::AccountId>;
-
- /// Admins map (collection)
- pub AdminList get(admin_list_collection): map hasher(blake2_128_concat) u64 => Vec<T::AccountId>;
-
- /// 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<T::AccountId>;
- pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64;
- }
-}
-
-// The pallet's events
-decl_event!(
- pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
- Created(u32, AccountId),
- }
-);
-
-// The pallet's dispatchable functions.
-decl_module! {
- /// The module declaration.
- pub struct Module<T: Trait> for enum Call where origin: T::Origin {
-
- // Initializing events
- // this is needed only if you are using events in your pallet
- fn deposit_event() = default;
-
- // Initializing events
- // this is needed only if you are using events in your module
- // fn deposit_event<T>() = 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 create_collection(origin, custom_data_sz: u32) -> DispatchResult {
- // Anyone can create a collection
- let who = ensure_signed(origin)?;
-
- // Generate next collection ID
- let next_id = Self::next_collection_id();
- <NextCollectionID>::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
- <Collection<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let owner = <Collection<T>>::get(collection_id).owner;
- ensure!(sender == owner, "You do not own this collection");
- <Collection<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let mut target_collection = <Collection<T>>::get(collection_id);
- ensure!(sender == target_collection.owner, "You do not own this collection");
-
- target_collection.owner = new_owner;
- <Collection<T>>::insert(collection_id, target_collection);
-
- Ok(())
- }
-
- #[weight = frame_support::weights::SimpleDispatchInfo::default()]
- pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {
-
- let sender = ensure_signed(origin)?;
- ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
-
- if !is_owner
- {
- ensure!(exists, no_perm_mes);
- ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
- }
-
- let mut admin_arr: Vec<T::AccountId> = Vec::new();
- if exists
- {
- admin_arr = <AdminList<T>>::get(collection_id);
- ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");
- }
-
- admin_arr.push(new_admin_id);
- <AdminList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
-
- if !is_owner
- {
- ensure!(exists, no_perm_mes);
- ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
- }
-
- if exists
- {
- let mut admin_arr = <AdminList<T>>::get(collection_id);
- admin_arr.retain(|i| *i != account_id);
- <AdminList<T>>::insert(collection_id, admin_arr);
- }
-
- Ok(())
- }
-
- #[weight = frame_support::weights::SimpleDispatchInfo::default()]
- pub fn create_item(origin, collection_id: u64, properties: Vec<u8>) -> DispatchResult {
-
- let sender = ensure_signed(origin)?;
- ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let target_collection = <Collection<T>>::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 = <AdminList<T>>::contains_key(collection_id);
-
- if !is_owner
- {
- ensure!(exists, no_perm_mes);
- ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
- }
-
- let new_balance = <Balance<T>>::get((collection_id, sender.clone())) + 1;
- <Balance<T>>::insert((collection_id, sender.clone()), new_balance);
-
- // Create new item
- let new_item = NftItemType {
- collection: collection_id,
- owner: sender,
- data: properties,
- };
-
- let current_index = <ItemListIndex>::get(collection_id);
- <ItemListIndex>::insert(collection_id, current_index+1);
- <ItemList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let target_collection = <Collection<T>>::get(collection_id);
- let is_owner = sender == target_collection.owner;
-
- ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");
- let item = <ItemList<T>>::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!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);
- ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
- }
- }
- <ItemList<T>>::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!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
-
- let target_collection = <Collection<T>>::get(collection_id);
- let is_owner = sender == target_collection.owner;
-
- ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");
- let mut item = <ItemList<T>>::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!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);
- ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);
- }
- }
- <ItemList<T>>::remove((collection_id, item_id));
-
- // change owner
- item.owner = new_owner;
- <ItemList<T>>::insert((collection_id, item_id), item);
-
- Ok(())
- }
- }
-}
\ No newline at end of file
pallets/template/src/mock.rsdiffbeforeafterboth--- a/pallets/template/src/mock.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-// Creating mock runtime here
-
-use crate::{Module, Trait};
-use sp_core::H256;
-use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
-use sp_runtime::{
- traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill,
-};
-use frame_system as system;
-
-impl_outer_origin! {
- pub enum Origin for Test {}
-}
-
-// For testing the pallet, we construct most of a mock runtime. This means
-// first constructing a configuration type (`Test`) which `impl`s each of the
-// configuration traits of pallets we want to use.
-#[derive(Clone, Eq, PartialEq)]
-pub struct Test;
-parameter_types! {
- pub const BlockHashCount: u64 = 250;
- pub const MaximumBlockWeight: Weight = 1024;
- pub const MaximumBlockLength: u32 = 2 * 1024;
- pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
-}
-impl system::Trait for Test {
- type Origin = Origin;
- type Call = ();
- type Index = u64;
- type BlockNumber = u64;
- type Hash = H256;
- type Hashing = BlakeTwo256;
- type AccountId = u64;
- type Lookup = IdentityLookup<Self::AccountId>;
- type Header = Header;
- type Event = ();
- type BlockHashCount = BlockHashCount;
- type MaximumBlockWeight = MaximumBlockWeight;
- type MaximumBlockLength = MaximumBlockLength;
- type AvailableBlockRatio = AvailableBlockRatio;
- type Version = ();
- type ModuleToIndex = ();
- type AccountData = ();
- type OnNewAccount = ();
- type OnKilledAccount = ();
-}
-impl Trait for Test {
- type Event = ();
-}
-pub type TemplateModule = Module<Test>;
-
-// This function basically just builds a genesis storage key/value store according to
-// our desired mockup.
-pub fn new_test_ext() -> sp_io::TestExternalities {
- system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
-}
pallets/template/src/tests.rsdiffbeforeafterboth--- a/pallets/template/src/tests.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Tests to be written here
-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 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::<Test>::NoneValue
-// );
-// });
-// }
runtime/Cargo.tomldiffbeforeafterboth--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -95,10 +95,10 @@
package = 'frame-system'
version = '2.0.0-alpha.6'
-[dependencies.template]
+[dependencies.nft]
default-features = false
-package = 'pallet-template'
-path = '../pallets/template'
+package = 'pallet-nft'
+path = '../pallets/nft'
version = '2.0.0-alpha.6'
[dependencies.timestamp]
@@ -120,7 +120,7 @@
edition = '2018'
homepage = 'https://substrate.dev'
license = 'Unlicense'
-name = 'node-template-runtime'
+name = 'nft-runtime'
repository = 'https://github.com/paritytech/substrate/'
version = '2.0.0-alpha.6'
@@ -154,5 +154,5 @@
'system/std',
'timestamp/std',
'transaction-payment/std',
- 'template/std',
+ 'nft/std',
]
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -38,7 +38,7 @@
};
/// Importing a template pallet
-pub use template;
+pub use nft;
/// An index to a block.
pub type BlockNumber = u32;
@@ -92,8 +92,8 @@
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
- spec_name: create_runtime_str!("node-template"),
- impl_name: create_runtime_str!("node-template"),
+ spec_name: create_runtime_str!("nft"),
+ impl_name: create_runtime_str!("nft"),
authoring_version: 1,
spec_version: 1,
impl_version: 1,
@@ -222,7 +222,7 @@
}
/// Used for the module template in `./template.rs`
-impl template::Trait for Runtime {
+impl nft::Trait for Runtime {
type Event = Event;
}
@@ -241,7 +241,7 @@
TransactionPayment: transaction_payment::{Module, Storage},
Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>},
// Used for the module template in `./template.rs`
- TemplateModule: template::{Module, Call, Storage, Event<T>},
+ Nft: nft::{Module, Call, Storage, Event<T>},
}
);