difftreelog
Merge pull request #197 from UniqueNetwork/feature/CORE-167
in: master
Limits logic fixed. Tests added
5 files changed
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -306,9 +306,6 @@
/// Amount of collections destroyed, used for total amount tracking with
/// CreatedCollectionCount
DestroyedCollectionCount: u32;
- /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)
- /// Account id (real)
- pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;
//#endregion
//#region Basic collections
@@ -1125,6 +1122,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = Self::get_collection(collection_id)?;
+ Self::meta_update_check(&sender, &collection, item_id)?;
Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;
@@ -1647,11 +1645,19 @@
collection.consume_sload()?;
let account_items: u32 =
<AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;
- ensure!(
- collection.limits.account_token_ownership_limit > account_items,
- Error::<T>::AccountTokenLimitExceeded
- );
+ // zero limit means collection limit is disabled
+ // otherwise get lower value
+ let limit = if collection.limits.account_token_ownership_limit == 0
+ || collection.limits.account_token_ownership_limit > ACCOUNT_TOKEN_OWNERSHIP_LIMIT
+ {
+ ACCOUNT_TOKEN_OWNERSHIP_LIMIT
+ } else {
+ collection.limits.account_token_ownership_limit
+ };
+
+ ensure!(limit > account_items, Error::<T>::AccountTokenLimitExceeded);
+
// preliminary transfer check
ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);
@@ -1674,12 +1680,23 @@
as u32)
.checked_add(amount)
.ok_or(Error::<T>::AccountTokenLimitExceeded)?;
+
+ // zero limit means collection limit is disabled
+ // otherwise get lower value
+ let account_token_limit = if collection.limits.account_token_ownership_limit == 0
+ || collection.limits.account_token_ownership_limit > ACCOUNT_TOKEN_OWNERSHIP_LIMIT
+ {
+ ACCOUNT_TOKEN_OWNERSHIP_LIMIT
+ } else {
+ collection.limits.account_token_ownership_limit
+ };
+
ensure!(
collection.limits.token_limit >= total_items,
Error::<T>::CollectionTokenLimitExceeded
);
ensure!(
- collection.limits.account_token_ownership_limit >= account_items,
+ account_token_limit >= account_items,
Error::<T>::AccountTokenLimitExceeded
);
@@ -2409,32 +2426,19 @@
item_index: TokenId,
owner: &T::CrossAccountId,
) -> DispatchResult {
- // add to account limit
collection.consume_sload()?;
- if <AccountItemCount<T>>::contains_key(owner.as_sub()) {
- // bound Owned tokens by a single address
+ let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());
+ if list_exists {
collection.consume_sload()?;
- let count = <AccountItemCount<T>>::get(owner.as_sub());
+ let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());
+
+ // bound Owned tokens by a single address in collection
+ let account_items: u32 = list.len() as u32;
ensure!(
- count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,
+ account_items < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,
Error::<T>::AddressOwnershipLimitExceeded
);
- collection.consume_sstore()?;
- <AccountItemCount<T>>::insert(
- owner.as_sub(),
- count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,
- );
- } else {
- collection.consume_sstore()?;
- <AccountItemCount<T>>::insert(owner.as_sub(), 1);
- }
-
- collection.consume_sload()?;
- let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());
- if list_exists {
- collection.consume_sload()?;
- let mut list = <AddressTokens<T>>::get(collection.id, owner.as_sub());
let item_contains = list.contains(&item_index.clone());
if !item_contains {
@@ -2457,16 +2461,6 @@
item_index: TokenId,
owner: &T::CrossAccountId,
) -> DispatchResult {
- // update counter
- collection.consume_sload()?;
- collection.consume_sstore()?;
- <AccountItemCount<T>>::insert(
- owner.as_sub(),
- <AccountItemCount<T>>::get(owner.as_sub())
- .checked_sub(1)
- .ok_or(Error::<T>::NumOverflow)?,
- );
-
collection.consume_sload()?;
let list_exists = <AddressTokens<T>>::contains_key(collection.id, owner.as_sub());
if list_exists {
pallets/nft/src/sponsorship.rsdiffbeforeafterboth1use crate::{2 Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket,3 ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, CreateItemData,4 CollectionMode,5};6use core::marker::PhantomData;7use up_sponsorship::SponsorshipHandler;8use frame_support::{9 traits::{IsSubType},10 storage::{StorageMap, StorageDoubleMap},11};12use nft_data_structs::{13 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,15};1617pub struct NftSponsorshipHandler<T>(PhantomData<T>);18impl<T: Config> NftSponsorshipHandler<T> {19 pub fn withdraw_create_item(20 who: &T::AccountId,21 collection_id: &CollectionId,22 _properties: &CreateItemData,23 ) -> Option<T::AccountId> {24 let collection = CollectionById::<T>::get(collection_id)?;2526 // sponsor timeout27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;2829 let limit = collection.limits.sponsor_transfer_timeout;30 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {31 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));32 let limit_time = last_tx_block + limit.into();33 if block_number <= limit_time {34 return None;35 }36 }37 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);3839 // check free create limit40 if collection.limits.sponsored_data_size >= (_properties.data_size() as u32) {41 collection.sponsorship.sponsor().cloned()42 } else {43 None44 }45 }4647 pub fn withdraw_transfer(48 who: &T::AccountId,49 collection_id: &CollectionId,50 item_id: &TokenId,51 ) -> Option<T::AccountId> {52 let collection = CollectionById::<T>::get(collection_id)?;5354 let mut sponsor_transfer = false;55 if collection.sponsorship.confirmed() {56 let collection_limits = collection.limits.clone();57 let collection_mode = collection.mode.clone();5859 // sponsor timeout60 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;61 sponsor_transfer = match collection_mode {62 CollectionMode::NFT => {63 // get correct limit64 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {65 collection_limits.sponsor_transfer_timeout66 } else {67 NFT_SPONSOR_TRANSFER_TIMEOUT68 };6970 let mut sponsored = true;71 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {72 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);73 let limit_time = last_tx_block + limit.into();74 if block_number <= limit_time {75 sponsored = false;76 }77 }78 if sponsored {79 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);80 }8182 sponsored83 }84 CollectionMode::Fungible(_) => {85 // get correct limit86 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {87 collection_limits.sponsor_transfer_timeout88 } else {89 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT90 };9192 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;93 let mut sponsored = true;94 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {95 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);96 let limit_time = last_tx_block + limit.into();97 if block_number <= limit_time {98 sponsored = false;99 }100 }101 if sponsored {102 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);103 }104105 sponsored106 }107 CollectionMode::ReFungible => {108 // get correct limit109 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {110 collection_limits.sponsor_transfer_timeout111 } else {112 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT113 };114115 let mut sponsored = true;116 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {117 let last_tx_block =118 ReFungibleTransferBasket::<T>::get(collection_id, item_id);119 let limit_time = last_tx_block + limit.into();120 if block_number <= limit_time {121 sponsored = false;122 }123 }124 if sponsored {125 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);126 }127128 sponsored129 }130 };131 }132133 if !sponsor_transfer {134 None135 } else {136 collection.sponsorship.sponsor().cloned()137 }138 }139140 pub fn withdraw_set_variable_meta_data(141 collection_id: &CollectionId,142 item_id: &TokenId,143 data: &[u8],144 ) -> Option<T::AccountId> {145 let mut sponsor_metadata_changes = false;146147 let collection = CollectionById::<T>::get(collection_id)?;148149 if collection.sponsorship.confirmed() &&150 // Can't sponsor fungible collection, this tx will be rejected151 // as invalid152 !matches!(collection.mode, CollectionMode::Fungible(_)) &&153 data.len() <= collection.limits.sponsored_data_size as usize154 {155 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {156 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;157158 if VariableMetaDataBasket::<T>::get(collection_id, item_id)159 .map(|last_block| block_number - last_block > rate_limit)160 .unwrap_or(true)161 {162 sponsor_metadata_changes = true;163 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);164 }165 }166 }167168 if !sponsor_metadata_changes {169 None170 } else {171 collection.sponsorship.sponsor().cloned()172 }173 }174}175176impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>177where178 T: Config,179 C: IsSubType<Call<T>>,180{181 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {182 match IsSubType::<Call<T>>::is_sub_type(call)? {183 Call::create_item(collection_id, _owner, properties) => {184 Self::withdraw_create_item(who, collection_id, properties)185 }186 Call::transfer(_new_owner, collection_id, item_id, _value) => {187 Self::withdraw_transfer(who, collection_id, item_id)188 }189 Call::set_variable_meta_data(collection_id, item_id, data) => {190 Self::withdraw_set_variable_meta_data(collection_id, item_id, data)191 }192 _ => None,193 }194 }195}1use crate::{2 Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket,3 ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, CreateItemData,4 CollectionMode,5};6use core::marker::PhantomData;7use up_sponsorship::SponsorshipHandler;8use frame_support::{9 traits::{IsSubType},10 storage::{StorageMap, StorageDoubleMap},11};12use nft_data_structs::{13 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,15};1617pub struct NftSponsorshipHandler<T>(PhantomData<T>);18impl<T: Config> NftSponsorshipHandler<T> {19 pub fn withdraw_create_item(20 who: &T::AccountId,21 collection_id: &CollectionId,22 _properties: &CreateItemData,23 ) -> Option<T::AccountId> {24 let collection = CollectionById::<T>::get(collection_id)?;2526 // sponsor timeout27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;2829 let limit = collection.limits.sponsor_transfer_timeout;30 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {31 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));32 let limit_time = last_tx_block + limit.into();33 if block_number <= limit_time {34 return None;35 }36 }37 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);3839 // check free create limit40 if collection.limits.sponsored_data_size >= (_properties.data_size() as u32) {41 collection.sponsorship.sponsor().cloned()42 } else {43 None44 }45 }4647 pub fn withdraw_transfer(48 who: &T::AccountId,49 collection_id: &CollectionId,50 item_id: &TokenId,51 ) -> Option<T::AccountId> {52 let collection = CollectionById::<T>::get(collection_id)?;5354 let mut sponsor_transfer = false;55 if collection.sponsorship.confirmed() {56 let collection_limits = collection.limits.clone();57 let collection_mode = collection.mode.clone();5859 // sponsor timeout60 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;61 sponsor_transfer = match collection_mode {62 CollectionMode::NFT => {63 // get correct limit64 let limit: u32 = if collection_limits.sponsor_transfer_timeout != 0 {65 if collection_limits.sponsor_transfer_timeout > NFT_SPONSOR_TRANSFER_TIMEOUT66 {67 collection_limits.sponsor_transfer_timeout68 } else {69 NFT_SPONSOR_TRANSFER_TIMEOUT70 }71 } else {72 073 };7475 let mut sponsored = true;76 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {77 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);78 let limit_time = last_tx_block + limit.into();79 if block_number <= limit_time {80 sponsored = false;81 }82 }83 if sponsored {84 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);85 }8687 sponsored88 }89 CollectionMode::Fungible(_) => {90 // get correct limit91 let limit: u32 = if collection_limits.sponsor_transfer_timeout != 0 {92 if collection_limits.sponsor_transfer_timeout93 > FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT94 {95 collection_limits.sponsor_transfer_timeout96 } else {97 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT98 }99 } else {100 0101 };102103 let mut sponsored = true;104 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {105 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);106 let limit_time = last_tx_block + limit.into();107 if block_number <= limit_time {108 sponsored = false;109 }110 }111 if sponsored {112 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);113 }114115 sponsored116 }117 CollectionMode::ReFungible => {118 // get correct limit119 let limit: u32 = if collection_limits.sponsor_transfer_timeout != 0 {120 if collection_limits.sponsor_transfer_timeout121 > REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT122 {123 collection_limits.sponsor_transfer_timeout124 } else {125 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT126 }127 } else {128 0129 };130131 let mut sponsored = true;132 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {133 let last_tx_block =134 ReFungibleTransferBasket::<T>::get(collection_id, item_id);135 let limit_time = last_tx_block + limit.into();136 if block_number <= limit_time {137 sponsored = false;138 }139 }140 if sponsored {141 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);142 }143144 sponsored145 }146 };147 }148149 if !sponsor_transfer {150 None151 } else {152 collection.sponsorship.sponsor().cloned()153 }154 }155156 pub fn withdraw_set_variable_meta_data(157 collection_id: &CollectionId,158 item_id: &TokenId,159 data: &[u8],160 ) -> Option<T::AccountId> {161 let mut sponsor_metadata_changes = false;162163 let collection = CollectionById::<T>::get(collection_id)?;164165 if collection.sponsorship.confirmed() &&166 // Can't sponsor fungible collection, this tx will be rejected167 // as invalid168 !matches!(collection.mode, CollectionMode::Fungible(_)) &&169 data.len() <= collection.limits.sponsored_data_size as usize170 {171 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {172 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;173174 if VariableMetaDataBasket::<T>::get(collection_id, item_id)175 .map(|last_block| block_number - last_block > rate_limit)176 .unwrap_or(true)177 {178 sponsor_metadata_changes = true;179 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);180 }181 }182 }183184 if !sponsor_metadata_changes {185 None186 } else {187 collection.sponsorship.sponsor().cloned()188 }189 }190}191192impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>193where194 T: Config,195 C: IsSubType<Call<T>>,196{197 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {198 match IsSubType::<Call<T>>::is_sub_type(call)? {199 Call::create_item(collection_id, _owner, properties) => {200 Self::withdraw_create_item(who, collection_id, properties)201 }202 Call::transfer(_new_owner, collection_id, item_id, _value) => {203 Self::withdraw_transfer(who, collection_id, item_id)204 }205 Call::set_variable_meta_data(collection_id, item_id, data) => {206 Self::withdraw_set_variable_meta_data(collection_id, item_id, data)207 }208 _ => None,209 }210 }211}pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1789,7 +1789,7 @@
let data = default_nft_data();
assert_noop!(
TemplateModule::create_item(origin1, 1, account(1), data.into()),
- Error::<Test>::AddressOwnershipLimitExceeded
+ Error::<Test>::AccountTokenLimitExceeded
);
});
}
@@ -2016,11 +2016,11 @@
let data = default_nft_data();
create_test_item(1, &data.into());
- TemplateModule::set_meta_update_permission_flag(
+ assert_ok!(TemplateModule::set_meta_update_permission_flag(
origin1.clone(),
collection_id,
MetaUpdatePermission::ItemOwner,
- );
+ ));
let variable_data = b"ten chars.".to_vec();
assert_ok!(TemplateModule::set_variable_meta_data(
@@ -2042,20 +2042,17 @@
#[test]
fn set_variable_meta_data_on_nft_with_item_owner_permission_flag_neg() {
new_test_ext().execute_with(|| {
- // default_limits();
-
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+ let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
assert_ok!(TemplateModule::set_mint_permission(
- origin2.clone(),
+ origin1.clone(),
collection_id,
true
));
assert_ok!(TemplateModule::add_to_white_list(
- origin2.clone(),
+ origin1.clone(),
collection_id,
account(1)
));
@@ -2064,226 +2061,226 @@
create_test_item(1, &data.into());
assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
+ origin1.clone(),
collection_id,
MetaUpdatePermission::ItemOwner,
));
- let variable_data = b"ten chars.++".to_vec();
+ let variable_data = b"1234567890123".to_vec();
assert_noop!(
TemplateModule::set_variable_meta_data(
- origin2,
+ origin1,
collection_id,
1,
variable_data.clone()
),
Error::<Test>::TokenVariableDataLimitExceeded
);
+ })
+}
- #[test]
- fn collection_transfer_flag_works() {
- new_test_ext().execute_with(|| {
- let origin1 = Origin::signed(1);
+#[test]
+fn collection_transfer_flag_works() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::set_transfers_enabled_flag(origin1, 1, true));
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ assert_ok!(TemplateModule::set_transfers_enabled_flag(origin1, 1, true));
- let data = default_nft_data();
- create_test_item(collection_id, &data.into());
- assert_eq!(TemplateModule::balance_count(1, 1), 1);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(TemplateModule::balance_count(1, 1), 1);
+ assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- let origin1 = Origin::signed(1);
+ let origin1 = Origin::signed(1);
- // default scenario
- assert_ok!(TemplateModule::transfer(origin1, account(2), 1, 1, 1000));
- assert_eq!(TemplateModule::nft_item_id(1, 1).unwrap().owner, account(2));
- assert_eq!(TemplateModule::balance_count(1, 1), 0);
- assert_eq!(TemplateModule::balance_count(1, 2), 1);
+ // default scenario
+ assert_ok!(TemplateModule::transfer(origin1, account(2), 1, 1, 1000));
+ assert_eq!(TemplateModule::nft_item_id(1, 1).unwrap().owner, account(2));
+ assert_eq!(TemplateModule::balance_count(1, 1), 0);
+ assert_eq!(TemplateModule::balance_count(1, 2), 1);
- assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
- });
- }
+ assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
+ });
+}
- #[test]
- fn set_variable_meta_data_on_nft_with_admin_flag() {
- new_test_ext().execute_with(|| {
- // default_limits();
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+ let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
- let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_mint_permission(
- origin2.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::add_to_white_list(
- origin2.clone(),
- collection_id,
- account(1)
- ));
+ assert_ok!(TemplateModule::set_mint_permission(
+ origin2.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(TemplateModule::add_to_white_list(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
- assert_ok!(TemplateModule::add_collection_admin(
- origin2.clone(),
- collection_id,
- account(1)
- ));
+ assert_ok!(TemplateModule::add_collection_admin(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
- let data = default_nft_data();
- create_test_item(1, &data.into());
+ let data = default_nft_data();
+ create_test_item(1, &data.into());
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::Admin,
- ));
+ assert_ok!(TemplateModule::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin,
+ ));
- let variable_data = b"test set_variable_meta_data method.".to_vec();
- assert_ok!(TemplateModule::set_variable_meta_data(
- origin1,
- collection_id,
- 1,
- variable_data.clone()
- ));
+ let variable_data = b"test.".to_vec();
+ assert_ok!(TemplateModule::set_variable_meta_data(
+ origin1,
+ collection_id,
+ 1,
+ variable_data.clone()
+ ));
- assert_eq!(
- TemplateModule::nft_item_id(collection_id, 1)
- .unwrap()
- .variable_data,
- variable_data
- );
- });
- }
+ assert_eq!(
+ TemplateModule::nft_item_id(collection_id, 1)
+ .unwrap()
+ .variable_data,
+ variable_data
+ );
+ });
+}
- #[test]
- fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
- new_test_ext().execute_with(|| {
- // default_limits();
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+ let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
- let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
+ let origin1 = Origin::signed(1);
+ let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_mint_permission(
- origin2.clone(),
- collection_id,
- true
- ));
- assert_ok!(TemplateModule::add_to_white_list(
- origin2.clone(),
- collection_id,
- account(1)
- ));
+ assert_ok!(TemplateModule::set_mint_permission(
+ origin2.clone(),
+ collection_id,
+ true
+ ));
+ assert_ok!(TemplateModule::add_to_white_list(
+ origin2.clone(),
+ collection_id,
+ account(1)
+ ));
- let data = default_nft_data();
- create_test_item(1, &data.into());
+ let data = default_nft_data();
+ create_test_item(1, &data.into());
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::Admin,
- ));
+ assert_ok!(TemplateModule::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin,
+ ));
- let variable_data = b"test set_variable_meta_data method.".to_vec();
- assert_noop!(
- TemplateModule::set_variable_meta_data(
- origin1,
- collection_id,
- 1,
- variable_data.clone()
- ),
- Error::<Test>::NoPermission
- );
- });
- }
+ let variable_data = b"test.".to_vec();
+ assert_noop!(
+ TemplateModule::set_variable_meta_data(
+ origin1,
+ collection_id,
+ 1,
+ variable_data.clone()
+ ),
+ Error::<Test>::NoPermission
+ );
+ });
+}
- #[test]
- fn set_variable_meta_flag_after_freeze() {
- new_test_ext().execute_with(|| {
- // default_limits();
+#[test]
+fn set_variable_meta_flag_after_freeze() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+ let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
- let origin2 = Origin::signed(2);
+ let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::None,
- ));
- assert_noop!(
- TemplateModule::set_meta_update_permission_flag(
- origin2.clone(),
- collection_id,
- MetaUpdatePermission::Admin
- ),
- Error::<Test>::MetadataFlagFrozen
- );
- });
- }
+ assert_ok!(TemplateModule::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::None,
+ ));
+ assert_noop!(
+ TemplateModule::set_meta_update_permission_flag(
+ origin2.clone(),
+ collection_id,
+ MetaUpdatePermission::Admin
+ ),
+ Error::<Test>::MetadataFlagFrozen
+ );
+ });
+}
- #[test]
- fn set_variable_meta_data_on_nft_with_none_flag_neg() {
- new_test_ext().execute_with(|| {
- // default_limits();
+#[test]
+fn set_variable_meta_data_on_nft_with_none_flag_neg() {
+ new_test_ext().execute_with(|| {
+ // default_limits();
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
- let origin1 = Origin::signed(1);
+ let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
+ let origin1 = Origin::signed(1);
- let data = default_nft_data();
- create_test_item(1, &data.into());
+ let data = default_nft_data();
+ create_test_item(1, &data.into());
- assert_ok!(TemplateModule::set_meta_update_permission_flag(
- origin1.clone(),
- collection_id,
- MetaUpdatePermission::None,
- ));
+ assert_ok!(TemplateModule::set_meta_update_permission_flag(
+ origin1.clone(),
+ collection_id,
+ MetaUpdatePermission::None,
+ ));
- let variable_data = b"test set_variable_meta_data method.".to_vec();
- assert_noop!(
- TemplateModule::set_variable_meta_data(
- origin1.clone(),
- collection_id,
- 1,
- variable_data.clone()
- ),
- Error::<Test>::MetadataUpdateDenied
- );
- });
- }
+ let variable_data = b"test.".to_vec();
+ assert_noop!(
+ TemplateModule::set_variable_meta_data(
+ origin1.clone(),
+ collection_id,
+ 1,
+ variable_data.clone()
+ ),
+ Error::<Test>::MetadataUpdateDenied
+ );
+ });
+}
- #[test]
- fn collection_transfer_flag_works_neg() {
- new_test_ext().execute_with(|| {
- let origin1 = Origin::signed(1);
+#[test]
+fn collection_transfer_flag_works_neg() {
+ new_test_ext().execute_with(|| {
+ let origin1 = Origin::signed(1);
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::set_transfers_enabled_flag(
- origin1, 1, false
- ));
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ assert_ok!(TemplateModule::set_transfers_enabled_flag(
+ origin1, 1, false
+ ));
- let data = default_nft_data();
- create_test_item(collection_id, &data.into());
- assert_eq!(TemplateModule::balance_count(1, 1), 1);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+ assert_eq!(TemplateModule::balance_count(1, 1), 1);
+ assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- let origin1 = Origin::signed(1);
+ let origin1 = Origin::signed(1);
- // default scenario
- assert_noop!(
- TemplateModule::transfer(origin1, account(2), 1, 1, 1000),
- Error::<Test>::TransferNotAllowed
- );
- assert_eq!(TemplateModule::nft_item_id(1, 1).unwrap().owner, account(1));
- assert_eq!(TemplateModule::balance_count(1, 1), 1);
- assert_eq!(TemplateModule::balance_count(1, 2), 0);
+ // default scenario
+ assert_noop!(
+ TemplateModule::transfer(origin1, account(2), 1, 1, 1000),
+ Error::<Test>::TransferNotAllowed
+ );
+ assert_eq!(TemplateModule::nft_item_id(1, 1).unwrap().owner, account(1));
+ assert_eq!(TemplateModule::balance_count(1, 1), 1);
+ assert_eq!(TemplateModule::balance_count(1, 2), 0);
- assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- });
- }
+ assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
});
}
tests/src/limits.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/limits.test.ts
@@ -0,0 +1,378 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import {
+ createCollectionExpectSuccess,
+ destroyCollectionExpectSuccess,
+ setCollectionLimitsExpectSuccess,
+ setCollectionSponsorExpectSuccess,
+ confirmSponsorshipExpectSuccess,
+ createItemExpectSuccess,
+ createItemExpectFailure,
+ transferExpectSuccess,
+ getFreeBalance,
+ waitNewBlocks,
+} from './util/helpers';
+import { expect } from 'chai';
+
+describe('Number of tokens per address (NFT)', () => {
+ let Alice: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ });
+ });
+
+ it('Collection limits allow greater number than chain limits, chain limits are enforced', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 20 });
+ for(let i = 0; i < 10; i++){
+ await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ }
+ await createItemExpectFailure(Alice, collectionId, 'NFT');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+
+ it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 1 });
+ await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ await createItemExpectFailure(Alice, collectionId, 'NFT');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('Number of tokens per address (ReFungible)', () => {
+ let Alice: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ });
+ });
+
+ it('Collection limits allow greater number than chain limits, chain limits are enforced', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible' }});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 20 });
+ for(let i = 0; i < 10; i++){
+ await createItemExpectSuccess(Alice, collectionId, 'ReFungible');
+ }
+ await createItemExpectFailure(Alice, collectionId, 'ReFungible');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+
+ it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible' }});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 1 });
+ await createItemExpectSuccess(Alice, collectionId, 'ReFungible');
+ await createItemExpectFailure(Alice, collectionId, 'ReFungible');
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('Sponsor timeout (NFT)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 7 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob);
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 5, fail
+ await waitNewBlocks(5);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie);
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 7, success
+ await waitNewBlocks(2); // 5 + 2
+ await transferExpectSuccess(collectionId, tokenId, Charlie, Bob);
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+
+ it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 1 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob);
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 1, fail
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie);
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 5, success
+ await waitNewBlocks(4);
+ await transferExpectSuccess(collectionId, tokenId, Charlie, Bob);
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('Sponsor timeout (Fungible)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 7 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'Fungible');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob, 10, 'Fungible');
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 5, fail
+ await waitNewBlocks(5);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 7, success
+ await waitNewBlocks(2); // 5 + 2
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+
+ it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 1 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'Fungible');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob, 10, 'Fungible');
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 1, fail
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 5, success
+ await waitNewBlocks(4);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('Sponsor timeout (ReFungible)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible' }});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 7 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'ReFungible');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob, 100, 'ReFungible');
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 5, fail
+ await waitNewBlocks(5);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 20, 'ReFungible');
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 7, success
+ await waitNewBlocks(2); // 5 + 2
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 20, 'ReFungible');
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+
+ it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 1 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob);
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 1, fail
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie);
+ const aliceBalanceAfterUnsponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+ // check setting SponsorTimeout = 5, success
+ await waitNewBlocks(4);
+ await transferExpectSuccess(collectionId, tokenId, Charlie, Bob);
+ const aliceBalanceAfterSponsoredTransaction = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+ await destroyCollectionExpectSuccess(collectionId);
+ });
+});
+
+describe('Collection zero limits (NFT)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Limits have 0 in tokens per address field, the chain limits are applied', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 0 });
+ for(let i = 0; i < 10; i++){
+ await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ }
+ await createItemExpectFailure(Alice, collectionId, 'NFT');
+ });
+
+ it('Limits have 0 in sponsor timeout, no limits are applied', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 0 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'NFT');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob);
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 0, success with next block
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie);
+ const aliceBalanceAfterSponsoredTransaction1 = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);
+ });
+});
+
+describe.only('Collection zero limits (Fungible)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Limits have 0 in sponsor timeout, no limits are applied', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 0 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'Fungible');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob, 10, 'Fungible');
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+
+ // check setting SponsorTimeout = 0, success with next block
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 2, 'Fungible');
+ const aliceBalanceAfterSponsoredTransaction1 = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);
+ });
+});
+
+describe.only('Collection zero limits (ReFungible)', () => {
+ let Alice: IKeyringPair;
+ let Bob: IKeyringPair;
+ let Charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ Alice = privateKey('//Alice');
+ Bob = privateKey('//Bob');
+ Charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('Limits have 0 in tokens per address field, the chain limits are applied', async () => {
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible' }});
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { AccountTokenOwnershipLimit: 0 });
+ for(let i = 0; i < 10; i++){
+ await createItemExpectSuccess(Alice, collectionId, 'ReFungible');
+ }
+ await createItemExpectFailure(Alice, collectionId, 'ReFungible');
+ });
+
+ it('Limits have 0 in sponsor timeout, no limits are applied', async () => {
+
+ const collectionId = await createCollectionExpectSuccess({ mode: { type: 'ReFungible' } });
+ await setCollectionLimitsExpectSuccess(Alice, collectionId, { SponsorTimeout: 0 });
+ const tokenId = await createItemExpectSuccess(Alice, collectionId, 'ReFungible');
+ await setCollectionSponsorExpectSuccess(collectionId, Alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+ await transferExpectSuccess(collectionId, tokenId, Alice, Bob, 100, 'ReFungible');
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 20, 'ReFungible');
+ const aliceBalanceBefore = (await getFreeBalance(Alice)).toNumber();
+
+ // check setting SponsorTimeout = 0, success with next block
+ await waitNewBlocks(1);
+ await transferExpectSuccess(collectionId, tokenId, Bob, Charlie, 20, 'ReFungible');
+ const aliceBalanceAfterSponsoredTransaction1 = (await getFreeBalance(Alice)).toNumber();
+ expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);
+ });
+});
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -811,6 +811,17 @@
}
export async function
+getFreeBalance(account: IKeyringPair) : Promise<BigNumber>
+{
+ let balance = new BigNumber(0) ;
+ await usingApi(async (api) => {
+ balance = new BigNumber((await api.query.system.account(account.address)).data.free.toString());
+ });
+
+ return balance;
+}
+
+export async function
scheduleTransferExpectSuccess(
collectionId: number,
tokenId: number,
@@ -884,8 +895,11 @@
if (type === 'ReFungible') {
const nftItemData =
(await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON() as unknown as IReFungibleTokenDataType;
- expect(nftItemData.Owner[0].Owner).to.be.deep.equal(to);
- expect(nftItemData.Owner[0].Fraction.toString()).to.be.equal(value.toString());
+ const expectedOwner = toSubstrateAddress(to);
+ const ownerIndex = nftItemData.Owner.findIndex(v => toSubstrateAddress(v.Owner as any as string) == expectedOwner);
+ expect(ownerIndex).to.not.equal(-1);
+ expect(nftItemData.Owner[ownerIndex].Owner).to.be.deep.equal(normalizeAccountId(to));
+ expect(nftItemData.Owner[ownerIndex].Fraction).to.be.greaterThanOrEqual(value as number);
}
});
}
@@ -1190,7 +1204,6 @@
export async function waitNewBlocks(blocksCount = 1): Promise<void> {
await usingApi(async (api) => {
const promise = new Promise<void>(async (resolve) => {
-
const unsubscribe = await api.rpc.chain.subscribeNewHeads(() => {
if (blocksCount > 0) {
blocksCount--;