difftreelog
Merge pull request #3 from usetech-llc/feature/white_list_tests
in: master
Feature/white list tests
4 files changed
node/src/chain_spec.rsdiffbeforeafterboth--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,8 +1,9 @@
-use nft_runtime::{
- AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
- SystemConfig, WASM_BINARY,
-};
-use nft_runtime::{ContractsConfig, ContractsSchedule};
+// use nft_runtime::{
+// AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
+// SystemConfig, WASM_BINARY,
+// };
+// use nft_runtime::{ContractsConfig, ContractsSchedule, NftConfig, CollectionType};
+use nft_runtime::*;
use sc_service::ChainType;
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_core::{sr25519, Pair, Public};
@@ -128,6 +129,25 @@
.collect(),
}),
sudo: Some(SudoConfig { key: root_key }),
+ nft: Some(NftConfig {
+ collection: vec![(1, CollectionType {
+ owner: get_account_id_from_seed::<sr25519::Public>("Alice"),
+ mode: CollectionMode::NFT(50),
+ access: AccessMode::Normal,
+ decimal_points: 0,
+ name: vec!(),
+ description: vec!(),
+ token_prefix: vec!(),
+ custom_data_size: 50,
+ mint_mode: false,
+ offchain_schema: vec!(),
+ sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
+ unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
+ })],
+ nft_item_id: vec!(),
+ fungible_item_id: vec!(),
+ refungible_item_id: vec!(),
+ }),
contracts: Some(ContractsConfig {
current_schedule: ContractsSchedule {
enable_println,
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1,7 +1,8 @@
#![cfg_attr(not(feature = "std"), no_std)]
-/// For more guidance on Substrate FRAME, see the example pallet
-/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs
+#[cfg(feature = "std")]
+pub use serde::*;
+
use codec::{Decode, Encode};
pub use frame_support::{
construct_runtime, decl_event, decl_module, decl_storage,
@@ -32,7 +33,6 @@
},
FixedPointOperand, FixedU128,
};
-use sp_std::prelude::*;
#[cfg(test)]
mod mock;
@@ -40,7 +40,11 @@
#[cfg(test)]
mod tests;
-#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]
+// Structs
+// #region
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum CollectionMode {
Invalid,
// custom data size
@@ -62,7 +66,8 @@
}
}
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum AccessMode {
Normal,
WhiteList,
@@ -79,15 +84,15 @@
}
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct Ownership<AccountId> {
pub owner: AccountId,
pub fraction: u128,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct CollectionType<AccountId> {
pub owner: AccountId,
pub mode: CollectionMode,
@@ -103,46 +108,46 @@
pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct CollectionAdminsType<AccountId> {
pub admin: AccountId,
pub collection_id: u64,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct NftItemType<AccountId> {
pub collection: u64,
pub owner: AccountId,
pub data: Vec<u8>,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct FungibleItemType<AccountId> {
pub collection: u64,
pub owner: AccountId,
pub value: u128,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ReFungibleItemType<AccountId> {
pub collection: u64,
pub owner: Vec<Ownership<AccountId>>,
pub data: Vec<u8>,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ApprovePermissions<AccountId> {
pub approved: AccountId,
pub amount: u64,
}
-#[derive(Encode, Decode, Default, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Debug))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct VestingItem<AccountId, Moment> {
pub sender: AccountId,
pub recipient: AccountId,
@@ -156,6 +161,8 @@
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
+// #endregion
+
decl_storage! {
trait Store for Module<T: Trait> as Nft {
@@ -165,7 +172,7 @@
ChainVersion: u64;
ItemListIndex: map hasher(blake2_128_concat) u64 => u64;
- pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;
+ pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;
pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;
pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;
@@ -176,9 +183,9 @@
pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;
/// Item collections
- pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
- pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
- pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
+ pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
+ pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
+ pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
/// Index list
pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
@@ -187,6 +194,26 @@
pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;
}
+ add_extra_genesis {
+ build(|config: &GenesisConfig<T>| {
+ // Modification of storage
+ for (_num, _c) in &config.collection {
+ <Module<T>>::init_collection(_c);
+ }
+
+ for (_num, _q, _i) in &config.nft_item_id {
+ <Module<T>>::init_nft_token(_i);
+ }
+
+ for (_num, _q, _i) in &config.fungible_item_id {
+ <Module<T>>::init_fungible_token(_i);
+ }
+
+ for (_num, _q, _i) in &config.refungible_item_id {
+ <Module<T>>::init_refungible_token(_i);
+ }
+ })
+ }
}
decl_event!(
@@ -541,14 +568,16 @@
let sender = ensure_signed(origin)?;
Self::collection_exists(collection_id)?;
- let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
- if !item_owner
- {
- if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
- Self::check_white_list(collection_id, sender.clone())?;
- }
+
+ // Transfer permissions check
+ let target_collection = <Collection<T>>::get(collection_id);
+ ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+ Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
+ "Only item owner, collection owner and admins can modify item");
+
+ if target_collection.access == AccessMode::WhiteList {
+ Self::check_white_list(collection_id, sender.clone())?;
}
- let target_collection = <Collection<T>>::get(collection_id);
match target_collection.mode
{
@@ -568,10 +597,18 @@
pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {
let sender = ensure_signed(origin)?;
- Self::check_white_list(collection_id, sender.clone())?;
- Self::check_white_list(collection_id, recipient.clone())?;
+
+ // Transfer permissions check
let target_collection = <Collection<T>>::get(collection_id);
+ ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+ Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
+ "Only item owner, collection owner and admins can modify item");
+ if target_collection.access == AccessMode::WhiteList {
+ Self::check_white_list(collection_id, sender.clone())?;
+ Self::check_white_list(collection_id, recipient.clone())?;
+ }
+
match target_collection.mode
{
CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,
@@ -588,14 +625,20 @@
let sender = ensure_signed(origin)?;
- // amount param stub
- let amount = 100000000;
+ // Transfer permissions check
+ let target_collection = <Collection<T>>::get(collection_id);
+ ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
+ Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
+ "Only item owner, collection owner and admins can approve");
- let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
- if !item_owner {
+ if target_collection.access == AccessMode::WhiteList {
+ Self::check_white_list(collection_id, sender.clone())?;
Self::check_white_list(collection_id, approved.clone())?;
}
+ // amount param stub
+ let amount = 100000000;
+
let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
if list_exists {
@@ -620,24 +663,31 @@
pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {
let sender = ensure_signed(origin)?;
- let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone()));
+ let mut appoved_transfer = false;
- ensure!(approved_list_exists, "Only approved addresses can call this method");
+ // Check approve
+ if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {
+ let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));
+ let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
+ appoved_transfer = opt_item.is_some();
+ ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");
+ }
- Self::check_white_list(collection_id, from.clone())?;
- Self::check_white_list(collection_id, recipient.clone())?;
+ // Transfer permissions check
+ let target_collection = <Collection<T>>::get(collection_id);
+ ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
+ "Only item owner, collection owner and admins can modify items");
- let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));
- let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
- ensure!(opt_item.is_some(), "No approve found");
- ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");
+ if target_collection.access == AccessMode::WhiteList {
+ Self::check_white_list(collection_id, sender.clone())?;
+ Self::check_white_list(collection_id, recipient.clone())?;
+ }
// remove approve
let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))
.into_iter().filter(|i| i.approved != sender.clone()).collect();
<ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);
- let target_collection = <Collection<T>>::get(collection_id);
match target_collection.mode
{
@@ -1115,6 +1165,80 @@
Ok(())
}
+ fn init_collection(item: &CollectionType<T::AccountId>){
+
+ // check params
+ assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");
+ assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");
+ assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");
+ assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");
+
+ // Generate next collection ID
+ let next_id = CreatedCollectionCount::get()
+ .checked_add(1)
+ .expect("collection id error");
+
+ CreatedCollectionCount::put(next_id);
+ }
+
+ fn init_nft_token(item: &NftItemType<T::AccountId>){
+
+ let current_index = <ItemListIndex>::get(item.collection)
+ .checked_add(1)
+ .expect("Item list index id error");
+
+ let item_owner = item.owner.clone();
+ let collection_id = item.collection.clone();
+ Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();
+
+ <ItemListIndex>::insert(collection_id, current_index);
+
+ // Update balance
+ let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
+ .checked_add(1)
+ .unwrap();
+ <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
+ }
+
+ fn init_fungible_token(item: &FungibleItemType<T::AccountId>){
+
+ let current_index = <ItemListIndex>::get(item.collection)
+ .checked_add(1)
+ .expect("Item list index id error");
+ let owner = item.owner.clone();
+ let value = item.value as u64;
+
+ Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
+
+ <ItemListIndex>::insert(item.collection, current_index);
+
+ // Update balance
+ let new_balance = <Balance<T>>::get(item.collection, owner.clone())
+ .checked_add(value)
+ .unwrap();
+ <Balance<T>>::insert(item.collection, owner.clone(), new_balance);
+ }
+
+ fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){
+
+ let current_index = <ItemListIndex>::get(item.collection)
+ .checked_add(1)
+ .expect("Item list index id error");
+
+ let value = item.owner.first().unwrap().fraction as u64;
+ let owner = item.owner.first().unwrap().owner.clone();
+
+ Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
+
+ <ItemListIndex>::insert(item.collection, current_index);
+
+ // Update balance
+ let new_balance = <Balance<T>>::get(item.collection, owner.clone())
+ .checked_add(value)
+ .unwrap();
+ <Balance<T>>::insert(item.collection, owner.clone(), new_balance);
+ }
+
fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {
let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
if list_exists {
@@ -1169,6 +1293,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Economic models
+// #region
/// Fee multiplier.
pub type Multiplier = FixedU128;
@@ -1363,3 +1488,4 @@
Ok(())
}
}
+// #endregion
\ No newline at end of file
pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -3,6 +3,8 @@
use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
use frame_support::{assert_noop, assert_ok};
+// Use cases tests region
+// #region
#[test]
fn create_nft_item() {
new_test_ext().execute_with(|| {
@@ -330,7 +332,7 @@
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
@@ -400,7 +402,7 @@
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
@@ -427,7 +429,7 @@
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
ApprovePermissions {
- approved: 10,
+ approved: 3,
amount: 100000000
}
);
@@ -472,7 +474,7 @@
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 10, 1, 1));
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
@@ -499,7 +501,7 @@
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
ApprovePermissions {
- approved: 10,
+ approved: 3,
amount: 100000000
}
);
@@ -955,3 +957,833 @@
assert_eq!(TemplateModule::balance_count(1, 2), 1);
});
}
+
+// #endregion
+
+// Coverage tests region
+// #region
+
+#[test]
+fn owner_can_add_address_to_white_list() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_eq!(TemplateModule::white_list(1)[0], 2);
+ });
+}
+
+#[test]
+fn admin_can_add_address_to_white_list() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3));
+ assert_eq!(TemplateModule::white_list(1)[0], 3);
+ });
+}
+
+#[test]
+fn nonprivileged_user_cannot_add_address_to_white_list() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_noop!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3), "You do not have permissions to modify this collection");
+ });
+}
+
+#[test]
+fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
+ new_test_ext().execute_with(|| {
+
+ let origin1 = Origin::signed(1);
+ assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+ });
+}
+
+#[test]
+fn nobody_can_add_address_to_white_list_of_deleted_collection() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ assert_noop!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+ });
+}
+
+// If address is already added to white list, nothing happens
+#[test]
+fn address_is_already_added_to_white_list() {
+ new_test_ext().execute_with(|| {
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_eq!(TemplateModule::white_list(1)[0], 2);
+ assert_eq!(TemplateModule::white_list(1).len(), 1);
+ });
+}
+
+#[test]
+fn owner_can_remove_address_from_white_list() {
+ new_test_ext().execute_with(|| {
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+ assert_eq!(TemplateModule::white_list(1).len(), 0);
+ });
+}
+
+#[test]
+fn admin_can_remove_address_from_white_list() {
+ new_test_ext().execute_with(|| {
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
+ assert_ok!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 3));
+ assert_eq!(TemplateModule::white_list(1).len(), 0);
+ });
+}
+
+#[test]
+fn nonprivileged_user_cannot_remove_address_from_white_list() {
+ new_test_ext().execute_with(|| {
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "You do not have permissions to modify this collection");
+ assert_eq!(TemplateModule::white_list(1)[0], 2);
+ });
+}
+
+#[test]
+fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
+ new_test_ext().execute_with(|| {
+
+ let origin1 = Origin::signed(1);
+ assert_noop!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2), "This collection does not exist");
+ });
+}
+
+#[test]
+fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
+ new_test_ext().execute_with(|| {
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ assert_noop!(TemplateModule::remove_from_white_list(origin2.clone(), 1, 2), "This collection does not exist");
+ assert_eq!(TemplateModule::white_list(1).len(), 0);
+ });
+}
+
+// If address is already removed from white list, nothing happens
+#[test]
+fn address_is_already_removed_from_white_list() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+ let origin1 = Origin::signed(1);
+
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+ assert_eq!(TemplateModule::white_list(1).len(), 0);
+ });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be transferred from a non-whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_1() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ assert_noop!(TemplateModule::transfer(
+ origin1.clone(),
+ 3,
+ 1,
+ 1,
+ 1
+ ), "Address is not in white list");
+ });
+}
+
+#[test]
+fn white_list_test_2() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ // do approve
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+ assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 1));
+
+ assert_noop!(TemplateModule::transfer_from(
+ origin1.clone(),
+ 1,
+ 3,
+ 1,
+ 1,
+ 1
+ ), "Address is not in white list");
+ });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be transferred to a non-whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_3() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+
+ assert_noop!(TemplateModule::transfer(
+ origin1.clone(),
+ 3,
+ 1,
+ 1,
+ 1
+ ), "Address is not in white list");
+ });
+}
+
+#[test]
+fn white_list_test_4() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ // do approve
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+ assert_ok!(TemplateModule::remove_from_white_list(origin1.clone(), 1, 2));
+
+ assert_noop!(TemplateModule::transfer_from(
+ origin1.clone(),
+ 1,
+ 3,
+ 1,
+ 1,
+ 1
+ ), "Address is not in white list");
+ });
+}
+
+// If Public Access mode is set to WhiteList, tokens can’t be destroyed by a non-whitelisted address (even if it owned them before enabling WhiteList mode)
+#[test]
+fn white_list_test_5() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_noop!(TemplateModule::burn_item(origin1.clone(), 1, 1), "Address is not in white list");
+ });
+}
+
+// If Public Access mode is set to WhiteList, oken transfers can’t be Approved by a non-whitelisted address (see Approve method).
+#[test]
+fn white_list_test_6() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+
+ // do approve
+ assert_noop!(TemplateModule::approve(origin1.clone(), 1, 1, 1), "Address is not in white list");
+ });
+}
+
+// If Public Access mode is set to WhiteList, tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests) and
+// tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests)
+#[test]
+fn white_list_test_7() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ assert_ok!(TemplateModule::transfer(
+ origin1.clone(),
+ 2,
+ 1,
+ 1,
+ 1
+ ));
+ });
+}
+
+#[test]
+fn white_list_test_8() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+ assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ // do approve
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+
+ assert_ok!(TemplateModule::transfer_from(
+ origin1.clone(),
+ 1,
+ 2,
+ 1,
+ 1,
+ 1
+ ));
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by owner.
+#[test]
+fn white_list_test_9() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by admin.
+#[test]
+fn white_list_test_10() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+ assert_ok!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ));
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens cannot be created by non-privileged and white listed address.
+#[test]
+fn white_list_test_11() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ assert_noop!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ), "Collection is not in mint mode");
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens cannot be created by non-privileged and non-white listed address.
+#[test]
+fn white_list_test_12() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, false));
+
+ assert_noop!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ), "Collection is not in mint mode");
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by owner.
+#[test]
+fn white_list_test_13() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+ assert_ok!(TemplateModule::create_item(
+ origin1.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 1
+ ));
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by admin.
+#[test]
+fn white_list_test_14() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+
+ assert_ok!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ));
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens cannot be created by non-privileged and non-white listed address.
+#[test]
+fn white_list_test_15() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+
+ assert_noop!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ), "Address is not in white list");
+ });
+}
+
+// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by non-privileged and white listed address.
+#[test]
+fn white_list_test_16() {
+ new_test_ext().execute_with(|| {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT(2000);
+
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
+ assert_ok!(TemplateModule::create_collection(
+ origin1.clone(),
+ col_name1.clone(),
+ col_desc1.clone(),
+ token_prefix1.clone(),
+ mode
+ ));
+
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+
+ assert_ok!(TemplateModule::create_item(
+ origin2.clone(),
+ 1,
+ [1, 2, 3].to_vec(),
+ 2
+ ));
+ });
+}
+
+// #endregion
\ No newline at end of file
runtime/src/lib.rsdiffbeforeafterboth1//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.23#![cfg_attr(not(feature = "std"), no_std)]4// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.5#![recursion_limit = "256"]67// Make the WASM binary available.8#[cfg(feature = "std")]9include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1011use contracts_rpc_runtime_api::ContractExecResult;12use grandpa::fg_primitives;13use grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};14use sp_api::impl_runtime_apis;15use sp_consensus_aura::sr25519::AuthorityId as AuraId;16use sp_core::{crypto::KeyTypeId, OpaqueMetadata};17use sp_runtime::{18 create_runtime_str, generic, impl_opaque_keys,19 traits::{20 BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating,21 Verify,22 },23 transaction_validity::{TransactionSource, TransactionValidity},24 ApplyExtrinsicResult, MultiSignature,25};26use sp_std::prelude::*;27#[cfg(feature = "std")]28use sp_version::NativeVersion;29use sp_version::RuntimeVersion;3031// A few exports that help ease life for downstream crates.32pub use balances::Call as BalancesCall;33pub use contracts::Schedule as ContractsSchedule;34pub use frame_support::{35 construct_runtime,36 dispatch::DispatchResult,37 parameter_types,38 traits::{39 Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness,40 WithdrawReason,41 },42 weights::{43 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},44 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,45 WeightToFeePolynomial,46 },47 StorageValue,48};49#[cfg(any(feature = "std", test))]50pub use sp_runtime::BuildStorage;51use sp_runtime::Perbill;52use system::{self as system};5354pub use timestamp::Call as TimestampCall;5556/// Importing a nft pallet57pub use nft;5859/// An index to a block.60pub type BlockNumber = u32;6162/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.63pub type Signature = MultiSignature;6465/// Some way of identifying an account on the chain. We intentionally make it equivalent66/// to the public key of our transaction signing scheme.67pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;6869/// The type for looking up accounts. We don't expect more than 4 billion of them, but you70/// never know...71pub type AccountIndex = u32;7273/// Balance of an account.74pub type Balance = u128;7576/// Index of a transaction in the chain.77pub type Index = u32;7879/// A hash of some data used by the chain.80pub type Hash = sp_core::H256;8182/// Digest item type.83pub type DigestItem = generic::DigestItem<Hash>;8485/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know86/// the specifics of the runtime. They can then be made to be agnostic over specific formats87/// of data like extrinsics, allowing for them to continue syncing the network through upgrades88/// to even the core data structures.89pub mod opaque {90 use super::*;9192 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;9394 /// Opaque block header type.95 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;96 /// Opaque block type.97 pub type Block = generic::Block<Header, UncheckedExtrinsic>;98 /// Opaque block identifier type.99 pub type BlockId = generic::BlockId<Block>;100101 impl_opaque_keys! {102 pub struct SessionKeys {103 pub aura: Aura,104 pub grandpa: Grandpa,105 }106 }107}108109/// This runtime version.110pub const VERSION: RuntimeVersion = RuntimeVersion {111 spec_name: create_runtime_str!("nft"),112 impl_name: create_runtime_str!("nft"),113 authoring_version: 1,114 spec_version: 2,115 impl_version: 1,116 apis: RUNTIME_API_VERSIONS,117 transaction_version: 1,118};119120pub const MILLISECS_PER_BLOCK: u64 = 6000;121122pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;123124// These time units are defined in number of blocks.125pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);126pub const HOURS: BlockNumber = MINUTES * 60;127pub const DAYS: BlockNumber = HOURS * 24;128129/// The version information used to identify this runtime when compiled natively.130#[cfg(feature = "std")]131pub fn native_version() -> NativeVersion {132 NativeVersion {133 runtime_version: VERSION,134 can_author_with: Default::default(),135 }136}137138parameter_types! {139 pub const BlockHashCount: BlockNumber = 2400;140 /// We allow for 2 seconds of compute with a 6 second average block time.141 pub const MaximumBlockWeight: Weight = 2 * WEIGHT_PER_SECOND;142 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);143 /// Assume 10% of weight for average on_initialize calls.144 pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()145 .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();146 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;147 pub const Version: RuntimeVersion = VERSION;148}149150impl system::Trait for Runtime {151 /// The basic call filter to use in dispatchable.152 type BaseCallFilter = ();153 /// The identifier used to distinguish between accounts.154 type AccountId = AccountId;155 /// The aggregated dispatch type that is available for extrinsics.156 type Call = Call;157 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.158 type Lookup = IdentityLookup<AccountId>;159 /// The index type for storing how many extrinsics an account has signed.160 type Index = Index;161 /// The index type for blocks.162 type BlockNumber = BlockNumber;163 /// The type for hashing blocks and tries.164 type Hash = Hash;165 /// The hashing algorithm used.166 type Hashing = BlakeTwo256;167 /// The header type.168 type Header = generic::Header<BlockNumber, BlakeTwo256>;169 /// The ubiquitous event type.170 type Event = Event;171 /// The ubiquitous origin type.172 type Origin = Origin;173 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).174 type BlockHashCount = BlockHashCount;175 /// Maximum weight of each block.176 type MaximumBlockWeight = MaximumBlockWeight;177 /// The weight of database operations that the runtime can invoke.178 type DbWeight = RocksDbWeight;179 /// The weight of the overhead invoked on the block import process, independent of the180 /// extrinsics included in that block.181 type BlockExecutionWeight = BlockExecutionWeight;182 /// The base weight of any extrinsic processed by the runtime, independent of the183 /// logic of that extrinsic. (Signature verification, nonce increment, fee, etc...)184 type ExtrinsicBaseWeight = ExtrinsicBaseWeight;185 /// The maximum weight that a single extrinsic of `Normal` dispatch class can have,186 /// idependent of the logic of that extrinsics. (Roughly max block weight - average on187 /// initialize cost).188 type MaximumExtrinsicWeight = MaximumExtrinsicWeight;189 /// Maximum size of all encoded transactions (in bytes) that are allowed in one block.190 type MaximumBlockLength = MaximumBlockLength;191 /// Portion of the block weight that is available to all normal transactions.192 type AvailableBlockRatio = AvailableBlockRatio;193 /// Version of the runtime.194 type Version = Version;195 /// Converts a module to the index of the module in `construct_runtime!`.196 /// This type is being generated by `construct_runtime!`.197 type ModuleToIndex = ModuleToIndex;198 /// What to do if a new account is created.199 type OnNewAccount = ();200 /// What to do if an account is fully reaped from the system.201 type OnKilledAccount = ();202 /// The data to be stored in an account.203 type AccountData = balances::AccountData<Balance>;204}205206impl aura::Trait for Runtime {207 type AuthorityId = AuraId;208}209210impl grandpa::Trait for Runtime {211 type Event = Event;212 type Call = Call;213214 type KeyOwnerProofSystem = ();215216 type KeyOwnerProof =217 <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;218219 type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(220 KeyTypeId,221 GrandpaId,222 )>>::IdentificationTuple;223224 type HandleEquivocation = ();225}226227parameter_types! {228 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;229}230231impl timestamp::Trait for Runtime {232 /// A timestamp: milliseconds since the unix epoch.233 type Moment = u64;234 type OnTimestampSet = Aura;235 type MinimumPeriod = MinimumPeriod;236}237238parameter_types! {239 // pub const ExistentialDeposit: u128 = 500;240 pub const ExistentialDeposit: u128 = 0;241}242243impl balances::Trait for Runtime {244 /// The type for recording an account's balance.245 type Balance = Balance;246 /// The ubiquitous event type.247 type Event = Event;248 type DustRemoval = ();249 type ExistentialDeposit = ExistentialDeposit;250 type AccountStore = System;251}252253pub const MILLICENTS: Balance = 1_000_000_000;254pub const CENTS: Balance = 1_000 * MILLICENTS;255pub const DOLLARS: Balance = 100 * CENTS;256257parameter_types! {258 pub const TombstoneDeposit: Balance = 16 * MILLICENTS;259 pub const RentByteFee: Balance = 4 * MILLICENTS;260 pub const RentDepositOffset: Balance = 1000 * MILLICENTS;261 pub const SurchargeReward: Balance = 150 * MILLICENTS;262}263264impl contracts::Trait for Runtime {265 type Call = Call;266 type Time = Timestamp;267 type Randomness = RandomnessCollectiveFlip;268 type Currency = Balances;269 type Event = Event;270 type DetermineContractAddress = contracts::SimpleAddressDeterminer<Runtime>;271 type TrieIdGenerator = contracts::TrieIdFromParentCounter<Runtime>;272 type RentPayment = ();273 type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap;274 type TombstoneDeposit = TombstoneDeposit;275 type StorageSizeOffset = contracts::DefaultStorageSizeOffset;276 type RentByteFee = RentByteFee;277 type RentDepositOffset = RentDepositOffset;278 type SurchargeReward = SurchargeReward;279 type MaxDepth = contracts::DefaultMaxDepth;280 type MaxValueSize = contracts::DefaultMaxValueSize;281 type WeightPrice = transaction_payment::Module<Self>;282}283284parameter_types! {285 pub const TransactionByteFee: Balance = 1;286}287288impl transaction_payment::Trait for Runtime {289 type Currency = balances::Module<Runtime>;290 type OnTransactionPayment = ();291 type TransactionByteFee = TransactionByteFee;292 type WeightToFee = IdentityFee<Balance>;293 type FeeMultiplierUpdate = ();294}295296impl sudo::Trait for Runtime {297 type Event = Event;298 type Call = Call;299}300301/// Used for the module nft in `./nft.rs`302impl nft::Trait for Runtime {303 type Event = Event;304}305306construct_runtime!(307 pub enum Runtime where308 Block = Block,309 NodeBlock = opaque::Block,310 UncheckedExtrinsic = UncheckedExtrinsic311 {312 System: system::{Module, Call, Config, Storage, Event<T>},313 RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},314 Contracts: contracts::{Module, Call, Config, Storage, Event<T>},315 Timestamp: timestamp::{Module, Call, Storage, Inherent},316 Aura: aura::{Module, Config<T>, Inherent(Timestamp)},317 Grandpa: grandpa::{Module, Call, Storage, Config, Event},318 Balances: balances::{Module, Call, Storage, Config<T>, Event<T>},319 TransactionPayment: transaction_payment::{Module, Storage},320 Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>},321 Nft: nft::{Module, Call, Storage, Event<T>},322 }323);324325/// The address format for describing accounts.326pub type Address = AccountId;327/// Block header type as expected by this runtime.328pub type Header = generic::Header<BlockNumber, BlakeTwo256>;329/// Block type as expected by this runtime.330pub type Block = generic::Block<Header, UncheckedExtrinsic>;331/// A Block signed with a Justification332pub type SignedBlock = generic::SignedBlock<Block>;333/// BlockId type as expected by this runtime.334pub type BlockId = generic::BlockId<Block>;335/// The SignedExtension to the basic transaction logic.336pub type SignedExtra = (337 system::CheckSpecVersion<Runtime>,338 system::CheckTxVersion<Runtime>,339 system::CheckGenesis<Runtime>,340 system::CheckEra<Runtime>,341 system::CheckNonce<Runtime>,342 system::CheckWeight<Runtime>,343 nft::ChargeTransactionPayment<Runtime>,344);345/// Unchecked extrinsic type as expected by this runtime.346pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;347/// Extrinsic type that has already been checked.348pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;349/// Executive: handles dispatch to the various modules.350pub type Executive =351 frame_executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;352353impl_runtime_apis! {354355 impl contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>356 for Runtime357 {358 fn call(359 origin: AccountId,360 dest: AccountId,361 value: Balance,362 gas_limit: u64,363 input_data: Vec<u8>,364 ) -> ContractExecResult {365 let exec_result =366 Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data);367 match exec_result {368 Ok(v) => ContractExecResult::Success {369 status: v.status,370 data: v.data,371 },372 Err(_) => ContractExecResult::Error,373 }374 }375376 fn get_storage(377 address: AccountId,378 key: [u8; 32],379 ) -> contracts_primitives::GetStorageResult {380 Contracts::get_storage(address, key)381 }382383 fn rent_projection(384 address: AccountId,385 ) -> contracts_primitives::RentProjectionResult<BlockNumber> {386 Contracts::rent_projection(address)387 }388 }389390 impl sp_api::Core<Block> for Runtime {391 fn version() -> RuntimeVersion {392 VERSION393 }394395 fn execute_block(block: Block) {396 Executive::execute_block(block)397 }398399 fn initialize_block(header: &<Block as BlockT>::Header) {400 Executive::initialize_block(header)401 }402 }403404 impl sp_api::Metadata<Block> for Runtime {405 fn metadata() -> OpaqueMetadata {406 Runtime::metadata().into()407 }408 }409410 impl sp_block_builder::BlockBuilder<Block> for Runtime {411 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {412 Executive::apply_extrinsic(extrinsic)413 }414415 fn finalize_block() -> <Block as BlockT>::Header {416 Executive::finalize_block()417 }418419 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {420 data.create_extrinsics()421 }422423 fn check_inherents(424 block: Block,425 data: sp_inherents::InherentData,426 ) -> sp_inherents::CheckInherentsResult {427 data.check_extrinsics(&block)428 }429430 fn random_seed() -> <Block as BlockT>::Hash {431 RandomnessCollectiveFlip::random_seed()432 }433 }434435 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {436 fn validate_transaction(437 source: TransactionSource,438 tx: <Block as BlockT>::Extrinsic,439 ) -> TransactionValidity {440 Executive::validate_transaction(source, tx)441 }442 }443444 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {445 fn offchain_worker(header: &<Block as BlockT>::Header) {446 Executive::offchain_worker(header)447 }448 }449450 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {451 fn slot_duration() -> u64 {452 Aura::slot_duration()453 }454455 fn authorities() -> Vec<AuraId> {456 Aura::authorities()457 }458 }459460 impl sp_session::SessionKeys<Block> for Runtime {461 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {462 opaque::SessionKeys::generate(seed)463 }464465 fn decode_session_keys(466 encoded: Vec<u8>,467 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {468 opaque::SessionKeys::decode_into_raw_public_keys(&encoded)469 }470 }471472 impl fg_primitives::GrandpaApi<Block> for Runtime {473 fn grandpa_authorities() -> GrandpaAuthorityList {474 Grandpa::grandpa_authorities()475 }476477 fn submit_report_equivocation_extrinsic(478 _equivocation_proof: fg_primitives::EquivocationProof<479 <Block as BlockT>::Hash,480 NumberFor<Block>,481 >,482 _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof,483 ) -> Option<()> {484 None485 }486487 fn generate_key_ownership_proof(488 _set_id: fg_primitives::SetId,489 _authority_id: GrandpaId,490 ) -> Option<fg_primitives::OpaqueKeyOwnershipProof> {491 // NOTE: this is the only implementation possible since we've492 // defined our key owner proof type as a bottom type (i.e. a type493 // with no values).494 None495 }496 }497}1//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.23#![cfg_attr(not(feature = "std"), no_std)]4// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.5#![recursion_limit = "256"]67// Make the WASM binary available.8#[cfg(feature = "std")]9include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1011use contracts_rpc_runtime_api::ContractExecResult;12use grandpa::fg_primitives;13use grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};14use sp_api::impl_runtime_apis;15use sp_consensus_aura::sr25519::AuthorityId as AuraId;16use sp_core::{crypto::KeyTypeId, OpaqueMetadata};17use sp_runtime::{18 create_runtime_str, generic, impl_opaque_keys,19 traits::{20 BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating,21 Verify,22 },23 transaction_validity::{TransactionSource, TransactionValidity},24 ApplyExtrinsicResult, MultiSignature,25};26use sp_std::prelude::*;27#[cfg(feature = "std")]28use sp_version::NativeVersion;29use sp_version::RuntimeVersion;3031// A few exports that help ease life for downstream crates.32pub use balances::Call as BalancesCall;33pub use contracts::Schedule as ContractsSchedule;34pub use frame_support::{35 construct_runtime,36 dispatch::DispatchResult,37 parameter_types,38 traits::{39 Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness,40 WithdrawReason,41 },42 weights::{43 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},44 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,45 WeightToFeePolynomial,46 },47 StorageValue,48};49#[cfg(any(feature = "std", test))]50pub use sp_runtime::BuildStorage;51use sp_runtime::Perbill;52use system::{self as system};5354pub use timestamp::Call as TimestampCall;5556/// Re-export a nft pallet57/// TODO: Check this re-export. Is this safe and good style?58extern crate nft;59pub use nft::*;6061/// An index to a block.62pub type BlockNumber = u32;6364/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.65pub type Signature = MultiSignature;6667/// Some way of identifying an account on the chain. We intentionally make it equivalent68/// to the public key of our transaction signing scheme.69pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;7071/// The type for looking up accounts. We don't expect more than 4 billion of them, but you72/// never know...73pub type AccountIndex = u32;7475/// Balance of an account.76pub type Balance = u128;7778/// Index of a transaction in the chain.79pub type Index = u32;8081/// A hash of some data used by the chain.82pub type Hash = sp_core::H256;8384/// Digest item type.85pub type DigestItem = generic::DigestItem<Hash>;8687/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know88/// the specifics of the runtime. They can then be made to be agnostic over specific formats89/// of data like extrinsics, allowing for them to continue syncing the network through upgrades90/// to even the core data structures.91pub mod opaque {92 use super::*;9394 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;9596 /// Opaque block header type.97 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;98 /// Opaque block type.99 pub type Block = generic::Block<Header, UncheckedExtrinsic>;100 /// Opaque block identifier type.101 pub type BlockId = generic::BlockId<Block>;102103 impl_opaque_keys! {104 pub struct SessionKeys {105 pub aura: Aura,106 pub grandpa: Grandpa,107 }108 }109}110111/// This runtime version.112pub const VERSION: RuntimeVersion = RuntimeVersion {113 spec_name: create_runtime_str!("nft"),114 impl_name: create_runtime_str!("nft"),115 authoring_version: 1,116 spec_version: 2,117 impl_version: 1,118 apis: RUNTIME_API_VERSIONS,119 transaction_version: 1,120};121122pub const MILLISECS_PER_BLOCK: u64 = 6000;123124pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;125126// These time units are defined in number of blocks.127pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);128pub const HOURS: BlockNumber = MINUTES * 60;129pub const DAYS: BlockNumber = HOURS * 24;130131/// The version information used to identify this runtime when compiled natively.132#[cfg(feature = "std")]133pub fn native_version() -> NativeVersion {134 NativeVersion {135 runtime_version: VERSION,136 can_author_with: Default::default(),137 }138}139140parameter_types! {141 pub const BlockHashCount: BlockNumber = 2400;142 /// We allow for 2 seconds of compute with a 6 second average block time.143 pub const MaximumBlockWeight: Weight = 2 * WEIGHT_PER_SECOND;144 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);145 /// Assume 10% of weight for average on_initialize calls.146 pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()147 .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();148 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;149 pub const Version: RuntimeVersion = VERSION;150}151152impl system::Trait for Runtime {153 /// The basic call filter to use in dispatchable.154 type BaseCallFilter = ();155 /// The identifier used to distinguish between accounts.156 type AccountId = AccountId;157 /// The aggregated dispatch type that is available for extrinsics.158 type Call = Call;159 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.160 type Lookup = IdentityLookup<AccountId>;161 /// The index type for storing how many extrinsics an account has signed.162 type Index = Index;163 /// The index type for blocks.164 type BlockNumber = BlockNumber;165 /// The type for hashing blocks and tries.166 type Hash = Hash;167 /// The hashing algorithm used.168 type Hashing = BlakeTwo256;169 /// The header type.170 type Header = generic::Header<BlockNumber, BlakeTwo256>;171 /// The ubiquitous event type.172 type Event = Event;173 /// The ubiquitous origin type.174 type Origin = Origin;175 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).176 type BlockHashCount = BlockHashCount;177 /// Maximum weight of each block.178 type MaximumBlockWeight = MaximumBlockWeight;179 /// The weight of database operations that the runtime can invoke.180 type DbWeight = RocksDbWeight;181 /// The weight of the overhead invoked on the block import process, independent of the182 /// extrinsics included in that block.183 type BlockExecutionWeight = BlockExecutionWeight;184 /// The base weight of any extrinsic processed by the runtime, independent of the185 /// logic of that extrinsic. (Signature verification, nonce increment, fee, etc...)186 type ExtrinsicBaseWeight = ExtrinsicBaseWeight;187 /// The maximum weight that a single extrinsic of `Normal` dispatch class can have,188 /// idependent of the logic of that extrinsics. (Roughly max block weight - average on189 /// initialize cost).190 type MaximumExtrinsicWeight = MaximumExtrinsicWeight;191 /// Maximum size of all encoded transactions (in bytes) that are allowed in one block.192 type MaximumBlockLength = MaximumBlockLength;193 /// Portion of the block weight that is available to all normal transactions.194 type AvailableBlockRatio = AvailableBlockRatio;195 /// Version of the runtime.196 type Version = Version;197 /// Converts a module to the index of the module in `construct_runtime!`.198 /// This type is being generated by `construct_runtime!`.199 type ModuleToIndex = ModuleToIndex;200 /// What to do if a new account is created.201 type OnNewAccount = ();202 /// What to do if an account is fully reaped from the system.203 type OnKilledAccount = ();204 /// The data to be stored in an account.205 type AccountData = balances::AccountData<Balance>;206}207208impl aura::Trait for Runtime {209 type AuthorityId = AuraId;210}211212impl grandpa::Trait for Runtime {213 type Event = Event;214 type Call = Call;215216 type KeyOwnerProofSystem = ();217218 type KeyOwnerProof =219 <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;220221 type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(222 KeyTypeId,223 GrandpaId,224 )>>::IdentificationTuple;225226 type HandleEquivocation = ();227}228229parameter_types! {230 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;231}232233impl timestamp::Trait for Runtime {234 /// A timestamp: milliseconds since the unix epoch.235 type Moment = u64;236 type OnTimestampSet = Aura;237 type MinimumPeriod = MinimumPeriod;238}239240parameter_types! {241 // pub const ExistentialDeposit: u128 = 500;242 pub const ExistentialDeposit: u128 = 0;243}244245impl balances::Trait for Runtime {246 /// The type for recording an account's balance.247 type Balance = Balance;248 /// The ubiquitous event type.249 type Event = Event;250 type DustRemoval = ();251 type ExistentialDeposit = ExistentialDeposit;252 type AccountStore = System;253}254255pub const MILLICENTS: Balance = 1_000_000_000;256pub const CENTS: Balance = 1_000 * MILLICENTS;257pub const DOLLARS: Balance = 100 * CENTS;258259parameter_types! {260 pub const TombstoneDeposit: Balance = 16 * MILLICENTS;261 pub const RentByteFee: Balance = 4 * MILLICENTS;262 pub const RentDepositOffset: Balance = 1000 * MILLICENTS;263 pub const SurchargeReward: Balance = 150 * MILLICENTS;264}265266impl contracts::Trait for Runtime {267 type Call = Call;268 type Time = Timestamp;269 type Randomness = RandomnessCollectiveFlip;270 type Currency = Balances;271 type Event = Event;272 type DetermineContractAddress = contracts::SimpleAddressDeterminer<Runtime>;273 type TrieIdGenerator = contracts::TrieIdFromParentCounter<Runtime>;274 type RentPayment = ();275 type SignedClaimHandicap = contracts::DefaultSignedClaimHandicap;276 type TombstoneDeposit = TombstoneDeposit;277 type StorageSizeOffset = contracts::DefaultStorageSizeOffset;278 type RentByteFee = RentByteFee;279 type RentDepositOffset = RentDepositOffset;280 type SurchargeReward = SurchargeReward;281 type MaxDepth = contracts::DefaultMaxDepth;282 type MaxValueSize = contracts::DefaultMaxValueSize;283 type WeightPrice = transaction_payment::Module<Self>;284}285286parameter_types! {287 pub const TransactionByteFee: Balance = 1;288}289290impl transaction_payment::Trait for Runtime {291 type Currency = balances::Module<Runtime>;292 type OnTransactionPayment = ();293 type TransactionByteFee = TransactionByteFee;294 type WeightToFee = IdentityFee<Balance>;295 type FeeMultiplierUpdate = ();296}297298impl sudo::Trait for Runtime {299 type Event = Event;300 type Call = Call;301}302303/// Used for the module nft in `./nft.rs`304impl nft::Trait for Runtime {305 type Event = Event;306}307308construct_runtime!(309 pub enum Runtime where310 Block = Block,311 NodeBlock = opaque::Block,312 UncheckedExtrinsic = UncheckedExtrinsic313 {314 System: system::{Module, Call, Config, Storage, Event<T>},315 RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},316 Contracts: contracts::{Module, Call, Config, Storage, Event<T>},317 Timestamp: timestamp::{Module, Call, Storage, Inherent},318 Aura: aura::{Module, Config<T>, Inherent(Timestamp)},319 Grandpa: grandpa::{Module, Call, Storage, Config, Event},320 Balances: balances::{Module, Call, Storage, Config<T>, Event<T>},321 TransactionPayment: transaction_payment::{Module, Storage},322 Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>},323 Nft: nft::{Module, Call, Config<T>, Storage, Event<T>},324 }325);326327/// The address format for describing accounts.328pub type Address = AccountId;329/// Block header type as expected by this runtime.330pub type Header = generic::Header<BlockNumber, BlakeTwo256>;331/// Block type as expected by this runtime.332pub type Block = generic::Block<Header, UncheckedExtrinsic>;333/// A Block signed with a Justification334pub type SignedBlock = generic::SignedBlock<Block>;335/// BlockId type as expected by this runtime.336pub type BlockId = generic::BlockId<Block>;337/// The SignedExtension to the basic transaction logic.338pub type SignedExtra = (339 system::CheckSpecVersion<Runtime>,340 system::CheckTxVersion<Runtime>,341 system::CheckGenesis<Runtime>,342 system::CheckEra<Runtime>,343 system::CheckNonce<Runtime>,344 system::CheckWeight<Runtime>,345 nft::ChargeTransactionPayment<Runtime>,346);347/// Unchecked extrinsic type as expected by this runtime.348pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;349/// Extrinsic type that has already been checked.350pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;351/// Executive: handles dispatch to the various modules.352pub type Executive =353 frame_executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;354355impl_runtime_apis! {356357 impl contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>358 for Runtime359 {360 fn call(361 origin: AccountId,362 dest: AccountId,363 value: Balance,364 gas_limit: u64,365 input_data: Vec<u8>,366 ) -> ContractExecResult {367 let exec_result =368 Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data);369 match exec_result {370 Ok(v) => ContractExecResult::Success {371 status: v.status,372 data: v.data,373 },374 Err(_) => ContractExecResult::Error,375 }376 }377378 fn get_storage(379 address: AccountId,380 key: [u8; 32],381 ) -> contracts_primitives::GetStorageResult {382 Contracts::get_storage(address, key)383 }384385 fn rent_projection(386 address: AccountId,387 ) -> contracts_primitives::RentProjectionResult<BlockNumber> {388 Contracts::rent_projection(address)389 }390 }391392 impl sp_api::Core<Block> for Runtime {393 fn version() -> RuntimeVersion {394 VERSION395 }396397 fn execute_block(block: Block) {398 Executive::execute_block(block)399 }400401 fn initialize_block(header: &<Block as BlockT>::Header) {402 Executive::initialize_block(header)403 }404 }405406 impl sp_api::Metadata<Block> for Runtime {407 fn metadata() -> OpaqueMetadata {408 Runtime::metadata().into()409 }410 }411412 impl sp_block_builder::BlockBuilder<Block> for Runtime {413 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {414 Executive::apply_extrinsic(extrinsic)415 }416417 fn finalize_block() -> <Block as BlockT>::Header {418 Executive::finalize_block()419 }420421 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {422 data.create_extrinsics()423 }424425 fn check_inherents(426 block: Block,427 data: sp_inherents::InherentData,428 ) -> sp_inherents::CheckInherentsResult {429 data.check_extrinsics(&block)430 }431432 fn random_seed() -> <Block as BlockT>::Hash {433 RandomnessCollectiveFlip::random_seed()434 }435 }436437 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {438 fn validate_transaction(439 source: TransactionSource,440 tx: <Block as BlockT>::Extrinsic,441 ) -> TransactionValidity {442 Executive::validate_transaction(source, tx)443 }444 }445446 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {447 fn offchain_worker(header: &<Block as BlockT>::Header) {448 Executive::offchain_worker(header)449 }450 }451452 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {453 fn slot_duration() -> u64 {454 Aura::slot_duration()455 }456457 fn authorities() -> Vec<AuraId> {458 Aura::authorities()459 }460 }461462 impl sp_session::SessionKeys<Block> for Runtime {463 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {464 opaque::SessionKeys::generate(seed)465 }466467 fn decode_session_keys(468 encoded: Vec<u8>,469 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {470 opaque::SessionKeys::decode_into_raw_public_keys(&encoded)471 }472 }473474 impl fg_primitives::GrandpaApi<Block> for Runtime {475 fn grandpa_authorities() -> GrandpaAuthorityList {476 Grandpa::grandpa_authorities()477 }478479 fn submit_report_equivocation_extrinsic(480 _equivocation_proof: fg_primitives::EquivocationProof<481 <Block as BlockT>::Hash,482 NumberFor<Block>,483 >,484 _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof,485 ) -> Option<()> {486 None487 }488489 fn generate_key_ownership_proof(490 _set_id: fg_primitives::SetId,491 _authority_id: GrandpaId,492 ) -> Option<fg_primitives::OpaqueKeyOwnershipProof> {493 // NOTE: this is the only implementation possible since we've494 // defined our key owner proof type as a bottom type (i.e. a type495 // with no values).496 None497 }498 }499}