diff --git a/pallets/nfts/src/benchmarking.rs b/pallets/nfts/src/benchmarking.rs index 7b384130..bcdbcc51 100644 --- a/pallets/nfts/src/benchmarking.rs +++ b/pallets/nfts/src/benchmarking.rs @@ -62,6 +62,27 @@ fn add_collection_metadata, I: 'static>() -> (T::AccountId, Account (caller, caller_lookup) } +fn approve_collection, I: 'static>( + index: u32, +) -> (T::AccountId, AccountIdLookupOf) { + let caller = Collection::::get(T::Helper::collection(0)).unwrap().owner; + if caller != whitelisted_caller() { + whitelist_account!(caller); + } + let caller_lookup = T::Lookup::unlookup(caller.clone()); + let delegate: T::AccountId = account("delegate", index, SEED); + let delegate_lookup = T::Lookup::unlookup(delegate.clone()); + let deadline = BlockNumberFor::::max_value(); + assert_ok!(Nfts::::approve_transfer( + SystemOrigin::Signed(caller.clone()).into(), + T::Helper::collection(0), + None, + delegate_lookup.clone(), + Some(deadline), + )); + (caller, caller_lookup) +} + fn mint_item, I: 'static>( index: u16, ) -> (T::ItemId, T::AccountId, AccountIdLookupOf) { @@ -571,7 +592,7 @@ benchmarks_instance_pallet! { } approve_transfer { - let i in 0..1; + let i in 0 .. 1; let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); @@ -589,7 +610,7 @@ benchmarks_instance_pallet! { } cancel_approval { - let i in 0..1; + let i in 0 .. 1; let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); @@ -598,9 +619,9 @@ benchmarks_instance_pallet! { let origin = SystemOrigin::Signed(caller.clone()).into(); let deadline = BlockNumberFor::::max_value(); let maybe_item = if i == 0 { - None + None } else { - Some(item) + Some(item) }; Nfts::::approve_transfer(origin, collection, maybe_item, delegate_lookup.clone(), Some(deadline))?; }: _(SystemOrigin::Signed(caller.clone()), collection, maybe_item, delegate_lookup) @@ -609,16 +630,27 @@ benchmarks_instance_pallet! { } clear_all_transfer_approvals { + let i in 0 .. 1; + let n in 0 .. T::ApprovalsLimit::get(); + let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); - let delegate: T::AccountId = account("delegate", 0, SEED); - let delegate_lookup = T::Lookup::unlookup(delegate.clone()); - let origin = SystemOrigin::Signed(caller.clone()).into(); - let deadline = BlockNumberFor::::max_value(); - Nfts::::approve_transfer(origin, collection, Some(item), delegate_lookup.clone(), Some(deadline))?; - }: _(SystemOrigin::Signed(caller.clone()), collection, item) + let (maybe_item, witness_allowances) = if i == 0 { + for i in 0 .. n { + approve_collection::(i); + } + (None, Some(n)) + } else { + let delegate: T::AccountId = account("delegate", 0, SEED); + let delegate_lookup = T::Lookup::unlookup(delegate.clone()); + let origin = SystemOrigin::Signed(caller.clone()).into(); + let deadline = BlockNumberFor::::max_value(); + Nfts::::approve_transfer(origin, collection, Some(item), delegate_lookup.clone(), Some(deadline))?; + (Some(item), None) + }; + }: _(SystemOrigin::Signed(caller.clone()), collection, maybe_item, witness_allowances) verify { - assert_last_event::(Event::AllApprovalsCancelled {collection, item, owner: caller}.into()); + assert_last_event::(Event::AllApprovalsCancelled {collection, item: maybe_item, owner: caller}.into()); } set_accept_ownership { diff --git a/pallets/nfts/src/common_functions.rs b/pallets/nfts/src/common_functions.rs index 9cc7d3b4..1aa151c3 100644 --- a/pallets/nfts/src/common_functions.rs +++ b/pallets/nfts/src/common_functions.rs @@ -39,11 +39,6 @@ impl, I: 'static> Pallet { Collection::::get(collection).map(|i| i.items) } - /// Get the allowances to spend items within the collection. - pub fn collection_allowances(collection: T::CollectionId) -> Option { - Collection::::get(collection).map(|i| i.allowances) - } - /// Get the metadata of the collection item. pub fn item_metadata( collection: T::CollectionId, diff --git a/pallets/nfts/src/features/approvals.rs b/pallets/nfts/src/features/approvals.rs index 22848f9d..df62a327 100644 --- a/pallets/nfts/src/features/approvals.rs +++ b/pallets/nfts/src/features/approvals.rs @@ -20,6 +20,7 @@ //! to have the functionality defined in this module. use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::BlockNumberFor; use crate::*; @@ -153,6 +154,14 @@ impl, I: 'static> Pallet { collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { + let collection_details = + Collection::::get(collection).ok_or(Error::::UnknownCollection)?; + + ensure!( + AccountAllowances::::get(collection, collection_details.owner) == 0, + Error::::DelegateApprovalConflict + ); + let mut details = Item::::get(collection, item).ok_or(Error::::UnknownCollection)?; @@ -165,7 +174,7 @@ impl, I: 'static> Pallet { Self::deposit_event(Event::AllApprovalsCancelled { collection, - item, + item: Some(item), owner: details.owner, }); @@ -178,17 +187,23 @@ impl, I: 'static> Pallet { /// a `delegate`. If `maybe_check_origin` is specified, the function ensures that the /// `check_origin` account is the owner of the collection, granting them permission to approve /// the transfer. The `delegate` is the account that will be allowed to take control of all - /// items within the collection. + /// items within the collection. Optionally, a `deadline` can be specified to set a time limit + /// for the approval. The `deadline` is expressed in block numbers and is added to the current + /// block number to determine the absolute deadline for the approval. After approving the + /// transfer, the function emits the `TransferApproved` event. /// /// - `maybe_check_origin`: The optional account that is required to be the owner of the item, /// granting permission to approve the transfer. If `None`, no permission check is performed. /// - `collection`: The identifier of the collection. /// - `delegate`: The account that will be allowed to take control of all items within the /// collection. + /// - `maybe_deadline`: The optional deadline (in block numbers) specifying the time limit for + /// the approval. pub(crate) fn do_approve_collection( maybe_check_origin: Option, collection: T::CollectionId, delegate: T::AccountId, + maybe_deadline: Option>, ) -> DispatchResult { ensure!( Self::is_pallet_feature_enabled(PalletFeature::Approvals), @@ -201,9 +216,9 @@ impl, I: 'static> Pallet { Error::::ItemsNonTransferable ); - let owner = Collection::::try_mutate( + let (owner, deadline) = Collection::::try_mutate( collection, - |maybe_collection_details| -> Result { + |maybe_collection_details| -> Result<(T::AccountId, Option>), DispatchError> { let collection_details = maybe_collection_details.as_mut().ok_or(Error::::UnknownCollection)?; let owner = collection_details.clone().owner; @@ -211,11 +226,21 @@ impl, I: 'static> Pallet { if let Some(check_origin) = maybe_check_origin { ensure!(check_origin == owner, Error::::NoPermission); } - Allowances::::mutate((&collection, &owner, &delegate), |allowance| { - *allowance = true; - }); - collection_details.allowances.saturating_inc(); - Ok(owner) + let now = frame_system::Pallet::::block_number(); + let deadline = maybe_deadline.map(|d| d.saturating_add(now)); + + AccountAllowances::::try_mutate(collection, &owner, |allowances| -> Result<(), DispatchError> { + ensure!(*allowances < T::ApprovalsLimit::get(), Error::::ReachedApprovalLimit); + Allowances::::mutate( + (&collection, &owner, &delegate), + |maybe_deadline| { + *maybe_deadline = Some(deadline); + }, + ); + allowances.saturating_inc(); + Ok(()) + })?; + Ok((owner, deadline)) }, )?; @@ -224,8 +249,9 @@ impl, I: 'static> Pallet { item: None, owner, delegate, - deadline: None, + deadline, }); + Ok(()) } @@ -238,8 +264,8 @@ impl, I: 'static> Pallet { /// function emits the `ApprovalCancelled` event. /// /// - `maybe_check_origin`: The optional account that is required to be the owner of the - /// collection, granting permission to cancel the approval. If `None`, no permission check is - /// performed. + /// collection or that the approval is past its deadline, granting permission to cancel the + /// approval. If `None`, no permission check is performed. /// - `collection`: The identifier of the collection /// - `delegate`: The account that was previously allowed to take control of all items within /// the collection. @@ -254,17 +280,98 @@ impl, I: 'static> Pallet { let collection_details = maybe_collection_details.as_mut().ok_or(Error::::UnknownCollection)?; let owner = collection_details.clone().owner; + let maybe_deadline = Allowances::::get((&collection, &owner, &delegate)) + .ok_or(Error::::NotDelegate)?; + + let is_past_deadline = if let Some(deadline) = maybe_deadline { + let now = frame_system::Pallet::::block_number(); + now > deadline + } else { + false + }; + + if !is_past_deadline { + if let Some(check_origin) = maybe_check_origin { + ensure!(check_origin == owner, Error::::NoPermission); + } + } Allowances::::remove((&collection, &owner, &delegate)); + AccountAllowances::::mutate(collection, &owner, |allowances| { + allowances.saturating_dec(); + }); + Ok(owner) + }, + )?; + + Self::deposit_event(Event::ApprovalCancelled { collection, owner, item: None, delegate }); + + Ok(()) + } + + /// Clears all collection approvals. + /// + /// This function is used to clear all approvals to transfer all items within the collections. + /// If `maybe_check_origin` is specified, the function ensures that the `check_origin` account + /// is the owner of the item, granting permission to clear all collection approvals. After + /// clearing all approvals, the function emits the `AllApprovalsCancelled` event. + /// + /// - `maybe_check_origin`: The optional account that is required to be the owner of the + /// collection, granting permission to clear all collection approvals. If `None`, no + /// permission check is performed. + /// - `collection`: The collection ID containing the item. + pub(crate) fn do_clear_all_collection_approvals( + maybe_check_origin: Option, + collection: T::CollectionId, + witness_allowances: u32, + ) -> DispatchResult { + let owner = Collection::::try_mutate( + collection, + |maybe_collection_details| -> Result { + let collection_details = + maybe_collection_details.as_mut().ok_or(Error::::UnknownCollection)?; + let owner = collection_details.clone().owner; + if let Some(check_origin) = maybe_check_origin { - ensure!(check_origin == owner, Error::::NoPermission); + ensure!(check_origin == owner.clone(), Error::::NoPermission); } - collection_details.allowances.saturating_dec(); + + AccountAllowances::::try_mutate( + collection, + &owner, + |allowances| -> Result<(), DispatchError> { + ensure!(*allowances == witness_allowances, Error::::BadWitness); + let _ = Allowances::::clear_prefix( + (collection, owner.clone()), + *allowances, + None, + ); + *allowances = 0; + + Ok(()) + }, + )?; Ok(owner) }, )?; - Self::deposit_event(Event::ApprovalCancelled { collection, owner, item: None, delegate }); + Self::deposit_event(Event::AllApprovalsCancelled { collection, item: None, owner }); + + Ok(()) + } + + // Check if a `delegate` has a permission to spend the collection. + fn check_collection_allowance( + collection: &T::CollectionId, + owner: &T::AccountId, + delegate: &T::AccountId, + ) -> Result<(), DispatchError> { + let maybe_deadline = Allowances::::get((&collection, &owner, &delegate)) + .ok_or(Error::::NoPermission)?; + if let Some(deadline) = maybe_deadline { + let block_number = frame_system::Pallet::::block_number(); + ensure!(block_number <= deadline, Error::::ApprovalExpired); + } Ok(()) } @@ -286,23 +393,23 @@ impl, I: 'static> Pallet { delegate: &T::AccountId, ) -> Result<(), DispatchError> { // Check if a `delegate` has a permission to spend the collection. - if Allowances::::get((&collection, &owner, &delegate)) { - if let Some(item) = item { - Item::::get(collection, item).ok_or(Error::::UnknownItem)?; + let check_collection_allowance_error = + match Self::check_collection_allowance(collection, owner, delegate) { + Ok(()) => return Ok(()), + Err(error) => error, }; - return Ok(()); - } // Check if a `delegate` has a permission to spend the collection item. if let Some(item) = item { let details = Item::::get(collection, item).ok_or(Error::::UnknownItem)?; - let deadline = details.approvals.get(delegate).ok_or(Error::::NoPermission)?; - if let Some(d) = deadline { + let maybe_deadline = + details.approvals.get(delegate).ok_or(Error::::NoPermission)?; + if let Some(deadline) = maybe_deadline { let block_number = frame_system::Pallet::::block_number(); - ensure!(block_number <= *d, Error::::ApprovalExpired); + ensure!(block_number <= *deadline, Error::::ApprovalExpired); } return Ok(()); }; - Err(Error::::NoPermission.into()) + Err(check_collection_allowance_error) } } diff --git a/pallets/nfts/src/features/create_delete_collection.rs b/pallets/nfts/src/features/create_delete_collection.rs index f5a01ebe..5be73fc9 100644 --- a/pallets/nfts/src/features/create_delete_collection.rs +++ b/pallets/nfts/src/features/create_delete_collection.rs @@ -55,7 +55,6 @@ impl, I: 'static> Pallet { item_metadatas: 0, item_configs: 0, attributes: 0, - allowances: 0, }, ); CollectionRoleOf::::insert( @@ -97,6 +96,9 @@ impl, I: 'static> Pallet { /// ([`NoPermission`](crate::Error::NoPermission)). /// - If the collection is not empty (contains items) /// ([`CollectionNotEmpty`](crate::Error::CollectionNotEmpty)). + /// - If the collection approvals is not empty (contains permissions to transfer all items + /// within the collection) + /// ([`CollectionApprovalsNotEmpty`](crate::Error::CollectionApprovalsNotEmpty)). /// - If the `witness` does not match the actual collection details /// ([`BadWitness`](crate::Error::BadWitness)). pub fn do_destroy_collection( @@ -110,8 +112,9 @@ impl, I: 'static> Pallet { if let Some(check_owner) = maybe_check_owner { ensure!(collection_details.owner == check_owner, Error::::NoPermission); } + let allowances = AccountAllowances::::get(collection, &collection_details.owner); ensure!(collection_details.items == 0, Error::::CollectionNotEmpty); - ensure!(collection_details.allowances == 0, Error::::AllowancesNotEmpty); + ensure!(allowances == 0, Error::::CollectionApprovalsNotEmpty); ensure!(collection_details.attributes == witness.attributes, Error::::BadWitness); ensure!( collection_details.item_metadatas == witness.item_metadatas, diff --git a/pallets/nfts/src/features/transfer.rs b/pallets/nfts/src/features/transfer.rs index c0896335..c8f66e4f 100644 --- a/pallets/nfts/src/features/transfer.rs +++ b/pallets/nfts/src/features/transfer.rs @@ -149,6 +149,15 @@ impl, I: 'static> Pallet { return Ok(()); } + // All collection approvals need to be removed because otherwise + // pre-approve attack would be possible, where the owner can approve their + // second account before making the transaction and then claiming all items + // within the collection back. + ensure!( + AccountAllowances::::get(collection, &details.owner) == 0, + Error::::CollectionApprovalsNotEmpty + ); + // Move the deposit to the new owner. T::Currency::repatriate_reserved( &details.owner, diff --git a/pallets/nfts/src/lib.rs b/pallets/nfts/src/lib.rs index 28b0c414..d3dfbce2 100644 --- a/pallets/nfts/src/lib.rs +++ b/pallets/nfts/src/lib.rs @@ -428,7 +428,19 @@ pub mod pallet { // Delegate Id. NMapKey, ), - bool, + Option>, + OptionQuery, + >; + + /// Number of collection approvals that owners granted. + #[pallet::storage] + pub type AccountAllowances, I: 'static = ()> = StorageDoubleMap< + _, + Twox64Concat, + T::CollectionId, + Blake2_128Concat, + T::AccountId, + u32, ValueQuery, >; @@ -504,8 +516,12 @@ pub mod pallet { owner: T::AccountId, delegate: T::AccountId, }, - /// All approvals of an item got cancelled. - AllApprovalsCancelled { collection: T::CollectionId, item: T::ItemId, owner: T::AccountId }, + /// All approvals of an item or a collection got cancelled. + AllApprovalsCancelled { + collection: T::CollectionId, + item: Option, + owner: T::AccountId, + }, /// A `collection` has had its config changed by the `Force` origin. CollectionConfigChanged { collection: T::CollectionId }, /// New metadata has been set for a `collection`. @@ -681,7 +697,7 @@ pub mod pallet { NotForSale, /// The provided bid is too low. BidTooLow, - /// The item has reached its approval limit. + /// The collection or item has reached its approval limit. ReachedApprovalLimit, /// The deadline has already expired. DeadlineExpired, @@ -719,8 +735,10 @@ pub mod pallet { CollectionNotEmpty, /// The witness data should be provided. WitnessRequired, - /// Cant' delete collections whose allowances. - AllowancesNotEmpty, + /// Cant' delete collections whose approvals. + CollectionApprovalsNotEmpty, + /// Collection approval and item approval conflicts. + DelegateApprovalConflict, } #[pallet::call] @@ -1339,7 +1357,12 @@ pub mod pallet { delegate, maybe_deadline, ), - None => Self::do_approve_collection(maybe_check_origin, collection, delegate), + None => Self::do_approve_collection( + maybe_check_origin, + collection, + delegate, + maybe_deadline, + ), } } @@ -1351,8 +1374,8 @@ pub mod pallet { /// /// Arguments: /// - `collection`: The collection of the item of whose approval will be cancelled. - /// - `item`: The optional item of the collection of whose approval will be cancelled. If - /// not provided, an allowance to transfer all items within the collection will be + /// - `maybe_item`: The optional item of the collection of whose approval will be cancelled. + /// If not provided, an allowance to transfer all items within the collection will be /// cancelled. /// - `delegate`: The account that is going to loose their approval. /// @@ -1386,22 +1409,39 @@ pub mod pallet { /// /// Arguments: /// - `collection`: The collection of the item of whose approvals will be cleared. - /// - `item`: The item of the collection of whose approvals will be cleared. + /// - `maybe_item`: The item of the collection of whose approvals will be cleared. The + /// optional item of the collection of whose approval will be cleared. If not provided, + /// all approvals to transfer items within collection /// /// Emits `AllApprovalsCancelled` on success. /// /// Weight: `O(1)` #[pallet::call_index(17)] - #[pallet::weight(T::WeightInfo::clear_all_transfer_approvals())] + #[pallet::weight( + T::WeightInfo::clear_all_transfer_approvals( + maybe_item.is_some() as u32, + witness_allowances.unwrap_or_default() + ) + )] pub fn clear_all_transfer_approvals( origin: OriginFor, collection: T::CollectionId, - item: T::ItemId, + maybe_item: Option, + witness_allowances: Option, ) -> DispatchResult { let maybe_check_origin = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; - Self::do_clear_all_transfer_approvals(maybe_check_origin, collection, item) + + match maybe_item { + Some(item) => + Self::do_clear_all_transfer_approvals(maybe_check_origin, collection, item), + None => Self::do_clear_all_collection_approvals( + maybe_check_origin, + collection, + witness_allowances.unwrap_or_default(), + ), + } } /// Disallows changing the metadata or attributes of the item. diff --git a/pallets/nfts/src/tests.rs b/pallets/nfts/src/tests.rs index 1f63a192..ead3d353 100644 --- a/pallets/nfts/src/tests.rs +++ b/pallets/nfts/src/tests.rs @@ -343,7 +343,7 @@ fn destroy_should_work() { 0, Nfts::get_destroy_witness(&0).unwrap() ), - Error::::AllowancesNotEmpty + Error::::CollectionApprovalsNotEmpty ); assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, None, account(3))); assert_ok!(Nfts::destroy( @@ -661,6 +661,19 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(2)), Some(0))); assert_eq!(System::consumers(&account(2)), 1); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + 0, + None, + account(3), + None + )); + assert_noop!( + Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(2)), + Error::::CollectionApprovalsNotEmpty + ); + assert_ok!(Nfts::cancel_approval(RuntimeOrigin::signed(account(1)), 0, None, account(3),)); + assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(2))); assert_eq!(System::consumers(&account(2)), 1); // one consumer is added due to deposit repatriation @@ -1890,7 +1903,7 @@ fn check_allowance_works() { None )); - // collection transfer approved. + // transfer all items within the collection approved (without deadline). assert_noop!( Nfts::check_allowance(&1, &None, &account(1), &account(2)), Error::::NoPermission @@ -1902,7 +1915,7 @@ fn check_allowance_works() { assert_ok!(Nfts::check_allowance(&0, &None, &account(1), &account(2))); assert_ok!(Nfts::check_allowance(&0, &Some(42), &account(1), &account(2))); - // collection item transfer approved. + // transfer a collection item approved (without deadline). assert_ok!(Nfts::approve_transfer( RuntimeOrigin::signed(account(2)), 0, @@ -1920,6 +1933,39 @@ fn check_allowance_works() { Error::::NoPermission ); assert_ok!(Nfts::check_allowance(&0, &Some(42), &account(2), &account(3))); + + // transfer all items within the collection approved (with deadline). + let current_block = 1; + let deadline = current_block + 10; + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(2)), + 0, + Some(42), + account(3), + Some(deadline) + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + 0, + None, + account(3), + Some(deadline + 1) + )); + System::set_block_number(deadline + 2); + assert_noop!( + Nfts::check_allowance(&0, &Some(42), &account(2), &account(3)), + Error::::ApprovalExpired + ); + assert_ok!(Nfts::check_allowance(&0, &None, &account(1), &account(3))); + System::set_block_number(deadline + 3); + assert_noop!( + Nfts::check_allowance(&0, &Some(42), &account(2), &account(3)), + Error::::ApprovalExpired + ); + assert_noop!( + Nfts::check_allowance(&0, &None, &account(1), &account(3)), + Error::::ApprovalExpired + ); }); } @@ -2043,8 +2089,8 @@ fn cancel_approval_collection_works_with_admin() { owner: account(1), delegate: account(3) })); - assert_eq!(Allowances::::get((0, account(2), account(3))), false); - assert_eq!(Nfts::collection_allowances(0).unwrap(), 0); + assert_eq!(Allowances::::get((0, account(2), account(3))).is_some(), false); + assert_eq!(AccountAllowances::::get(0, account(2)), 0); assert_noop!( Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(4)), @@ -2148,6 +2194,32 @@ fn approvals_limit_works() { }); } +#[test] +fn collection_approvals_limit_works() { + new_test_ext().execute_with(|| { + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + account(1), + default_collection_config() + )); + + for i in 3..13 { + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + 0, + None, + account(i), + None + )); + } + // the limit is 10 + assert_noop!( + Nfts::approve_transfer(RuntimeOrigin::signed(account(1)), 0, None, account(14), None), + Error::::ReachedApprovalLimit + ); + }); +} + #[test] fn approval_collection_works_with_admin() { new_test_ext().execute_with(|| { @@ -2200,8 +2272,8 @@ fn approval_collection_works_with_admin() { delegate: account(3), deadline: None })); - assert_eq!(Allowances::::get((0, account(1), account(3))), true); - assert_eq!(Nfts::collection_allowances(0).unwrap(), 1); + assert!(Allowances::::get((0, account(1), account(3))).is_some()); + assert_eq!(AccountAllowances::::get(0, account(1)), 1); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(4))); }); } @@ -2383,15 +2455,25 @@ fn clear_all_transfer_approvals_works() { )); assert_noop!( - Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(3)), 0, 42), + Nfts::clear_all_transfer_approvals( + RuntimeOrigin::signed(account(3)), + 0, + Some(42), + None + ), Error::::NoPermission ); - assert_ok!(Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(2)), 0, 42)); + assert_ok!(Nfts::clear_all_transfer_approvals( + RuntimeOrigin::signed(account(2)), + 0, + Some(42), + None + )); assert!(events().contains(&Event::::AllApprovalsCancelled { collection: 0, - item: 42, + item: Some(42), owner: account(2), })); assert_eq!(approvals(0, 42), vec![]); @@ -2407,6 +2489,72 @@ fn clear_all_transfer_approvals_works() { }); } +#[test] +fn clear_all_collection_approvals_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), + default_item_config() + )); + + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + 0, + None, + account(3), + None + )); + assert_ok!(Nfts::approve_transfer( + RuntimeOrigin::signed(account(1)), + 0, + None, + account(4), + None + )); + + assert_noop!( + Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(2)), 0, None, Some(2)), + Error::::NoPermission + ); + + assert_noop!( + Nfts::clear_all_transfer_approvals(RuntimeOrigin::signed(account(1)), 0, None, Some(1)), + Error::::BadWitness + ); + + assert_ok!(Nfts::clear_all_transfer_approvals( + RuntimeOrigin::signed(account(1)), + 0, + None, + Some(2) + )); + assert!(events().contains(&Event::::AllApprovalsCancelled { + collection: 0, + item: None, + owner: account(1), + })); + assert_eq!(Allowances::::iter_prefix((0, account(1))).count(), 0); + assert_eq!(AccountAllowances::::get(0, account(1)), 0); + + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(account(3)), 0, 42, account(5)), + Error::::NoPermission + ); + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(account(4)), 0, 42, account(5)), + Error::::NoPermission + ); + }); +} + #[test] fn total_supply_should_works() { new_test_ext().execute_with(|| { diff --git a/pallets/nfts/src/types.rs b/pallets/nfts/src/types.rs index 56c9cbaf..941da6ca 100644 --- a/pallets/nfts/src/types.rs +++ b/pallets/nfts/src/types.rs @@ -106,8 +106,6 @@ pub struct CollectionDetails { pub item_configs: u32, /// The total number of attributes for this collection. pub attributes: u32, - /// The total number of allowances to spend all items within collections. - pub allowances: u32, } /// Witness data for the destroy transactions. diff --git a/pallets/nfts/src/weights.rs b/pallets/nfts/src/weights.rs index 0abd73dc..28e7056c 100644 --- a/pallets/nfts/src/weights.rs +++ b/pallets/nfts/src/weights.rs @@ -61,7 +61,7 @@ pub trait WeightInfo { fn clear_collection_metadata() -> Weight; fn approve_transfer(i: u32, ) -> Weight; fn cancel_approval(i: u32, ) -> Weight; - fn clear_all_transfer_approvals() -> Weight; + fn clear_all_transfer_approvals(i: u32, n: u32, ) -> Weight; fn set_accept_ownership() -> Weight; fn set_collection_max_supply() -> Weight; fn update_mint_settings() -> Weight; @@ -117,7 +117,9 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:0) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionRoleOf` (r:1 w:1) @@ -135,19 +137,17 @@ 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: `32169 + a * (366 ±0)` + // Measured: `32165 + a * (366 ±0)` // Estimated: `2523990 + a * (2954 ±0)` - // Minimum execution time: 976_000_000 picoseconds. - Weight::from_parts(489_392_768, 2523990) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(2_634, 0).saturating_mul(m.into())) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(406_042, 0).saturating_mul(c.into())) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(5_358_742, 0).saturating_mul(a.into())) - .saturating_add(T::DbWeight::get().reads(1004_u64)) + // Minimum execution time: 986_000_000 picoseconds. + Weight::from_parts(985_849_637, 2523990) + // Standard Error: 13_005 + .saturating_add(Weight::from_parts(6_541, 0).saturating_mul(c.into())) + // Standard Error: 13_005 + .saturating_add(Weight::from_parts(5_088_456, 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)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(a.into()))) @@ -228,8 +228,8 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } - /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + //// Storage: `Nfts::Collection` (r:1 w:1) + /// 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`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) @@ -248,10 +248,10 @@ 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: `609` + // Measured: `605` // Estimated: `6068` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(41_000_000, 6068) + // Minimum execution time: 38_000_000 picoseconds. + Weight::from_parts(39_000_000, 6068) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -317,16 +317,16 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, 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::CollectionAccount` (r:0 w:2) /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `417` // Estimated: `3593` - // Minimum execution time: 19_000_000 picoseconds. + // Minimum execution time: 18_000_000 picoseconds. Weight::from_parts(19_000_000, 3593) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -543,47 +543,60 @@ 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::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:1) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::Allowances` (r:1 w:1) - /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(801), added: 3276, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 1]`. fn approve_transfer(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `304 + i * (33 ±0)` - // Estimated: `4266 + i * (2163 ±0)` + // Measured: `360` + // Estimated: `3578 + i * (2163 ±0)` // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(15_371_428, 4266) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + Weight::from_parts(18_575_510, 3578) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2163).saturating_mul(i.into())) } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::Allowances` (r:1 w:1) - /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(801), added: 3276, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:1) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 1]`. fn cancel_approval(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `397` - // Estimated: `4266 + i * (2163 ±0)` + // Measured: `496` + // Estimated: `3578 + i * (2163 ±0)` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(17_800_000, 4266) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + Weight::from_parts(21_048_979, 3578) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2163).saturating_mul(i.into())) } + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:0) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) - fn clear_all_transfer_approvals() -> Weight { + /// Storage: `Nfts::Allowances` (r:20 w:20) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// The range of component `i` is `[0, 1]`. + /// The range of component `n` is `[0, 20]`. + fn clear_all_transfer_approvals(i: u32, _n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `345` - // Estimated: `4326` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 4326) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `1614` + // Estimated: `52750 + i * (4325 ±496_172_781_796_926)` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(35_525_221, 52750) + .saturating_add(T::DbWeight::get().reads(22_u64)) + .saturating_add(T::DbWeight::get().writes(22_u64)) + .saturating_add(Weight::from_parts(0, 4325).saturating_mul(i.into())) } /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) @@ -834,7 +847,9 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:0) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::ItemMetadataOf` (r:1 w:0) /// Proof: `Nfts::ItemMetadataOf` (`max_values`: None, `max_size`: Some(347), added: 2822, mode: `MaxEncodedLen`) /// Storage: `Nfts::CollectionRoleOf` (r:1 w:1) @@ -852,19 +867,17 @@ 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: `32169 + a * (366 ±0)` + // Measured: `32165 + a * (366 ±0)` // Estimated: `2523990 + a * (2954 ±0)` - // Minimum execution time: 976_000_000 picoseconds. - Weight::from_parts(489_392_768, 2523990) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(2_634, 0).saturating_mul(m.into())) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(406_042, 0).saturating_mul(c.into())) - // Standard Error: 29_146 - .saturating_add(Weight::from_parts(5_358_742, 0).saturating_mul(a.into())) - .saturating_add(RocksDbWeight::get().reads(1004_u64)) + // Minimum execution time: 986_000_000 picoseconds. + Weight::from_parts(985_849_637, 2523990) + // Standard Error: 13_005 + .saturating_add(Weight::from_parts(6_541, 0).saturating_mul(c.into())) + // Standard Error: 13_005 + .saturating_add(Weight::from_parts(5_088_456, 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)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(a.into()))) @@ -946,7 +959,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(8_u64)) } /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// 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`) /// Storage: `Nfts::CollectionConfigOf` (r:1 w:0) @@ -965,10 +978,10 @@ 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: `609` + // Measured: `605` // Estimated: `6068` - // Minimum execution time: 39_000_000 picoseconds. - Weight::from_parts(41_000_000, 6068) + // Minimum execution time: 38_000_000 picoseconds. + Weight::from_parts(39_000_000, 6068) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -1034,16 +1047,16 @@ impl WeightInfo for () { /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, 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::CollectionAccount` (r:0 w:2) /// Proof: `Nfts::CollectionAccount` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn transfer_ownership() -> Weight { // Proof Size summary in bytes: - // Measured: `421` + // Measured: `417` // Estimated: `3593` - // Minimum execution time: 19_000_000 picoseconds. + // Minimum execution time: 18_000_000 picoseconds. Weight::from_parts(19_000_000, 3593) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -1260,47 +1273,60 @@ 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::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:1) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::Allowances` (r:1 w:1) - /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(801), added: 3276, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 1]`. fn approve_transfer(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `304 + i * (33 ±0)` - // Estimated: `4266 + i * (2163 ±0)` + // Measured: `360` + // Estimated: `3578 + i * (2163 ±0)` // Minimum execution time: 13_000_000 picoseconds. - Weight::from_parts(15_371_428, 4266) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + Weight::from_parts(18_575_510, 3578) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2163).saturating_mul(i.into())) } /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) /// Storage: `Nfts::Collection` (r:1 w:1) - /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(88), added: 2563, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) /// Storage: `Nfts::Allowances` (r:1 w:1) - /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(801), added: 3276, mode: `MaxEncodedLen`) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:1) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// The range of component `i` is `[0, 1]`. fn cancel_approval(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `397` - // Estimated: `4266 + i * (2163 ±0)` + // Measured: `496` + // Estimated: `3578 + i * (2163 ±0)` // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(17_800_000, 4266) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + Weight::from_parts(21_048_979, 3578) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 2163).saturating_mul(i.into())) } + /// Storage: `Nfts::Collection` (r:1 w:0) + /// Proof: `Nfts::Collection` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `Nfts::AccountAllowances` (r:1 w:0) + /// Proof: `Nfts::AccountAllowances` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `Nfts::Item` (r:1 w:1) /// Proof: `Nfts::Item` (`max_values`: None, `max_size`: Some(861), added: 3336, mode: `MaxEncodedLen`) - fn clear_all_transfer_approvals() -> Weight { + /// Storage: `Nfts::Allowances` (r:20 w:20) + /// Proof: `Nfts::Allowances` (`max_values`: None, `max_size`: Some(113), added: 2588, mode: `MaxEncodedLen`) + /// The range of component `i` is `[0, 1]`. + /// The range of component `n` is `[0, 20]`. + fn clear_all_transfer_approvals(i: u32, _n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `345` - // Estimated: `4326` - // Minimum execution time: 11_000_000 picoseconds. - Weight::from_parts(11_000_000, 4326) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `1614` + // Estimated: `52750 + i * (4325 ±496_172_781_796_926)` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(35_525_221, 52750) + .saturating_add(RocksDbWeight::get().reads(22_u64)) + .saturating_add(RocksDbWeight::get().writes(22_u64)) + .saturating_add(Weight::from_parts(0, 4325).saturating_mul(i.into())) } /// Storage: `Nfts::OwnershipAcceptance` (r:1 w:1) /// Proof: `Nfts::OwnershipAcceptance` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`)