difftreelog
Merge branch 'develop' into feature/NFTPAR-93
in: master
# Conflicts: # tests/package-lock.json # tests/package.json
11 files changed
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -142,6 +142,12 @@
"enable_println": "bool",
"max_subject_len": "u32"
},
+ "AccessMode": {
+ "_enum": [
+ "Normal",
+ "WhiteList"
+ ]
+ },
"CollectionMode": {
"_enum": {
"Invalid": null,
@@ -181,19 +187,25 @@
"CollectionType": {
"Owner": "AccountId",
"Mode": "CollectionMode",
- "Access": "u8",
+ "Access": "AccessMode",
"DecimalPoints": "u32",
"Name": "Vec<u16>",
"Description": "Vec<u16>",
"TokenPrefix": "Vec<u8>",
"CustomDataSize": "u32",
+ "MintMode": "bool",
"OffchainSchema": "Vec<u8>",
"Sponsor": "AccountId",
"UnconfirmedSponsor": "AccountId"
},
+ "ApprovePermissions": {
+ "Approved": "AccountId",
+ "Amount": "u64"
+ },
"RawData": "Vec<u8>",
"Address": "AccountId",
"LookupSource": "AccountId",
"Weight": "u64"
}
+
```
\ No newline at end of file
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.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]223/// For more guidance on Substrate FRAME, see the example pallet3#[cfg(feature = "std")]4/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs4pub use serde::*;55use codec::{Decode, Encode};6use codec::{Decode, Encode};6pub use frame_support::{7pub use frame_support::{32 },33 },33 FixedPointOperand, FixedU128,34 FixedPointOperand, FixedU128,34};35};35use sp_std::prelude::*;363637#[cfg(test)]37#[cfg(test)]38mod mock;38mod mock;393940#[cfg(test)]40#[cfg(test)]41mod tests;41mod tests;424243// Structs43#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]44// #region4546#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]47#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]44pub enum CollectionMode {48pub enum CollectionMode {45 Invalid,49 Invalid,46 // custom data size50 // custom data size62 }66 }63}67}646865#[derive(Encode, Decode, Debug, Clone, PartialEq)]69#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]66pub enum AccessMode {71pub enum AccessMode {67 Normal,72 Normal,68 WhiteList,73 WhiteList,79 }84 }80}85}818682#[derive(Encode, Decode, Default, Clone, PartialEq)]87#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]83#[cfg_attr(feature = "std", derive(Debug))]88#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub struct Ownership<AccountId> {89pub struct Ownership<AccountId> {85 pub owner: AccountId,90 pub owner: AccountId,86 pub fraction: u128,91 pub fraction: u128,87}92}889389#[derive(Encode, Decode, Default, Clone, PartialEq)]94#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]90#[cfg_attr(feature = "std", derive(Debug))]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]91pub struct CollectionType<AccountId> {96pub struct CollectionType<AccountId> {92 pub owner: AccountId,97 pub owner: AccountId,93 pub mode: CollectionMode,98 pub mode: CollectionMode,97 pub description: Vec<u16>, // 256 include null escape char102 pub description: Vec<u16>, // 256 include null escape char98 pub token_prefix: Vec<u8>, // 16 include null escape char103 pub token_prefix: Vec<u8>, // 16 include null escape char99 pub custom_data_size: u32,104 pub custom_data_size: u32,105 pub mint_mode: bool,100 pub offchain_schema: Vec<u8>,106 pub offchain_schema: Vec<u8>,101 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender107 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender102 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship108 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship103}109}104110105#[derive(Encode, Decode, Default, Clone, PartialEq)]111#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]106#[cfg_attr(feature = "std", derive(Debug))]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]107pub struct CollectionAdminsType<AccountId> {113pub struct CollectionAdminsType<AccountId> {108 pub admin: AccountId,114 pub admin: AccountId,109 pub collection_id: u64,115 pub collection_id: u64,110}116}111117112#[derive(Encode, Decode, Default, Clone, PartialEq)]118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]113#[cfg_attr(feature = "std", derive(Debug))]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]114pub struct NftItemType<AccountId> {120pub struct NftItemType<AccountId> {115 pub collection: u64,121 pub collection: u64,116 pub owner: AccountId,122 pub owner: AccountId,117 pub data: Vec<u8>,123 pub data: Vec<u8>,118}124}119125120#[derive(Encode, Decode, Default, Clone, PartialEq)]126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Debug))]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct FungibleItemType<AccountId> {128pub struct FungibleItemType<AccountId> {123 pub collection: u64,129 pub collection: u64,124 pub owner: AccountId,130 pub owner: AccountId,125 pub value: u128,131 pub value: u128,126}132}127133128#[derive(Encode, Decode, Default, Clone, PartialEq)]134#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]129#[cfg_attr(feature = "std", derive(Debug))]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]130pub struct ReFungibleItemType<AccountId> {136pub struct ReFungibleItemType<AccountId> {131 pub collection: u64,137 pub collection: u64,132 pub owner: Vec<Ownership<AccountId>>,138 pub owner: Vec<Ownership<AccountId>>,133 pub data: Vec<u8>,139 pub data: Vec<u8>,134}140}135141136#[derive(Encode, Decode, Default, Clone, PartialEq)]142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]137#[cfg_attr(feature = "std", derive(Debug))]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]138pub struct ApprovePermissions<AccountId> {144pub struct ApprovePermissions<AccountId> {139 pub approved: AccountId,145 pub approved: AccountId,140 pub amount: u64,146 pub amount: u64,141}147}142148143#[derive(Encode, Decode, Default, Clone, PartialEq)]149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]144#[cfg_attr(feature = "std", derive(Debug))]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]145pub struct VestingItem<AccountId, Moment> {151pub struct VestingItem<AccountId, Moment> {146 pub sender: AccountId,152 pub sender: AccountId,147 pub recipient: AccountId,153 pub recipient: AccountId,155 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;161 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;156}162}157163164// #endregion165158decl_storage! {166decl_storage! {159 trait Store for Module<T: Trait> as Nft {167 trait Store for Module<T: Trait> as Nft {160168164 ChainVersion: u64;172 ChainVersion: u64;165 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;173 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;166174167 pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;175 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;168 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;176 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;169 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;177 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;170178175 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;183 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;176184177 /// Item collections185 /// Item collections178 pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;186 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;179 pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;187 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;180 pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;188 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;181189182 // Active vesting list183 // pub VestingList get(fn vesting): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => VestingItem<T::AccountId, T::Moment>;184185 /// Index list190 /// Index list186 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;191 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;187192188 // Sponsorship193 // Sponsorship189 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;194 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;190 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;195 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;191 }196 }197 add_extra_genesis {198 build(|config: &GenesisConfig<T>| {199 // Modification of storage200 for (_num, _c) in &config.collection {201 <Module<T>>::init_collection(_c);202 }203204 for (_num, _q, _i) in &config.nft_item_id {205 <Module<T>>::init_nft_token(_i);206 }207208 for (_num, _q, _i) in &config.fungible_item_id {209 <Module<T>>::init_fungible_token(_i);210 }211212 for (_num, _q, _i) in &config.refungible_item_id {213 <Module<T>>::init_refungible_token(_i);214 }215 })216 }192}217}193218194decl_event!(219decl_event!(209234210 fn on_initialize(now: T::BlockNumber) -> Weight {235 fn on_initialize(now: T::BlockNumber) -> Weight {211236212 if ChainVersion::get() == 0237 if ChainVersion::get() < 2213 {238 {214 let value = NextCollectionID::get();239 let value = NextCollectionID::get();215 CreatedCollectionCount::put(value);240 CreatedCollectionCount::put(value);224 // @param customDataSz size of custom data in each collection item249 // @param customDataSz size of custom data in each collection item225 // returns collection ID250 // returns collection ID226 #[weight = 0]251 #[weight = 0]227 pub fn create_collection( origin,252 pub fn create_collection(origin,228 collection_name: Vec<u16>,253 collection_name: Vec<u16>,229 collection_description: Vec<u16>,254 collection_description: Vec<u16>,230 token_prefix: Vec<u8>,255 token_prefix: Vec<u8>,231 mode: CollectionMode) -> DispatchResult {256 mode: CollectionMode) -> DispatchResult {232257233 // Anyone can create a collection258 // Anyone can create a collection234 let who = ensure_signed(origin)?;259 let who = ensure_signed(origin)?;271 owner: who.clone(),296 owner: who.clone(),272 name: name,297 name: name,273 mode: mode.clone(),298 mode: mode.clone(),299 mint_mode: false,274 access: AccessMode::Normal,300 access: AccessMode::Normal,275 description: description,301 description: description,276 decimal_points: decimal_points,302 decimal_points: decimal_points,309 }335 }310336311 #[weight = 0]337 #[weight = 0]338 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{339340 let sender = ensure_signed(origin)?;341 Self::check_owner_or_admin_permissions(collection_id, sender)?;342343 let mut white_list_collection: Vec<T::AccountId>;344 if <WhiteList<T>>::contains_key(collection_id) {345 white_list_collection = <WhiteList<T>>::get(collection_id);346 if !white_list_collection.contains(&address.clone())347 {348 white_list_collection.push(address.clone());349 }350 }351 else {352 white_list_collection = Vec::new();353 white_list_collection.push(address.clone());354 }355356 <WhiteList<T>>::insert(collection_id, white_list_collection);357 Ok(())358 }359360 #[weight = 0]361 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{362363 let sender = ensure_signed(origin)?;364 Self::check_owner_or_admin_permissions(collection_id, sender)?;365366 if <WhiteList<T>>::contains_key(collection_id) {367 let mut white_list_collection = <WhiteList<T>>::get(collection_id);368 if white_list_collection.contains(&address.clone())369 {370 white_list_collection.retain(|i| *i != address.clone());371 <WhiteList<T>>::insert(collection_id, white_list_collection);372 }373 }374375 Ok(())376 }377378 #[weight = 0]379 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult380 {381 let sender = ensure_signed(origin)?;382383 Self::check_owner_permissions(collection_id, sender)?;384 let mut target_collection = <Collection<T>>::get(collection_id);385 target_collection.access = mode;386 <Collection<T>>::insert(collection_id, target_collection);387388 Ok(())389 }390391 #[weight = 0]392 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult393 {394 let sender = ensure_signed(origin)?;395396 Self::check_owner_permissions(collection_id, sender)?;397 let mut target_collection = <Collection<T>>::get(collection_id);398 target_collection.mint_mode = mint_permission;399 <Collection<T>>::insert(collection_id, target_collection);400401 Ok(())402 }403404 #[weight = 0]312 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {405 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {313406314 let sender = ensure_signed(origin)?;407 let sender = ensure_signed(origin)?;405 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {498 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {406499407 let sender = ensure_signed(origin)?;500 let sender = ensure_signed(origin)?;501 Self::collection_exists(collection_id)?;408 let target_collection = <Collection<T>>::get(collection_id);502 let target_collection = <Collection<T>>::get(collection_id);409 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;410503504 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {505 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");506 Self::check_white_list(collection_id, &owner)?;507 Self::check_white_list(collection_id, &sender)?;508 }509411 match target_collection.mode510 match target_collection.mode412 {511 {413 CollectionMode::NFT(_) => {512 CollectionMode::NFT(_) => {469 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {568 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {470569471 let sender = ensure_signed(origin)?;570 let sender = ensure_signed(origin)?;472 let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);571 Self::collection_exists(collection_id)?;473 if !item_owner572474 {573 // Transfer permissions check475 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;476 }477 let target_collection = <Collection<T>>::get(collection_id);574 let target_collection = <Collection<T>>::get(collection_id);575 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 576 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 577 "Only item owner, collection owner and admins can modify item");478578579 if target_collection.access == AccessMode::WhiteList {580 Self::check_white_list(collection_id, &sender)?;581 }582479 match target_collection.mode583 match target_collection.mode480 {584 {481 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,585 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,494 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {598 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {495599496 let sender = ensure_signed(origin)?;600 let sender = ensure_signed(origin)?;497 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");498601602 // Transfer permissions check499 let target_collection = <Collection<T>>::get(collection_id);603 let target_collection = <Collection<T>>::get(collection_id);604 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 605 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 606 "Only item owner, collection owner and admins can modify item");500607501 // TODO: implement other modes608 if target_collection.access == AccessMode::WhiteList {609 Self::check_white_list(collection_id, &sender)?;610 Self::check_white_list(collection_id, &recipient)?;611 }612502 match target_collection.mode613 match target_collection.mode503 {614 {515626516 let sender = ensure_signed(origin)?;627 let sender = ensure_signed(origin)?;517628629 // Transfer permissions check630 let target_collection = <Collection<T>>::get(collection_id);631 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 632 Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 633 "Only item owner, collection owner and admins can approve");634635 if target_collection.access == AccessMode::WhiteList {636 Self::check_white_list(collection_id, &sender)?;637 Self::check_white_list(collection_id, &approved)?;638 }639518 // amount param stub640 // amount param stub519 let amount = 100000000;641 let amount = 100000000;520642521 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");522523 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));643 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));524 if list_exists {644 if list_exists {525645544 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {664 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {545665546 let sender = ensure_signed(origin)?;666 let sender = ensure_signed(origin)?;547 let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone()));667 let mut appoved_transfer = false;668548 if approved_list_exists669 // Check approve549 {670 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {550 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));671 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));551 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());672 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());552 ensure!(opt_item.is_some(), "No approve found");673 appoved_transfer = opt_item.is_some();553 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");674 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");554555 // remove approve556 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))557 .into_iter().filter(|i| i.approved != sender.clone()).collect();558 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);559 }675 }560 else561 {562 Self::check_owner_or_admin_permissions(collection_id, sender)?;563 }564676677 // Transfer permissions check565 let target_collection = <Collection<T>>::get(collection_id);678 let target_collection = <Collection<T>>::get(collection_id);679 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 680 "Only item owner, collection owner and admins can modify items");566681682 if target_collection.access == AccessMode::WhiteList {683 Self::check_white_list(collection_id, &sender)?;684 Self::check_white_list(collection_id, &recipient)?;685 }686687 // remove approve688 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))689 .into_iter().filter(|i| i.approved != sender.clone()).collect();690 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);691692567 match target_collection.mode693 match target_collection.mode568 {694 {569 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,695 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,675 Ok(())801 Ok(())676 }802 }677803678 fn burn_refungible_item(804 fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {679 collection_id: u64,680 item_id: u64,681 owner: T::AccountId,682 ) -> DispatchResult {683 ensure!(805 ensure!(684 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),806 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),685 "Item does not exists"807 "Item does not exists"770 Ok(())892 Ok(())771 }893 }772894773 fn check_owner_or_admin_permissions(895 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {774 collection_id: u64,775 subject: T::AccountId,776 ) -> DispatchResult {777 Self::collection_exists(collection_id)?;778896779 let target_collection = <Collection<T>>::get(collection_id);897 let target_collection = <Collection<T>>::get(collection_id);780 let is_owner = subject == target_collection.owner;898 let mut result: bool = subject == target_collection.owner;781782 let no_perm_mes = "You do not have permissions to modify this collection";783 let exists = <AdminList<T>>::contains_key(collection_id);899 let exists = <AdminList<T>>::contains_key(collection_id);784900785 if !is_owner {901 if !result & exists {786 ensure!(exists, no_perm_mes);787 ensure!(902 if <AdminList<T>>::get(collection_id).contains(&subject) {788 <AdminList<T>>::get(collection_id).contains(&subject),789 no_perm_mes903 result = true790 );904 }791 }905 }906907 result908 }909910 fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {911 912 Self::collection_exists(collection_id)?;913 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());914915 ensure!(result, "You do not have permissions to modify this collection");792 Ok(())916 Ok(())793 }917 }794918812 }936 }813 }937 }814938939 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {940941 let mes = "Address is not in white list";942 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);943 let wl = <WhiteList<T>>::get(collection_id);944 ensure!(wl.contains(address), mes);945946 Ok(())947 }948815 fn transfer_fungible(949 fn transfer_fungible(816 collection_id: u64,950 collection_id: u64,817 item_id: u64,951 item_id: u64,818 value: u64,952 value: u64,819 owner: T::AccountId,953 owner: T::AccountId,820 new_owner: T::AccountId,954 new_owner: T::AccountId,821 ) -> DispatchResult {955 ) -> DispatchResult {956957 ensure!(958 <FungibleItemList<T>>::contains_key(collection_id, item_id),959 "Item not exists"960 );961822 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);962 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);823 let amount = full_item.value;963 let amount = full_item.value;824964903 owner: T::AccountId,1043 owner: T::AccountId,904 new_owner: T::AccountId,1044 new_owner: T::AccountId,905 ) -> DispatchResult {1045 ) -> DispatchResult {10461047 ensure!(1048 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1049 "Item not exists"1050 );1051906 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1052 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);907 let item = full_item1053 let item = full_item908 .owner1054 .owner983 sender: T::AccountId,1129 sender: T::AccountId,984 new_owner: T::AccountId,1130 new_owner: T::AccountId,985 ) -> DispatchResult {1131 ) -> DispatchResult {1132 1133 ensure!(1134 <NftItemList<T>>::contains_key(collection_id, item_id),1135 "Item not exists"1136 );1137986 let mut item = <NftItemList<T>>::get(collection_id, item_id);1138 let mut item = <NftItemList<T>>::get(collection_id, item_id);9871139988 ensure!(1140 ensure!(1012 // reset approved list1164 // reset approved list1013 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1165 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1014 Ok(())1166 Ok(())1167 }11681169 fn init_collection(item: &CollectionType<T::AccountId>){11701171 // check params1172 assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");1173 assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");1174 assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");1175 assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");1176 1177 // Generate next collection ID1178 let next_id = CreatedCollectionCount::get()1179 .checked_add(1)1180 .expect("collection id error");1181 1182 CreatedCollectionCount::put(next_id); 1183 }11841185 fn init_nft_token(item: &NftItemType<T::AccountId>){11861187 let current_index = <ItemListIndex>::get(item.collection)1188 .checked_add(1)1189 .expect("Item list index id error");11901191 let item_owner = item.owner.clone();1192 let collection_id = item.collection.clone();1193 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();11941195 <ItemListIndex>::insert(collection_id, current_index);11961197 // Update balance1198 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1199 .checked_add(1)1200 .unwrap();1201 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1202 }12031204 fn init_fungible_token(item: &FungibleItemType<T::AccountId>){12051206 let current_index = <ItemListIndex>::get(item.collection)1207 .checked_add(1)1208 .expect("Item list index id error");1209 let owner = item.owner.clone();1210 let value = item.value as u64;12111212 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12131214 <ItemListIndex>::insert(item.collection, current_index);12151216 // Update balance1217 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1218 .checked_add(value)1219 .unwrap();1220 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1221 }12221223 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){12241225 let current_index = <ItemListIndex>::get(item.collection)1226 .checked_add(1)1227 .expect("Item list index id error");12281229 let value = item.owner.first().unwrap().fraction as u64;1230 let owner = item.owner.first().unwrap().owner.clone();12311232 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12331234 <ItemListIndex>::insert(item.collection, current_index);12351236 // Update balance1237 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1238 .checked_add(value)1239 .unwrap();1240 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1015 }1241 }101612421017 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {1243 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {106812941069////////////////////////////////////////////////////////////////////////////////////////////////////1295////////////////////////////////////////////////////////////////////////////////////////////////////1070// Economic models1296// Economic models1297// #region107112981072/// Fee multiplier.1299/// Fee multiplier.1073pub type Multiplier = FixedU128;1300pub type Multiplier = FixedU128;1262 Ok(())1489 Ok(())1263 }1490 }1264}1491}12651492// #endregionpallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,8 +1,10 @@
// Tests to be written here
use crate::mock::*;
-use crate::{ApprovePermissions, CollectionMode, Ownership};
+use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
use frame_support::{assert_noop, assert_ok};
+// Use cases tests region
+// #region
#[test]
fn create_nft_item() {
new_test_ext().execute_with(|| {
@@ -321,15 +323,16 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
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],
@@ -390,15 +393,16 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
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],
@@ -425,7 +429,7 @@
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
ApprovePermissions {
- approved: 10,
+ approved: 3,
amount: 100000000
}
);
@@ -461,15 +465,16 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
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],
@@ -496,7 +501,7 @@
assert_eq!(
TemplateModule::approved(1, (1, 1))[0],
ApprovePermissions {
- approved: 10,
+ approved: 3,
amount: 100000000
}
);
@@ -573,7 +578,6 @@
let mode: CollectionMode = CollectionMode::NFT(2000);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
assert_ok!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
@@ -583,7 +587,7 @@
));
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
- origin2.clone(),
+ origin1.clone(),
1,
[1, 2, 3].to_vec(),
1
@@ -614,7 +618,6 @@
let mode: CollectionMode = CollectionMode::Fungible(3);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
assert_ok!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
@@ -624,7 +627,7 @@
));
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
- origin2.clone(),
+ origin1.clone(),
1,
[].to_vec(),
1
@@ -661,6 +664,11 @@
token_prefix1.clone(),
mode
));
+
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
origin2.clone(),
@@ -928,6 +936,13 @@
// approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
+
assert_ok!(TemplateModule::transfer_from(
origin2.clone(),
1,
@@ -942,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
run-testnet.shdiffbeforeafterboth--- a/run-testnet.sh
+++ b/run-testnet.sh
@@ -49,6 +49,7 @@
--rpc-port $RPCPORT \
--name $NODE \
--ws-external \
+ --ws-max-connections 10000 \
--rpc-cors all \
-lruntime \
$BOOTNODES;
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -53,8 +53,10 @@
pub use timestamp::Call as TimestampCall;
-/// Importing a nft pallet
-pub use nft;
+/// Re-export a nft pallet
+/// TODO: Check this re-export. Is this safe and good style?
+extern crate nft;
+pub use nft::*;
/// An index to a block.
pub type BlockNumber = u32;
@@ -111,7 +113,7 @@
spec_name: create_runtime_str!("nft"),
impl_name: create_runtime_str!("nft"),
authoring_version: 1,
- spec_version: 1,
+ spec_version: 2,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
@@ -318,7 +320,7 @@
Balances: balances::{Module, Call, Storage, Config<T>, Event<T>},
TransactionPayment: transaction_payment::{Module, Storage},
Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>},
- Nft: nft::{Module, Call, Storage, Event<T>},
+ Nft: nft::{Module, Call, Config<T>, Storage, Event<T>},
}
);
tests/src/blocks-production.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/blocks-production.test.ts
@@ -0,0 +1,48 @@
+import usingApi from "./substrate/substrate-api";
+import promisifySubstrate from "./substrate/promisify-substrate";
+import { expect } from "chai";
+
+describe('Blocks Production', () => {
+ it('Node produces new blocks', async () => {
+ await usingApi(async api => {
+ const blocksPromise = promisifySubstrate(api, () => {
+ return new Promise<number[]>((resolve, reject) => {
+ const blockNumbers: number[] = [];
+ const unsubscribe = api.rpc.chain.subscribeNewHeads(async head => {
+ blockNumbers.push(head.number.toNumber());
+ if(blockNumbers.length >= 2) {
+ (await unsubscribe)();
+ resolve(blockNumbers);
+ }
+ });
+ })
+ })();
+
+ let blocks: number[] | undefined = undefined;
+
+ const timeoutPromise = new Promise<void>((resolve, reject) => {
+ let secondsPassed = 0;
+ let incrementSeconds = () => {
+ secondsPassed++;
+ if(secondsPassed > 5 * 60) {
+ reject('Block production test failed due to timeout.');
+ return;
+ }
+
+ if(blocks) {
+ resolve();
+ return;
+ }
+
+ setTimeout(incrementSeconds, 1000);
+ }
+
+ incrementSeconds();
+ });
+
+ blocks = await Promise.race([blocksPromise, timeoutPromise]) as number[];
+
+ expect(blocks[0]).to.be.lessThan(blocks[1]);
+ });
+ });
+});
tests/src/config.tsdiffbeforeafterboth--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -34,6 +34,20 @@
"ReFungible": "(u32, u32)"
}
},
+ "Ownership": {
+ "Owner": "AccountId",
+ "Fraction": "u128"
+ },
+ "FungibleItemType": {
+ "Collection": "u64",
+ "Owner": "AccountId",
+ "Value": "u128"
+ },
+ "ReFungibleItemType": {
+ "Collection": "u64",
+ "Owner": "Vec<Ownership>",
+ "Data": "Vec<u8>"
+ },
"NftItemType": {
"Collection": "u64",
"Owner": "AccountId",
@@ -57,10 +71,15 @@
"Description": "Vec<u16>",
"TokenPrefix": "Vec<u8>",
"CustomDataSize": "u32",
+ "MintMode": "bool",
"OffchainSchema": "Vec<u8>",
"Sponsor": "AccountId",
"UnconfirmedSponsor": "AccountId"
},
+ "ApprovePermissions": {
+ "Approved": "AccountId",
+ "Amount": "u64"
+ },
"RawData": "Vec<u8>",
"Address": "AccountId",
"LookupSource": "AccountId",
tests/src/connection.test.tsdiffbeforeafterboth--- a/tests/src/connection.test.ts
+++ b/tests/src/connection.test.ts
@@ -15,9 +15,9 @@
});
});
- it('Cannot connect to 0.0.0.0', () => {
- const neverConnectProvider = new WsProvider('ws://0.0.0.0:9944');
- expect((async () => {
+ it('Cannot connect to 255.255.255.255', async () => {
+ const neverConnectProvider = new WsProvider('ws://255.255.255.255:9944');
+ await expect((async () => {
await usingApi(async api => {
const health = await api.rpc.system.health();
}, { provider: neverConnectProvider });
tests/src/substrate/promisify-substrate.tsdiffbeforeafterboth--- a/tests/src/substrate/promisify-substrate.ts
+++ b/tests/src/substrate/promisify-substrate.ts
@@ -4,19 +4,21 @@
export default function promisifySubstrate<T extends (...args: any[]) => any>(api: ApiPromise, action: T): (...args: Parameters<T>) => Promise<PromiseType<ReturnType<T>>> {
return (...args: Parameters<T>) => {
- const promise = new Promise<PromiseType<ReturnType<T>>>((resolve, reject) => {
+ const promise = new Promise<PromiseType<ReturnType<T>>>((resolve: ((result: PromiseType<ReturnType<T>>) => void) | undefined, reject: ((error: any) => void) | undefined) => {
const cleanup = () => {
api.off('disconnected', fail);
api.off('error', fail);
+ resolve = undefined;
+ reject = undefined;
};
const success = (r: any) => {
+ resolve && resolve(r);
cleanup();
- resolve(r);
};
const fail = (error: any) => {
+ reject && reject(error);
cleanup();
- reject(error);
};
api.on('disconnected', fail);
tests/src/substrate/substrate-api.tsdiffbeforeafterboth--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -10,13 +10,16 @@
export default async function usingApi(action: (api: ApiPromise) => Promise<void>, settings: ApiOptions | undefined = undefined): Promise<void> {
settings = settings || defaultApiOptions();
- let api: ApiPromise | undefined = undefined;
+ let api: ApiPromise = new ApiPromise(settings);
try {
- api = new ApiPromise(settings);
- await promisifySubstrate(api, () => api && api.isReady)();
- await action(api);
+ await promisifySubstrate(api, async () => {
+ if(api) {
+ await api.isReadyOrError;
+ await action(api);
+ }
+ })();
} finally {
- api && api.disconnect();
+ await api.disconnect();
}
}
\ No newline at end of file