From 8a341d92586b669519911f5bea7db2e14b2dd4e2 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Wed, 18 Dec 2024 08:01:08 +0000 Subject: [PATCH 01/40] refactor: account balance deposit --- pallets/nfts/src/features/approvals.rs | 5 +- .../nfts/src/features/create_delete_item.rs | 52 ++++++++++++---- pallets/nfts/src/features/transfer.rs | 60 ++++++++++++++++--- pallets/nfts/src/lib.rs | 6 +- pallets/nfts/src/mock.rs | 1 + 5 files changed, 102 insertions(+), 22 deletions(-) diff --git a/pallets/nfts/src/features/approvals.rs b/pallets/nfts/src/features/approvals.rs index e40fb6e8..005f5eac 100644 --- a/pallets/nfts/src/features/approvals.rs +++ b/pallets/nfts/src/features/approvals.rs @@ -216,7 +216,10 @@ impl, I: 'static> Pallet { Self::is_pallet_feature_enabled(PalletFeature::Approvals), Error::::MethodDisabled ); - ensure!(AccountBalance::::get(collection, &owner) > 0, Error::::NoItemOwned); + ensure!( + AccountBalance::::contains_key(collection, &owner), + Error::::NoItemOwned + ); let collection_config = Self::get_collection_config(&collection)?; ensure!( diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index 0e51759c..824a2627 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -68,10 +68,6 @@ impl, I: 'static> Pallet { collection_details.items.saturating_inc(); - AccountBalance::::mutate(collection, &mint_to, |balance| { - balance.saturating_inc(); - }); - let collection_config = Self::get_collection_config(&collection)?; let deposit_amount = match collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { @@ -83,6 +79,31 @@ impl, I: 'static> Pallet { Some(depositor) => depositor, }; + AccountBalance::::mutate( + collection, + &mint_to, + |maybe_balance| -> DispatchResult { + match maybe_balance { + None => { + // This is questionable - should collection config be able to override + // chain security? + let deposit_amount = match collection_config + .is_setting_enabled(CollectionSetting::DepositRequired) + { + true => T::BalanceDeposit::get(), + false => Zero::zero(), + }; + T::Currency::reserve(&deposit_account, deposit_amount)?; + *maybe_balance = Some((1, (deposit_account.clone(), deposit_amount))); + }, + Some((balance, _deposit)) => { + balance.saturating_inc(); + }, + } + Ok(()) + }, + )?; + let item_owner = mint_to.clone(); Account::::insert((&item_owner, &collection, &item), ()); @@ -264,12 +285,23 @@ impl, I: 'static> Pallet { PendingSwapOf::::remove(collection, item); ItemAttributesApprovalsOf::::remove(collection, item); - let balance = AccountBalance::::take(collection, &owner) - .checked_sub(1) - .ok_or(ArithmeticError::Underflow)?; - if balance > 0 { - AccountBalance::::insert(collection, &owner, balance); - } + AccountBalance::::try_mutate_exists( + collection, + &owner, + |maybe_balance| -> DispatchResult { + match maybe_balance { + None => return Err(Error::::NoItemOwned.into()), + Some((mut balance, (depositor_account, deposit_amount))) => { + balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; + if balance == 0 { + T::Currency::unreserve(&depositor_account, *deposit_amount); + *maybe_balance = None; + } + Ok(()) + }, + } + }, + )?; if remove_config { ItemConfigOf::::remove(collection, item); diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index ffe0befe..81463d5d 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -19,6 +19,7 @@ //! of the NFTs pallet. use frame_support::pallet_prelude::*; +use sp_runtime::ArithmeticError; use crate::*; @@ -87,16 +88,57 @@ impl, I: 'static> Pallet { with_details(&collection_details, &mut details)?; // Update account balance of the owner. - let balance = AccountBalance::::take(collection, &details.owner) - .checked_sub(1) - .ok_or(Error::::NoItemOwned)?; - if balance > 0 { - AccountBalance::::insert(collection, &details.owner, balance); - } + AccountBalance::::try_mutate_exists( + collection, + &details.owner, + |maybe_balance| -> DispatchResult { + // NOTE: same code as do_burn implementation - should be refactored into a + // reusable function for both burn and the source part of the transfer + match maybe_balance { + None => return Err(Error::::NoItemOwned.into()), + Some((mut balance, (depositor_account, deposit_amount))) => { + balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; + if balance == 0 { + T::Currency::unreserve(&depositor_account, *deposit_amount); + *maybe_balance = None; + } + Ok(()) + }, + } + }, + )?; // Update account balance of the destination account. - AccountBalance::::mutate(collection, &dest, |balance| { - balance.saturating_inc(); - }); + AccountBalance::::mutate(collection, &dest, |maybe_balance| -> DispatchResult { + // NOTE: very similar code to do_mint implementation - should be refactored into a + // reusable function for both minting and the dest part of the transfer + match maybe_balance { + None => { + let deposit_account = match frame_system::Pallet::::account_exists(&dest) { + // NOTE: no balance check, would fail in reservation below if dest has + // insufficient funds. As it is just a transfer and not a mint, could caller + // just resubmit once dest balance rectified? + true => dest.clone(), + // TODO: replace with signed origin account, which requires a parameter to + // be added to the function + false => collection_details.owner, + }; + // This is questionable - should collection config be able to override + // chain security? + let deposit_amount = match collection_config + .is_setting_enabled(CollectionSetting::DepositRequired) + { + true => T::BalanceDeposit::get(), + false => Zero::zero(), + }; + T::Currency::reserve(&deposit_account, deposit_amount)?; + *maybe_balance = Some((1, (deposit_account, deposit_amount))); + }, + Some((balance, _deposit)) => { + balance.saturating_inc(); + }, + } + Ok(()) + })?; // Update account ownership information. Account::::remove((&details.owner, &collection, &item)); diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 2851e826..2ff8a803 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -259,6 +259,9 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + #[pallet::constant] + type BalanceDeposit: Get>; } /// Details of a collection. @@ -435,8 +438,7 @@ pub mod pallet { T::CollectionId, Blake2_128Concat, T::AccountId, - u32, - ValueQuery, + (u32, (T::AccountId, DepositBalanceOf)), >; /// Permission for a delegate to transfer all owner's collection items. diff --git a/pallets/nfts/src/mock.rs b/pallets/nfts/src/mock.rs index c6e4211e..5d3015cf 100644 --- a/pallets/nfts/src/mock.rs +++ b/pallets/nfts/src/mock.rs @@ -65,6 +65,7 @@ parameter_types! { impl Config for Test { type ApprovalsLimit = ConstU32<10>; type AttributeDepositBase = ConstU64<1>; + type BalanceDeposit = ConstU64<1>; type CollectionApprovalDeposit = ConstU64<1>; type CollectionDeposit = ConstU64<2>; type CollectionId = u32; From e391888119e2fa03930615c54df1372708ee683e Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:32:15 +0700 Subject: [PATCH 02/40] chore: revert test changes --- pallets/nfts/src/tests.rs | 2045 +++++++++++++++++-------------------- 1 file changed, 914 insertions(+), 1131 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 19ace89f..f1c36c69 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -224,184 +224,125 @@ fn basic_setup_works() { #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - let (collection_id_1, collection_id_2) = (0, 1); - let (item_id_1, item_id_2) = (42, 69); - let owner_1 = account(1); - let owner_2 = account(2); - assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - owner_1.clone(), + account(1), default_collection_config() )); - assert_eq!(collections(), vec![(owner_1.clone(), collection_id_1)]); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(owner_1.clone()), - collection_id_1, - item_id_1, - owner_1.clone(), - None - )); - assert_eq!(AccountBalance::::get(collection_id_1, &owner_1), 1); - assert_eq!(items(), vec![(owner_1.clone(), collection_id_1, item_id_1)]); + assert_eq!(collections(), vec![(account(1), 0)]); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_eq!(AccountBalance::::get(0, &account(1)), 1); + assert_eq!(items(), vec![(account(1), 0, 42)]); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - owner_2.clone(), + account(2), default_collection_config() )); - assert_eq!( - collections(), - vec![(owner_1.clone(), collection_id_1), (owner_2.clone(), collection_id_2)] - ); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(owner_2), - collection_id_2, - item_id_2, - owner_1.clone(), - None - )); - assert_eq!(AccountBalance::::get(collection_id_2, &owner_1), 1); - assert_eq!( - items(), - vec![ - (owner_1.clone(), collection_id_1, item_id_1), - (owner_1, collection_id_2, item_id_2) - ] - ); + assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); + assert_eq!(AccountBalance::::get(1, &account(1)), 1); + assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); } #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { - let collection_id = 0; - let owner = account(1); - - Balances::make_free_balance_be(&owner, 100); + Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(owner.clone()), - owner.clone(), + RuntimeOrigin::signed(account(1)), + account(1), collection_config_with_all_settings_enabled() )); - assert_eq!(Balances::reserved_balance(&owner), 2); - assert_eq!(collections(), vec![(owner.clone(), collection_id)]); + assert_eq!(Balances::reserved_balance(&account(1)), 2); + assert_eq!(collections(), vec![(account(1), 0)]); assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, bvec![0, 0] )); - assert_eq!(Balances::reserved_balance(&owner), 5); - assert!(CollectionMetadataOf::::contains_key(collection_id)); + assert_eq!(Balances::reserved_balance(&account(1)), 5); + assert!(CollectionMetadataOf::::contains_key(0)); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, 42, account(10), default_item_config() )); - assert_eq!(AccountBalance::::get(collection_id, account(10)), 1); - assert_eq!(Balances::reserved_balance(&owner), 6); + assert_eq!(Balances::reserved_balance(&account(1)), 6); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, 69, account(20), default_item_config() )); - assert_eq!(AccountBalance::::get(collection_id, account(20)), 1); - assert_eq!(Balances::reserved_balance(&owner), 7); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 70, - owner.clone(), - None - )); - assert_eq!(AccountBalance::::get(collection_id, &owner), 1); - assert_eq!( - items(), - vec![ - (owner.clone(), collection_id, 70), - (account(10), collection_id, 42), - (account(20), collection_id, 69) - ] - ); - assert_eq!(Collection::::get(collection_id).unwrap().items, 3); - assert_eq!(Collection::::get(collection_id).unwrap().item_metadatas, 0); - assert_eq!(Collection::::get(collection_id).unwrap().item_configs, 3); - - assert_eq!(Balances::reserved_balance(&owner), 8); - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 70, - account(2) - )); - assert_eq!(AccountBalance::::get(collection_id, &owner), 0); - assert_eq!(AccountBalance::::get(collection_id, account(2)), 1); - assert_eq!(Balances::reserved_balance(&owner), 8); + assert_eq!(AccountBalance::::get(0, account(20)), 1); + assert_eq!(Balances::reserved_balance(&account(1)), 7); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); + assert_eq!(AccountBalance::::get(0, &account(1)), 1); + assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); + assert_eq!(Collection::::get(0).unwrap().items, 3); + assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); + assert_eq!(Collection::::get(0).unwrap().item_configs, 3); + + assert_eq!(Balances::reserved_balance(&account(1)), 8); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); + assert_eq!(AccountBalance::::get(0, &account(1)), 0); + assert_eq!(AccountBalance::::get(0, account(2)), 1); + assert_eq!(Balances::reserved_balance(&account(1)), 8); assert_eq!(Balances::reserved_balance(&account(2)), 0); - assert_ok!(Nfts::set_metadata( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 42, - bvec![42, 42] - )); - assert_eq!(Balances::reserved_balance(&owner), 11); - assert!(ItemMetadataOf::::contains_key(collection_id, 42)); - assert_ok!(Nfts::set_metadata( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 69, - bvec![69, 69] - )); - assert_eq!(Balances::reserved_balance(&owner), 14); - assert!(ItemMetadataOf::::contains_key(collection_id, 69)); - assert!(ItemConfigOf::::contains_key(collection_id, 69)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); + assert_eq!(Balances::reserved_balance(&account(1)), 11); + assert!(ItemMetadataOf::::contains_key(0, 42)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); + assert_eq!(Balances::reserved_balance(&account(1)), 14); + assert!(ItemMetadataOf::::contains_key(0, 69)); + assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); assert_eq!(w.item_metadatas, 2); assert_eq!(w.item_configs, 3); assert_noop!( - Nfts::destroy(RuntimeOrigin::signed(owner.clone()), collection_id, w), + Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w), Error::::CollectionNotEmpty ); - assert_eq!(AccountBalance::::get(collection_id, &owner), 0); assert_ok!(Nfts::set_attribute( - RuntimeOrigin::signed(owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, Some(69), AttributeNamespace::CollectionOwner, bvec![0], bvec![0], )); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), collection_id, 42)); - assert_eq!(AccountBalance::::get(collection_id, account(10)), 0); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), collection_id, 69)); - assert_eq!(AccountBalance::::get(collection_id, account(10)), 0); - assert_ok!(Nfts::burn(RuntimeOrigin::root(), collection_id, 70)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), 0, 42)); + assert_eq!(AccountBalance::::get(0, account(10)), 0); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), 0, 69)); + assert_eq!(AccountBalance::::get(0, account(20)), 0); + assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 70)); - let w = Nfts::get_destroy_witness(&collection_id).unwrap(); + let w = Nfts::get_destroy_witness(&0).unwrap(); assert_eq!(w.attributes, 1); assert_eq!(w.item_metadatas, 0); assert_eq!(w.item_configs, 0); - assert_ok!(Nfts::destroy(RuntimeOrigin::signed(owner.clone()), collection_id, w)); - assert_eq!(AccountBalance::::get(collection_id, &owner), 0); - assert_eq!(Balances::reserved_balance(&owner), 0); - - assert!(!Collection::::contains_key(collection_id)); - assert!(!CollectionConfigOf::::contains_key(collection_id)); - assert!(!Item::::contains_key(collection_id, 42)); - assert!(!Item::::contains_key(collection_id, 69)); - assert!(!CollectionMetadataOf::::contains_key(collection_id)); - assert!(!ItemMetadataOf::::contains_key(collection_id, 42)); - assert!(!ItemMetadataOf::::contains_key(collection_id, 69)); - assert!(!ItemConfigOf::::contains_key(collection_id, 69)); - assert_eq!(attributes(collection_id), vec![]); + assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); + assert_eq!(AccountBalance::::get(0, &account(1)), 0); + assert_eq!(Balances::reserved_balance(&account(1)), 0); + + assert!(!Collection::::contains_key(0)); + assert!(!CollectionConfigOf::::contains_key(0)); + assert!(!Item::::contains_key(0, 42)); + assert!(!Item::::contains_key(0, 69)); + assert!(!CollectionMetadataOf::::contains_key(0)); + assert!(!ItemMetadataOf::::contains_key(0, 42)); + assert!(!ItemMetadataOf::::contains_key(0, 69)); + assert!(!ItemConfigOf::::contains_key(0, 69)); + assert_eq!(attributes(0), vec![]); assert_eq!(collections(), vec![]); assert_eq!(items(), vec![]); }); @@ -432,70 +373,54 @@ fn destroy_with_bad_witness_should_not_work() { #[test] fn destroy_should_work() { new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&collection_owner, 100); - Balances::make_free_balance_be(&item_owner, 100); + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::create( - RuntimeOrigin::signed(collection_owner.clone()), - collection_owner.clone(), + RuntimeOrigin::signed(account(1)), + account(1), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(2), None)); assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + account(3), None )); assert_noop!( Nfts::destroy( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - Nfts::get_destroy_witness(&collection_id).unwrap() + RuntimeOrigin::signed(account(1)), + 0, + Nfts::get_destroy_witness(&0).unwrap() ), Error::::CollectionNotEmpty ); - assert_ok!(Nfts::lock_item_transfer( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id - )); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id)); - assert_eq!(Collection::::get(collection_id).unwrap().item_configs, 1); - assert_eq!(ItemConfigOf::::iter_prefix(collection_id).count() as u32, 1); - assert!(ItemConfigOf::::contains_key(collection_id, item_id)); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(account(1)), 0, 42)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(2)), 0, 42)); + assert_eq!(Collection::::get(0).unwrap().item_configs, 1); + assert_eq!(ItemConfigOf::::iter_prefix(0).count() as u32, 1); + assert!(ItemConfigOf::::contains_key(0, 42)); assert_noop!( Nfts::destroy( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - Nfts::get_destroy_witness(&collection_id).unwrap() + RuntimeOrigin::signed(account(1)), + 0, + Nfts::get_destroy_witness(&0).unwrap() ), Error::::CollectionApprovalsExist ); assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner), - collection_id, - delegate + RuntimeOrigin::signed(account(2)), + 0, + account(3) )); assert_ok!(Nfts::destroy( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - Nfts::get_destroy_witness(&collection_id).unwrap() + RuntimeOrigin::signed(account(1)), + 0, + Nfts::get_destroy_witness(&0).unwrap() )); - assert!(!ItemConfigOf::::contains_key(collection_id, item_id)); - assert_eq!(ItemConfigOf::::iter_prefix(collection_id).count() as u32, 0); + assert!(!ItemConfigOf::::contains_key(0, 42)); + assert_eq!(ItemConfigOf::::iter_prefix(0).count() as u32, 0); }); } @@ -1889,57 +1814,50 @@ fn force_update_collection_should_work() { #[test] fn burn_works() { new_test_ext().execute_with(|| { - let admin = account(2); - let collection_id = 0; - let collection_owner = account(1); - let item_id_1 = 42; - let item_id_2 = 69; - let item_owner = account(5); - - Balances::make_free_balance_be(&collection_owner, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), collection_config_with_all_settings_enabled() )); assert_ok!(Nfts::set_team( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - Some(admin.clone()), + RuntimeOrigin::signed(account(1)), + 0, + Some(account(2)), Some(account(3)), Some(account(4)), )); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id_1), + Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), Error::::UnknownItem ); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(admin.clone()), - collection_id, - item_id_1, - item_owner.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(5), default_item_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(admin), - collection_id, - item_id_2, - item_owner.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 69, + account(5), default_item_config() )); - assert_eq!(AccountBalance::::get(collection_id, &item_owner), 2); - assert_eq!(Balances::reserved_balance(collection_owner.clone()), 2); + assert_eq!(Balances::reserved_balance(account(1)), 2); assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(0)), collection_id, item_id_1), + Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission ); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id_1)); - assert_ok!(Nfts::burn(RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id_2)); - assert!(!AccountBalance::::contains_key(collection_id, &item_owner)); - assert_eq!(Balances::reserved_balance(collection_owner), 0); + + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); + assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); + assert!(!AccountBalance::::contains_key(0, &account(5))); + assert_eq!(Balances::reserved_balance(account(1)), 0); }); } @@ -2013,476 +1931,118 @@ fn approval_lifecycle_works() { } #[test] -fn check_approval_without_deadline_works() { +fn cancel_approval_works() { new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&item_owner, 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), default_collection_config() )); - // Item doesn't exist. + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), + default_item_config() + )); + + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), + None + )); assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::NoPermission + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 1, 42, account(3)), + Error::::UnknownItem ); assert_noop!( - Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 43, account(3)), Error::::UnknownItem ); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - // No approval. assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Nfts::cancel_approval(RuntimeOrigin::signed(account(3)), 0, 42, account(3)), Error::::NoPermission ); assert_noop!( - Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), - Error::::NoPermission + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), + Error::::NotDelegate ); - // Approve collection without deadline. - { - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - None - )); - assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); - assert_ok!(Nfts::check_approval( - &collection_id, - &Some(item_id), - &item_owner, - &delegate - )); - assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone() - )); - } - // Approve item without deadline. - { - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), - None - )); - assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::NoPermission - ); - assert_ok!(Nfts::check_approval( - &collection_id, - &Some(item_id), - &item_owner, - &delegate - )); - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - )); - } - // Approve collection and item without deadline. - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), - None - )); + + // Throws `DelegateApprovalConflict`` if the delegate has been granted a collection + // approval. assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - None - )); - assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); - assert_ok!(Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate)); - }); -} - -#[test] -fn check_approval_with_deadline_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&item_owner, 100); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - default_collection_config() - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - // Approve collection with deadline. - { - let deadline: BlockNumberFor = 10; - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - Some(deadline), - )); - assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); - assert_ok!(Nfts::check_approval( - &collection_id, - &Some(item_id), - &item_owner, - &delegate - )); - // Expire approval. - System::set_block_number(deadline + System::block_number() + 1); - assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::ApprovalExpired - ); - assert_noop!( - Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), - Error::::NoPermission - ); - assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - )); - } - // Approve item with deadline. - { - let deadline: BlockNumberFor = 20; - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), - Some(deadline), - )); - assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::NoPermission - ); - assert_ok!(Nfts::check_approval( - &collection_id, - &Some(item_id), - &item_owner, - &delegate - )); - // Expire approval. - System::set_block_number(deadline + System::block_number() + 1); - assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::NoPermission - ); - assert_noop!( - Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), - Error::::ApprovalExpired - ); - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), - )); - } - assert_noop!( - Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), - Error::::NoPermission - ); - assert_noop!( - Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), - Error::::NoPermission - ); - }); -} - -#[test] -fn cancel_approval_works() { - new_test_ext().execute_with(|| { - let collection_owner = account(1); - let collection_id = 0; - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&item_owner, 100); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - default_collection_config() - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + account(3), None )); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - 1, - item_id, - delegate.clone() - ), - Error::::UnknownItem - ); - assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - 43, - delegate.clone() - ), - Error::::UnknownItem - ); - assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(delegate.clone()), - collection_id, - item_id, - delegate.clone() - ), - Error::::NoPermission - ); - assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - account(4) - ), - Error::::NotDelegate - ); - - // Throws `DelegateApprovalConflict`` if the delegate has been granted a collection - // approval. - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - None - )); - assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3)), Error::::DelegateApprovalConflict ); assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone() + RuntimeOrigin::signed(account(2)), + 0, + account(3) )); - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - )); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); assert!(events().contains(&Event::::ApprovalCancelled { - collection: collection_id, - item: Some(item_id), - owner: item_owner.clone(), - delegate: delegate.clone() + collection: 0, + item: Some(42), + owner: account(2), + delegate: account(3) })); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3)), Error::::NotDelegate ); let current_block = 1; System::set_block_number(current_block); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, 69, - item_owner.clone(), + account(2), default_item_config() )); // approval expires after 2 blocks. assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), Some(2) )); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(account(5)), - collection_id, - item_id, - delegate.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(5)), 0, 42, account(3)), Error::::NoPermission ); System::set_block_number(current_block + 3); // 5 can cancel the approval since the deadline has passed. - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::signed(account(5)), - collection_id, - item_id, - delegate.clone() - )); - assert!(events().contains(&Event::::ApprovalCancelled { - collection: collection_id, - item: Some(item_id), - owner: item_owner, - delegate - })); - assert_eq!(approvals(collection_id, 69), vec![]); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(5)), 0, 42, account(3))); + assert_eq!(approvals(0, 69), vec![]); }); } #[test] -fn cancel_collection_approval_works() { +fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - // Origin checks for the `cancel_collection_approval`. - for origin in [root(), none()] { - assert_noop!( - Nfts::cancel_collection_approval(origin, collection_id, delegate.clone()), - BadOrigin - ); - } - // Origin checks for the `force_cancel_collection_approval`. - for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { - assert_noop!( - Nfts::force_cancel_collection_approval( - origin, - item_owner.clone(), - collection_id, - delegate.clone() - ), - BadOrigin - ); - } - - Balances::make_free_balance_be(&item_owner, 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), default_collection_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - - for (origin, maybe_owner) in [ - // Parameters for `cancel_collection_approval`. - (RuntimeOrigin::signed(item_owner.clone()), None), - // Parameters for `force_cancel_collection_approval`. - (root(), Some(&item_owner)), - ] { - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - None - )); - // Cancel an approval for a non existing collection. - assert_noop!( - cancel_collection_approval(origin.clone(), maybe_owner, 1, &delegate), - Error::::Unapproved - ); - // Cancel an unapproved delegate. - assert_noop!( - cancel_collection_approval( - origin.clone(), - maybe_owner, - collection_id, - &account(69), - ), - Error::::Unapproved - ); - - // Successfully cancel a collection approval. - assert_ok!(cancel_collection_approval( - origin.clone(), - maybe_owner, - collection_id, - &delegate - )); - assert_eq!(Balances::reserved_balance(&item_owner), 0); - assert!(!CollectionApprovals::contains_key((collection_id, &item_owner, &delegate))); - System::assert_last_event( - Event::::ApprovalCancelled { - collection: collection_id, - item: None, - owner: item_owner.clone(), - delegate: delegate.clone(), - } - .into(), - ); - } - }); -} - -#[test] -fn approving_multiple_accounts_works() { - new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - account(1), - default_collection_config() - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - 0, - 42, - account(2), + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), default_item_config() )); @@ -2585,155 +2145,6 @@ fn approvals_limit_works() { }); } -#[test] -fn approve_collection_transfer_works() { - new_test_ext().execute_with(|| { - let mut collection_id = 0; - let collection_owner = account(1); - let deadline: BlockNumberFor = 20; - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - // Origin checks for the `approve_collection_transfer`. - for origin in [root(), none()] { - assert_noop!( - Nfts::approve_collection_transfer(origin, collection_id, delegate.clone(), None), - BadOrigin - ); - } - // Origin checks for the `force_approve_collection_transfer`. - for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { - assert_noop!( - Nfts::force_approve_collection_transfer( - origin, - item_owner.clone(), - collection_id, - delegate.clone(), - None - ), - BadOrigin - ); - } - - // Provide balance to accounts. - Balances::make_free_balance_be(&item_owner, 100); - Balances::make_free_balance_be(&delegate, 100); - - for (origin, maybe_item_owner) in [ - // Parameters for `approve_collection_transfer`. - (RuntimeOrigin::signed(item_owner.clone()), None), - // Parameters for `force_approve_collection_transfer`. - (root(), Some(&item_owner)), - ] { - // Approve unknown collection, throws error `Error::NoItemOwned`. - assert_noop!( - approve_collection_transfer( - origin.clone(), - maybe_item_owner, - collection_id, - &delegate, - None - ), - Error::::NoItemOwned - ); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - default_collection_config() - )); - - // Approve collection without items, throws error `Error::NoItemOwned`. - assert_noop!( - Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), - None - ), - Error::::NoItemOwned - ); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - - // Approving a collection to a delegate with: - // 1. no deadline. - // 2. no deadline, again. - // 3. deadline. - // 3. equal deadline. - // 4. larger deadline. - // 5. smaller deadline. - // 6. no deadline, again. - // - // This tests all cases of approving the same delegate. - for deadline in [ - None, - None, - Some(deadline), - Some(deadline), - Some(deadline * 2), - Some(deadline), - None, - ] { - assert_ok!(approve_collection_transfer( - origin.clone(), - maybe_item_owner, - collection_id, - &delegate, - deadline, - )); - let deadline = deadline.map(|d| d + 1); - System::assert_last_event( - Event::::TransferApproved { - collection: collection_id, - item: None, - owner: item_owner.clone(), - delegate: delegate.clone(), - deadline, - } - .into(), - ); - assert_eq!(Balances::reserved_balance(&item_owner), 1); - assert_eq!( - CollectionApprovals::get((collection_id, &item_owner, &delegate)), - Some((deadline, CollectionApprovalDeposit::get())) - ); - } - - // Set collection settings to non transferable, throws error - // `Error::ItemsNonTransferable`. - assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - CollectionSettings::from_disabled(CollectionSetting::TransferableItems.into()) - )); - assert_noop!( - approve_collection_transfer( - origin.clone(), - maybe_item_owner, - collection_id, - &delegate, - None - ), - Error::::ItemsNonTransferable - ); - - // To reset reserved balance. - assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone() - )); - collection_id.saturating_inc(); - } - }); -} - #[test] fn approval_deadline_works() { new_test_ext().execute_with(|| { @@ -2789,104 +2200,68 @@ fn approval_deadline_works() { #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - let collection_id = 0; - let collection_owner = account(1); - - Balances::make_free_balance_be(&item_owner, 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), default_collection_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), default_item_config() )); assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), None )); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - 1, - item_id, - collection_owner.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 1, 42, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - 43, - collection_owner.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 43, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - account(4) - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), Error::::NotDelegate ); // Throws `DelegateApprovalConflict`` if the delegate has been granted a collection // approval. assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + account(3), None )); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3)), Error::::DelegateApprovalConflict ); assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone() + RuntimeOrigin::signed(account(2)), + 0, + account(3) )); - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - )); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); assert!(events().contains(&Event::::ApprovalCancelled { - collection: collection_id, - item: Some(item_id), - owner: item_owner.clone(), - delegate + collection: 0, + item: Some(42), + owner: account(2), + delegate: account(3) })); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner), - collection_id, - item_id, - account(5) - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(1)), Error::::NotDelegate ); }); @@ -2895,88 +2270,67 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - let collection_owner = account(1); - let collection_id = 0; - let delegate = account(3); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&item_owner, 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), default_collection_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), default_item_config() )); assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), None )); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), 1, item_id, collection_owner.clone()), + Nfts::cancel_approval(RuntimeOrigin::root(), 1, 42, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::root(), - collection_id, - 43, - collection_owner.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 43, account(1)), Error::::UnknownItem ); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), collection_id, item_id, account(4)), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(4)), Error::::NotDelegate ); // Throws `DelegateApprovalConflict`` if the delegate has been granted a collection // approval. assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone(), + RuntimeOrigin::signed(account(2)), + 0, + account(3), None )); assert_noop!( - Nfts::cancel_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone() - ), + Nfts::cancel_approval(RuntimeOrigin::signed(account(2)), 0, 42, account(3)), Error::::DelegateApprovalConflict ); assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - delegate.clone() + RuntimeOrigin::signed(account(2)), + 0, + account(3) )); - assert_ok!(Nfts::cancel_approval( - RuntimeOrigin::root(), - collection_id, - item_id, - delegate.clone() - )); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(3))); assert!(events().contains(&Event::::ApprovalCancelled { - collection: collection_id, - item: Some(item_id), - owner: item_owner, - delegate + collection: 0, + item: Some(42), + owner: account(2), + delegate: account(3) })); assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::root(), collection_id, item_id, collection_owner), + Nfts::cancel_approval(RuntimeOrigin::root(), 0, 42, account(1)), Error::::NotDelegate ); }); @@ -2985,326 +2339,138 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let delegate_1 = account(3); - let delegate_2 = account(4); - let item_id = 42; - let item_owner = account(2); - - Balances::make_free_balance_be(&collection_owner, 100); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - collection_owner.clone(), + account(1), default_collection_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, + RuntimeOrigin::signed(account(1)), + 0, 43, - collection_owner.clone(), + account(1), default_item_config() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), default_item_config() )); assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate_1.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(3), None )); assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate_2.clone(), + RuntimeOrigin::signed(account(2)), + 0, + 42, + account(4), None )); assert_noop!( - Nfts::clear_all_transfer_approvals( - RuntimeOrigin::signed(delegate_1.clone()), - collection_id, - item_id - ), + Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(3)), 0, 42), Error::::NoPermission ); + assert_ok!(Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(2)), 0, 42)); + // Throws `DelegateApprovalConflict` if there are existing collection approvals. assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - delegate_1.clone(), + RuntimeOrigin::signed(account(1)), + 0, + account(3), None )); assert_noop!( - Nfts::clear_all_transfer_approvals( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id - ), + Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(1)), 0, 42), Error::::DelegateApprovalConflict ); assert_ok!(Nfts::cancel_collection_approval( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - delegate_1.clone() + RuntimeOrigin::signed(account(1)), + 0, + account(3) )); - assert_ok!(Nfts::clear_all_transfer_approvals( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id - )); + assert_ok!(Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(2)), 0, 42)); assert!(events().contains(&Event::::AllApprovalsCancelled { - collection: collection_id, - item: item_id, - owner: item_owner.clone(), + collection: 0, + item: 42, + owner: account(2), })); - assert_eq!(approvals(collection_id, item_id), vec![]); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), - 0 - ); + assert_eq!(approvals(0, 42), vec![]); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(delegate_1), collection_id, item_id, account(5)), + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(5)), Error::::NoPermission ); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(delegate_2), collection_id, item_id, account(5)), + Nfts::transfer(RuntimeOrigin::signed(account(4)), 0, 42, account(5)), Error::::NoPermission ); }); } #[test] -fn clear_collection_approvals_works() { +fn max_supply_should_work() { new_test_ext().execute_with(|| { - let balance = 100; let collection_id = 0; - let item_id = 42; - let item_owner = account(1); - let delegates = 10..20; - - // Origin checks for the `clear_collection_approvals`. - for origin in [root(), none()] { - assert_noop!( - Nfts::clear_collection_approvals(origin, collection_id, 0), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } - // Origin checks for the `force_clear_collection_approvals`. - for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { - assert_noop!( - Nfts::force_clear_collection_approvals( - origin, - item_owner.clone(), - collection_id, - 0 - ), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } + let user_id = account(1); + let max_supply = 1; + // validate set_collection_max_supply assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - item_owner.clone(), + user_id.clone(), default_collection_config() )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(item_owner.clone()), + assert_eq!(CollectionConfigOf::::get(collection_id).unwrap().max_supply, None); + assert!(!events().contains(&Event::::CollectionMaxSupplySet { + collection: collection_id, + max_supply, + })); + + assert_ok!(Nfts::set_collection_max_supply( + RuntimeOrigin::signed(user_id.clone()), collection_id, - item_id, - item_owner.clone(), - default_item_config() + max_supply )); - Balances::make_free_balance_be(&item_owner, balance); - - for (origin, maybe_owner) in [ - // Parameters for `clear_collection_approvals`. - (root(), Some(&item_owner)), - // Parameters for `force_clear_collection_approvals`. - (RuntimeOrigin::signed(item_owner.clone()), None), - ] { - // Approve delegates. - let mut approvals = 0u32; - for i in delegates.clone() { - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - account(i), - None - )); - approvals.saturating_inc(); - } + assert_eq!( + CollectionConfigOf::::get(collection_id).unwrap().max_supply, + Some(max_supply) + ); - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), - approvals as usize - ); - assert!(!events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals - })); + assert!(events().contains(&Event::::CollectionMaxSupplySet { + collection: collection_id, + max_supply, + })); - // Partially remove collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), - Ok(Some(WeightOf::clear_collection_approvals(1)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), - approvals as usize - 1 - ); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 1 - })); - - // Successfully remove all collection approvals. Only charges post-dispatch weight for - // the removed approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(9)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) - .count() - .is_zero()); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: approvals - 1 - })); - - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 0 - })); - - // Ensure delegates are not able to transfer. - for i in delegates.clone() { - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(account(i)), - collection_id, - item_id, - account(5) - ), - Error::::NoPermission - ); - } - } - }); -} - -#[test] -fn collection_item_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let total_items = 10; - let user_id = account(1); - - // No collection. - assert_eq!(Nfts::collection_items(collection_id), None); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id.clone(), - default_collection_config() - )); - - // Mint items and validate the total supply. - (0..total_items).into_iter().for_each(|i| { - assert_ok!(Nfts::force_mint( - RuntimeOrigin::root(), - collection_id, - i, - user_id.clone(), - ItemConfig::default() - )); - }); - assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); - }); -} - -#[test] -fn max_supply_should_work() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let user_id = account(1); - let max_supply = 1; - - // validate set_collection_max_supply - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id.clone(), - default_collection_config() - )); - assert_eq!(CollectionConfigOf::::get(collection_id).unwrap().max_supply, None); - assert!(!events().contains(&Event::::CollectionMaxSupplySet { - collection: collection_id, - max_supply, - })); - - assert_ok!(Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id.clone()), - collection_id, - max_supply - )); - assert_eq!( - CollectionConfigOf::::get(collection_id).unwrap().max_supply, - Some(max_supply) - ); - - assert!(events().contains(&Event::::CollectionMaxSupplySet { - collection: collection_id, - max_supply, - })); - - assert_ok!(Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id.clone()), - collection_id, - max_supply + 1 - )); - assert_ok!(Nfts::lock_collection( - RuntimeOrigin::signed(user_id.clone()), - collection_id, - CollectionSettings::from_disabled(CollectionSetting::UnlockedMaxSupply.into()) - )); - assert_noop!( - Nfts::set_collection_max_supply( - RuntimeOrigin::signed(user_id.clone()), - collection_id, - max_supply + 2 - ), - Error::::MaxSupplyLocked - ); + assert_ok!(Nfts::set_collection_max_supply( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + max_supply + 1 + )); + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + CollectionSettings::from_disabled(CollectionSetting::UnlockedMaxSupply.into()) + )); + assert_noop!( + Nfts::set_collection_max_supply( + RuntimeOrigin::signed(user_id.clone()), + collection_id, + max_supply + 2 + ), + Error::::MaxSupplyLocked + ); // validate we can't mint more to max supply assert_ok!(Nfts::mint( @@ -4945,6 +4111,623 @@ fn clear_collection_metadata_works() { }); } +#[test] +fn collection_item_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let total_items = 10; + let user_id = account(1); + + // No collection. + assert_eq!(Nfts::collection_items(collection_id), None); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); + + // Mint items and validate the total supply. + (0..total_items).into_iter().for_each(|i| { + assert_ok!(Nfts::force_mint( + RuntimeOrigin::root(), + collection_id, + i, + user_id.clone(), + ItemConfig::default() + )); + }); + assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); + }); +} + +#[test] +fn clear_collection_approvals_works() { + new_test_ext().execute_with(|| { + let balance = 100; + let collection_id = 0; + let item_id = 42; + let item_owner = account(1); + let delegates = 10..20; + + // Origin checks for the `clear_collection_approvals`. + for origin in [root(), none()] { + assert_noop!( + Nfts::clear_collection_approvals(origin, collection_id, 0), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + // Origin checks for the `force_clear_collection_approvals`. + for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { + assert_noop!( + Nfts::force_clear_collection_approvals( + origin, + item_owner.clone(), + collection_id, + 0 + ), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + item_owner.clone(), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + Balances::make_free_balance_be(&item_owner, balance); + + for (origin, maybe_owner) in [ + // Parameters for `clear_collection_approvals`. + (root(), Some(&item_owner)), + // Parameters for `force_clear_collection_approvals`. + (RuntimeOrigin::signed(item_owner.clone()), None), + ] { + // Approve delegates. + let mut approvals = 0u32; + for i in delegates.clone() { + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + account(i), + None + )); + approvals.saturating_inc(); + } + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), + approvals as usize + ); + assert!(!events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals + })); + + // Partially remove collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), + Ok(Some(WeightOf::clear_collection_approvals(1)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), + approvals as usize - 1 + ); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 1 + })); + + // Successfully remove all collection approvals. Only charges post-dispatch weight for + // the removed approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(9)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) + .count() + .is_zero()); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: approvals - 1 + })); + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 0 + })); + + // Ensure delegates are not able to transfer. + for i in delegates.clone() { + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(account(i)), + collection_id, + item_id, + account(5) + ), + Error::::NoPermission + ); + } + } + }); +} + +#[test] +fn approve_collection_transfer_works() { + new_test_ext().execute_with(|| { + let mut collection_id = 0; + let collection_owner = account(1); + let deadline: BlockNumberFor = 20; + let delegate = account(3); + let item_id = 42; + let item_owner = account(2); + + // Origin checks for the `approve_collection_transfer`. + for origin in [root(), none()] { + assert_noop!( + Nfts::approve_collection_transfer(origin, collection_id, delegate.clone(), None), + BadOrigin + ); + } + // Origin checks for the `force_approve_collection_transfer`. + for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { + assert_noop!( + Nfts::force_approve_collection_transfer( + origin, + item_owner.clone(), + collection_id, + delegate.clone(), + None + ), + BadOrigin + ); + } + + // Provide balance to accounts. + Balances::make_free_balance_be(&item_owner, 100); + Balances::make_free_balance_be(&delegate, 100); + + for (origin, maybe_item_owner) in [ + // Parameters for `approve_collection_transfer`. + (RuntimeOrigin::signed(item_owner.clone()), None), + // Parameters for `force_approve_collection_transfer`. + (root(), Some(&item_owner)), + ] { + // Approve unknown collection, throws error `Error::NoItemOwned`. + assert_noop!( + approve_collection_transfer( + origin.clone(), + maybe_item_owner, + collection_id, + &delegate, + None + ), + Error::::NoItemOwned + ); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + default_collection_config() + )); + + // Approve collection without items, throws error `Error::NoItemOwned`. + assert_noop!( + Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + None + ), + Error::::NoItemOwned + ); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + + // Approving a collection to a delegate with: + // 1. no deadline. + // 2. no deadline, again. + // 3. deadline. + // 3. equal deadline. + // 4. larger deadline. + // 5. smaller deadline. + // 6. no deadline, again. + // + // This tests all cases of approving the same delegate. + for deadline in [ + None, + None, + Some(deadline), + Some(deadline), + Some(deadline * 2), + Some(deadline), + None, + ] { + assert_ok!(approve_collection_transfer( + origin.clone(), + maybe_item_owner, + collection_id, + &delegate, + deadline, + )); + let deadline = deadline.map(|d| d + 1); + System::assert_last_event( + Event::::TransferApproved { + collection: collection_id, + item: None, + owner: item_owner.clone(), + delegate: delegate.clone(), + deadline, + } + .into(), + ); + assert_eq!(Balances::reserved_balance(&item_owner), 1); + assert_eq!( + CollectionApprovals::get((collection_id, &item_owner, &delegate)), + Some((deadline, CollectionApprovalDeposit::get())) + ); + } + + // Set collection settings to non transferable, throws error + // `Error::ItemsNonTransferable`. + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + CollectionSettings::from_disabled(CollectionSetting::TransferableItems.into()) + )); + assert_noop!( + approve_collection_transfer( + origin.clone(), + maybe_item_owner, + collection_id, + &delegate, + None + ), + Error::::ItemsNonTransferable + ); + + // To reset reserved balance. + assert_ok!(Nfts::cancel_collection_approval( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone() + )); + collection_id.saturating_inc(); + } + }); +} + +#[test] +fn cancel_collection_approval_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let collection_owner = account(1); + let delegate = account(3); + let item_id = 42; + let item_owner = account(2); + + // Origin checks for the `cancel_collection_approval`. + for origin in [root(), none()] { + assert_noop!( + Nfts::cancel_collection_approval(origin, collection_id, delegate.clone()), + BadOrigin + ); + } + // Origin checks for the `force_cancel_collection_approval`. + for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { + assert_noop!( + Nfts::force_cancel_collection_approval( + origin, + item_owner.clone(), + collection_id, + delegate.clone() + ), + BadOrigin + ); + } + + Balances::make_free_balance_be(&item_owner, 100); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(collection_owner), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + + for (origin, maybe_owner) in [ + // Parameters for `cancel_collection_approval`. + (RuntimeOrigin::signed(item_owner.clone()), None), + // Parameters for `force_cancel_collection_approval`. + (root(), Some(&item_owner)), + ] { + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + None + )); + // Cancel an approval for a non existing collection. + assert_noop!( + cancel_collection_approval(origin.clone(), maybe_owner, 1, &delegate), + Error::::Unapproved + ); + // Cancel an unapproved delegate. + assert_noop!( + cancel_collection_approval( + origin.clone(), + maybe_owner, + collection_id, + &account(69), + ), + Error::::Unapproved + ); + + // Successfully cancel a collection approval. + assert_ok!(cancel_collection_approval( + origin.clone(), + maybe_owner, + collection_id, + &delegate + )); + assert_eq!(Balances::reserved_balance(&item_owner), 0); + assert!(!CollectionApprovals::contains_key((collection_id, &item_owner, &delegate))); + System::assert_last_event( + Event::::ApprovalCancelled { + collection: collection_id, + item: None, + owner: item_owner.clone(), + delegate: delegate.clone(), + } + .into(), + ); + } + }); +} + +#[test] +fn check_approval_without_deadline_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let collection_owner = account(1); + let delegate = account(3); + let item_id = 42; + let item_owner = account(2); + + Balances::make_free_balance_be(&item_owner, 100); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + default_collection_config() + )); + // Item doesn't exist. + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_noop!( + Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Error::::UnknownItem + ); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + // No approval. + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_noop!( + Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Error::::NoPermission + ); + // Approve collection without deadline. + { + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + None + )); + assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); + assert_ok!(Nfts::check_approval( + &collection_id, + &Some(item_id), + &item_owner, + &delegate + )); + assert_ok!(Nfts::cancel_collection_approval( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone() + )); + } + // Approve item without deadline. + { + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + delegate.clone(), + None + )); + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_ok!(Nfts::check_approval( + &collection_id, + &Some(item_id), + &item_owner, + &delegate + )); + assert_ok!(Nfts::cancel_approval( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + delegate.clone() + )); + } + // Approve collection and item without deadline. + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + delegate.clone(), + None + )); + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + None + )); + assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); + assert_ok!(Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate)); + }); +} + +#[test] +fn check_approval_with_deadline_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let collection_owner = account(1); + let delegate = account(3); + let item_id = 42; + let item_owner = account(2); + + Balances::make_free_balance_be(&item_owner, 100); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + // Approve collection with deadline. + { + let deadline: BlockNumberFor = 10; + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + Some(deadline), + )); + assert_ok!(Nfts::check_approval(&collection_id, &None, &item_owner, &delegate)); + assert_ok!(Nfts::check_approval( + &collection_id, + &Some(item_id), + &item_owner, + &delegate + )); + // Expire approval. + System::set_block_number(deadline + System::block_number() + 1); + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::ApprovalExpired + ); + assert_noop!( + Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Error::::NoPermission + ); + assert_ok!(Nfts::cancel_collection_approval( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + delegate.clone(), + )); + } + // Approve item with deadline. + { + let deadline: BlockNumberFor = 20; + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + delegate.clone(), + Some(deadline), + )); + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_ok!(Nfts::check_approval( + &collection_id, + &Some(item_id), + &item_owner, + &delegate + )); + // Expire approval. + System::set_block_number(deadline + System::block_number() + 1); + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_noop!( + Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Error::::ApprovalExpired + ); + assert_ok!(Nfts::cancel_approval( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + delegate.clone(), + )); + } + assert_noop!( + Nfts::check_approval(&collection_id, &None, &item_owner, &delegate), + Error::::NoPermission + ); + assert_noop!( + Nfts::check_approval(&collection_id, &Some(item_id), &item_owner, &delegate), + Error::::NoPermission + ); + }); +} + #[test] fn ensure_collection_approval_bytes() { let key = Blake2_128Concat::max_len::<::CollectionId>() + From 31368ca08fa741b2af20242965d8462642bac93e Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:47:09 +0700 Subject: [PATCH 03/40] refactor: account balance deposit & tests --- pallets/nfts/src/common_functions.rs | 49 +- pallets/nfts/src/features/atomic_swap.rs | 11 +- pallets/nfts/src/features/buy_sell.rs | 2 +- .../nfts/src/features/create_delete_item.rs | 49 +- pallets/nfts/src/features/transfer.rs | 67 +- pallets/nfts/src/impl_nonfungibles.rs | 4 +- pallets/nfts/src/lib.rs | 2 +- pallets/nfts/src/tests.rs | 594 ++++++++++++------ runtime/devnet/src/config/assets.rs | 3 + runtime/testnet/src/config/assets.rs | 3 + 10 files changed, 492 insertions(+), 292 deletions(-) diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 38f6cfc8..1156c1d8 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -19,7 +19,7 @@ use alloc::vec::Vec; -use frame_support::pallet_prelude::*; +use frame_support::{pallet_prelude::*, sp_runtime::ArithmeticError}; use crate::*; @@ -92,6 +92,53 @@ impl, I: 'static> Pallet { Self::deposit_event(Event::NextCollectionIdIncremented { next_id }); } + /// Increase the number of `collection` items owned by the `account`. If the `account` does not + /// have an existing balance, create a new `AccountBalance` record and reserve `deposit_amount` + /// from the `deposit_account`. + pub(crate) fn increment_account_balance( + collection: T::CollectionId, + account: &T::AccountId, + deposit_account: T::AccountId, + deposit_amount: DepositBalanceOf, + ) -> DispatchResult { + AccountBalance::::mutate(collection, &account, |maybe_balance| -> DispatchResult { + match maybe_balance { + None => { + T::Currency::reserve(&deposit_account, deposit_amount)?; + *maybe_balance = Some((1, (deposit_account, deposit_amount))); + }, + Some((balance, _deposit)) => { + balance.saturating_inc(); + }, + } + Ok(()) + }) + } + + /// Reduce the number of `collection` items owned by the `account`. If the `account` balance + /// reaches zero after the reduction, remove the `AccountBalance` record and unreserve the + /// associated deposited funds. + pub(crate) fn decrement_account_balance( + collection: T::CollectionId, + account: &T::AccountId, + ) -> DispatchResult { + AccountBalance::::try_mutate_exists( + collection, + &account, + |maybe_balance| -> DispatchResult { + let (balance, (depositor_account, deposit_amount)) = + maybe_balance.as_mut().ok_or(Error::::NoItemOwned)?; + + *balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; + if *balance == 0 { + T::Currency::unreserve(depositor_account, *deposit_amount); + *maybe_balance = None; + } + Ok(()) + }, + ) + } + #[allow(missing_docs)] #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn set_next_id(id: T::CollectionId) { diff --git a/pallets/nfts/src/features/atomic_swap.rs b/pallets/nfts/src/features/atomic_swap.rs index 75945d43..2bc7a794 100644 --- a/pallets/nfts/src/features/atomic_swap.rs +++ b/pallets/nfts/src/features/atomic_swap.rs @@ -209,10 +209,15 @@ impl, I: 'static> Pallet { } // This also removes the swap. - Self::do_transfer(send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| { - Ok(()) - })?; Self::do_transfer( + caller, + send_collection_id, + send_item_id, + receive_item.owner.clone(), + |_, _| Ok(()), + )?; + Self::do_transfer( + receive_item.owner.clone(), receive_collection_id, receive_item_id, send_item.owner.clone(), diff --git a/pallets/nfts/src/features/buy_sell.rs b/pallets/nfts/src/features/buy_sell.rs index 476053ee..ff8acbb2 100644 --- a/pallets/nfts/src/features/buy_sell.rs +++ b/pallets/nfts/src/features/buy_sell.rs @@ -158,7 +158,7 @@ impl, I: 'static> Pallet { let old_owner = details.owner.clone(); - Self::do_transfer(collection, item, buyer.clone(), |_, _| Ok(()))?; + Self::do_transfer(buyer.clone(), collection, item, buyer.clone(), |_, _| Ok(()))?; Self::deposit_event(Event::ItemBought { collection, diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index 824a2627..6eca0e6c 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -18,7 +18,7 @@ //! This module contains helper methods to perform functionality associated with minting and burning //! items for the NFTs pallet. -use frame_support::{pallet_prelude::*, sp_runtime::ArithmeticError, traits::ExistenceRequirement}; +use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use crate::*; @@ -74,34 +74,13 @@ impl, I: 'static> Pallet { true => T::ItemDeposit::get(), false => Zero::zero(), }; - let deposit_account = match maybe_depositor { - None => collection_details.owner.clone(), - Some(depositor) => depositor, - }; + let deposit_account = maybe_depositor.unwrap_or(collection_details.owner.clone()); - AccountBalance::::mutate( + Self::increment_account_balance( collection, &mint_to, - |maybe_balance| -> DispatchResult { - match maybe_balance { - None => { - // This is questionable - should collection config be able to override - // chain security? - let deposit_amount = match collection_config - .is_setting_enabled(CollectionSetting::DepositRequired) - { - true => T::BalanceDeposit::get(), - false => Zero::zero(), - }; - T::Currency::reserve(&deposit_account, deposit_amount)?; - *maybe_balance = Some((1, (deposit_account.clone(), deposit_amount))); - }, - Some((balance, _deposit)) => { - balance.saturating_inc(); - }, - } - Ok(()) - }, + deposit_account.clone(), + deposit_amount, )?; let item_owner = mint_to.clone(); @@ -285,23 +264,7 @@ impl, I: 'static> Pallet { PendingSwapOf::::remove(collection, item); ItemAttributesApprovalsOf::::remove(collection, item); - AccountBalance::::try_mutate_exists( - collection, - &owner, - |maybe_balance| -> DispatchResult { - match maybe_balance { - None => return Err(Error::::NoItemOwned.into()), - Some((mut balance, (depositor_account, deposit_amount))) => { - balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; - if balance == 0 { - T::Currency::unreserve(&depositor_account, *deposit_amount); - *maybe_balance = None; - } - Ok(()) - }, - } - }, - )?; + Self::decrement_account_balance(collection, &owner)?; if remove_config { ItemConfigOf::::remove(collection, item); diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 81463d5d..f7a9ba5c 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -19,13 +19,13 @@ //! of the NFTs pallet. use frame_support::pallet_prelude::*; -use sp_runtime::ArithmeticError; use crate::*; impl, I: 'static> Pallet { /// Transfer an NFT to the specified destination account. /// + /// - `caller`: The account calling the method to transfer the collection item. /// - `collection`: The ID of the collection to which the NFT belongs. /// - `item`: The ID of the NFT to transfer. /// - `dest`: The destination account to which the NFT will be transferred. @@ -46,6 +46,7 @@ impl, I: 'static> Pallet { /// - If the collection or item is non-transferable /// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)). pub fn do_transfer( + caller: T::AccountId, collection: T::CollectionId, item: T::ItemId, dest: T::AccountId, @@ -88,57 +89,21 @@ impl, I: 'static> Pallet { with_details(&collection_details, &mut details)?; // Update account balance of the owner. - AccountBalance::::try_mutate_exists( - collection, - &details.owner, - |maybe_balance| -> DispatchResult { - // NOTE: same code as do_burn implementation - should be refactored into a - // reusable function for both burn and the source part of the transfer - match maybe_balance { - None => return Err(Error::::NoItemOwned.into()), - Some((mut balance, (depositor_account, deposit_amount))) => { - balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; - if balance == 0 { - T::Currency::unreserve(&depositor_account, *deposit_amount); - *maybe_balance = None; - } - Ok(()) - }, - } - }, - )?; + Self::decrement_account_balance(collection, &details.owner)?; + // Update account balance of the destination account. - AccountBalance::::mutate(collection, &dest, |maybe_balance| -> DispatchResult { - // NOTE: very similar code to do_mint implementation - should be refactored into a - // reusable function for both minting and the dest part of the transfer - match maybe_balance { - None => { - let deposit_account = match frame_system::Pallet::::account_exists(&dest) { - // NOTE: no balance check, would fail in reservation below if dest has - // insufficient funds. As it is just a transfer and not a mint, could caller - // just resubmit once dest balance rectified? - true => dest.clone(), - // TODO: replace with signed origin account, which requires a parameter to - // be added to the function - false => collection_details.owner, - }; - // This is questionable - should collection config be able to override - // chain security? - let deposit_amount = match collection_config - .is_setting_enabled(CollectionSetting::DepositRequired) - { - true => T::BalanceDeposit::get(), - false => Zero::zero(), - }; - T::Currency::reserve(&deposit_account, deposit_amount)?; - *maybe_balance = Some((1, (deposit_account, deposit_amount))); - }, - Some((balance, _deposit)) => { - balance.saturating_inc(); - }, - } - Ok(()) - })?; + let deposit_amount = + match collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { + true => T::BalanceDeposit::get(), + false => Zero::zero(), + }; + // If the destination account exists, it reserves the `BalanceDeposit` for the new + // `AccountBalance`. Otherwise, the caller is responsible. + let deposit_account = match frame_system::Pallet::::account_exists(&dest) { + true => dest.clone(), + false => caller, + }; + Self::increment_account_balance(collection, &dest, deposit_account, deposit_amount)?; // Update account ownership information. Account::::remove((&details.owner, &collection, &item)); diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index b014e3ed..5e9a5206 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -411,7 +411,9 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { - Self::do_transfer(*collection, *item, destination.clone(), |_, _| Ok(())) + Self::do_transfer(destination.clone(), *collection, *item, destination.clone(), |_, _| { + Ok(()) + }) } fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 2ff8a803..ad2268b7 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -1088,7 +1088,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - Self::do_transfer(collection, item, dest, |_, details| { + Self::do_transfer(origin.clone(), collection, item, dest, |_, details| { if details.owner != origin { Self::check_approval(&collection, &Some(item), &details.owner, &origin)?; } diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index f1c36c69..63992dc0 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -231,7 +231,8 @@ fn basic_minting_should_work() { )); assert_eq!(collections(), vec![(account(1), 0)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(AccountBalance::::get(0, &account(1)), 1); + // Minting skips deposit as `DepositRequired` is disabled. + assert_eq!(AccountBalance::::get(0, &account(1)), Some((1, (account(1), 0)))); assert_eq!(items(), vec![(account(1), 0, 42)]); assert_ok!(Nfts::force_create( @@ -241,7 +242,8 @@ fn basic_minting_should_work() { )); assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); - assert_eq!(AccountBalance::::get(1, &account(1)), 1); + // Minting skips deposit as `DepositRequired` is disabled. + assert_eq!(AccountBalance::::get(1, &account(1)), Some((1, (account(2), 0)))); assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); } @@ -273,7 +275,7 @@ fn lifecycle_should_work() { account(10), default_item_config() )); - assert_eq!(Balances::reserved_balance(&account(1)), 6); + assert_eq!(Balances::reserved_balance(&account(1)), 6 + 1); // item deposit, 1 - balance deposit assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), 0, @@ -281,27 +283,27 @@ fn lifecycle_should_work() { account(20), default_item_config() )); - assert_eq!(AccountBalance::::get(0, account(20)), 1); - assert_eq!(Balances::reserved_balance(&account(1)), 7); + assert_eq!(AccountBalance::::get(0, account(20)), Some((1, (account(1), 1)))); + assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2); // item deposit, 2 - balance deposit assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); - assert_eq!(AccountBalance::::get(0, &account(1)), 1); + assert_eq!(AccountBalance::::get(0, &account(1)), Some((1, (account(1), 1)))); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); assert_eq!(Collection::::get(0).unwrap().item_configs, 3); - assert_eq!(Balances::reserved_balance(&account(1)), 8); + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3); // item deposit, 3 - balance deposit assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert_eq!(AccountBalance::::get(0, &account(1)), 0); - assert_eq!(AccountBalance::::get(0, account(2)), 1); - assert_eq!(Balances::reserved_balance(&account(1)), 8); - assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert!(!AccountBalance::::contains_key(0, &account(1))); + assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(2), 1)))); + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2); // item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&account(1)), 11); + assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2); // item deposit, 2 - balance deposit assert!(ItemMetadataOf::::contains_key(0, 42)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&account(1)), 14); + assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2); // item deposit, 2 - balance deposit assert!(ItemMetadataOf::::contains_key(0, 69)); assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -321,9 +323,9 @@ fn lifecycle_should_work() { bvec![0], )); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), 0, 42)); - assert_eq!(AccountBalance::::get(0, account(10)), 0); + assert!(!AccountBalance::::contains_key(0, account(10))); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), 0, 69)); - assert_eq!(AccountBalance::::get(0, account(20)), 0); + assert!(!AccountBalance::::contains_key(0, account(20))); assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 70)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -331,7 +333,7 @@ fn lifecycle_should_work() { assert_eq!(w.item_metadatas, 0); assert_eq!(w.item_configs, 0); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); - assert_eq!(AccountBalance::::get(0, &account(1)), 0); + assert!(!AccountBalance::::contains_key(0, &account(1))); assert_eq!(Balances::reserved_balance(&account(1)), 0); assert!(!Collection::::contains_key(0)); @@ -432,8 +434,9 @@ fn mint_should_work() { account(1), default_collection_config() )); + // Minting skips deposit as `DepositRequired` is disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(AccountBalance::::get(0, account(1)), 1); + assert_eq!(AccountBalance::::get(0, account(1)), Some((1, (account(1), 0)))); assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); assert_eq!(collections(), vec![(account(1), 0)]); assert_eq!(items(), vec![(account(1), 0, 42)]); @@ -499,7 +502,7 @@ fn mint_should_work() { account(2), Some(MintWitness { mint_price: Some(1), ..Default::default() }) )); - assert_eq!(AccountBalance::::get(0, account(2)), 1); + assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(2), 0)))); assert_eq!(Balances::total_balance(&account(2)), 99); // validate types @@ -538,7 +541,7 @@ fn mint_should_work() { account(2), Some(MintWitness { owned_item: Some(43), ..Default::default() }) )); - assert_eq!(AccountBalance::::get(1, account(2)), 1); + assert_eq!(AccountBalance::::get(1, account(2)), Some((1, (account(2), 0)))); assert!(events().contains(&Event::::PalletAttributeSet { collection: 0, item: Some(43), @@ -576,8 +579,11 @@ fn transfer_should_work() { default_item_config() )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); - assert_eq!(AccountBalance::::get(0, account(2)), 0); - assert_eq!(AccountBalance::::get(0, account(3)), 1); + // Transferring skips deposit as `DepositRequired` is disabled. + assert!(!AccountBalance::::contains_key(0, account(2))); + assert_eq!(AccountBalance::::get(0, account(3)), Some((1, (account(2), 0)))); + assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert_eq!(Balances::reserved_balance(&account(3)), 0); assert_eq!(items(), vec![(account(3), 0, 42)]); assert_noop!( Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), @@ -592,8 +598,8 @@ fn transfer_should_work() { None )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4))); - assert_eq!(AccountBalance::::get(0, account(3)), 0); - assert_eq!(AccountBalance::::get(0, account(4)), 1); + assert!(!AccountBalance::::contains_key(0, account(3))); + assert_eq!(AccountBalance::::get(0, account(4)), Some((1, (account(2), 0)))); // validate we can't transfer non-transferable items let collection_id = 1; assert_ok!(Nfts::force_create( @@ -622,6 +628,7 @@ fn transfer_should_work() { #[test] fn locking_transfer_should_work() { new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), account(1), @@ -748,7 +755,7 @@ fn transfer_owner_should_work() { bvec![0u8; 20], )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!(Balances::reserved_balance(&account(1)), 2); // 1 - metadata deposit, 1 - balance deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(3)), Some(0))); assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(2)), 0, account(3))); @@ -761,7 +768,8 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); // reserved_balance of accounts 1 & 2 should be unchanged: assert_eq!(Balances::reserved_balance(&account(1)), 1); - assert_eq!(Balances::reserved_balance(&account(2)), 0); + // reserves account 2's balance for the transfer. + assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit // 2's acceptance from before is reset when it became an owner, so it cannot be transferred // without a fresh acceptance. @@ -953,7 +961,7 @@ fn set_item_metadata_should_work() { // Successfully add metadata and take deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); - assert_eq!(Balances::free_balance(&account(1)), 8); + assert_eq!(Balances::free_balance(&account(1)), 7); // free_balance - metadata deposit - item deposit - balance deposit assert!(ItemMetadataOf::::contains_key(0, 42)); // Force origin works, too. @@ -961,9 +969,9 @@ fn set_item_metadata_should_work() { // Update deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 15])); - assert_eq!(Balances::free_balance(&account(1)), 13); + assert_eq!(Balances::free_balance(&account(1)), 12); // free_balance - metadata deposit - item deposit - balance deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 25])); - assert_eq!(Balances::free_balance(&account(1)), 3); + assert_eq!(Balances::free_balance(&account(1)), 2); // free_balance - metadata deposit - item deposit - balance deposit // Cannot over-reserve assert_noop!( @@ -1048,7 +1056,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 10); + assert_eq!(Balances::reserved_balance(account(1)), 10 + 1); // 10 - attribute deposit, 1 - balance deposit assert_eq!(Collection::::get(0).unwrap().owner_deposit, 9); assert_ok!(Nfts::set_attribute( @@ -1067,7 +1075,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 19); + assert_eq!(Balances::reserved_balance(account(1)), 19 + 1); // 19 - attribute deposit, 1 - balance deposit assert_eq!(Collection::::get(0).unwrap().owner_deposit, 18); assert_ok!(Nfts::clear_attribute( @@ -1084,7 +1092,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 16); + assert_eq!(Balances::reserved_balance(account(1)), 16 + 1); // 16 - attribute deposit, 1 - balance deposit assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 0)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -1329,7 +1337,7 @@ fn set_item_owner_attributes_should_work() { assert_eq!(deposit.account, Some(account(3))); assert_eq!(deposit.amount, 13); assert_eq!(Balances::reserved_balance(account(2)), 3); - assert_eq!(Balances::reserved_balance(account(3)), 13); + assert_eq!(Balances::reserved_balance(account(3)), 13 + 1); // 13 - attribute deposit, 1 - balance deposit // validate attributes on item deletion assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0)); @@ -1578,7 +1586,7 @@ fn set_attribute_should_respect_lock() { (Some(1), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 11); + assert_eq!(Balances::reserved_balance(account(1)), 11 + 1); // 12 - attribute deposit, 1 - balance deposit assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![])); assert_ok!(Nfts::lock_collection( @@ -1719,7 +1727,7 @@ fn force_update_collection_should_work() { )); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0; 20])); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(account(1)), 65); + assert_eq!(Balances::reserved_balance(account(1)), 65 + 2); // 65 - metadata deposit, 2 - balance deposit // force item status to be free holding assert_ok!(Nfts::force_collection_config( @@ -1748,7 +1756,7 @@ fn force_update_collection_should_work() { Some(account(4)), )); assert_eq!(collections(), vec![(account(5), 0)]); - assert_eq!(Balances::reserved_balance(account(1)), 2); + assert_eq!(Balances::reserved_balance(account(1)), 2 + 2); // 2 - metadata deposit, 2 - balance deposit assert_eq!(Balances::reserved_balance(account(5)), 63); assert_ok!(Nfts::redeposit( @@ -1756,7 +1764,7 @@ fn force_update_collection_should_work() { 0, bvec![0, 42, 50, 69, 100] )); - assert_eq!(Balances::reserved_balance(account(1)), 0); + assert_eq!(Balances::reserved_balance(account(1)), 2); // 2 - balance deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(5)), 0, 42, bvec![0; 20])); assert_eq!(Balances::reserved_balance(account(5)), 42); @@ -1847,15 +1855,20 @@ fn burn_works() { account(5), default_item_config() )); - assert_eq!(Balances::reserved_balance(account(1)), 2); assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission ); + assert_eq!(Balances::reserved_balance(account(1)), 2 + 1); // 2 - item deposit, 1 - balance deposit assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); + // Unreserve the `item deposit` while retaining the reservation for `balance deposit`. + assert_eq!(Balances::reserved_balance(account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); + // Unreserve the balance deposit when the account balance reaches zero. Remove the + // `AccountBalance` record. + assert_eq!(Balances::reserved_balance(account(1)), 0); assert!(!AccountBalance::::contains_key(0, &account(5))); assert_eq!(Balances::reserved_balance(account(1)), 0); }); @@ -2755,7 +2768,10 @@ fn buy_item_should_work() { item_1, price_1 + 1, )); - assert_eq!(AccountBalance::::get(collection_id, &user_2), 1); + assert_eq!( + AccountBalance::::get(collection_id, &user_2), + Some((1, (user_2.clone(), 0))) + ); // validate the new owner & balances let item = Item::::get(collection_id, item_1).unwrap(); @@ -3213,8 +3229,9 @@ fn claim_swap_should_work() { item_1, Some(price_with_direction.clone()), )); - assert_eq!(AccountBalance::::get(collection_id, &user_1), 2); - assert_eq!(AccountBalance::::get(collection_id, &user_2), 3); + // collection `DepositRequired` setting is disabled. + assert_eq!(AccountBalance::::get(collection_id, &user_1), Some((2, (user_1.clone(), 0)))); + assert_eq!(AccountBalance::::get(collection_id, &user_2), Some((3, (user_1.clone(), 0)))); // validate the new owner let item = Item::::get(collection_id, item_1).unwrap(); @@ -3614,7 +3631,7 @@ fn pre_signed_mints_should_work() { assert_eq!(deposit.amount, 3); assert_eq!(Balances::free_balance(&user_0), 100 - 2 + 10); // 2 - collection deposit, 10 - mint price - assert_eq!(Balances::free_balance(&user_2), 100 - 1 - 3 - 6 - 10); // 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price + assert_eq!(Balances::free_balance(&user_2), 100 - 1 - 1 - 3 - 6 - 10); // 1 - balance deposit, 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price assert_noop!( Nfts::mint_pre_signed( @@ -3789,7 +3806,7 @@ fn pre_signed_attributes_should_work() { assert_eq!(deposit.account, Some(user_2.clone())); assert_eq!(deposit.amount, 3); - assert_eq!(Balances::free_balance(&user_1), 100 - 2 - 1); // 2 - collection deposit, 1 - item deposit + assert_eq!(Balances::free_balance(&user_1), 100 - 2 - 1 - 1); // 2 - collection deposit, 1 - item deposit, 1 - balance deposit assert_eq!(Balances::free_balance(&user_2), 100 - 6); // 6 - attributes // validate the deposit gets returned on attribute update from collection's owner @@ -4112,169 +4129,116 @@ fn clear_collection_metadata_works() { } #[test] -fn collection_item_works() { +fn mint_requires_deposit_should_works() { new_test_ext().execute_with(|| { - let collection_id = 0; - let total_items = 10; - let user_id = account(1); - - // No collection. - assert_eq!(Nfts::collection_items(collection_id), None); - + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - user_id.clone(), - default_collection_config() + account(1), + collection_config_with_all_settings_enabled() )); + // Minting reserves deposit from the collection owner. + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); + assert_eq!(AccountBalance::::get(0, account(1)), Some((1, (account(1), 1)))); + assert_eq!(Balances::reserved_balance(&account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit + assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); - // Mint items and validate the total supply. - (0..total_items).into_iter().for_each(|i| { - assert_ok!(Nfts::force_mint( - RuntimeOrigin::root(), - collection_id, - i, - user_id.clone(), - ItemConfig::default() - )); - }); - assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); + // Minting for accounts with a non-zero balance requires no additional reserves. + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 43, account(1), None)); + assert_eq!(AccountBalance::::get(0, account(1)), Some((2, (account(1), 1)))); + assert_eq!(Balances::reserved_balance(&account(1)), 2 + 1); // 2 - item deposit, 2 - balance deposit + assert_eq!(Nfts::owner(0, 43).unwrap(), account(1)); + + // Increase the reserved balance on minting for a new `AccountBalance` record. + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 44, account(2), None)); + assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(1), 1)))); + assert_eq!(Balances::reserved_balance(&account(1)), 3 + 2); // 3 - item deposit, 2 - balance deposit + assert_eq!(Nfts::owner(0, 44).unwrap(), account(2)); + + // Minting by an admin. + assert_ok!(Nfts::set_team( + RuntimeOrigin::signed(account(1)), + 0, + Some(account(2)), + Some(account(3)), + Some(account(4)), + )); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 45, account(5), None)); + assert_eq!(AccountBalance::::get(0, account(5)), Some((1, (account(2), 1)))); + assert_eq!(Balances::reserved_balance(&account(2)), 1 + 1); // 1 - item deposit, 1 - balance deposit + assert_eq!(Nfts::owner(0, 45).unwrap(), account(5)); + + assert_eq!(collections(), vec![(account(1), 0)]); + assert_eq!( + items(), + vec![ + (account(1), 0, 42), + (account(1), 0, 43), + (account(2), 0, 44), + (account(5), 0, 45) + ] + ); }); } #[test] -fn clear_collection_approvals_works() { +fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { - let balance = 100; - let collection_id = 0; - let item_id = 42; - let item_owner = account(1); - let delegates = 10..20; - - // Origin checks for the `clear_collection_approvals`. - for origin in [root(), none()] { - assert_noop!( - Nfts::clear_collection_approvals(origin, collection_id, 0), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } - // Origin checks for the `force_clear_collection_approvals`. - for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { - assert_noop!( - Nfts::force_clear_collection_approvals( - origin, - item_owner.clone(), - collection_id, - 0 - ), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } - + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - item_owner.clone(), - default_collection_config() + account(1), + collection_config_with_all_settings_enabled() )); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - item_owner.clone(), + RuntimeOrigin::signed(account(1)), + 0, + 42, + account(2), default_item_config() )); - Balances::make_free_balance_be(&item_owner, balance); - - for (origin, maybe_owner) in [ - // Parameters for `clear_collection_approvals`. - (root(), Some(&item_owner)), - // Parameters for `force_clear_collection_approvals`. - (RuntimeOrigin::signed(item_owner.clone()), None), - ] { - // Approve delegates. - let mut approvals = 0u32; - for i in delegates.clone() { - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - account(i), - None - )); - approvals.saturating_inc(); - } - - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), - approvals as usize - ); - assert!(!events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals - })); - - // Partially remove collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), - Ok(Some(WeightOf::clear_collection_approvals(1)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), - approvals as usize - 1 - ); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 1 - })); - - // Successfully remove all collection approvals. Only charges post-dispatch weight for - // the removed approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(9)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) - .count() - .is_zero()); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: approvals - 1 - })); - - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 0 - })); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + 0, + 43, + account(2), + default_item_config() + )); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); + assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(1), 1)))); + // Reserve funds from account 2 as account 3 does not exist. + assert_eq!(AccountBalance::::get(0, account(3)), Some((1, (account(2), 1)))); + assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(3)), 0); + assert_eq!(items(), vec![(account(2), 0, 43), (account(3), 0, 42)]); + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), + Error::::NoPermission + ); - // Ensure delegates are not able to transfer. - for i in delegates.clone() { - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(account(i)), - collection_id, - item_id, - account(5) - ), - Error::::NoPermission - ); - } - } + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(3)), + 0, + 42, + account(2), + None + )); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4))); + assert!(!AccountBalance::::contains_key(0, account(3))); + assert_eq!(AccountBalance::::get(0, account(4)), Some((1, (account(2), 1)))); + // Reserve funds from account 2 as account 4 does not exist. Unreserve deposited funds from + // account 2 for creating the `AccountBalance` record of account 3. + assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(4)), 0); + assert_eq!(items(), vec![(account(2), 0, 43), (account(4), 0, 42)]); + + // Transfer to account 5 which has non-zero balance and reserves funds from account 5. + Balances::make_free_balance_be(&account(5), 10); + assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 43, account(5))); + assert_eq!(AccountBalance::::get(0, account(5)), Some((1, (account(5), 1)))); + assert_eq!(items(), vec![(account(4), 0, 42), (account(5), 0, 43),]); }); } @@ -4520,6 +4484,143 @@ fn cancel_collection_approval_works() { }); } +#[test] +fn clear_collection_approvals_works() { + new_test_ext().execute_with(|| { + let balance = 100; + let collection_id = 0; + let item_id = 42; + let item_owner = account(1); + let delegates = 10..20; + + // Origin checks for the `clear_collection_approvals`. + for origin in [root(), none()] { + assert_noop!( + Nfts::clear_collection_approvals(origin, collection_id, 0), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + // Origin checks for the `force_clear_collection_approvals`. + for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { + assert_noop!( + Nfts::force_clear_collection_approvals( + origin, + item_owner.clone(), + collection_id, + 0 + ), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + item_owner.clone(), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + Balances::make_free_balance_be(&item_owner, balance); + + for (origin, maybe_owner) in [ + // Parameters for `clear_collection_approvals`. + (root(), Some(&item_owner)), + // Parameters for `force_clear_collection_approvals`. + (RuntimeOrigin::signed(item_owner.clone()), None), + ] { + // Approve delegates. + let mut approvals = 0u32; + for i in delegates.clone() { + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + account(i), + None + )); + approvals.saturating_inc(); + } + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), + approvals as usize + ); + assert!(!events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals + })); + + // Partially remove collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), + Ok(Some(WeightOf::clear_collection_approvals(1)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), + approvals as usize - 1 + ); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 1 + })); + + // Successfully remove all collection approvals. Only charges post-dispatch weight for + // the removed approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(9)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) + .count() + .is_zero()); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: approvals - 1 + })); + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 0 + })); + + // Ensure delegates are not able to transfer. + for i in delegates.clone() { + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(account(i)), + collection_id, + item_id, + account(5) + ), + Error::::NoPermission + ); + } + } + }); +} + #[test] fn check_approval_without_deadline_works() { new_test_ext().execute_with(|| { @@ -4728,11 +4829,122 @@ fn check_approval_with_deadline_works() { }); } +#[test] +fn collection_item_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let total_items = 10; + let user_id = account(1); + + // No collection. + assert_eq!(Nfts::collection_items(collection_id), None); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); + + // Mint items and validate the total supply. + (0..total_items).into_iter().for_each(|i| { + assert_ok!(Nfts::force_mint( + RuntimeOrigin::root(), + collection_id, + i, + user_id.clone(), + ItemConfig::default() + )); + }); + assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); + }); +} + +#[test] +fn increment_account_balance_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + + Balances::make_free_balance_be(&account(1), 100); + // Increment the balance of a new account. + assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!( + AccountBalance::::get(collection_id, &account(1)), + Some((1, (account(1), 1))) + ); + + // Increment the balance of a non-zero balance account. No additional reserves. + (1..10u64).into_iter().for_each(|i| { + assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!( + AccountBalance::::get(collection_id, &account(1)), + Some(((i + 1) as u32, (account(1), 1))) + ); + }); + }); +} + +#[test] +fn decrement_account_balance_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let balance = 3u32; + + Balances::make_free_balance_be(&account(1), 100); + // Decrement non-existing account balance. + assert_noop!( + Nfts::decrement_account_balance(collection_id, &account(1)), + Error::::NoItemOwned + ); + + (0..balance).into_iter().for_each(|_| { + assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + }); + + // Successfully decrement the account balance. + let balance_before_decrement = + AccountBalance::::get(collection_id, &account(1)).map(|a| a.0).unwrap(); + assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); + let balance_after_decrement = + AccountBalance::::get(collection_id, &account(1)).map(|a| a.0).unwrap(); + assert_eq!(balance_before_decrement - 1, balance_after_decrement); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!( + AccountBalance::::get(collection_id, &account(1)), + Some((balance - 1, (account(1), 1))) + ); + + assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); + assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!( + AccountBalance::::get(collection_id, &account(1)), + Some((balance - 2, (account(1), 1))) + ); + + // If account balance is zero, the `AccountBalance` record is removed and depositor's fund + // is unreserved. + assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); + assert_eq!(Balances::reserved_balance(&account(1)), 0); + assert!(!AccountBalance::::contains_key(collection_id, &account(1))); + }); +} + +#[test] +fn ensure_account_balance_bytes() { + let key = Blake2_128Concat::max_len::<::CollectionId>() + .saturating_add(Blake2_128Concat::max_len::()); + let value = u32::max_encoded_len() + .saturating_add(AccountId::max_encoded_len()) + .saturating_add(DepositBalanceOf::::max_encoded_len()); + assert_eq!(key + value, 112); +} + #[test] fn ensure_collection_approval_bytes() { - let key = Blake2_128Concat::max_len::<::CollectionId>() + - Blake2_128Concat::max_len::() + - Blake2_128Concat::max_len::(); + let key = Blake2_128Concat::max_len::<::CollectionId>() + .saturating_add(Blake2_128Concat::max_len::()) + .saturating_add(Blake2_128Concat::max_len::()); let value = Option::>::max_encoded_len() .saturating_add(BalanceOf::::max_encoded_len()); assert_eq!(key + value, 133); diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 0ff2a0cf..55ff2381 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -29,6 +29,8 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); + // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) + pub const NftsBalanceDeposit: Balance = deposit(1, 133); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); @@ -43,6 +45,7 @@ impl pallet_nfts::Config for Runtime { // TODO: source from primitives type ApprovalsLimit = ConstU32<20>; type AttributeDepositBase = NftsAttributeDepositBase; + type BalanceDeposit = NftsBalanceDeposit; type CollectionApprovalDeposit = NftsCollectionApprovalDeposit; type CollectionDeposit = NftsCollectionDeposit; // TODO: source from primitives diff --git a/runtime/testnet/src/config/assets.rs b/runtime/testnet/src/config/assets.rs index 0ff2a0cf..55ff2381 100644 --- a/runtime/testnet/src/config/assets.rs +++ b/runtime/testnet/src/config/assets.rs @@ -29,6 +29,8 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); + // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) + pub const NftsBalanceDeposit: Balance = deposit(1, 133); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); @@ -43,6 +45,7 @@ impl pallet_nfts::Config for Runtime { // TODO: source from primitives type ApprovalsLimit = ConstU32<20>; type AttributeDepositBase = NftsAttributeDepositBase; + type BalanceDeposit = NftsBalanceDeposit; type CollectionApprovalDeposit = NftsCollectionApprovalDeposit; type CollectionDeposit = NftsCollectionDeposit; // TODO: source from primitives From 3f9e07e3a95bae85554eff113ba2ca4e6499cb1b Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:35:07 +0700 Subject: [PATCH 04/40] refactor: account balance deposit & tests --- pallets/nfts/src/benchmarking.rs | 4 ++-- pallets/nfts/src/lib.rs | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index bfe961e8..83f285b1 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -301,7 +301,7 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) verify { assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); @@ -757,7 +757,7 @@ benchmarks_instance_pallet! { let duration = T::MaxDeadlineDuration::get(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); let origin = SystemOrigin::Signed(caller.clone()); frame_system::Pallet::::set_block_number(One::one()); Nfts::::transfer(origin.clone().into(), collection, item2, target_lookup)?; diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index ad2268b7..782ecd82 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -178,12 +178,6 @@ pub mod pallet { #[pallet::constant] type CollectionDeposit: Get>; - /// The basic amount of funds that must be reserved for a collection approval. - // Key: `sizeof((CollectionId, AccountId, AccountId))` bytes. - // Value: `sizeof((Option, Balance))` bytes. - #[pallet::constant] - type CollectionApprovalDeposit: Get>; - /// The basic amount of funds that must be reserved for an item. #[pallet::constant] type ItemDeposit: Get>; @@ -260,6 +254,15 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + /// The basic amount of funds that must be reserved for a collection approval. + // Key: `sizeof((CollectionId, AccountId, AccountId))` bytes. + // Value: `sizeof((Option, Balance))` bytes. + #[pallet::constant] + type CollectionApprovalDeposit: Get>; + + /// The basic amount of funds that must be reversed for an account balance. + // Key: `sizeof((CollectionId, AccountId))` bytes. + // Value: `sizeof((u32, Some(AccountId, Balance)))` bytes. #[pallet::constant] type BalanceDeposit: Get>; } From 5ecb661a99689a8fb42de42c8ac22f1dbac9b136 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:50:05 +0700 Subject: [PATCH 05/40] chore: benchmarking --- pallets/nfts/src/tests.rs | 2 +- pallets/nfts/src/weights.rs | 212 ++++++++++++++------------- runtime/devnet/src/config/assets.rs | 2 +- runtime/testnet/src/config/assets.rs | 2 +- 4 files changed, 115 insertions(+), 103 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 63992dc0..e5b4c27c 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -768,7 +768,7 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); // reserved_balance of accounts 1 & 2 should be unchanged: assert_eq!(Balances::reserved_balance(&account(1)), 1); - // reserves account 2's balance for the transfer. + // transfer the collection item reserves account 2's balance. assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit // 2's acceptance from before is reset when it became an owner, so it cannot be transferred diff --git a/pallets/nfts/src/weights.rs b/pallets/nfts/src/weights.rs index 6e291bfc..292a9db0 100644 --- a/pallets/nfts/src/weights.rs +++ b/pallets/nfts/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for `pallet_nfts` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 40.0.0 -//! DATE: 2024-12-12, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-12-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `R0GUE`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` @@ -166,7 +166,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) @@ -175,8 +175,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `382` // Estimated: `4326` - // Minimum execution time: 40_000_000 picoseconds. - Weight::from_parts(43_000_000, 4326) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(37_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -189,7 +189,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) @@ -198,8 +198,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `382` // Estimated: `4326` - // Minimum execution time: 38_000_000 picoseconds. - Weight::from_parts(40_000_000, 4326) + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(38_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -214,7 +214,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) @@ -225,10 +225,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `584` + // Measured: `634` // Estimated: `4326` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(47_000_000, 4326) + // Minimum execution time: 40_000_000 picoseconds. + Weight::from_parts(44_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -243,7 +243,9 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:2) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) @@ -252,12 +254,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `613` - // Estimated: `6084` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(45_000_000, 6084) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Measured: `765` + // Estimated: `6180` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(54_000_000, 6180) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) } /// Storage: `Nfts::Collection` (r:1 w:0) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) @@ -287,8 +289,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 3534) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(12_000_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -300,8 +302,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 3534) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -556,14 +558,14 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) fn approve_collection_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `450` + // Measured: `477` // Estimated: `3602` // Minimum execution time: 20_000_000 picoseconds. Weight::from_parts(21_000_000, 3602) @@ -571,17 +573,17 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) fn force_approve_collection_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `450` + // Measured: `477` // Estimated: `3602` // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(21_000_000, 3602) + Weight::from_parts(24_000_000, 3602) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -732,19 +734,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:2) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `725` - // Estimated: `6084` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(48_000_000, 6084) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Measured: `877` + // Estimated: `6180` + // Minimum execution time: 50_000_000 picoseconds. + Weight::from_parts(57_000_000, 6180) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { @@ -764,8 +768,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `421` // Estimated: `7662` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 7662) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -777,8 +781,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `440` // Estimated: `4326` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(14_000_000, 4326) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -795,19 +799,21 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::ItemConfigOf` (r:2 w:0) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:4) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:2) /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `916` + // Measured: `1118` // Estimated: `7662` - // Minimum execution time: 81_000_000 picoseconds. + // Minimum execution time: 77_000_000 picoseconds. Weight::from_parts(87_000_000, 7662) - .saturating_add(T::DbWeight::get().reads(11_u64)) - .saturating_add(T::DbWeight::get().writes(12_u64)) + .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: `Nfts::CollectionRoleOf` (r:2 w:0) /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) @@ -818,11 +824,11 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::Collection` (r:1 w:1) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) - /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Attribute` (r:10 w:10) /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) @@ -834,10 +840,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `485` // Estimated: `6078 + n * (2954 ±0)` - // Minimum execution time: 102_000_000 picoseconds. - Weight::from_parts(115_875_572, 6078) - // Standard Error: 214_394 - .saturating_add(Weight::from_parts(29_706_731, 0).saturating_mul(n.into())) + // Minimum execution time: 90_000_000 picoseconds. + Weight::from_parts(97_702_243, 6078) + // Standard Error: 110_243 + .saturating_add(Weight::from_parts(21_787_114, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) @@ -957,7 +963,7 @@ impl WeightInfo for () { /// Storage: `Nfts::CollectionRoleOf` (r:1 w:0) /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) @@ -966,8 +972,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `382` // Estimated: `4326` - // Minimum execution time: 40_000_000 picoseconds. - Weight::from_parts(43_000_000, 4326) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(37_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -980,7 +986,7 @@ impl WeightInfo for () { /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) @@ -989,8 +995,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `382` // Estimated: `4326` - // Minimum execution time: 38_000_000 picoseconds. - Weight::from_parts(40_000_000, 4326) + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(38_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1005,7 +1011,7 @@ impl WeightInfo for () { /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:1) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) @@ -1016,14 +1022,14 @@ impl WeightInfo for () { /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn burn() -> Weight { // Proof Size summary in bytes: - // Measured: `584` + // Measured: `634` // Estimated: `4326` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(47_000_000, 4326) + // Minimum execution time: 40_000_000 picoseconds. + Weight::from_parts(44_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } - /// Storage: `Nfts::Collection` (r:1 w:0) +/// Storage: `Nfts::Collection` (r:1 w:0) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::Attribute` (r:1 w:0) /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) @@ -1034,7 +1040,9 @@ impl WeightInfo for () { /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:2) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:1) @@ -1043,12 +1051,12 @@ impl WeightInfo for () { /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `613` - // Estimated: `6084` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(45_000_000, 6084) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + // Measured: `765` + // Estimated: `6180` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(54_000_000, 6180) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: `Nfts::Collection` (r:1 w:0) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) @@ -1078,8 +1086,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 3534) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(12_000_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1091,8 +1099,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 3534) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1347,14 +1355,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) fn approve_collection_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `450` + // Measured: `477` // Estimated: `3602` // Minimum execution time: 20_000_000 picoseconds. Weight::from_parts(21_000_000, 3602) @@ -1362,17 +1370,17 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) fn force_approve_collection_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `450` + // Measured: `477` // Estimated: `3602` // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(21_000_000, 3602) + Weight::from_parts(24_000_000, 3602) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1523,19 +1531,21 @@ impl WeightInfo for () { /// Storage: `Nfts::ItemConfigOf` (r:1 w:0) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:2) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::PendingSwapOf` (r:0 w:1) /// Proof: `Nfts::PendingSwapOf` (`max_values`: None, `max_size`: Some(71), added: 2546, mode: `MaxEncodedLen`) fn buy_item() -> Weight { // Proof Size summary in bytes: - // Measured: `725` - // Estimated: `6084` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(48_000_000, 6084) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + // Measured: `877` + // Estimated: `6180` + // Minimum execution time: 50_000_000 picoseconds. + Weight::from_parts(57_000_000, 6180) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { @@ -1555,8 +1565,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `421` // Estimated: `7662` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(15_000_000, 7662) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1568,8 +1578,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `440` // Estimated: `4326` - // Minimum execution time: 14_000_000 picoseconds. - Weight::from_parts(14_000_000, 4326) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1586,19 +1596,21 @@ impl WeightInfo for () { /// Storage: `Nfts::ItemConfigOf` (r:2 w:0) /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:2 w:2) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Nfts::Account` (r:0 w:4) /// Proof: `Nfts::Account` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemPriceOf` (r:0 w:2) /// Proof: `Nfts::ItemPriceOf` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`) fn claim_swap() -> Weight { // Proof Size summary in bytes: - // Measured: `916` + // Measured: `1118` // Estimated: `7662` - // Minimum execution time: 81_000_000 picoseconds. + // Minimum execution time: 77_000_000 picoseconds. Weight::from_parts(87_000_000, 7662) - .saturating_add(RocksDbWeight::get().reads(11_u64)) - .saturating_add(RocksDbWeight::get().writes(12_u64)) + .saturating_add(RocksDbWeight::get().reads(12_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: `Nfts::CollectionRoleOf` (r:2 w:0) /// Proof: `Nfts::CollectionRoleOf` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) @@ -1609,11 +1621,11 @@ impl WeightInfo for () { /// Storage: `Nfts::Collection` (r:1 w:1) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::AccountBalance` (r:1 w:1) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) - /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Nfts::ItemConfigOf` (r:1 w:1) + /// Proof: `Nfts::ItemConfigOf` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) /// Storage: `Nfts::Attribute` (r:10 w:10) /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemMetadataOf` (r:1 w:1) @@ -1625,10 +1637,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `485` // Estimated: `6078 + n * (2954 ±0)` - // Minimum execution time: 102_000_000 picoseconds. - Weight::from_parts(115_875_572, 6078) - // Standard Error: 214_394 - .saturating_add(Weight::from_parts(29_706_731, 0).saturating_mul(n.into())) + // Minimum execution time: 90_000_000 picoseconds. + Weight::from_parts(97_702_243, 6078) + // Standard Error: 110_243 + .saturating_add(Weight::from_parts(21_787_114, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 55ff2381..ac93d14a 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -30,7 +30,7 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) - pub const NftsBalanceDeposit: Balance = deposit(1, 133); + pub const NftsBalanceDeposit: Balance = deposit(1, 112); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); diff --git a/runtime/testnet/src/config/assets.rs b/runtime/testnet/src/config/assets.rs index 55ff2381..ac93d14a 100644 --- a/runtime/testnet/src/config/assets.rs +++ b/runtime/testnet/src/config/assets.rs @@ -30,7 +30,7 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) - pub const NftsBalanceDeposit: Balance = deposit(1, 133); + pub const NftsBalanceDeposit: Balance = deposit(1, 112); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); From ee364fbdcf992a434328eddf78c109451b1d6cb8 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:19:07 +0700 Subject: [PATCH 06/40] fix: clippy warning --- pallets/nfts/src/common_functions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 1156c1d8..d96e527a 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -101,7 +101,7 @@ impl, I: 'static> Pallet { deposit_account: T::AccountId, deposit_amount: DepositBalanceOf, ) -> DispatchResult { - AccountBalance::::mutate(collection, &account, |maybe_balance| -> DispatchResult { + AccountBalance::::mutate(collection, account, |maybe_balance| -> DispatchResult { match maybe_balance { None => { T::Currency::reserve(&deposit_account, deposit_amount)?; @@ -124,7 +124,7 @@ impl, I: 'static> Pallet { ) -> DispatchResult { AccountBalance::::try_mutate_exists( collection, - &account, + account, |maybe_balance| -> DispatchResult { let (balance, (depositor_account, deposit_amount)) = maybe_balance.as_mut().ok_or(Error::::NoItemOwned)?; From 5015e90831d896e85620225f8ae6639319c4aeff Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:42:27 +0700 Subject: [PATCH 07/40] chore: comments --- pallets/nfts/src/tests.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index e5b4c27c..b2405a2e 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -275,7 +275,7 @@ fn lifecycle_should_work() { account(10), default_item_config() )); - assert_eq!(Balances::reserved_balance(&account(1)), 6 + 1); // item deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 6 + 1); // 1 - balance deposit assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), 0, @@ -284,7 +284,7 @@ fn lifecycle_should_work() { default_item_config() )); assert_eq!(AccountBalance::::get(0, account(20)), Some((1, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2); // item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2); // 2 - balance deposit assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); assert_eq!(AccountBalance::::get(0, &account(1)), Some((1, (account(1), 1)))); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); @@ -292,18 +292,18 @@ fn lifecycle_should_work() { assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); assert_eq!(Collection::::get(0).unwrap().item_configs, 3); - assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3); // item deposit, 3 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3); // 3 - balance deposit assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); assert!(!AccountBalance::::contains_key(0, &account(1))); assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(2), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2); // item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2); // 2 - balance deposit assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2); // item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2); // 2 - balance deposit assert!(ItemMetadataOf::::contains_key(0, 42)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2); // item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2); // 2 - balance deposit assert!(ItemMetadataOf::::contains_key(0, 69)); assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -4144,10 +4144,10 @@ fn mint_requires_deposit_should_works() { assert_eq!(Balances::reserved_balance(&account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); - // Minting for accounts with a non-zero balance requires no additional reserves. + // Mint for accounts with a non-zero balance requires no additional reserves. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 43, account(1), None)); assert_eq!(AccountBalance::::get(0, account(1)), Some((2, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 2 + 1); // 2 - item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 2 + 1); // 2 - item deposit, 1 - balance deposit assert_eq!(Nfts::owner(0, 43).unwrap(), account(1)); // Increase the reserved balance on minting for a new `AccountBalance` record. @@ -4156,7 +4156,7 @@ fn mint_requires_deposit_should_works() { assert_eq!(Balances::reserved_balance(&account(1)), 3 + 2); // 3 - item deposit, 2 - balance deposit assert_eq!(Nfts::owner(0, 44).unwrap(), account(2)); - // Minting by an admin. + // Mint by an admin. assert_ok!(Nfts::set_team( RuntimeOrigin::signed(account(1)), 0, From 94f0c74d9f672e1095035c0a0c2978d0b12fcd49 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:47:59 +0700 Subject: [PATCH 08/40] chore: resolve comments (exclude weights) --- pallets/nfts/src/benchmarking.rs | 8 +++-- pallets/nfts/src/common_functions.rs | 32 ++++++++++--------- pallets/nfts/src/features/approvals.rs | 4 ++- pallets/nfts/src/features/atomic_swap.rs | 4 +-- pallets/nfts/src/features/buy_sell.rs | 2 +- .../nfts/src/features/create_delete_item.rs | 6 ++-- pallets/nfts/src/features/transfer.rs | 17 +++++----- pallets/nfts/src/impl_nonfungibles.rs | 4 +-- pallets/nfts/src/lib.rs | 4 +-- pallets/nfts/src/tests.rs | 16 +++++++--- pallets/nfts/src/types.rs | 3 ++ pallets/nfts/src/weights.rs | 2 +- 12 files changed, 59 insertions(+), 43 deletions(-) diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index 83f285b1..9a5e0c32 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -274,6 +274,7 @@ benchmarks_instance_pallet! { mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::BalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, None) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); @@ -282,6 +283,7 @@ benchmarks_instance_pallet! { force_mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::BalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, default_item_config()) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); @@ -301,7 +303,7 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); + T::Currency::make_free_balance_be(&target, T::BalanceDeposit::get() + T::BalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) verify { assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); @@ -667,7 +669,7 @@ benchmarks_instance_pallet! { let price = ItemPrice::::from(0u32); let origin = SystemOrigin::Signed(seller.clone()).into(); Nfts::::set_price(origin, collection, item, Some(price), Some(buyer_lookup))?; - T::Currency::make_free_balance_be(&buyer, DepositBalanceOf::::max_value()); + T::Currency::make_free_balance_be(&buyer, T::Currency::minimum_balance() + price + T::BalanceDeposit::get()); }: _(SystemOrigin::Signed(buyer.clone()), collection, item, price) verify { assert_last_event::(Event::ItemBought { @@ -757,7 +759,7 @@ benchmarks_instance_pallet! { let duration = T::MaxDeadlineDuration::get(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); + T::Currency::make_free_balance_be(&target, T::BalanceDeposit::get() + T::BalanceDeposit::get()); let origin = SystemOrigin::Signed(caller.clone()); frame_system::Pallet::::set_block_number(One::one()); Nfts::::transfer(origin.clone().into(), collection, item2, target_lookup)?; diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index d96e527a..88ed7012 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -92,20 +92,22 @@ impl, I: 'static> Pallet { Self::deposit_event(Event::NextCollectionIdIncremented { next_id }); } - /// Increase the number of `collection` items owned by the `account`. If the `account` does not - /// have an existing balance, create a new `AccountBalance` record and reserve `deposit_amount` - /// from the `deposit_account`. + /// Increment the number of items in the `collection` owned by the `owner`. If no entry exists + /// for the `owner` in `AccountBalance`, create a new record and reserve `deposit_amount` from + /// the `deposit_account`. pub(crate) fn increment_account_balance( collection: T::CollectionId, - account: &T::AccountId, - deposit_account: T::AccountId, - deposit_amount: DepositBalanceOf, + owner: &T::AccountId, + (deposit_account, deposit_amount): ( + &::AccountId, + DepositBalanceOf, + ), ) -> DispatchResult { - AccountBalance::::mutate(collection, account, |maybe_balance| -> DispatchResult { + AccountBalance::::mutate(collection, owner, |maybe_balance| -> DispatchResult { match maybe_balance { None => { T::Currency::reserve(&deposit_account, deposit_amount)?; - *maybe_balance = Some((1, (deposit_account, deposit_amount))); + *maybe_balance = Some((1, (deposit_account.clone(), deposit_amount))); }, Some((balance, _deposit)) => { balance.saturating_inc(); @@ -115,23 +117,23 @@ impl, I: 'static> Pallet { }) } - /// Reduce the number of `collection` items owned by the `account`. If the `account` balance - /// reaches zero after the reduction, remove the `AccountBalance` record and unreserve the - /// associated deposited funds. + /// Decrement the number of `collection` items owned by the `owner`. If the `owner`'s item + /// count reaches zero after the reduction, remove the `AccountBalance` record and unreserve + /// the deposited funds. pub(crate) fn decrement_account_balance( collection: T::CollectionId, - account: &T::AccountId, + owner: &T::AccountId, ) -> DispatchResult { AccountBalance::::try_mutate_exists( collection, - account, + owner, |maybe_balance| -> DispatchResult { - let (balance, (depositor_account, deposit_amount)) = + let (balance, (deposit_account, deposit_amount)) = maybe_balance.as_mut().ok_or(Error::::NoItemOwned)?; *balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; if *balance == 0 { - T::Currency::unreserve(depositor_account, *deposit_amount); + T::Currency::unreserve(deposit_account, *deposit_amount); *maybe_balance = None; } Ok(()) diff --git a/pallets/nfts/src/features/approvals.rs b/pallets/nfts/src/features/approvals.rs index 005f5eac..9021aac1 100644 --- a/pallets/nfts/src/features/approvals.rs +++ b/pallets/nfts/src/features/approvals.rs @@ -217,7 +217,9 @@ impl, I: 'static> Pallet { Error::::MethodDisabled ); ensure!( - AccountBalance::::contains_key(collection, &owner), + AccountBalance::::get(collection, &owner) + .filter(|(balance, _)| *balance > 0) + .is_some(), Error::::NoItemOwned ); diff --git a/pallets/nfts/src/features/atomic_swap.rs b/pallets/nfts/src/features/atomic_swap.rs index 2bc7a794..35e85752 100644 --- a/pallets/nfts/src/features/atomic_swap.rs +++ b/pallets/nfts/src/features/atomic_swap.rs @@ -210,14 +210,14 @@ impl, I: 'static> Pallet { // This also removes the swap. Self::do_transfer( - caller, + &caller, send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| Ok(()), )?; Self::do_transfer( - receive_item.owner.clone(), + &caller, receive_collection_id, receive_item_id, send_item.owner.clone(), diff --git a/pallets/nfts/src/features/buy_sell.rs b/pallets/nfts/src/features/buy_sell.rs index ff8acbb2..d28478cb 100644 --- a/pallets/nfts/src/features/buy_sell.rs +++ b/pallets/nfts/src/features/buy_sell.rs @@ -158,7 +158,7 @@ impl, I: 'static> Pallet { let old_owner = details.owner.clone(); - Self::do_transfer(buyer.clone(), collection, item, buyer.clone(), |_, _| Ok(()))?; + Self::do_transfer(&buyer, collection, item, buyer.clone(), |_, _| Ok(()))?; Self::deposit_event(Event::ItemBought { collection, diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index 6eca0e6c..9a2e0983 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -74,13 +74,13 @@ impl, I: 'static> Pallet { true => T::ItemDeposit::get(), false => Zero::zero(), }; - let deposit_account = maybe_depositor.unwrap_or(collection_details.owner.clone()); + let deposit_account = + maybe_depositor.unwrap_or_else(|| collection_details.owner.clone()); Self::increment_account_balance( collection, &mint_to, - deposit_account.clone(), - deposit_amount, + (&deposit_account, deposit_amount), )?; let item_owner = mint_to.clone(); diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index f7a9ba5c..296e65bd 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -25,7 +25,7 @@ use crate::*; impl, I: 'static> Pallet { /// Transfer an NFT to the specified destination account. /// - /// - `caller`: The account calling the method to transfer the collection item. + /// - `caller`: The account transferring the collection item. /// - `collection`: The ID of the collection to which the NFT belongs. /// - `item`: The ID of the NFT to transfer. /// - `dest`: The destination account to which the NFT will be transferred. @@ -46,7 +46,7 @@ impl, I: 'static> Pallet { /// - If the collection or item is non-transferable /// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)). pub fn do_transfer( - caller: T::AccountId, + caller: &T::AccountId, collection: T::CollectionId, item: T::ItemId, dest: T::AccountId, @@ -97,13 +97,14 @@ impl, I: 'static> Pallet { true => T::BalanceDeposit::get(), false => Zero::zero(), }; - // If the destination account exists, it reserves the `BalanceDeposit` for the new - // `AccountBalance`. Otherwise, the caller is responsible. - let deposit_account = match frame_system::Pallet::::account_exists(&dest) { - true => dest.clone(), - false => caller, + // The destination account covers the `BalanceDeposit` if it has sufficient balance. + // Otherwise, the caller is accountable for it. + let deposit_account = match T::Currency::free_balance(&dest) >= T::BalanceDeposit::get() { + true => &dest, + false => &caller, }; - Self::increment_account_balance(collection, &dest, deposit_account, deposit_amount)?; + + Self::increment_account_balance(collection, &dest, (deposit_account, deposit_amount))?; // Update account ownership information. Account::::remove((&details.owner, &collection, &item)); diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index 5e9a5206..5567e9e9 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -411,9 +411,7 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { - Self::do_transfer(destination.clone(), *collection, *item, destination.clone(), |_, _| { - Ok(()) - }) + Self::do_transfer(&destination, *collection, *item, destination.clone(), |_, _| Ok(())) } fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 782ecd82..13510100 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -441,7 +441,7 @@ pub mod pallet { T::CollectionId, Blake2_128Concat, T::AccountId, - (u32, (T::AccountId, DepositBalanceOf)), + (u32, AccountDepositOf), >; /// Permission for a delegate to transfer all owner's collection items. @@ -1091,7 +1091,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - Self::do_transfer(origin.clone(), collection, item, dest, |_, details| { + Self::do_transfer(&origin, collection, item, dest, |_, details| { if details.owner != origin { Self::check_approval(&collection, &Some(item), &details.owner, &origin)?; } diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index b2405a2e..613aeef8 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -4865,8 +4865,8 @@ fn increment_account_balance_works() { let collection_id = 0; Balances::make_free_balance_be(&account(1), 100); - // Increment the balance of a new account. - assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + // Increment the balance of owned collection items of a new account. + assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), (&account(1), 1))); assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!( AccountBalance::::get(collection_id, &account(1)), @@ -4875,7 +4875,11 @@ fn increment_account_balance_works() { // Increment the balance of a non-zero balance account. No additional reserves. (1..10u64).into_iter().for_each(|i| { - assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + assert_ok!(Nfts::increment_account_balance( + collection_id, + &account(1), + (&account(1), 1) + )); assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!( AccountBalance::::get(collection_id, &account(1)), @@ -4899,7 +4903,11 @@ fn decrement_account_balance_works() { ); (0..balance).into_iter().for_each(|_| { - assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), account(1), 1)); + assert_ok!(Nfts::increment_account_balance( + collection_id, + &account(1), + (&account(1), 1) + )); }); // Successfully decrement the account balance. diff --git a/pallets/nfts/src/types.rs b/pallets/nfts/src/types.rs index f08f1d09..135702b0 100644 --- a/pallets/nfts/src/types.rs +++ b/pallets/nfts/src/types.rs @@ -89,6 +89,9 @@ pub(super) type PreSignedAttributesOf = PreSignedAttributes< ::AccountId, BlockNumberFor, >; +/// A type alias for the depositor account and its associated deposited amount. +pub(super) type AccountDepositOf = + (::AccountId, DepositBalanceOf); /// Information about a collection. #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] diff --git a/pallets/nfts/src/weights.rs b/pallets/nfts/src/weights.rs index 292a9db0..5d985d13 100644 --- a/pallets/nfts/src/weights.rs +++ b/pallets/nfts/src/weights.rs @@ -1029,7 +1029,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } -/// Storage: `Nfts::Collection` (r:1 w:0) + /// Storage: `Nfts::Collection` (r:1 w:0) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::Attribute` (r:1 w:0) /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) From d3ae74053cdb03e4fd960068d8db5afd0aa979e7 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Sun, 22 Dec 2024 11:19:48 +0700 Subject: [PATCH 09/40] chore: resolve review comments --- pallets/nfts/src/common_functions.rs | 2 +- pallets/nfts/src/features/transfer.rs | 4 +- pallets/nfts/src/impl_nonfungibles.rs | 2 +- pallets/nfts/src/tests.rs | 396 ++++++++++++++++---------- 4 files changed, 249 insertions(+), 155 deletions(-) diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 88ed7012..55a70cd0 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -106,7 +106,7 @@ impl, I: 'static> Pallet { AccountBalance::::mutate(collection, owner, |maybe_balance| -> DispatchResult { match maybe_balance { None => { - T::Currency::reserve(&deposit_account, deposit_amount)?; + T::Currency::reserve(deposit_account, deposit_amount)?; *maybe_balance = Some((1, (deposit_account.clone(), deposit_amount))); }, Some((balance, _deposit)) => { diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 296e65bd..0549a617 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -99,9 +99,9 @@ impl, I: 'static> Pallet { }; // The destination account covers the `BalanceDeposit` if it has sufficient balance. // Otherwise, the caller is accountable for it. - let deposit_account = match T::Currency::free_balance(&dest) >= T::BalanceDeposit::get() { + let deposit_account = match T::Currency::can_reserve(&dest, T::BalanceDeposit::get()) { true => &dest, - false => &caller, + false => caller, }; Self::increment_account_balance(collection, &dest, (deposit_account, deposit_amount))?; diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index 5567e9e9..60e89a74 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -411,7 +411,7 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { - Self::do_transfer(&destination, *collection, *item, destination.clone(), |_, _| Ok(())) + Self::do_transfer(destination, *collection, *item, destination.clone(), |_, _| Ok(())) } fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 613aeef8..e73783c2 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -21,12 +21,10 @@ use enumflags2::BitFlags; use frame_support::{ assert_noop, assert_ok, dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo}, - pallet_prelude::MaxEncodedLen, traits::{ tokens::nonfungibles_v2::{Create, Destroy, Inspect, Mutate}, - Currency, Get, + Currency, Get, StorageInfoTrait, }, - Blake2_128Concat, StorageHasher, }; use frame_system::pallet_prelude::BlockNumberFor; use pallet_balances::Error as BalancesError; @@ -40,6 +38,7 @@ use sp_runtime::{ use crate::{mock::*, Event, SystemConfig, *}; type AccountIdOf = ::AccountId; +type AccountBalance = crate::AccountBalance; type CollectionApprovals = crate::CollectionApprovals; type CollectionApprovalDeposit = ::CollectionApprovalDeposit; type CollectionId = ::CollectionId; @@ -232,7 +231,7 @@ fn basic_minting_should_work() { assert_eq!(collections(), vec![(account(1), 0)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); // Minting skips deposit as `DepositRequired` is disabled. - assert_eq!(AccountBalance::::get(0, &account(1)), Some((1, (account(1), 0)))); + assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 0)))); assert_eq!(items(), vec![(account(1), 0, 42)]); assert_ok!(Nfts::force_create( @@ -243,7 +242,7 @@ fn basic_minting_should_work() { assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); // Minting skips deposit as `DepositRequired` is disabled. - assert_eq!(AccountBalance::::get(1, &account(1)), Some((1, (account(2), 0)))); + assert_eq!(AccountBalance::get(1, &account(1)), Some((1, (account(2), 0)))); assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); } @@ -283,10 +282,10 @@ fn lifecycle_should_work() { account(20), default_item_config() )); - assert_eq!(AccountBalance::::get(0, account(20)), Some((1, (account(1), 1)))); + assert_eq!(AccountBalance::get(0, account(20)), Some((1, (account(1), 1)))); assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2); // 2 - balance deposit assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); - assert_eq!(AccountBalance::::get(0, &account(1)), Some((1, (account(1), 1)))); + assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 1)))); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); @@ -294,8 +293,8 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3); // 3 - balance deposit assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert!(!AccountBalance::::contains_key(0, &account(1))); - assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(2), 1)))); + assert!(!AccountBalance::contains_key(0, &account(1))); + assert_eq!(AccountBalance::get(0, account(2)), Some((1, (account(2), 1)))); assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2); // 2 - balance deposit assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit @@ -323,9 +322,9 @@ fn lifecycle_should_work() { bvec![0], )); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), 0, 42)); - assert!(!AccountBalance::::contains_key(0, account(10))); + assert!(!AccountBalance::contains_key(0, account(10))); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), 0, 69)); - assert!(!AccountBalance::::contains_key(0, account(20))); + assert!(!AccountBalance::contains_key(0, account(20))); assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 70)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -333,7 +332,7 @@ fn lifecycle_should_work() { assert_eq!(w.item_metadatas, 0); assert_eq!(w.item_configs, 0); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); - assert!(!AccountBalance::::contains_key(0, &account(1))); + assert!(!AccountBalance::contains_key(0, &account(1))); assert_eq!(Balances::reserved_balance(&account(1)), 0); assert!(!Collection::::contains_key(0)); @@ -436,7 +435,7 @@ fn mint_should_work() { )); // Minting skips deposit as `DepositRequired` is disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(AccountBalance::::get(0, account(1)), Some((1, (account(1), 0)))); + assert_eq!(AccountBalance::get(0, account(1)), Some((1, (account(1), 0)))); assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); assert_eq!(collections(), vec![(account(1), 0)]); assert_eq!(items(), vec![(account(1), 0, 42)]); @@ -502,7 +501,7 @@ fn mint_should_work() { account(2), Some(MintWitness { mint_price: Some(1), ..Default::default() }) )); - assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(2), 0)))); + assert_eq!(AccountBalance::get(0, account(2)), Some((1, (account(2), 0)))); assert_eq!(Balances::total_balance(&account(2)), 99); // validate types @@ -541,7 +540,7 @@ fn mint_should_work() { account(2), Some(MintWitness { owned_item: Some(43), ..Default::default() }) )); - assert_eq!(AccountBalance::::get(1, account(2)), Some((1, (account(2), 0)))); + assert_eq!(AccountBalance::get(1, account(2)), Some((1, (account(2), 0)))); assert!(events().contains(&Event::::PalletAttributeSet { collection: 0, item: Some(43), @@ -580,8 +579,8 @@ fn transfer_should_work() { )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); // Transferring skips deposit as `DepositRequired` is disabled. - assert!(!AccountBalance::::contains_key(0, account(2))); - assert_eq!(AccountBalance::::get(0, account(3)), Some((1, (account(2), 0)))); + assert!(!AccountBalance::contains_key(0, account(2))); + assert_eq!(AccountBalance::get(0, account(3)), Some((1, (account(2), 0)))); assert_eq!(Balances::reserved_balance(&account(2)), 0); assert_eq!(Balances::reserved_balance(&account(3)), 0); assert_eq!(items(), vec![(account(3), 0, 42)]); @@ -598,8 +597,8 @@ fn transfer_should_work() { None )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4))); - assert!(!AccountBalance::::contains_key(0, account(3))); - assert_eq!(AccountBalance::::get(0, account(4)), Some((1, (account(2), 0)))); + assert!(!AccountBalance::contains_key(0, account(3))); + assert_eq!(AccountBalance::get(0, account(4)), Some((1, (account(2), 0)))); // validate we can't transfer non-transferable items let collection_id = 1; assert_ok!(Nfts::force_create( @@ -1863,13 +1862,14 @@ fn burn_works() { assert_eq!(Balances::reserved_balance(account(1)), 2 + 1); // 2 - item deposit, 1 - balance deposit assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); - // Unreserve the `item deposit` while retaining the reservation for `balance deposit`. + // Burn an item unreserving the item deposit while retaining the reservation for `balance + // deposit`. assert_eq!(Balances::reserved_balance(account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); - // Unreserve the balance deposit when the account balance reaches zero. Remove the - // `AccountBalance` record. + // Burn an item unreserving the balance deposit when the value of `AccountBalance` reaches + // zero. Remove the `AccountBalance` record. assert_eq!(Balances::reserved_balance(account(1)), 0); - assert!(!AccountBalance::::contains_key(0, &account(5))); + assert!(!AccountBalance::contains_key(0, &account(5))); assert_eq!(Balances::reserved_balance(account(1)), 0); }); } @@ -2768,10 +2768,7 @@ fn buy_item_should_work() { item_1, price_1 + 1, )); - assert_eq!( - AccountBalance::::get(collection_id, &user_2), - Some((1, (user_2.clone(), 0))) - ); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2.clone(), 0)))); // validate the new owner & balances let item = Item::::get(collection_id, item_1).unwrap(); @@ -3230,8 +3227,8 @@ fn claim_swap_should_work() { Some(price_with_direction.clone()), )); // collection `DepositRequired` setting is disabled. - assert_eq!(AccountBalance::::get(collection_id, &user_1), Some((2, (user_1.clone(), 0)))); - assert_eq!(AccountBalance::::get(collection_id, &user_2), Some((3, (user_1.clone(), 0)))); + assert_eq!(AccountBalance::get(collection_id, &user_1), Some((2, (user_1.clone(), 0)))); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((3, (user_1.clone(), 0)))); // validate the new owner let item = Item::::get(collection_id, item_1).unwrap(); @@ -3604,6 +3601,7 @@ fn pre_signed_mints_should_work() { signature.clone(), user_1.clone(), )); + assert_eq!(AccountBalance::get(0, user_2.clone()), Some((1, (user_2.clone(), 1)))); assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); let metadata = ItemMetadataOf::::get(0, 0).unwrap(); assert_eq!( @@ -4129,62 +4127,99 @@ fn clear_collection_metadata_works() { } #[test] -fn mint_requires_deposit_should_works() { +fn mint_requires_deposit_works() { new_test_ext().execute_with(|| { - Balances::make_free_balance_be(&account(1), 100); - Balances::make_free_balance_be(&account(2), 100); + let collection_id = 0; + let collection_owner = account(1); + let mut item_id = 42; + let mut item_owner = account(2); + assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - account(1), + collection_owner.clone(), collection_config_with_all_settings_enabled() )); + + // Throws error `BalancesError::InsufficientBalance`. + assert_noop!( + Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + ), + BalancesError::::InsufficientBalance + ); + + Balances::make_free_balance_be(&collection_owner, 100); // Minting reserves deposit from the collection owner. - assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(AccountBalance::::get(0, account(1)), Some((1, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit - assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); + { + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, item_owner.clone()), + Some((1, (collection_owner.clone(), 1))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 1 + 1); // 1 - item deposit, 1 - balance deposit + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } // Mint for accounts with a non-zero balance requires no additional reserves. - assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 43, account(1), None)); - assert_eq!(AccountBalance::::get(0, account(1)), Some((2, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 2 + 1); // 2 - item deposit, 1 - balance deposit - assert_eq!(Nfts::owner(0, 43).unwrap(), account(1)); + { + item_id = 43; + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((2, (collection_owner.clone(), 1))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 2 + 1); // 2 - item deposit, 1 - balance deposit + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } // Increase the reserved balance on minting for a new `AccountBalance` record. - assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 44, account(2), None)); - assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 3 + 2); // 3 - item deposit, 2 - balance deposit - assert_eq!(Nfts::owner(0, 44).unwrap(), account(2)); - - // Mint by an admin. - assert_ok!(Nfts::set_team( - RuntimeOrigin::signed(account(1)), - 0, - Some(account(2)), - Some(account(3)), - Some(account(4)), - )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 0, 45, account(5), None)); - assert_eq!(AccountBalance::::get(0, account(5)), Some((1, (account(2), 1)))); - assert_eq!(Balances::reserved_balance(&account(2)), 1 + 1); // 1 - item deposit, 1 - balance deposit - assert_eq!(Nfts::owner(0, 45).unwrap(), account(5)); + { + item_id = 44; + item_owner = account(3); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((1, (collection_owner.clone(), 1))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2); // 3 - item deposit, 2 - balance deposit + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } assert_eq!(collections(), vec![(account(1), 0)]); - assert_eq!( - items(), - vec![ - (account(1), 0, 42), - (account(1), 0, 43), - (account(2), 0, 44), - (account(5), 0, 45) - ] - ); + assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); }); } #[test] fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { + let collection_id = 0; + let mut dest = account(3); + let mut item_id = 42; + let owner = account(2); + Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::force_create( @@ -4192,53 +4227,105 @@ fn transfer_requires_deposit_works() { account(1), collection_config_with_all_settings_enabled() )); + + // Transfer an unknown collection item, throws error `Error::UnknownItem`. + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + ), + Error::::UnknownItem + ); + assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), - 0, - 42, - account(2), - default_item_config() - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - 0, - 43, - account(2), + collection_id, + item_id, + owner.clone(), default_item_config() )); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); - assert_eq!(AccountBalance::::get(0, account(2)), Some((1, (account(1), 1)))); - // Reserve funds from account 2 as account 3 does not exist. - assert_eq!(AccountBalance::::get(0, account(3)), Some((1, (account(2), 1)))); - assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit - assert_eq!(Balances::reserved_balance(&account(3)), 0); - assert_eq!(items(), vec![(account(2), 0, 43), (account(3), 0, 42)]); + + // Throws error `NoItemOwned` since `owner` does not have an `AccountBalance` record + // created. + let account_balance = AccountBalance::take(collection_id, &owner); assert_noop!( - Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4)), - Error::::NoPermission + Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + ), + Error::::NoItemOwned ); + AccountBalance::set(collection_id, &owner, account_balance); - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(account(3)), - 0, - 42, - account(2), - None - )); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(4))); - assert!(!AccountBalance::::contains_key(0, account(3))); - assert_eq!(AccountBalance::::get(0, account(4)), Some((1, (account(2), 1)))); - // Reserve funds from account 2 as account 4 does not exist. Unreserve deposited funds from - // account 2 for creating the `AccountBalance` record of account 3. - assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit - assert_eq!(Balances::reserved_balance(&account(4)), 0); - assert_eq!(items(), vec![(account(2), 0, 43), (account(4), 0, 42)]); - - // Transfer to account 5 which has non-zero balance and reserves funds from account 5. - Balances::make_free_balance_be(&account(5), 10); - assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 43, account(5))); - assert_eq!(AccountBalance::::get(0, account(5)), Some((1, (account(5), 1)))); - assert_eq!(items(), vec![(account(4), 0, 42), (account(5), 0, 43),]); + // Reserve funds from `owner` as `dest` does not exist. + { + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), 1)))); + assert_eq!(Balances::reserved_balance(&owner), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&dest), 0); + assert!(items().contains(&(dest, collection_id, item_id))); + } + + // Reserve funds from `owner` since `dest` lacks sufficient balance. + { + item_id = 43; + dest = account(4); + + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + collection_id, + item_id, + owner.clone(), + default_item_config() + )); + + Balances::make_free_balance_be(&dest, 1); // 1 - ED + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 1)))); + assert_eq!(Balances::reserved_balance(&owner), 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&dest), 0); + assert!(items().contains(&(dest, collection_id, item_id))); + } + + // Reserves funds from `dest` since `dest` exists and has sufficient balance. + { + item_id = 44; + dest = account(5); + + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + collection_id, + item_id, + owner.clone(), + default_item_config() + )); + Balances::make_free_balance_be(&dest, 2); // 1 - ED, 1 - balance deposit + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (dest.clone(), 1)))); + assert_eq!(Balances::reserved_balance(&owner), 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&dest), 1); // 1 - balance deposit + assert!(Balances::usable_balance(&dest).is_zero()); + assert!(items().contains(&(dest, collection_id, item_id))); + } }); } @@ -4863,27 +4950,35 @@ fn collection_item_works() { fn increment_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; + let deposit_account = account(1); + let owner = account(2); - Balances::make_free_balance_be(&account(1), 100); - // Increment the balance of owned collection items of a new account. - assert_ok!(Nfts::increment_account_balance(collection_id, &account(1), (&account(1), 1))); + // Throws error `BalancesError::InsufficientBalance`. + assert_noop!( + Nfts::increment_account_balance(collection_id, &owner, (&deposit_account, 1)), + BalancesError::::InsufficientBalance + ); + + Balances::make_free_balance_be(&deposit_account, 100); + // Initialize `AccountBalance` and increase the collection item count for the new account. + assert_ok!(Nfts::increment_account_balance(collection_id, &owner, (&deposit_account, 1))); assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!( - AccountBalance::::get(collection_id, &account(1)), - Some((1, (account(1), 1))) + AccountBalance::get(collection_id, &owner), + Some((1, (deposit_account.clone(), 1))) ); // Increment the balance of a non-zero balance account. No additional reserves. - (1..10u64).into_iter().for_each(|i| { + (1..10).into_iter().for_each(|i| { assert_ok!(Nfts::increment_account_balance( collection_id, - &account(1), - (&account(1), 1) + &owner, + (&deposit_account, 1) )); - assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!(Balances::reserved_balance(&deposit_account), 1); assert_eq!( - AccountBalance::::get(collection_id, &account(1)), - Some(((i + 1) as u32, (account(1), 1))) + AccountBalance::get(collection_id, &owner), + Some((i + 1, (deposit_account.clone(), 1))) ); }); }); @@ -4894,66 +4989,65 @@ fn decrement_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; let balance = 3u32; + let deposit_account = account(1); + let owner = account(2); - Balances::make_free_balance_be(&account(1), 100); - // Decrement non-existing account balance. + Balances::make_free_balance_be(&deposit_account, 100); + // Decrement non-existing `AccountBalance` record. assert_noop!( - Nfts::decrement_account_balance(collection_id, &account(1)), + Nfts::decrement_account_balance(collection_id, &deposit_account), Error::::NoItemOwned ); (0..balance).into_iter().for_each(|_| { assert_ok!(Nfts::increment_account_balance( collection_id, - &account(1), - (&account(1), 1) + &owner, + (&deposit_account, 1) )); }); - // Successfully decrement the account balance. + // Successfully decrement the value of the `AccountBalance` entry. let balance_before_decrement = - AccountBalance::::get(collection_id, &account(1)).map(|a| a.0).unwrap(); - assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); + AccountBalance::get(collection_id, &owner).map(|a| a.0).unwrap(); + assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); let balance_after_decrement = - AccountBalance::::get(collection_id, &account(1)).map(|a| a.0).unwrap(); + AccountBalance::get(collection_id, &owner).map(|a| a.0).unwrap(); assert_eq!(balance_before_decrement - 1, balance_after_decrement); - assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_eq!(Balances::reserved_balance(&deposit_account), 1); assert_eq!( - AccountBalance::::get(collection_id, &account(1)), - Some((balance - 1, (account(1), 1))) + AccountBalance::get(collection_id, &owner), + Some((balance - 1, (deposit_account.clone(), 1))) ); - assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); - assert_eq!(Balances::reserved_balance(&account(1)), 1); + // Decrement value of `AccountBalance` if it is non-zero. + assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); + assert_eq!(Balances::reserved_balance(&deposit_account), 1); assert_eq!( - AccountBalance::::get(collection_id, &account(1)), - Some((balance - 2, (account(1), 1))) + AccountBalance::get(collection_id, &owner), + Some((balance - 2, (deposit_account.clone(), 1))) ); - // If account balance is zero, the `AccountBalance` record is removed and depositor's fund - // is unreserved. - assert_ok!(Nfts::decrement_account_balance(collection_id, &account(1))); - assert_eq!(Balances::reserved_balance(&account(1)), 0); - assert!(!AccountBalance::::contains_key(collection_id, &account(1))); + // `AccountBalance` record is deleted, and the depositor's funds are unreserved if + // the `AccountBalance` value reaches zero after decrementing. + assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); + assert_eq!(Balances::reserved_balance(&deposit_account), 0); + assert!(!AccountBalance::contains_key(collection_id, &owner)); }); } #[test] fn ensure_account_balance_bytes() { - let key = Blake2_128Concat::max_len::<::CollectionId>() - .saturating_add(Blake2_128Concat::max_len::()); - let value = u32::max_encoded_len() - .saturating_add(AccountId::max_encoded_len()) - .saturating_add(DepositBalanceOf::::max_encoded_len()); - assert_eq!(key + value, 112); + assert!(AccountBalance::storage_info() + .first() + .filter(|i| i.max_size.unwrap_or_default() == 112) + .is_some()); } #[test] fn ensure_collection_approval_bytes() { - let key = Blake2_128Concat::max_len::<::CollectionId>() - .saturating_add(Blake2_128Concat::max_len::()) - .saturating_add(Blake2_128Concat::max_len::()); - let value = Option::>::max_encoded_len() - .saturating_add(BalanceOf::::max_encoded_len()); - assert_eq!(key + value, 133); + assert!(CollectionApprovals::storage_info() + .first() + .filter(|i| i.max_size.unwrap_or_default() == 133) + .is_some()); } From ed6d6a27ca6d655e162e09283ffdf1fd7a935132 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Sun, 22 Dec 2024 13:08:12 +0700 Subject: [PATCH 10/40] fix: deposit bytes --- pallets/nfts/src/tests.rs | 18 +-------------- runtime/devnet/src/config/assets.rs | 33 ++++++++++++++++++++++++---- runtime/testnet/src/config/assets.rs | 33 ++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index e73783c2..cbee8aa9 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -23,7 +23,7 @@ use frame_support::{ dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo}, traits::{ tokens::nonfungibles_v2::{Create, Destroy, Inspect, Mutate}, - Currency, Get, StorageInfoTrait, + Currency, Get, }, }; use frame_system::pallet_prelude::BlockNumberFor; @@ -5035,19 +5035,3 @@ fn decrement_account_balance_works() { assert!(!AccountBalance::contains_key(collection_id, &owner)); }); } - -#[test] -fn ensure_account_balance_bytes() { - assert!(AccountBalance::storage_info() - .first() - .filter(|i| i.max_size.unwrap_or_default() == 112) - .is_some()); -} - -#[test] -fn ensure_collection_approval_bytes() { - assert!(CollectionApprovals::storage_info() - .first() - .filter(|i| i.max_size.unwrap_or_default() == 133) - .is_some()); -} diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index ac93d14a..95604ef0 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -29,11 +29,11 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); - // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) - pub const NftsBalanceDeposit: Balance = deposit(1, 112); + // Key = 68 bytes (4+16+32+16), Value = 52 bytes (4+32+16) + pub const NftsBalanceDeposit: Balance = deposit(1, 120); pub const NftsCollectionDeposit: Balance = 10 * UNIT; - // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) - pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); + // Key = 116 bytes (4+16+32+16+32+16), Value = 21 bytes (1+4+16) + pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 137); pub const NftsItemDeposit: Balance = UNIT / 100; pub const NftsMetadataDepositBase: Balance = deposit(1, 129); pub const NftsAttributeDepositBase: Balance = deposit(1, 0); @@ -126,3 +126,28 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type WeightInfo = pallet_assets::weights::SubstrateWeight; } + +#[cfg(test)] +mod tests { + use frame_support::traits::StorageInfoTrait; + + use super::*; + + #[test] + fn ensure_account_balance_deposit() { + let max_size = pallet_nfts::AccountBalance::::storage_info() + .first() + .and_then(|info| info.max_size) + .unwrap_or_default(); + assert_eq!(deposit(1, max_size), NftsBalanceDeposit::get()); + } + + #[test] + fn ensure_collection_approval_deposit() { + let max_size = pallet_nfts::CollectionApprovals::::storage_info() + .first() + .and_then(|info| info.max_size) + .unwrap_or_default(); + assert_eq!(deposit(1, max_size), NftsCollectionApprovalDeposit::get()); + } +} diff --git a/runtime/testnet/src/config/assets.rs b/runtime/testnet/src/config/assets.rs index ac93d14a..95604ef0 100644 --- a/runtime/testnet/src/config/assets.rs +++ b/runtime/testnet/src/config/assets.rs @@ -29,11 +29,11 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); - // Key = 68 bytes (4+16+32+16), Value = 44 bytes (4+32+8) - pub const NftsBalanceDeposit: Balance = deposit(1, 112); + // Key = 68 bytes (4+16+32+16), Value = 52 bytes (4+32+16) + pub const NftsBalanceDeposit: Balance = deposit(1, 120); pub const NftsCollectionDeposit: Balance = 10 * UNIT; - // Key = 116 bytes (4+16+32+16+32+16), Value = 17 bytes (1+8+8) - pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 133); + // Key = 116 bytes (4+16+32+16+32+16), Value = 21 bytes (1+4+16) + pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 137); pub const NftsItemDeposit: Balance = UNIT / 100; pub const NftsMetadataDepositBase: Balance = deposit(1, 129); pub const NftsAttributeDepositBase: Balance = deposit(1, 0); @@ -126,3 +126,28 @@ impl pallet_assets::Config for Runtime { type StringLimit = AssetsStringLimit; type WeightInfo = pallet_assets::weights::SubstrateWeight; } + +#[cfg(test)] +mod tests { + use frame_support::traits::StorageInfoTrait; + + use super::*; + + #[test] + fn ensure_account_balance_deposit() { + let max_size = pallet_nfts::AccountBalance::::storage_info() + .first() + .and_then(|info| info.max_size) + .unwrap_or_default(); + assert_eq!(deposit(1, max_size), NftsBalanceDeposit::get()); + } + + #[test] + fn ensure_collection_approval_deposit() { + let max_size = pallet_nfts::CollectionApprovals::::storage_info() + .first() + .and_then(|info| info.max_size) + .unwrap_or_default(); + assert_eq!(deposit(1, max_size), NftsCollectionApprovalDeposit::get()); + } +} From bda3e47c4d87e65063dd971005b65e448c088598 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:41:08 +0700 Subject: [PATCH 11/40] chore: update test & weights --- pallets/nfts/src/tests.rs | 10 + pallets/nfts/src/weights.rs | 686 ++++++++++++++++++------------------ 2 files changed, 357 insertions(+), 339 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index cbee8aa9..c5a410f9 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -1835,6 +1835,7 @@ fn burn_works() { Some(account(4)), )); + // Throws error `UnknownItem` if burning an unknown collection item. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), Error::::UnknownItem @@ -1855,6 +1856,15 @@ fn burn_works() { default_item_config() )); + // Throws error `NoItemOwned` due to `do_transfer`. + let account_balance = AccountBalance::take(0, account(5)); + assert_noop!( + Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), + Error::::NoItemOwned + ); + AccountBalance::set(0, account(5), account_balance); + + // Throws error `NoPermission`. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission diff --git a/pallets/nfts/src/weights.rs b/pallets/nfts/src/weights.rs index 5d985d13..974b1f8f 100644 --- a/pallets/nfts/src/weights.rs +++ b/pallets/nfts/src/weights.rs @@ -2,13 +2,13 @@ //! Autogenerated weights for `pallet_nfts` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 40.0.0 -//! DATE: 2024-12-19, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-12-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `R0GUE`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/pop-node +// ./target/production/pop-node // benchmark // pallet // --chain=dev @@ -60,14 +60,8 @@ pub trait WeightInfo { fn set_collection_metadata() -> Weight; fn clear_collection_metadata() -> Weight; fn approve_transfer() -> Weight; - fn approve_collection_transfer() -> Weight; - fn force_approve_collection_transfer() -> Weight; fn cancel_approval() -> Weight; - fn cancel_collection_approval() -> Weight; - fn force_cancel_collection_approval() -> Weight; fn clear_all_transfer_approvals() -> Weight; - fn clear_collection_approvals(n: u32, ) -> Weight; - fn force_clear_collection_approvals(n: u32, ) -> Weight; fn set_accept_ownership() -> Weight; fn set_collection_max_supply() -> Weight; fn update_mint_settings() -> Weight; @@ -79,6 +73,12 @@ pub trait WeightInfo { fn claim_swap() -> Weight; fn mint_pre_signed(n: u32, ) -> Weight; fn set_attributes_pre_signed(n: u32, ) -> Weight; + fn approve_collection_transfer() -> Weight; + fn force_approve_collection_transfer() -> Weight; + fn cancel_collection_approval() -> Weight; + fn force_cancel_collection_approval() -> Weight; + fn clear_collection_approvals(n: u32, ) -> Weight; + fn force_clear_collection_approvals(n: u32, ) -> Weight; } /// Weights for `pallet_nfts` using the Substrate node and recommended hardware. @@ -98,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `105` // Estimated: `3549` - // Minimum execution time: 29_000_000 picoseconds. - Weight::from_parts(37_000_000, 3549) + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(21_000_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -117,8 +117,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3549` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(15_000_000, 3549) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -143,14 +143,18 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { + fn destroy(m: u32, c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32137 + a * (366 ±0)` // Estimated: `2523990 + a * (2954 ±0)` - // Minimum execution time: 1_005_000_000 picoseconds. - Weight::from_parts(2_234_507_890, 2523990) - // Standard Error: 87_675 - .saturating_add(Weight::from_parts(5_794_302, 0).saturating_mul(a.into())) + // Minimum execution time: 893_000_000 picoseconds. + Weight::from_parts(814_084_483, 2523990) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(26_897, 0).saturating_mul(m.into())) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(87_809, 0).saturating_mul(c.into())) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(4_861_675, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1005_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(T::DbWeight::get().writes(1005_u64)) @@ -176,7 +180,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `382` // Estimated: `4326` // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(37_000_000, 4326) + Weight::from_parts(38_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -199,7 +203,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `382` // Estimated: `4326` // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(38_000_000, 4326) + Weight::from_parts(39_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -227,8 +231,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `634` // Estimated: `4326` - // Minimum execution time: 40_000_000 picoseconds. - Weight::from_parts(44_000_000, 4326) + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(43_000_000, 4326) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -256,8 +260,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `765` // Estimated: `6180` - // Minimum execution time: 45_000_000 picoseconds. - Weight::from_parts(54_000_000, 6180) + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(50_000_000, 6180) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -272,10 +276,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `690 + i * (108 ±0)` // Estimated: `3549 + i * (3336 ±0)` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 3549) - // Standard Error: 33_010 - .saturating_add(Weight::from_parts(17_115_197, 0).saturating_mul(i.into())) + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 3549) + // Standard Error: 41_268 + .saturating_add(Weight::from_parts(12_689_751, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -289,8 +293,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 10_000_000 picoseconds. - Weight::from_parts(12_000_000, 3534) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -303,7 +307,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `395` // Estimated: `3534` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3534) + Weight::from_parts(11_000_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -315,8 +319,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `267` // Estimated: `3549` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3549) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(10_000_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -332,8 +336,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3593` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(21_000_000, 3593) + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(15_000_000, 3593) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -345,8 +349,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `296` // Estimated: `6078` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 6078) + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(25_000_000, 6078) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -358,8 +362,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238` // Estimated: `3549` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3549) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -371,8 +375,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `203` // Estimated: `3549` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(10_000_000, 3549) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 3549) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -384,8 +388,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 3534) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(11_000_000, 3534) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -403,8 +407,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3944` - // Minimum execution time: 38_000_000 picoseconds. - Weight::from_parts(39_000_000, 3944) + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(28_000_000, 3944) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -416,8 +420,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `271` // Estimated: `3944` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(20_000_000, 3944) + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 3944) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -433,8 +437,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `943` // Estimated: `3944` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(36_000_000, 3944) + // Minimum execution time: 26_000_000 picoseconds. + Weight::from_parts(27_000_000, 3944) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -446,8 +450,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `308` // Estimated: `4466` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 4466) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 4466) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -464,10 +468,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `686 + n * (398 ±0)` // Estimated: `4466 + n * (2954 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 4466) - // Standard Error: 11_913 - .saturating_add(Weight::from_parts(5_265_987, 0).saturating_mul(n.into())) + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 4466) + // Standard Error: 11_599 + .saturating_add(Weight::from_parts(4_782_076, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -488,8 +492,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3812` - // Minimum execution time: 32_000_000 picoseconds. - Weight::from_parts(38_000_000, 3812) + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(24_000_000, 3812) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -505,8 +509,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `809` // Estimated: `3812` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(33_000_000, 3812) + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3812) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -522,8 +526,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `325` // Estimated: `3759` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(30_000_000, 3759) + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 3759) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -539,8 +543,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `643` // Estimated: `3759` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(30_000_000, 3759) + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3759) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -557,36 +561,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) - /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn approve_collection_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `477` - // Estimated: `3602` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(21_000_000, 3602) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) - /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn force_approve_collection_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `477` - // Estimated: `3602` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(24_000_000, 3602) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:0) @@ -595,33 +569,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `345` // Estimated: `4326` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 4326) + // Minimum execution time: 12_000_000 picoseconds. + Weight::from_parts(13_000_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn cancel_collection_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `359` - // Estimated: `3602` - // Minimum execution time: 16_000_000 picoseconds. - Weight::from_parts(17_000_000, 3602) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn force_cancel_collection_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `359` - // Estimated: `3602` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(17_000_000, 3602) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:0) @@ -631,50 +583,18 @@ impl WeightInfo for SubstrateWeight { // Measured: `345` // Estimated: `4326` // Minimum execution time: 12_000_000 picoseconds. - Weight::from_parts(12_000_000, 4326) + Weight::from_parts(14_000_000, 4326) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - /// The range of component `n` is `[1, 1000]`. - fn clear_collection_approvals(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `327 + n * (75 ±0)` - // Estimated: `3602 + n * (2612 ±0)` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3602) - // Standard Error: 7_954 - .saturating_add(Weight::from_parts(3_625_558, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) - } - /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - /// The range of component `n` is `[1, 1000]`. - fn force_clear_collection_approvals(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `327 + n * (75 ±0)` - // Estimated: `3602 + n * (2612 ±0)` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3602) - // Standard Error: 5_861 - .saturating_add(Weight::from_parts(3_576_220, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) - } /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3517` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(10_000_000, 3517) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3517) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -686,8 +606,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `267` // Estimated: `3549` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(13_000_000, 3549) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(10_000_000, 3549) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -699,8 +619,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `250` // Estimated: `3538` - // Minimum execution time: 12_000_000 picoseconds. - Weight::from_parts(13_000_000, 3538) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(11_000_000, 3538) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -716,8 +636,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `478` // Estimated: `4326` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(18_000_000, 4326) + // Minimum execution time: 12_000_000 picoseconds. + Weight::from_parts(13_000_000, 4326) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -745,8 +665,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `877` // Estimated: `6180` - // Minimum execution time: 50_000_000 picoseconds. - Weight::from_parts(57_000_000, 6180) + // Minimum execution time: 53_000_000 picoseconds. + Weight::from_parts(55_000_000, 6180) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -756,9 +676,9 @@ impl WeightInfo for SubstrateWeight { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_971_321, 0) - // Standard Error: 11_267 - .saturating_add(Weight::from_parts(1_831_565, 0).saturating_mul(n.into())) + Weight::from_parts(1_497_803, 0) + // Standard Error: 5_639 + .saturating_add(Weight::from_parts(1_502_698, 0).saturating_mul(n.into())) } /// Storage: `Nfts::Item` (r:2 w:0) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) @@ -769,7 +689,7 @@ impl WeightInfo for SubstrateWeight { // Measured: `421` // Estimated: `7662` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 7662) + Weight::from_parts(11_000_000, 7662) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -810,8 +730,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1118` // Estimated: `7662` - // Minimum execution time: 77_000_000 picoseconds. - Weight::from_parts(87_000_000, 7662) + // Minimum execution time: 79_000_000 picoseconds. + Weight::from_parts(82_000_000, 7662) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } @@ -840,10 +760,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `485` // Estimated: `6078 + n * (2954 ±0)` - // Minimum execution time: 90_000_000 picoseconds. - Weight::from_parts(97_702_243, 6078) - // Standard Error: 110_243 - .saturating_add(Weight::from_parts(21_787_114, 0).saturating_mul(n.into())) + // Minimum execution time: 91_000_000 picoseconds. + Weight::from_parts(95_221_709, 6078) + // Standard Error: 128_983 + .saturating_add(Weight::from_parts(22_458_028, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) @@ -867,16 +787,100 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `514` // Estimated: `4466 + n * (2954 ±0)` - // Minimum execution time: 51_000_000 picoseconds. - Weight::from_parts(66_554_620, 4466) - // Standard Error: 115_497 - .saturating_add(Weight::from_parts(27_385_703, 0).saturating_mul(n.into())) + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(54_326_784, 4466) + // Standard Error: 99_430 + .saturating_add(Weight::from_parts(20_625_932, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) } + /// Storage: `Nfts::AccountBalance` (r:1 w:0) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn approve_collection_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `500` + // Estimated: `3602` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(24_000_000, 3602) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::AccountBalance` (r:1 w:0) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn force_approve_collection_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `500` + // Estimated: `3602` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(24_000_000, 3602) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn cancel_collection_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `359` + // Estimated: `3602` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(17_000_000, 3602) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn force_cancel_collection_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `359` + // Estimated: `3602` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(17_000_000, 3602) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1000]`. + fn clear_collection_approvals(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `327 + n * (75 ±0)` + // Estimated: `3602 + n * (2612 ±0)` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(22_000_000, 3602) + // Standard Error: 8_973 + .saturating_add(Weight::from_parts(3_888_488, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) + } + /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1000]`. + fn force_clear_collection_approvals(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `327 + n * (75 ±0)` + // Estimated: `3602 + n * (2612 ±0)` + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(33_601_852, 3602) + // Standard Error: 7_525 + .saturating_add(Weight::from_parts(3_464_828, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) + } } // For backwards compatibility and tests. @@ -895,8 +899,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `105` // Estimated: `3549` - // Minimum execution time: 29_000_000 picoseconds. - Weight::from_parts(37_000_000, 3549) + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(21_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -914,8 +918,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3549` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(15_000_000, 3549) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -940,14 +944,18 @@ impl WeightInfo for () { /// The range of component `m` is `[0, 1000]`. /// The range of component `c` is `[0, 1000]`. /// The range of component `a` is `[0, 1000]`. - fn destroy(_m: u32, _c: u32, a: u32, ) -> Weight { + fn destroy(m: u32, c: u32, a: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `32137 + a * (366 ±0)` // Estimated: `2523990 + a * (2954 ±0)` - // Minimum execution time: 1_005_000_000 picoseconds. - Weight::from_parts(2_234_507_890, 2523990) - // Standard Error: 87_675 - .saturating_add(Weight::from_parts(5_794_302, 0).saturating_mul(a.into())) + // Minimum execution time: 893_000_000 picoseconds. + Weight::from_parts(814_084_483, 2523990) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(26_897, 0).saturating_mul(m.into())) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(87_809, 0).saturating_mul(c.into())) + // Standard Error: 18_466 + .saturating_add(Weight::from_parts(4_861_675, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1005_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(a.into()))) .saturating_add(RocksDbWeight::get().writes(1005_u64)) @@ -973,7 +981,7 @@ impl WeightInfo for () { // Measured: `382` // Estimated: `4326` // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(37_000_000, 4326) + Weight::from_parts(38_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -996,7 +1004,7 @@ impl WeightInfo for () { // Measured: `382` // Estimated: `4326` // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(38_000_000, 4326) + Weight::from_parts(39_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1024,12 +1032,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `634` // Estimated: `4326` - // Minimum execution time: 40_000_000 picoseconds. - Weight::from_parts(44_000_000, 4326) + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(43_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } - /// Storage: `Nfts::Collection` (r:1 w:0) + /// Storage: `Nfts::Collection` (r:1 w:0) /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::Attribute` (r:1 w:0) /// Proof: `Nfts::Attribute` (`max_values`: None, `max_size`: Some(479), added: 2954, mode: `MaxEncodedLen`) @@ -1053,8 +1061,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `765` // Estimated: `6180` - // Minimum execution time: 45_000_000 picoseconds. - Weight::from_parts(54_000_000, 6180) + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(50_000_000, 6180) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1069,10 +1077,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `690 + i * (108 ±0)` // Estimated: `3549 + i * (3336 ±0)` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 3549) - // Standard Error: 33_010 - .saturating_add(Weight::from_parts(17_115_197, 0).saturating_mul(i.into())) + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 3549) + // Standard Error: 41_268 + .saturating_add(Weight::from_parts(12_689_751, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into()))) @@ -1086,8 +1094,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 10_000_000 picoseconds. - Weight::from_parts(12_000_000, 3534) + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(11_000_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1100,7 +1108,7 @@ impl WeightInfo for () { // Measured: `395` // Estimated: `3534` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3534) + Weight::from_parts(11_000_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1112,8 +1120,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `267` // Estimated: `3549` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3549) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(10_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1129,8 +1137,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `417` // Estimated: `3593` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(21_000_000, 3593) + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(15_000_000, 3593) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1142,8 +1150,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `296` // Estimated: `6078` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 6078) + // Minimum execution time: 24_000_000 picoseconds. + Weight::from_parts(25_000_000, 6078) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1155,8 +1163,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238` // Estimated: `3549` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 3549) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1168,8 +1176,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `203` // Estimated: `3549` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(10_000_000, 3549) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(7_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1181,8 +1189,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `395` // Estimated: `3534` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(14_000_000, 3534) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(11_000_000, 3534) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1200,8 +1208,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3944` - // Minimum execution time: 38_000_000 picoseconds. - Weight::from_parts(39_000_000, 3944) + // Minimum execution time: 28_000_000 picoseconds. + Weight::from_parts(28_000_000, 3944) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1213,8 +1221,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `271` // Estimated: `3944` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(20_000_000, 3944) + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 3944) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1230,8 +1238,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `943` // Estimated: `3944` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(36_000_000, 3944) + // Minimum execution time: 26_000_000 picoseconds. + Weight::from_parts(27_000_000, 3944) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1243,8 +1251,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `308` // Estimated: `4466` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 4466) + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 4466) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1261,10 +1269,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `686 + n * (398 ±0)` // Estimated: `4466 + n * (2954 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 4466) - // Standard Error: 11_913 - .saturating_add(Weight::from_parts(5_265_987, 0).saturating_mul(n.into())) + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 4466) + // Standard Error: 11_599 + .saturating_add(Weight::from_parts(4_782_076, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1285,8 +1293,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3812` - // Minimum execution time: 32_000_000 picoseconds. - Weight::from_parts(38_000_000, 3812) + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(24_000_000, 3812) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1302,8 +1310,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `809` // Estimated: `3812` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(33_000_000, 3812) + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3812) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1319,8 +1327,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `325` // Estimated: `3759` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(30_000_000, 3759) + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 3759) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1336,8 +1344,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `643` // Estimated: `3759` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(30_000_000, 3759) + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(23_000_000, 3759) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1354,36 +1362,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) - /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn approve_collection_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `477` - // Estimated: `3602` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(21_000_000, 3602) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Nfts::AccountBalance` (r:1 w:0) - /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) - /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn force_approve_collection_transfer() -> Weight { - // Proof Size summary in bytes: - // Measured: `477` - // Estimated: `3602` - // Minimum execution time: 20_000_000 picoseconds. - Weight::from_parts(24_000_000, 3602) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:0) @@ -1392,33 +1370,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `345` // Estimated: `4326` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 4326) + // Minimum execution time: 12_000_000 picoseconds. + Weight::from_parts(13_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn cancel_collection_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `359` - // Estimated: `3602` - // Minimum execution time: 16_000_000 picoseconds. - Weight::from_parts(17_000_000, 3602) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - fn force_cancel_collection_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `359` - // Estimated: `3602` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(17_000_000, 3602) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionApprovals` (r:1 w:0) @@ -1428,50 +1384,18 @@ impl WeightInfo for () { // Measured: `345` // Estimated: `4326` // Minimum execution time: 12_000_000 picoseconds. - Weight::from_parts(12_000_000, 4326) + Weight::from_parts(14_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - /// The range of component `n` is `[1, 1000]`. - fn clear_collection_approvals(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `327 + n * (75 ±0)` - // Estimated: `3602 + n * (2612 ±0)` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3602) - // Standard Error: 7_954 - .saturating_add(Weight::from_parts(3_625_558, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) - } - /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) - /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) - /// The range of component `n` is `[1, 1000]`. - fn force_clear_collection_approvals(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `327 + n * (75 ±0)` - // Estimated: `3602 + n * (2612 ±0)` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3602) - // Standard Error: 5_861 - .saturating_add(Weight::from_parts(3_576_220, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) - } /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn set_accept_ownership() -> Weight { // Proof Size summary in bytes: // Measured: `3` // Estimated: `3517` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(10_000_000, 3517) + // Minimum execution time: 7_000_000 picoseconds. + Weight::from_parts(8_000_000, 3517) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1483,8 +1407,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `267` // Estimated: `3549` - // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(13_000_000, 3549) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(10_000_000, 3549) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1496,8 +1420,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `250` // Estimated: `3538` - // Minimum execution time: 12_000_000 picoseconds. - Weight::from_parts(13_000_000, 3538) + // Minimum execution time: 10_000_000 picoseconds. + Weight::from_parts(11_000_000, 3538) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1513,8 +1437,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `478` // Estimated: `4326` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(18_000_000, 4326) + // Minimum execution time: 12_000_000 picoseconds. + Weight::from_parts(13_000_000, 4326) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1542,8 +1466,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `877` // Estimated: `6180` - // Minimum execution time: 50_000_000 picoseconds. - Weight::from_parts(57_000_000, 6180) + // Minimum execution time: 53_000_000 picoseconds. + Weight::from_parts(55_000_000, 6180) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1553,9 +1477,9 @@ impl WeightInfo for () { // Measured: `0` // Estimated: `0` // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_971_321, 0) - // Standard Error: 11_267 - .saturating_add(Weight::from_parts(1_831_565, 0).saturating_mul(n.into())) + Weight::from_parts(1_497_803, 0) + // Standard Error: 5_639 + .saturating_add(Weight::from_parts(1_502_698, 0).saturating_mul(n.into())) } /// Storage: `Nfts::Item` (r:2 w:0) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) @@ -1566,7 +1490,7 @@ impl WeightInfo for () { // Measured: `421` // Estimated: `7662` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(12_000_000, 7662) + Weight::from_parts(11_000_000, 7662) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1607,8 +1531,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1118` // Estimated: `7662` - // Minimum execution time: 77_000_000 picoseconds. - Weight::from_parts(87_000_000, 7662) + // Minimum execution time: 79_000_000 picoseconds. + Weight::from_parts(82_000_000, 7662) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } @@ -1637,10 +1561,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `485` // Estimated: `6078 + n * (2954 ±0)` - // Minimum execution time: 90_000_000 picoseconds. - Weight::from_parts(97_702_243, 6078) - // Standard Error: 110_243 - .saturating_add(Weight::from_parts(21_787_114, 0).saturating_mul(n.into())) + // Minimum execution time: 91_000_000 picoseconds. + Weight::from_parts(95_221_709, 6078) + // Standard Error: 128_983 + .saturating_add(Weight::from_parts(22_458_028, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) @@ -1664,14 +1588,98 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `514` // Estimated: `4466 + n * (2954 ±0)` - // Minimum execution time: 51_000_000 picoseconds. - Weight::from_parts(66_554_620, 4466) - // Standard Error: 115_497 - .saturating_add(Weight::from_parts(27_385_703, 0).saturating_mul(n.into())) + // Minimum execution time: 47_000_000 picoseconds. + Weight::from_parts(54_326_784, 4466) + // Standard Error: 99_430 + .saturating_add(Weight::from_parts(20_625_932, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) .saturating_add(Weight::from_parts(0, 2954).saturating_mul(n.into())) } + /// Storage: `Nfts::AccountBalance` (r:1 w:0) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn approve_collection_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `500` + // Estimated: `3602` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(24_000_000, 3602) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::AccountBalance` (r:1 w:0) + /// Proof: `Nfts::AccountBalance` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) + /// Proof: `Nfts::CollectionConfigOf` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn force_approve_collection_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `500` + // Estimated: `3602` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(24_000_000, 3602) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn cancel_collection_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `359` + // Estimated: `3602` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(17_000_000, 3602) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1 w:1) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + fn force_cancel_collection_approval() -> Weight { + // Proof Size summary in bytes: + // Measured: `359` + // Estimated: `3602` + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(17_000_000, 3602) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1000]`. + fn clear_collection_approvals(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `327 + n * (75 ±0)` + // Estimated: `3602 + n * (2612 ±0)` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(22_000_000, 3602) + // Standard Error: 8_973 + .saturating_add(Weight::from_parts(3_888_488, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) + } + /// Storage: `Nfts::CollectionApprovals` (r:1000 w:999) + /// Proof: `Nfts::CollectionApprovals` (`max_values`: None, `max_size`: Some(137), added: 2612, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 1000]`. + fn force_clear_collection_approvals(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `327 + n * (75 ±0)` + // Estimated: `3602 + n * (2612 ±0)` + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(33_601_852, 3602) + // Standard Error: 7_525 + .saturating_add(Weight::from_parts(3_464_828, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2612).saturating_mul(n.into())) + } } From 460859f85ac516a7a102122eb6365ff6dc77e01c Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:00:50 +0700 Subject: [PATCH 12/40] chore: benchmarking --- pallets/nfts/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index 9a5e0c32..481fbe17 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -303,7 +303,7 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::BalanceDeposit::get() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::BalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) verify { assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); @@ -759,7 +759,7 @@ benchmarks_instance_pallet! { let duration = T::MaxDeadlineDuration::get(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::BalanceDeposit::get() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::BalanceDeposit::get()); let origin = SystemOrigin::Signed(caller.clone()); frame_system::Pallet::::set_block_number(One::one()); Nfts::::transfer(origin.clone().into(), collection, item2, target_lookup)?; From 663066e9f6b7f14332049d26c3a0ce598baf83f9 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:40:55 +0700 Subject: [PATCH 13/40] chore: update comments --- pallets/nfts/src/tests.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index c5a410f9..9dd4b940 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -1835,7 +1835,7 @@ fn burn_works() { Some(account(4)), )); - // Throws error `UnknownItem` if burning an unknown collection item. + // Throws error `Error::UnknownItem` if burning an unknown collection item. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), Error::::UnknownItem @@ -1856,7 +1856,8 @@ fn burn_works() { default_item_config() )); - // Throws error `NoItemOwned` due to `do_transfer`. + // Throws error `Error::NoItemOwned` if the account does not have an entry in + // `AccountBalance`. let account_balance = AccountBalance::take(0, account(5)); assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), @@ -1864,7 +1865,7 @@ fn burn_works() { ); AccountBalance::set(0, account(5), account_balance); - // Throws error `NoPermission`. + // Throws error `Error::NoPermission`. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission From d9751702d2da34dc8ab91b40aeec3b1caff6a171 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 23 Dec 2024 21:43:59 +0700 Subject: [PATCH 14/40] chore: reorder type --- pallets/nfts/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 9dd4b940..6972f7c2 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -37,8 +37,8 @@ use sp_runtime::{ use crate::{mock::*, Event, SystemConfig, *}; -type AccountIdOf = ::AccountId; type AccountBalance = crate::AccountBalance; +type AccountIdOf = ::AccountId; type CollectionApprovals = crate::CollectionApprovals; type CollectionApprovalDeposit = ::CollectionApprovalDeposit; type CollectionId = ::CollectionId; From f316e2fa61b255ac5b4d338bc1d317654bb38963 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:34:30 +0700 Subject: [PATCH 15/40] chore: resolve comments --- pallets/nfts/src/common_functions.rs | 5 +- .../nfts/src/features/create_delete_item.rs | 17 +- pallets/nfts/src/tests.rs | 168 ++++++++++-------- 3 files changed, 99 insertions(+), 91 deletions(-) diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 55a70cd0..024e2d4e 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -98,10 +98,7 @@ impl, I: 'static> Pallet { pub(crate) fn increment_account_balance( collection: T::CollectionId, owner: &T::AccountId, - (deposit_account, deposit_amount): ( - &::AccountId, - DepositBalanceOf, - ), + (deposit_account, deposit_amount): (&T::AccountId, DepositBalanceOf), ) -> DispatchResult { AccountBalance::::mutate(collection, owner, |maybe_balance| -> DispatchResult { match maybe_balance { diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index 9a2e0983..cc26b15e 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -69,18 +69,17 @@ impl, I: 'static> Pallet { collection_details.items.saturating_inc(); let collection_config = Self::get_collection_config(&collection)?; - let deposit_amount = - match collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { - true => T::ItemDeposit::get(), - false => Zero::zero(), - }; + let deposit_required = + collection_config.is_setting_enabled(CollectionSetting::DepositRequired); let deposit_account = maybe_depositor.unwrap_or_else(|| collection_details.owner.clone()); + let balance_depoist = + deposit_required.then_some(T::BalanceDeposit::get()).unwrap_or_default(); Self::increment_account_balance( collection, &mint_to, - (&deposit_account, deposit_amount), + (&deposit_account, balance_depoist), )?; let item_owner = mint_to.clone(); @@ -93,9 +92,11 @@ impl, I: 'static> Pallet { collection_details.item_configs.saturating_inc(); } - T::Currency::reserve(&deposit_account, deposit_amount)?; + let item_deposit = + deposit_required.then_some(T::ItemDeposit::get()).unwrap_or_default(); + T::Currency::reserve(&deposit_account, item_deposit)?; - let deposit = ItemDeposit { account: deposit_account, amount: deposit_amount }; + let deposit = ItemDeposit { account: deposit_account, amount: item_deposit }; let details = ItemDetails { owner: item_owner, approvals: ApprovalsOf::::default(), diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 6972f7c2..fa1bf825 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -213,6 +213,10 @@ fn cancel_collection_approval( } } +fn balance_deposit() -> DepositBalanceOf { + <::BalanceDeposit>::get() +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -250,6 +254,7 @@ fn basic_minting_should_work() { #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::create( @@ -274,7 +279,7 @@ fn lifecycle_should_work() { account(10), default_item_config() )); - assert_eq!(Balances::reserved_balance(&account(1)), 6 + 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 6 + balance_deposit); assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), 0, @@ -283,7 +288,7 @@ fn lifecycle_should_work() { default_item_config() )); assert_eq!(AccountBalance::get(0, account(20)), Some((1, (account(1), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2 * balance_deposit); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 1)))); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); @@ -291,18 +296,18 @@ fn lifecycle_should_work() { assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); assert_eq!(Collection::::get(0).unwrap().item_configs, 3); - assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3); // 3 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3 * balance_deposit); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); assert!(!AccountBalance::contains_key(0, &account(1))); assert_eq!(AccountBalance::get(0, account(2)), Some((1, (account(2), 1)))); - assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2); // 2 - balance deposit - assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2 * balance_deposit); assert!(ItemMetadataOf::::contains_key(0, 42)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2 * balance_deposit); assert!(ItemMetadataOf::::contains_key(0, 69)); assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -714,6 +719,8 @@ fn origin_guards_should_work() { #[test] fn transfer_owner_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); Balances::make_free_balance_be(&account(3), 100); @@ -754,7 +761,7 @@ fn transfer_owner_should_work() { bvec![0u8; 20], )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(Balances::reserved_balance(&account(1)), 2); // 1 - metadata deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(&account(1)), 1 + balance_deposit); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(3)), Some(0))); assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(2)), 0, account(3))); @@ -765,10 +772,10 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&account(3)), 44); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); - // reserved_balance of accounts 1 & 2 should be unchanged: - assert_eq!(Balances::reserved_balance(&account(1)), 1); - // transfer the collection item reserves account 2's balance. - assert_eq!(Balances::reserved_balance(&account(2)), 1); // 1 - balance deposit + // Reserve `balance_deposit` from account 2 and reserved balance of account 1 remains the + // same. + assert_eq!(Balances::reserved_balance(&account(1)), balance_deposit); + assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); // 2's acceptance from before is reset when it became an owner, so it cannot be transferred // without a fresh acceptance. @@ -943,6 +950,8 @@ fn set_collection_metadata_should_work() { #[test] fn set_item_metadata_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 30); // Cannot add metadata to unknown item @@ -960,7 +969,7 @@ fn set_item_metadata_should_work() { // Successfully add metadata and take deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 20])); - assert_eq!(Balances::free_balance(&account(1)), 7); // free_balance - metadata deposit - item deposit - balance deposit + assert_eq!(Balances::free_balance(&account(1)), 8 - balance_deposit); assert!(ItemMetadataOf::::contains_key(0, 42)); // Force origin works, too. @@ -968,9 +977,9 @@ fn set_item_metadata_should_work() { // Update deposit assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 15])); - assert_eq!(Balances::free_balance(&account(1)), 12); // free_balance - metadata deposit - item deposit - balance deposit + assert_eq!(Balances::free_balance(&account(1)), 13 - balance_deposit); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0u8; 25])); - assert_eq!(Balances::free_balance(&account(1)), 2); // free_balance - metadata deposit - item deposit - balance deposit + assert_eq!(Balances::free_balance(&account(1)), 3 - balance_deposit); // Cannot over-reserve assert_noop!( @@ -1014,6 +1023,8 @@ fn set_item_metadata_should_work() { #[test] fn set_collection_owner_attributes_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( @@ -1055,7 +1066,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 10 + 1); // 10 - attribute deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 10 + balance_deposit); assert_eq!(Collection::::get(0).unwrap().owner_deposit, 9); assert_ok!(Nfts::set_attribute( @@ -1074,7 +1085,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 19 + 1); // 19 - attribute deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 19 + balance_deposit); assert_eq!(Collection::::get(0).unwrap().owner_deposit, 18); assert_ok!(Nfts::clear_attribute( @@ -1091,7 +1102,7 @@ fn set_collection_owner_attributes_should_work() { (Some(0), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 16 + 1); // 16 - attribute deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 16 + balance_deposit); assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 0)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -1184,6 +1195,8 @@ fn set_collection_system_attributes_should_work() { #[test] fn set_item_owner_attributes_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); Balances::make_free_balance_be(&account(3), 100); @@ -1336,7 +1349,7 @@ fn set_item_owner_attributes_should_work() { assert_eq!(deposit.account, Some(account(3))); assert_eq!(deposit.amount, 13); assert_eq!(Balances::reserved_balance(account(2)), 3); - assert_eq!(Balances::reserved_balance(account(3)), 13 + 1); // 13 - attribute deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(3)), 13 + balance_deposit); // validate attributes on item deletion assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0)); @@ -1543,6 +1556,8 @@ fn validate_deposit_required_setting() { #[test] fn set_attribute_should_respect_lock() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( @@ -1585,7 +1600,7 @@ fn set_attribute_should_respect_lock() { (Some(1), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 11 + 1); // 12 - attribute deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 11 + balance_deposit); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![])); assert_ok!(Nfts::lock_collection( @@ -1704,6 +1719,8 @@ fn preserve_config_for_frozen_items() { #[test] fn force_update_collection_should_work() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( @@ -1726,7 +1743,7 @@ fn force_update_collection_should_work() { )); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![0; 20])); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![0; 20])); - assert_eq!(Balances::reserved_balance(account(1)), 65 + 2); // 65 - metadata deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 65 + 2 * balance_deposit); // force item status to be free holding assert_ok!(Nfts::force_collection_config( @@ -1755,7 +1772,7 @@ fn force_update_collection_should_work() { Some(account(4)), )); assert_eq!(collections(), vec![(account(5), 0)]); - assert_eq!(Balances::reserved_balance(account(1)), 2 + 2); // 2 - metadata deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 2 + 2 * balance_deposit); assert_eq!(Balances::reserved_balance(account(5)), 63); assert_ok!(Nfts::redeposit( @@ -1763,7 +1780,7 @@ fn force_update_collection_should_work() { 0, bvec![0, 42, 50, 69, 100] )); - assert_eq!(Balances::reserved_balance(account(1)), 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 2 * balance_deposit); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(5)), 0, 42, bvec![0; 20])); assert_eq!(Balances::reserved_balance(account(5)), 42); @@ -1821,6 +1838,8 @@ fn force_update_collection_should_work() { #[test] fn burn_works() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), @@ -1871,11 +1890,11 @@ fn burn_works() { Error::::NoPermission ); - assert_eq!(Balances::reserved_balance(account(1)), 2 + 1); // 2 - item deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 2 + balance_deposit); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); // Burn an item unreserving the item deposit while retaining the reservation for `balance // deposit`. - assert_eq!(Balances::reserved_balance(account(1)), 1 + 1); // 1 - item deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(account(1)), 1 + balance_deposit); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); // Burn an item unreserving the balance deposit when the value of `AccountBalance` reaches // zero. Remove the `AccountBalance` record. @@ -4142,6 +4161,7 @@ fn mint_requires_deposit_works() { new_test_ext().execute_with(|| { let collection_id = 0; let collection_owner = account(1); + let balance_deposit = balance_deposit(); let mut item_id = 42; let mut item_owner = account(2); @@ -4177,7 +4197,7 @@ fn mint_requires_deposit_works() { AccountBalance::get(collection_id, item_owner.clone()), Some((1, (collection_owner.clone(), 1))) ); - assert_eq!(Balances::reserved_balance(&collection_owner), 1 + 1); // 1 - item deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); } @@ -4195,7 +4215,7 @@ fn mint_requires_deposit_works() { AccountBalance::get(collection_id, &item_owner), Some((2, (collection_owner.clone(), 1))) ); - assert_eq!(Balances::reserved_balance(&collection_owner), 2 + 1); // 2 - item deposit, 1 - balance deposit + assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); } @@ -4214,7 +4234,7 @@ fn mint_requires_deposit_works() { AccountBalance::get(collection_id, &item_owner), Some((1, (collection_owner.clone(), 1))) ); - assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2); // 3 - item deposit, 2 - balance deposit + assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); } @@ -4227,6 +4247,7 @@ fn mint_requires_deposit_works() { fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { let collection_id = 0; + let balance_deposit = balance_deposit(); let mut dest = account(3); let mut item_id = 42; let owner = account(2); @@ -4281,7 +4302,7 @@ fn transfer_requires_deposit_works() { dest.clone() )); assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), 1)))); - assert_eq!(Balances::reserved_balance(&owner), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&owner), balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -4299,7 +4320,7 @@ fn transfer_requires_deposit_works() { default_item_config() )); - Balances::make_free_balance_be(&dest, 1); // 1 - ED + Balances::make_free_balance_be(&dest, 1); assert_ok!(Nfts::transfer( RuntimeOrigin::signed(owner.clone()), collection_id, @@ -4307,7 +4328,7 @@ fn transfer_requires_deposit_works() { dest.clone() )); assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 1)))); - assert_eq!(Balances::reserved_balance(&owner), 2); // 2 - balance deposit + assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -4324,7 +4345,7 @@ fn transfer_requires_deposit_works() { owner.clone(), default_item_config() )); - Balances::make_free_balance_be(&dest, 2); // 1 - ED, 1 - balance deposit + Balances::make_free_balance_be(&dest, 1 + balance_deposit); assert_ok!(Nfts::transfer( RuntimeOrigin::signed(owner.clone()), collection_id, @@ -4332,8 +4353,8 @@ fn transfer_requires_deposit_works() { dest.clone() )); assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (dest.clone(), 1)))); - assert_eq!(Balances::reserved_balance(&owner), 2); // 2 - balance deposit - assert_eq!(Balances::reserved_balance(&dest), 1); // 1 - balance deposit + assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), balance_deposit); assert!(Balances::usable_balance(&dest).is_zero()); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -4962,36 +4983,40 @@ fn increment_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; let deposit_account = account(1); + let deposit_amount = balance_deposit(); let owner = account(2); - // Throws error `BalancesError::InsufficientBalance`. assert_noop!( - Nfts::increment_account_balance(collection_id, &owner, (&deposit_account, 1)), + Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + ), BalancesError::::InsufficientBalance ); - Balances::make_free_balance_be(&deposit_account, 100); // Initialize `AccountBalance` and increase the collection item count for the new account. - assert_ok!(Nfts::increment_account_balance(collection_id, &owner, (&deposit_account, 1))); - assert_eq!(Balances::reserved_balance(&account(1)), 1); + assert_ok!(Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + )); + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); assert_eq!( AccountBalance::get(collection_id, &owner), - Some((1, (deposit_account.clone(), 1))) + Some((1, (deposit_account.clone(), deposit_amount))) ); - // Increment the balance of a non-zero balance account. No additional reserves. - (1..10).into_iter().for_each(|i| { - assert_ok!(Nfts::increment_account_balance( - collection_id, - &owner, - (&deposit_account, 1) - )); - assert_eq!(Balances::reserved_balance(&deposit_account), 1); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((i + 1, (deposit_account.clone(), 1))) - ); - }); + assert_ok!(Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + )); + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((2, (deposit_account.clone(), deposit_amount))) + ); }); } @@ -4999,8 +5024,9 @@ fn increment_account_balance_works() { fn decrement_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; - let balance = 3u32; + let balance = 2u32; let deposit_account = account(1); + let deposit_amount = balance_deposit(); let owner = account(2); Balances::make_free_balance_be(&deposit_account, 100); @@ -5009,36 +5035,20 @@ fn decrement_account_balance_works() { Nfts::decrement_account_balance(collection_id, &deposit_account), Error::::NoItemOwned ); - - (0..balance).into_iter().for_each(|_| { - assert_ok!(Nfts::increment_account_balance( - collection_id, - &owner, - (&deposit_account, 1) - )); - }); - - // Successfully decrement the value of the `AccountBalance` entry. - let balance_before_decrement = - AccountBalance::get(collection_id, &owner).map(|a| a.0).unwrap(); - assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); - let balance_after_decrement = - AccountBalance::get(collection_id, &owner).map(|a| a.0).unwrap(); - assert_eq!(balance_before_decrement - 1, balance_after_decrement); - assert_eq!(Balances::reserved_balance(&deposit_account), 1); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((balance - 1, (deposit_account.clone(), 1))) + // Set account balance and reserve `DepositBalance`. + AccountBalance::insert( + collection_id, + &owner, + (&balance, (&deposit_account, deposit_amount)), ); - - // Decrement value of `AccountBalance` if it is non-zero. + ::Currency::reserve(&deposit_account, deposit_amount).expect("should work"); + // Successfully decrement the value of the `AccountBalance` entry. assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); - assert_eq!(Balances::reserved_balance(&deposit_account), 1); assert_eq!( AccountBalance::get(collection_id, &owner), - Some((balance - 2, (deposit_account.clone(), 1))) + Some((balance - 1, (deposit_account.clone(), deposit_amount))) ); - + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); // `AccountBalance` record is deleted, and the depositor's funds are unreserved if // the `AccountBalance` value reaches zero after decrementing. assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); From 28159d0dd7c6eaaeac8125259765f56ece6537e5 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:43:21 +0700 Subject: [PATCH 16/40] chore: comments --- pallets/nfts/src/features/create_delete_item.rs | 4 ++-- pallets/nfts/src/tests.rs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index cc26b15e..830cfe4e 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -74,12 +74,12 @@ impl, I: 'static> Pallet { let deposit_account = maybe_depositor.unwrap_or_else(|| collection_details.owner.clone()); - let balance_depoist = + let balance_deposit = deposit_required.then_some(T::BalanceDeposit::get()).unwrap_or_default(); Self::increment_account_balance( collection, &mint_to, - (&deposit_account, balance_depoist), + (&deposit_account, balance_deposit), )?; let item_owner = mint_to.clone(); diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index fa1bf825..71d11891 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -233,8 +233,9 @@ fn basic_minting_should_work() { default_collection_config() )); assert_eq!(collections(), vec![(account(1), 0)]); + // Minting doesn't require a reserve because the collection's DepositRequired setting is + // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - // Minting skips deposit as `DepositRequired` is disabled. assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 0)))); assert_eq!(items(), vec![(account(1), 0, 42)]); @@ -245,7 +246,6 @@ fn basic_minting_should_work() { )); assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); - // Minting skips deposit as `DepositRequired` is disabled. assert_eq!(AccountBalance::get(1, &account(1)), Some((1, (account(2), 0)))); assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); @@ -438,7 +438,8 @@ fn mint_should_work() { account(1), default_collection_config() )); - // Minting skips deposit as `DepositRequired` is disabled. + // Minting doesn't require a reserve because the collection's DepositRequired setting is + // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(AccountBalance::get(0, account(1)), Some((1, (account(1), 0)))); assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); From 911c5a92c9cb69fca44da328b0761a284be8afe1 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:56:23 +0700 Subject: [PATCH 17/40] chore: use balance deposit variable --- pallets/nfts/src/tests.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 71d11891..d05d4d30 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -255,6 +255,7 @@ fn basic_minting_should_work() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); assert_ok!(Nfts::create( @@ -287,10 +288,10 @@ fn lifecycle_should_work() { account(20), default_item_config() )); - assert_eq!(AccountBalance::get(0, account(20)), Some((1, (account(1), 1)))); + assert_eq!(AccountBalance::get(0, account(20)), Some((1, (account(1), balance_deposit)))); assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2 * balance_deposit); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); - assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 1)))); + assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), balance_deposit)))); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); @@ -299,7 +300,7 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3 * balance_deposit); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); assert!(!AccountBalance::contains_key(0, &account(1))); - assert_eq!(AccountBalance::get(0, account(2)), Some((1, (account(2), 1)))); + assert_eq!(AccountBalance::get(0, account(2)), Some((1, (account(2), balance_deposit)))); assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); @@ -3617,6 +3618,7 @@ fn pre_signed_mints_should_work() { let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); let user_2 = account(2); let user_3 = account(3); + let balance_deposit = balance_deposit(); Balances::make_free_balance_be(&user_0, 100); Balances::make_free_balance_be(&user_2, 100); @@ -3632,7 +3634,10 @@ fn pre_signed_mints_should_work() { signature.clone(), user_1.clone(), )); - assert_eq!(AccountBalance::get(0, user_2.clone()), Some((1, (user_2.clone(), 1)))); + assert_eq!( + AccountBalance::get(0, user_2.clone()), + Some((1, (user_2.clone(), balance_deposit))) + ); assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); let metadata = ItemMetadataOf::::get(0, 0).unwrap(); assert_eq!( @@ -3660,7 +3665,7 @@ fn pre_signed_mints_should_work() { assert_eq!(deposit.amount, 3); assert_eq!(Balances::free_balance(&user_0), 100 - 2 + 10); // 2 - collection deposit, 10 - mint price - assert_eq!(Balances::free_balance(&user_2), 100 - 1 - 1 - 3 - 6 - 10); // 1 - balance deposit, 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price + assert_eq!(Balances::free_balance(&user_2), 100 - balance_deposit - 1 - 3 - 6 - 10); // 1 - balance deposit, 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price assert_noop!( Nfts::mint_pre_signed( @@ -4196,7 +4201,7 @@ fn mint_requires_deposit_works() { )); assert_eq!( AccountBalance::get(collection_id, item_owner.clone()), - Some((1, (collection_owner.clone(), 1))) + Some((1, (collection_owner.clone(), balance_deposit))) ); assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); @@ -4214,7 +4219,7 @@ fn mint_requires_deposit_works() { )); assert_eq!( AccountBalance::get(collection_id, &item_owner), - Some((2, (collection_owner.clone(), 1))) + Some((2, (collection_owner.clone(), balance_deposit))) ); assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); @@ -4233,7 +4238,7 @@ fn mint_requires_deposit_works() { )); assert_eq!( AccountBalance::get(collection_id, &item_owner), - Some((1, (collection_owner.clone(), 1))) + Some((1, (collection_owner.clone(), balance_deposit))) ); assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); @@ -4302,7 +4307,7 @@ fn transfer_requires_deposit_works() { item_id, dest.clone() )); - assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), 1)))); + assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), balance_deposit)))); assert_eq!(Balances::reserved_balance(&owner), balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); @@ -4328,7 +4333,10 @@ fn transfer_requires_deposit_works() { item_id, dest.clone() )); - assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 1)))); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (owner.clone(), balance_deposit))) + ); assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); @@ -4353,7 +4361,10 @@ fn transfer_requires_deposit_works() { item_id, dest.clone() )); - assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (dest.clone(), 1)))); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (dest.clone(), balance_deposit))) + ); assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&dest), balance_deposit); assert!(Balances::usable_balance(&dest).is_zero()); From a16ecdc289bb285fbdc88072ee17d3d4a86370de Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:09:56 +0700 Subject: [PATCH 18/40] chore: update comments --- pallets/nfts/src/tests.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index d05d4d30..15284cc4 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -233,7 +233,7 @@ fn basic_minting_should_work() { default_collection_config() )); assert_eq!(collections(), vec![(account(1), 0)]); - // Minting doesn't require a reserve because the collection's DepositRequired setting is + // Minting doesn't require a reserve because the collection's `DepositRequired` setting is // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 0)))); @@ -439,7 +439,7 @@ fn mint_should_work() { account(1), default_collection_config() )); - // Minting doesn't require a reserve because the collection's DepositRequired setting is + // Minting doesn't require a reserve because the collection's `DepositRequired` setting is // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(AccountBalance::get(0, account(1)), Some((1, (account(1), 0)))); @@ -585,7 +585,8 @@ fn transfer_should_work() { default_item_config() )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); - // Transferring skips deposit as `DepositRequired` is disabled. + // Transferring doesn't require a reserve because the collection's `DepositRequired` is + // disabled. assert!(!AccountBalance::contains_key(0, account(2))); assert_eq!(AccountBalance::get(0, account(3)), Some((1, (account(2), 0)))); assert_eq!(Balances::reserved_balance(&account(2)), 0); From 02db7a55b2da6af0d2fe62483a144f3770a15aa7 Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:21:52 +0700 Subject: [PATCH 19/40] chore: apply suggestion --- pallets/nfts/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 15284cc4..be5ce92b 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -777,7 +777,7 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); // Reserve `balance_deposit` from account 2 and reserved balance of account 1 remains the // same. - assert_eq!(Balances::reserved_balance(&account(1)), balance_deposit); + assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); // 2's acceptance from before is reset when it became an owner, so it cannot be transferred From 78a381522f8a6aebffbf2e6d40d028762b99c76b Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 08:54:48 +0700 Subject: [PATCH 20/40] chore: apply review suggestions Co-authored-by: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> --- pallets/nfts/src/tests.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index be5ce92b..1334b32d 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -233,7 +233,7 @@ fn basic_minting_should_work() { default_collection_config() )); assert_eq!(collections(), vec![(account(1), 0)]); - // Minting doesn't require a reserve because the collection's `DepositRequired` setting is + // Minting doesn't require a deposit because the collection's `DepositRequired` setting is // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(AccountBalance::get(0, &account(1)), Some((1, (account(1), 0)))); @@ -439,7 +439,7 @@ fn mint_should_work() { account(1), default_collection_config() )); - // Minting doesn't require a reserve because the collection's `DepositRequired` setting is + // Minting doesn't require a deposit because the collection's `DepositRequired` setting is // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(AccountBalance::get(0, account(1)), Some((1, (account(1), 0)))); @@ -585,7 +585,7 @@ fn transfer_should_work() { default_item_config() )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(2)), 0, 42, account(3))); - // Transferring doesn't require a reserve because the collection's `DepositRequired` is + // Transferring doesn't require a deposit because the collection's `DepositRequired` is // disabled. assert!(!AccountBalance::contains_key(0, account(2))); assert_eq!(AccountBalance::get(0, account(3)), Some((1, (account(2), 0)))); @@ -775,8 +775,8 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&account(3)), 44); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); - // Reserve `balance_deposit` from account 2 and reserved balance of account 1 remains the - // same. + // The reserved balance of account 1 should remain the same. For account 2 the `balance_deposit` is + // reserved because it is the new item owner. assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); @@ -3666,7 +3666,7 @@ fn pre_signed_mints_should_work() { assert_eq!(deposit.amount, 3); assert_eq!(Balances::free_balance(&user_0), 100 - 2 + 10); // 2 - collection deposit, 10 - mint price - assert_eq!(Balances::free_balance(&user_2), 100 - balance_deposit - 1 - 3 - 6 - 10); // 1 - balance deposit, 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price + assert_eq!(Balances::free_balance(&user_2), 100 - balance_deposit - 1 - 3 - 6 - 10); // 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price assert_noop!( Nfts::mint_pre_signed( @@ -3841,7 +3841,7 @@ fn pre_signed_attributes_should_work() { assert_eq!(deposit.account, Some(user_2.clone())); assert_eq!(deposit.amount, 3); - assert_eq!(Balances::free_balance(&user_1), 100 - 2 - 1 - 1); // 2 - collection deposit, 1 - item deposit, 1 - balance deposit + assert_eq!(Balances::free_balance(&user_1), 100 - 2 - 1 - balance_deposit()); // 2 - collection deposit, 1 - item deposit assert_eq!(Balances::free_balance(&user_2), 100 - 6); // 6 - attributes // validate the deposit gets returned on attribute update from collection's owner From 26b43f30b818da4bf50b0823cd4ad56dbfb568a4 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:00:10 +0700 Subject: [PATCH 21/40] chore: revert back test case order --- pallets/nfts/src/tests.rs | 334 +++++++++++++++++++------------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 1334b32d..b104f70c 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -4163,6 +4163,173 @@ fn clear_collection_metadata_works() { }); } +#[test] +fn collection_item_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let total_items = 10; + let user_id = account(1); + + // No collection. + assert_eq!(Nfts::collection_items(collection_id), None); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id.clone(), + default_collection_config() + )); + + // Mint items and validate the total supply. + (0..total_items).into_iter().for_each(|i| { + assert_ok!(Nfts::force_mint( + RuntimeOrigin::root(), + collection_id, + i, + user_id.clone(), + ItemConfig::default() + )); + }); + assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); + }); +} + +#[test] +fn clear_collection_approvals_works() { + new_test_ext().execute_with(|| { + let balance = 100; + let collection_id = 0; + let item_id = 42; + let item_owner = account(1); + let delegates = 10..20; + + // Origin checks for the `clear_collection_approvals`. + for origin in [root(), none()] { + assert_noop!( + Nfts::clear_collection_approvals(origin, collection_id, 0), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + // Origin checks for the `force_clear_collection_approvals`. + for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { + assert_noop!( + Nfts::force_clear_collection_approvals( + origin, + item_owner.clone(), + collection_id, + 0 + ), + BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) + ); + } + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + item_owner.clone(), + default_collection_config() + )); + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + default_item_config() + )); + Balances::make_free_balance_be(&item_owner, balance); + + for (origin, maybe_owner) in [ + // Parameters for `clear_collection_approvals`. + (root(), Some(&item_owner)), + // Parameters for `force_clear_collection_approvals`. + (RuntimeOrigin::signed(item_owner.clone()), None), + ] { + // Approve delegates. + let mut approvals = 0u32; + for i in delegates.clone() { + assert_ok!(Nfts::approve_collection_transfer( + RuntimeOrigin::signed(item_owner.clone()), + collection_id, + account(i), + None + )); + approvals.saturating_inc(); + } + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), + approvals as usize + ); + assert!(!events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals + })); + + // Partially remove collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), + Ok(Some(WeightOf::clear_collection_approvals(1)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); + assert_eq!( + CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), + approvals as usize - 1 + ); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 1 + })); + + // Successfully remove all collection approvals. Only charges post-dispatch weight for + // the removed approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(9)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) + .count() + .is_zero()); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: approvals - 1 + })); + + // Remove zero collection approvals. + assert_eq!( + clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), + Ok(Some(WeightOf::clear_collection_approvals(0)).into()) + ); + assert_eq!(Balances::free_balance(&item_owner), balance); + assert!(events().contains(&Event::::ApprovalsCancelled { + collection: collection_id, + owner: item_owner.clone(), + approvals: 0 + })); + + // Ensure delegates are not able to transfer. + for i in delegates.clone() { + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(account(i)), + collection_id, + item_id, + account(5) + ), + Error::::NoPermission + ); + } + } + }); +} + #[test] fn mint_requires_deposit_works() { new_test_ext().execute_with(|| { @@ -4616,143 +4783,6 @@ fn cancel_collection_approval_works() { }); } -#[test] -fn clear_collection_approvals_works() { - new_test_ext().execute_with(|| { - let balance = 100; - let collection_id = 0; - let item_id = 42; - let item_owner = account(1); - let delegates = 10..20; - - // Origin checks for the `clear_collection_approvals`. - for origin in [root(), none()] { - assert_noop!( - Nfts::clear_collection_approvals(origin, collection_id, 0), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } - // Origin checks for the `force_clear_collection_approvals`. - for origin in [RuntimeOrigin::signed(item_owner.clone()), none()] { - assert_noop!( - Nfts::force_clear_collection_approvals( - origin, - item_owner.clone(), - collection_id, - 0 - ), - BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) - ); - } - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - item_owner.clone(), - default_collection_config() - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - Balances::make_free_balance_be(&item_owner, balance); - - for (origin, maybe_owner) in [ - // Parameters for `clear_collection_approvals`. - (root(), Some(&item_owner)), - // Parameters for `force_clear_collection_approvals`. - (RuntimeOrigin::signed(item_owner.clone()), None), - ] { - // Approve delegates. - let mut approvals = 0u32; - for i in delegates.clone() { - assert_ok!(Nfts::approve_collection_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - account(i), - None - )); - approvals.saturating_inc(); - } - - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner, collection_id, 0), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, &item_owner)).count(), - approvals as usize - ); - assert!(!events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals - })); - - // Partially remove collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 1), - Ok(Some(WeightOf::clear_collection_approvals(1)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance - (approvals as u64) + 1); - assert_eq!( - CollectionApprovals::iter_prefix((collection_id, item_owner.clone())).count(), - approvals as usize - 1 - ); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 1 - })); - - // Successfully remove all collection approvals. Only charges post-dispatch weight for - // the removed approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(9)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(CollectionApprovals::iter_prefix((collection_id, item_owner.clone())) - .count() - .is_zero()); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: approvals - 1 - })); - - // Remove zero collection approvals. - assert_eq!( - clear_collection_approvals(origin.clone(), maybe_owner.clone(), collection_id, 10), - Ok(Some(WeightOf::clear_collection_approvals(0)).into()) - ); - assert_eq!(Balances::free_balance(&item_owner), balance); - assert!(events().contains(&Event::::ApprovalsCancelled { - collection: collection_id, - owner: item_owner.clone(), - approvals: 0 - })); - - // Ensure delegates are not able to transfer. - for i in delegates.clone() { - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(account(i)), - collection_id, - item_id, - account(5) - ), - Error::::NoPermission - ); - } - } - }); -} - #[test] fn check_approval_without_deadline_works() { new_test_ext().execute_with(|| { @@ -4961,36 +4991,6 @@ fn check_approval_with_deadline_works() { }); } -#[test] -fn collection_item_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let total_items = 10; - let user_id = account(1); - - // No collection. - assert_eq!(Nfts::collection_items(collection_id), None); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id.clone(), - default_collection_config() - )); - - // Mint items and validate the total supply. - (0..total_items).into_iter().for_each(|i| { - assert_ok!(Nfts::force_mint( - RuntimeOrigin::root(), - collection_id, - i, - user_id.clone(), - ItemConfig::default() - )); - }); - assert_eq!(Nfts::collection_items(collection_id), Some(total_items)); - }); -} - #[test] fn increment_account_balance_works() { new_test_ext().execute_with(|| { From 8210e9ca30b0d9f2329cd5b564f84f20ff6c30ea Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:04:16 +0700 Subject: [PATCH 22/40] chore: format --- pallets/nfts/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index b104f70c..f53263e3 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -775,8 +775,8 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&account(3)), 44); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); - // The reserved balance of account 1 should remain the same. For account 2 the `balance_deposit` is - // reserved because it is the new item owner. + // The reserved balance of account 1 should remain the same. For account 2 the + // `balance_deposit` is reserved because it is the new item owner. assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); From ff8d560fdc179a26d8c561a8d8f831de6f444439 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 09:10:02 +0700 Subject: [PATCH 23/40] chore: remove redudant checks --- pallets/nfts/src/tests.rs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index f53263e3..31847062 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -280,6 +280,7 @@ fn lifecycle_should_work() { account(10), default_item_config() )); + assert!(!AccountBalance::contains_key(0, &account(1))); assert_eq!(Balances::reserved_balance(&account(1)), 6 + balance_deposit); assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), @@ -1198,8 +1199,6 @@ fn set_collection_system_attributes_should_work() { #[test] fn set_item_owner_attributes_should_work() { new_test_ext().execute_with(|| { - let balance_deposit = balance_deposit(); - Balances::make_free_balance_be(&account(1), 100); Balances::make_free_balance_be(&account(2), 100); Balances::make_free_balance_be(&account(3), 100); @@ -1352,7 +1351,7 @@ fn set_item_owner_attributes_should_work() { assert_eq!(deposit.account, Some(account(3))); assert_eq!(deposit.amount, 13); assert_eq!(Balances::reserved_balance(account(2)), 3); - assert_eq!(Balances::reserved_balance(account(3)), 13 + balance_deposit); + assert_eq!(Balances::reserved_balance(account(3)), 13 + balance_deposit()); // validate attributes on item deletion assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0)); @@ -1559,8 +1558,6 @@ fn validate_deposit_required_setting() { #[test] fn set_attribute_should_respect_lock() { new_test_ext().execute_with(|| { - let balance_deposit = balance_deposit(); - Balances::make_free_balance_be(&account(1), 100); assert_ok!(Nfts::force_create( @@ -1603,7 +1600,7 @@ fn set_attribute_should_respect_lock() { (Some(1), AttributeNamespace::CollectionOwner, bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(account(1)), 11 + balance_deposit); + assert_eq!(Balances::reserved_balance(account(1)), 11 + balance_deposit()); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(account(1)), 0, bvec![])); assert_ok!(Nfts::lock_collection( @@ -1857,7 +1854,6 @@ fn burn_works() { Some(account(4)), )); - // Throws error `Error::UnknownItem` if burning an unknown collection item. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), Error::::UnknownItem @@ -1878,16 +1874,6 @@ fn burn_works() { default_item_config() )); - // Throws error `Error::NoItemOwned` if the account does not have an entry in - // `AccountBalance`. - let account_balance = AccountBalance::take(0, account(5)); - assert_noop!( - Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42), - Error::::NoItemOwned - ); - AccountBalance::set(0, account(5), account_balance); - - // Throws error `Error::NoPermission`. assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission From 569aae7fed36e25acd15320296096466e4727445 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:52:56 +0700 Subject: [PATCH 24/40] chore: reorder --- pallets/nfts/src/tests.rs | 422 +++++++++++++++++++------------------- 1 file changed, 211 insertions(+), 211 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 31847062..ad7ffbaf 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -4316,217 +4316,6 @@ fn clear_collection_approvals_works() { }); } -#[test] -fn mint_requires_deposit_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let balance_deposit = balance_deposit(); - let mut item_id = 42; - let mut item_owner = account(2); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - collection_config_with_all_settings_enabled() - )); - - // Throws error `BalancesError::InsufficientBalance`. - assert_noop!( - Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - ), - BalancesError::::InsufficientBalance - ); - - Balances::make_free_balance_be(&collection_owner, 100); - // Minting reserves deposit from the collection owner. - { - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, item_owner.clone()), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - // Mint for accounts with a non-zero balance requires no additional reserves. - { - item_id = 43; - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((2, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - // Increase the reserved balance on minting for a new `AccountBalance` record. - { - item_id = 44; - item_owner = account(3); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - assert_eq!(collections(), vec![(account(1), 0)]); - assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); - }); -} - -#[test] -fn transfer_requires_deposit_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let balance_deposit = balance_deposit(); - let mut dest = account(3); - let mut item_id = 42; - let owner = account(2); - - Balances::make_free_balance_be(&account(1), 100); - Balances::make_free_balance_be(&account(2), 100); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - account(1), - collection_config_with_all_settings_enabled() - )); - - // Transfer an unknown collection item, throws error `Error::UnknownItem`. - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - ), - Error::::UnknownItem - ); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - collection_id, - item_id, - owner.clone(), - default_item_config() - )); - - // Throws error `NoItemOwned` since `owner` does not have an `AccountBalance` record - // created. - let account_balance = AccountBalance::take(collection_id, &owner); - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - ), - Error::::NoItemOwned - ); - AccountBalance::set(collection_id, &owner, account_balance); - - // Reserve funds from `owner` as `dest` does not exist. - { - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - )); - assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), balance_deposit)))); - assert_eq!(Balances::reserved_balance(&owner), balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), 0); - assert!(items().contains(&(dest, collection_id, item_id))); - } - - // Reserve funds from `owner` since `dest` lacks sufficient balance. - { - item_id = 43; - dest = account(4); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - collection_id, - item_id, - owner.clone(), - default_item_config() - )); - - Balances::make_free_balance_be(&dest, 1); - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - )); - assert_eq!( - AccountBalance::get(collection_id, &dest), - Some((1, (owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), 0); - assert!(items().contains(&(dest, collection_id, item_id))); - } - - // Reserves funds from `dest` since `dest` exists and has sufficient balance. - { - item_id = 44; - dest = account(5); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - collection_id, - item_id, - owner.clone(), - default_item_config() - )); - Balances::make_free_balance_be(&dest, 1 + balance_deposit); - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - )); - assert_eq!( - AccountBalance::get(collection_id, &dest), - Some((1, (dest.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), balance_deposit); - assert!(Balances::usable_balance(&dest).is_zero()); - assert!(items().contains(&(dest, collection_id, item_id))); - } - }); -} - #[test] fn approve_collection_transfer_works() { new_test_ext().execute_with(|| { @@ -4977,6 +4766,217 @@ fn check_approval_with_deadline_works() { }); } +#[test] +fn mint_requires_deposit_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let collection_owner = account(1); + let balance_deposit = balance_deposit(); + let mut item_id = 42; + let mut item_owner = account(2); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + collection_config_with_all_settings_enabled() + )); + + // Throws error `BalancesError::InsufficientBalance`. + assert_noop!( + Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + ), + BalancesError::::InsufficientBalance + ); + + Balances::make_free_balance_be(&collection_owner, 100); + // Minting reserves deposit from the collection owner. + { + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, item_owner.clone()), + Some((1, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + // Mint for accounts with a non-zero balance requires no additional reserves. + { + item_id = 43; + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((2, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + // Increase the reserved balance on minting for a new `AccountBalance` record. + { + item_id = 44; + item_owner = account(3); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((1, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + assert_eq!(collections(), vec![(account(1), 0)]); + assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); + }); +} + +#[test] +fn transfer_requires_deposit_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let balance_deposit = balance_deposit(); + let mut dest = account(3); + let mut item_id = 42; + let owner = account(2); + + Balances::make_free_balance_be(&account(1), 100); + Balances::make_free_balance_be(&account(2), 100); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + collection_config_with_all_settings_enabled() + )); + + // Transfer an unknown collection item, throws error `Error::UnknownItem`. + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + ), + Error::::UnknownItem + ); + + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + collection_id, + item_id, + owner.clone(), + default_item_config() + )); + + // Throws error `NoItemOwned` since `owner` does not have an `AccountBalance` record + // created. + let account_balance = AccountBalance::take(collection_id, &owner); + assert_noop!( + Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + ), + Error::::NoItemOwned + ); + AccountBalance::set(collection_id, &owner, account_balance); + + // Reserve funds from `owner` as `dest` does not exist. + { + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), balance_deposit)))); + assert_eq!(Balances::reserved_balance(&owner), balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), 0); + assert!(items().contains(&(dest, collection_id, item_id))); + } + + // Reserve funds from `owner` since `dest` lacks sufficient balance. + { + item_id = 43; + dest = account(4); + + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + collection_id, + item_id, + owner.clone(), + default_item_config() + )); + + Balances::make_free_balance_be(&dest, 1); + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), 0); + assert!(items().contains(&(dest, collection_id, item_id))); + } + + // Reserves funds from `dest` since `dest` exists and has sufficient balance. + { + item_id = 44; + dest = account(5); + + assert_ok!(Nfts::force_mint( + RuntimeOrigin::signed(account(1)), + collection_id, + item_id, + owner.clone(), + default_item_config() + )); + Balances::make_free_balance_be(&dest, 1 + balance_deposit); + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(owner.clone()), + collection_id, + item_id, + dest.clone() + )); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (dest.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), balance_deposit); + assert!(Balances::usable_balance(&dest).is_zero()); + assert!(items().contains(&(dest, collection_id, item_id))); + } + }); +} + #[test] fn increment_account_balance_works() { new_test_ext().execute_with(|| { From 40a8bd846f34a39598becab9d1bc88236e4009d7 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 14:32:33 +0700 Subject: [PATCH 25/40] chore: rebase --- pallets/nfts/src/tests.rs | 55 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index bb190ae6..9339aa7b 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -39,7 +39,6 @@ use crate::{mock::*, Event, SystemConfig, *}; type AccountBalance = crate::AccountBalance; type AccountIdOf = ::AccountId; -type AccountBalance = crate::AccountBalance; type CollectionApprovals = crate::CollectionApprovals; type CollectionApprovalDeposit = ::CollectionApprovalDeposit; type CollectionId = ::CollectionId; @@ -171,9 +170,8 @@ fn clear_collection_approvals( limit: u32, ) -> DispatchResultWithPostInfo { match maybe_owner { - Some(owner) => { - Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit) - }, + Some(owner) => + Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit), None => Nfts::clear_collection_approvals(origin, collection, limit), } } @@ -193,9 +191,8 @@ fn approve_collection_transfer( delegate.clone(), maybe_deadline, ), - None => { - Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline) - }, + None => + Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline), } } @@ -3265,9 +3262,9 @@ fn collection_locking_should_work() { let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); let full_lock_config = collection_config_from_disabled_settings( - CollectionSetting::TransferableItems - | CollectionSetting::UnlockedMetadata - | CollectionSetting::UnlockedAttributes, + CollectionSetting::TransferableItems | + CollectionSetting::UnlockedMetadata | + CollectionSetting::UnlockedAttributes, ); assert_eq!(stored_config, full_lock_config); }); @@ -4879,11 +4876,13 @@ fn mint_should_update_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; let owner = account(1); + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&owner, 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), owner.clone(), - default_collection_config() + collection_config_with_all_settings_enabled() )); assert_ok!(Nfts::mint( @@ -4893,7 +4892,10 @@ fn mint_should_update_account_balance_works() { owner.clone(), None )); - assert_eq!(AccountBalance::get(collection_id, &owner), 1); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (owner.clone(), balance_deposit))) + ); // Additive. assert_ok!(Nfts::mint( @@ -4903,17 +4905,23 @@ fn mint_should_update_account_balance_works() { owner.clone(), None )); - assert_eq!(AccountBalance::get(collection_id, &owner), 2); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((2, (owner.clone(), balance_deposit))) + ); // Mint with witness data. assert_ok!(Nfts::mint( - RuntimeOrigin::signed(account(1)), + RuntimeOrigin::signed(owner.clone()), collection_id, 44, account(2), Some(MintWitness { mint_price: Some(1), ..Default::default() }) )); - assert_eq!(AccountBalance::get(collection_id, account(2)), 1); + assert_eq!( + AccountBalance::get(collection_id, account(2)), + Some((1, (owner, balance_deposit))) + ); }); } @@ -4945,7 +4953,10 @@ fn burn_should_update_account_balance_works() { )); assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 42)); - assert_eq!(AccountBalance::get(collection_id, &owner), 1); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (owner.clone(), balance_deposit()))) + ); assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 43)); assert!(!AccountBalance::contains_key(collection_id, &owner)); @@ -4979,7 +4990,7 @@ fn transfer_should_update_account_balance_works() { dest.clone() )); assert!(!AccountBalance::contains_key(collection_id, &owner)); - assert_eq!(AccountBalance::get(collection_id, &dest), 1); + assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 0)))); assert_ok!(Nfts::approve_transfer( RuntimeOrigin::signed(dest.clone()), @@ -4995,7 +5006,7 @@ fn transfer_should_update_account_balance_works() { account(4) )); assert!(!AccountBalance::contains_key(collection_id, &dest)); - assert_eq!(AccountBalance::get(collection_id, account(4)), 1); + assert_eq!(AccountBalance::get(collection_id, account(4)), Some((1, (owner, 0)))); }); } @@ -5053,8 +5064,8 @@ fn claim_swap_should_update_account_balance_works() { item_1, Some(price_with_direction), )); - assert_eq!(AccountBalance::get(collection_id, &user_1), 1); - assert_eq!(AccountBalance::get(collection_id, &user_2), 1); + assert_eq!(AccountBalance::get(collection_id, &user_1), Some((1, (user_1, 0)))); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); }); } @@ -5099,8 +5110,8 @@ fn buy_item_should_update_account_balance_works() { item_1, price_1 + 1, )); - assert_eq!(AccountBalance::get(collection_id, &user_1), 0); - assert_eq!(AccountBalance::get(collection_id, &user_2), 1); + assert!(!AccountBalance::contains_key(collection_id, &user_1)); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); }); } From 3d1e367144e4f12209f248a8443e224c8443d77f Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 14:37:42 +0700 Subject: [PATCH 26/40] chore: update tests --- pallets/nfts/src/tests.rs | 222 ++++++++++++++++++++++---------------- 1 file changed, 130 insertions(+), 92 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 9339aa7b..a90c0399 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -3485,7 +3485,6 @@ fn pre_signed_mints_should_work() { let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); let user_2 = account(2); let user_3 = account(3); - let balance_deposit = balance_deposit(); Balances::make_free_balance_be(&user_0, 100); Balances::make_free_balance_be(&user_2, 100); @@ -3501,10 +3500,6 @@ fn pre_signed_mints_should_work() { signature.clone(), user_1.clone(), )); - assert_eq!( - AccountBalance::get(0, user_2.clone()), - Some((1, (user_2.clone(), balance_deposit))) - ); assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); let metadata = ItemMetadataOf::::get(0, 0).unwrap(); assert_eq!( @@ -4784,93 +4779,6 @@ fn check_approval_with_deadline_works() { }); } -#[test] -fn mint_requires_deposit_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let balance_deposit = balance_deposit(); - let mut item_id = 42; - let mut item_owner = account(2); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - collection_config_with_all_settings_enabled() - )); - - // Throws error `BalancesError::InsufficientBalance`. - assert_noop!( - Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - ), - BalancesError::::InsufficientBalance - ); - - Balances::make_free_balance_be(&collection_owner, 100); - // Minting reserves deposit from the collection owner. - { - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, item_owner.clone()), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - // Mint for accounts with a non-zero balance requires no additional reserves. - { - item_id = 43; - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((2, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - // Increase the reserved balance on minting for a new `AccountBalance` record. - { - item_id = 44; - item_owner = account(3); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - assert_eq!(collections(), vec![(account(1), 0)]); - assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); - }); -} - #[test] fn mint_should_update_account_balance_works() { new_test_ext().execute_with(|| { @@ -5161,6 +5069,136 @@ fn destroy_with_collection_approvals_returns_error() { }); } +#[test] +fn mint_requires_deposit_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let collection_owner = account(1); + let balance_deposit = balance_deposit(); + let mut item_id = 42; + let mut item_owner = account(2); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + collection_owner.clone(), + collection_config_with_all_settings_enabled() + )); + + // Throws error `BalancesError::InsufficientBalance`. + assert_noop!( + Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + ), + BalancesError::::InsufficientBalance + ); + + Balances::make_free_balance_be(&collection_owner, 100); + // Minting reserves deposit from the collection owner. + { + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, item_owner.clone()), + Some((1, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + // Mint for accounts with a non-zero balance requires no additional reserves. + { + item_id = 43; + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((2, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + // Increase the reserved balance on minting for a new `AccountBalance` record. + { + item_id = 44; + item_owner = account(3); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(collection_owner.clone()), + collection_id, + item_id, + item_owner.clone(), + None + )); + assert_eq!( + AccountBalance::get(collection_id, &item_owner), + Some((1, (collection_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); + assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); + } + + assert_eq!(collections(), vec![(account(1), 0)]); + assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); + }); +} + +#[test] +fn pre_signed_mint_requires_deposit_works() { + new_test_ext().execute_with(|| { + let user_0 = account(0); + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], + metadata: vec![0, 1], + only_account: None, + deadline: 10000000, + mint_price: Some(10), + }; + let message = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); + let user_2 = account(2); + let balance_deposit = balance_deposit(); + + Balances::make_free_balance_be(&user_0, 100); + Balances::make_free_balance_be(&user_2, 100); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(user_0.clone()), + user_1.clone(), + collection_config_with_all_settings_enabled(), + )); + + assert_ok!(Nfts::mint_pre_signed( + RuntimeOrigin::signed(user_2.clone()), + Box::new(mint_data.clone()), + signature.clone(), + user_1.clone(), + )); + assert_eq!( + AccountBalance::get(0, user_2.clone()), + Some((1, (user_2.clone(), balance_deposit))) + ); + assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); + }) +} + #[test] fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { From b6c2529be43c9cb8e9467543d145ac1e615e44db Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:02:05 +0700 Subject: [PATCH 27/40] chore: apply suggestion --- pallets/nfts/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index a90c0399..1b56efd0 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -749,7 +749,7 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); // The reserved balance of account 1 should remain the same. For account 2 the - // `balance_deposit` is reserved because it is the new item owner. + // `balance_deposit` is reserved because it is the new item owner. assert_eq!(Balances::reserved_balance(&account(1)), 1); assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); From e1a8c4c07b9d5c3ea71512ddf598bff930c1070e Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:31:14 +0700 Subject: [PATCH 28/40] chore: test --- pallets/nfts/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 1b56efd0..d389a8e0 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -1845,13 +1845,13 @@ fn burn_works() { account(5), default_item_config() )); + assert_eq!(Balances::reserved_balance(account(1)), 2 + balance_deposit); assert_noop!( Nfts::burn(RuntimeOrigin::signed(account(0)), 0, 42), Error::::NoPermission ); - assert_eq!(Balances::reserved_balance(account(1)), 2 + balance_deposit); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); // Burn an item unreserving the item deposit while retaining the reservation for `balance // deposit`. @@ -3527,7 +3527,7 @@ fn pre_signed_mints_should_work() { assert_eq!(deposit.amount, 3); assert_eq!(Balances::free_balance(&user_0), 100 - 2 + 10); // 2 - collection deposit, 10 - mint price - assert_eq!(Balances::free_balance(&user_2), 100 - balance_deposit - 1 - 3 - 6 - 10); // 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price + assert_eq!(Balances::free_balance(&user_2), 100 - balance_deposit() - 1 - 3 - 6 - 10); // 1 - item deposit, 3 - metadata, 6 - attributes, 10 - mint price assert_noop!( Nfts::mint_pre_signed( @@ -5195,7 +5195,7 @@ fn pre_signed_mint_requires_deposit_works() { AccountBalance::get(0, user_2.clone()), Some((1, (user_2.clone(), balance_deposit))) ); - assert_eq!(items(), vec![(user_2.clone(), 0, 0)]); + assert_eq!(items(), vec![(user_2, 0, 0)]); }) } From e2aedbaf6aeed1ea61eb3fb8db44c3e78071b1e8 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 08:40:36 +0700 Subject: [PATCH 29/40] chore: update tests --- pallets/nfts/src/tests.rs | 133 ++++++++------------------------------ 1 file changed, 26 insertions(+), 107 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index d389a8e0..cc63559c 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -37,8 +37,8 @@ use sp_runtime::{ use crate::{mock::*, Event, SystemConfig, *}; -type AccountBalance = crate::AccountBalance; type AccountIdOf = ::AccountId; +type AccountBalance = crate::AccountBalance; type CollectionApprovals = crate::CollectionApprovals; type CollectionApprovalDeposit = ::CollectionApprovalDeposit; type CollectionId = ::CollectionId; @@ -5069,6 +5069,9 @@ fn destroy_with_collection_approvals_returns_error() { }); } +#[test] +fn burn_removes_deposit_works() {} + #[test] fn mint_requires_deposit_works() { new_test_ext().execute_with(|| { @@ -5076,7 +5079,7 @@ fn mint_requires_deposit_works() { let collection_owner = account(1); let balance_deposit = balance_deposit(); let mut item_id = 42; - let mut item_owner = account(2); + let item_owner = account(2); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), @@ -5132,133 +5135,50 @@ fn mint_requires_deposit_works() { assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); } - // Increase the reserved balance on minting for a new `AccountBalance` record. - { - item_id = 44; - item_owner = account(3); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 3 + 2 * balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - assert_eq!(collections(), vec![(account(1), 0)]); - assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43), (account(3), 0, 44)]); + assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43)]); }); } -#[test] -fn pre_signed_mint_requires_deposit_works() { - new_test_ext().execute_with(|| { - let user_0 = account(0); - let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); - let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); - let user_1 = user_1_signer.clone().into_account(); - let mint_data = PreSignedMint { - collection: 0, - item: 0, - attributes: vec![(vec![0], vec![1]), (vec![2], vec![3])], - metadata: vec![0, 1], - only_account: None, - deadline: 10000000, - mint_price: Some(10), - }; - let message = Encode::encode(&mint_data); - let signature = MultiSignature::Sr25519(user_1_pair.sign(&message)); - let user_2 = account(2); - let balance_deposit = balance_deposit(); - - Balances::make_free_balance_be(&user_0, 100); - Balances::make_free_balance_be(&user_2, 100); - assert_ok!(Nfts::create( - RuntimeOrigin::signed(user_0.clone()), - user_1.clone(), - collection_config_with_all_settings_enabled(), - )); - - assert_ok!(Nfts::mint_pre_signed( - RuntimeOrigin::signed(user_2.clone()), - Box::new(mint_data.clone()), - signature.clone(), - user_1.clone(), - )); - assert_eq!( - AccountBalance::get(0, user_2.clone()), - Some((1, (user_2.clone(), balance_deposit))) - ); - assert_eq!(items(), vec![(user_2, 0, 0)]); - }) -} - #[test] fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { let collection_id = 0; + let collection_owner = account(1); let balance_deposit = balance_deposit(); let mut dest = account(3); let mut item_id = 42; - let owner = account(2); + let item_owner = account(2); - Balances::make_free_balance_be(&account(1), 100); - Balances::make_free_balance_be(&account(2), 100); + Balances::make_free_balance_be(&collection_owner, 100); + Balances::make_free_balance_be(&item_owner, 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), - account(1), + collection_owner.clone(), collection_config_with_all_settings_enabled() )); - // Transfer an unknown collection item, throws error `Error::UnknownItem`. - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - ), - Error::::UnknownItem - ); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), + RuntimeOrigin::signed(collection_owner.clone()), collection_id, item_id, - owner.clone(), + item_owner.clone(), default_item_config() )); - // Throws error `NoItemOwned` since `owner` does not have an `AccountBalance` record - // created. - let account_balance = AccountBalance::take(collection_id, &owner); - assert_noop!( - Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - dest.clone() - ), - Error::::NoItemOwned - ); - AccountBalance::set(collection_id, &owner, account_balance); - // Reserve funds from `owner` as `dest` does not exist. { assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), + RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id, dest.clone() )); - assert_eq!(AccountBalance::get(0, &dest), Some((1, (owner.clone(), balance_deposit)))); - assert_eq!(Balances::reserved_balance(&owner), balance_deposit); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (item_owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&item_owner), balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -5272,22 +5192,22 @@ fn transfer_requires_deposit_works() { RuntimeOrigin::signed(account(1)), collection_id, item_id, - owner.clone(), + item_owner.clone(), default_item_config() )); Balances::make_free_balance_be(&dest, 1); assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), + RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id, dest.clone() )); assert_eq!( AccountBalance::get(collection_id, &dest), - Some((1, (owner.clone(), balance_deposit))) + Some((1, (item_owner.clone(), balance_deposit))) ); - assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -5301,12 +5221,12 @@ fn transfer_requires_deposit_works() { RuntimeOrigin::signed(account(1)), collection_id, item_id, - owner.clone(), + item_owner.clone(), default_item_config() )); Balances::make_free_balance_be(&dest, 1 + balance_deposit); assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), + RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id, dest.clone() @@ -5315,9 +5235,8 @@ fn transfer_requires_deposit_works() { AccountBalance::get(collection_id, &dest), Some((1, (dest.clone(), balance_deposit))) ); - assert_eq!(Balances::reserved_balance(&owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); assert_eq!(Balances::reserved_balance(&dest), balance_deposit); - assert!(Balances::usable_balance(&dest).is_zero()); assert!(items().contains(&(dest, collection_id, item_id))); } }); From 1a805f5c2ac5c5766f35de5eaf5d43352f3264eb Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 08:43:31 +0700 Subject: [PATCH 30/40] chore: update tests --- pallets/nfts/src/tests.rs | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index f7a48b3c..ea98b2c7 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -230,7 +230,6 @@ fn basic_minting_should_work() { )); assert_eq!(collections(), vec![(account(1), 0)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); - assert_eq!(AccountBalance::get(0, &account(1)), 1); assert_eq!(items(), vec![(account(1), 0, 42)]); assert_ok!(Nfts::force_create( @@ -240,7 +239,6 @@ fn basic_minting_should_work() { )); assert_eq!(collections(), vec![(account(1), 0), (account(2), 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(2)), 1, 69, account(1), None)); - assert_eq!(AccountBalance::get(1, &account(1)), 1); assert_eq!(items(), vec![(account(1), 0, 42), (account(1), 1, 69)]); }); } @@ -280,10 +278,8 @@ fn lifecycle_should_work() { account(20), default_item_config() )); - assert_eq!(AccountBalance::get(0, account(20)), 1); assert_eq!(Balances::reserved_balance(&account(1)), 7); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); - assert_eq!(AccountBalance::get(0, &account(1)), 1); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); @@ -291,8 +287,6 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&account(1)), 8); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert!(!AccountBalance::contains_key(0, &account(1))); - assert_eq!(AccountBalance::get(0, account(2)), 1); assert_eq!(Balances::reserved_balance(&account(1)), 8); assert_eq!(Balances::reserved_balance(&account(2)), 0); @@ -320,9 +314,7 @@ fn lifecycle_should_work() { bvec![0], )); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(10)), 0, 42)); - assert!(!AccountBalance::contains_key(0, account(10))); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(20)), 0, 69)); - assert!(!AccountBalance::contains_key(0, account(20))); assert_ok!(Nfts::burn(RuntimeOrigin::root(), 0, 70)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -330,7 +322,6 @@ fn lifecycle_should_work() { assert_eq!(w.item_metadatas, 0); assert_eq!(w.item_configs, 0); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(account(1)), 0, w)); - assert!(!AccountBalance::contains_key(0, &account(1))); assert_eq!(Balances::reserved_balance(&account(1)), 0); assert!(!Collection::::contains_key(0)); @@ -4783,16 +4774,6 @@ fn mint_should_update_account_balance_works() { None )); assert_eq!(AccountBalance::get(collection_id, &owner), 2); - - // Mint with witness data. - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(account(1)), - collection_id, - 44, - account(2), - Some(MintWitness { mint_price: Some(1), ..Default::default() }) - )); - assert_eq!(AccountBalance::get(collection_id, account(2)), 1); }); } @@ -4859,22 +4840,6 @@ fn transfer_should_update_account_balance_works() { )); assert!(!AccountBalance::contains_key(collection_id, &owner)); assert_eq!(AccountBalance::get(collection_id, &dest), 1); - - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(dest.clone()), - collection_id, - item_id, - owner.clone(), - None - )); - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(owner.clone()), - collection_id, - item_id, - account(4) - )); - assert!(!AccountBalance::contains_key(collection_id, &dest)); - assert_eq!(AccountBalance::get(collection_id, account(4)), 1); }); } From d3b314185b1a76680facd47ae273e21b866edd5c Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 08:50:32 +0700 Subject: [PATCH 31/40] chore: reformat --- pallets/nfts/src/tests.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 4e35e792..65ad205d 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -170,9 +170,8 @@ fn clear_collection_approvals( limit: u32, ) -> DispatchResultWithPostInfo { match maybe_owner { - Some(owner) => { - Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit) - }, + Some(owner) => + Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit), None => Nfts::clear_collection_approvals(origin, collection, limit), } } @@ -192,9 +191,8 @@ fn approve_collection_transfer( delegate.clone(), maybe_deadline, ), - None => { - Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline) - }, + None => + Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline), } } @@ -3255,9 +3253,9 @@ fn collection_locking_should_work() { let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); let full_lock_config = collection_config_from_disabled_settings( - CollectionSetting::TransferableItems - | CollectionSetting::UnlockedMetadata - | CollectionSetting::UnlockedAttributes, + CollectionSetting::TransferableItems | + CollectionSetting::UnlockedMetadata | + CollectionSetting::UnlockedAttributes, ); assert_eq!(stored_config, full_lock_config); }); From 7820d792a27598797b8d620dc3489bfd7e01086b Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 09:01:54 +0700 Subject: [PATCH 32/40] chore: update tests --- pallets/nfts/src/tests.rs | 53 ++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 65ad205d..ca17ffde 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -5031,9 +5031,6 @@ fn destroy_with_collection_approvals_returns_error() { }); } -#[test] -fn burn_removes_deposit_works() {} - #[test] fn mint_requires_deposit_works() { new_test_ext().execute_with(|| { @@ -5049,18 +5046,6 @@ fn mint_requires_deposit_works() { collection_config_with_all_settings_enabled() )); - // Throws error `BalancesError::InsufficientBalance`. - assert_noop!( - Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - ), - BalancesError::::InsufficientBalance - ); - Balances::make_free_balance_be(&collection_owner, 100); // Minting reserves deposit from the collection owner. { @@ -5102,6 +5087,44 @@ fn mint_requires_deposit_works() { }); } +#[test] +fn burn_removes_deposit_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let owner = account(1); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + owner.clone(), + collection_config_with_all_settings_enabled() + )); + + Balances::make_free_balance_be(&owner, 100); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(owner.clone()), + collection_id, + 42, + owner.clone(), + None + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(owner.clone()), + collection_id, + 43, + owner.clone(), + None + )); + + assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 42)); + assert_eq!(Balances::reserved_balance(&owner), 1 + balance_deposit()); + assert_eq!(items(), vec![(owner.clone(), 0, 43)]); + + assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 43)); + assert_eq!(Balances::reserved_balance(&owner), 0); + assert_eq!(items(), vec![]); + }); +} + #[test] fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { From df965e1833fdd3c6166fe9c8baf29ca54c0f66cc Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 09:01:54 +0700 Subject: [PATCH 33/40] chore: update tests --- pallets/nfts/src/tests.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index ca17ffde..0023b762 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -233,8 +233,6 @@ fn basic_minting_should_work() { default_collection_config() )); assert_eq!(collections(), vec![(account(1), 0)]); - // Minting doesn't require a deposit because the collection's `DepositRequired` setting is - // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(items(), vec![(account(1), 0, 42)]); @@ -278,7 +276,6 @@ fn lifecycle_should_work() { account(10), default_item_config() )); - assert!(!AccountBalance::contains_key(0, &account(1))); assert_eq!(Balances::reserved_balance(&account(1)), 6 + balance_deposit); assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(account(1)), @@ -287,7 +284,7 @@ fn lifecycle_should_work() { account(20), default_item_config() )); - assert_eq!(Balances::reserved_balance(&account(1)), 7); + assert_eq!(Balances::reserved_balance(&account(1)), 7 + 2 * balance_deposit); assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 70, account(1), None)); assert_eq!(items(), vec![(account(1), 0, 70), (account(10), 0, 42), (account(20), 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 3); @@ -296,8 +293,8 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3 * balance_deposit); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert_eq!(Balances::reserved_balance(&account(1)), 8); - assert_eq!(Balances::reserved_balance(&account(2)), 0); + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2 * balance_deposit); From 11590742bd05d5884a2a8c5748e2daf72d244d34 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:08:57 +0700 Subject: [PATCH 34/40] chore: format --- pallets/nfts/src/tests.rs | 45 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 4247bfcf..c525497d 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -170,9 +170,8 @@ fn clear_collection_approvals( limit: u32, ) -> DispatchResultWithPostInfo { match maybe_owner { - Some(owner) => { - Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit) - }, + Some(owner) => + Nfts::force_clear_collection_approvals(origin, owner.clone(), collection, limit), None => Nfts::clear_collection_approvals(origin, collection, limit), } } @@ -192,9 +191,8 @@ fn approve_collection_transfer( delegate.clone(), maybe_deadline, ), - None => { - Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline) - }, + None => + Nfts::approve_collection_transfer(origin, collection, delegate.clone(), maybe_deadline), } } @@ -588,11 +586,13 @@ fn mint_should_update_account_balance_works() { new_test_ext().execute_with(|| { let collection_id = 0; let owner = account(1); + let balance_deposit = balance_deposit(); + Balances::make_free_balance_be(&owner, 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), owner.clone(), - default_collection_config() + collection_config_with_all_settings_enabled() )); assert_ok!(Nfts::mint( @@ -602,7 +602,10 @@ fn mint_should_update_account_balance_works() { owner.clone(), None )); - assert_eq!(AccountBalance::get(collection_id, &owner), 1); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (owner.clone(), balance_deposit))) + ); // Additive. assert_ok!(Nfts::mint( @@ -612,7 +615,10 @@ fn mint_should_update_account_balance_works() { owner.clone(), None )); - assert_eq!(AccountBalance::get(collection_id, &owner), 2); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((2, (owner.clone(), balance_deposit))) + ); }); } @@ -756,7 +762,7 @@ fn transfer_should_update_account_balance_works() { dest.clone() )); assert!(!AccountBalance::contains_key(collection_id, &owner)); - assert_eq!(AccountBalance::get(collection_id, &dest), 1); + assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 0)))); }); } @@ -2146,7 +2152,10 @@ fn burn_should_update_account_balance_works() { )); assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 42)); - assert_eq!(AccountBalance::get(collection_id, &owner), 1); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (owner.clone(), balance_deposit()))) + ); assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 43)); assert!(!AccountBalance::contains_key(collection_id, &owner)); @@ -3206,8 +3215,8 @@ fn buy_item_should_update_account_balance_works() { item_1, price_1 + 1, )); - assert_eq!(AccountBalance::get(collection_id, &user_1), 0); - assert_eq!(AccountBalance::get(collection_id, &user_2), 1); + assert!(!AccountBalance::contains_key(collection_id, &user_1)); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); }); } @@ -3683,8 +3692,8 @@ fn claim_swap_should_update_account_balance_works() { item_1, Some(price_with_direction), )); - assert_eq!(AccountBalance::get(collection_id, &user_1), 1); - assert_eq!(AccountBalance::get(collection_id, &user_2), 1); + assert_eq!(AccountBalance::get(collection_id, &user_1), Some((1, (user_1, 0)))); + assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); }); } @@ -3763,9 +3772,9 @@ fn collection_locking_should_work() { let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); let full_lock_config = collection_config_from_disabled_settings( - CollectionSetting::TransferableItems - | CollectionSetting::UnlockedMetadata - | CollectionSetting::UnlockedAttributes, + CollectionSetting::TransferableItems | + CollectionSetting::UnlockedMetadata | + CollectionSetting::UnlockedAttributes, ); assert_eq!(stored_config, full_lock_config); }); From c5fd9e3b56a3382e852ce7248f8bce534691c142 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Mon, 6 Jan 2025 11:50:43 +0700 Subject: [PATCH 35/40] chore: resolve review comments --- pallets/nfts/src/benchmarking.rs | 10 +++++----- pallets/nfts/src/common_functions.rs | 2 +- pallets/nfts/src/features/approvals.rs | 2 +- pallets/nfts/src/features/create_delete_item.rs | 5 +++-- pallets/nfts/src/features/transfer.rs | 15 ++++++++------- pallets/nfts/src/lib.rs | 5 +++-- pallets/nfts/src/mock.rs | 2 +- pallets/nfts/src/tests.rs | 2 +- runtime/devnet/src/config/assets.rs | 6 +++--- runtime/testnet/src/config/assets.rs | 6 +++--- 10 files changed, 29 insertions(+), 26 deletions(-) diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index 481fbe17..4dfc3b3e 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -274,7 +274,7 @@ benchmarks_instance_pallet! { mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); - T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::CollectionBalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, None) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); @@ -283,7 +283,7 @@ benchmarks_instance_pallet! { force_mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); - T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance() + T::CollectionDeposit::get() + T::CollectionBalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, default_item_config()) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); @@ -303,7 +303,7 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::CollectionBalanceDeposit::get()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) verify { assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); @@ -669,7 +669,7 @@ benchmarks_instance_pallet! { let price = ItemPrice::::from(0u32); let origin = SystemOrigin::Signed(seller.clone()).into(); Nfts::::set_price(origin, collection, item, Some(price), Some(buyer_lookup))?; - T::Currency::make_free_balance_be(&buyer, T::Currency::minimum_balance() + price + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&buyer, T::Currency::minimum_balance() + price + T::CollectionBalanceDeposit::get()); }: _(SystemOrigin::Signed(buyer.clone()), collection, item, price) verify { assert_last_event::(Event::ItemBought { @@ -759,7 +759,7 @@ benchmarks_instance_pallet! { let duration = T::MaxDeadlineDuration::get(); let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::BalanceDeposit::get()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::CollectionBalanceDeposit::get()); let origin = SystemOrigin::Signed(caller.clone()); frame_system::Pallet::::set_block_number(One::one()); Nfts::::transfer(origin.clone().into(), collection, item2, target_lookup)?; diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 024e2d4e..904a2334 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -129,7 +129,7 @@ impl, I: 'static> Pallet { maybe_balance.as_mut().ok_or(Error::::NoItemOwned)?; *balance = balance.checked_sub(1).ok_or(ArithmeticError::Underflow)?; - if *balance == 0 { + if balance.is_zero() { T::Currency::unreserve(deposit_account, *deposit_amount); *maybe_balance = None; } diff --git a/pallets/nfts/src/features/approvals.rs b/pallets/nfts/src/features/approvals.rs index 9021aac1..fd1e6ae4 100644 --- a/pallets/nfts/src/features/approvals.rs +++ b/pallets/nfts/src/features/approvals.rs @@ -218,7 +218,7 @@ impl, I: 'static> Pallet { ); ensure!( AccountBalance::::get(collection, &owner) - .filter(|(balance, _)| *balance > 0) + .filter(|(balance, _)| !balance.is_zero()) .is_some(), Error::::NoItemOwned ); diff --git a/pallets/nfts/src/features/create_delete_item.rs b/pallets/nfts/src/features/create_delete_item.rs index 830cfe4e..5c687d42 100644 --- a/pallets/nfts/src/features/create_delete_item.rs +++ b/pallets/nfts/src/features/create_delete_item.rs @@ -74,8 +74,9 @@ impl, I: 'static> Pallet { let deposit_account = maybe_depositor.unwrap_or_else(|| collection_details.owner.clone()); - let balance_deposit = - deposit_required.then_some(T::BalanceDeposit::get()).unwrap_or_default(); + let balance_deposit = deposit_required + .then_some(T::CollectionBalanceDeposit::get()) + .unwrap_or_default(); Self::increment_account_balance( collection, &mint_to, diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 0549a617..28b30e27 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -94,15 +94,16 @@ impl, I: 'static> Pallet { // Update account balance of the destination account. let deposit_amount = match collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { - true => T::BalanceDeposit::get(), + true => T::CollectionBalanceDeposit::get(), false => Zero::zero(), }; - // The destination account covers the `BalanceDeposit` if it has sufficient balance. - // Otherwise, the caller is accountable for it. - let deposit_account = match T::Currency::can_reserve(&dest, T::BalanceDeposit::get()) { - true => &dest, - false => caller, - }; + // The destination account covers the `CollectionBalanceDeposit` if it has sufficient + // balance. Otherwise, the caller is accountable for it. + let deposit_account = + match T::Currency::can_reserve(&dest, T::CollectionBalanceDeposit::get()) { + true => &dest, + false => caller, + }; Self::increment_account_balance(collection, &dest, (deposit_account, deposit_amount))?; diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 13510100..1b1174c5 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -260,11 +260,11 @@ pub mod pallet { #[pallet::constant] type CollectionApprovalDeposit: Get>; - /// The basic amount of funds that must be reversed for an account balance. + /// The basic amount of funds that must be reversed for an account's collection balance. // Key: `sizeof((CollectionId, AccountId))` bytes. // Value: `sizeof((u32, Some(AccountId, Balance)))` bytes. #[pallet::constant] - type BalanceDeposit: Get>; + type CollectionBalanceDeposit: Get>; } /// Details of a collection. @@ -441,6 +441,7 @@ pub mod pallet { T::CollectionId, Blake2_128Concat, T::AccountId, + // (Account's collection items, Deposit details). (u32, AccountDepositOf), >; diff --git a/pallets/nfts/src/mock.rs b/pallets/nfts/src/mock.rs index 5d3015cf..efd72722 100644 --- a/pallets/nfts/src/mock.rs +++ b/pallets/nfts/src/mock.rs @@ -65,8 +65,8 @@ parameter_types! { impl Config for Test { type ApprovalsLimit = ConstU32<10>; type AttributeDepositBase = ConstU64<1>; - type BalanceDeposit = ConstU64<1>; type CollectionApprovalDeposit = ConstU64<1>; + type CollectionBalanceDeposit = ConstU64<1>; type CollectionDeposit = ConstU64<2>; type CollectionId = u32; type CreateOrigin = AsEnsureOriginWithArg>; diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index c525497d..2b80f61a 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -214,7 +214,7 @@ fn cancel_collection_approval( } fn balance_deposit() -> DepositBalanceOf { - <::BalanceDeposit>::get() + <::CollectionBalanceDeposit>::get() } #[test] diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 95604ef0..6a54f368 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -30,7 +30,7 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); // Key = 68 bytes (4+16+32+16), Value = 52 bytes (4+32+16) - pub const NftsBalanceDeposit: Balance = deposit(1, 120); + pub const NftsCollectionBalanceDeposit: Balance = deposit(1, 120); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 21 bytes (1+4+16) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 137); @@ -45,8 +45,8 @@ impl pallet_nfts::Config for Runtime { // TODO: source from primitives type ApprovalsLimit = ConstU32<20>; type AttributeDepositBase = NftsAttributeDepositBase; - type BalanceDeposit = NftsBalanceDeposit; type CollectionApprovalDeposit = NftsCollectionApprovalDeposit; + type CollectionBalanceDeposit = NftsCollectionBalanceDeposit; type CollectionDeposit = NftsCollectionDeposit; // TODO: source from primitives type CollectionId = CollectionId; @@ -139,7 +139,7 @@ mod tests { .first() .and_then(|info| info.max_size) .unwrap_or_default(); - assert_eq!(deposit(1, max_size), NftsBalanceDeposit::get()); + assert_eq!(deposit(1, max_size), NftsCollectionBalanceDeposit::get()); } #[test] diff --git a/runtime/testnet/src/config/assets.rs b/runtime/testnet/src/config/assets.rs index 95604ef0..6a54f368 100644 --- a/runtime/testnet/src/config/assets.rs +++ b/runtime/testnet/src/config/assets.rs @@ -30,7 +30,7 @@ parameter_types! { parameter_types! { pub NftsPalletFeatures: PalletFeatures = PalletFeatures::all_enabled(); // Key = 68 bytes (4+16+32+16), Value = 52 bytes (4+32+16) - pub const NftsBalanceDeposit: Balance = deposit(1, 120); + pub const NftsCollectionBalanceDeposit: Balance = deposit(1, 120); pub const NftsCollectionDeposit: Balance = 10 * UNIT; // Key = 116 bytes (4+16+32+16+32+16), Value = 21 bytes (1+4+16) pub const NftsCollectionApprovalDeposit: Balance = deposit(1, 137); @@ -45,8 +45,8 @@ impl pallet_nfts::Config for Runtime { // TODO: source from primitives type ApprovalsLimit = ConstU32<20>; type AttributeDepositBase = NftsAttributeDepositBase; - type BalanceDeposit = NftsBalanceDeposit; type CollectionApprovalDeposit = NftsCollectionApprovalDeposit; + type CollectionBalanceDeposit = NftsCollectionBalanceDeposit; type CollectionDeposit = NftsCollectionDeposit; // TODO: source from primitives type CollectionId = CollectionId; @@ -139,7 +139,7 @@ mod tests { .first() .and_then(|info| info.max_size) .unwrap_or_default(); - assert_eq!(deposit(1, max_size), NftsBalanceDeposit::get()); + assert_eq!(deposit(1, max_size), NftsCollectionBalanceDeposit::get()); } #[test] From af0b71e31f7051061248830e47d6f67ed042bccc Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:41:22 +0700 Subject: [PATCH 36/40] fix: account balance logic (#418) --- pallets/nfts/src/features/atomic_swap.rs | 4 ++-- pallets/nfts/src/features/buy_sell.rs | 2 +- pallets/nfts/src/features/transfer.rs | 12 ++++------- pallets/nfts/src/impl_nonfungibles.rs | 2 +- pallets/nfts/src/lib.rs | 2 +- pallets/nfts/src/tests.rs | 26 ++++++++++++------------ 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/pallets/nfts/src/features/atomic_swap.rs b/pallets/nfts/src/features/atomic_swap.rs index 35e85752..2b70e112 100644 --- a/pallets/nfts/src/features/atomic_swap.rs +++ b/pallets/nfts/src/features/atomic_swap.rs @@ -210,14 +210,14 @@ impl, I: 'static> Pallet { // This also removes the swap. Self::do_transfer( - &caller, + Some(&caller), send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| Ok(()), )?; Self::do_transfer( - &caller, + Some(&caller), receive_collection_id, receive_item_id, send_item.owner.clone(), diff --git a/pallets/nfts/src/features/buy_sell.rs b/pallets/nfts/src/features/buy_sell.rs index d28478cb..25fd80c9 100644 --- a/pallets/nfts/src/features/buy_sell.rs +++ b/pallets/nfts/src/features/buy_sell.rs @@ -158,7 +158,7 @@ impl, I: 'static> Pallet { let old_owner = details.owner.clone(); - Self::do_transfer(&buyer, collection, item, buyer.clone(), |_, _| Ok(()))?; + Self::do_transfer(Some(&buyer), collection, item, buyer.clone(), |_, _| Ok(()))?; Self::deposit_event(Event::ItemBought { collection, diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 28b30e27..68d6c39f 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -46,7 +46,7 @@ impl, I: 'static> Pallet { /// - If the collection or item is non-transferable /// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)). pub fn do_transfer( - caller: &T::AccountId, + caller: Option<&T::AccountId>, collection: T::CollectionId, item: T::ItemId, dest: T::AccountId, @@ -97,13 +97,9 @@ impl, I: 'static> Pallet { true => T::CollectionBalanceDeposit::get(), false => Zero::zero(), }; - // The destination account covers the `CollectionBalanceDeposit` if it has sufficient - // balance. Otherwise, the caller is accountable for it. - let deposit_account = - match T::Currency::can_reserve(&dest, T::CollectionBalanceDeposit::get()) { - true => &dest, - false => caller, - }; + // Reserve `CollectionBalanceDeposit` from the caller if provided. Otherwise, reserve from + // the collection item's owner. + let deposit_account = caller.unwrap_or(&details.owner); Self::increment_account_balance(collection, &dest, (deposit_account, deposit_amount))?; diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index 60e89a74..5186d86d 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -411,7 +411,7 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { - Self::do_transfer(destination, *collection, *item, destination.clone(), |_, _| Ok(())) + Self::do_transfer(None, *collection, *item, destination.clone(), |_, _| Ok(())) } fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 1b1174c5..0cc0d93f 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -1092,7 +1092,7 @@ pub mod pallet { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - Self::do_transfer(&origin, collection, item, dest, |_, details| { + Self::do_transfer(None, collection, item, dest, |_, details| { if details.owner != origin { Self::check_approval(&collection, &Some(item), &details.owner, &origin)?; } diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 2b80f61a..6a457c9c 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -293,14 +293,14 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3 * balance_deposit); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 70, account(2))); - assert_eq!(Balances::reserved_balance(&account(1)), 8 + 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); + assert_eq!(Balances::reserved_balance(&account(1)), 8 + 3 * balance_deposit); + assert_eq!(Balances::reserved_balance(&account(2)), 0); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 42, bvec![42, 42])); - assert_eq!(Balances::reserved_balance(&account(1)), 11 + 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&account(1)), 11 + 3 * balance_deposit); assert!(ItemMetadataOf::::contains_key(0, 42)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(account(1)), 0, 69, bvec![69, 69])); - assert_eq!(Balances::reserved_balance(&account(1)), 14 + 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&account(1)), 14 + 3 * balance_deposit); assert!(ItemMetadataOf::::contains_key(0, 69)); assert!(ItemConfigOf::::contains_key(0, 69)); let w = Nfts::get_destroy_witness(&0).unwrap(); @@ -859,10 +859,10 @@ fn transfer_requires_deposit_works() { )); assert_eq!( AccountBalance::get(collection_id, &dest), - Some((1, (dest.clone(), balance_deposit))) + Some((1, (item_owner.clone(), balance_deposit))) ); - assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), balance_deposit); + assert_eq!(Balances::reserved_balance(&item_owner), 3 * balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } }); @@ -1011,10 +1011,10 @@ fn transfer_owner_should_work() { assert_eq!(Balances::reserved_balance(&account(3)), 44); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(1)), 0, 42, account(2))); - // The reserved balance of account 1 should remain the same. For account 2 the - // `balance_deposit` is reserved because it is the new item owner. - assert_eq!(Balances::reserved_balance(&account(1)), 1); - assert_eq!(Balances::reserved_balance(&account(2)), balance_deposit); + // The reserved balance of account 1 is incremented to create a new storage record for the + // account 2. + assert_eq!(Balances::reserved_balance(&account(1)), 1 + balance_deposit); + assert_eq!(Balances::reserved_balance(&account(2)), 0); // 2's acceptance from before is reset when it became an owner, so it cannot be transferred // without a fresh acceptance. @@ -1585,8 +1585,8 @@ fn set_item_owner_attributes_should_work() { Attribute::::get((0, Some(0), AttributeNamespace::ItemOwner, &key)).unwrap(); assert_eq!(deposit.account, Some(account(3))); assert_eq!(deposit.amount, 13); - assert_eq!(Balances::reserved_balance(account(2)), 3); - assert_eq!(Balances::reserved_balance(account(3)), 13 + balance_deposit()); + assert_eq!(Balances::reserved_balance(account(2)), 3 + balance_deposit()); + assert_eq!(Balances::reserved_balance(account(3)), 13); // validate attributes on item deletion assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(3)), 0, 0)); From 270039e1b2efdcc836870891872139565f5b49ab Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:16:54 +0700 Subject: [PATCH 37/40] chore: update benchmarking.rs --- pallets/nfts/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index 4dfc3b3e..33c7dc5c 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -303,7 +303,7 @@ benchmarks_instance_pallet! { let target: T::AccountId = account("target", 0, SEED); let target_lookup = T::Lookup::unlookup(target.clone()); - T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance() + T::CollectionBalanceDeposit::get()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); }: _(SystemOrigin::Signed(caller.clone()), collection, item, target_lookup) verify { assert_last_event::(Event::Transferred { collection, item, from: caller, to: target }.into()); From b1719a6dd1525aaaff25a3705e5015bf9a57a336 Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:02:32 +0700 Subject: [PATCH 38/40] chore: update tests --- pallets/nfts/src/tests.rs | 168 +++++++++++++++++++++++++++++--------- 1 file changed, 129 insertions(+), 39 deletions(-) diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 6a457c9c..672180af 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -615,10 +615,7 @@ fn mint_should_update_account_balance_works() { owner.clone(), None )); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((2, (owner.clone(), balance_deposit))) - ); + assert_eq!(AccountBalance::get(collection_id, &owner), Some((2, (owner, balance_deposit)))); }); } @@ -762,16 +759,17 @@ fn transfer_should_update_account_balance_works() { dest.clone() )); assert!(!AccountBalance::contains_key(collection_id, &owner)); - assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner.clone(), 0)))); + assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner, 0)))); }); } #[test] fn transfer_requires_deposit_works() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); let collection_id = 0; let collection_owner = account(1); - let balance_deposit = balance_deposit(); + let delegate = account(5); let mut dest = account(3); let mut item_id = 42; let item_owner = account(2); @@ -792,7 +790,7 @@ fn transfer_requires_deposit_works() { default_item_config() )); - // Reserve funds from `owner` as `dest` does not exist. + // Reserve funds from `item_owner` when transferring to `dest`. { assert_ok!(Nfts::transfer( RuntimeOrigin::signed(item_owner.clone()), @@ -809,50 +807,29 @@ fn transfer_requires_deposit_works() { assert!(items().contains(&(dest, collection_id, item_id))); } - // Reserve funds from `owner` since `dest` lacks sufficient balance. + // Reserve funds from the `item_owner` during a delegated transfer initiated by the + // `delegate` to the `dest`. { item_id = 43; dest = account(4); assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), + RuntimeOrigin::signed(collection_owner), collection_id, item_id, item_owner.clone(), default_item_config() )); - - Balances::make_free_balance_be(&dest, 1); - assert_ok!(Nfts::transfer( + assert_ok!(Nfts::approve_transfer( RuntimeOrigin::signed(item_owner.clone()), collection_id, item_id, - dest.clone() + delegate.clone(), + None )); - assert_eq!( - AccountBalance::get(collection_id, &dest), - Some((1, (item_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), 0); - assert!(items().contains(&(dest, collection_id, item_id))); - } - // Reserves funds from `dest` since `dest` exists and has sufficient balance. - { - item_id = 44; - dest = account(5); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(account(1)), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - Balances::make_free_balance_be(&dest, 1 + balance_deposit); assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(item_owner.clone()), + RuntimeOrigin::signed(account(5)), collection_id, item_id, dest.clone() @@ -861,7 +838,8 @@ fn transfer_requires_deposit_works() { AccountBalance::get(collection_id, &dest), Some((1, (item_owner.clone(), balance_deposit))) ); - assert_eq!(Balances::reserved_balance(&item_owner), 3 * balance_deposit); + assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); + assert_eq!(Balances::reserved_balance(&delegate), 0); assert_eq!(Balances::reserved_balance(&dest), 0); assert!(items().contains(&(dest, collection_id, item_id))); } @@ -2116,9 +2094,6 @@ fn burn_works() { ); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 42)); - // Burn an item unreserving the item deposit while retaining the reservation for `balance - // deposit`. - assert_eq!(Balances::reserved_balance(account(1)), 1 + balance_deposit); assert_ok!(Nfts::burn(RuntimeOrigin::signed(account(5)), 0, 69)); assert_eq!(Balances::reserved_balance(account(1)), 0); }); @@ -3220,6 +3195,55 @@ fn buy_item_should_update_account_balance_works() { }); } +#[test] +fn buy_item_requires_deposit_works() { + new_test_ext().execute_with(|| { + let user_1 = account(1); + let user_2 = account(2); + let collection_id = 0; + let item_1 = 1; + let price_1 = 20; + let initial_balance = 100; + + Balances::make_free_balance_be(&user_1, initial_balance); + Balances::make_free_balance_be(&user_2, initial_balance); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_1.clone(), + collection_config_with_all_settings_enabled() + )); + + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_1, + user_1.clone(), + None + )); + + assert_ok!(Nfts::set_price( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_1, + Some(price_1), + None, + )); + + assert_ok!(Nfts::buy_item( + RuntimeOrigin::signed(user_2.clone()), + collection_id, + item_1, + price_1 + 1, + )); + assert!(!AccountBalance::contains_key(collection_id, &user_1)); + assert_eq!( + AccountBalance::get(collection_id, &user_2), + Some((1, (user_2, balance_deposit()))) + ); + }); +} + #[test] fn pay_tips_should_work() { new_test_ext().execute_with(|| { @@ -3697,6 +3721,72 @@ fn claim_swap_should_update_account_balance_works() { }); } +#[test] +fn claim_swap_requires_deposit_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + let user_1 = account(1); + let user_2 = account(2); + let collection_id = 0; + let balance_deposit = balance_deposit(); + let item_1 = 1; + let item_2 = 2; + let price = 100; + let price_with_direction = + PriceWithDirection { amount: price, direction: PriceDirection::Receive.clone() }; + + Balances::make_free_balance_be(&user_1, 1000); + Balances::make_free_balance_be(&user_2, 1000); + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_1.clone(), + collection_config_with_all_settings_enabled() + )); + + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_1, + user_1.clone(), + None, + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_2, + user_2.clone(), + None, + )); + assert_ok!(Nfts::create_swap( + RuntimeOrigin::signed(user_1.clone()), + collection_id, + item_1, + collection_id, + Some(item_2), + Some(price_with_direction.clone()), + 2 + )); + + assert_ok!(Nfts::claim_swap( + RuntimeOrigin::signed(user_2.clone()), + collection_id, + item_2, + collection_id, + item_1, + Some(price_with_direction), + )); + assert_eq!( + AccountBalance::get(collection_id, &user_1), + Some((1, (user_1, balance_deposit))) + ); + assert_eq!( + AccountBalance::get(collection_id, &user_2), + Some((1, (user_2, balance_deposit))) + ); + }); +} + #[test] fn various_collection_settings() { new_test_ext().execute_with(|| { From 986a5b6b258846c413d6f33ed7698bfb140a3bfe Mon Sep 17 00:00:00 2001 From: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:15:10 +0200 Subject: [PATCH 39/40] refactor: account balance (#421) --- pallets/nfts/src/common_functions.rs | 84 ++++ pallets/nfts/src/features/atomic_swap.rs | 6 +- pallets/nfts/src/features/transfer.rs | 22 +- pallets/nfts/src/impl_nonfungibles.rs | 4 + pallets/nfts/src/lib.rs | 4 + pallets/nfts/src/tests.rs | 544 ++++++----------------- 6 files changed, 234 insertions(+), 430 deletions(-) diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 904a2334..f27a9029 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -151,3 +151,87 @@ impl, I: 'static> Pallet { .expect("Failed to get next collection ID") } } + +#[cfg(test)] +mod tests { + use crate::{mock::*, tests::*, Currency, Error, ReservableCurrency}; + + #[test] + fn increment_account_balance_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let deposit_account = account(1); + let deposit_amount = balance_deposit(); + let owner = account(2); + assert_noop!( + Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + ), + BalancesError::::InsufficientBalance + ); + Balances::make_free_balance_be(&deposit_account, 100); + // Initialize `AccountBalance` and increase the collection item count for the new + // account. + assert_ok!(Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + )); + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (deposit_account.clone(), deposit_amount))) + ); + // Increment the balance of a non-zero balance account. No additional reserves. + assert_ok!(Nfts::increment_account_balance( + collection_id, + &owner, + (&deposit_account, deposit_amount) + )); + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((2, (deposit_account.clone(), deposit_amount))) + ); + }); + } + + #[test] + fn decrement_account_balance_works() { + new_test_ext().execute_with(|| { + let collection_id = 0; + let balance = 2u32; + let deposit_account = account(1); + let deposit_amount = balance_deposit(); + let owner = account(2); + + Balances::make_free_balance_be(&deposit_account, 100); + // Decrement non-existing `AccountBalance` record. + assert_noop!( + Nfts::decrement_account_balance(collection_id, &deposit_account), + Error::::NoItemOwned + ); + // Set account balance and reserve `DepositBalance`. + AccountBalance::insert( + collection_id, + &owner, + (&balance, (&deposit_account, deposit_amount)), + ); + Balances::reserve(&deposit_account, deposit_amount).expect("should work"); + // Successfully decrement the value of the `AccountBalance` entry. + assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((balance - 1, (deposit_account.clone(), deposit_amount))) + ); + assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); + // `AccountBalance` record is deleted, and the depositor's funds are unreserved if + // the `AccountBalance` value reaches zero after decrementing. + assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); + assert_eq!(Balances::reserved_balance(&deposit_account), 0); + assert!(!AccountBalance::contains_key(collection_id, &owner)); + }); + } +} diff --git a/pallets/nfts/src/features/atomic_swap.rs b/pallets/nfts/src/features/atomic_swap.rs index 2b70e112..383fad05 100644 --- a/pallets/nfts/src/features/atomic_swap.rs +++ b/pallets/nfts/src/features/atomic_swap.rs @@ -210,14 +210,16 @@ impl, I: 'static> Pallet { // This also removes the swap. Self::do_transfer( - Some(&caller), + Some(&receive_item.owner), send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| Ok(()), )?; + // Owner of `send_item` is responsible for the deposit if the collection balance + // went to zero due to the previous transfer. Self::do_transfer( - Some(&caller), + Some(&send_item.owner), receive_collection_id, receive_item_id, send_item.owner.clone(), diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 68d6c39f..3c0e704d 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -25,7 +25,8 @@ use crate::*; impl, I: 'static> Pallet { /// Transfer an NFT to the specified destination account. /// - /// - `caller`: The account transferring the collection item. + /// - `depositor`: The account reserving the `CollectionBalanceDeposit` from if `dest` holds no + /// items in the collection. /// - `collection`: The ID of the collection to which the NFT belongs. /// - `item`: The ID of the NFT to transfer. /// - `dest`: The destination account to which the NFT will be transferred. @@ -46,7 +47,7 @@ impl, I: 'static> Pallet { /// - If the collection or item is non-transferable /// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)). pub fn do_transfer( - caller: Option<&T::AccountId>, + depositor: Option<&T::AccountId>, collection: T::CollectionId, item: T::ItemId, dest: T::AccountId, @@ -91,16 +92,15 @@ impl, I: 'static> Pallet { // Update account balance of the owner. Self::decrement_account_balance(collection, &details.owner)?; - // Update account balance of the destination account. - let deposit_amount = - match collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { - true => T::CollectionBalanceDeposit::get(), - false => Zero::zero(), - }; - // Reserve `CollectionBalanceDeposit` from the caller if provided. Otherwise, reserve from - // the collection item's owner. - let deposit_account = caller.unwrap_or(&details.owner); + let deposit_amount = collection_config + .is_setting_enabled(CollectionSetting::DepositRequired) + .then_some(T::CollectionBalanceDeposit::get()) + .unwrap_or_default(); + // Reserve `CollectionBalanceDeposit` from the depositor if provided. Otherwise, reserve + // from the item's owner. + let deposit_account = depositor.unwrap_or(&details.owner); + // Update account balance of the destination account. Self::increment_account_balance(collection, &dest, (deposit_account, deposit_amount))?; // Update account ownership information. diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index 5186d86d..22517397 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -411,6 +411,10 @@ impl, I: 'static> Transfer for Pallet { item: &Self::ItemId, destination: &T::AccountId, ) -> DispatchResult { + // The item's owner pays for the deposit of `AccountBalance` if the `dest` holds no items + // in `collection`. A malicious actor could have a deposit reserved from `dest` without + // them knowing about the transfer. The deposit amount can be accounted for in the off chain + // price of the NFT. Self::do_transfer(None, *collection, *item, destination.clone(), |_, _| Ok(())) } diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 0cc0d93f..c9f6a503 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -1092,6 +1092,10 @@ pub mod pallet { let origin = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; + // The item's owner pays for the deposit of `AccountBalance` if the `dest` holds no + // items in `collection`. A malicious actor could have a deposit reserved from `dest` + // without them knowing about the transfer. The deposit amount can be accounted for + // in the off chain price of the NFT. Self::do_transfer(None, collection, item, dest, |_, details| { if details.owner != origin { Self::check_approval(&collection, &Some(item), &details.owner, &origin)?; diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index b723e5db..fbf73ce3 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -18,7 +18,7 @@ //! Tests for Nfts pallet. use enumflags2::BitFlags; -use frame_support::{ +pub(crate) use frame_support::{ assert_noop, assert_ok, dispatch::WithPostDispatchInfo, traits::{ @@ -27,7 +27,7 @@ use frame_support::{ }, }; use frame_system::pallet_prelude::BlockNumberFor; -use pallet_balances::Error as BalancesError; +pub(crate) use pallet_balances::Error as BalancesError; use sp_core::{bounded::BoundedVec, Pair}; use sp_runtime::{ traits::{Dispatchable, IdentifyAccount}, @@ -38,19 +38,15 @@ use sp_runtime::{ use crate::{mock::*, Event, SystemConfig, *}; type AccountIdOf = ::AccountId; -type AccountBalance = crate::AccountBalance; +pub(crate) type AccountBalance = crate::AccountBalance; type CollectionApprovals = crate::CollectionApprovals; type CollectionApprovalDeposit = ::CollectionApprovalDeposit; type WeightOf = ::WeightInfo; -fn account(id: u8) -> AccountIdOf { +pub(crate) fn account(id: u8) -> AccountIdOf { [id; 32].into() } -fn root() -> RuntimeOrigin { - RuntimeOrigin::root() -} - fn none() -> RuntimeOrigin { RuntimeOrigin::none() } @@ -162,6 +158,14 @@ fn item_config_from_disabled_settings(settings: BitFlags) -> ItemCo ItemConfig { settings: ItemSettings::from_disabled(settings) } } +pub(crate) fn balance_deposit() -> DepositBalanceOf { + <::CollectionBalanceDeposit>::get() +} + +pub(crate) fn item_deposit() -> DepositBalanceOf { + <::ItemDeposit>::get() +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -399,8 +403,6 @@ fn mint_should_work() { account(1), default_collection_config() )); - // Minting doesn't require a deposit because the collection's `DepositRequired` setting is - // disabled. assert_ok!(Nfts::mint(RuntimeOrigin::signed(account(1)), 0, 42, account(1), None)); assert_eq!(Nfts::owner(0, 42).unwrap(), account(1)); assert_eq!(collections(), vec![(account(1), 0)]); @@ -529,9 +531,10 @@ fn mint_should_work() { #[test] fn mint_should_update_account_balance_works() { new_test_ext().execute_with(|| { - let collection_id = 0; - let owner = account(1); let balance_deposit = balance_deposit(); + let collection_id = 0; + let dest = account(1); + let owner = account(2); Balances::make_free_balance_be(&owner, 100); assert_ok!(Nfts::force_create( @@ -539,84 +542,34 @@ fn mint_should_update_account_balance_works() { owner.clone(), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint( RuntimeOrigin::signed(owner.clone()), collection_id, 42, - owner.clone(), + dest.clone(), None )); + // Minting reserves a deposit because `dest` doesn't have an item in the collection yet and + // thus a storage item has to be created. assert_eq!( - AccountBalance::get(collection_id, &owner), + AccountBalance::get(collection_id, dest.clone()), Some((1, (owner.clone(), balance_deposit))) ); - - // Additive. + assert_eq!(Balances::reserved_balance(&owner), item_deposit() + balance_deposit); assert_ok!(Nfts::mint( RuntimeOrigin::signed(owner.clone()), collection_id, 43, - owner.clone(), + dest.clone(), None )); - assert_eq!(AccountBalance::get(collection_id, &owner), Some((2, (owner, balance_deposit)))); - }); -} - -#[test] -fn mint_requires_deposit_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let collection_owner = account(1); - let balance_deposit = balance_deposit(); - let mut item_id = 42; - let item_owner = account(2); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - collection_config_with_all_settings_enabled() - )); - - Balances::make_free_balance_be(&collection_owner, 100); - // Minting reserves deposit from the collection owner. - { - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, item_owner.clone()), - Some((1, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 1 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - // Mint for accounts with a non-zero balance requires no additional reserves. - { - item_id = 43; - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(collection_owner.clone()), - collection_id, - item_id, - item_owner.clone(), - None - )); - assert_eq!( - AccountBalance::get(collection_id, &item_owner), - Some((2, (collection_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&collection_owner), 2 + balance_deposit); - assert_eq!(Nfts::owner(collection_id, item_id).unwrap(), item_owner); - } - - assert_eq!(collections(), vec![(account(1), 0)]); - assert_eq!(items(), vec![(account(2), 0, 42), (account(2), 0, 43)]); + // `dest` already has an item in the collection so no extra deposit is reserved. + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((2, (owner.clone(), balance_deposit))) + ); + assert_eq!(Balances::reserved_balance(&owner), (2 * item_deposit()) + balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), 0); }); } @@ -680,15 +633,18 @@ fn transfer_should_work() { #[test] fn transfer_should_update_account_balance_works() { new_test_ext().execute_with(|| { + let balance_deposit = balance_deposit(); let collection_id = 0; - let dest = account(3); + let delegate = account(1); + let dest = account(2); let item_id = 42; - let owner = account(1); + let owner = account(3); + Balances::make_free_balance_be(&owner, 100); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), owner.clone(), - default_collection_config() + collection_config_with_all_settings_enabled() )); assert_ok!(Nfts::force_mint( RuntimeOrigin::signed(owner.clone()), @@ -704,90 +660,38 @@ fn transfer_should_update_account_balance_works() { dest.clone() )); assert!(!AccountBalance::contains_key(collection_id, &owner)); - assert_eq!(AccountBalance::get(collection_id, &dest), Some((1, (owner, 0)))); - }); -} - -#[test] -fn transfer_requires_deposit_works() { - new_test_ext().execute_with(|| { - let balance_deposit = balance_deposit(); - let collection_id = 0; - let collection_owner = account(1); - let delegate = account(5); - let mut dest = account(3); - let mut item_id = 42; - let item_owner = account(2); + assert_eq!( + AccountBalance::get(collection_id, &dest), + Some((1, (owner.clone(), balance_deposit))) + ); + // Reserve funds from `owner` when transferring to `dest`. + assert_eq!(Balances::reserved_balance(&owner), item_deposit() + balance_deposit); + assert_eq!(Balances::reserved_balance(&dest), 0); - Balances::make_free_balance_be(&collection_owner, 100); - Balances::make_free_balance_be(&item_owner, 100); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - collection_owner.clone(), - collection_config_with_all_settings_enabled() + // Approve `delegate` for approved transfer case. + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(dest.clone()), + collection_id, + item_id, + delegate.clone(), + None )); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner.clone()), + Balances::make_free_balance_be(&dest, Balances::minimum_balance() + balance_deposit); + assert_ok!(Nfts::transfer( + RuntimeOrigin::signed(delegate.clone()), collection_id, item_id, - item_owner.clone(), - default_item_config() + owner.clone() )); - - // Reserve funds from `item_owner` when transferring to `dest`. - { - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - dest.clone() - )); - assert_eq!( - AccountBalance::get(collection_id, &dest), - Some((1, (item_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&item_owner), balance_deposit); - assert_eq!(Balances::reserved_balance(&dest), 0); - assert!(items().contains(&(dest, collection_id, item_id))); - } - - // Reserve funds from the `item_owner` during a delegated transfer initiated by the - // `delegate` to the `dest`. - { - item_id = 43; - dest = account(4); - - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(collection_owner), - collection_id, - item_id, - item_owner.clone(), - default_item_config() - )); - assert_ok!(Nfts::approve_transfer( - RuntimeOrigin::signed(item_owner.clone()), - collection_id, - item_id, - delegate.clone(), - None - )); - - assert_ok!(Nfts::transfer( - RuntimeOrigin::signed(account(5)), - collection_id, - item_id, - dest.clone() - )); - assert_eq!( - AccountBalance::get(collection_id, &dest), - Some((1, (item_owner.clone(), balance_deposit))) - ); - assert_eq!(Balances::reserved_balance(&item_owner), 2 * balance_deposit); - assert_eq!(Balances::reserved_balance(&delegate), 0); - assert_eq!(Balances::reserved_balance(&dest), 0); - assert!(items().contains(&(dest, collection_id, item_id))); - } + assert_eq!( + AccountBalance::get(collection_id, &owner), + Some((1, (dest.clone(), balance_deposit))) + ); + // Reserve funds from `dest` during a delegated transfer initiated by the + // `delegate` back to `owner`. + assert_eq!(Balances::reserved_balance(&owner), item_deposit()); + assert_eq!(Balances::reserved_balance(&delegate), 0); + assert_eq!(Balances::reserved_balance(&dest), balance_deposit); }); } @@ -2076,47 +1980,11 @@ fn burn_should_update_account_balance_works() { AccountBalance::get(collection_id, &owner), Some((1, (owner.clone(), balance_deposit()))) ); + assert_eq!(Balances::reserved_balance(&owner), item_deposit() + balance_deposit()); assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 43)); assert!(!AccountBalance::contains_key(collection_id, &owner)); - }); -} - -#[test] -fn burn_removes_deposit_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let owner = account(1); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - owner.clone(), - collection_config_with_all_settings_enabled() - )); - - Balances::make_free_balance_be(&owner, 100); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 42, - owner.clone(), - None - )); - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(owner.clone()), - collection_id, - 43, - owner.clone(), - None - )); - - assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 42)); - assert_eq!(Balances::reserved_balance(&owner), 1 + balance_deposit()); - assert_eq!(items(), vec![(owner.clone(), 0, 43)]); - - assert_ok!(Nfts::burn(RuntimeOrigin::signed(owner.clone()), collection_id, 43)); assert_eq!(Balances::reserved_balance(&owner), 0); - assert_eq!(items(), vec![]); }); } @@ -3148,92 +3016,45 @@ fn buy_item_should_update_account_balance_works() { let user_1 = account(1); let user_2 = account(2); let collection_id = 0; - let item_1 = 1; - let price_1 = 20; - let initial_balance = 100; - - Balances::make_free_balance_be(&user_1, initial_balance); - Balances::make_free_balance_be(&user_2, initial_balance); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_1.clone(), - default_collection_config() - )); - - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_1.clone()), - collection_id, - item_1, - user_1.clone(), - None - )); - - assert_ok!(Nfts::set_price( - RuntimeOrigin::signed(user_1.clone()), - collection_id, - item_1, - Some(price_1), - None, - )); - - assert_ok!(Nfts::buy_item( - RuntimeOrigin::signed(user_2.clone()), - collection_id, - item_1, - price_1 + 1, - )); - assert!(!AccountBalance::contains_key(collection_id, &user_1)); - assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); - }); -} - -#[test] -fn buy_item_requires_deposit_works() { - new_test_ext().execute_with(|| { - let user_1 = account(1); - let user_2 = account(2); - let collection_id = 0; - let item_1 = 1; - let price_1 = 20; - let initial_balance = 100; - - Balances::make_free_balance_be(&user_1, initial_balance); - Balances::make_free_balance_be(&user_2, initial_balance); + let item = 1; + let price = 20; + Balances::make_free_balance_be(&user_1, 100); + Balances::make_free_balance_be( + &user_2, + Balances::minimum_balance() + price + balance_deposit(), + ); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), user_1.clone(), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint( RuntimeOrigin::signed(user_1.clone()), collection_id, - item_1, + item, user_1.clone(), None )); - assert_ok!(Nfts::set_price( RuntimeOrigin::signed(user_1.clone()), collection_id, - item_1, - Some(price_1), + item, + Some(price), None, )); - assert_ok!(Nfts::buy_item( RuntimeOrigin::signed(user_2.clone()), collection_id, - item_1, - price_1 + 1, + item, + price, )); assert!(!AccountBalance::contains_key(collection_id, &user_1)); assert_eq!( AccountBalance::get(collection_id, &user_2), - Some((1, (user_2, balance_deposit()))) + Some((1, (user_2.clone(), balance_deposit()))) ); + assert_eq!(Balances::reserved_balance(&user_2), balance_deposit()); }); } @@ -3669,74 +3490,15 @@ fn claim_swap_should_update_account_balance_works() { PriceWithDirection { amount: price, direction: PriceDirection::Receive.clone() }; Balances::make_free_balance_be(&user_1, 1000); - Balances::make_free_balance_be(&user_2, 1000); - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_1.clone(), - default_collection_config() - )); - - assert_ok!(Nfts::mint( - RuntimeOrigin::signed(user_1.clone()), - collection_id, - item_1, - user_1.clone(), - None, - )); - assert_ok!(Nfts::force_mint( - RuntimeOrigin::signed(user_1.clone()), - collection_id, - item_2, - user_2.clone(), - default_item_config(), - )); - assert_ok!(Nfts::create_swap( - RuntimeOrigin::signed(user_1.clone()), - collection_id, - item_1, - collection_id, - Some(item_2), - Some(price_with_direction.clone()), - 2 - )); - - assert_ok!(Nfts::claim_swap( - RuntimeOrigin::signed(user_2.clone()), - collection_id, - item_2, - collection_id, - item_1, - Some(price_with_direction), - )); - assert_eq!(AccountBalance::get(collection_id, &user_1), Some((1, (user_1, 0)))); - assert_eq!(AccountBalance::get(collection_id, &user_2), Some((1, (user_2, 0)))); - }); -} - -#[test] -fn claim_swap_requires_deposit_works() { - new_test_ext().execute_with(|| { - System::set_block_number(1); - let user_1 = account(1); - let user_2 = account(2); - let collection_id = 0; - let balance_deposit = balance_deposit(); - let item_1 = 1; - let item_2 = 2; - let price = 100; - let price_with_direction = - PriceWithDirection { amount: price, direction: PriceDirection::Receive.clone() }; - - Balances::make_free_balance_be(&user_1, 1000); - Balances::make_free_balance_be(&user_2, 1000); - + Balances::make_free_balance_be( + &user_2, + Balances::minimum_balance() + balance_deposit() + price, + ); assert_ok!(Nfts::force_create( RuntimeOrigin::root(), user_1.clone(), collection_config_with_all_settings_enabled() )); - assert_ok!(Nfts::mint( RuntimeOrigin::signed(user_1.clone()), collection_id, @@ -3751,6 +3513,10 @@ fn claim_swap_requires_deposit_works() { user_2.clone(), None, )); + assert_eq!( + Balances::reserved_balance(&user_1), + (2 * item_deposit()) + (2 * balance_deposit()) + ); assert_ok!(Nfts::create_swap( RuntimeOrigin::signed(user_1.clone()), collection_id, @@ -3760,7 +3526,6 @@ fn claim_swap_requires_deposit_works() { Some(price_with_direction.clone()), 2 )); - assert_ok!(Nfts::claim_swap( RuntimeOrigin::signed(user_2.clone()), collection_id, @@ -3771,12 +3536,16 @@ fn claim_swap_requires_deposit_works() { )); assert_eq!( AccountBalance::get(collection_id, &user_1), - Some((1, (user_1, balance_deposit))) + Some((1, (user_1.clone(), balance_deposit()))) ); assert_eq!( AccountBalance::get(collection_id, &user_2), - Some((1, (user_2, balance_deposit))) + Some((1, (user_2.clone(), balance_deposit()))) ); + assert_eq!(Balances::reserved_balance(&user_1), (2 * item_deposit()) + balance_deposit()); + // User pays for its own deposit (previously paid by the account who signed the mint + // transaction). + assert_eq!(Balances::reserved_balance(&user_2), balance_deposit()); }); } @@ -4656,7 +4425,7 @@ fn clear_collection_approvals_works() { let item_owner = account(1); let delegates = 10..20; - for origin in [root(), none()] { + for origin in [RuntimeOrigin::root(), none()] { assert_noop!( Nfts::clear_collection_approvals(origin, collection_id, 0), BadOrigin.with_weight(WeightOf::clear_collection_approvals(0)) @@ -4751,7 +4520,12 @@ fn clear_collection_approvals_works() { // Remove collection approvals while there are none. assert_eq!( - Nfts::force_clear_collection_approvals(root(), item_owner.clone(), collection_id, 10), + Nfts::force_clear_collection_approvals( + RuntimeOrigin::root(), + item_owner.clone(), + collection_id, + 10 + ), Ok(Some(WeightOf::clear_collection_approvals(0)).into()) ); assert_eq!(Balances::free_balance(&item_owner), balance); @@ -4823,7 +4597,12 @@ fn force_clear_collection_approvals_works() { // Remove zero collection approvals. assert_eq!( - Nfts::force_clear_collection_approvals(root(), item_owner.clone(), collection_id, 0), + Nfts::force_clear_collection_approvals( + RuntimeOrigin::root(), + item_owner.clone(), + collection_id, + 0 + ), Ok(Some(WeightOf::clear_collection_approvals(0)).into()) ); assert_eq!(Balances::free_balance(&item_owner), balance - approvals as u64); @@ -4841,7 +4620,7 @@ fn force_clear_collection_approvals_works() { let limit = 1; assert_eq!( Nfts::force_clear_collection_approvals( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, limit @@ -4863,7 +4642,12 @@ fn force_clear_collection_approvals_works() { // Successfully remove all collection approvals. Only charges post-dispatch weight for // the removed approvals. assert_eq!( - Nfts::force_clear_collection_approvals(root(), item_owner.clone(), collection_id, 10), + Nfts::force_clear_collection_approvals( + RuntimeOrigin::root(), + item_owner.clone(), + collection_id, + 10 + ), Ok(Some(WeightOf::clear_collection_approvals(approvals)).into()) ); assert_eq!(Balances::free_balance(&item_owner), balance); @@ -4878,7 +4662,12 @@ fn force_clear_collection_approvals_works() { // Remove collection approvals while there are none. assert_eq!( - Nfts::force_clear_collection_approvals(root(), item_owner.clone(), collection_id, 10), + Nfts::force_clear_collection_approvals( + RuntimeOrigin::root(), + item_owner.clone(), + collection_id, + 10 + ), Ok(Some(WeightOf::clear_collection_approvals(0)).into()) ); assert_eq!(Balances::free_balance(&item_owner), balance); @@ -4914,7 +4703,7 @@ fn approve_collection_transfer_works() { let item_id = 42; let item_owner = account(2); - for origin in [root(), none()] { + for origin in [RuntimeOrigin::root(), none()] { assert_noop!( Nfts::approve_collection_transfer(origin, collection_id, delegate.clone(), None), BadOrigin @@ -4931,7 +4720,7 @@ fn approve_collection_transfer_works() { Error::::NoItemOwned ); assert_ok!(Nfts::force_create( - root(), + RuntimeOrigin::root(), collection_owner.clone(), default_collection_config() )); @@ -5045,7 +4834,7 @@ fn force_approve_collection_transfer_works() { // Approve unknown collection. assert_noop!( Nfts::force_approve_collection_transfer( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone(), @@ -5061,7 +4850,7 @@ fn force_approve_collection_transfer_works() { // Approve collection without items. assert_noop!( Nfts::force_approve_collection_transfer( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone(), @@ -5079,7 +4868,7 @@ fn force_approve_collection_transfer_works() { // Approve collection without balance. assert_noop!( Nfts::force_approve_collection_transfer( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone(), @@ -5103,7 +4892,7 @@ fn force_approve_collection_transfer_works() { [None, None, Some(deadline), Some(deadline), Some(deadline * 2), Some(deadline), None] { assert_ok!(Nfts::force_approve_collection_transfer( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone(), @@ -5135,7 +4924,7 @@ fn force_approve_collection_transfer_works() { )); assert_noop!( Nfts::force_approve_collection_transfer( - root(), + RuntimeOrigin::root(), item_owner, collection_id, delegate, @@ -5155,7 +4944,7 @@ fn cancel_collection_approval_works() { let item_id = 42; let item_owner = account(2); - for origin in [root(), none()] { + for origin in [RuntimeOrigin::root(), none()] { assert_noop!( Nfts::cancel_collection_approval(origin, collection_id, delegate.clone()), BadOrigin @@ -5254,7 +5043,7 @@ fn force_cancel_collection_approval_works() { // Cancel an approval for a non existing collection. assert_noop!( Nfts::force_cancel_collection_approval( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone() @@ -5270,7 +5059,7 @@ fn force_cancel_collection_approval_works() { // Cancel an unapproved delegate. assert_noop!( Nfts::force_cancel_collection_approval( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, account(69) @@ -5279,7 +5068,7 @@ fn force_cancel_collection_approval_works() { ); // Successfully cancel a collection approval. assert_ok!(Nfts::force_cancel_collection_approval( - root(), + RuntimeOrigin::root(), item_owner.clone(), collection_id, delegate.clone() @@ -5502,82 +5291,3 @@ fn check_approval_with_deadline_works() { ); }); } - -#[test] -fn increment_account_balance_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let deposit_account = account(1); - let deposit_amount = balance_deposit(); - let owner = account(2); - // Throws error `BalancesError::InsufficientBalance`. - assert_noop!( - Nfts::increment_account_balance( - collection_id, - &owner, - (&deposit_account, deposit_amount) - ), - BalancesError::::InsufficientBalance - ); - Balances::make_free_balance_be(&deposit_account, 100); - // Initialize `AccountBalance` and increase the collection item count for the new account. - assert_ok!(Nfts::increment_account_balance( - collection_id, - &owner, - (&deposit_account, deposit_amount) - )); - assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((1, (deposit_account.clone(), deposit_amount))) - ); - // Increment the balance of a non-zero balance account. No additional reserves. - assert_ok!(Nfts::increment_account_balance( - collection_id, - &owner, - (&deposit_account, deposit_amount) - )); - assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((2, (deposit_account.clone(), deposit_amount))) - ); - }); -} - -#[test] -fn decrement_account_balance_works() { - new_test_ext().execute_with(|| { - let collection_id = 0; - let balance = 2u32; - let deposit_account = account(1); - let deposit_amount = balance_deposit(); - let owner = account(2); - - Balances::make_free_balance_be(&deposit_account, 100); - // Decrement non-existing `AccountBalance` record. - assert_noop!( - Nfts::decrement_account_balance(collection_id, &deposit_account), - Error::::NoItemOwned - ); - // Set account balance and reserve `DepositBalance`. - AccountBalance::insert( - collection_id, - &owner, - (&balance, (&deposit_account, deposit_amount)), - ); - ::Currency::reserve(&deposit_account, deposit_amount).expect("should work"); - // Successfully decrement the value of the `AccountBalance` entry. - assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); - assert_eq!( - AccountBalance::get(collection_id, &owner), - Some((balance - 1, (deposit_account.clone(), deposit_amount))) - ); - assert_eq!(Balances::reserved_balance(&deposit_account), deposit_amount); - // `AccountBalance` record is deleted, and the depositor's funds are unreserved if - // the `AccountBalance` value reaches zero after decrementing. - assert_ok!(Nfts::decrement_account_balance(collection_id, &owner)); - assert_eq!(Balances::reserved_balance(&deposit_account), 0); - assert!(!AccountBalance::contains_key(collection_id, &owner)); - }); -} From 1aa79f342e483c37d34bf3b7f6ad0b3a8eeada0a Mon Sep 17 00:00:00 2001 From: chungquantin <56880684+chungquantin@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:15:12 +0700 Subject: [PATCH 40/40] chore: resolve feedback comments --- pallets/nfts/src/features/atomic_swap.rs | 4 ++-- pallets/nfts/src/features/buy_sell.rs | 2 +- pallets/nfts/src/features/transfer.rs | 6 +++--- pallets/nfts/src/impl_nonfungibles.rs | 8 ++++---- pallets/nfts/src/lib.rs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pallets/nfts/src/features/atomic_swap.rs b/pallets/nfts/src/features/atomic_swap.rs index 383fad05..1ffd6dce 100644 --- a/pallets/nfts/src/features/atomic_swap.rs +++ b/pallets/nfts/src/features/atomic_swap.rs @@ -210,19 +210,19 @@ impl, I: 'static> Pallet { // This also removes the swap. Self::do_transfer( - Some(&receive_item.owner), send_collection_id, send_item_id, receive_item.owner.clone(), + Some(&receive_item.owner), |_, _| Ok(()), )?; // Owner of `send_item` is responsible for the deposit if the collection balance // went to zero due to the previous transfer. Self::do_transfer( - Some(&send_item.owner), receive_collection_id, receive_item_id, send_item.owner.clone(), + Some(&send_item.owner), |_, _| Ok(()), )?; diff --git a/pallets/nfts/src/features/buy_sell.rs b/pallets/nfts/src/features/buy_sell.rs index 25fd80c9..2b4d2967 100644 --- a/pallets/nfts/src/features/buy_sell.rs +++ b/pallets/nfts/src/features/buy_sell.rs @@ -158,7 +158,7 @@ impl, I: 'static> Pallet { let old_owner = details.owner.clone(); - Self::do_transfer(Some(&buyer), collection, item, buyer.clone(), |_, _| Ok(()))?; + Self::do_transfer(collection, item, buyer.clone(), Some(&buyer), |_, _| Ok(()))?; Self::deposit_event(Event::ItemBought { collection, diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index 3c0e704d..9f5dc77b 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -25,11 +25,11 @@ use crate::*; impl, I: 'static> Pallet { /// Transfer an NFT to the specified destination account. /// - /// - `depositor`: The account reserving the `CollectionBalanceDeposit` from if `dest` holds no - /// items in the collection. /// - `collection`: The ID of the collection to which the NFT belongs. /// - `item`: The ID of the NFT to transfer. /// - `dest`: The destination account to which the NFT will be transferred. + /// - `depositor`: The account reserving the `CollectionBalanceDeposit` from if `dest` holds no + /// items in the collection. /// - `with_details`: A closure that provides access to the collection and item details, /// allowing customization of the transfer process. /// @@ -47,10 +47,10 @@ impl, I: 'static> Pallet { /// - If the collection or item is non-transferable /// ([`ItemsNonTransferable`](crate::Error::ItemsNonTransferable)). pub fn do_transfer( - depositor: Option<&T::AccountId>, collection: T::CollectionId, item: T::ItemId, dest: T::AccountId, + depositor: Option<&T::AccountId>, with_details: impl FnOnce( &CollectionDetailsFor, &mut ItemDetailsFor, diff --git a/pallets/nfts/src/impl_nonfungibles.rs b/pallets/nfts/src/impl_nonfungibles.rs index 22517397..e8fa4457 100644 --- a/pallets/nfts/src/impl_nonfungibles.rs +++ b/pallets/nfts/src/impl_nonfungibles.rs @@ -412,10 +412,10 @@ impl, I: 'static> Transfer for Pallet { destination: &T::AccountId, ) -> DispatchResult { // The item's owner pays for the deposit of `AccountBalance` if the `dest` holds no items - // in `collection`. A malicious actor could have a deposit reserved from `dest` without - // them knowing about the transfer. The deposit amount can be accounted for in the off chain - // price of the NFT. - Self::do_transfer(None, *collection, *item, destination.clone(), |_, _| Ok(())) + // in `collection`. If `dest` paid the deposit, a malicious actor could transfer NFTs to + // reserve involuntary deposits for `dest` . The deposit amount can be accounted for in the + // off chain price of the NFT. + Self::do_transfer(*collection, *item, destination.clone(), None, |_, _| Ok(())) } fn disable_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> DispatchResult { diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index c9f6a503..ef1f5e5f 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -1096,7 +1096,7 @@ pub mod pallet { // items in `collection`. A malicious actor could have a deposit reserved from `dest` // without them knowing about the transfer. The deposit amount can be accounted for // in the off chain price of the NFT. - Self::do_transfer(None, collection, item, dest, |_, details| { + Self::do_transfer(collection, item, dest, None, |_, details| { if details.owner != origin { Self::check_approval(&collection, &Some(item), &details.owner, &origin)?; }