difftreelog
Merge branch 'develop' into feature/NFTPAR-288
in: master
25 files changed
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -323,6 +323,8 @@
CollectionNotFound,
/// Item not exists.
TokenNotFound,
+ /// Admin not found
+ AdminNotFound,
/// Arithmetic calculation overflow.
NumOverflow,
/// Account already has admin role.
@@ -426,7 +428,7 @@
pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;
/// Tokens transfer baskets
- pub CreateItemBasket get(fn create_item_basket): map hasher(identity) CollectionId => T::BlockNumber;
+ pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;
pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;
pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
@@ -812,13 +814,11 @@
let sender = ensure_signed(origin)?;
Self::check_owner_or_admin_permissions(collection_id, sender)?;
+ ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);
- if <AdminList<T>>::contains_key(collection_id)
- {
- let mut admin_arr = <AdminList<T>>::get(collection_id);
- admin_arr.retain(|i| *i != account_id);
- <AdminList<T>>::insert(collection_id, admin_arr);
- }
+ let mut admin_arr = <AdminList<T>>::get(collection_id);
+ admin_arr.retain(|i| *i != account_id);
+ <AdminList<T>>::insert(collection_id, admin_arr);
Ok(())
}
@@ -1009,7 +1009,7 @@
{
CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,
CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,
- CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,
+ CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, &sender)?,
_ => ()
};
@@ -1092,6 +1092,9 @@
let sender = ensure_signed(origin)?;
+ Self::collection_exists(collection_id)?;
+ Self::token_exists(collection_id, item_id, &sender)?;
+
// Transfer permissions check
let target_collection = <Collection<T>>::get(collection_id);
ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
@@ -1161,7 +1164,7 @@
}
// Reduce approval by transferred amount or remove if remaining approval drops to 0
- if approval - value > 0 {
+ if approval.checked_sub(value).unwrap_or(0) > 0 {
<Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);
}
else {
@@ -1216,7 +1219,8 @@
let sender = ensure_signed(origin)?;
Self::collection_exists(collection_id)?;
-
+ Self::token_exists(collection_id, item_id, &sender)?;
+
ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);
// Modify permissions check
@@ -1224,8 +1228,6 @@
ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||
Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
Error::<T>::NoPermission);
-
- Self::item_exists(collection_id, item_id, &target_collection.mode)?;
match target_collection.mode
{
@@ -1320,7 +1322,7 @@
Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
// check schema limit
- ensure!(schema.len() as u32 > ChainLimit::get().const_on_chain_schema_limit, "");
+ ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");
let mut target_collection = <Collection<T>>::get(collection_id);
target_collection.const_on_chain_schema = schema;
@@ -1351,7 +1353,7 @@
Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
// check schema limit
- ensure!(schema.len() as u32 > ChainLimit::get().variable_on_chain_schema_limit, "");
+ ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");
let mut target_collection = <Collection<T>>::get(collection_id);
target_collection.variable_on_chain_schema = schema;
@@ -1677,13 +1679,13 @@
let value = item.owner.first().unwrap().fraction;
let owner = item.owner.first().unwrap().owner.clone();
- Self::add_token_index(collection_id, current_index, owner.clone())?;
+ Self::add_token_index(collection_id, current_index, &owner)?;
<ItemListIndex>::insert(collection_id, current_index);
<ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);
// Update balance
- let new_balance = <Balance<T>>::get(collection_id, owner.clone())
+ let new_balance = <Balance<T>>::get(collection_id, &owner)
.checked_add(value)
.ok_or(Error::<T>::NumOverflow)?;
<Balance<T>>::insert(collection_id, owner.clone(), new_balance);
@@ -1697,7 +1699,7 @@
.ok_or(Error::<T>::NumOverflow)?;
let item_owner = item.owner.clone();
- Self::add_token_index(collection_id, current_index, item.owner.clone())?;
+ Self::add_token_index(collection_id, current_index, &item.owner)?;
<ItemListIndex>::insert(collection_id, current_index);
<NftItemList<T>>::insert(collection_id, current_index, item);
@@ -1714,29 +1716,44 @@
fn burn_refungible_item(
collection_id: CollectionId,
item_id: TokenId,
- owner: T::AccountId,
+ owner: &T::AccountId,
) -> DispatchResult {
ensure!(
<ReFungibleItemList<T>>::contains_key(collection_id, item_id),
Error::<T>::TokenNotFound
);
- let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);
- let item = collection
+ let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);
+ let rft_balance = token
.owner
.iter()
- .filter(|&i| i.owner == owner)
+ .filter(|&i| i.owner == *owner)
.next()
.unwrap();
- Self::remove_token_index(collection_id, item_id, owner.clone())?;
+ Self::remove_token_index(collection_id, item_id, owner)?;
// update balance
- let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
- .checked_sub(item.fraction)
+ let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())
+ .checked_sub(rft_balance.fraction)
.ok_or(Error::<T>::NumOverflow)?;
- <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
+ <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);
- <ReFungibleItemList<T>>::remove(collection_id, item_id);
+ // Re-create owners list with sender removed
+ let index = token
+ .owner
+ .iter()
+ .position(|i| i.owner == *owner)
+ .unwrap();
+ token.owner.remove(index);
+ let owner_count = token.owner.len();
+ // Burn the token completely if this was the last (only) owner
+ if owner_count == 0 {
+ <ReFungibleItemList<T>>::remove(collection_id, item_id);
+ }
+ else {
+ <ReFungibleItemList<T>>::insert(collection_id, item_id, token);
+ }
+
Ok(())
}
@@ -1746,10 +1763,10 @@
Error::<T>::TokenNotFound
);
let item = <NftItemList<T>>::get(collection_id, item_id);
- Self::remove_token_index(collection_id, item_id, item.owner.clone())?;
+ Self::remove_token_index(collection_id, item_id, &item.owner)?;
// update balance
- let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
+ let new_balance = <Balance<T>>::get(collection_id, &item.owner)
.checked_sub(1)
.ok_or(Error::<T>::NumOverflow)?;
<Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
@@ -1858,16 +1875,33 @@
Ok(())
}
+ /// Check if token exists. In case of Fungible, check if there is an entry for
+ /// the owner in fungible balances double map
+ fn token_exists(
+ collection_id: CollectionId,
+ item_id: TokenId,
+ owner: &T::AccountId
+ ) -> DispatchResult {
+ let target_collection = <Collection<T>>::get(collection_id);
+ let exists = match target_collection.mode
+ {
+ CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),
+ CollectionMode::Fungible(_) => <FungibleItemList<T>>::contains_key(collection_id, owner),
+ CollectionMode::ReFungible(_) => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
+ _ => false
+ };
+
+ ensure!(exists == true, Error::<T>::TokenNotFound);
+ Ok(())
+ }
+
fn transfer_fungible(
collection_id: CollectionId,
value: u128,
owner: &T::AccountId,
recipient: &T::AccountId,
) -> DispatchResult {
- ensure!(
- <FungibleItemList<T>>::contains_key(collection_id, owner),
- Error::<T>::TokenNotFound
- );
+ Self::token_exists(collection_id, 0, owner)?;
let mut balance = <FungibleItemList<T>>::get(collection_id, owner);
ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);
@@ -1897,10 +1931,7 @@
owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
- ensure!(
- <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
- Error::<T>::TokenNotFound
- );
+ Self::token_exists(collection_id, item_id, &owner)?;
let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);
let item = full_item
@@ -1941,7 +1972,7 @@
<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
// update index collection
- Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;
+ Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;
} else {
let mut new_full_item = full_item.clone();
new_full_item
@@ -1966,7 +1997,7 @@
owner: new_owner.clone(),
fraction: value,
});
- Self::add_token_index(collection_id, item_id, new_owner.clone())?;
+ Self::add_token_index(collection_id, item_id, &new_owner)?;
}
<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
@@ -1981,10 +2012,7 @@
sender: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
- ensure!(
- <NftItemList<T>>::contains_key(collection_id, item_id),
- Error::<T>::TokenNotFound
- );
+ Self::token_exists(collection_id, item_id, &sender)?;
let mut item = <NftItemList<T>>::get(collection_id, item_id);
@@ -2010,25 +2038,11 @@
<NftItemList<T>>::insert(collection_id, item_id, item);
// update index collection
- Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;
+ Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;
Ok(())
}
- fn item_exists(
- collection_id: CollectionId,
- item_id: TokenId,
- mode: &CollectionMode
- ) -> DispatchResult {
- match mode {
- CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
- CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
- _ => ()
- };
-
- Ok(())
- }
-
fn set_re_fungible_variable_data(
collection_id: CollectionId,
item_id: TokenId,
@@ -2090,12 +2104,12 @@
.unwrap();
let item_owner = item.owner.clone();
- Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();
+ Self::add_token_index(collection_id, current_index, &item.owner).unwrap();
<ItemListIndex>::insert(collection_id, current_index);
// Update balance
- let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
+ let new_balance = <Balance<T>>::get(collection_id, &item_owner)
.checked_add(1)
.unwrap();
<Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
@@ -2106,7 +2120,7 @@
.checked_add(1)
.unwrap();
- Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();
+ Self::add_token_index(collection_id, current_index, owner).unwrap();
<ItemListIndex>::insert(collection_id, current_index);
@@ -2125,24 +2139,24 @@
let value = item.owner.first().unwrap().fraction;
let owner = item.owner.first().unwrap().owner.clone();
- Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();
+ Self::add_token_index(collection_id, current_index, &owner).unwrap();
<ItemListIndex>::insert(collection_id, current_index);
// Update balance
- let new_balance = <Balance<T>>::get(collection_id, owner.clone())
+ let new_balance = <Balance<T>>::get(collection_id, &owner)
.checked_add(value)
.unwrap();
<Balance<T>>::insert(collection_id, owner.clone(), new_balance);
}
- fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {
+ fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {
// add to account limit
- if <AccountItemCount<T>>::contains_key(owner.clone()) {
+ if <AccountItemCount<T>>::contains_key(owner) {
// bound Owned tokens by a single address
- let count = <AccountItemCount<T>>::get(owner.clone());
+ let count = <AccountItemCount<T>>::get(owner);
ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);
<AccountItemCount<T>>::insert(owner.clone(), count
@@ -2153,9 +2167,9 @@
<AccountItemCount<T>>::insert(owner.clone(), 1);
}
- let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
+ let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);
if list_exists {
- let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
+ let mut list = <AddressTokens<T>>::get(collection_id, owner);
let item_contains = list.contains(&item_index.clone());
if !item_contains {
@@ -2166,8 +2180,7 @@
} else {
let mut itm = Vec::new();
itm.push(item_index.clone());
- <AddressTokens<T>>::insert(collection_id, owner, itm);
-
+ <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);
}
Ok(())
@@ -2176,24 +2189,24 @@
fn remove_token_index(
collection_id: CollectionId,
item_index: TokenId,
- owner: T::AccountId,
+ owner: &T::AccountId,
) -> DispatchResult {
// update counter
<AccountItemCount<T>>::insert(owner.clone(),
- <AccountItemCount<T>>::get(owner.clone())
+ <AccountItemCount<T>>::get(owner)
.checked_sub(1)
.ok_or(Error::<T>::NumOverflow)?);
- let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());
+ let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);
if list_exists {
- let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());
+ let mut list = <AddressTokens<T>>::get(collection_id, owner);
let item_contains = list.contains(&item_index.clone());
if item_contains {
list.retain(|&item| item != item_index);
- <AddressTokens<T>>::insert(collection_id, owner, list);
+ <AddressTokens<T>>::insert(collection_id, owner.clone(), list);
}
}
@@ -2203,8 +2216,8 @@
fn move_token_index(
collection_id: CollectionId,
item_index: TokenId,
- old_owner: T::AccountId,
- new_owner: T::AccountId,
+ old_owner: &T::AccountId,
+ new_owner: &T::AccountId,
) -> DispatchResult {
Self::remove_token_index(collection_id, item_index, old_owner)?;
Self::add_token_index(collection_id, item_index, new_owner)?;
@@ -2319,15 +2332,15 @@
let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;
let mut sponsored = true;
- if <CreateItemBasket<T>>::contains_key(collection_id) {
- let last_tx_block = <CreateItemBasket<T>>::get(collection_id);
+ if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {
+ let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));
let limit_time = last_tx_block + limit.into();
if block_number <= limit_time {
sponsored = false;
}
}
if sponsored {
- <CreateItemBasket<T>>::insert(collection_id, block_number);
+ <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);
}
// check free create limit
pallets/nft/src/tests.rsdiffbeforeafterboth1// Tests to be written here2use super::*;3use crate::mock::*;4use crate::{AccessMode, CollectionMode,5 Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,6 CollectionId, TokenId, MAX_DECIMAL_POINTS};7use frame_support::{assert_noop, assert_ok};8use frame_system::{ RawOrigin };910fn default_collection_numbers_limit() -> u32 {11 1012}1314fn default_limits() {15 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {16 collection_numbers_limit: default_collection_numbers_limit(),17 account_token_ownership_limit: 10,18 collections_admins_limit: 5,19 custom_data_limit: 2048,20 nft_sponsor_transfer_timeout: 15,21 fungible_sponsor_transfer_timeout: 15,22 refungible_sponsor_transfer_timeout: 15,23 const_on_chain_schema_limit: 1024,24 offchain_schema_limit: 1024,25 variable_on_chain_schema_limit: 1024,26 }));27}2829fn default_nft_data() -> CreateNftData {30 CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }31}3233fn default_fungible_data () -> CreateFungibleData {34 CreateFungibleData { value: 5 }35}3637fn default_re_fungible_data () -> CreateReFungibleData {38 CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }39}4041fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: CollectionId) -> CollectionId {42 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();43 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();44 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();4546 let origin1 = Origin::signed(owner);47 assert_ok!(TemplateModule::create_collection(48 origin1.clone(),49 col_name1.clone(),50 col_desc1.clone(),51 token_prefix1.clone(),52 mode.clone()53 ));5455 let saved_col_name: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();56 let saved_description: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();57 let saved_prefix: Vec<u8> = b"token_prefix1\0".to_vec();58 assert_eq!(TemplateModule::collection(id).owner, owner);59 assert_eq!(TemplateModule::collection(id).name, saved_col_name);60 assert_eq!(TemplateModule::collection(id).mode, *mode);61 assert_eq!(TemplateModule::collection(id).description, saved_description);62 assert_eq!(TemplateModule::collection(id).token_prefix, saved_prefix);63 id64}6566fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {67 create_test_collection_for_owner(&mode, 1, id)68}6970fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {71 let origin1 = Origin::signed(1);72 assert_ok!(TemplateModule::create_item(73 origin1.clone(),74 collection_id,75 1,76 data.clone()77 ));7879}8081// Use cases tests region82// #region8384#[test]85fn set_version_schema() {86 new_test_ext().execute_with(|| {87 default_limits();88 let origin1 = Origin::signed(1);89 let collection_id = create_test_collection(&CollectionMode::NFT, 1);90 91 assert_ok!(TemplateModule::set_schema_version(origin1, collection_id, SchemaVersion::Unique));92 assert_eq!(TemplateModule::collection(collection_id).schema_version, SchemaVersion::Unique);93 });94}9596#[test]97fn create_fungible_collection_fails_with_large_decimal_numbers() {98 new_test_ext().execute_with(|| {99 default_limits();100101 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();102 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();103 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();104105 let origin1 = Origin::signed(1);106 assert_noop!(TemplateModule::create_collection(107 origin1,108 col_name1,109 col_desc1,110 token_prefix1,111 CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1)112 ), Error::<Test>::CollectionDecimalPointLimitExceeded);113 }); 114}115116#[test]117fn create_re_fungible_collection_fails_with_large_decimal_numbers() {118 new_test_ext().execute_with(|| {119 default_limits();120121 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();122 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();123 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();124125 let origin1 = Origin::signed(1);126 assert_noop!(TemplateModule::create_collection(127 origin1,128 col_name1,129 col_desc1,130 token_prefix1,131 CollectionMode::ReFungible(MAX_DECIMAL_POINTS + 1)132 ), Error::<Test>::CollectionDecimalPointLimitExceeded);133 });134}135136#[test]137fn create_nft_item() {138 new_test_ext().execute_with(|| {139 default_limits();140 let collection_id = create_test_collection(&CollectionMode::NFT, 1);141 142 let data = default_nft_data();143 create_test_item(collection_id, &data.clone().into());144 assert_eq!(TemplateModule::nft_item_id(collection_id, 1).const_data, data.const_data);145 assert_eq!(TemplateModule::nft_item_id(collection_id, 1).variable_data, data.variable_data);146 });147}148149// Use cases tests region150// #region151#[test]152fn create_nft_multiple_items() {153 new_test_ext().execute_with(|| {154 default_limits();155 156 create_test_collection(&CollectionMode::NFT, 1);157158 let origin1 = Origin::signed(1);159160 let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];161162 assert_ok!(TemplateModule::create_multiple_items(163 origin1.clone(),164 1,165 1,166 items_data.clone().into_iter().map(|d| { d.into() }).collect()167 ));168 for (index, data) in items_data.iter().enumerate() {169 assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as TokenId).const_data.to_vec(), data.const_data);170 assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as TokenId).variable_data.to_vec(), data.variable_data);171 }172 });173}174175#[test]176fn create_refungible_item() {177 new_test_ext().execute_with(|| {178 default_limits();179 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);180181 let data = default_re_fungible_data();182 create_test_item(collection_id, &data.clone().into());183 assert_eq!(184 TemplateModule::refungible_item_id(collection_id, 1).const_data,185 data.const_data186 );187 assert_eq!(188 TemplateModule::refungible_item_id(collection_id, 1).variable_data,189 data.variable_data190 );191 assert_eq!(192 TemplateModule::refungible_item_id(collection_id, 1).owner[0],193 Ownership {194 owner: 1,195 fraction: 1000196 }197 );198 });199}200201#[test]202fn create_multiple_refungible_items() {203 new_test_ext().execute_with(|| {204 default_limits();205 206 create_test_collection(&CollectionMode::ReFungible(3), 1);207208 let origin1 = Origin::signed(1);209210 let items_data = vec![default_re_fungible_data(), default_re_fungible_data(), default_re_fungible_data()];211212 assert_ok!(TemplateModule::create_multiple_items(213 origin1.clone(),214 1,215 1,216 items_data.clone().into_iter().map(|d| { d.into() }).collect()217 ));218 for (index, data) in items_data.iter().enumerate() {219220 let item = TemplateModule::refungible_item_id(1, (index + 1) as TokenId);221 assert_eq!(item.const_data.to_vec(), data.const_data);222 assert_eq!(item.variable_data.to_vec(), data.variable_data);223 assert_eq!(224 item.owner[0],225 Ownership {226 owner: 1,227 fraction: 1000228 }229 );230 }231 });232}233234#[test]235fn create_fungible_item() {236 new_test_ext().execute_with(|| {237 default_limits();238 239 let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);240241 let data = default_fungible_data();242 create_test_item(collection_id, &data.into());243244 assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).value, 5);245 });246}247248//#[test]249// fn create_multiple_fungible_items() {250// new_test_ext().execute_with(|| {251// default_limits();252253// create_test_collection(&CollectionMode::Fungible(3), 1);254255// let origin1 = Origin::signed(1);256257// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];258259// assert_ok!(TemplateModule::create_multiple_items(260// origin1.clone(),261// 1,262// 1,263// items_data.clone().into_iter().map(|d| { d.into() }).collect()264// ));265 266// for (index, _) in items_data.iter().enumerate() {267// assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);268// }269// assert_eq!(TemplateModule::balance_count(1, 1), 3000);270// assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);271// });272// }273274#[test]275fn transfer_fungible_item() {276 new_test_ext().execute_with(|| {277 default_limits();278 279 let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);280281 let origin1 = Origin::signed(1);282 let origin2 = Origin::signed(2);283284 let data = default_fungible_data();285 create_test_item(collection_id, &data.into());286287 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 5);288 assert_eq!(TemplateModule::balance_count(1, 1), 5);289290 // change owner scenario291 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 5));292 assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 0);293 assert_eq!(TemplateModule::balance_count(1, 1), 0);294 assert_eq!(TemplateModule::balance_count(1, 2), 5);295296 // split item scenario297 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 3));298 assert_eq!(TemplateModule::balance_count(1, 2), 2);299 assert_eq!(TemplateModule::balance_count(1, 3), 3);300301 // split item and new owner has account scenario302 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 1));303 assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 1);304 assert_eq!(TemplateModule::fungible_item_id(1, 3).value, 4);305 assert_eq!(TemplateModule::balance_count(1, 2), 1);306 assert_eq!(TemplateModule::balance_count(1, 3), 4);307 });308}309310#[test]311fn transfer_refungible_item() {312 new_test_ext().execute_with(|| {313 default_limits();314 315 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);316317 let data = default_re_fungible_data();318 create_test_item(collection_id, &data.clone().into());319320 let origin1 = Origin::signed(1);321 let origin2 = Origin::signed(2);322 assert_eq!(323 TemplateModule::refungible_item_id(collection_id, 1).const_data,324 data.const_data325 );326 assert_eq!(327 TemplateModule::refungible_item_id(collection_id, 1).variable_data,328 data.variable_data329 );330 assert_eq!(331 TemplateModule::refungible_item_id(collection_id, 1).owner[0],332 Ownership {333 owner: 1,334 fraction: 1000335 }336 );337 assert_eq!(TemplateModule::balance_count(1, 1), 1000);338 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);339340 // change owner scenario341 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));342 assert_eq!(343 TemplateModule::refungible_item_id(1, 1).owner[0],344 Ownership {345 owner: 2,346 fraction: 1000347 }348 );349 assert_eq!(TemplateModule::balance_count(1, 1), 0);350 assert_eq!(TemplateModule::balance_count(1, 2), 1000);351 // assert_eq!(TemplateModule::address_tokens(1, 1), []);352 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);353354 // split item scenario355 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 500));356 assert_eq!(357 TemplateModule::refungible_item_id(1, 1).owner[0],358 Ownership {359 owner: 2,360 fraction: 500361 }362 );363 assert_eq!(364 TemplateModule::refungible_item_id(1, 1).owner[1],365 Ownership {366 owner: 3,367 fraction: 500368 }369 );370 assert_eq!(TemplateModule::balance_count(1, 2), 500);371 assert_eq!(TemplateModule::balance_count(1, 3), 500);372 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);373 assert_eq!(TemplateModule::address_tokens(1, 3), [1]);374375 // split item and new owner has account scenario376 assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 200));377 assert_eq!(378 TemplateModule::refungible_item_id(1, 1).owner[0],379 Ownership {380 owner: 2,381 fraction: 300382 }383 );384 assert_eq!(385 TemplateModule::refungible_item_id(1, 1).owner[1],386 Ownership {387 owner: 3,388 fraction: 700389 }390 );391 assert_eq!(TemplateModule::balance_count(1, 2), 300);392 assert_eq!(TemplateModule::balance_count(1, 3), 700);393 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);394 assert_eq!(TemplateModule::address_tokens(1, 3), [1]);395 });396}397398#[test]399fn transfer_nft_item() {400 new_test_ext().execute_with(|| {401 default_limits();402 403 let collection_id = create_test_collection(&CollectionMode::NFT, 1);404405 let data = default_nft_data();406 create_test_item(collection_id, &data.into());407 assert_eq!(TemplateModule::balance_count(1, 1), 1);408 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);409410 let origin1 = Origin::signed(1);411 // default scenario412 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));413 assert_eq!(TemplateModule::nft_item_id(1, 1).owner, 2);414 assert_eq!(TemplateModule::balance_count(1, 1), 0);415 assert_eq!(TemplateModule::balance_count(1, 2), 1);416 // assert_eq!(TemplateModule::address_tokens(1, 1), []);417 assert_eq!(TemplateModule::address_tokens(1, 2), [1]);418 });419}420421#[test]422fn nft_approve_and_transfer_from() {423 new_test_ext().execute_with(|| {424 default_limits();425 426 let collection_id = create_test_collection(&CollectionMode::NFT, 1);427428 let data = default_nft_data();429 create_test_item(collection_id, &data.into());430431 let origin1 = Origin::signed(1);432 let origin2 = Origin::signed(2);433434 assert_eq!(TemplateModule::balance_count(1, 1), 1);435 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);436437 // neg transfer438 assert_noop!(TemplateModule::transfer_from(439 origin2.clone(),440 1,441 2,442 1,443 1,444 1), Error::<Test>::NoPermission);445446 // do approve447 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));448 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);449 assert_eq!(450 TemplateModule::approved(1, (1, 1, 2)),451 5452 );453454 assert_ok!(TemplateModule::transfer_from(455 origin2.clone(),456 1,457 2,458 1,459 1,460 1461 ));462 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4);463 });464}465466#[test]467fn nft_approve_and_transfer_from_white_list() {468 new_test_ext().execute_with(|| {469 default_limits();470 471 let collection_id = create_test_collection(&CollectionMode::NFT, 1);472473 let origin1 = Origin::signed(1);474 let origin2 = Origin::signed(2);475476 let data = default_nft_data();477 create_test_item(collection_id, &data.clone().into());478479 assert_eq!(TemplateModule::nft_item_id(1, 1).const_data, data.const_data);480 assert_eq!(TemplateModule::balance_count(1, 1), 1);481 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);482483 assert_ok!(TemplateModule::set_mint_permission(484 origin1.clone(),485 1,486 true487 ));488 assert_ok!(TemplateModule::set_public_access_mode(489 origin1.clone(),490 1,491 AccessMode::WhiteList492 ));493 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));494 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));495 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));496497 // do approve498 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));499 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);500 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));501 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);502503 assert_ok!(TemplateModule::transfer_from(504 origin2.clone(),505 1,506 3,507 1,508 1,509 1510 ));511 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4);512 });513}514515#[test]516fn refungible_approve_and_transfer_from() {517 new_test_ext().execute_with(|| {518 default_limits();519 520 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);521 522 let origin1 = Origin::signed(1);523 let origin2 = Origin::signed(2);524525 let data = default_re_fungible_data();526 create_test_item(collection_id, &data.into());527528 assert_eq!(TemplateModule::balance_count(1, 1), 1000);529 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);530531 assert_ok!(TemplateModule::set_mint_permission(532 origin1.clone(),533 1,534 true535 ));536 assert_ok!(TemplateModule::set_public_access_mode(537 origin1.clone(),538 1,539 AccessMode::WhiteList540 ));541 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));542 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));543 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));544545 // do approve546 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));547 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);548 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000));549 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000);550551 assert_ok!(TemplateModule::transfer_from(552 origin2.clone(),553 1,554 3,555 1,556 1,557 100558 ));559 assert_eq!(TemplateModule::balance_count(1, 1), 900);560 assert_eq!(TemplateModule::balance_count(1, 3), 100);561 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);562 assert_eq!(TemplateModule::address_tokens(1, 3), [1]);563564 assert_eq!(565 TemplateModule::approved(1, (1, 1, 3)),566 900567 );568 });569}570571#[test]572fn fungible_approve_and_transfer_from() {573 new_test_ext().execute_with(|| {574 default_limits();575 576 let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);577 578 let data = default_fungible_data();579 create_test_item(collection_id, &data.into());580581 let origin1 = Origin::signed(1);582 let origin2 = Origin::signed(2);583584 assert_eq!(TemplateModule::balance_count(1, 1), 5);585586 assert_ok!(TemplateModule::set_mint_permission(587 origin1.clone(),588 1,589 true590 ));591 assert_ok!(TemplateModule::set_public_access_mode(592 origin1.clone(),593 1,594 AccessMode::WhiteList595 ));596 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));597 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));598 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));599600 // do approve601 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));602 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);603 assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));604 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);605 assert_eq!(606 TemplateModule::approved(1, (1, 1, 2)),607 5608 );609610 assert_ok!(TemplateModule::transfer_from(611 origin2.clone(),612 1,613 3,614 1,615 1,616 4617 ));618 assert_eq!(TemplateModule::balance_count(1, 1), 1);619 assert_eq!(TemplateModule::balance_count(1, 3), 4);620621 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);622 assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1);623624 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));625 assert_noop!(TemplateModule::transfer_from(626 origin2.clone(),627 1,628 3,629 1,630 1,631 4632 ), Error::<Test>::TokenValueNotEnough);633 });634}635636#[test]637fn change_collection_owner() {638 new_test_ext().execute_with(|| {639 default_limits();640 641 let collection_id = create_test_collection(&CollectionMode::NFT, 1);642 643 let origin1 = Origin::signed(1);644 assert_ok!(TemplateModule::change_collection_owner(645 origin1.clone(),646 collection_id,647 2648 ));649 assert_eq!(TemplateModule::collection(collection_id).owner, 2);650 });651}652653#[test]654fn destroy_collection() {655 new_test_ext().execute_with(|| {656 default_limits();657 658 let collection_id = create_test_collection(&CollectionMode::NFT, 1);659 660 let origin1 = Origin::signed(1);661 assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));662 });663}664665#[test]666fn burn_nft_item() {667 new_test_ext().execute_with(|| {668 default_limits();669 670 let collection_id = create_test_collection(&CollectionMode::NFT, 1);671672 let origin1 = Origin::signed(1);673 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));674 675 let data = default_nft_data();676 create_test_item(collection_id, &data.into());677678 // check balance (collection with id = 1, user id = 1)679 assert_eq!(TemplateModule::balance_count(1, 1), 1);680681 // burn item682 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));683 assert_noop!(684 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),685 Error::<Test>::TokenNotFound686 );687688 assert_eq!(TemplateModule::balance_count(1, 1), 0);689 });690}691692#[test]693fn burn_fungible_item() {694 new_test_ext().execute_with(|| {695 default_limits();696 697 let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);698 699 let origin1 = Origin::signed(1);700 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));701 702 let data = default_fungible_data();703 create_test_item(collection_id, &data.into());704705 // check balance (collection with id = 1, user id = 1)706 assert_eq!(TemplateModule::balance_count(1, 1), 5);707708 // burn item709 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));710 assert_noop!(711 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),712 Error::<Test>::TokenNotFound713 );714715 assert_eq!(TemplateModule::balance_count(1, 1), 0);716 });717}718719#[test]720fn burn_refungible_item() {721 new_test_ext().execute_with(|| {722 default_limits();723 724 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);725 let origin1 = Origin::signed(1);726727 assert_ok!(TemplateModule::set_mint_permission(728 origin1.clone(),729 collection_id,730 true731 ));732 assert_ok!(TemplateModule::set_public_access_mode(733 origin1.clone(),734 collection_id,735 AccessMode::WhiteList736 ));737 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));738739 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));740 741 let data = default_re_fungible_data();742 create_test_item(collection_id, &data.into());743744 // check balance (collection with id = 1, user id = 2)745 assert_eq!(TemplateModule::balance_count(1, 1), 1000);746747 // burn item748 assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000));749 assert_noop!(750 TemplateModule::burn_item(origin1.clone(), 1, 1, 1000),751 Error::<Test>::TokenNotFound752 );753754 assert_eq!(TemplateModule::balance_count(1, 1), 0);755 });756}757758#[test]759fn add_collection_admin() {760 new_test_ext().execute_with(|| {761 default_limits();762 763 let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);764 create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);765 create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);766 767 let origin1 = Origin::signed(1);768769 // collection admin770 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 2));771 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 3));772773 assert_eq!(TemplateModule::admin_list_collection(collection1_id).contains(&2), true);774 assert_eq!(TemplateModule::admin_list_collection(collection1_id).contains(&3), true);775 });776}777778#[test]779fn remove_collection_admin() {780 new_test_ext().execute_with(|| {781 default_limits();782 783 let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);784 create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);785 create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);786787 let origin1 = Origin::signed(1);788 let origin2 = Origin::signed(2);789790 // collection admin791 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 2));792 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 3));793794 assert_eq!(TemplateModule::admin_list_collection(1).contains(&2), true);795 assert_eq!(TemplateModule::admin_list_collection(1).contains(&3), true);796797 // remove admin798 assert_ok!(TemplateModule::remove_collection_admin(799 origin2.clone(),800 1,801 3802 ));803 assert_eq!(TemplateModule::admin_list_collection(1).contains(&3), false);804 });805}806807#[test]808fn balance_of() {809 new_test_ext().execute_with(|| {810 default_limits();811 812 let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1);813 let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2);814 let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible(3), 3);815 816 // check balance before817 assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 0);818 assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 0);819 assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 0);820821 let nft_data = default_nft_data();822 create_test_item(nft_collection_id, &nft_data.into());823 824 let fungible_data = default_fungible_data();825 create_test_item(fungible_collection_id, &fungible_data.into());826 827 let re_fungible_data = default_re_fungible_data();828 create_test_item(re_fungible_collection_id, &re_fungible_data.into());829830 // check balance (collection with id = 1, user id = 1)831 assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);832 assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5);833 assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);834 assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);835 assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5);836 assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);837 });838}839840#[test]841fn approve() {842 new_test_ext().execute_with(|| {843 default_limits();844 845 let collection_id = create_test_collection(&CollectionMode::NFT, 1);846 847 let data = default_nft_data();848 create_test_item(collection_id, &data.into());849850 let origin1 = Origin::signed(1);851 852 // approve853 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));854 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);855 });856}857858#[test]859fn transfer_from() {860 new_test_ext().execute_with(|| {861 default_limits();862 863 let collection_id = create_test_collection(&CollectionMode::NFT, 1);864 let origin1 = Origin::signed(1);865 let origin2 = Origin::signed(2);866867 let data = default_nft_data();868 create_test_item(collection_id, &data.into());869870 // approve871 assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));872 assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);873874 assert_ok!(TemplateModule::set_mint_permission(875 origin1.clone(),876 1,877 true878 ));879 assert_ok!(TemplateModule::set_public_access_mode(880 origin1.clone(),881 1,882 AccessMode::WhiteList883 ));884 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));885 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));886 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));887888 assert_ok!(TemplateModule::transfer_from(889 origin2.clone(),890 1,891 2,892 1,893 1,894 1895 ));896897 // after transfer898 assert_eq!(TemplateModule::balance_count(1, 1), 0);899 assert_eq!(TemplateModule::balance_count(1, 2), 1);900 });901}902903// #endregion904905// Coverage tests region906// #region907908#[test]909fn owner_can_add_address_to_white_list() {910 new_test_ext().execute_with(|| {911 default_limits();912 913 let collection_id = create_test_collection(&CollectionMode::NFT, 1);914915 let origin1 = Origin::signed(1);916 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));917 assert_eq!(TemplateModule::white_list(collection_id, 2), true);918 });919}920921#[test]922fn admin_can_add_address_to_white_list() {923 new_test_ext().execute_with(|| {924 default_limits();925 926 let collection_id = create_test_collection(&CollectionMode::NFT, 1);927 let origin1 = Origin::signed(1);928 let origin2 = Origin::signed(2);929930 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));931 assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3));932 assert_eq!(TemplateModule::white_list(collection_id, 3), true);933 });934}935936#[test]937fn nonprivileged_user_cannot_add_address_to_white_list() {938 new_test_ext().execute_with(|| {939 default_limits();940 941 let collection_id = create_test_collection(&CollectionMode::NFT, 1);942943 let origin2 = Origin::signed(2);944 assert_noop!(945 TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3),946 Error::<Test>::NoPermission947 );948 });949}950951#[test]952fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {953 new_test_ext().execute_with(|| {954 default_limits();955956 let origin1 = Origin::signed(1);957958 assert_noop!(959 TemplateModule::add_to_white_list(origin1.clone(), 1, 2),960 Error::<Test>::CollectionNotFound961 );962 });963}964965#[test]966fn nobody_can_add_address_to_white_list_of_deleted_collection() {967 new_test_ext().execute_with(|| {968 default_limits();969 970 let collection_id = create_test_collection(&CollectionMode::NFT, 1);971972 let origin1 = Origin::signed(1);973 assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));974 assert_noop!(975 TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2),976 Error::<Test>::CollectionNotFound977 );978 });979}980981// If address is already added to white list, nothing happens982#[test]983fn address_is_already_added_to_white_list() {984 new_test_ext().execute_with(|| {985 default_limits();986 987 let collection_id = create_test_collection(&CollectionMode::NFT, 1);988 let origin1 = Origin::signed(1);989 990 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));991 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));992 assert_eq!(TemplateModule::white_list(collection_id, 2), true);993 });994}995996#[test]997fn owner_can_remove_address_from_white_list() {998 new_test_ext().execute_with(|| {999 default_limits();1000 1001 let collection_id = create_test_collection(&CollectionMode::NFT, 1);10021003 let origin1 = Origin::signed(1);1004 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1005 assert_ok!(TemplateModule::remove_from_white_list(1006 origin1.clone(),1007 collection_id,1008 21009 ));1010 assert_eq!(TemplateModule::white_list(collection_id, 2), false);1011 });1012}10131014#[test]1015fn admin_can_remove_address_from_white_list() {1016 new_test_ext().execute_with(|| {1017 default_limits();1018 1019 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1020 let origin1 = Origin::signed(1);1021 let origin2 = Origin::signed(2);10221023 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));10241025 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 3));1026 assert_ok!(TemplateModule::remove_from_white_list(1027 origin2.clone(),1028 collection_id,1029 31030 ));1031 assert_eq!(TemplateModule::white_list(collection_id, 3), false);1032 });1033}10341035#[test]1036fn nonprivileged_user_cannot_remove_address_from_white_list() {1037 new_test_ext().execute_with(|| {1038 default_limits();1039 1040 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1041 let origin1 = Origin::signed(1);1042 let origin2 = Origin::signed(2);10431044 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1045 assert_noop!(1046 TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),1047 Error::<Test>::NoPermission1048 );1049 assert_eq!(TemplateModule::white_list(collection_id, 2), true);1050 });1051}10521053#[test]1054fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {1055 new_test_ext().execute_with(|| {1056 default_limits();1057 let origin1 = Origin::signed(1);10581059 assert_noop!(1060 TemplateModule::remove_from_white_list(origin1.clone(), 1, 2),1061 Error::<Test>::CollectionNotFound1062 );1063 });1064}10651066#[test]1067fn nobody_can_remove_address_from_white_list_of_deleted_collection() {1068 new_test_ext().execute_with(|| {1069 default_limits();1070 1071 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1072 let origin1 = Origin::signed(1);1073 let origin2 = Origin::signed(2);10741075 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1076 assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));1077 assert_noop!(1078 TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),1079 Error::<Test>::CollectionNotFound1080 );1081 assert_eq!(TemplateModule::white_list(collection_id, 2), false);1082 });1083}10841085// If address is already removed from white list, nothing happens1086#[test]1087fn address_is_already_removed_from_white_list() {1088 new_test_ext().execute_with(|| {1089 default_limits();1090 1091 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1092 let origin1 = Origin::signed(1);10931094 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));1095 assert_ok!(TemplateModule::remove_from_white_list(1096 origin1.clone(),1097 collection_id,1098 21099 ));1100 assert_ok!(TemplateModule::remove_from_white_list(1101 origin1.clone(),1102 collection_id,1103 21104 ));1105 assert_eq!(TemplateModule::white_list(collection_id, 2), false);1106 });1107}11081109// If Public Access mode is set to WhiteList, tokens can’t be transferred from a non-whitelisted address with transfer or transferFrom (2 tests)1110#[test]1111fn white_list_test_1() {1112 new_test_ext().execute_with(|| {1113 default_limits();1114 1115 let collection_id = create_test_collection(&CollectionMode::NFT, 1);11161117 let origin1 = Origin::signed(1);1118 1119 let data = default_nft_data();1120 create_test_item(collection_id, &data.into());11211122 assert_ok!(TemplateModule::set_public_access_mode(1123 origin1.clone(),1124 collection_id,1125 AccessMode::WhiteList1126 ));1127 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));11281129 assert_noop!(1130 TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),1131 Error::<Test>::AddresNotInWhiteList1132 );1133 });1134}11351136#[test]1137fn white_list_test_2() {1138 new_test_ext().execute_with(|| {1139 default_limits();1140 1141 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1142 let origin1 = Origin::signed(1);1143 1144 let data = default_nft_data();1145 create_test_item(collection_id, &data.into());11461147 assert_ok!(TemplateModule::set_public_access_mode(1148 origin1.clone(),1149 collection_id,1150 AccessMode::WhiteList1151 ));1152 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));1153 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));11541155 // do approve1156 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));1157 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);11581159 assert_ok!(TemplateModule::remove_from_white_list(1160 origin1.clone(),1161 1,1162 11163 ));11641165 assert_noop!(1166 TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),1167 Error::<Test>::AddresNotInWhiteList1168 );1169 });1170}11711172// If Public Access mode is set to WhiteList, tokens can’t be transferred to a non-whitelisted address with transfer or transferFrom (2 tests)1173#[test]1174fn white_list_test_3() {1175 new_test_ext().execute_with(|| {1176 default_limits();1177 1178 let collection_id = create_test_collection(&CollectionMode::NFT, 1);11791180 let origin1 = Origin::signed(1);1181 1182 let data = default_nft_data();1183 create_test_item(collection_id, &data.into());11841185 assert_ok!(TemplateModule::set_public_access_mode(1186 origin1.clone(),1187 collection_id,1188 AccessMode::WhiteList1189 ));1190 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));11911192 assert_noop!(1193 TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),1194 Error::<Test>::AddresNotInWhiteList1195 );1196 });1197}11981199#[test]1200fn white_list_test_4() {1201 new_test_ext().execute_with(|| {1202 default_limits();1203 1204 let collection_id = create_test_collection(&CollectionMode::NFT, 1);12051206 let origin1 = Origin::signed(1);12071208 let data = default_nft_data();1209 create_test_item(collection_id, &data.into());12101211 assert_ok!(TemplateModule::set_public_access_mode(1212 origin1.clone(),1213 collection_id,1214 AccessMode::WhiteList1215 ));1216 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 1));1217 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));12181219 // do approve1220 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));1221 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);12221223 assert_ok!(TemplateModule::remove_from_white_list(1224 origin1.clone(),1225 collection_id,1226 21227 ));12281229 assert_noop!(1230 TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),1231 Error::<Test>::AddresNotInWhiteList1232 );1233 });1234}12351236// 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)1237#[test]1238fn white_list_test_5() {1239 new_test_ext().execute_with(|| {1240 default_limits();1241 1242 let collection_id = create_test_collection(&CollectionMode::NFT, 1);12431244 let origin1 = Origin::signed(1);12451246 let data = default_nft_data();1247 create_test_item(collection_id, &data.into());12481249 assert_ok!(TemplateModule::set_public_access_mode(1250 origin1.clone(),1251 collection_id,1252 AccessMode::WhiteList1253 ));1254 assert_noop!(1255 TemplateModule::burn_item(origin1.clone(), 1, 1, 5),1256 Error::<Test>::AddresNotInWhiteList1257 );1258 });1259}12601261// If Public Access mode is set to WhiteList, token transfers can’t be Approved by a non-whitelisted address (see Approve method).1262#[test]1263fn white_list_test_6() {1264 new_test_ext().execute_with(|| {1265 default_limits();1266 1267 let collection_id = create_test_collection(&CollectionMode::NFT, 1);12681269 let origin1 = Origin::signed(1);12701271 let data = default_nft_data();1272 create_test_item(collection_id, &data.into());12731274 assert_ok!(TemplateModule::set_public_access_mode(1275 origin1.clone(),1276 collection_id,1277 AccessMode::WhiteList1278 ));12791280 // do approve1281 assert_noop!(1282 TemplateModule::approve(origin1.clone(), 1, 1, 1, 5),1283 Error::<Test>::AddresNotInWhiteList1284 );1285 });1286}12871288// If Public Access mode is set to WhiteList, tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests) and1289// tokens can be transferred from a whitelisted address with transfer or transferFrom (2 tests)1290#[test]1291fn white_list_test_7() {1292 new_test_ext().execute_with(|| {1293 default_limits();1294 1295 let collection_id = create_test_collection(&CollectionMode::NFT, 1);12961297 let data = default_nft_data();1298 create_test_item(collection_id, &data.into());1299 1300 let origin1 = Origin::signed(1);13011302 assert_ok!(TemplateModule::set_public_access_mode(1303 origin1.clone(),1304 collection_id,1305 AccessMode::WhiteList1306 ));1307 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 1));1308 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));13091310 assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1));1311 });1312}13131314#[test]1315fn white_list_test_8() {1316 new_test_ext().execute_with(|| {1317 default_limits();1318 1319 let collection_id = create_test_collection(&CollectionMode::NFT, 1);13201321 let data = default_nft_data();1322 create_test_item(collection_id, &data.into());1323 1324 let origin1 = Origin::signed(1);13251326 assert_ok!(TemplateModule::set_public_access_mode(1327 origin1.clone(),1328 collection_id,1329 AccessMode::WhiteList1330 ));1331 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 1));1332 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));13331334 // do approve1335 assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 5));1336 assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 5);13371338 assert_ok!(TemplateModule::transfer_from(1339 origin1.clone(),1340 1,1341 2,1342 1,1343 1,1344 11345 ));1346 });1347}13481349// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by owner.1350#[test]1351fn white_list_test_9() {1352 new_test_ext().execute_with(|| {1353 default_limits();1354 1355 let collection_id = create_test_collection(&CollectionMode::NFT, 1);1356 let origin1 = Origin::signed(1);13571358 assert_ok!(TemplateModule::set_public_access_mode(1359 origin1.clone(),1360 collection_id,1361 AccessMode::WhiteList1362 ));1363 assert_ok!(TemplateModule::set_mint_permission(1364 origin1.clone(),1365 collection_id,1366 false1367 ));13681369 let data = default_nft_data();1370 create_test_item(collection_id, &data.into());1371 });1372}13731374// If Public Access mode is set to WhiteList, and Mint Permission is set to false, tokens can be created by admin.1375#[test]1376fn white_list_test_10() {1377 new_test_ext().execute_with(|| {1378 default_limits();1379 1380 let collection_id = create_test_collection(&CollectionMode::NFT, 1);13811382 let origin1 = Origin::signed(1);1383 let origin2 = Origin::signed(2);13841385 assert_ok!(TemplateModule::set_public_access_mode(1386 origin1.clone(),1387 collection_id,1388 AccessMode::WhiteList1389 ));1390 assert_ok!(TemplateModule::set_mint_permission(1391 origin1.clone(),1392 collection_id,1393 false1394 ));13951396 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));13971398 assert_ok!(TemplateModule::create_item(1399 origin2.clone(),1400 collection_id,1401 2,1402 default_nft_data().into()1403 ));1404 });1405}14061407// 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.1408#[test]1409fn white_list_test_11() {1410 new_test_ext().execute_with(|| {1411 default_limits();1412 1413 let collection_id = create_test_collection(&CollectionMode::NFT, 1);14141415 let origin1 = Origin::signed(1);1416 let origin2 = Origin::signed(2);14171418 assert_ok!(TemplateModule::set_public_access_mode(1419 origin1.clone(),1420 collection_id,1421 AccessMode::WhiteList1422 ));1423 assert_ok!(TemplateModule::set_mint_permission(1424 origin1.clone(),1425 collection_id,1426 false1427 ));1428 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));14291430 assert_noop!(1431 TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),1432 Error::<Test>::PublicMintingNotAllowed1433 );1434 });1435}14361437// 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.1438#[test]1439fn white_list_test_12() {1440 new_test_ext().execute_with(|| {1441 default_limits();1442 1443 let collection_id = create_test_collection(&CollectionMode::NFT, 1);14441445 let origin1 = Origin::signed(1);1446 let origin2 = Origin::signed(2);14471448 assert_ok!(TemplateModule::set_public_access_mode(1449 origin1.clone(),1450 collection_id,1451 AccessMode::WhiteList1452 ));1453 assert_ok!(TemplateModule::set_mint_permission(1454 origin1.clone(),1455 collection_id,1456 false1457 ));14581459 assert_noop!(1460 TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),1461 Error::<Test>::PublicMintingNotAllowed1462 );1463 });1464}14651466// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by owner.1467#[test]1468fn white_list_test_13() {1469 new_test_ext().execute_with(|| {1470 default_limits();1471 1472 let collection_id = create_test_collection(&CollectionMode::NFT, 1);14731474 let origin1 = Origin::signed(1);14751476 assert_ok!(TemplateModule::set_public_access_mode(1477 origin1.clone(),1478 collection_id,1479 AccessMode::WhiteList1480 ));1481 assert_ok!(TemplateModule::set_mint_permission(1482 origin1.clone(),1483 collection_id,1484 true1485 ));14861487 let data = default_nft_data();1488 create_test_item(collection_id, &data.into());1489 });1490}14911492// If Public Access mode is set to WhiteList, and Mint Permission is set to true, tokens can be created by admin.1493#[test]1494fn white_list_test_14() {1495 new_test_ext().execute_with(|| {1496 default_limits();1497 1498 let collection_id = create_test_collection(&CollectionMode::NFT, 1);14991500 let origin1 = Origin::signed(1);1501 let origin2 = Origin::signed(2);15021503 assert_ok!(TemplateModule::set_public_access_mode(1504 origin1.clone(),1505 collection_id,1506 AccessMode::WhiteList1507 ));1508 assert_ok!(TemplateModule::set_mint_permission(1509 origin1.clone(),1510 collection_id,1511 true1512 ));15131514 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));15151516 assert_ok!(TemplateModule::create_item(1517 origin2.clone(),1518 1,1519 2,1520 default_nft_data().into()1521 ));1522 });1523}15241525// 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.1526#[test]1527fn white_list_test_15() {1528 new_test_ext().execute_with(|| {1529 default_limits();1530 1531 let collection_id = create_test_collection(&CollectionMode::NFT, 1);15321533 let origin1 = Origin::signed(1);1534 let origin2 = Origin::signed(2);15351536 assert_ok!(TemplateModule::set_public_access_mode(1537 origin1.clone(),1538 collection_id,1539 AccessMode::WhiteList1540 ));1541 assert_ok!(TemplateModule::set_mint_permission(1542 origin1.clone(),1543 collection_id,1544 true1545 ));15461547 assert_noop!(1548 TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),1549 Error::<Test>::AddresNotInWhiteList1550 );1551 });1552}15531554// 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.1555#[test]1556fn white_list_test_16() {1557 new_test_ext().execute_with(|| {1558 default_limits();1559 1560 let collection_id = create_test_collection(&CollectionMode::NFT, 1);15611562 let origin1 = Origin::signed(1);1563 let origin2 = Origin::signed(2);15641565 assert_ok!(TemplateModule::set_public_access_mode(1566 origin1.clone(),1567 collection_id,1568 AccessMode::WhiteList1569 ));1570 assert_ok!(TemplateModule::set_mint_permission(1571 origin1.clone(),1572 collection_id,1573 true1574 ));1575 assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));15761577 assert_ok!(TemplateModule::create_item(1578 origin2.clone(),1579 1,1580 2,1581 default_nft_data().into()1582 ));1583 });1584}15851586// Total number of collections. Positive test1587#[test]1588fn total_number_collections_bound() {1589 new_test_ext().execute_with(|| {1590 default_limits();1591 1592 create_test_collection(&CollectionMode::NFT, 1);1593 });1594}15951596// Total number of collections. Negotive test1597#[test]1598fn total_number_collections_bound_neg() {1599 new_test_ext().execute_with(|| {1600 default_limits();16011602 let origin1 = Origin::signed(1);16031604 for i in 0..default_collection_numbers_limit() {1605 create_test_collection(&CollectionMode::NFT, i + 1);1606 }16071608 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();1609 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();1610 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();16111612 // 11-th collection in chain. Expects error1613 assert_noop!(TemplateModule::create_collection(1614 origin1.clone(),1615 col_name1.clone(),1616 col_desc1.clone(),1617 token_prefix1.clone(),1618 CollectionMode::NFT1619 ), Error::<Test>::TotalCollectionsLimitExceeded);1620 });1621}16221623// Owned tokens by a single address. Positive test1624#[test]1625fn owned_tokens_bound() {1626 new_test_ext().execute_with(|| {1627 default_limits();1628 1629 let collection_id = create_test_collection(&CollectionMode::NFT, 1);16301631 let data = default_nft_data();1632 create_test_item(collection_id, &data.clone().into());1633 create_test_item(collection_id, &data.into());1634 });1635}16361637// Owned tokens by a single address. Negotive test1638#[test]1639fn owned_tokens_bound_neg() {1640 new_test_ext().execute_with(|| {1641 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1642 collection_numbers_limit: 10,1643 account_token_ownership_limit: 1,1644 collections_admins_limit: 5,1645 custom_data_limit: 2048,1646 nft_sponsor_transfer_timeout: 15,1647 fungible_sponsor_transfer_timeout: 15,1648 refungible_sponsor_transfer_timeout: 15,1649 const_on_chain_schema_limit: 1024,1650 offchain_schema_limit: 1024,1651 variable_on_chain_schema_limit: 1024,1652 }));1653 1654 let collection_id = create_test_collection(&CollectionMode::NFT, 1);16551656 let origin1 = Origin::signed(1);1657 let data = default_nft_data();1658 create_test_item(collection_id, &data.clone().into());16591660 assert_noop!(TemplateModule::create_item(1661 origin1.clone(),1662 1,1663 1,1664 data.into()1665 ), Error::<Test>::AddressOwnershipLimitExceeded);1666 });1667}16681669// Number of collection admins. Positive test1670#[test]1671fn collection_admins_bound() {1672 new_test_ext().execute_with(|| {1673 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1674 collection_numbers_limit: 10,1675 account_token_ownership_limit: 10,1676 collections_admins_limit: 2,1677 custom_data_limit: 2048,1678 nft_sponsor_transfer_timeout: 15,1679 fungible_sponsor_transfer_timeout: 15,1680 refungible_sponsor_transfer_timeout: 15,1681 const_on_chain_schema_limit: 1024,1682 offchain_schema_limit: 1024,1683 variable_on_chain_schema_limit: 1024,1684 }));1685 1686 let collection_id = create_test_collection(&CollectionMode::NFT, 1);16871688 let origin1 = Origin::signed(1);1689 1690 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));1691 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3));1692 });1693}16941695// Number of collection admins. Negotive test1696#[test]1697fn collection_admins_bound_neg() {1698 new_test_ext().execute_with(|| {1699 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1700 collection_numbers_limit: 10,1701 account_token_ownership_limit: 1,1702 collections_admins_limit: 1,1703 custom_data_limit: 2048,1704 nft_sponsor_transfer_timeout: 15,1705 fungible_sponsor_transfer_timeout: 15,1706 refungible_sponsor_transfer_timeout: 15,1707 const_on_chain_schema_limit: 1024,1708 offchain_schema_limit: 1024,1709 variable_on_chain_schema_limit: 1024,1710 }));1711 1712 let collection_id = create_test_collection(&CollectionMode::NFT, 1);17131714 let origin1 = Origin::signed(1);17151716 assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));1717 assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3), Error::<Test>::CollectionAdminsLimitExceeded);1718 });1719}17201721// NFT custom data size. Negative test const_data.1722#[test]1723fn custom_data_size_nft_const_data_bound_neg() {1724 new_test_ext().execute_with(|| {1725 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1726 collection_numbers_limit: 10,1727 account_token_ownership_limit: 10,1728 collections_admins_limit: 5,1729 custom_data_limit: 2,1730 nft_sponsor_transfer_timeout: 15,1731 fungible_sponsor_transfer_timeout: 15,1732 refungible_sponsor_transfer_timeout: 15,1733 const_on_chain_schema_limit: 1024,1734 offchain_schema_limit: 1024,1735 variable_on_chain_schema_limit: 1024,1736 }));1737 1738 let collection_id = create_test_collection(&CollectionMode::NFT, 1);17391740 let origin1 = Origin::signed(1);1741 let too_big_const_data = CreateItemData::NFT(CreateNftData{1742 const_data: vec![1, 2, 3, 4],1743 variable_data: vec![]1744 });17451746 assert_noop!(TemplateModule::create_item(1747 origin1.clone(),1748 collection_id,1749 1,1750 too_big_const_data1751 ), Error::<Test>::TokenConstDataLimitExceeded);1752 });1753}17541755// NFT custom data size. Negative test variable_data.1756#[test]1757fn custom_data_size_nft_variable_data_bound_neg() {1758 new_test_ext().execute_with(|| {1759 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1760 collection_numbers_limit: 10,1761 account_token_ownership_limit: 10,1762 collections_admins_limit: 5,1763 custom_data_limit: 2,1764 nft_sponsor_transfer_timeout: 15,1765 fungible_sponsor_transfer_timeout: 15,1766 refungible_sponsor_transfer_timeout: 15,1767 const_on_chain_schema_limit: 1024,1768 offchain_schema_limit: 1024,1769 variable_on_chain_schema_limit: 1024,1770 }));17711772 let collection_id = create_test_collection(&CollectionMode::NFT, 1);17731774 let origin1 = Origin::signed(1);1775 let too_big_const_data = CreateItemData::NFT(CreateNftData{1776 const_data: vec![],1777 variable_data: vec![1, 2, 3, 4]1778 });17791780 assert_noop!(TemplateModule::create_item(1781 origin1.clone(),1782 collection_id,1783 1,1784 too_big_const_data1785 ), Error::<Test>::TokenVariableDataLimitExceeded);1786 });1787}17881789// Re fungible custom data size. Negative test const_data.1790#[test]1791fn custom_data_size_re_fungible_const_data_bound_neg() {1792 new_test_ext().execute_with(|| {1793 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1794 collection_numbers_limit: 10,1795 account_token_ownership_limit: 10,1796 collections_admins_limit: 5,1797 custom_data_limit: 2,1798 nft_sponsor_transfer_timeout: 15,1799 fungible_sponsor_transfer_timeout: 15,1800 refungible_sponsor_transfer_timeout: 15,1801 const_on_chain_schema_limit: 1024,1802 offchain_schema_limit: 1024,1803 variable_on_chain_schema_limit: 1024,1804 }));18051806 let collection_id = create_test_collection(&CollectionMode::NFT, 1);18071808 let origin1 = Origin::signed(1);1809 let too_big_const_data = CreateItemData::NFT(CreateNftData{1810 const_data: vec![1, 2, 3, 4],1811 variable_data: vec![]1812 });18131814 assert_noop!(TemplateModule::create_item(1815 origin1.clone(),1816 collection_id,1817 1,1818 too_big_const_data1819 ), Error::<Test>::TokenConstDataLimitExceeded);1820 });1821}18221823// Re fungible custom data size. Negative test variable_data.1824#[test]1825fn custom_data_size_re_fungible_variable_data_bound_neg() {1826 new_test_ext().execute_with(|| {1827 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1828 collection_numbers_limit: 10,1829 account_token_ownership_limit: 10,1830 collections_admins_limit: 5,1831 custom_data_limit: 2,1832 nft_sponsor_transfer_timeout: 15,1833 fungible_sponsor_transfer_timeout: 15,1834 refungible_sponsor_transfer_timeout: 15,1835 const_on_chain_schema_limit: 1024,1836 offchain_schema_limit: 1024,1837 variable_on_chain_schema_limit: 1024,1838 }));18391840 let collection_id = create_test_collection(&CollectionMode::NFT, 1);18411842 let origin1 = Origin::signed(1);1843 let too_big_const_data = CreateItemData::NFT(CreateNftData{1844 const_data: vec![],1845 variable_data: vec![1, 2, 3, 4]1846 });18471848 assert_noop!(TemplateModule::create_item(1849 origin1.clone(),1850 collection_id,1851 1,1852 too_big_const_data1853 ), Error::<Test>::TokenVariableDataLimitExceeded);1854 });1855}1856// #endregion18571858#[test]1859fn set_const_on_chain_schema() {1860 new_test_ext().execute_with(|| {1861 default_limits();18621863 let collection_id = create_test_collection(&CollectionMode::NFT, 1);18641865 let origin1 = Origin::signed(1);1866 assert_ok!(TemplateModule::set_const_on_chain_schema(origin1, collection_id, b"test const on chain schema".to_vec()));18671868 assert_eq!(TemplateModule::collection(collection_id).const_on_chain_schema, b"test const on chain schema".to_vec());1869 assert_eq!(TemplateModule::collection(collection_id).variable_on_chain_schema, b"".to_vec());1870 });1871}18721873#[test]1874fn set_variable_on_chain_schema() {1875 new_test_ext().execute_with(|| {1876 default_limits();1877 1878 let collection_id = create_test_collection(&CollectionMode::NFT, 1);18791880 let origin1 = Origin::signed(1);1881 assert_ok!(TemplateModule::set_variable_on_chain_schema(origin1, collection_id, b"test variable on chain schema".to_vec()));18821883 assert_eq!(TemplateModule::collection(collection_id).const_on_chain_schema, b"".to_vec());1884 assert_eq!(TemplateModule::collection(collection_id).variable_on_chain_schema, b"test variable on chain schema".to_vec());1885 });1886}18871888#[test]1889fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {1890 new_test_ext().execute_with(|| {1891 default_limits();18921893 let collection_id = create_test_collection(&CollectionMode::NFT, 1);18941895 let origin1 = Origin::signed(1);1896 1897 let data = default_nft_data();1898 create_test_item(1, &data.into());1899 1900 let variable_data = b"test set_variable_meta_data method.".to_vec();1901 assert_ok!(TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data.clone()));19021903 assert_eq!(TemplateModule::nft_item_id(collection_id, 1).variable_data, variable_data);1904 });1905}19061907#[test]1908fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {1909 new_test_ext().execute_with(|| {1910 default_limits();19111912 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);19131914 let origin1 = Origin::signed(1);19151916 let data = default_re_fungible_data();1917 create_test_item(1, &data.into());19181919 let variable_data = b"test set_variable_meta_data method.".to_vec();1920 assert_ok!(TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data.clone()));19211922 assert_eq!(TemplateModule::refungible_item_id(collection_id, 1).variable_data, variable_data);1923 });1924}192519261927#[test]1928fn set_variable_meta_data_on_fungible_token_fails() {1929 new_test_ext().execute_with(|| {1930 default_limits();19311932 let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);19331934 let origin1 = Origin::signed(1);19351936 let data = default_fungible_data();1937 create_test_item(1, &data.into());19381939 let variable_data = b"test set_variable_meta_data method.".to_vec();1940 assert_noop!(TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data.clone()), Error::<Test>::CantStoreMetadataInFungibleTokens);1941 });1942}19431944#[test]1945fn set_variable_meta_data_on_nft_token_fails_for_big_data() {1946 new_test_ext().execute_with(|| {1947 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1948 collection_numbers_limit: default_collection_numbers_limit(),1949 account_token_ownership_limit: 10,1950 collections_admins_limit: 5,1951 custom_data_limit: 10,1952 nft_sponsor_transfer_timeout: 15,1953 fungible_sponsor_transfer_timeout: 15,1954 refungible_sponsor_transfer_timeout: 15,1955 const_on_chain_schema_limit: 1024,1956 offchain_schema_limit: 1024,1957 variable_on_chain_schema_limit: 1024,1958 }));19591960 let collection_id = create_test_collection(&CollectionMode::NFT, 1);19611962 let origin1 = Origin::signed(1);19631964 let data = default_nft_data();1965 create_test_item(1, &data.into());19661967 let variable_data = b"test set_variable_meta_data method, bigger than limits.".to_vec();1968 assert_noop!(TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data), Error::<Test>::TokenVariableDataLimitExceeded);1969 });1970}19711972#[test]1973fn set_variable_meta_data_on_re_fungible_token_fails_for_big_data() {1974 new_test_ext().execute_with(|| {1975 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 1976 collection_numbers_limit: default_collection_numbers_limit(),1977 account_token_ownership_limit: 10,1978 collections_admins_limit: 5,1979 custom_data_limit: 10,1980 nft_sponsor_transfer_timeout: 15,1981 fungible_sponsor_transfer_timeout: 15,1982 refungible_sponsor_transfer_timeout: 15,1983 const_on_chain_schema_limit: 1024,1984 offchain_schema_limit: 1024,1985 variable_on_chain_schema_limit: 1024,1986 }));198719881989 let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);19901991 let origin1 = Origin::signed(1);19921993 let data = default_re_fungible_data();1994 create_test_item(1, &data.into());19951996 let variable_data = b"test set_variable_meta_data method, bigger than limits.".to_vec();1997 assert_noop!(TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data), Error::<Test>::TokenVariableDataLimitExceeded);1998 });1999}tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -18,10 +18,19 @@
"scripts": {
"test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts",
"load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts",
+ "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
"testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
"testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
+ "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
"testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
- "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts"
+ "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+ "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
+ "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
+ "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+ "testToggleContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractWhiteList.test.ts",
+ "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts",
+ "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",
+ "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts"
},
"author": "",
"license": "Apache 2.0",
tests/src/addCollectionAdmin.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -0,0 +1,130 @@
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+ it('Add collection admin.', async () => {
+ await usingApi(async (api) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const alice = privateKey('//Alice');
+ const bob = privateKey('//Bob');
+
+ const collection: any = (await api.query.nft.collection(collectionId));
+ expect(collection.Owner.toString()).to.be.eq(alice.address);
+
+ const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, bob.address);
+ await submitTransactionAsync(alice, changeAdminTx);
+
+ const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddAdmin).to.be.contains(bob.address);
+ });
+ });
+
+ it('Add admin using added collection admin.', async () => {
+ await usingApi(async (api) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+
+ const collection: any = (await api.query.nft.collection(collectionId));
+ expect(collection.Owner.toString()).to.be.eq(Alice.address);
+
+ const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+ await submitTransactionAsync(Alice, changeAdminTx);
+
+ const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddAdmin).to.be.contains(Bob.address);
+
+ const changeAdminTxCharlie = api.tx.nft.addCollectionAdmin(collectionId, Charlie.address);
+ await submitTransactionAsync(Bob, changeAdminTxCharlie);
+ const adminListAfterAddNewAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddNewAdmin).to.be.contains(Bob.address);
+ expect(adminListAfterAddNewAdmin).to.be.contains(Charlie.address);
+ });
+ });
+});
+
+describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+ it("Not owner can't add collection admin.", async () => {
+ await usingApi(async (api) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const alice = privateKey('//Alice');
+ const nonOwner = privateKey('//Bob_stash');
+
+ const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, alice.address);
+ await expect(submitTransactionExpectFailAsync(nonOwner, changeAdminTx)).to.be.rejected;
+
+ const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddAdmin).not.to.be.contains(alice.address);
+
+ // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+ await createCollectionExpectSuccess();
+ });
+ });
+ it("Can't add collection admin of not existing collection.", async () => {
+ await usingApi(async (api) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = (1 << 32) - 1;
+ const alice = privateKey('//Alice');
+ const bob = privateKey('//Bob');
+
+ const changeOwnerTx = api.tx.nft.addCollectionAdmin(collectionId, bob.address);
+ await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;
+
+ // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+ await createCollectionExpectSuccess();
+ });
+ });
+
+ it("Can't add an admin to a destroyed collection.", async () => {
+ await usingApi(async (api) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ await destroyCollectionExpectSuccess(collectionId);
+ const changeOwnerTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+ await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected;
+
+ // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+ await createCollectionExpectSuccess();
+ });
+ });
+
+ it('Add an admin to a collection that has reached the maximum number of admins limit', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const accounts = [
+ 'GsvVmjr1CBHwQHw84pPHMDxgNY3iBLz6Qn7qS3CH8qPhrHz',
+ 'FoQJpPyadYccjavVdTWxpxU7rUEaYhfLCPwXgkfD6Zat9QP',
+ 'JKspFU6ohf1Grg3Phdzj2pSgWvsYWzSfKghhfzMbdhNBWs5',
+ 'Fr4NzY1udSFFLzb2R3qxVQkwz9cZraWkyfH4h3mVVk7BK7P',
+ 'DfnTB4z7eUvYRqcGtTpFsLC69o6tvBSC1pEv8vWPZFtCkaK',
+ 'HnMAUz7r2G8G3hB27SYNyit5aJmh2a5P4eMdDtACtMFDbam',
+ 'DE14BzQ1bDXWPKeLoAqdLAm1GpyAWaWF1knF74cEZeomTBM',
+ ];
+ const collectionId = await createCollectionExpectSuccess();
+
+ const chainLimit = await api.query.nft.chainLimit() as unknown as { collections_admins_limit: BN };
+ const chainLimitNumber = chainLimit.collections_admins_limit.toNumber();
+ expect(chainLimitNumber).to.be.equal(5);
+
+ for (let i = 0; i < chainLimitNumber; i++) {
+ const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, accounts[i]);
+ await submitTransactionAsync(Alice, changeAdminTx);
+ const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddAdmin).to.be.contains(accounts[i]);
+ }
+
+ const tx = api.tx.nft.addCollectionAdmin(collectionId, accounts[chainLimitNumber]);
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+});
tests/src/addToContractWhiteList.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/addToContractWhiteList.test.ts
@@ -0,0 +1,91 @@
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
+import privateKey from "./substrate/privateKey";
+import {
+ deployFlipper,
+ getFlipValue
+} from "./util/contracthelpers";
+import {
+ getGenericResult
+} from "./util/helpers"
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+
+describe('Integration Test addToContractWhiteList', () => {
+
+ it(`Add an address to a contract white list`, async () => {
+ await usingApi(async api => {
+ const bob = privateKey("//Bob");
+ const [contract, deployer] = await deployFlipper(api);
+
+ const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+ const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+ const addEvents = await submitTransactionAsync(deployer, addTx);
+ const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+ expect(getGenericResult(addEvents).success).to.be.true;
+ expect(whiteListedBefore).to.be.false;
+ expect(whiteListedAfter).to.be.true;
+ });
+ });
+
+ it(`Adding same address to white list repeatedly should not produce errors`, async () => {
+ await usingApi(async api => {
+ const bob = privateKey("//Bob");
+ const [contract, deployer] = await deployFlipper(api);
+
+ const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+ const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+ const addEvents = await submitTransactionAsync(deployer, addTx);
+ const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+ const addAgainEvents = await submitTransactionAsync(deployer, addTx);
+ const whiteListedAgainAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+ expect(getGenericResult(addEvents).success).to.be.true;
+ expect(whiteListedBefore).to.be.false;
+ expect(whiteListedAfter).to.be.true;
+ expect(getGenericResult(addAgainEvents).success).to.be.true;
+ expect(whiteListedAgainAfter).to.be.true;
+ });
+ });
+});
+
+describe('Negative Integration Test addToContractWhiteList', () => {
+
+ it(`Add an address to a white list of a non-contract`, async () => {
+ await usingApi(async api => {
+ const alice = privateKey("//Bob");
+ const bob = privateKey("//Bob");
+ const charlieGuineaPig = privateKey("//Charlie");
+
+ const whiteListedBefore = (await api.query.nft.contractWhiteList(charlieGuineaPig.address, bob.address)).toJSON();
+ const addTx = api.tx.nft.addToContractWhiteList(charlieGuineaPig.address, bob.address);
+ await expect(submitTransactionExpectFailAsync(alice, addTx)).to.be.rejected;
+ const whiteListedAfter = (await api.query.nft.contractWhiteList(charlieGuineaPig.address, bob.address)).toJSON();
+
+ expect(whiteListedBefore).to.be.false;
+ expect(whiteListedAfter).to.be.false;
+ });
+ });
+
+ it(`Add to a contract white list using a non-owner address`, async () => {
+ await usingApi(async api => {
+ const bob = privateKey("//Bob");
+ const [contract, deployer] = await deployFlipper(api);
+
+ const whiteListedBefore = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+ const addTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+ await expect(submitTransactionExpectFailAsync(bob, addTx)).to.be.rejected;
+ const whiteListedAfter = (await api.query.nft.contractWhiteList(contract.address, bob.address)).toJSON();
+
+ expect(whiteListedBefore).to.be.false;
+ expect(whiteListedAfter).to.be.false;
+ });
+ });
+
+});
tests/src/addToWhiteList.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/addToWhiteList.test.ts
@@ -0,0 +1,81 @@
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi, { submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+ addToWhiteListExpectSuccess,
+ createCollectionExpectSuccess,
+ createItemExpectSuccess,
+ destroyCollectionExpectSuccess,
+ enablePublicMintingExpectSuccess,
+ enableWhiteListExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+
+describe('Integration Test ext. addToWhiteList()', () => {
+
+ before(async () => {
+ await usingApi(async (api) => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ });
+ });
+
+ it('Execute the extrinsic with parameters: Collection ID and address to add to the white list', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+ });
+
+ it('Whitelisted minting: list restrictions', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ await enablePublicMintingExpectSuccess(Alice, collectionId);
+ await createItemExpectSuccess(Bob, collectionId, 'NFT', Bob.address);
+ });
+});
+
+describe('Negative Integration Test ext. addToWhiteList()', () => {
+
+ it('White list an address in the collection that does not exist', async () => {
+ await usingApi(async (api) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+ const Bob = privateKey('//Bob');
+
+ const tx = api.tx.nft.addToWhiteList(collectionId, Bob.address);
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+
+ it('White list an address in the collection that was destroyed', async () => {
+ await usingApi(async (api) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = await createCollectionExpectSuccess();
+ await destroyCollectionExpectSuccess(collectionId);
+ const tx = api.tx.nft.addToWhiteList(collectionId, Bob.address);
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+
+ it('White list an address in the collection that does not have white list access enabled', async () => {
+ await usingApi(async (api) => {
+ const Alice = privateKey('//Alice');
+ const Ferdie = privateKey('//Ferdie');
+ const collectionId = await createCollectionExpectSuccess();
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ await enablePublicMintingExpectSuccess(Alice, collectionId);
+ const tx = api.tx.nft.createItem(collectionId, Ferdie.address, 'NFT');
+ await expect(submitTransactionExpectFailAsync(Ferdie, tx)).to.be.rejected;
+ });
+ });
+
+});
tests/src/approve.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/approve.test.ts
@@ -0,0 +1,140 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+ approveExpectFail,
+ approveExpectSuccess,
+ createCollectionExpectSuccess,
+ createItemExpectSuccess,
+ destroyCollectionExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
+ it('Execute the extrinsic and check approvedList', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const nftCollectionId = await createCollectionExpectSuccess();
+ // nft
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+ // reFungible
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+ });
+ });
+
+ it('Remove approval by using 0 amount', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const nftCollectionId = await createCollectionExpectSuccess();
+ // nft
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);
+ await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
+ // reFungible
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
+ });
+ });
+});
+
+describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
+ it('Approve for a collection that does not exist', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ // nft
+ const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+ // fungible
+ const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+ // reFungible
+ const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+ });
+ });
+
+ it('Approve for a collection that was destroyed', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ await destroyCollectionExpectSuccess(nftCollectionId);
+ await approveExpectFail(nftCollectionId, 1, Alice, Bob);
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await destroyCollectionExpectSuccess(fungibleCollectionId);
+ await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
+ // reFungible
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ await destroyCollectionExpectSuccess(reFungibleCollectionId);
+ await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
+ });
+ });
+
+ it('Approve transfer of a token that does not exist', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ await approveExpectFail(nftCollectionId, 2, Alice, Bob);
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
+ // reFungible
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
+ });
+ });
+
+ it('Approve using the address that does not own the approved token', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const nftCollectionId = await createCollectionExpectSuccess();
+ // nft
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
+ // reFungible
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
+ });
+ });
+});
tests/src/burnItem.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/burnItem.test.ts
@@ -0,0 +1,237 @@
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import {
+ createCollectionExpectSuccess,
+ createItemExpectSuccess,
+ getGenericResult,
+ destroyCollectionExpectSuccess
+} from './util/helpers';
+import { nullPublicKey } from "./accounts";
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+
+describe('integration test: ext. burnItem():', () => {
+ before(async () => {
+ await usingApi(async (api) => {
+ const keyring = new Keyring({ type: 'sr25519' });
+ alice = keyring.addFromUri(`//Alice`);
+ bob = keyring.addFromUri(`//Bob`);
+ });
+ });
+
+ it('Burn item in NFT collection', async () => {
+ const createMode = 'NFT';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getGenericResult(events);
+ // Get the item
+ const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
+ // tslint:disable-next-line:no-unused-expression
+ expect(item).to.be.not.null;
+ expect(item.Owner).to.be.equal(nullPublicKey);
+ });
+
+ });
+ it('Burn item in Fungible collection', async () => {
+ const createMode = 'Fungible';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+ await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens
+ const tokenId = 0; // ignored
+
+ await usingApi(async (api) => {
+ // Destroy 1 of 10
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getGenericResult(events);
+
+ // Get alice balance
+ const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+
+ // What to expect
+ expect(result.success).to.be.true;
+ expect(balance).to.be.not.null;
+ expect(balance.Value).to.be.equal(9);
+ });
+
+ });
+ it('Burn item in ReFungible collection', async () => {
+ const createMode = 'ReFungible';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+ const events = await submitTransactionAsync(alice, tx);
+ const result = getGenericResult(events);
+
+ // Get alice balance
+ const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+
+ // What to expect
+ expect(result.success).to.be.true;
+ expect(balance).to.be.not.null;
+ expect(balance.Owner.length).to.be.equal(0);
+ });
+
+ });
+
+ it('Burn owned portion of item in ReFungible collection', async () => {
+ const createMode = 'ReFungible';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+ await usingApi(async (api) => {
+ // Transfer 1/100 of the token to Bob
+ const transfertx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 1);
+ const events1 = await submitTransactionAsync(alice, transfertx);
+ const result1 = getGenericResult(events1);
+
+ // Get balances
+ const balanceBefore: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+
+ // Bob burns his portion
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const events2 = await submitTransactionAsync(bob, tx);
+ const result2 = getGenericResult(events2);
+
+ // Get balances
+ const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+ // console.log(balance);
+
+ // What to expect before burning
+ expect(result1.success).to.be.true;
+ expect(balanceBefore).to.be.not.null;
+ expect(balanceBefore.Owner.length).to.be.equal(2);
+ expect(balanceBefore.Owner[0].Owner).to.be.equal(alice.address);
+ expect(balanceBefore.Owner[0].Fraction).to.be.equal(99);
+ expect(balanceBefore.Owner[1].Owner).to.be.equal(bob.address);
+ expect(balanceBefore.Owner[1].Fraction).to.be.equal(1);
+
+ // What to expect after burning
+ expect(result2.success).to.be.true;
+ expect(balance).to.be.not.null;
+ expect(balance.Owner.length).to.be.equal(1);
+ expect(balance.Owner[0].Fraction).to.be.equal(99);
+ expect(balance.Owner[0].Owner).to.be.equal(alice.address);
+ });
+
+ });
+
+});
+
+describe('Negative integration test: ext. burnItem():', () => {
+ before(async () => {
+ await usingApi(async (api) => {
+ const keyring = new Keyring({ type: 'sr25519' });
+ alice = keyring.addFromUri(`//Alice`);
+ bob = keyring.addFromUri(`//Bob`);
+ });
+ });
+
+ it('Burn a token in a destroyed collection', async () => {
+ const createMode = 'NFT';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+ await destroyCollectionExpectSuccess(collectionId);
+
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const badTransaction = async function () {
+ await submitTransactionExpectFailAsync(alice, tx);
+ };
+ await expect(badTransaction()).to.be.rejected;
+ });
+
+ });
+
+ it('Burn a token that was never created', async () => {
+ const createMode = 'NFT';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+ const tokenId = 10;
+
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const badTransaction = async function () {
+ await submitTransactionExpectFailAsync(alice, tx);
+ };
+ await expect(badTransaction()).to.be.rejected;
+ });
+
+ });
+
+ it('Burn a token using the address that does not own it', async () => {
+ const createMode = 'NFT';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const badTransaction = async function () {
+ await submitTransactionExpectFailAsync(bob, tx);
+ };
+ await expect(badTransaction()).to.be.rejected;
+ });
+
+ });
+
+ it('Transfer a burned a token', async () => {
+ const createMode = 'NFT';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+ const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+ await usingApi(async (api) => {
+
+ const burntx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+ const events1 = await submitTransactionAsync(alice, burntx);
+ const result1 = getGenericResult(events1);
+ expect(result1.success).to.be.true;
+
+ const tx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 0);
+ const badTransaction = async function () {
+ await submitTransactionExpectFailAsync(alice, tx);
+ };
+ await expect(badTransaction()).to.be.rejected;
+ });
+
+ });
+
+ it('Burn more than owned in Fungible collection', async () => {
+ const createMode = 'Fungible';
+ const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+ // Helper creates 10 fungible tokens
+ await createItemExpectSuccess(alice, collectionId, createMode);
+ const tokenId = 0; // ignored
+
+ await usingApi(async (api) => {
+ // Destroy 11 of 10
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, 11);
+ const badTransaction = async function () {
+ await submitTransactionExpectFailAsync(alice, tx);
+ };
+ await expect(badTransaction()).to.be.rejected;
+
+ // Get alice balance
+ const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+
+ // What to expect
+ expect(balance).to.be.not.null;
+ expect(balance.Value).to.be.equal(10);
+ });
+
+ });
+
+});
tests/src/confirmSponsorship.test.tsdiffbeforeafterboth--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -89,7 +89,7 @@
});
it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {
- const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -115,7 +115,7 @@
});
it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {
- const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -170,7 +170,7 @@
});
});
- it('NFT: Sponsoring is rate limited', async () => {
+ it('NFT: Sponsoring of transfers is rate limited', async () => {
const collectionId = await createCollectionExpectSuccess();
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -210,7 +210,7 @@
});
it('Fungible: Sponsoring is rate limited', async () => {
- const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -247,7 +247,7 @@
});
it('ReFungible: Sponsoring is rate limited', async () => {
- const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -285,6 +285,51 @@
});
});
+ it('NFT: Sponsoring of createItem is rate limited', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+ // Enable collection white list
+ await enableWhiteListExpectSuccess(alice, collectionId);
+
+ // Enable public minting
+ await enablePublicMintingExpectSuccess(alice, collectionId);
+
+ await usingApi(async (api) => {
+ // Find unused address
+ const zeroBalance = await findUnusedAddress(api);
+
+ // Add zeroBalance address to white list
+ await addToWhiteListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+ // Mint token using unused address as signer - gets sponsored
+ await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+
+ // Second mint should fail
+ const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+ const consoleError = console.error;
+ const consoleLog = console.log;
+ console.error = () => {};
+ console.log = () => {};
+ const badTransaction = async function () {
+ await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+ };
+ await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+ console.error = consoleError;
+ console.log = consoleLog;
+ const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());
+
+ // Try again after Zero gets some balance - now it should succeed
+ const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
+ await submitTransactionAsync(alice, balancetx);
+ await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
+
+ expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;
+ });
+ });
+
});
describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
tests/src/contracts.test.tsdiffbeforeafterboth--- a/tests/src/contracts.test.ts
+++ b/tests/src/contracts.test.ts
@@ -2,95 +2,20 @@
import chaiAsPromised from 'chai-as-promised';
import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
import fs from "fs";
-import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
-import { IKeyringPair } from "@polkadot/types/types";
-import { ApiPromise, Keyring } from "@polkadot/api";
-import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types";
+import { Abi, ContractPromise as Contract } from "@polkadot/api-contract";
import privateKey from "./substrate/privateKey";
+import {
+ deployFlipper,
+ getFlipValue
+} from "./util/contracthelpers";
chai.use(chaiAsPromised);
const expect = chai.expect;
-import { BigNumber } from 'bignumber.js';
-import { findUnusedAddress } from './util/helpers';
const value = 0;
const gasLimit = 3000n * 1000000n;
-const endowment = `1000000000000000`;
const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';
-
-function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
- return new Promise<Blueprint>(async (resolve, reject) => {
- const unsub = await code
- .createBlueprint()
- .signAndSend(alice, (result) => {
- if (result.status.isInBlock || result.status.isFinalized) {
- // here we have an additional field in the result, containing the blueprint
- resolve(result.blueprint);
- unsub();
- }
- })
- });
-}
-
-function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
- return new Promise<any>(async (resolve, reject) => {
- const endowment = 1000000000000000n;
- const initValue = true;
-
- const unsub = await blueprint.tx
- .new(endowment, gasLimit, initValue)
- .signAndSend(alice, (result) => {
- if (result.status.isInBlock || result.status.isFinalized) {
- unsub();
- resolve(result);
- }
- });
- });
-}
-async function prepareDeployer(api: ApiPromise) {
- // Find unused address
- const deployer = await findUnusedAddress(api);
-
- // Transfer balance to it
- const keyring = new Keyring({ type: 'sr25519' });
- const alice = keyring.addFromUri(`//Alice`);
- let amount = new BigNumber(endowment);
- amount = amount.plus(1e15);
- const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
- await submitTransactionAsync(alice, tx);
-
- return deployer;
-}
-
-export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
- const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
- const abi = new Abi(metadata);
-
- const deployer = await prepareDeployer(api);
-
- const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
-
- const code = new CodePromise(api, abi, wasm);
-
- const blueprint = await deployBlueprint(deployer, code);
- const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
-
- const initialGetResponse = await getFlipValue(contract, deployer);
- expect(initialGetResponse).to.be.true;
-
- return [contract, deployer];
-}
-
-async function getFlipValue(contract: Contract, deployer: IKeyringPair) {
- const result = await contract.query.get(deployer.address, value, gasLimit);
-
- if(!result.result.isSuccess) {
- throw `Failed to get flipper value`;
- }
- return (result.result.asSuccess.data[0] == 0x00) ? false : true;
-}
-
describe('Contracts', () => {
it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
await usingApi(async api => {
@@ -103,68 +28,6 @@
const afterFlipGetResponse = await getFlipValue(contract, deployer);
expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');
- });
- });
-
- it(`Whitelisted account can call contract.`, async () => {
- await usingApi(async api => {
- const bob = privateKey("//Bob");
-
- const [contract, deployer] = await deployFlipper(api);
-
- let expectedFlipValue = await getFlipValue(contract, deployer);
-
- const flip = contract.exec('flip', value, gasLimit);
- await submitTransactionAsync(bob, flip);
- expectedFlipValue = !expectedFlipValue;
- const afterFlip = await getFlipValue(contract,deployer);
- expect(afterFlip).to.be.eq(expectedFlipValue, `Anyone can call new contract.`);
-
- const deployerCanFlip = async () => {
- expectedFlipValue = !expectedFlipValue;
- const deployerFlip = contract.exec('flip', value, gasLimit);
- await submitTransactionAsync(deployer, deployerFlip);
- const aliceFlip1Response = await getFlipValue(contract, deployer);
- expect(aliceFlip1Response).to.be.eq(expectedFlipValue, `Deployer always can flip.`);
- };
- await deployerCanFlip();
-
- const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
- const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx);
- const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit);
- await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected;
- const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer);
- expect(flipValueAfterEnableWhiteList).to.be.eq(expectedFlipValue, `Enabling whitelist doesn't make it possible to call contract for everyone.`);
-
- await deployerCanFlip();
-
- const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
- const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx);
- const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit);
- await submitTransactionAsync(bob, flipWithWhitelistedBob);
- expectedFlipValue = !expectedFlipValue;
- const flipAfterWhiteListed = await getFlipValue(contract,deployer);
- expect(flipAfterWhiteListed).to.be.eq(expectedFlipValue, `Bob was whitelisted, now he can flip.`);
-
- await deployerCanFlip();
-
- const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address);
- const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx);
- const bobRemoved = contract.exec('flip', value, gasLimit);
- await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected;
- const afterBobRemoved = await getFlipValue(contract, deployer);
- expect(afterBobRemoved).to.be.eq(expectedFlipValue, `Bob can't call contract, now when he is removeed from white list.`);
-
- await deployerCanFlip();
-
- const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false);
- const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx);
- const whiteListDisabledFlip = contract.exec('flip', value, gasLimit);
- await submitTransactionAsync(bob, whiteListDisabledFlip);
- expectedFlipValue = !expectedFlipValue;
- const afterWhiteListDisabled = await getFlipValue(contract,deployer);
- expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`);
-
});
});
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -5,15 +5,15 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import { default as usingApi } from "./substrate/substrate-api";
-import { createCollectionExpectSuccess, createCollectionExpectFailure, CollectionMode } from "./util/helpers";
+import { default as usingApi } from './substrate/substrate-api';
+import { createCollectionExpectFailure, createCollectionExpectSuccess } from './util/helpers';
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('integration test: ext. createCollection():', () => {
it('Create new NFT collection', async () => {
- await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+ await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
});
it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {
await createCollectionExpectSuccess({name: 'A'.repeat(64)});
@@ -25,34 +25,35 @@
await createCollectionExpectSuccess({tokenPrefix: 'A'.repeat(16)});
});
it('Create new Fungible collection', async () => {
- await createCollectionExpectSuccess({mode: 'Fungible'});
+ await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
});
it('Create new ReFungible collection', async () => {
- await createCollectionExpectSuccess({mode: 'ReFungible'});
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
});
});
describe('(!negative test!) integration test: ext. createCollection():', () => {
it('(!negative test!) create new NFT collection whith incorrect data (mode)', async () => {
await usingApi(async (api) => {
- const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+ const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
- const badTransaction = async function () {
- await createCollectionExpectSuccess({mode: 'BadMode' as CollectionMode});
+ const badTransaction = async () => {
+ await createCollectionExpectSuccess({mode: {type: 'Invalid'}});
};
+ // tslint:disable-next-line:no-unused-expression
expect(badTransaction()).to.be.rejected;
- const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+ const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Incorrect collection created.');
});
});
it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {
- await createCollectionExpectFailure({name: 'A'.repeat(65)});
+ await createCollectionExpectFailure({ name: 'A'.repeat(65), mode: {type: 'NFT'}});
});
it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {
- await createCollectionExpectFailure({description: 'A'.repeat(257)});
+ await createCollectionExpectFailure({ description: 'A'.repeat(257), mode: { type: 'NFT' }});
});
it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
- await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17)});
+ await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
});
});
tests/src/createItem.test.tsdiffbeforeafterboth--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -18,17 +18,17 @@
it('Create new item in NFT collection', async () => {
const createMode = 'NFT';
- const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+ const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
await createItemExpectSuccess(alice, newCollectionID, createMode);
});
it('Create new item in Fungible collection', async () => {
const createMode = 'Fungible';
- const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+ const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
await createItemExpectSuccess(alice, newCollectionID, createMode);
});
it('Create new item in ReFungible collection', async () => {
const createMode = 'ReFungible';
- const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+ const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
await createItemExpectSuccess(alice, newCollectionID, createMode);
});
});
tests/src/destroyCollection.test.tsdiffbeforeafterboth--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -14,11 +14,11 @@
await destroyCollectionExpectSuccess(collectionId);
});
it('Fungible collection can be destroyed', async () => {
- const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
await destroyCollectionExpectSuccess(collectionId);
});
it('ReFungible collection can be destroyed', async () => {
- const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
await destroyCollectionExpectSuccess(collectionId);
});
});
tests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -0,0 +1,82 @@
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+ it('Remove collection admin.', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const collection: any = (await api.query.nft.collection(collectionId));
+ expect(collection.Owner.toString()).to.be.eq(Alice.address);
+ // first - add collection admin Bob
+ const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+ await submitTransactionAsync(Alice, addAdminTx);
+
+ const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterAddAdmin).to.be.contains(Bob.address);
+
+ // then remove bob from admins of collection
+ const removeAdminTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+ await submitTransactionAsync(Alice, removeAdminTx);
+
+ const adminListAfterRemoveAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListAfterRemoveAdmin).not.to.be.contains(Bob.address);
+ });
+ });
+});
+
+describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+ it('Can\'t remove collection admin from not existing collection', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = (1 << 32) - 1;
+ const alice = privateKey('//Alice');
+ const bob = privateKey('//Bob');
+
+ const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, bob.address);
+ await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;
+
+ // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+ await createCollectionExpectSuccess();
+ });
+ });
+
+ it('Can\'t remove collection admin from deleted collection', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = await createCollectionExpectSuccess();
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+
+ await destroyCollectionExpectSuccess(collectionId);
+
+ const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+ await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected;
+
+ // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+ await createCollectionExpectSuccess();
+ });
+ });
+
+ it('Remove admin from collection that has no admins', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const collectionId = await createCollectionExpectSuccess();
+
+ const adminListBeforeAddAdmin: any = (await api.query.nft.adminList(collectionId));
+ expect(adminListBeforeAddAdmin).to.have.lengthOf(0);
+
+ const tx = api.tx.nft.removeCollectionAdmin(collectionId, Alice.address);
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+});
tests/src/setCollectionLimits.test.tsdiffbeforeafterboth--- a/tests/src/setCollectionLimits.test.ts
+++ b/tests/src/setCollectionLimits.test.ts
@@ -32,7 +32,7 @@
});
it('choose or create collection for testing', async () => {
await usingApi(async () => {
- collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+ collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
});
});
});
tests/src/setCollectionSponsor.test.tsdiffbeforeafterboth--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -30,11 +30,11 @@
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
});
it('Set Fungible collection sponsor', async () => {
- const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+ const collectionId = await createCollectionExpectSuccess({ mode: {type: 'Fungible', decimalPoints: 0} });
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
});
it('Set ReFungible collection sponsor', async () => {
- const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+ const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} });
await setCollectionSponsorExpectSuccess(collectionId, bob.address);
});
tests/src/setPublicAccessMode.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/setPublicAccessMode.test.ts
@@ -0,0 +1,89 @@
+// https://unique-network.readthedocs.io/en/latest/jsapi.html#setschemaversion
+import { ApiPromise, Keyring } from '@polkadot/api';
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi, {submitTransactionExpectFailAsync} from './substrate/substrate-api';
+import {
+ addToWhiteListExpectSuccess,
+ createCollectionExpectSuccess,
+ createItemExpectSuccess,
+ destroyCollectionExpectSuccess,
+ enablePublicMintingExpectSuccess,
+ enableWhiteListExpectSuccess,
+} from './util/helpers';
+import { utf16ToStr } from './util/util';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+
+describe('Integration Test setPublicAccessMode(): ', () => {
+ before(async () => {
+ await usingApi(async (api) => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ });
+ });
+
+ it('Run extrinsic with collection id parameters, set the whitelist mode for the collection', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const collectionId: number = await createCollectionExpectSuccess();
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ await enablePublicMintingExpectSuccess(Alice, collectionId);
+ await addToWhiteListExpectSuccess(Alice, collectionId, Bob.address);
+ await createItemExpectSuccess(Bob, collectionId, 'NFT', Bob.address);
+ });
+ });
+
+ it('Whitelisted collection limits', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const collectionId = await createCollectionExpectSuccess();
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ await enablePublicMintingExpectSuccess(Alice, collectionId);
+ const tx = api.tx.nft.createItem(collectionId, Bob.address, 'NFT');
+ await expect(submitTransactionExpectFailAsync(Bob, tx)).to.be.rejected;
+ });
+ });
+});
+
+describe('Negative Integration Test ext. setPublicAccessMode(): ', () => {
+ it('Set a non-existent collection', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ // tslint:disable-next-line: radix
+ const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+ const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+
+ it('Set the collection that has been deleted', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = await createCollectionExpectSuccess();
+ await destroyCollectionExpectSuccess(collectionId);
+ const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+ await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+ });
+ });
+
+ it('Re-set the list mode already set in quantity', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const collectionId: number = await createCollectionExpectSuccess();
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ await enableWhiteListExpectSuccess(Alice, collectionId);
+ });
+ });
+
+ it('Execute method not on behalf of the collection owner', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ // tslint:disable-next-line: no-bitwise
+ const collectionId = await createCollectionExpectSuccess();
+ const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
+ await expect(submitTransactionExpectFailAsync(Bob, tx)).to.be.rejected;
+ });
+ });
+});
tests/src/setSchemaVersion.test.tsdiffbeforeafterboth--- a/tests/src/setSchemaVersion.test.ts
+++ b/tests/src/setSchemaVersion.test.ts
@@ -35,7 +35,7 @@
});
it('choose or create collection for testing', async () => {
await usingApi(async () => {
- collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+ collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
});
});
});
@@ -93,12 +93,6 @@
const nonExistedCollectionId = collectionCount + 1;
tx = api.tx.nft.setSchemaVersion(nonExistedCollectionId, 'ImageURL');
await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
- /*try {
- await submitTransactionAsync(alice, tx);
- } catch (e) {
- // tslint:disable-next-line:no-unused-expression
- expect(e).to.be.exist;
- }*/
});
});
@@ -119,13 +113,6 @@
await destroyCollectionExpectSuccess(collectionIdForTesting);
tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
- /*try {
- tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
- await submitTransactionAsync(alice, tx);
- } catch (e) {
- // tslint:disable-next-line:no-unused-expression
- expect(e).to.be.exist;
- }*/
});
});
});
tests/src/substrate/get-balance.tsdiffbeforeafterboth--- a/tests/src/substrate/get-balance.ts
+++ b/tests/src/substrate/get-balance.ts
@@ -3,12 +3,12 @@
// file 'LICENSE', which is part of this source code package.
//
-import { ApiPromise } from "@polkadot/api";
-import promisifySubstrate from "./promisify-substrate";
-import {AccountInfo} from "@polkadot/types/interfaces/system";
+import { ApiPromise } from '@polkadot/api';
+import {AccountInfo} from '@polkadot/types/interfaces/system';
+import promisifySubstrate from './promisify-substrate';
-export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<bigint[]> {
- const balance = promisifySubstrate(api, (accounts: string[]) => api.query.system.account.multi(accounts));
+export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<Array<bigint>> {
+ const balance = promisifySubstrate(api, (acc: string[]) => api.query.system.account.multi(acc));
const responce = await balance(accounts) as unknown as AccountInfo[];
- return responce.map(r => r.data.free.toBigInt().valueOf());
+ return responce.map((r) => r.data.free.toBigInt().valueOf());
}
tests/src/substrate/substrate-api.tsdiffbeforeafterboth--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -58,21 +58,22 @@
return TransactionStatus.Fail;
}
-export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
- return new Promise(async function(resolve, reject) {
+export function
+submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
+ return new Promise(async (resolve, reject) => {
try {
await transaction.signAndSend(sender, ({ events = [], status }) => {
const transactionStatus = getTransactionStatus(events, status);
- if (transactionStatus == TransactionStatus.Success) {
+ if (transactionStatus === TransactionStatus.Success) {
resolve(events);
- } else if (transactionStatus == TransactionStatus.Fail) {
+ } else if (transactionStatus === TransactionStatus.Fail) {
console.log(`Something went wrong with transaction. Status: ${status}`);
reject(events);
}
});
} catch (e) {
- console.log("Error: ", e);
+ console.log('Error: ', e);
reject(e);
}
});
@@ -90,7 +91,6 @@
res(rec);
console.error = consoleError;
console.log = consoleLog;
-
});
};
const reject = (errror: any) => {
@@ -104,6 +104,8 @@
await transaction.signAndSend(sender, ({ events = [], status }) => {
const transactionStatus = getTransactionStatus(events, status);
+ console.log('transactionStatus', transactionStatus, 'events', events);
+
if (transactionStatus == TransactionStatus.Success) {
resolve(events);
} else if (transactionStatus == TransactionStatus.Fail) {
@@ -114,4 +116,4 @@
reject(e);
}
});
-}
\ No newline at end of file
+}
tests/src/toggleContractWhiteList.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/toggleContractWhiteList.test.ts
@@ -0,0 +1,151 @@
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
+import privateKey from "./substrate/privateKey";
+import {
+ deployFlipper,
+ getFlipValue
+} from "./util/contracthelpers";
+import {
+ getGenericResult
+} from "./util/helpers"
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+
+describe('Integration Test toggleContractWhiteList', () => {
+
+ it(`Enable white list contract mode`, async () => {
+ await usingApi(async api => {
+ const [contract, deployer] = await deployFlipper(api);
+
+ const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+ const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+ const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+ const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+ expect(getGenericResult(enableEvents).success).to.be.true;
+ expect(enabledBefore).to.be.false;
+ expect(enabled).to.be.true;
+ });
+ });
+
+ it(`Only whitelisted account can call contract`, async () => {
+ await usingApi(async api => {
+ const bob = privateKey("//Bob");
+
+ const [contract, deployer] = await deployFlipper(api);
+
+ let flipValueBefore = await getFlipValue(contract, deployer);
+ const flip = contract.exec('flip', value, gasLimit);
+ await submitTransactionAsync(bob, flip);
+ const flipValueAfter = await getFlipValue(contract,deployer);
+ expect(flipValueAfter).to.be.eq(!flipValueBefore, `Anyone can call new contract.`);
+
+ const deployerCanFlip = async () => {
+ let flipValueBefore = await getFlipValue(contract, deployer);
+ const deployerFlip = contract.exec('flip', value, gasLimit);
+ await submitTransactionAsync(deployer, deployerFlip);
+ const aliceFlip1Response = await getFlipValue(contract, deployer);
+ expect(aliceFlip1Response).to.be.eq(!flipValueBefore, `Deployer always can flip.`);
+ };
+ await deployerCanFlip();
+
+ flipValueBefore = await getFlipValue(contract, deployer);
+ const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+ const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx);
+ const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit);
+ await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected;
+ const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer);
+ expect(flipValueAfterEnableWhiteList).to.be.eq(flipValueBefore, `Enabling whitelist doesn't make it possible to call contract for everyone.`);
+
+ await deployerCanFlip();
+
+ flipValueBefore = await getFlipValue(contract, deployer);
+ const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address);
+ const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx);
+ const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit);
+ await submitTransactionAsync(bob, flipWithWhitelistedBob);
+ const flipAfterWhiteListed = await getFlipValue(contract,deployer);
+ expect(flipAfterWhiteListed).to.be.eq(!flipValueBefore, `Bob was whitelisted, now he can flip.`);
+
+ await deployerCanFlip();
+
+ flipValueBefore = await getFlipValue(contract, deployer);
+ const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address);
+ const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx);
+ const bobRemoved = contract.exec('flip', value, gasLimit);
+ await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected;
+ const afterBobRemoved = await getFlipValue(contract, deployer);
+ expect(afterBobRemoved).to.be.eq(flipValueBefore, `Bob can't call contract, now when he is removeed from white list.`);
+
+ await deployerCanFlip();
+
+ flipValueBefore = await getFlipValue(contract, deployer);
+ const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false);
+ const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx);
+ const whiteListDisabledFlip = contract.exec('flip', value, gasLimit);
+ await submitTransactionAsync(bob, whiteListDisabledFlip);
+ const afterWhiteListDisabled = await getFlipValue(contract,deployer);
+ expect(afterWhiteListDisabled).to.be.eq(!flipValueBefore, `Anyone can call contract with disabled whitelist.`);
+
+ });
+ });
+
+ it(`Enabling white list repeatedly should not produce errors`, async () => {
+ await usingApi(async api => {
+ const [contract, deployer] = await deployFlipper(api);
+
+ const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+ const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+ const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+ const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+ const enableAgainEvents = await submitTransactionAsync(deployer, enableWhiteListTx);
+ const enabledAgain = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+ expect(getGenericResult(enableEvents).success).to.be.true;
+ expect(enabledBefore).to.be.false;
+ expect(enabled).to.be.true;
+ expect(getGenericResult(enableAgainEvents).success).to.be.true;
+ expect(enabledAgain).to.be.true;
+ });
+ });
+
+});
+
+describe('Negative Integration Test toggleContractWhiteList', () => {
+
+ it(`Enable white list for a non-contract`, async () => {
+ await usingApi(async api => {
+ const alice = privateKey("//Alice");
+ const bobGuineaPig = privateKey("//Bob");
+
+ const enabledBefore = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON();
+ const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(bobGuineaPig.address, true);
+ await expect(submitTransactionExpectFailAsync(alice, enableWhiteListTx)).to.be.rejected;
+ const enabled = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON();
+
+ expect(enabledBefore).to.be.false;
+ expect(enabled).to.be.false;
+ });
+ });
+
+ it(`Enable white list using a non-owner address`, async () => {
+ await usingApi(async api => {
+ const bob = privateKey("//Bob");
+ const [contract, deployer] = await deployFlipper(api);
+
+ const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+ const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true);
+ await expect(submitTransactionExpectFailAsync(bob, enableWhiteListTx)).to.be.rejected;
+ const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON();
+
+ expect(enabledBefore).to.be.false;
+ expect(enabled).to.be.false;
+ });
+ });
+
+});
tests/src/transfer.test.tsdiffbeforeafterboth--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -3,49 +3,174 @@
// file 'LICENSE', which is part of this source code package.
//
-import { expect, assert } from "chai";
-import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
-import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts";
-import privateKey from "./substrate/privateKey";
-import getBalance from "./substrate/get-balance";
-import { BigNumber } from 'bignumber.js';
-import { findUnusedAddress } from './util/helpers'
+import { ApiPromise } from '@polkadot/api';
+import { IKeyringPair } from '@polkadot/types/types';
+import { expect } from 'chai';
+import { alicesPublicKey, bobsPublicKey } from './accounts';
+import getBalance from './substrate/get-balance';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync } from './substrate/substrate-api';
+import {
+ burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess,
+ destroyCollectionExpectSuccess,
+ findUnusedAddress,
+ getCreateCollectionResult,
+ getCreateItemResult,
+ transferExpectFail,
+ transferExpectSuccess,
+} from './util/helpers';
-describe('Transfer', () => {
- it('Balance transfers', async () => {
- await usingApi(async api => {
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+let Charlie: IKeyringPair;
+
+describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+ it('Balance transfers and check balance', async () => {
+ await usingApi(async (api: ApiPromise) => {
const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
const alicePrivateKey = privateKey('//Alice');
-
+
const transfer = api.tx.balances.transfer(bobsPublicKey, 1n);
- const result = await submitTransactionAsync(alicePrivateKey, transfer);
+ const events = await submitTransactionAsync(alicePrivateKey, transfer);
+ const result = getCreateItemResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
+ // tslint:disable-next-line:no-unused-expression
expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
+ // tslint:disable-next-line:no-unused-expression
expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;
});
});
it('Inability to pay fees error message is correct', async () => {
- await usingApi(async api => {
+ await usingApi(async (api) => {
// Find unused address
const pk = await findUnusedAddress(api);
- const error = console.error;
- const log = console.log;
- console.log = function () {};
- console.error = function () {};
-
const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n);
- const badTransaction = async function () {
- const result = await submitTransactionAsync(pk, badTransfer);
+ // const events = await submitTransactionAsync(pk, badTransfer);
+ const badTransaction = async () => {
+ const events = await submitTransactionAsync(pk, badTransfer);
+ const result = getCreateCollectionResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.false;
};
- await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
+ expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');
+ });
+ });
- console.log = log;
- console.error = error;
+ it('Create collection, balance transfers and check balance', async () => {
+ await usingApi(async (api) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await transferExpectSuccess(reFungibleCollectionId,
+ newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
});
});
});
+
+describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+ before(async () => {
+ await usingApi(async (api: ApiPromise) => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+ it('Transfer with not existed collection_id', async () => {
+ await usingApi(async (api) => {
+ // nft
+ const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await transferExpectFail(nftCollectionCount + 1, 1, Alice, Bob, 1);
+ // fungible
+ const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await transferExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob, 1);
+ // reFungible
+ const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await transferExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob, 1);
+ });
+ });
+ it('Transfer with deleted collection_id', async () => {
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await destroyCollectionExpectSuccess(nftCollectionId);
+ await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await destroyCollectionExpectSuccess(fungibleCollectionId);
+ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await destroyCollectionExpectSuccess(reFungibleCollectionId);
+ await transferExpectFail(reFungibleCollectionId,
+ newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
+ });
+ it('Transfer with not existed item_id', async () => {
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ await transferExpectFail(nftCollectionId, 2, Alice, Bob, 1, 'NFT');
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await transferExpectFail(fungibleCollectionId, 2, Alice, Bob, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ await transferExpectFail(reFungibleCollectionId,
+ 2, Alice, Bob, 1, 'ReFungible');
+ });
+ it('Transfer with deleted item_id', async () => {
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await burnItemExpectSuccess(Alice, nftCollectionId, newNftTokenId, 1);
+ await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await burnItemExpectSuccess(Alice, fungibleCollectionId, newFungibleTokenId, 10);
+ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1);
+ await transferExpectFail(reFungibleCollectionId,
+ newReFungibleTokenId, Alice, Bob, 1, 'ReFungible');
+ });
+ it('Transfer with recipient that is not owner', async () => {
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await transferExpectFail(nftCollectionId, newNftTokenId, Charlie, Bob, 1, 'NFT');
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Charlie, Bob, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await transferExpectFail(reFungibleCollectionId,
+ newReFungibleTokenId, Charlie, Bob, 1, 'ReFungible');
+ });
+});
tests/src/transferFrom.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/transferFrom.test.ts
@@ -0,0 +1,188 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+import { ApiPromise } from '@polkadot/api';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+ approveExpectFail,
+ approveExpectSuccess,
+ createCollectionExpectSuccess,
+ createItemExpectSuccess,
+ destroyCollectionExpectSuccess,
+ transferFromExpectFail,
+ transferFromExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+ it('Execute the extrinsic and check nftItemList - owner of token', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+ await transferFromExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1, 'NFT');
+
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+ await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1, 'Fungible');
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+ await transferFromExpectSuccess(reFungibleCollectionId,
+ newReFungibleTokenId, Bob, Alice, Charlie, 1, 'ReFungible');
+ });
+ });
+});
+
+describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+ it('transferFrom for a collection that does not exist', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+ // nft
+ const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+
+ await transferFromExpectFail(nftCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+
+ // fungible
+ const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+
+ await transferFromExpectFail(fungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+ // reFungible
+ const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+ await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+
+ await transferFromExpectFail(reFungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+ });
+ });
+
+ /* it('transferFrom for a collection that was destroyed', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ this test copies approve negative test
+ });
+ }); */
+
+ /* it('transferFrom a token that does not exist', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ this test copies approve negative test
+ });
+ }); */
+
+ /* it('transferFrom a token that was deleted', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ this test copies approve negative test
+ });
+ }); */
+
+ it('transferFrom for not approved address', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+
+ await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1);
+
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1);
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await transferFromExpectFail(reFungibleCollectionId,
+ newReFungibleTokenId, Bob, Alice, Charlie, 1);
+ });
+ });
+
+ it('transferFrom incorrect token count', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+ await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 2);
+
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 2);
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+ await transferFromExpectFail(reFungibleCollectionId,
+ newReFungibleTokenId, Bob, Alice, Charlie, 2);
+ });
+ });
+
+ it('execute transferFrom from account that is not owner of collection', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const Alice = privateKey('//Alice');
+ const Bob = privateKey('//Bob');
+ const Charlie = privateKey('//CHARLIE');
+ const Dave = privateKey('//DAVE');
+ // nft
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+ try {
+ await approveExpectFail(nftCollectionId, newNftTokenId, Dave, Bob);
+ await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+ } catch (e) {
+ // tslint:disable-next-line:no-unused-expression
+ expect(e).to.be.exist;
+ }
+
+ // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+
+ // fungible
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+ try {
+ await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Bob);
+ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Alice, Charlie, 1);
+ } catch (e) {
+ // tslint:disable-next-line:no-unused-expression
+ expect(e).to.be.exist;
+ }
+ // reFungible
+ const reFungibleCollectionId = await
+ createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+ const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+ try {
+ await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Bob);
+ await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Alice, Charlie, 1);
+ } catch (e) {
+ // tslint:disable-next-line:no-unused-expression
+ expect(e).to.be.exist;
+ }
+ });
+ });
+});
tests/src/util/contracthelpers.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/util/contracthelpers.ts
@@ -0,0 +1,94 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import chai from "chai";
+import chaiAsPromised from 'chai-as-promised';
+import { submitTransactionAsync } from "../substrate/substrate-api";
+import fs from "fs";
+import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract";
+import { IKeyringPair } from "@polkadot/types/types";
+import { ApiPromise, Keyring } from "@polkadot/api";
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+import { BigNumber } from 'bignumber.js';
+import { findUnusedAddress } from '../util/helpers';
+
+const value = 0;
+const gasLimit = 3000n * 1000000n;
+const endowment = `1000000000000000`;
+
+function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise<Blueprint> {
+ return new Promise<Blueprint>(async (resolve, reject) => {
+ const unsub = await code
+ .createBlueprint()
+ .signAndSend(alice, (result) => {
+ if (result.status.isInBlock || result.status.isFinalized) {
+ // here we have an additional field in the result, containing the blueprint
+ resolve(result.blueprint);
+ unsub();
+ }
+ })
+ });
+}
+
+function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise<any> {
+ return new Promise<any>(async (resolve, reject) => {
+ const endowment = 1000000000000000n;
+ const initValue = true;
+
+ const unsub = await blueprint.tx
+ .new(endowment, gasLimit, initValue)
+ .signAndSend(alice, (result) => {
+ if (result.status.isInBlock || result.status.isFinalized) {
+ unsub();
+ resolve(result);
+ }
+ });
+ });
+}
+
+async function prepareDeployer(api: ApiPromise) {
+ // Find unused address
+ const deployer = await findUnusedAddress(api);
+
+ // Transfer balance to it
+ const keyring = new Keyring({ type: 'sr25519' });
+ const alice = keyring.addFromUri(`//Alice`);
+ let amount = new BigNumber(endowment);
+ amount = amount.plus(1e15);
+ const tx = api.tx.balances.transfer(deployer.address, amount.toFixed());
+ await submitTransactionAsync(alice, tx);
+
+ return deployer;
+}
+
+export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> {
+ const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
+ const abi = new Abi(metadata);
+
+ const deployer = await prepareDeployer(api);
+
+ const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
+
+ const code = new CodePromise(api, abi, wasm);
+
+ const blueprint = await deployBlueprint(deployer, code);
+ const contract = (await deployContract(deployer, blueprint))['contract'] as Contract;
+
+ const initialGetResponse = await getFlipValue(contract, deployer);
+ expect(initialGetResponse).to.be.true;
+
+ return [contract, deployer];
+}
+
+export async function getFlipValue(contract: Contract, deployer: IKeyringPair) {
+ const result = await contract.query.get(deployer.address, value, gasLimit);
+
+ if(!result.result.isSuccess) {
+ throw `Failed to get flipper value`;
+ }
+ return (result.result.asSuccess.data[0] == 0x00) ? false : true;
+}
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -3,20 +3,20 @@
// file 'LICENSE', which is part of this source code package.
//
+import { ApiPromise, Keyring } from '@polkadot/api';
+import { Enum, Struct } from '@polkadot/types/codec';
+import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import { u128 } from '@polkadot/types/primitive';
+import { IKeyringPair } from '@polkadot/types/types';
+import { BigNumber } from 'bignumber.js';
+import BN from 'bn.js';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
-import { ApiPromise, Keyring } from "@polkadot/api";
-import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api";
+import { alicesPublicKey, nullPublicKey } from '../accounts';
import privateKey from '../substrate/privateKey';
-import { alicesPublicKey, nullPublicKey } from "../accounts";
-import { strToUTF16, utf16ToStr, hexToStr } from './util';
-import { IKeyringPair } from '@polkadot/types/types';
-import { BigNumber } from 'bignumber.js';
-import { Struct, Enum } from '@polkadot/types/codec';
-import { u128 } from '@polkadot/types/primitive';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';
import { ICollectionInterface } from '../types';
-import BN from "bn.js";
+import { hexToStr, strToUTF16, utf16ToStr } from './util';
chai.use(chaiAsPromised);
const expect = chai.expect;
@@ -25,24 +25,45 @@
success: boolean,
};
-type CreateCollectionResult = {
- success: boolean,
- collectionId: number
-};
+interface CreateCollectionResult {
+ success: boolean;
+ collectionId: number;
+}
+
+interface CreateItemResult {
+ success: boolean;
+ collectionId: number;
+ itemId: number;
+}
+
+interface IReFungibleOwner {
+ Fraction: BN;
+ Owner: number[];
+}
+
+interface ITokenDataType {
+ Owner: number[];
+ ConstData: number[];
+ VariableData: number[];
+}
+
+interface IFungibleTokenDataType {
+ Value: BN;
+}
-type CreateItemResult = {
- success: boolean,
- collectionId: number,
- itemId: number
-};
+interface IReFungibleTokenDataType {
+ Owner: IReFungibleOwner[];
+ ConstData: number[];
+ VariableData: number[];
+}
export function getGenericResult(events: EventRecord[]): GenericResult {
- let result: GenericResult = {
- success: false
- }
+ const result: GenericResult = {
+ success: false,
+ };
events.forEach(({ phase, event: { data, method, section } }) => {
// console.log(` ${phase}: ${section}.${method}:: ${data}`);
- if (method == 'ExtrinsicSuccess') {
+ if (method === 'ExtrinsicSuccess') {
result.success = true;
}
});
@@ -60,10 +81,10 @@
collectionId = parseInt(data[0].toString());
}
});
- let result: CreateCollectionResult = {
+ const result: CreateCollectionResult = {
success,
- collectionId
- }
+ collectionId,
+ };
return result;
}
@@ -80,27 +101,60 @@
itemId = parseInt(data[1].toString());
}
});
- let result: CreateItemResult = {
+ const result: CreateItemResult = {
success,
collectionId,
- itemId
- }
+ itemId,
+ };
return result;
}
-export type CollectionMode = 'NFT' | 'Fungible' | 'ReFungible';
+interface Invalid {
+ type: 'Invalid';
+}
+
+interface Nft {
+ type: 'NFT';
+}
+
+interface Fungible {
+ type: 'Fungible';
+ decimalPoints: number;
+}
+
+interface ReFungible {
+ type: 'ReFungible';
+ decimalPoints: number;
+}
+
+interface Nft {
+ type: 'NFT'
+}
+
+interface Fungible {
+ type: 'Fungible',
+ decimalPoints: number
+}
+
+interface ReFungible {
+ type: 'ReFungible',
+ decimalPoints: number
+}
+
+type CollectionMode = Nft | Fungible | ReFungible | Invalid;
+
export type CreateCollectionParams = {
mode: CollectionMode,
name: string,
description: string,
- tokenPrefix: string
+ tokenPrefix: string,
};
const defaultCreateCollectionParams: CreateCollectionParams = {
+ description: 'description',
+ mode: { type: 'NFT' },
name: 'name',
- description: 'description',
- mode: 'NFT',
- tokenPrefix: 'prefix'
+ tokenPrefix: 'prefix',
}
export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
@@ -109,25 +163,39 @@
let collectionId: number = 0;
await usingApi(async (api) => {
// Get number of collections before the transaction
- const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+ const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
- const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+
+ let modeprm = {};
+ if (mode.type === 'NFT') {
+ modeprm = {nft: null};
+ } else if (mode.type === 'Fungible') {
+ modeprm = {fungible: mode.decimalPoints};
+ } else if (mode.type === 'ReFungible') {
+ modeprm = {refungible: mode.decimalPoints};
+ } else if (mode.type === 'Invalid') {
+ modeprm = {invalid: null};
+ }
+
+ const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getCreateCollectionResult(events);
// Get number of collections after the transaction
- const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+ const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(result.collectionId)).toJSON();
// What to expect
+ // tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.true;
expect(result.collectionId).to.be.equal(BcollectionCount);
+ // tslint:disable-next-line:no-unused-expression
expect(collection).to.be.not.null;
- expect(BcollectionCount).to.be.equal(AcollectionCount+1, 'Error: NFT collection NOT created.');
+ expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');
expect(collection.Owner).to.be.equal(alicesPublicKey);
expect(utf16ToStr(collection.Name)).to.be.equal(name);
expect(utf16ToStr(collection.Description)).to.be.equal(description);
@@ -138,17 +206,28 @@
return collectionId;
}
-
+
export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {
const {name, description, mode, tokenPrefix } = {...defaultCreateCollectionParams, ...params};
+ let modeprm = {};
+ if (mode.type === 'NFT') {
+ modeprm = {nft: null};
+ } else if (mode.type === 'Fungible') {
+ modeprm = {fungible: mode.decimalPoints};
+ } else if (mode.type === 'ReFungible') {
+ modeprm = {refungible: mode.decimalPoints};
+ } else if (mode.type === 'Invalid') {
+ modeprm = {invalid: null};
+ }
+
await usingApi(async (api) => {
// Get number of collections before the transaction
const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
// Run the CreateCollection transaction
const alicePrivateKey = privateKey('//Alice');
- const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+ const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
const result = getCreateCollectionResult(events);
@@ -156,11 +235,12 @@
const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
// What to expect
+ // tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.false;
expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');
});
}
-
+
export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
let bal = new BigNumber(0);
let unused;
@@ -170,7 +250,7 @@
unused = keyring.addFromUri(`//${randomSeed}`);
bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());
} while (bal.toFixed() != '0');
- return unused;
+ return unused;
}
function getDestroyResult(events: EventRecord[]): boolean {
@@ -201,7 +281,7 @@
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getDestroyResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
@@ -220,7 +300,7 @@
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getGenericResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
@@ -239,7 +319,7 @@
const events = await submitTransactionAsync(alicePrivateKey, tx);
const result = getGenericResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
@@ -278,7 +358,7 @@
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
@@ -300,48 +380,200 @@
export interface CreateFungibleData extends Struct {
readonly value: u128;
-};
+}
-export interface CreateReFungibleData extends Struct {};
-export interface CreateNftData extends Struct {};
+export interface CreateReFungibleData extends Struct {}
+export interface CreateNftData extends Struct {}
export interface CreateItemData extends Enum {
- NFT: CreateNftData,
- Fungible: CreateFungibleData,
- ReFungible: CreateReFungibleData
-};
+ NFT: CreateNftData;
+ Fungible: CreateFungibleData;
+ ReFungible: CreateReFungibleData;
+}
+
+export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {
+ await usingApi(async (api) => {
+ const tx = api.tx.nft.burnItem(collectionId, tokenId, value);
+ const events = await submitTransactionAsync(owner, tx);
+ const result = getGenericResult(events);
+ // Get the item
+ const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
+ // tslint:disable-next-line:no-unused-expression
+ expect(item).to.be.not.null;
+ expect(item.Owner).to.be.equal(nullPublicKey);
+ });
+}
+
+export async function
+approveExpectSuccess(collectionId: number,
+ tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+ await usingApi(async (api: ApiPromise) => {
+ const allowanceBefore =
+ await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+ const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+ const events = await submitTransactionAsync(owner, approveNftTx);
+ const result = getCreateItemResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
+ const allowanceAfter =
+ await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+ expect(allowanceAfter.toNumber() - allowanceBefore.toNumber()).to.be.equal(amount);
+ });
+}
+
+export async function
+transferFromExpectSuccess(collectionId: number,
+ tokenId: number,
+ accountApproved: IKeyringPair,
+ accountFrom: IKeyringPair,
+ accountTo: IKeyringPair,
+ value: number = 1,
+ type: string = 'NFT') {
+ await usingApi(async (api: ApiPromise) => {
+ let balanceBefore = new BN(0);
+ if (type === 'Fungible') {
+ balanceBefore = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+ }
+ const transferFromTx = await api.tx.nft.transferFrom(
+ accountFrom.address, accountTo.address, collectionId, tokenId, value);
+ const events = await submitTransactionAsync(accountFrom, transferFromTx);
+ const result = getCreateItemResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
+ if (type === 'NFT') {
+ const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType;
+ expect(nftItemData.Owner.toString()).to.be.equal(accountTo.address);
+ }
+ if (type === 'Fungible') {
+ const balanceAfter = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+ expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value);
+ }
+ if (type === 'ReFungible') {
+ const nftItemData =
+ await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType;
+ expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(accountTo.address);
+ expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value);
+ }
+ });
+}
+
+export async function
+transferFromExpectFail(collectionId: number,
+ tokenId: number,
+ accountApproved: IKeyringPair,
+ accountFrom: IKeyringPair,
+ accountTo: IKeyringPair,
+ value: number = 1) {
+ await usingApi(async (api: ApiPromise) => {
+ const transferFromTx = await api.tx.nft.transferFrom(
+ accountFrom.address, accountTo.address, collectionId, tokenId, value);
+ const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;
+ const result = getCreateCollectionResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.false;
+ });
+}
+
+export async function
+transferExpectSuccess(collectionId: number,
+ tokenId: number,
+ sender: IKeyringPair,
+ recipient: IKeyringPair,
+ value: number = 1,
+ type: string = 'NFT') {
+ await usingApi(async (api: ApiPromise) => {
+ let balanceBefore = new BN(0);
+ if (type === 'Fungible') {
+ balanceBefore = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN;
+ }
+ const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value);
+ const events = await submitTransactionAsync(sender, transferTx);
+ const result = getCreateItemResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.true;
+ if (type === 'NFT') {
+ const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType;
+ expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);
+ }
+ if (type === 'Fungible') {
+ const balanceAfter = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN;
+ expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value);
+ }
+ if (type === 'ReFungible') {
+ const nftItemData =
+ await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType;
+ expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(recipient.address);
+ expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value);
+ }
+ });
+}
+
+export async function
+transferExpectFail(collectionId: number,
+ tokenId: number,
+ sender: IKeyringPair,
+ recipient: IKeyringPair,
+ value: number = 1,
+ type: string = 'NFT') {
+ await usingApi(async (api: ApiPromise) => {
+ const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value);
+ const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;
+ if (events && Array.isArray(events)) {
+ const result = getCreateCollectionResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.false;
+ }
+ });
+}
+
+export async function
+approveExpectFail(collectionId: number,
+ tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+ await usingApi(async (api: ApiPromise) => {
+ const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+ const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;
+ const result = getCreateCollectionResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.false;
+ });
+}
-export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
+export async function createItemExpectSuccess(
+ sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
let newItemId: number = 0;
await usingApi(async (api) => {
- const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
- const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
+ const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+ const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
const AItemBalance = new BigNumber(Aitem.Value);
- if (owner === '') owner = sender.address;
+ if (owner === '') {
+ owner = sender.address;
+ }
let tx;
- if (createMode == 'Fungible') {
- let createData = {fungible: {value: 10}};
+ if (createMode === 'Fungible') {
+ const createData = {fungible: {value: 10}};
tx = api.tx.nft.createItem(collectionId, owner, createData);
- }
- else {
+ } else {
tx = api.tx.nft.createItem(collectionId, owner, createMode);
}
const events = await submitTransactionAsync(sender, tx);
const result = getCreateItemResult(events);
- const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
- const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
+ const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+ const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
const BItemBalance = new BigNumber(Bitem.Value);
// What to expect
+ // tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.true;
- if (createMode == 'Fungible') {
+ if (createMode === 'Fungible') {
expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);
- }
- else {
- expect(BItemCount).to.be.equal(AItemCount+1);
+ } else {
+ expect(BItemCount).to.be.equal(AItemCount + 1);
}
expect(collectionId).to.be.equal(result.collectionId);
expect(BItemCount).to.be.equal(result.itemId);
@@ -358,10 +590,11 @@
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
+ // tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.true;
expect(collection.Access).to.be.equal('WhiteList');
});
@@ -375,7 +608,7 @@
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
- // Get the collection
+ // Get the collection
const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
// What to expect
@@ -387,17 +620,22 @@
export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
await usingApi(async (api) => {
+ const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();
+
// Run the transaction
const tx = api.tx.nft.addToWhiteList(collectionId, address);
const events = await submitTransactionAsync(sender, tx);
const result = getGenericResult(events);
- // Get the collection
- const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
+ const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();
// What to expect
+ // tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.true;
- expect(collection.MintMode).to.be.equal(true);
+ // tslint:disable-next-line: no-unused-expression
+ expect(whiteListedBefore).to.be.false;
+ // tslint:disable-next-line: no-unused-expression
+ expect(whiteListedAfter).to.be.true;
});
}