From 9d6261892814fa27c97881c0321c008d7340b54b Mon Sep 17 00:00:00 2001
From: Liam Aharon <liam.aharon@hotmail.com>
Date: Sat, 6 Apr 2024 16:10:46 +1100
Subject: [PATCH] `pallet-uniques`: decrement `total_deposit` when clearing
 collection metadata (#3976)

Decrements `total_deposit` when collection metadata is cleared in
`pallet-nfts` and `pallet-uniques`.
---
 prdoc/pr_3976.prdoc                           | 14 +++++++
 substrate/frame/nfts/src/features/metadata.rs |  4 +-
 substrate/frame/nfts/src/tests.rs             | 41 +++++++++++++++++++
 substrate/frame/uniques/src/lib.rs            |  4 +-
 substrate/frame/uniques/src/tests.rs          | 38 +++++++++++++++++
 5 files changed, 99 insertions(+), 2 deletions(-)
 create mode 100644 prdoc/pr_3976.prdoc

diff --git a/prdoc/pr_3976.prdoc b/prdoc/pr_3976.prdoc
new file mode 100644
index 00000000000..95afab6d424
--- /dev/null
+++ b/prdoc/pr_3976.prdoc
@@ -0,0 +1,14 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Decrement total_deposit when clearing collection metadata
+
+doc:
+  - audience: Runtime Dev
+    description: Decrements total_deposit by the appropriate amount when collection metadata is cleared.
+
+crates:
+  - name: pallet-uniques
+    bump: patch
+  - name: pallet-nfts
+    bump: patch
diff --git a/substrate/frame/nfts/src/features/metadata.rs b/substrate/frame/nfts/src/features/metadata.rs
index e177f39bb8b..85edd294d50 100644
--- a/substrate/frame/nfts/src/features/metadata.rs
+++ b/substrate/frame/nfts/src/features/metadata.rs
@@ -247,7 +247,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 			);
 		}
 
-		let details =
+		let mut details =
 			Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
 		let collection_config = Self::get_collection_config(&collection)?;
 
@@ -260,6 +260,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		CollectionMetadataOf::<T, I>::try_mutate_exists(collection, |metadata| {
 			let deposit = metadata.take().ok_or(Error::<T, I>::UnknownCollection)?.deposit;
 			T::Currency::unreserve(&details.owner, deposit);
+			details.owner_deposit.saturating_reduce(deposit);
+			Collection::<T, I>::insert(&collection, details);
 			Self::deposit_event(Event::CollectionMetadataCleared { collection });
 			Ok(())
 		})
diff --git a/substrate/frame/nfts/src/tests.rs b/substrate/frame/nfts/src/tests.rs
index 6bf9427f4e6..4d23aca64ce 100644
--- a/substrate/frame/nfts/src/tests.rs
+++ b/substrate/frame/nfts/src/tests.rs
@@ -3835,3 +3835,44 @@ fn basic_create_collection_with_id_should_work() {
 		);
 	});
 }
+
+#[test]
+fn clear_collection_metadata_works() {
+	new_test_ext().execute_with(|| {
+		// Start with an account with 100 tokens, 10 of which are reserved
+		Balances::make_free_balance_be(&account(1), 100);
+		Balances::reserve(&account(1), 10).unwrap();
+
+		// Creating a collection increases owner deposit by 2
+		assert_ok!(Nfts::create(
+			RuntimeOrigin::signed(account(1)),
+			account(1),
+			collection_config_with_all_settings_enabled()
+		));
+		assert_eq!(Collection::<Test>::get(0).unwrap().owner_deposit, 2);
+		assert_eq!(Balances::reserved_balance(&account(1)), 12);
+
+		// Setting collection metadata increases owner deposit by 10
+		assert_ok!(Nfts::set_collection_metadata(
+			RuntimeOrigin::signed(account(1)),
+			0,
+			bvec![0, 0, 0, 0, 0, 0, 0, 0, 0],
+		));
+		assert_eq!(Collection::<Test>::get(0).unwrap().owner_deposit, 12);
+		assert_eq!(Balances::reserved_balance(&account(1)), 22);
+
+		// Clearing collection metadata decreases owner deposit by 10
+		assert_ok!(Nfts::clear_collection_metadata(RuntimeOrigin::signed(account(1)), 0));
+		assert_eq!(Collection::<Test>::get(0).unwrap().owner_deposit, 2);
+		assert_eq!(Balances::reserved_balance(&account(1)), 12);
+
+		// Destroying the collection removes it from storage
+		assert_ok!(Nfts::destroy(
+			RuntimeOrigin::signed(account(1)),
+			0,
+			DestroyWitness { item_configs: 0, item_metadatas: 0, attributes: 0 }
+		));
+		assert_eq!(Collection::<Test>::get(0), None);
+		assert_eq!(Balances::reserved_balance(&account(1)), 10);
+	});
+}
diff --git a/substrate/frame/uniques/src/lib.rs b/substrate/frame/uniques/src/lib.rs
index f7cc6b044d7..2291d19de2b 100644
--- a/substrate/frame/uniques/src/lib.rs
+++ b/substrate/frame/uniques/src/lib.rs
@@ -1399,7 +1399,7 @@ pub mod pallet {
 				.map(|_| None)
 				.or_else(|origin| ensure_signed(origin).map(Some))?;
 
-			let details =
+			let mut details =
 				Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
 			if let Some(check_owner) = &maybe_check_owner {
 				ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
@@ -1411,6 +1411,8 @@ pub mod pallet {
 
 				let deposit = metadata.take().ok_or(Error::<T, I>::UnknownCollection)?.deposit;
 				T::Currency::unreserve(&details.owner, deposit);
+				details.total_deposit.saturating_reduce(deposit);
+				Collection::<T, I>::insert(&collection, details);
 				Self::deposit_event(Event::CollectionMetadataCleared { collection });
 				Ok(())
 			})
diff --git a/substrate/frame/uniques/src/tests.rs b/substrate/frame/uniques/src/tests.rs
index afd0352bf90..5dfe43c9688 100644
--- a/substrate/frame/uniques/src/tests.rs
+++ b/substrate/frame/uniques/src/tests.rs
@@ -1062,3 +1062,41 @@ fn buy_item_should_work() {
 		}
 	});
 }
+
+#[test]
+fn clear_collection_metadata_works() {
+	new_test_ext().execute_with(|| {
+		// Start with an account with 100 balance, 10 of which are reserved
+		Balances::make_free_balance_be(&1, 100);
+		Balances::reserve(&1, 10).unwrap();
+
+		// Create a Unique which increases total_deposit by 2
+		assert_ok!(Uniques::create(RuntimeOrigin::signed(1), 0, 123));
+		assert_eq!(Collection::<Test>::get(0).unwrap().total_deposit, 2);
+		assert_eq!(Balances::reserved_balance(&1), 12);
+
+		// Set collection metadata which increases total_deposit by 10
+		assert_ok!(Uniques::set_collection_metadata(
+			RuntimeOrigin::signed(1),
+			0,
+			bvec![0, 0, 0, 0, 0, 0, 0, 0, 0],
+			false
+		));
+		assert_eq!(Collection::<Test>::get(0).unwrap().total_deposit, 12);
+		assert_eq!(Balances::reserved_balance(&1), 22);
+
+		// Clearing collection metadata reduces total_deposit by the expected amount
+		assert_ok!(Uniques::clear_collection_metadata(RuntimeOrigin::signed(1), 0));
+		assert_eq!(Collection::<Test>::get(0).unwrap().total_deposit, 2);
+		assert_eq!(Balances::reserved_balance(&1), 12);
+
+		// Destroying the collection removes it from storage
+		assert_ok!(Uniques::destroy(
+			RuntimeOrigin::signed(1),
+			0,
+			DestroyWitness { items: 0, item_metadatas: 0, attributes: 0 }
+		));
+		assert_eq!(Collection::<Test>::get(0), None);
+		assert_eq!(Balances::reserved_balance(&1), 10);
+	});
+}
-- 
GitLab