difftreelog
Merge branch 'develop' into feature/NFTPAR-288
in: master
25 files changed
pallets/nft/src/lib.rsdiffbeforeafterboth323 CollectionNotFound,323 CollectionNotFound,324 /// Item not exists.324 /// Item not exists.325 TokenNotFound,325 TokenNotFound,326 /// Admin not found327 AdminNotFound,326 /// Arithmetic calculation overflow.328 /// Arithmetic calculation overflow.327 NumOverflow, 329 NumOverflow, 328 /// Account already has admin role.330 /// Account already has admin role.426 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;428 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;427429428 /// Tokens transfer baskets430 /// Tokens transfer baskets429 pub CreateItemBasket get(fn create_item_basket): map hasher(identity) CollectionId => T::BlockNumber;431 pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;430 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;432 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;431 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;433 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;432 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;434 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;812814813 let sender = ensure_signed(origin)?;815 let sender = ensure_signed(origin)?;814 Self::check_owner_or_admin_permissions(collection_id, sender)?;816 Self::check_owner_or_admin_permissions(collection_id, sender)?;817 ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);815818816 if <AdminList<T>>::contains_key(collection_id)819 let mut admin_arr = <AdminList<T>>::get(collection_id);817 {818 let mut admin_arr = <AdminList<T>>::get(collection_id);819 admin_arr.retain(|i| *i != account_id);820 admin_arr.retain(|i| *i != account_id);820 <AdminList<T>>::insert(collection_id, admin_arr);821 <AdminList<T>>::insert(collection_id, admin_arr);821 }822822823 Ok(())823 Ok(())824 }824 }1009 {1009 {1010 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1010 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1011 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,1011 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,1012 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1012 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, &sender)?,1013 _ => ()1013 _ => ()1014 };1014 };10151015109210921093 let sender = ensure_signed(origin)?;1093 let sender = ensure_signed(origin)?;109410941095 Self::collection_exists(collection_id)?;1096 Self::token_exists(collection_id, item_id, &sender)?;10971095 // Transfer permissions check1098 // Transfer permissions check1096 let target_collection = <Collection<T>>::get(collection_id);1099 let target_collection = <Collection<T>>::get(collection_id);1097 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1100 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1161 }1164 }116211651163 // Reduce approval by transferred amount or remove if remaining approval drops to 01166 // Reduce approval by transferred amount or remove if remaining approval drops to 01164 if approval - value > 0 {1167 if approval.checked_sub(value).unwrap_or(0) > 0 {1165 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1168 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1166 }1169 }1167 else {1170 else {1216 let sender = ensure_signed(origin)?;1219 let sender = ensure_signed(origin)?;1217 1220 1218 Self::collection_exists(collection_id)?;1221 Self::collection_exists(collection_id)?;1219 1222 Self::token_exists(collection_id, item_id, &sender)?;12231220 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1224 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);122112251225 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1229 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1226 Error::<T>::NoPermission);1230 Error::<T>::NoPermission);122712311228 Self::item_exists(collection_id, item_id, &target_collection.mode)?;12291230 match target_collection.mode1232 match target_collection.mode1231 {1233 {1232 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1234 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1320 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1322 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;132113231322 // check schema limit1324 // check schema limit1323 ensure!(schema.len() as u32 > ChainLimit::get().const_on_chain_schema_limit, "");1325 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");132413261325 let mut target_collection = <Collection<T>>::get(collection_id);1327 let mut target_collection = <Collection<T>>::get(collection_id);1326 target_collection.const_on_chain_schema = schema;1328 target_collection.const_on_chain_schema = schema;1351 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1353 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;135213541353 // check schema limit1355 // check schema limit1354 ensure!(schema.len() as u32 > ChainLimit::get().variable_on_chain_schema_limit, "");1356 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");135513571356 let mut target_collection = <Collection<T>>::get(collection_id);1358 let mut target_collection = <Collection<T>>::get(collection_id);1357 target_collection.variable_on_chain_schema = schema;1359 target_collection.variable_on_chain_schema = schema;1677 let value = item.owner.first().unwrap().fraction;1679 let value = item.owner.first().unwrap().fraction;1678 let owner = item.owner.first().unwrap().owner.clone();1680 let owner = item.owner.first().unwrap().owner.clone();167916811680 Self::add_token_index(collection_id, current_index, owner.clone())?;1682 Self::add_token_index(collection_id, current_index, &owner)?;168116831682 <ItemListIndex>::insert(collection_id, current_index);1684 <ItemListIndex>::insert(collection_id, current_index);1683 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);1685 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);168416861685 // Update balance1687 // Update balance1686 let new_balance = <Balance<T>>::get(collection_id, owner.clone())1688 let new_balance = <Balance<T>>::get(collection_id, &owner)1687 .checked_add(value)1689 .checked_add(value)1688 .ok_or(Error::<T>::NumOverflow)?;1690 .ok_or(Error::<T>::NumOverflow)?;1689 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);1691 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);1697 .ok_or(Error::<T>::NumOverflow)?;1699 .ok_or(Error::<T>::NumOverflow)?;169817001699 let item_owner = item.owner.clone();1701 let item_owner = item.owner.clone();1700 Self::add_token_index(collection_id, current_index, item.owner.clone())?;1702 Self::add_token_index(collection_id, current_index, &item.owner)?;170117031702 <ItemListIndex>::insert(collection_id, current_index);1704 <ItemListIndex>::insert(collection_id, current_index);1703 <NftItemList<T>>::insert(collection_id, current_index, item);1705 <NftItemList<T>>::insert(collection_id, current_index, item);1714 fn burn_refungible_item(1716 fn burn_refungible_item(1715 collection_id: CollectionId,1717 collection_id: CollectionId,1716 item_id: TokenId,1718 item_id: TokenId,1717 owner: T::AccountId,1719 owner: &T::AccountId,1718 ) -> DispatchResult {1720 ) -> DispatchResult {1719 ensure!(1721 ensure!(1720 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1722 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1721 Error::<T>::TokenNotFound1723 Error::<T>::TokenNotFound1722 );1724 );1723 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1725 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1724 let item = collection1726 let rft_balance = token1725 .owner1727 .owner1726 .iter()1728 .iter()1727 .filter(|&i| i.owner == owner)1729 .filter(|&i| i.owner == *owner)1728 .next()1730 .next()1729 .unwrap();1731 .unwrap();1730 Self::remove_token_index(collection_id, item_id, owner.clone())?;1732 Self::remove_token_index(collection_id, item_id, owner)?;173117331732 // update balance1734 // update balance1733 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1735 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1734 .checked_sub(item.fraction)1736 .checked_sub(rft_balance.fraction)1735 .ok_or(Error::<T>::NumOverflow)?;1737 .ok_or(Error::<T>::NumOverflow)?;1736 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1738 <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);173717391738 <ReFungibleItemList<T>>::remove(collection_id, item_id);1740 // Re-create owners list with sender removed1741 let index = token1742 .owner1743 .iter()1744 .position(|i| i.owner == *owner)1745 .unwrap();1746 token.owner.remove(index);1747 let owner_count = token.owner.len();173917481749 // Burn the token completely if this was the last (only) owner1750 if owner_count == 0 {1751 <ReFungibleItemList<T>>::remove(collection_id, item_id);1752 }1753 else {1754 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1755 }17561740 Ok(())1757 Ok(())1741 }1758 }174217591746 Error::<T>::TokenNotFound1763 Error::<T>::TokenNotFound1747 );1764 );1748 let item = <NftItemList<T>>::get(collection_id, item_id);1765 let item = <NftItemList<T>>::get(collection_id, item_id);1749 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1766 Self::remove_token_index(collection_id, item_id, &item.owner)?;175017671751 // update balance1768 // update balance1752 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1769 let new_balance = <Balance<T>>::get(collection_id, &item.owner)1753 .checked_sub(1)1770 .checked_sub(1)1754 .ok_or(Error::<T>::NumOverflow)?;1771 .ok_or(Error::<T>::NumOverflow)?;1755 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1772 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1858 Ok(())1875 Ok(())1859 }1876 }186018771878 /// Check if token exists. In case of Fungible, check if there is an entry for 1879 /// the owner in fungible balances double map1880 fn token_exists(1881 collection_id: CollectionId,1882 item_id: TokenId,1883 owner: &T::AccountId1884 ) -> DispatchResult {1885 let target_collection = <Collection<T>>::get(collection_id);1886 let exists = match target_collection.mode1887 {1888 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1889 CollectionMode::Fungible(_) => <FungibleItemList<T>>::contains_key(collection_id, owner),1890 CollectionMode::ReFungible(_) => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1891 _ => false1892 };18931894 ensure!(exists == true, Error::<T>::TokenNotFound);1895 Ok(())1896 }18971861 fn transfer_fungible(1898 fn transfer_fungible(1862 collection_id: CollectionId,1899 collection_id: CollectionId,1863 value: u128,1900 value: u128,1864 owner: &T::AccountId,1901 owner: &T::AccountId,1865 recipient: &T::AccountId,1902 recipient: &T::AccountId,1866 ) -> DispatchResult {1903 ) -> DispatchResult {1867 ensure!(1904 Self::token_exists(collection_id, 0, owner)?;1868 <FungibleItemList<T>>::contains_key(collection_id, owner),1869 Error::<T>::TokenNotFound1870 );187119051872 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1906 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1873 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);1907 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);1897 owner: T::AccountId,1931 owner: T::AccountId,1898 new_owner: T::AccountId,1932 new_owner: T::AccountId,1899 ) -> DispatchResult {1933 ) -> DispatchResult {1900 ensure!(1934 Self::token_exists(collection_id, item_id, &owner)?;1901 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1902 Error::<T>::TokenNotFound1903 );190419351905 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1936 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1906 let item = full_item1937 let item = full_item1941 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1972 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);194219731943 // update index collection1974 // update index collection1944 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1975 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1945 } else {1976 } else {1946 let mut new_full_item = full_item.clone();1977 let mut new_full_item = full_item.clone();1947 new_full_item1978 new_full_item1966 owner: new_owner.clone(),1997 owner: new_owner.clone(),1967 fraction: value,1998 fraction: value,1968 });1999 });1969 Self::add_token_index(collection_id, item_id, new_owner.clone())?;2000 Self::add_token_index(collection_id, item_id, &new_owner)?;1970 }2001 }197120021972 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2003 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1981 sender: T::AccountId,2012 sender: T::AccountId,1982 new_owner: T::AccountId,2013 new_owner: T::AccountId,1983 ) -> DispatchResult {2014 ) -> DispatchResult {1984 ensure!(2015 Self::token_exists(collection_id, item_id, &sender)?;1985 <NftItemList<T>>::contains_key(collection_id, item_id),1986 Error::<T>::TokenNotFound1987 );198820161989 let mut item = <NftItemList<T>>::get(collection_id, item_id);2017 let mut item = <NftItemList<T>>::get(collection_id, item_id);199020182010 <NftItemList<T>>::insert(collection_id, item_id, item);2038 <NftItemList<T>>::insert(collection_id, item_id, item);201120392012 // update index collection2040 // update index collection2013 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;2041 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;201420422015 Ok(())2043 Ok(())2016 }2044 }2017 2045 2018 fn item_exists(2019 collection_id: CollectionId,2020 item_id: TokenId,2021 mode: &CollectionMode2022 ) -> DispatchResult {2023 match mode {2024 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2025 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2026 _ => ()2027 };2028 2029 Ok(())2030 }20312032 fn set_re_fungible_variable_data(2046 fn set_re_fungible_variable_data(2033 collection_id: CollectionId,2047 collection_id: CollectionId,2034 item_id: TokenId,2048 item_id: TokenId,2090 .unwrap();2104 .unwrap();209121052092 let item_owner = item.owner.clone();2106 let item_owner = item.owner.clone();2093 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();2107 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();209421082095 <ItemListIndex>::insert(collection_id, current_index);2109 <ItemListIndex>::insert(collection_id, current_index);209621102097 // Update balance2111 // Update balance2098 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2112 let new_balance = <Balance<T>>::get(collection_id, &item_owner)2099 .checked_add(1)2113 .checked_add(1)2100 .unwrap();2114 .unwrap();2101 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2115 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2106 .checked_add(1)2120 .checked_add(1)2107 .unwrap();2121 .unwrap();210821222109 Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();2123 Self::add_token_index(collection_id, current_index, owner).unwrap();211021242111 <ItemListIndex>::insert(collection_id, current_index);2125 <ItemListIndex>::insert(collection_id, current_index);211221262125 let value = item.owner.first().unwrap().fraction;2139 let value = item.owner.first().unwrap().fraction;2126 let owner = item.owner.first().unwrap().owner.clone();2140 let owner = item.owner.first().unwrap().owner.clone();212721412128 Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();2142 Self::add_token_index(collection_id, current_index, &owner).unwrap();212921432130 <ItemListIndex>::insert(collection_id, current_index);2144 <ItemListIndex>::insert(collection_id, current_index);213121452132 // Update balance2146 // Update balance2133 let new_balance = <Balance<T>>::get(collection_id, owner.clone())2147 let new_balance = <Balance<T>>::get(collection_id, &owner)2134 .checked_add(value)2148 .checked_add(value)2135 .unwrap();2149 .unwrap();2136 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2150 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2137 }2151 }213821522139 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {2153 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {214021542141 // add to account limit2155 // add to account limit2142 if <AccountItemCount<T>>::contains_key(owner.clone()) {2156 if <AccountItemCount<T>>::contains_key(owner) {214321572144 // bound Owned tokens by a single address2158 // bound Owned tokens by a single address2145 let count = <AccountItemCount<T>>::get(owner.clone());2159 let count = <AccountItemCount<T>>::get(owner);2146 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);2160 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);214721612148 <AccountItemCount<T>>::insert(owner.clone(), count2162 <AccountItemCount<T>>::insert(owner.clone(), count2153 <AccountItemCount<T>>::insert(owner.clone(), 1);2167 <AccountItemCount<T>>::insert(owner.clone(), 1);2154 }2168 }215521692156 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2170 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2157 if list_exists {2171 if list_exists {2158 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2172 let mut list = <AddressTokens<T>>::get(collection_id, owner);2159 let item_contains = list.contains(&item_index.clone());2173 let item_contains = list.contains(&item_index.clone());216021742161 if !item_contains {2175 if !item_contains {2166 } else {2180 } else {2167 let mut itm = Vec::new();2181 let mut itm = Vec::new();2168 itm.push(item_index.clone());2182 itm.push(item_index.clone());2169 <AddressTokens<T>>::insert(collection_id, owner, itm);2183 <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2170 2171 }2184 }217221852173 Ok(())2186 Ok(())2176 fn remove_token_index(2189 fn remove_token_index(2177 collection_id: CollectionId,2190 collection_id: CollectionId,2178 item_index: TokenId,2191 item_index: TokenId,2179 owner: T::AccountId,2192 owner: &T::AccountId,2180 ) -> DispatchResult {2193 ) -> DispatchResult {218121942182 // update counter2195 // update counter2183 <AccountItemCount<T>>::insert(owner.clone(), 2196 <AccountItemCount<T>>::insert(owner.clone(), 2184 <AccountItemCount<T>>::get(owner.clone())2197 <AccountItemCount<T>>::get(owner)2185 .checked_sub(1)2198 .checked_sub(1)2186 .ok_or(Error::<T>::NumOverflow)?);2199 .ok_or(Error::<T>::NumOverflow)?);21872200218822012189 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2202 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2190 if list_exists {2203 if list_exists {2191 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2204 let mut list = <AddressTokens<T>>::get(collection_id, owner);2192 let item_contains = list.contains(&item_index.clone());2205 let item_contains = list.contains(&item_index.clone());219322062194 if item_contains {2207 if item_contains {2195 list.retain(|&item| item != item_index);2208 list.retain(|&item| item != item_index);2196 <AddressTokens<T>>::insert(collection_id, owner, list);2209 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2197 }2210 }2198 }2211 }219922122203 fn move_token_index(2216 fn move_token_index(2204 collection_id: CollectionId,2217 collection_id: CollectionId,2205 item_index: TokenId,2218 item_index: TokenId,2206 old_owner: T::AccountId,2219 old_owner: &T::AccountId,2207 new_owner: T::AccountId,2220 new_owner: &T::AccountId,2208 ) -> DispatchResult {2221 ) -> DispatchResult {2209 Self::remove_token_index(collection_id, item_index, old_owner)?;2222 Self::remove_token_index(collection_id, item_index, old_owner)?;2210 Self::add_token_index(collection_id, item_index, new_owner)?;2223 Self::add_token_index(collection_id, item_index, new_owner)?;231923322320 let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2333 let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2321 let mut sponsored = true;2334 let mut sponsored = true;2322 if <CreateItemBasket<T>>::contains_key(collection_id) {2335 if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2323 let last_tx_block = <CreateItemBasket<T>>::get(collection_id);2336 let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2324 let limit_time = last_tx_block + limit.into();2337 let limit_time = last_tx_block + limit.into();2325 if block_number <= limit_time {2338 if block_number <= limit_time {2326 sponsored = false;2339 sponsored = false;2327 }2340 }2328 }2341 }2329 if sponsored {2342 if sponsored {2330 <CreateItemBasket<T>>::insert(collection_id, block_number);2343 <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2331 }2344 }233223452333 // check free create limit2346 // check free create limitpallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,7 +1,7 @@
// Tests to be written here
use super::*;
use crate::mock::*;
-use crate::{AccessMode, ApprovePermissions, CollectionMode,
+use crate::{AccessMode, CollectionMode,
Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,
CollectionId, TokenId, MAX_DECIMAL_POINTS};
use frame_support::{assert_noop, assert_ok};
@@ -12,14 +12,17 @@
}
fn default_limits() {
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
+ assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: default_collection_numbers_limit(),
account_token_ownership_limit: 10,
collections_admins_limit: 5,
custom_data_limit: 2048,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
}
@@ -28,7 +31,7 @@
}
fn default_fungible_data () -> CreateFungibleData {
- CreateFungibleData { }
+ CreateFungibleData { value: 5 }
}
fn default_re_fungible_data () -> CreateReFungibleData {
@@ -238,35 +241,35 @@
let data = default_fungible_data();
create_test_item(collection_id, &data.into());
- assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1);
+ assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).value, 5);
});
}
-#[test]
-fn create_multiple_fungible_items() {
- new_test_ext().execute_with(|| {
- default_limits();
+//#[test]
+// fn create_multiple_fungible_items() {
+// new_test_ext().execute_with(|| {
+// default_limits();
- create_test_collection(&CollectionMode::Fungible(3), 1);
+// create_test_collection(&CollectionMode::Fungible(3), 1);
- let origin1 = Origin::signed(1);
+// let origin1 = Origin::signed(1);
- let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
+// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
- assert_ok!(TemplateModule::create_multiple_items(
- origin1.clone(),
- 1,
- 1,
- items_data.clone().into_iter().map(|d| { d.into() }).collect()
- ));
+// assert_ok!(TemplateModule::create_multiple_items(
+// origin1.clone(),
+// 1,
+// 1,
+// items_data.clone().into_iter().map(|d| { d.into() }).collect()
+// ));
- for (index, _) in items_data.iter().enumerate() {
- assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).owner, 1);
- }
- assert_eq!(TemplateModule::balance_count(1, 1), 3000);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
- });
-}
+// for (index, _) in items_data.iter().enumerate() {
+// assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);
+// }
+// assert_eq!(TemplateModule::balance_count(1, 1), 3000);
+// assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
+// });
+// }
#[test]
fn transfer_fungible_item() {
@@ -281,36 +284,26 @@
let data = default_fungible_data();
create_test_item(collection_id, &data.into());
- assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
- assert_eq!(TemplateModule::balance_count(1, 1), 1000);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+ assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 5);
+ assert_eq!(TemplateModule::balance_count(1, 1), 5);
// change owner scenario
- assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));
- assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
- assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 1000);
+ assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 5));
+ assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 0);
assert_eq!(TemplateModule::balance_count(1, 1), 0);
- assert_eq!(TemplateModule::balance_count(1, 2), 1000);
- // assert_eq!(TemplateModule::address_tokens(1, 1), []);
- assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
+ assert_eq!(TemplateModule::balance_count(1, 2), 5);
// split item scenario
- assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 500));
- assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
- assert_eq!(TemplateModule::fungible_item_id(1, 2).owner, 3);
- assert_eq!(TemplateModule::balance_count(1, 2), 500);
- assert_eq!(TemplateModule::balance_count(1, 3), 500);
- assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
- assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+ assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 3));
+ assert_eq!(TemplateModule::balance_count(1, 2), 2);
+ assert_eq!(TemplateModule::balance_count(1, 3), 3);
// split item and new owner has account scenario
- assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 200));
- assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 300);
- assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 700);
- assert_eq!(TemplateModule::balance_count(1, 2), 300);
- assert_eq!(TemplateModule::balance_count(1, 3), 700);
- assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
- assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+ assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 1));
+ assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 1);
+ assert_eq!(TemplateModule::fungible_item_id(1, 3).value, 4);
+ assert_eq!(TemplateModule::balance_count(1, 2), 1);
+ assert_eq!(TemplateModule::balance_count(1, 3), 4);
});
}
@@ -451,14 +444,11 @@
1), Error::<Test>::NoPermission);
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 2,
- amount: 100000000
- }
+ TemplateModule::approved(1, (1, 1, 2)),
+ 5
);
assert_ok!(TemplateModule::transfer_from(
@@ -469,7 +459,7 @@
1,
1
));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4);
});
}
@@ -505,17 +495,10 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
- assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 2,
- amount: 100000000
- }
- );
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
assert_ok!(TemplateModule::transfer_from(
origin2.clone(),
@@ -525,7 +508,7 @@
1,
1
));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+ assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4);
});
}
@@ -560,17 +543,10 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
- assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 2,
- amount: 100000000
- }
- );
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000);
assert_ok!(TemplateModule::transfer_from(
origin2.clone(),
@@ -585,13 +561,9 @@
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 3,
- amount: 100000000
- }
+ TemplateModule::approved(1, (1, 1, 3)),
+ 900
);
});
}
@@ -609,8 +581,7 @@
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_eq!(TemplateModule::balance_count(1, 1), 1000);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+ assert_eq!(TemplateModule::balance_count(1, 1), 5);
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
@@ -627,16 +598,13 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 2,
- amount: 100000000
- }
+ TemplateModule::approved(1, (1, 1, 2)),
+ 5
);
assert_ok!(TemplateModule::transfer_from(
@@ -645,37 +613,23 @@
3,
1,
1,
- 100
+ 4
));
- assert_eq!(TemplateModule::balance_count(1, 1), 900);
- assert_eq!(TemplateModule::balance_count(1, 3), 100);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+ assert_eq!(TemplateModule::balance_count(1, 1), 1);
+ assert_eq!(TemplateModule::balance_count(1, 3), 4);
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
- assert_eq!(
- TemplateModule::approved(1, (1, 1))[0],
- ApprovePermissions {
- approved: 3,
- amount: 100000000
- }
- );
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+ assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1);
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_ok!(TemplateModule::transfer_from(
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+ assert_noop!(TemplateModule::transfer_from(
origin2.clone(),
1,
3,
1,
1,
- 900
- ));
- assert_eq!(TemplateModule::balance_count(1, 1), 0);
- assert_eq!(TemplateModule::balance_count(1, 3), 1000);
- // assert_eq!(TemplateModule::address_tokens(1, 1), []);
- assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
-
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+ 4
+ ), Error::<Test>::TokenValueNotEnough);
});
}
@@ -725,9 +679,9 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1);
// burn item
- assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
assert_noop!(
- TemplateModule::burn_item(origin1.clone(), 1, 1),
+ TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
Error::<Test>::TokenNotFound
);
@@ -749,12 +703,12 @@
create_test_item(collection_id, &data.into());
// check balance (collection with id = 1, user id = 1)
- assert_eq!(TemplateModule::balance_count(1, 1), 1000);
+ assert_eq!(TemplateModule::balance_count(1, 1), 5);
// burn item
- assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
assert_noop!(
- TemplateModule::burn_item(origin1.clone(), 1, 1),
+ TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
Error::<Test>::TokenNotFound
);
@@ -791,9 +745,9 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
// burn item
- assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000));
assert_noop!(
- TemplateModule::burn_item(origin1.clone(), 1, 1),
+ TemplateModule::burn_item(origin1.clone(), 1, 1, 1000),
Error::<Test>::TokenNotFound
);
@@ -875,10 +829,10 @@
// check balance (collection with id = 1, user id = 1)
assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);
- assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 1000);
+ assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5);
assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);
assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);
- assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).owner, 1);
+ assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5);
assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);
});
}
@@ -896,8 +850,8 @@
let origin1 = Origin::signed(1);
// approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
});
}
@@ -914,8 +868,8 @@
create_test_item(collection_id, &data.into());
// approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
@@ -1199,8 +1153,8 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
@@ -1263,8 +1217,8 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
@@ -1298,13 +1252,13 @@
AccessMode::WhiteList
));
assert_noop!(
- TemplateModule::burn_item(origin1.clone(), 1, 1),
+ TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
Error::<Test>::AddresNotInWhiteList
);
});
}
-// If Public Access mode is set to WhiteList, oken transfers can’t be Approved by a non-whitelisted address (see Approve method).
+// If Public Access mode is set to WhiteList, token transfers can’t be Approved by a non-whitelisted address (see Approve method).
#[test]
fn white_list_test_6() {
new_test_ext().execute_with(|| {
@@ -1313,6 +1267,10 @@
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
collection_id,
@@ -1321,7 +1279,7 @@
// do approve
assert_noop!(
- TemplateModule::approve(origin1.clone(), 1, 1, 1),
+ TemplateModule::approve(origin1.clone(), 1, 1, 1, 5),
Error::<Test>::AddresNotInWhiteList
);
});
@@ -1374,8 +1332,8 @@
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
// do approve
- assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
- assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+ assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 5));
+ assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 5);
assert_ok!(TemplateModule::transfer_from(
origin1.clone(),
@@ -1687,7 +1645,10 @@
custom_data_limit: 2048,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1716,7 +1677,10 @@
custom_data_limit: 2048,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1739,7 +1703,10 @@
custom_data_limit: 2048,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1762,7 +1729,10 @@
custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1793,7 +1763,10 @@
custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1824,7 +1797,10 @@
custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1855,7 +1831,10 @@
custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1972,7 +1951,10 @@
custom_data_limit: 10,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -1997,7 +1979,10 @@
custom_data_limit: 10,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ const_on_chain_schema_limit: 1024,
+ offchain_schema_limit: 1024,
+ variable_on_chain_schema_limit: 1024,
}));
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;
});
}