From 166ae5ae1295c2431c83421439c3869d4f40de7e Mon Sep 17 00:00:00 2001
From: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
Date: Tue, 19 Dec 2023 15:23:58 +0200
Subject: [PATCH] [NFTs] Fix consumers issue (#2653)

When we call the `set_accept_ownership` method, we increase the number
of account consumers, but then we don't decrease it on collection
transfer, which leads to the wrong consumers number an account has.
---
 substrate/frame/nfts/src/features/transfer.rs | 17 ++++++++-------
 substrate/frame/nfts/src/lib.rs               |  6 +++---
 substrate/frame/nfts/src/tests.rs             |  5 +++++
 substrate/frame/uniques/src/lib.rs            | 21 +++++++++++--------
 substrate/frame/uniques/src/tests.rs          |  3 +++
 5 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/substrate/frame/nfts/src/features/transfer.rs b/substrate/frame/nfts/src/features/transfer.rs
index 0471bd67b29..ac73d283ee0 100644
--- a/substrate/frame/nfts/src/features/transfer.rs
+++ b/substrate/frame/nfts/src/features/transfer.rs
@@ -124,10 +124,10 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	pub(crate) fn do_transfer_ownership(
 		origin: T::AccountId,
 		collection: T::CollectionId,
-		owner: T::AccountId,
+		new_owner: T::AccountId,
 	) -> DispatchResult {
 		// Check if the new owner is acceptable based on the collection's acceptance settings.
-		let acceptable_collection = OwnershipAcceptance::<T, I>::get(&owner);
+		let acceptable_collection = OwnershipAcceptance::<T, I>::get(&new_owner);
 		ensure!(acceptable_collection.as_ref() == Some(&collection), Error::<T, I>::Unaccepted);
 
 		// Try to retrieve and mutate the collection details.
@@ -135,27 +135,28 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 			let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
 			// Check if the `origin` is the current owner of the collection.
 			ensure!(origin == details.owner, Error::<T, I>::NoPermission);
-			if details.owner == owner {
+			if details.owner == new_owner {
 				return Ok(())
 			}
 
 			// Move the deposit to the new owner.
 			T::Currency::repatriate_reserved(
 				&details.owner,
-				&owner,
+				&new_owner,
 				details.owner_deposit,
 				Reserved,
 			)?;
 
 			// Update account ownership information.
 			CollectionAccount::<T, I>::remove(&details.owner, &collection);
-			CollectionAccount::<T, I>::insert(&owner, &collection, ());
+			CollectionAccount::<T, I>::insert(&new_owner, &collection, ());
 
-			details.owner = owner.clone();
-			OwnershipAcceptance::<T, I>::remove(&owner);
+			details.owner = new_owner.clone();
+			OwnershipAcceptance::<T, I>::remove(&new_owner);
+			frame_system::Pallet::<T>::dec_consumers(&new_owner);
 
 			// Emit `OwnerChanged` event.
-			Self::deposit_event(Event::OwnerChanged { collection, new_owner: owner });
+			Self::deposit_event(Event::OwnerChanged { collection, new_owner });
 			Ok(())
 		})
 	}
diff --git a/substrate/frame/nfts/src/lib.rs b/substrate/frame/nfts/src/lib.rs
index 92b27432ab2..a7d505e2e39 100644
--- a/substrate/frame/nfts/src/lib.rs
+++ b/substrate/frame/nfts/src/lib.rs
@@ -1153,11 +1153,11 @@ pub mod pallet {
 		pub fn transfer_ownership(
 			origin: OriginFor<T>,
 			collection: T::CollectionId,
-			owner: AccountIdLookupOf<T>,
+			new_owner: AccountIdLookupOf<T>,
 		) -> DispatchResult {
 			let origin = ensure_signed(origin)?;
-			let owner = T::Lookup::lookup(owner)?;
-			Self::do_transfer_ownership(origin, collection, owner)
+			let new_owner = T::Lookup::lookup(new_owner)?;
+			Self::do_transfer_ownership(origin, collection, new_owner)
 		}
 
 		/// Change the Issuer, Admin and Freezer of a collection.
diff --git a/substrate/frame/nfts/src/tests.rs b/substrate/frame/nfts/src/tests.rs
index 0c32aea2be0..9e521537534 100644
--- a/substrate/frame/nfts/src/tests.rs
+++ b/substrate/frame/nfts/src/tests.rs
@@ -614,8 +614,13 @@ fn transfer_owner_should_work() {
 			Nfts::transfer_ownership(RuntimeOrigin::signed(account(1)), 0, account(2)),
 			Error::<Test>::Unaccepted
 		);
+		assert_eq!(System::consumers(&account(2)), 0);
+
 		assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(account(2)), Some(0)));
+		assert_eq!(System::consumers(&account(2)), 1);
+
 		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
 
 		assert_eq!(collections(), vec![(account(2), 0)]);
 		assert_eq!(Balances::total_balance(&account(1)), 98);
diff --git a/substrate/frame/uniques/src/lib.rs b/substrate/frame/uniques/src/lib.rs
index 63a6e83de07..253a318e474 100644
--- a/substrate/frame/uniques/src/lib.rs
+++ b/substrate/frame/uniques/src/lib.rs
@@ -856,34 +856,37 @@ pub mod pallet {
 		pub fn transfer_ownership(
 			origin: OriginFor<T>,
 			collection: T::CollectionId,
-			owner: AccountIdLookupOf<T>,
+			new_owner: AccountIdLookupOf<T>,
 		) -> DispatchResult {
 			let origin = ensure_signed(origin)?;
-			let owner = T::Lookup::lookup(owner)?;
+			let new_owner = T::Lookup::lookup(new_owner)?;
 
-			let acceptable_collection = OwnershipAcceptance::<T, I>::get(&owner);
+			let acceptable_collection = OwnershipAcceptance::<T, I>::get(&new_owner);
 			ensure!(acceptable_collection.as_ref() == Some(&collection), Error::<T, I>::Unaccepted);
 
 			Collection::<T, I>::try_mutate(collection.clone(), |maybe_details| {
 				let details = maybe_details.as_mut().ok_or(Error::<T, I>::UnknownCollection)?;
 				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
-				if details.owner == owner {
+				if details.owner == new_owner {
 					return Ok(())
 				}
 
 				// Move the deposit to the new owner.
 				T::Currency::repatriate_reserved(
 					&details.owner,
-					&owner,
+					&new_owner,
 					details.total_deposit,
 					Reserved,
 				)?;
+
 				CollectionAccount::<T, I>::remove(&details.owner, &collection);
-				CollectionAccount::<T, I>::insert(&owner, &collection, ());
-				details.owner = owner.clone();
-				OwnershipAcceptance::<T, I>::remove(&owner);
+				CollectionAccount::<T, I>::insert(&new_owner, &collection, ());
+
+				details.owner = new_owner.clone();
+				OwnershipAcceptance::<T, I>::remove(&new_owner);
+				frame_system::Pallet::<T>::dec_consumers(&new_owner);
 
-				Self::deposit_event(Event::OwnerChanged { collection, new_owner: owner });
+				Self::deposit_event(Event::OwnerChanged { collection, new_owner });
 				Ok(())
 			})
 		}
diff --git a/substrate/frame/uniques/src/tests.rs b/substrate/frame/uniques/src/tests.rs
index 52f7df3b5ef..351dac09f7f 100644
--- a/substrate/frame/uniques/src/tests.rs
+++ b/substrate/frame/uniques/src/tests.rs
@@ -254,8 +254,11 @@ fn transfer_owner_should_work() {
 			Uniques::transfer_ownership(RuntimeOrigin::signed(1), 0, 2),
 			Error::<Test>::Unaccepted
 		);
+		assert_eq!(System::consumers(&2), 0);
 		assert_ok!(Uniques::set_accept_ownership(RuntimeOrigin::signed(2), Some(0)));
+		assert_eq!(System::consumers(&2), 1);
 		assert_ok!(Uniques::transfer_ownership(RuntimeOrigin::signed(1), 0, 2));
+		assert_eq!(System::consumers(&2), 1);
 
 		assert_eq!(collections(), vec![(2, 0)]);
 		assert_eq!(Balances::total_balance(&1), 98);
-- 
GitLab